oc::result<void> SonyElfFormatWriter::get_entry(File &file, Entry &entry)
{
    OUTCOME_TRYV(m_seg->get_entry(file, entry, m_writer));

    auto swentry = m_seg->entry();

    // Silently handle cmdline entry
    if (swentry->type == SONY_ELF_ENTRY_CMDLINE) {
        entry.clear();

        entry.set_size(m_cmdline.size());

        auto set_as_fatal = finally([&] {
            m_writer.set_fatal();
        });

        OUTCOME_TRYV(write_entry(file, entry));
        OUTCOME_TRYV(write_data(file, m_cmdline.data(), m_cmdline.size()));
        OUTCOME_TRYV(finish_entry(file));
        OUTCOME_TRYV(get_entry(file, entry));

        set_as_fatal.dismiss();
    }

    return oc::success();
}
예제 #2
0
oc::result<void> socket_write_string_array(int fd, const std::vector<std::string> &list)
{
    if (list.size() > INT32_MAX) {
        return std::errc::invalid_argument;
    }

    OUTCOME_TRYV(socket_write_int32(fd, static_cast<int32_t>(list.size())));

    for (const std::string &str : list) {
        OUTCOME_TRYV(socket_write_string(fd, str));
    }

    return oc::success();
}
oc::result<void> AndroidFormatWriter::finish_entry(File &file)
{
    OUTCOME_TRYV(m_seg->finish_entry(file, m_writer));

    auto swentry = m_seg->entry();

    // Update SHA1 hash
    uint32_t le32_size = mb_htole32(*swentry->size);

    // Include size for everything except empty DT images
    if ((swentry->type != ENTRY_TYPE_DEVICE_TREE || *swentry->size > 0)
            && !SHA1_Update(&m_sha_ctx, &le32_size, sizeof(le32_size))) {
        m_writer.set_fatal();
        return AndroidError::Sha1UpdateError;
    }

    switch (swentry->type) {
    case ENTRY_TYPE_KERNEL:
        m_hdr.kernel_size = *swentry->size;
        break;
    case ENTRY_TYPE_RAMDISK:
        m_hdr.ramdisk_size = *swentry->size;
        break;
    case ENTRY_TYPE_SECONDBOOT:
        m_hdr.second_size = *swentry->size;
        break;
    case ENTRY_TYPE_DEVICE_TREE:
        m_hdr.dt_size = *swentry->size;
        break;
    }

    return oc::success();
}
예제 #4
0
oc::result<void> LokiFormatReader::read_header(File &file, Header &header)
{
    uint64_t kernel_offset;
    uint64_t ramdisk_offset;
    uint64_t dt_offset = 0;
    uint32_t kernel_size;
    uint32_t ramdisk_size;

    // New-style images record the original values of changed fields in the
    // Android header
    if (m_loki_hdr.orig_kernel_size != 0
            && m_loki_hdr.orig_ramdisk_size != 0
            && m_loki_hdr.ramdisk_addr != 0) {
        OUTCOME_TRYV(read_header_new(m_reader, file, m_hdr, m_loki_hdr, header,
                                     kernel_offset, kernel_size,
                                     ramdisk_offset, ramdisk_size,
                                     dt_offset));
    } else {
        OUTCOME_TRYV(read_header_old(m_reader, file, m_hdr, m_loki_hdr, header,
                                     kernel_offset, kernel_size,
                                     ramdisk_offset, ramdisk_size));
    }

    std::vector<SegmentReaderEntry> entries;

    entries.push_back({
        ENTRY_TYPE_KERNEL, kernel_offset, kernel_size, false
    });
    entries.push_back({
        ENTRY_TYPE_RAMDISK, ramdisk_offset, ramdisk_size, false
    });
    if (m_hdr.dt_size > 0 && dt_offset != 0) {
        entries.push_back({
            ENTRY_TYPE_DEVICE_TREE, dt_offset, m_hdr.dt_size, false
        });
    }

    return m_seg->set_entries(std::move(entries));
}
예제 #5
0
oc::result<ArchRegs> read_regs(pid_t tid)
{
    ArchRegs ar;

    iovec iov;
    iov.iov_base = &ar.m_regs;
    iov.iov_len = sizeof(ar.m_regs);

    OUTCOME_TRYV(detail::read_raw_regs(tid, iov));

    ar.m_abi = NATIVE_ARCH_ABI;

    return std::move(ar);
}
예제 #6
0
oc::result<void> socket_write_string(int fd, const std::string &str)
{
    if (str.size() > INT32_MAX) {
        return std::errc::invalid_argument;
    }

    OUTCOME_TRYV(socket_write_int32(fd, static_cast<int32_t>(str.size())));
    OUTCOME_TRY(n, socket_write(fd, str.data(), str.size()));
    if (n != str.size()) {
        return std::errc::io_error;
    }

    return oc::success();
}
예제 #7
0
oc::result<void> socket_write_bytes(int fd, const void *data, size_t len)
{
    if (len > INT32_MAX) {
        return std::errc::invalid_argument;
    }

    OUTCOME_TRYV(socket_write_int32(fd, static_cast<int32_t>(len)));
    OUTCOME_TRY(n, socket_write(fd, data, len));
    if (n != len) {
        return std::errc::io_error;
    }

    return oc::success();
}
oc::result<void> SonyElfFormatWriter::finish_entry(File &file)
{
    OUTCOME_TRYV(m_seg->finish_entry(file, m_writer));

    auto swentry = m_seg->entry();

    switch (swentry->type) {
    case ENTRY_TYPE_KERNEL:
        m_hdr_kernel.p_offset = static_cast<Elf32_Off>(swentry->offset);
        m_hdr_kernel.p_filesz = *swentry->size;
        m_hdr_kernel.p_memsz = *swentry->size;
        break;
    case ENTRY_TYPE_RAMDISK:
        m_hdr_ramdisk.p_offset = static_cast<Elf32_Off>(swentry->offset);
        m_hdr_ramdisk.p_filesz = *swentry->size;
        m_hdr_ramdisk.p_memsz = *swentry->size;
        break;
    case ENTRY_TYPE_SONY_IPL:
        m_hdr_ipl.p_offset = static_cast<Elf32_Off>(swentry->offset);
        m_hdr_ipl.p_filesz = *swentry->size;
        m_hdr_ipl.p_memsz = *swentry->size;
        break;
    case ENTRY_TYPE_SONY_RPM:
        m_hdr_rpm.p_offset = static_cast<Elf32_Off>(swentry->offset);
        m_hdr_rpm.p_filesz = *swentry->size;
        m_hdr_rpm.p_memsz = *swentry->size;
        break;
    case ENTRY_TYPE_SONY_APPSBL:
        m_hdr_appsbl.p_offset = static_cast<Elf32_Off>(swentry->offset);
        m_hdr_appsbl.p_filesz = *swentry->size;
        m_hdr_appsbl.p_memsz = *swentry->size;
        break;
    case SONY_ELF_ENTRY_CMDLINE:
        m_hdr_cmdline.p_offset = static_cast<Elf32_Off>(swentry->offset);
        m_hdr_cmdline.p_filesz = *swentry->size;
        m_hdr_cmdline.p_memsz = *swentry->size;
        break;
    }

    if (*swentry->size > 0) {
        ++m_hdr.e_phnum;
    }

    return oc::success();
}
예제 #9
0
oc::result<void> MtkFormatWriter::finish_entry(File &file)
{
    OUTCOME_TRYV(m_seg->finish_entry(file, m_writer));

    auto swentry = m_seg->entry();

    if ((swentry->type == ENTRY_TYPE_KERNEL
            || swentry->type == ENTRY_TYPE_RAMDISK)
            && *swentry->size == UINT32_MAX - sizeof(MtkHeader)) {
        m_writer.set_fatal();
        return MtkError::EntryTooLargeToFitMtkHeader;
    } else if ((swentry->type == ENTRY_TYPE_MTK_KERNEL_HEADER
            || swentry->type == ENTRY_TYPE_MTK_RAMDISK_HEADER)
            && *swentry->size != sizeof(MtkHeader)) {
        m_writer.set_fatal();
        return MtkError::InvalidEntrySizeForMtkHeader;
    }

    switch (swentry->type) {
    case ENTRY_TYPE_KERNEL:
        m_hdr.kernel_size = static_cast<uint32_t>(
                *swentry->size + sizeof(MtkHeader));
        break;
    case ENTRY_TYPE_RAMDISK:
        m_hdr.ramdisk_size = static_cast<uint32_t>(
                *swentry->size + sizeof(MtkHeader));
        break;
    case ENTRY_TYPE_SECONDBOOT:
        m_hdr.second_size = *swentry->size;
        break;
    case ENTRY_TYPE_DEVICE_TREE:
        m_hdr.dt_size = *swentry->size;
        break;
    }

    return oc::success();
}
예제 #10
0
oc::result<void> MtkFormatWriter::write_header(File &file, const Header &header)
{
    // Construct header
    m_hdr = {};
    memcpy(m_hdr.magic, android::BOOT_MAGIC, android::BOOT_MAGIC_SIZE);

    if (auto address = header.kernel_address()) {
        m_hdr.kernel_addr = *address;
    }
    if (auto address = header.ramdisk_address()) {
        m_hdr.ramdisk_addr = *address;
    }
    if (auto address = header.secondboot_address()) {
        m_hdr.second_addr = *address;
    }
    if (auto address = header.kernel_tags_address()) {
        m_hdr.tags_addr = *address;
    }
    if (auto page_size = header.page_size()) {
        switch (*page_size) {
        case 2048:
        case 4096:
        case 8192:
        case 16384:
        case 32768:
        case 65536:
        case 131072:
            m_hdr.page_size = *page_size;
            break;
        default:
            //DEBUG("Invalid page size: %" PRIu32, *page_size);
            return android::AndroidError::InvalidPageSize;
        }
    } else {
        return android::AndroidError::MissingPageSize;
    }

    if (auto board_name = header.board_name()) {
        if (board_name->size() >= sizeof(m_hdr.name)) {
            return android::AndroidError::BoardNameTooLong;
        }

        strncpy(reinterpret_cast<char *>(m_hdr.name), board_name->c_str(),
                sizeof(m_hdr.name) - 1);
        m_hdr.name[sizeof(m_hdr.name) - 1] = '\0';
    }
    if (auto cmdline = header.kernel_cmdline()) {
        if (cmdline->size() >= sizeof(m_hdr.cmdline)) {
            return android::AndroidError::KernelCmdlineTooLong;
        }

        strncpy(reinterpret_cast<char *>(m_hdr.cmdline), cmdline->c_str(),
                sizeof(m_hdr.cmdline) - 1);
        m_hdr.cmdline[sizeof(m_hdr.cmdline) - 1] = '\0';
    }

    // TODO: UNUSED
    // TODO: ID

    std::vector<SegmentWriterEntry> entries;

    entries.push_back({ ENTRY_TYPE_MTK_KERNEL_HEADER, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_KERNEL, 0, {}, m_hdr.page_size });
    entries.push_back({ ENTRY_TYPE_MTK_RAMDISK_HEADER, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_RAMDISK, 0, {}, m_hdr.page_size });
    entries.push_back({ ENTRY_TYPE_SECONDBOOT, 0, {}, m_hdr.page_size });
    entries.push_back({ ENTRY_TYPE_DEVICE_TREE, 0, {}, m_hdr.page_size });

    OUTCOME_TRYV(m_seg->set_entries(std::move(entries)));

    // Start writing after first page
    auto seek_ret = file.seek(m_hdr.page_size, SEEK_SET);
    if (!seek_ret) {
        if (file.is_fatal()) { m_writer.set_fatal(); }
        return seek_ret.as_failure();
    }

    return oc::success();
}
예제 #11
0
oc::result<void> MtkFormatWriter::close(File &file)
{
    auto reset_state = finally([&] {
        m_hdr = {};
        m_seg = {};
    });

    if (m_writer.is_open()) {
        auto swentry = m_seg->entry();

        // If successful, finish up the boot image
        if (swentry == m_seg->entries().end()) {
            auto file_size = file.seek(0, SEEK_CUR);
            if (!file_size) {
                if (file.is_fatal()) { m_writer.set_fatal(); }
                return file_size.as_failure();
            }

            // Truncate to set size
            auto truncate_ret = file.truncate(file_size.value());
            if (!truncate_ret) {
                if (file.is_fatal()) { m_writer.set_fatal(); }
                return truncate_ret.as_failure();
            }

            // Update MTK header sizes
            for (auto const &entry : m_seg->entries()) {
                if (entry.type == ENTRY_TYPE_MTK_KERNEL_HEADER) {
                    OUTCOME_TRYV(_mtk_header_update_size(
                            m_writer, file, entry.offset,
                            static_cast<uint32_t>(
                                    m_hdr.kernel_size - sizeof(MtkHeader))));
                } else if (entry.type == ENTRY_TYPE_MTK_RAMDISK_HEADER) {
                    OUTCOME_TRYV(_mtk_header_update_size(
                            m_writer, file, entry.offset,
                            static_cast<uint32_t>(
                                    m_hdr.ramdisk_size - sizeof(MtkHeader))));
                }
            }

            // We need to take the performance hit and compute the SHA1 here.
            // We can't fill in the sizes in the MTK headers when we're writing
            // them. Thus, if we calculated the SHA1sum during write, it would
            // be incorrect.
            OUTCOME_TRYV(_mtk_compute_sha1(
                    m_writer, *m_seg, file,
                    reinterpret_cast<unsigned char *>(m_hdr.id)));

            // Convert fields back to little-endian
            android_fix_header_byte_order(m_hdr);

            // Seek back to beginning to write header
            auto seek_ret = file.seek(0, SEEK_SET);
            if (!seek_ret) {
                if (file.is_fatal()) { m_writer.set_fatal(); }
                return seek_ret.as_failure();
            }

            // Write header
            auto ret = file_write_exact(file, &m_hdr, sizeof(m_hdr));
            if (!ret) {
                if (file.is_fatal()) { m_writer.set_fatal(); }
                return ret.as_failure();
            }
        }
    }

    return oc::success();
}
oc::result<void> SonyElfFormatWriter::write_header(File &file,
                                                   const Header &header)
{
    m_cmdline.clear();

    m_hdr = {};
    m_hdr_kernel = {};
    m_hdr_ramdisk = {};
    m_hdr_cmdline = {};
    m_hdr_ipl = {};
    m_hdr_rpm = {};
    m_hdr_appsbl = {};

    // Construct ELF header
    memcpy(&m_hdr.e_ident, SONY_E_IDENT, SONY_EI_NIDENT);
    m_hdr.e_type = 2;
    m_hdr.e_machine = 40;
    m_hdr.e_version = 1;
    m_hdr.e_phoff = 52;
    m_hdr.e_shoff = 0;
    m_hdr.e_flags = 0;
    m_hdr.e_ehsize = sizeof(Sony_Elf32_Ehdr);
    m_hdr.e_phentsize = sizeof(Sony_Elf32_Phdr);
    m_hdr.e_shentsize = 0;
    m_hdr.e_shnum = 0;
    m_hdr.e_shstrndx = 0;

    if (auto entrypoint_address = header.entrypoint_address()) {
        m_hdr.e_entry = *entrypoint_address;
    } else if (auto kernel_address = header.kernel_address()) {
        m_hdr.e_entry = *kernel_address;
    }

    // Construct kernel program header
    m_hdr_kernel.p_type = SONY_E_TYPE_KERNEL;
    m_hdr_kernel.p_flags = SONY_E_FLAGS_KERNEL;
    m_hdr_kernel.p_align = 0;

    if (auto address = header.kernel_address()) {
        m_hdr_kernel.p_vaddr = *address;
        m_hdr_kernel.p_paddr = *address;
    }

    // Construct ramdisk program header
    m_hdr_ramdisk.p_type = SONY_E_TYPE_RAMDISK;
    m_hdr_ramdisk.p_flags = SONY_E_FLAGS_RAMDISK;
    m_hdr_ramdisk.p_align = 0;

    if (auto address = header.ramdisk_address()) {
        m_hdr_ramdisk.p_vaddr = *address;
        m_hdr_ramdisk.p_paddr = *address;
    }

    // Construct cmdline program header
    m_hdr_cmdline.p_type = SONY_E_TYPE_CMDLINE;
    m_hdr_cmdline.p_vaddr = 0;
    m_hdr_cmdline.p_paddr = 0;
    m_hdr_cmdline.p_flags = SONY_E_FLAGS_CMDLINE;
    m_hdr_cmdline.p_align = 0;

    if (auto cmdline = header.kernel_cmdline()) {
        m_cmdline = *cmdline;
    }

    // Construct IPL program header
    m_hdr_ipl.p_type = SONY_E_TYPE_IPL;
    m_hdr_ipl.p_flags = SONY_E_FLAGS_IPL;
    m_hdr_ipl.p_align = 0;

    if (auto address = header.sony_ipl_address()) {
        m_hdr_ipl.p_vaddr = *address;
        m_hdr_ipl.p_paddr = *address;
    }

    // Construct RPM program header
    m_hdr_rpm.p_type = SONY_E_TYPE_RPM;
    m_hdr_rpm.p_flags = SONY_E_FLAGS_RPM;
    m_hdr_rpm.p_align = 0;

    if (auto address = header.sony_rpm_address()) {
        m_hdr_rpm.p_vaddr = *address;
        m_hdr_rpm.p_paddr = *address;
    }

    // Construct APPSBL program header
    m_hdr_appsbl.p_type = SONY_E_TYPE_APPSBL;
    m_hdr_appsbl.p_flags = SONY_E_FLAGS_APPSBL;
    m_hdr_appsbl.p_align = 0;

    if (auto address = header.sony_appsbl_address()) {
        m_hdr_appsbl.p_vaddr = *address;
        m_hdr_appsbl.p_paddr = *address;
    }

    std::vector<SegmentWriterEntry> entries;

    entries.push_back({ ENTRY_TYPE_KERNEL, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_RAMDISK, 0, {}, 0 });
    entries.push_back({ SONY_ELF_ENTRY_CMDLINE, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_SONY_IPL, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_SONY_RPM, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_SONY_APPSBL, 0, {}, 0 });

    OUTCOME_TRYV(m_seg->set_entries(std::move(entries)));

    // Start writing at offset 4096
    auto seek_ret = file.seek(4096, SEEK_SET);
    if (!seek_ret) {
        if (file.is_fatal()) { m_writer.set_fatal(); }
        return seek_ret.as_failure();
    }

    return oc::success();
}
예제 #13
0
/*!
 * \brief Read header for new-style Loki image
 *
 * \param[in] reader Reader to set error message
 * \param[in] file File handle
 * \param[in] hdr Android header for image
 * \param[in] loki_hdr Loki header for image
 * \param[out] header Header instance to store header values
 * \param[out] kernel_offset_out Pointer to store kernel offset
 * \param[out] kernel_size_out Pointer to store kernel size
 * \param[out] ramdisk_offset_out Pointer to store ramdisk offset
 * \param[out] ramdisk_size_out Pointer to store ramdisk size
 * \param[out] dt_offset_out Pointer to store device tree offset
 *
 * \return Nothing if the header is successfully read. Otherwise, a specific
 *         error code.
 */
