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(); }
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(); }
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)); }
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); }
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(); }
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(); }
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(); }
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(); }
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(); }
/*! * \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(); }
/*! * \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(); }
/*! * \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(); }