// Copyright 2011 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "third_party/zlib/google/zip_internal.h" #include #include #include #include #include "base/files/file_path.h" #include "base/logging.h" #include "base/no_destructor.h" #include "base/notreached.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #if defined(USE_SYSTEM_MINIZIP) #include #include #include #else #include "third_party/zlib/contrib/minizip/unzip.h" #include "third_party/zlib/contrib/minizip/zip.h" #if defined(OS_WIN) #include "third_party/zlib/contrib/minizip/iowin32.h" #elif defined(OS_POSIX) #include "third_party/zlib/contrib/minizip/ioapi.h" #endif // defined(OS_POSIX) #endif // defined(USE_SYSTEM_MINIZIP) namespace { #if defined(OS_WIN) typedef struct { HANDLE hf; int error; } WIN32FILE_IOWIN; // This function is derived from third_party/minizip/iowin32.c. // Its only difference is that it treats the filename as UTF-8 and // uses the Unicode version of CreateFile. void* ZipOpenFunc(void* opaque, const void* filename, int mode) { DWORD desired_access = 0, creation_disposition = 0; DWORD share_mode = 0, flags_and_attributes = 0; HANDLE file = 0; void* ret = NULL; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) { desired_access = GENERIC_READ; creation_disposition = OPEN_EXISTING; share_mode = FILE_SHARE_READ; } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) { desired_access = GENERIC_WRITE | GENERIC_READ; creation_disposition = OPEN_EXISTING; } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) { desired_access = GENERIC_WRITE | GENERIC_READ; creation_disposition = CREATE_ALWAYS; } if (filename != nullptr && desired_access != 0) { file = CreateFileW( base::UTF8ToWide(static_cast(filename)).c_str(), desired_access, share_mode, nullptr, creation_disposition, flags_and_attributes, nullptr); } if (file == INVALID_HANDLE_VALUE) file = NULL; if (file != NULL) { WIN32FILE_IOWIN file_ret; file_ret.hf = file; file_ret.error = 0; ret = malloc(sizeof(WIN32FILE_IOWIN)); if (ret == NULL) CloseHandle(file); else *(static_cast(ret)) = file_ret; } return ret; } #endif #if defined(OS_POSIX) || defined(OS_FUCHSIA) // Callback function for zlib that opens a file stream from a file descriptor. // Since we do not own the file descriptor, dup it so that we can fdopen/fclose // a file stream. void* FdOpenFileFunc(void* opaque, const void* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) mode_fopen = "r+b"; else if (mode & ZLIB_FILEFUNC_MODE_CREATE) mode_fopen = "wb"; if ((filename != NULL) && (mode_fopen != NULL)) { int fd = dup(*static_cast(opaque)); if (fd != -1) file = fdopen(fd, mode_fopen); } return file; } int FdCloseFileFunc(void* opaque, void* stream) { fclose(static_cast(stream)); free(opaque); // malloc'ed in FillFdOpenFileFunc() return 0; } // Fills |pzlib_filecunc_def| appropriately to handle the zip file // referred to by |fd|. void FillFdOpenFileFunc(zlib_filefunc64_def* pzlib_filefunc_def, int fd) { fill_fopen64_filefunc(pzlib_filefunc_def); pzlib_filefunc_def->zopen64_file = FdOpenFileFunc; pzlib_filefunc_def->zclose_file = FdCloseFileFunc; int* ptr_fd = static_cast(malloc(sizeof(fd))); *ptr_fd = fd; pzlib_filefunc_def->opaque = ptr_fd; } #endif // defined(OS_POSIX) #if defined(OS_WIN) // Callback function for zlib that opens a file stream from a Windows handle. // Does not take ownership of the handle. void* HandleOpenFileFunc(void* opaque, const void* /*filename*/, int mode) { WIN32FILE_IOWIN file_ret; file_ret.hf = static_cast(opaque); file_ret.error = 0; if (file_ret.hf == INVALID_HANDLE_VALUE) return NULL; void* ret = malloc(sizeof(WIN32FILE_IOWIN)); if (ret != NULL) *(static_cast(ret)) = file_ret; return ret; } int HandleCloseFileFunc(void* opaque, void* stream) { free(stream); // malloc'ed in HandleOpenFileFunc() return 0; } #endif // A struct that contains data required for zlib functions to extract files from // a zip archive stored in memory directly. The following I/O API functions // expect their opaque parameters refer to this struct. struct ZipBuffer { const char* data; // weak ZPOS64_T length; ZPOS64_T offset; }; // Opens the specified file. When this function returns a non-NULL pointer, zlib // uses this pointer as a stream parameter while compressing or uncompressing // data. (Returning NULL represents an error.) This function initializes the // given opaque parameter and returns it because this parameter stores all // information needed for uncompressing data. (This function does not support // writing compressed data and it returns NULL for this case.) void* OpenZipBuffer(void* opaque, const void* /*filename*/, int mode) { if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) { NOTREACHED(); return NULL; } ZipBuffer* buffer = static_cast(opaque); if (!buffer || !buffer->data || !buffer->length) return NULL; buffer->offset = 0; return opaque; } // Reads compressed data from the specified stream. This function copies data // refered by the opaque parameter and returns the size actually copied. uLong ReadZipBuffer(void* opaque, void* /*stream*/, void* buf, uLong size) { ZipBuffer* buffer = static_cast(opaque); DCHECK_LE(buffer->offset, buffer->length); ZPOS64_T remaining_bytes = buffer->length - buffer->offset; if (!buffer || !buffer->data || !remaining_bytes) return 0; if (size > remaining_bytes) size = remaining_bytes; memcpy(buf, &buffer->data[buffer->offset], size); buffer->offset += size; return size; } // Writes compressed data to the stream. This function always returns zero // because this implementation is only for reading compressed data. uLong WriteZipBuffer(void* /*opaque*/, void* /*stream*/, const void* /*buf*/, uLong /*size*/) { NOTREACHED(); return 0; } // Returns the offset from the beginning of the data. ZPOS64_T GetOffsetOfZipBuffer(void* opaque, void* /*stream*/) { ZipBuffer* buffer = static_cast(opaque); if (!buffer) return -1; return buffer->offset; } // Moves the current offset to the specified position. long SeekZipBuffer(void* opaque, void* /*stream*/, ZPOS64_T offset, int origin) { ZipBuffer* buffer = static_cast(opaque); if (!buffer) return -1; if (origin == ZLIB_FILEFUNC_SEEK_CUR) { buffer->offset = std::min(buffer->offset + offset, buffer->length); return 0; } if (origin == ZLIB_FILEFUNC_SEEK_END) { buffer->offset = (buffer->length > offset) ? buffer->length - offset : 0; return 0; } if (origin == ZLIB_FILEFUNC_SEEK_SET) { buffer->offset = std::min(buffer->length, offset); return 0; } NOTREACHED(); return -1; } // Closes the input offset and deletes all resources used for compressing or // uncompressing data. This function deletes the ZipBuffer object referred by // the opaque parameter since zlib deletes the unzFile object and it does not // use this object any longer. int CloseZipBuffer(void* opaque, void* /*stream*/) { if (opaque) free(opaque); return 0; } // Returns the last error happened when reading or writing data. This function // always returns zero, which means there are not any errors. int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) { return 0; } // Returns a zip_fileinfo struct with the time represented by |file_time|. zip_fileinfo TimeToZipFileInfo(const base::Time& file_time) { base::Time::Exploded file_time_parts; file_time.UTCExplode(&file_time_parts); zip_fileinfo zip_info = {}; if (file_time_parts.year >= 1980) { // This if check works around the handling of the year value in // contrib/minizip/zip.c in function zip64local_TmzDateToDosDate // It assumes that dates below 1980 are in the double digit format. // Hence the fail safe option is to leave the date unset. Some programs // might show the unset date as 1980-0-0 which is invalid. zip_info.tmz_date.tm_year = file_time_parts.year; zip_info.tmz_date.tm_mon = file_time_parts.month - 1; zip_info.tmz_date.tm_mday = file_time_parts.day_of_month; zip_info.tmz_date.tm_hour = file_time_parts.hour; zip_info.tmz_date.tm_min = file_time_parts.minute; zip_info.tmz_date.tm_sec = file_time_parts.second; } return zip_info; } } // namespace namespace zip { namespace internal { unzFile OpenForUnzipping(const std::string& file_name_utf8) { zlib_filefunc64_def* zip_func_ptrs = nullptr; #if defined(OS_WIN) zlib_filefunc64_def zip_funcs; fill_win32_filefunc64(&zip_funcs); zip_funcs.zopen64_file = ZipOpenFunc; zip_func_ptrs = &zip_funcs; #endif return unzOpen2_64(file_name_utf8.c_str(), zip_func_ptrs); } #if defined(OS_POSIX) || defined(OS_FUCHSIA) unzFile OpenFdForUnzipping(int zip_fd) { zlib_filefunc64_def zip_funcs; FillFdOpenFileFunc(&zip_funcs, zip_fd); // Passing dummy "fd" filename to zlib. return unzOpen2_64("fd", &zip_funcs); } #endif #if defined(OS_WIN) unzFile OpenHandleForUnzipping(HANDLE zip_handle) { zlib_filefunc64_def zip_funcs; fill_win32_filefunc64(&zip_funcs); zip_funcs.zopen64_file = HandleOpenFileFunc; zip_funcs.zclose_file = HandleCloseFileFunc; zip_funcs.opaque = zip_handle; return unzOpen2_64("fd", &zip_funcs); } #endif // static unzFile PrepareMemoryForUnzipping(const std::string& data) { if (data.empty()) return NULL; ZipBuffer* buffer = static_cast(malloc(sizeof(ZipBuffer))); if (!buffer) return NULL; buffer->data = data.data(); buffer->length = data.length(); buffer->offset = 0; zlib_filefunc64_def zip_functions; zip_functions.zopen64_file = OpenZipBuffer; zip_functions.zread_file = ReadZipBuffer; zip_functions.zwrite_file = WriteZipBuffer; zip_functions.ztell64_file = GetOffsetOfZipBuffer; zip_functions.zseek64_file = SeekZipBuffer; zip_functions.zclose_file = CloseZipBuffer; zip_functions.zerror_file = GetErrorOfZipBuffer; zip_functions.opaque = buffer; return unzOpen2_64(nullptr, &zip_functions); } zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) { zlib_filefunc64_def* zip_func_ptrs = nullptr; #if defined(OS_WIN) zlib_filefunc64_def zip_funcs; fill_win32_filefunc64(&zip_funcs); zip_funcs.zopen64_file = ZipOpenFunc; zip_func_ptrs = &zip_funcs; #endif return zipOpen2_64(file_name_utf8.c_str(), append_flag, nullptr, zip_func_ptrs); } #if defined(OS_POSIX) || defined(OS_FUCHSIA) zipFile OpenFdForZipping(int zip_fd, int append_flag) { zlib_filefunc64_def zip_funcs; FillFdOpenFileFunc(&zip_funcs, zip_fd); // Passing dummy "fd" filename to zlib. return zipOpen2_64("fd", append_flag, nullptr, &zip_funcs); } #endif bool ZipOpenNewFileInZip(zipFile zip_file, const std::string& str_path, base::Time last_modified_time, Compression compression) { // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT // Setting the Language encoding flag so the file is told to be in utf-8. const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11; const zip_fileinfo file_info = TimeToZipFileInfo(last_modified_time); const int err = zipOpenNewFileInZip4_64( /*file=*/zip_file, /*filename=*/str_path.c_str(), /*zip_fileinfo=*/&file_info, /*extrafield_local=*/nullptr, /*size_extrafield_local=*/0u, /*extrafield_global=*/nullptr, /*size_extrafield_global=*/0u, /*comment=*/nullptr, /*method=*/compression, /*level=*/Z_DEFAULT_COMPRESSION, /*raw=*/0, /*windowBits=*/-MAX_WBITS, /*memLevel=*/DEF_MEM_LEVEL, /*strategy=*/Z_DEFAULT_STRATEGY, /*password=*/nullptr, /*crcForCrypting=*/0, /*versionMadeBy=*/0, /*flagBase=*/LANGUAGE_ENCODING_FLAG, /*zip64=*/1); if (err != ZIP_OK) { DLOG(ERROR) << "Cannot open ZIP file entry '" << str_path << "': zipOpenNewFileInZip4_64 returned " << err; return false; } return true; } Compression GetCompressionMethod(const base::FilePath& path) { // Get the filename extension in lower case. const base::FilePath::StringType ext = base::ToLowerASCII(path.FinalExtension()); if (ext.empty()) return kDeflated; using StringPiece = base::FilePath::StringPieceType; // Skip the leading dot. StringPiece ext_without_dot = ext; DCHECK_EQ(ext_without_dot.front(), FILE_PATH_LITERAL('.')); ext_without_dot.remove_prefix(1); // Well known filename extensions of files that a likely to be already // compressed. The extensions are in lower case without the leading dot. static const base::NoDestructor< std::unordered_set>> exts(std::initializer_list{ FILE_PATH_LITERAL("3g2"), // FILE_PATH_LITERAL("3gp"), // FILE_PATH_LITERAL("7z"), // FILE_PATH_LITERAL("7zip"), // FILE_PATH_LITERAL("aac"), // FILE_PATH_LITERAL("avi"), // FILE_PATH_LITERAL("bz"), // FILE_PATH_LITERAL("bz2"), // FILE_PATH_LITERAL("crx"), // FILE_PATH_LITERAL("gif"), // FILE_PATH_LITERAL("gz"), // FILE_PATH_LITERAL("jar"), // FILE_PATH_LITERAL("jpeg"), // FILE_PATH_LITERAL("jpg"), // FILE_PATH_LITERAL("lz"), // FILE_PATH_LITERAL("m2v"), // FILE_PATH_LITERAL("m4p"), // FILE_PATH_LITERAL("m4v"), // FILE_PATH_LITERAL("mng"), // FILE_PATH_LITERAL("mov"), // FILE_PATH_LITERAL("mp2"), // FILE_PATH_LITERAL("mp3"), // FILE_PATH_LITERAL("mp4"), // FILE_PATH_LITERAL("mpe"), // FILE_PATH_LITERAL("mpeg"), // FILE_PATH_LITERAL("mpg"), // FILE_PATH_LITERAL("mpv"), // FILE_PATH_LITERAL("ogg"), // FILE_PATH_LITERAL("ogv"), // FILE_PATH_LITERAL("png"), // FILE_PATH_LITERAL("qt"), // FILE_PATH_LITERAL("rar"), // FILE_PATH_LITERAL("taz"), // FILE_PATH_LITERAL("tb2"), // FILE_PATH_LITERAL("tbz"), // FILE_PATH_LITERAL("tbz2"), // FILE_PATH_LITERAL("tgz"), // FILE_PATH_LITERAL("tlz"), // FILE_PATH_LITERAL("tz"), // FILE_PATH_LITERAL("tz2"), // FILE_PATH_LITERAL("vob"), // FILE_PATH_LITERAL("webm"), // FILE_PATH_LITERAL("wma"), // FILE_PATH_LITERAL("wmv"), // FILE_PATH_LITERAL("xz"), // FILE_PATH_LITERAL("z"), // FILE_PATH_LITERAL("zip"), // }); if (exts->count(ext_without_dot)) return kStored; return kDeflated; } } // namespace internal } // namespace zip