diff options
Diffstat (limited to 'minzip/Zip.c')
-rw-r--r-- | minzip/Zip.c | 158 |
1 files changed, 105 insertions, 53 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); |