1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "third_party/zlib/google/zip_internal.h" 6 7 #include <algorithm> 8 9 #include "base/files/file_util.h" 10 #include "base/logging.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "base/time/time.h" 13 14 #if defined(USE_SYSTEM_MINIZIP) 15 #include <minizip/ioapi.h> 16 #include <minizip/unzip.h> 17 #include <minizip/zip.h> 18 #else 19 #include "third_party/zlib/contrib/minizip/unzip.h" 20 #include "third_party/zlib/contrib/minizip/zip.h" 21 #if defined(OS_WIN) 22 #include "third_party/zlib/contrib/minizip/iowin32.h" 23 #elif defined(OS_POSIX) 24 #include "third_party/zlib/contrib/minizip/ioapi.h" 25 #endif // defined(OS_POSIX) 26 #endif // defined(USE_SYSTEM_MINIZIP) 27 28 namespace { 29 30 #if defined(OS_WIN) 31 typedef struct { 32 HANDLE hf; 33 int error; 34 } WIN32FILE_IOWIN; 35 36 // This function is derived from third_party/minizip/iowin32.c. 37 // Its only difference is that it treats the char* as UTF8 and 38 // uses the Unicode version of CreateFile. 39 void* ZipOpenFunc(void *opaque, const char* filename, int mode) { 40 DWORD desired_access = 0, creation_disposition = 0; 41 DWORD share_mode = 0, flags_and_attributes = 0; 42 HANDLE file = 0; 43 void* ret = NULL; 44 45 if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) { 46 desired_access = GENERIC_READ; 47 creation_disposition = OPEN_EXISTING; 48 share_mode = FILE_SHARE_READ; 49 } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) { 50 desired_access = GENERIC_WRITE | GENERIC_READ; 51 creation_disposition = OPEN_EXISTING; 52 } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) { 53 desired_access = GENERIC_WRITE | GENERIC_READ; 54 creation_disposition = CREATE_ALWAYS; 55 } 56 57 base::string16 filename16 = base::UTF8ToUTF16(filename); 58 if ((filename != NULL) && (desired_access != 0)) { 59 file = CreateFile(filename16.c_str(), desired_access, share_mode, 60 NULL, creation_disposition, flags_and_attributes, NULL); 61 } 62 63 if (file == INVALID_HANDLE_VALUE) 64 file = NULL; 65 66 if (file != NULL) { 67 WIN32FILE_IOWIN file_ret; 68 file_ret.hf = file; 69 file_ret.error = 0; 70 ret = malloc(sizeof(WIN32FILE_IOWIN)); 71 if (ret == NULL) 72 CloseHandle(file); 73 else 74 *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret; 75 } 76 return ret; 77 } 78 #endif 79 80 #if defined(OS_POSIX) 81 // Callback function for zlib that opens a file stream from a file descriptor. 82 void* FdOpenFileFunc(void* opaque, const char* filename, int mode) { 83 FILE* file = NULL; 84 const char* mode_fopen = NULL; 85 86 if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) 87 mode_fopen = "rb"; 88 else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) 89 mode_fopen = "r+b"; 90 else if (mode & ZLIB_FILEFUNC_MODE_CREATE) 91 mode_fopen = "wb"; 92 93 if ((filename != NULL) && (mode_fopen != NULL)) 94 file = fdopen(*static_cast<int*>(opaque), mode_fopen); 95 96 return file; 97 } 98 99 // We don't actually close the file stream since that would close 100 // the underlying file descriptor, and we don't own it. However we do need to 101 // flush buffers and free |opaque| since we malloc'ed it in FillFdOpenFileFunc. 102 int CloseFileFunc(void* opaque, void* stream) { 103 fflush(static_cast<FILE*>(stream)); 104 free(opaque); 105 return 0; 106 } 107 108 // Fills |pzlib_filecunc_def| appropriately to handle the zip file 109 // referred to by |fd|. 110 void FillFdOpenFileFunc(zlib_filefunc_def* pzlib_filefunc_def, int fd) { 111 fill_fopen_filefunc(pzlib_filefunc_def); 112 pzlib_filefunc_def->zopen_file = FdOpenFileFunc; 113 pzlib_filefunc_def->zclose_file = CloseFileFunc; 114 int* ptr_fd = static_cast<int*>(malloc(sizeof(fd))); 115 *ptr_fd = fd; 116 pzlib_filefunc_def->opaque = ptr_fd; 117 } 118 #endif // defined(OS_POSIX) 119 120 #if defined(OS_WIN) 121 // Callback function for zlib that opens a file stream from a Windows handle. 122 void* HandleOpenFileFunc(void* opaque, const char* filename, int mode) { 123 WIN32FILE_IOWIN file_ret; 124 file_ret.hf = static_cast<HANDLE>(opaque); 125 file_ret.error = 0; 126 if (file_ret.hf == INVALID_HANDLE_VALUE) 127 return NULL; 128 129 void* ret = malloc(sizeof(WIN32FILE_IOWIN)); 130 if (ret != NULL) 131 *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret; 132 return ret; 133 } 134 #endif 135 136 // A struct that contains data required for zlib functions to extract files from 137 // a zip archive stored in memory directly. The following I/O API functions 138 // expect their opaque parameters refer to this struct. 139 struct ZipBuffer { 140 const char* data; // weak 141 size_t length; 142 size_t offset; 143 }; 144 145 // Opens the specified file. When this function returns a non-NULL pointer, zlib 146 // uses this pointer as a stream parameter while compressing or uncompressing 147 // data. (Returning NULL represents an error.) This function initializes the 148 // given opaque parameter and returns it because this parameter stores all 149 // information needed for uncompressing data. (This function does not support 150 // writing compressed data and it returns NULL for this case.) 151 void* OpenZipBuffer(void* opaque, const char* /*filename*/, int mode) { 152 if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) { 153 NOTREACHED(); 154 return NULL; 155 } 156 ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque); 157 if (!buffer || !buffer->data || !buffer->length) 158 return NULL; 159 buffer->offset = 0; 160 return opaque; 161 } 162 163 // Reads compressed data from the specified stream. This function copies data 164 // refered by the opaque parameter and returns the size actually copied. 165 uLong ReadZipBuffer(void* opaque, void* /*stream*/, void* buf, uLong size) { 166 ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque); 167 DCHECK_LE(buffer->offset, buffer->length); 168 size_t remaining_bytes = buffer->length - buffer->offset; 169 if (!buffer || !buffer->data || !remaining_bytes) 170 return 0; 171 size = std::min(size, static_cast<uLong>(remaining_bytes)); 172 memcpy(buf, &buffer->data[buffer->offset], size); 173 buffer->offset += size; 174 return size; 175 } 176 177 // Writes compressed data to the stream. This function always returns zero 178 // because this implementation is only for reading compressed data. 179 uLong WriteZipBuffer(void* /*opaque*/, 180 void* /*stream*/, 181 const void* /*buf*/, 182 uLong /*size*/) { 183 NOTREACHED(); 184 return 0; 185 } 186 187 // Returns the offset from the beginning of the data. 188 long GetOffsetOfZipBuffer(void* opaque, void* /*stream*/) { 189 ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque); 190 if (!buffer) 191 return -1; 192 return static_cast<long>(buffer->offset); 193 } 194 195 // Moves the current offset to the specified position. 196 long SeekZipBuffer(void* opaque, void* /*stream*/, uLong offset, int origin) { 197 ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque); 198 if (!buffer) 199 return -1; 200 if (origin == ZLIB_FILEFUNC_SEEK_CUR) { 201 buffer->offset = std::min(buffer->offset + static_cast<size_t>(offset), 202 buffer->length); 203 return 0; 204 } 205 if (origin == ZLIB_FILEFUNC_SEEK_END) { 206 buffer->offset = (buffer->length > offset) ? buffer->length - offset : 0; 207 return 0; 208 } 209 if (origin == ZLIB_FILEFUNC_SEEK_SET) { 210 buffer->offset = std::min(buffer->length, static_cast<size_t>(offset)); 211 return 0; 212 } 213 NOTREACHED(); 214 return -1; 215 } 216 217 // Closes the input offset and deletes all resources used for compressing or 218 // uncompressing data. This function deletes the ZipBuffer object referred by 219 // the opaque parameter since zlib deletes the unzFile object and it does not 220 // use this object any longer. 221 int CloseZipBuffer(void* opaque, void* /*stream*/) { 222 if (opaque) 223 free(opaque); 224 return 0; 225 } 226 227 // Returns the last error happened when reading or writing data. This function 228 // always returns zero, which means there are not any errors. 229 int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) { 230 return 0; 231 } 232 233 // Returns a zip_fileinfo struct with the time represented by |file_time|. 234 zip_fileinfo TimeToZipFileInfo(const base::Time& file_time) { 235 base::Time::Exploded file_time_parts; 236 file_time.LocalExplode(&file_time_parts); 237 238 zip_fileinfo zip_info = {}; 239 if (file_time_parts.year >= 1980) { 240 // This if check works around the handling of the year value in 241 // contrib/minizip/zip.c in function zip64local_TmzDateToDosDate 242 // It assumes that dates below 1980 are in the double digit format. 243 // Hence the fail safe option is to leave the date unset. Some programs 244 // might show the unset date as 1980-0-0 which is invalid. 245 zip_info.tmz_date.tm_year = file_time_parts.year; 246 zip_info.tmz_date.tm_mon = file_time_parts.month - 1; 247 zip_info.tmz_date.tm_mday = file_time_parts.day_of_month; 248 zip_info.tmz_date.tm_hour = file_time_parts.hour; 249 zip_info.tmz_date.tm_min = file_time_parts.minute; 250 zip_info.tmz_date.tm_sec = file_time_parts.second; 251 } 252 253 return zip_info; 254 } 255 } // namespace 256 257 namespace zip { 258 namespace internal { 259 260 unzFile OpenForUnzipping(const std::string& file_name_utf8) { 261 zlib_filefunc_def* zip_func_ptrs = NULL; 262 #if defined(OS_WIN) 263 zlib_filefunc_def zip_funcs; 264 fill_win32_filefunc(&zip_funcs); 265 zip_funcs.zopen_file = ZipOpenFunc; 266 zip_func_ptrs = &zip_funcs; 267 #endif 268 return unzOpen2(file_name_utf8.c_str(), zip_func_ptrs); 269 } 270 271 #if defined(OS_POSIX) 272 unzFile OpenFdForUnzipping(int zip_fd) { 273 zlib_filefunc_def zip_funcs; 274 FillFdOpenFileFunc(&zip_funcs, zip_fd); 275 // Passing dummy "fd" filename to zlib. 276 return unzOpen2("fd", &zip_funcs); 277 } 278 #endif 279 280 #if defined(OS_WIN) 281 unzFile OpenHandleForUnzipping(HANDLE zip_handle) { 282 zlib_filefunc_def zip_funcs; 283 fill_win32_filefunc(&zip_funcs); 284 zip_funcs.zopen_file = HandleOpenFileFunc; 285 zip_funcs.opaque = zip_handle; 286 return unzOpen2("fd", &zip_funcs); 287 } 288 #endif 289 290 // static 291 unzFile PrepareMemoryForUnzipping(const std::string& data) { 292 if (data.empty()) 293 return NULL; 294 295 ZipBuffer* buffer = static_cast<ZipBuffer*>(malloc(sizeof(ZipBuffer))); 296 if (!buffer) 297 return NULL; 298 buffer->data = data.data(); 299 buffer->length = data.length(); 300 buffer->offset = 0; 301 302 zlib_filefunc_def zip_functions; 303 zip_functions.zopen_file = OpenZipBuffer; 304 zip_functions.zread_file = ReadZipBuffer; 305 zip_functions.zwrite_file = WriteZipBuffer; 306 zip_functions.ztell_file = GetOffsetOfZipBuffer; 307 zip_functions.zseek_file = SeekZipBuffer; 308 zip_functions.zclose_file = CloseZipBuffer; 309 zip_functions.zerror_file = GetErrorOfZipBuffer; 310 zip_functions.opaque = static_cast<void*>(buffer); 311 return unzOpen2(NULL, &zip_functions); 312 } 313 314 zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) { 315 zlib_filefunc_def* zip_func_ptrs = NULL; 316 #if defined(OS_WIN) 317 zlib_filefunc_def zip_funcs; 318 fill_win32_filefunc(&zip_funcs); 319 zip_funcs.zopen_file = ZipOpenFunc; 320 zip_func_ptrs = &zip_funcs; 321 #endif 322 return zipOpen2(file_name_utf8.c_str(), 323 append_flag, 324 NULL, // global comment 325 zip_func_ptrs); 326 } 327 328 #if defined(OS_POSIX) 329 zipFile OpenFdForZipping(int zip_fd, int append_flag) { 330 zlib_filefunc_def zip_funcs; 331 FillFdOpenFileFunc(&zip_funcs, zip_fd); 332 // Passing dummy "fd" filename to zlib. 333 return zipOpen2("fd", append_flag, NULL, &zip_funcs); 334 } 335 #endif 336 337 zip_fileinfo GetFileInfoForZipping(const base::FilePath& path) { 338 base::Time file_time; 339 base::File::Info file_info; 340 if (base::GetFileInfo(path, &file_info)) 341 file_time = file_info.last_modified; 342 return TimeToZipFileInfo(file_time); 343 } 344 345 bool ZipOpenNewFileInZip(zipFile zip_file, 346 const std::string& str_path, 347 const zip_fileinfo* file_info) { 348 // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT 349 // Setting the Language encoding flag so the file is told to be in utf-8. 350 const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11; 351 352 if (ZIP_OK != zipOpenNewFileInZip4( 353 zip_file, // file 354 str_path.c_str(), // filename 355 file_info, // zipfi 356 NULL, // extrafield_local, 357 0u, // size_extrafield_local 358 NULL, // extrafield_global 359 0u, // size_extrafield_global 360 NULL, // comment 361 Z_DEFLATED, // method 362 Z_DEFAULT_COMPRESSION, // level 363 0, // raw 364 -MAX_WBITS, // windowBits 365 DEF_MEM_LEVEL, // memLevel 366 Z_DEFAULT_STRATEGY, // strategy 367 NULL, // password 368 0, // crcForCrypting 369 0, // versionMadeBy 370 LANGUAGE_ENCODING_FLAG)) { // flagBase 371 DLOG(ERROR) << "Could not open zip file entry " << str_path; 372 return false; 373 } 374 return true; 375 } 376 377 } // namespace internal 378 } // namespace zip 379