oc::result<void>
LokiFormatReader::read_header_new(Reader &reader, File &file,
                                  const android::AndroidHeader &hdr,
                                  const LokiHeader &loki_hdr,
                                  Header &header,
                                  uint64_t &kernel_offset_out,
                                  uint32_t &kernel_size_out,
                                  uint64_t &ramdisk_offset_out,
                                  uint32_t &ramdisk_size_out,
                                  uint64_t &dt_offset_out)
{
    uint32_t fake_size;
    uint32_t ramdisk_addr;

    if (hdr.page_size == 0) {
        return LokiError::PageSizeCannotBeZero;
    }

    if (is_lg_ramdisk_address(hdr.ramdisk_addr)) {
        fake_size = hdr.page_size;
    } else {
        fake_size = 0x200;
    }

    // Find original ramdisk address
    OUTCOME_TRYV(find_ramdisk_address(reader, file, hdr, loki_hdr,
                                      ramdisk_addr));

    // Restore original values in boot image header
    kernel_size_out = loki_hdr.orig_kernel_size;
    ramdisk_size_out = loki_hdr.orig_ramdisk_size;

    auto *name_ptr = reinterpret_cast<const char *>(hdr.name);
    auto name_size = strnlen(name_ptr, sizeof(hdr.name));

    auto *cmdline_ptr = reinterpret_cast<const char *>(hdr.cmdline);
    auto cmdline_size = strnlen(cmdline_ptr, sizeof(hdr.cmdline));

    header.set_supported_fields(NEW_SUPPORTED_FIELDS);
    header.set_board_name({{name_ptr, name_size}});
    header.set_kernel_cmdline({{cmdline_ptr, cmdline_size}});
    header.set_page_size(hdr.page_size);
    header.set_kernel_address(hdr.kernel_addr);
    header.set_ramdisk_address(ramdisk_addr);
    header.set_secondboot_address(hdr.second_addr);
    header.set_kernel_tags_address(hdr.tags_addr);

    uint64_t pos = 0;

    // pos cannot overflow due to the nature of the operands (adding UINT32_MAX
    // a few times can't overflow a uint64_t). File length overflow is checked
    // during read.

    // Header
    pos += hdr.page_size;

    // Kernel
    kernel_offset_out = pos;
    pos += loki_hdr.orig_kernel_size;
    pos += align_page_size<uint64_t>(pos, hdr.page_size);

    // Ramdisk
    ramdisk_offset_out = pos;
    pos += loki_hdr.orig_ramdisk_size;
    pos += align_page_size<uint64_t>(pos, hdr.page_size);

    // Device tree
    if (hdr.dt_size != 0) {
        pos += fake_size;
    }
    dt_offset_out = pos;
    pos += hdr.dt_size;
    pos += align_page_size<uint64_t>(pos, hdr.page_size);

    return oc::success();
}
예제 #14
0
/*!
 * \brief Read header for old-style Loki image
 *
 * \param[in] reader Reader to set error message
 * \param[in] file File handle
 * \param[in] hdr Android header for image
 * \param[in] loki_hdr Loki header for image
 * \param[out] header Header instance to store header values
 * \param[out] kernel_offset_out Pointer to store kernel offset
 * \param[out] kernel_size_out Pointer to store kernel size
 * \param[out] ramdisk_offset_out Pointer to store ramdisk offset
 * \param[out] ramdisk_size_out Pointer to store ramdisk size
 *
 * \return Nothing if the header is successfully read. Otherwise, a specific
 *         error code.
 */
