summaryrefslogtreecommitdiffstats
path: root/minzip
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--minzip/Zip.c158
-rw-r--r--minzip/Zip.h13
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);