bool FileUtils::mzExtractFile(unzFile uf, const std::string &directory) { unz_file_info64 fi; std::string filename; if (!mzGetInfo(uf, &fi, &filename)) { return false; } std::string fullPath(directory); fullPath += "/"; fullPath += filename; std::string parentPath = io::dirName(fullPath); if (!io::createDirectories(parentPath)) { FLOGW("%s: Failed to create directory: %s", parentPath.c_str(), io::lastErrorString().c_str()); } io::File file; if (!file.open(fullPath, io::File::OpenWrite)) { FLOGE("%s: Failed to open for writing: %s", fullPath.c_str(), file.errorString().c_str()); return false; } int ret = unzOpenCurrentFile(uf); if (ret != UNZ_OK) { FLOGE("miniunz: Failed to open inner file: %s", mzUnzErrorString(ret).c_str()); return false; } int n; char buf[32768]; uint64_t bytesWritten; while ((n = unzReadCurrentFile(uf, buf, sizeof(buf))) > 0) { if (!file.write(buf, n, &bytesWritten)) { FLOGE("%s: Failed to write file: %s", fullPath.c_str(), file.errorString().c_str()); unzCloseCurrentFile(uf); return false; } } if (n != 0) { FLOGE("miniunz: Finished before reaching inner file's EOF: %s", mzUnzErrorString(n).c_str()); } ret = unzCloseCurrentFile(uf); if (ret != UNZ_OK) { FLOGE("miniunz: Failed to close inner file: %s", mzUnzErrorString(ret).c_str()); return false; } return n == 0; }
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(); }
PatchFilePatcher::PatchFilePatcher(const PatcherConfig * const pc, const FileInfo * const info, const PatchInfo::AutoPatcherArgs &args) : m_impl(new Impl()) { m_impl->pc = pc; m_impl->info = info; m_impl->args = args; // The arguments should have the patch to the file if (args.find(ArgFile) == args.end()) { LOGW("Arguments does not contain the path to a patch file"); return; } m_impl->patchFile = pc->dataDirectory() + "/patches/" + args.at(ArgFile); std::string contents; auto ret = FileUtils::readToString(m_impl->patchFile, &contents); if (!ret) { LOGW("Failed to read patch file"); return; } std::vector<std::string> lines; boost::split(lines, contents, boost::is_any_of("\n")); const std::regex reOrigFile("---\\s+(.+?)(?:$|\t)"); for (auto it = lines.begin(); it != lines.end(); ++it) { std::smatch what; if (std::regex_search(*it, what, reOrigFile)) { std::string file = what.str(1); if (boost::starts_with(file, "\"") && boost::ends_with(file, "\"")) { file.erase(file.begin()); file.erase(file.end() - 1); } // Skip files containing escaped characters if (file.find("\\") != std::string::npos) { FLOGW("Skipping file with escaped characters in filename: {}", file); continue; } // Strip leading slash (-p1) auto it = file.find("/"); if (it != std::string::npos) { file.erase(file.begin(), file.begin() + it + 1); } FLOGD("Found file in patch: {}", file); m_impl->files.push_back(file); } } }
void MultiBootPatcher::Impl::closeOutputArchive() { assert(zOutput != nullptr); int ret = FileUtils::mzCloseOutputFile(zOutput); if (ret != ZIP_OK) { FLOGW("minizip: Failed to close archive (error code: {})", ret); } zOutput = nullptr; }
/*! * \brief First pass of patching operation * * This performs the following operations: * * - If the PatchInfo has the `hasBootImage` parameter set to true for the * current file, then patch the boot images and copy them to the output file. * - Files needed by an AutoPatcher are extracted to the temporary directory. * - Otherwise, the file is copied directly to the output zip. */ bool MultiBootPatcher::Impl::pass1(zipFile const zOutput, const std::string &temporaryDir, const std::unordered_set<std::string> &exclude) { // Boot image params bool hasBootImage = info->patchInfo()->hasBootImage(); auto piBootImages = info->patchInfo()->bootImages(); int ret = unzGoToFirstFile(zInput); if (ret != UNZ_OK) { error = PatcherError::createArchiveError( ErrorCode::ArchiveReadHeaderError, std::string()); return false; } do { if (cancelled) return false; unz_file_info64 fi; std::string curFile; if (!FileUtils::mzGetInfo(zInput, &fi, &curFile)) { error = PatcherError::createArchiveError( ErrorCode::ArchiveReadHeaderError, curFile); return false; } updateFiles(++files, maxFiles); updateDetails(curFile); // Skip files that should be patched and added in pass 2 if (exclude.find(curFile) != exclude.end()) { if (!FileUtils::mzExtractFile(zInput, temporaryDir)) { error = PatcherError::createArchiveError( ErrorCode::ArchiveReadDataError, curFile); return false; } continue; } // Try to patch the patchinfo-defined boot images as well as // files that end in a common boot image extension bool inList = std::find(piBootImages.begin(), piBootImages.end(), curFile) != piBootImages.end(); bool isExtImg = boost::ends_with(curFile, ".img"); bool isExtLok = boost::ends_with(curFile, ".lok"); // Boot images should be over about 30 MiB. This check is here so the // patcher won't try to read a multi-gigabyte system image into RAM bool isSizeOK = fi.uncompressed_size <= 30 * 1024 * 1024; if (hasBootImage && (inList || isExtImg || isExtLok) && isSizeOK) { // Load the file into memory std::vector<unsigned char> data; if (!FileUtils::mzReadToMemory(zInput, &data, &laProgressCb, this)) { error = PatcherError::createArchiveError( ErrorCode::ArchiveReadDataError, curFile); return false; } // If the file contains the boot image magic string, then // assume it really is a boot image and patch it if (data.size() >= 512) { const char *magic = BootImage::BootMagic; unsigned int size = BootImage::BootMagicSize; auto end = data.begin() + 512; auto it = std::search(data.begin(), end, magic, magic + size); if (it != end) { if (!patchBootImage(&data)) { return false; } // Update total size maxBytes += (data.size() - fi.uncompressed_size); } } auto ret2 = FileUtils::mzAddFile(zOutput, curFile, data); if (!ret2) { error = ret2; return false; } bytes += data.size(); } else { // Directly copy other files to the output zip // Rename the installer for mbtool if (curFile == "META-INF/com/google/android/update-binary") { curFile = "META-INF/com/google/android/update-binary.orig"; } if (!FileUtils::mzCopyFileRaw(zInput, zOutput, curFile, &laProgressCb, this)) { FLOGW("minizip: Failed to copy raw data: {}", curFile); error = PatcherError::createArchiveError( ErrorCode::ArchiveWriteDataError, curFile); return false; } bytes += fi.uncompressed_size; } } while ((ret = unzGoToNextFile(zInput)) == UNZ_OK); if (ret != UNZ_END_OF_LIST_OF_FILE) { error = PatcherError::createArchiveError( ErrorCode::ArchiveReadHeaderError, std::string()); 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(); }
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; }