diff options
Diffstat (limited to '')
-rw-r--r-- | minzip/Zip.c | 158 | ||||
-rw-r--r-- | minzip/Zip.h | 13 |
2 files changed, 113 insertions, 58 deletions
diff --git a/minzip/Zip.c b/minzip/Zip.c index b5e446716..12e06f6d8 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -828,7 +828,7 @@ static const char *targetEntryPath(MzPathHelper *helper, ZipEntry *pEntry) */ bool mzExtractRecursive(const ZipArchive *pArchive, const char *zipDir, const char *targetDir, - const struct utimbuf *timestamp, + int flags, const struct utimbuf *timestamp, void (*callback)(const char *fn, void *), void *cookie, struct selabel_handle *sehnd) { @@ -932,19 +932,30 @@ bool mzExtractRecursive(const ZipArchive *pArchive, break; } + /* With DRY_RUN set, invoke the callback but don't do anything else. + */ + if (flags & MZ_EXTRACT_DRY_RUN) { + if (callback != NULL) callback(targetFile, cookie); + continue; + } + + /* Create the file or directory. + */ #define UNZIP_DIRMODE 0755 #define UNZIP_FILEMODE 0644 - /* - * Create the file or directory. We ignore directory entries - * because we recursively create paths to each file entry we encounter - * in the zip archive anyway. - * - * NOTE: A "directory entry" in a zip archive is just a zero length - * entry that ends in a "/". They're not mandatory and many tools get - * rid of them. We need to process them only if we want to preserve - * empty directories from the archive. - */ - if (pEntry->fileName[pEntry->fileNameLen-1] != '/') { + if (pEntry->fileName[pEntry->fileNameLen-1] == '/') { + if (!(flags & MZ_EXTRACT_FILES_ONLY)) { + int ret = dirCreateHierarchy( + targetFile, UNZIP_DIRMODE, timestamp, false, sehnd); + if (ret != 0) { + LOGE("Can't create containing directory for \"%s\": %s\n", + targetFile, strerror(errno)); + ok = false; + break; + } + LOGD("Extracted dir \"%s\"\n", targetFile); + } + } else { /* This is not a directory. First, make sure that * the containing directory exists. */ @@ -957,56 +968,97 @@ bool mzExtractRecursive(const ZipArchive *pArchive, break; } - /* - * The entry is a regular file or a symlink. Open the target for writing. - * - * TODO: This behavior for symlinks seems rather bizarre. For a - * symlink foo/bar/baz -> foo/tar/taz, we will create a file called - * "foo/bar/baz" whose contents are the literal "foo/tar/taz". We - * warn about this for now and preserve older behavior. + /* With FILES_ONLY set, we need to ignore metadata entirely, + * so treat symlinks as regular files. */ - if (mzIsZipEntrySymlink(pEntry)) { - LOGE("Symlink entry \"%.*s\" will be output as a regular file.", - pEntry->fileNameLen, pEntry->fileName); - } + if (!(flags & MZ_EXTRACT_FILES_ONLY) && mzIsZipEntrySymlink(pEntry)) { + /* The entry is a symbolic link. + * The relative target of the symlink is in the + * data section of this entry. + */ + if (pEntry->uncompLen == 0) { + LOGE("Symlink entry \"%s\" has no target\n", + targetFile); + ok = false; + break; + } + char *linkTarget = malloc(pEntry->uncompLen + 1); + if (linkTarget == NULL) { + ok = false; + break; + } + ok = mzReadZipEntry(pArchive, pEntry, linkTarget, + pEntry->uncompLen); + if (!ok) { + LOGE("Can't read symlink target for \"%s\"\n", + targetFile); + free(linkTarget); + break; + } + linkTarget[pEntry->uncompLen] = '\0'; - char *secontext = NULL; + /* Make the link. + */ + ret = symlink(linkTarget, targetFile); + if (ret != 0) { + LOGE("Can't symlink \"%s\" to \"%s\": %s\n", + targetFile, linkTarget, strerror(errno)); + free(linkTarget); + ok = false; + break; + } + LOGD("Extracted symlink \"%s\" -> \"%s\"\n", + targetFile, linkTarget); + free(linkTarget); + } else { + /* The entry is a regular file. + * Open the target for writing. + */ - if (sehnd) { - selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE); - setfscreatecon(secontext); - } + char *secontext = NULL; - int fd = creat(targetFile, UNZIP_FILEMODE); + if (sehnd) { + selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE); + setfscreatecon(secontext); + } - if (secontext) { - freecon(secontext); - setfscreatecon(NULL); - } + int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC|O_SYNC + , UNZIP_FILEMODE); - if (fd < 0) { - LOGE("Can't create target file \"%s\": %s\n", - targetFile, strerror(errno)); - ok = false; - break; - } + if (secontext) { + freecon(secontext); + setfscreatecon(NULL); + } - bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); - close(fd); - if (!ok) { - LOGE("Error extracting \"%s\"\n", targetFile); - ok = false; - break; - } + if (fd < 0) { + LOGE("Can't create target file \"%s\": %s\n", + targetFile, strerror(errno)); + ok = false; + break; + } - if (timestamp != NULL && utime(targetFile, timestamp)) { - LOGE("Error touching \"%s\"\n", targetFile); - ok = false; - break; - } + bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); + if (ok) { + ok = (fsync(fd) == 0); + } + if (close(fd) != 0) { + ok = false; + } + if (!ok) { + LOGE("Error extracting \"%s\"\n", targetFile); + ok = false; + break; + } - LOGV("Extracted file \"%s\"\n", targetFile); - ++extractCount; + if (timestamp != NULL && utime(targetFile, timestamp)) { + LOGE("Error touching \"%s\"\n", targetFile); + ok = false; + break; + } + + LOGV("Extracted file \"%s\"\n", targetFile); + ++extractCount; + } } if (callback != NULL) callback(targetFile, cookie); diff --git a/minzip/Zip.h b/minzip/Zip.h index a2b2c26fc..54eab3222 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -143,12 +143,9 @@ bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, const ZipEntry *pEntry, unsigned char* buffer); /* - * Inflate all files under zipDir to the directory specified by + * Inflate all entries under zipDir to the directory specified by * targetDir, which must exist and be a writable directory. * - * Directory entries and symlinks are not extracted. - * - * * The immediate children of zipDir will become the immediate * children of targetDir; e.g., if the archive contains the entries * @@ -163,15 +160,21 @@ bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, * /tmp/two * /tmp/d/three * + * flags is zero or more of the following: + * + * MZ_EXTRACT_FILES_ONLY - only unpack files, not directories or symlinks + * MZ_EXTRACT_DRY_RUN - don't do anything, but do invoke the callback + * * If timestamp is non-NULL, file timestamps will be set accordingly. * * If callback is non-NULL, it will be invoked with each unpacked file. * * Returns true on success, false on failure. */ +enum { MZ_EXTRACT_FILES_ONLY = 1, MZ_EXTRACT_DRY_RUN = 2 }; bool mzExtractRecursive(const ZipArchive *pArchive, const char *zipDir, const char *targetDir, - const struct utimbuf *timestamp, + int flags, const struct utimbuf *timestamp, void (*callback)(const char *fn, void*), void *cookie, struct selabel_handle *sehnd); |