oc::result<void>
LokiFormatReader::read_header_old(Reader &reader, File &file,
                                  const android::AndroidHeader &hdr,
                                  const LokiHeader &loki_hdr,
                                  Header &header,
                                  uint64_t &kernel_offset_out,
                                  uint32_t &kernel_size_out,
                                  uint64_t &ramdisk_offset_out,
                                  uint32_t &ramdisk_size_out)
{
    uint32_t tags_addr;
    uint32_t kernel_size;
    uint32_t ramdisk_size;
    uint32_t ramdisk_addr;
    uint64_t gzip_offset;

    if (hdr.page_size == 0) {
        return LokiError::PageSizeCannotBeZero;
    }

    // The kernel tags address is invalid in the old loki images, so use the
    // default for jflte
    tags_addr = hdr.kernel_addr - android::DEFAULT_KERNEL_OFFSET
            + android::DEFAULT_TAGS_OFFSET;

    // Try to guess kernel size
    OUTCOME_TRYV(find_linux_kernel_size(reader, file, hdr.page_size,
                                        kernel_size));

    // Look for gzip offset for the ramdisk
    OUTCOME_TRYV(find_gzip_offset_old(
            reader, file, hdr.page_size + kernel_size
                    + align_page_size<uint32_t>(kernel_size, hdr.page_size),
            gzip_offset));

    // Try to guess ramdisk size
    OUTCOME_TRYV(find_ramdisk_size_old(reader, file, hdr,
                                       static_cast<uint32_t>(gzip_offset),
                                       ramdisk_size));

    // Guess original ramdisk address
    OUTCOME_TRYV(find_ramdisk_address(reader, file, hdr, loki_hdr,
                                      ramdisk_addr));

    kernel_size_out = kernel_size;
    ramdisk_size_out = ramdisk_size;

    auto *name_ptr = reinterpret_cast<const char *>(hdr.name);
    auto name_size = strnlen(name_ptr, sizeof(hdr.name));

    auto *cmdline_ptr = reinterpret_cast<const char *>(hdr.cmdline);
    auto cmdline_size = strnlen(cmdline_ptr, sizeof(hdr.cmdline));

    header.set_supported_fields(OLD_SUPPORTED_FIELDS);
    header.set_board_name({{name_ptr, name_size}});
    header.set_kernel_cmdline({{cmdline_ptr, cmdline_size}});
    header.set_page_size(hdr.page_size);
    header.set_kernel_address(hdr.kernel_addr);
    header.set_ramdisk_address(ramdisk_addr);
    header.set_secondboot_address(hdr.second_addr);
    header.set_kernel_tags_address(tags_addr);

    uint64_t pos = 0;

    // pos cannot overflow due to the nature of the operands (adding UINT32_MAX
    // a few times can't overflow a uint64_t). File length overflow is checked
    // during read.

    // Header
    pos += hdr.page_size;

    // Kernel
    kernel_offset_out = pos;
    pos += kernel_size;
    pos += align_page_size<uint64_t>(pos, hdr.page_size);

    // Ramdisk
    ramdisk_offset_out = pos = gzip_offset;
    pos += ramdisk_size;
    pos += align_page_size<uint64_t>(pos, hdr.page_size);

    return oc::success();
}
예제 #15
0
/*!
 * \brief Find gzip ramdisk offset in old-style Loki image
 *
 * This function will search for gzip headers (`0x1f8b08`) with a flags byte of
 * `0x00` or `0x08`. It will find the first occurrence of either magic string.
 * If both are found, the one with the flags byte set to `0x08` takes precedence
 * as it indiciates that the original filename field is set. This is usually the
 * case for ramdisks packed via the `gzip` command line tool.
 *
 * \pre The file position can be at any offset prior to calling this function.
 *
 * \post The file pointer position is undefined after this function returns.
 *       Use File::seek() to return to a known position.
 *
 * \param[in] reader Reader to set error message
 * \param[in] file File handle
 * \param[in] start_offset Starting offset for search
 * \param[out] gzip_offset_out Pointer to store gzip ramdisk offset
 *
 * \return
 *   * Nothing if a gzip offset is found
 *   * A LokiError if no gzip offsets are found
 *   * A specific error if any file operation fails
 */
