ErrorCode MinizipUtils::add_file(zipFile zf, const std::string &name, const std::vector<unsigned char> &contents) { // Obviously never true, but we'll keep it here just in case bool zip64 = static_cast<uint64_t>(contents.size()) >= ((1ull << 32) - 1); zip_fileinfo zi; memset(&zi, 0, sizeof(zi)); int ret = zipOpenNewFileInZip2_64( zf, // file name.c_str(), // filename &zi, // zip_fileinfo nullptr, // extrafield_local 0, // size_extrafield_local nullptr, // extrafield_global 0, // size_extrafield_global nullptr, // comment Z_DEFLATED, // method Z_DEFAULT_COMPRESSION, // level 0, // raw zip64 // zip64 ); if (ret != ZIP_OK) { LOGE("minizip: Failed to open inner file: %s", zip_error_string(ret).c_str()); return ErrorCode::ArchiveWriteDataError; } // Write data to file ret = zipWriteInFileInZip(zf, contents.data(), static_cast<uint32_t>(contents.size())); if (ret != ZIP_OK) { LOGE("minizip: Failed to write inner file data: %s", zip_error_string(ret).c_str()); zipCloseFileInZip(zf); return ErrorCode::ArchiveWriteDataError; } ret = zipCloseFileInZip(zf); if (ret != ZIP_OK) { LOGE("minizip: Failed to close inner file: %s", zip_error_string(ret).c_str()); return ErrorCode::ArchiveWriteDataError; } return ErrorCode::NoError; }
PatcherError FileUtils::mzAddFile(zipFile zf, const std::string &name, const std::vector<unsigned char> &contents) { // Obviously never true, but we'll keep it here just in case bool zip64 = (uint64_t) contents.size() >= ((1ull << 32) - 1); zip_fileinfo zi; memset(&zi, 0, sizeof(zi)); int ret = zipOpenNewFileInZip2_64( zf, // file name.c_str(), // filename &zi, // zip_fileinfo nullptr, // extrafield_local 0, // size_extrafield_local nullptr, // extrafield_global 0, // size_extrafield_global nullptr, // comment Z_DEFLATED, // method Z_DEFAULT_COMPRESSION, // level 0, // raw zip64 // zip64 ); if (ret != ZIP_OK) { FLOGW("minizip: Failed to add file (error code: {}): [memory]", ret); return PatcherError::createArchiveError( ErrorCode::ArchiveWriteDataError, name); } // Write data to file ret = zipWriteInFileInZip(zf, contents.data(), contents.size()); if (ret != ZIP_OK) { FLOGW("minizip: Failed to write data (error code: {}): [memory]", ret); zipCloseFileInZip(zf); return PatcherError::createArchiveError( ErrorCode::ArchiveWriteDataError, name); } zipCloseFileInZip(zf); return PatcherError(); }
bool OdinPatcher::Impl::processContents() { archive_entry *entry; int laRet; int mzRet; zipFile zf = MinizipUtils::ctxGetZipFile(zOutput); while ((laRet = archive_read_next_header(aInput, &entry)) == ARCHIVE_OK) { if (cancelled) return false; const char *name = archive_entry_pathname(entry); if (!name) { continue; } updateDetails(name); if (strcmp(name, "boot.img") == 0) { LOGD("Handling boot.img"); // Boot images should never be over about 30 MiB. This check is here // so the patcher won't try to read a multi-gigabyte system image // into RAM la_int64_t size = archive_entry_size(entry); if (size > 30 * 1024 * 1024) { LOGE("Boot image exceeds 30 MiB: %" PRId64, size); error = ErrorCode::BootImageTooLargeError; return false; } std::vector<unsigned char> data; if (size > 0) { data.reserve(size); } char buf[10240]; la_ssize_t n; while ((n = archive_read_data(aInput, buf, sizeof(buf))) > 0) { data.insert(data.end(), buf, buf + n); } if (n != 0) { LOGE("libarchive: Failed to read data: %s", archive_error_string(aInput)); error = ErrorCode::ArchiveReadDataError; return false; } if (!MultiBootPatcher::patchBootImage(pc, info, &data, &error)) { return false; } auto errorRet = MinizipUtils::addFile(zf, name, data); if (errorRet != ErrorCode::NoError) { error = errorRet; return false; } continue; } else if (!StringUtils::starts_with(name, "cache.img") && !StringUtils::starts_with(name, "system.img")) { LOGD("Skipping %s", name); if (archive_read_data_skip(aInput) != ARCHIVE_OK) { LOGE("libarchive: Failed to skip data: %s", archive_error_string(aInput)); error = ErrorCode::ArchiveReadDataError; return false; } continue; } LOGD("Handling %s", name); std::string zipName(name); if (StringUtils::ends_with(name, ".ext4")) { zipName.erase(zipName.size() - 5); } zipName += ".sparse"; // Ha! I'll be impressed if a Samsung firmware image does NOT need zip64 int zip64 = archive_entry_size(entry) > ((1ll << 32) - 1); zip_fileinfo zi; memset(&zi, 0, sizeof(zi)); // Open file in output zip mzRet = zipOpenNewFileInZip2_64( zf, // file zipName.c_str(), // filename &zi, // zip_fileinfo nullptr, // extrafield_local 0, // size_extrafield_local nullptr, // extrafield_global 0, // size_extrafield_global nullptr, // comment Z_DEFLATED, // method Z_DEFAULT_COMPRESSION, // level 0, // raw zip64 // zip64 ); if (mzRet != ZIP_OK) { LOGE("minizip: Failed to open new file in output zip: %s", MinizipUtils::zipErrorString(mzRet).c_str()); error = ErrorCode::ArchiveWriteHeaderError; return false; } la_ssize_t nRead; char buf[10240]; while ((nRead = archive_read_data(aInput, buf, sizeof(buf))) > 0) { if (cancelled) return false; mzRet = zipWriteInFileInZip(zf, buf, nRead); if (mzRet != ZIP_OK) { LOGE("minizip: Failed to write %s in output zip: %s", zipName.c_str(), MinizipUtils::zipErrorString(mzRet).c_str()); error = ErrorCode::ArchiveWriteDataError; zipCloseFileInZip(zf); return false; } } if (nRead != 0) { LOGE("libarchive: Failed to read %s: %s", name, archive_error_string(aInput)); error = ErrorCode::ArchiveReadDataError; zipCloseFileInZip(zf); return false; } // Close file in output zip mzRet = zipCloseFileInZip(zf); if (mzRet != ZIP_OK) { LOGE("minizip: Failed to close file in output zip: %s", MinizipUtils::zipErrorString(mzRet).c_str()); error = ErrorCode::ArchiveWriteDataError; return false; } } if (laRet != ARCHIVE_EOF) { LOGE("libarchive: Failed to read header: %s", archive_error_string(aInput)); error = ErrorCode::ArchiveReadHeaderError; return false; } if (cancelled) return false; return true; }
PatcherError FileUtils::mzAddFile(zipFile zf, const std::string &name, const std::string &path) { // Copy file into archive std::ifstream file(path, std::ios::binary); if (file.fail()) { return PatcherError::createIOError( ErrorCode::FileOpenError, path); } file.seekg(0, std::ios::end); auto size = file.tellg(); file.seekg(0, std::ios::beg); bool zip64 = (uint64_t) size >= ((1ull << 32) - 1); zip_fileinfo zi; memset(&zi, 0, sizeof(zi)); mzGetFileTime(path, &zi.tmz_date, &zi.dosDate); int ret = zipOpenNewFileInZip2_64( zf, // file name.c_str(), // filename &zi, // zip_fileinfo nullptr, // extrafield_local 0, // size_extrafield_local nullptr, // extrafield_global 0, // size_extrafield_global nullptr, // comment Z_DEFLATED, // method Z_DEFAULT_COMPRESSION, // level 0, // raw zip64 // zip64 ); if (ret != ZIP_OK) { FLOGW("minizip: Failed to add file (error code: {}): {}", ret, path); return PatcherError::createArchiveError( ErrorCode::ArchiveWriteDataError, name); } // Write data to file char buf[32768]; std::streamsize n; while (!file.eof()) { file.read(buf, 32768); n = file.gcount(); ret = zipWriteInFileInZip(zf, buf, n); if (ret != ZIP_OK) { FLOGW("minizip: Failed to write data (error code: {}): {}", ret, path); zipCloseFileInZip(zf); return PatcherError::createArchiveError( ErrorCode::ArchiveWriteDataError, name); } } if (file.bad()) { zipCloseFileInZip(zf); return PatcherError::createIOError( ErrorCode::FileReadError, path); } zipCloseFileInZip(zf); return PatcherError(); }
bool FileUtils::mzCopyFileRaw(unzFile uf, zipFile zf, const std::string &name, void (*cb)(uint64_t bytes, void *), void *userData) { unz_file_info64 ufi; if (!mzGetInfo(uf, &ufi, nullptr)) { return false; } bool zip64 = ufi.uncompressed_size >= ((1ull << 32) - 1); zip_fileinfo zfi; memset(&zfi, 0, sizeof(zfi)); zfi.dosDate = ufi.dosDate; zfi.tmz_date.tm_sec = ufi.tmu_date.tm_sec; zfi.tmz_date.tm_min = ufi.tmu_date.tm_min; zfi.tmz_date.tm_hour = ufi.tmu_date.tm_hour; zfi.tmz_date.tm_mday = ufi.tmu_date.tm_mday; zfi.tmz_date.tm_mon = ufi.tmu_date.tm_mon; zfi.tmz_date.tm_year = ufi.tmu_date.tm_year; zfi.internal_fa = ufi.internal_fa; zfi.external_fa = ufi.external_fa; int method; int level; // Open raw file in input zip int ret = unzOpenCurrentFile2( uf, // file &method, // method &level, // level 1 // raw ); if (ret != UNZ_OK) { return false; } // Open raw file in output zip ret = zipOpenNewFileInZip2_64( zf, // file name.c_str(), // filename &zfi, // zip_fileinfo nullptr, // extrafield_local 0, // size_extrafield_local nullptr, // extrafield_global 0, // size_extrafield_global nullptr, // comment method, // method level, // level 1, // raw zip64 // zip64 ); if (ret != ZIP_OK) { unzCloseCurrentFile(uf); return false; } uint64_t bytes = 0; // Exceeds Android's default stack size, unfortunately, so allocate // on the heap std::vector<unsigned char> buf(1024 * 1024); // 1MiB int bytes_read; double ratio; while ((bytes_read = unzReadCurrentFile(uf, buf.data(), buf.size())) > 0) { bytes += bytes_read; if (cb) { // Scale this to the uncompressed size for the purposes of a // progress bar ratio = (double) bytes / ufi.compressed_size; cb(ratio * ufi.uncompressed_size, userData); } ret = zipWriteInFileInZip(zf, buf.data(), bytes_read); if (ret != ZIP_OK) { unzCloseCurrentFile(uf); zipCloseFileInZip(zf); return false; } } unzCloseCurrentFile(uf); zipCloseFileInZipRaw64(zf, ufi.uncompressed_size, ufi.crc); return bytes_read == 0; }
ErrorCode MinizipUtils::add_file(zipFile zf, const std::string &name, const std::string &path) { // Copy file into archive StandardFile file; int ret; auto open_ret = FileUtils::open_file(file, path, FileOpenMode::ReadOnly); if (!open_ret) { LOGE("%s: Failed to open for reading: %s", path.c_str(), open_ret.error().message().c_str()); return ErrorCode::FileOpenError; } auto size = file.seek(0, SEEK_END); if (!size) { LOGE("%s: Failed to seek file: %s", path.c_str(), size.error().message().c_str()); return ErrorCode::FileSeekError; } auto seek_ret = file.seek(0, SEEK_SET); if (!seek_ret) { LOGE("%s: Failed to seek file: %s", path.c_str(), seek_ret.error().message().c_str()); return ErrorCode::FileSeekError; } bool zip64 = size.value() >= ((1ull << 32) - 1); zip_fileinfo zi; memset(&zi, 0, sizeof(zi)); if (!get_file_time(path, &zi.dos_date)) { LOGE("%s: Failed to get modification time", path.c_str()); return ErrorCode::FileOpenError; } ret = zipOpenNewFileInZip2_64( zf, // file name.c_str(), // filename &zi, // zip_fileinfo nullptr, // extrafield_local 0, // size_extrafield_local nullptr, // extrafield_global 0, // size_extrafield_global nullptr, // comment Z_DEFLATED, // method Z_DEFAULT_COMPRESSION, // level 0, // raw zip64 // zip64 ); if (ret != ZIP_OK) { LOGE("minizip: Failed to open inner file: %s", zip_error_string(ret).c_str()); return ErrorCode::ArchiveWriteDataError; } // Write data to file char buf[32768]; while (true) { auto bytes_read = file.read(buf, sizeof(buf)); if (!bytes_read) { LOGE("%s: Failed to read data: %s", path.c_str(), bytes_read.error().message().c_str()); zipCloseFileInZip(zf); return ErrorCode::FileReadError; } else if (bytes_read.value() == 0) { break; } ret = zipWriteInFileInZip( zf, buf, static_cast<uint32_t>(bytes_read.value())); if (ret != ZIP_OK) { LOGE("minizip: Failed to write inner file data: %s", zip_error_string(ret).c_str()); zipCloseFileInZip(zf); return ErrorCode::ArchiveWriteDataError; } } ret = zipCloseFileInZip(zf); if (ret != ZIP_OK) { LOGE("minizip: Failed to close inner file: %s", zip_error_string(ret).c_str()); return ErrorCode::ArchiveWriteDataError; } return ErrorCode::NoError; }
bool MinizipUtils::copy_file_raw(unzFile uf, zipFile zf, const std::string &name, void (*cb)(uint64_t bytes, void *), void *userData) { unz_file_info64 ufi; if (!get_info(uf, &ufi, nullptr)) { return false; } bool zip64 = ufi.uncompressed_size >= ((1ull << 32) - 1); zip_fileinfo zfi; memset(&zfi, 0, sizeof(zfi)); zfi.dos_date = ufi.dos_date; zfi.internal_fa = ufi.internal_fa; zfi.external_fa = ufi.external_fa; int method; int level; // Open raw file in input zip int ret = unzOpenCurrentFile2( uf, // file &method, // method &level, // level 1 // raw ); if (ret != UNZ_OK) { LOGE("miniunz: Failed to open inner file: %s", unz_error_string(ret).c_str()); return false; } // Open raw file in output zip ret = zipOpenNewFileInZip2_64( zf, // file name.c_str(), // filename &zfi, // zip_fileinfo nullptr, // extrafield_local 0, // size_extrafield_local nullptr, // extrafield_global 0, // size_extrafield_global nullptr, // comment static_cast<uint16_t>(method), // method level, // level 1, // raw zip64 // zip64 ); if (ret != ZIP_OK) { LOGE("minizip: Failed to open inner file: %s", zip_error_string(ret).c_str()); unzCloseCurrentFile(uf); return false; } uint64_t bytes = 0; // minizip no longer supports buffers larger than UINT16_MAX char buf[UINT16_MAX]; int bytes_read; double ratio; while ((bytes_read = unzReadCurrentFile(uf, buf, sizeof(buf))) > 0) { bytes += static_cast<uint64_t>(bytes_read); if (cb) { // Scale this to the uncompressed size for the purposes of a // progress bar ratio = static_cast<double>(bytes) / static_cast<double>(ufi.compressed_size); cb(static_cast<uint64_t>( ratio * static_cast<double>(ufi.uncompressed_size)), userData); } ret = zipWriteInFileInZip(zf, buf, static_cast<uint32_t>(bytes_read)); if (ret != ZIP_OK) { LOGE("minizip: Failed to write data to inner file: %s", zip_error_string(ret).c_str()); unzCloseCurrentFile(uf); zipCloseFileInZip(zf); return false; } } if (bytes_read != 0) { LOGE("miniunz: Finished before reaching inner file's EOF: %s", unz_error_string(bytes_read).c_str()); } bool close_success = true; ret = unzCloseCurrentFile(uf); if (ret != UNZ_OK) { LOGE("miniunz: Failed to close inner file: %s", unz_error_string(ret).c_str()); close_success = false; } ret = zipCloseFileInZipRaw64(zf, ufi.uncompressed_size, ufi.crc); if (ret != ZIP_OK) { LOGE("minizip: Failed to close inner file: %s", zip_error_string(ret).c_str()); close_success = false; } return bytes_read == 0 && close_success; }
ErrorCode FileUtils::mzAddFile(zipFile zf, const std::string &name, const std::string &path) { // Copy file into archive io::File file; if (!file.open(path, io::File::OpenRead)) { FLOGE("%s: Failed to open for reading: %s", path.c_str(), file.errorString().c_str()); return ErrorCode::FileOpenError; } uint64_t size; file.seek(0, io::File::SeekEnd); file.tell(&size); file.seek(0, io::File::SeekBegin); bool zip64 = size >= ((1ull << 32) - 1); zip_fileinfo zi; memset(&zi, 0, sizeof(zi)); mzGetFileTime(path, &zi.tmz_date, &zi.dosDate); int ret = zipOpenNewFileInZip2_64( zf, // file name.c_str(), // filename &zi, // zip_fileinfo nullptr, // extrafield_local 0, // size_extrafield_local nullptr, // extrafield_global 0, // size_extrafield_global nullptr, // comment Z_DEFLATED, // method Z_DEFAULT_COMPRESSION, // level 0, // raw zip64 // zip64 ); if (ret != ZIP_OK) { FLOGW("minizip: Failed to add file (error code: %d): %s", ret, path.c_str()); return ErrorCode::ArchiveWriteDataError; } // Write data to file char buf[32768]; uint64_t bytesRead; while (file.read(buf, sizeof(buf), &bytesRead)) { ret = zipWriteInFileInZip(zf, buf, bytesRead); if (ret != ZIP_OK) { FLOGW("minizip: Failed to write data (error code: %d): %s", ret, path.c_str()); zipCloseFileInZip(zf); return ErrorCode::ArchiveWriteDataError; } } if (file.error() != io::File::ErrorEndOfFile) { zipCloseFileInZip(zf); FLOGE("%s: Failed to read file: %s", path.c_str(), file.errorString().c_str()); return ErrorCode::FileReadError; } zipCloseFileInZip(zf); return ErrorCode::NoError; }