oc::result<void>
LokiFormatReader::find_gzip_offset_old(Reader &reader, File &file,
                                       uint32_t start_offset,
                                       uint64_t &gzip_offset_out)
{
    struct SearchResult
    {
        std::optional<uint64_t> flag0_offset;
        std::optional<uint64_t> flag8_offset;
    };

    // gzip header:
    // byte 0-1 : magic bytes 0x1f, 0x8b
    // byte 2   : compression (0x08 = deflate)
    // byte 3   : flags
    // byte 4-7 : modification timestamp
    // byte 8   : compression flags
    // byte 9   : operating system

    static const unsigned char gzip_deflate_magic[] = { 0x1f, 0x8b, 0x08 };

    SearchResult result = {};

    // Find first result with flags == 0x00 and flags == 0x08
    auto result_cb = [&](File &file_, uint64_t offset)
            -> oc::result<FileSearchAction> {
        unsigned char flags;

        // Stop early if possible
        if (result.flag0_offset && result.flag8_offset) {
            return FileSearchAction::Stop;
        }

        // Save original position
        OUTCOME_TRY(orig_offset, file_.seek(0, SEEK_CUR));

        // Seek to flags byte
        OUTCOME_TRYV(file_.seek(static_cast<int64_t>(offset + 3), SEEK_SET));

        // Read next bytes for flags
        auto ret = file_read_exact(file_, &flags, sizeof(flags));
        if (!ret) {
            if (ret.error() == FileError::UnexpectedEof) {
                return FileSearchAction::Stop;
            } else {
                return ret.as_failure();
            }
        }

        if (!result.flag0_offset && flags == 0x00) {
            result.flag0_offset = offset;
        } else if (!result.flag8_offset && flags == 0x08) {
            result.flag8_offset = offset;
        }

        // Restore original position as per contract
        OUTCOME_TRYV(file_.seek(static_cast<int64_t>(orig_offset), SEEK_SET));

        return FileSearchAction::Continue;
    };

    auto ret = file_search(file, start_offset, {}, 0, gzip_deflate_magic,
                           sizeof(gzip_deflate_magic), {}, result_cb);
    if (!ret) {
        if (file.is_fatal()) { reader.set_fatal(); }
        return ret.as_failure();
    }

    // Prefer gzip header with original filename flag since most loki'd boot
    // images will have been compressed manually with the gzip tool
    if (result.flag8_offset) {
        gzip_offset_out = *result.flag8_offset;
    } else if (result.flag0_offset) {
        gzip_offset_out = *result.flag0_offset;
    } else {
        return LokiError::NoRamdiskGzipHeaderFound;
    }

    return oc::success();
}