Use .part suffix for temp file names. Untested for BTRFS cloning.
diff --git a/developer-xcode/libzip.xcodeproj/project.pbxproj b/developer-xcode/libzip.xcodeproj/project.pbxproj index acd593a..08c5e07 100644 --- a/developer-xcode/libzip.xcodeproj/project.pbxproj +++ b/developer-xcode/libzip.xcodeproj/project.pbxproj
@@ -171,7 +171,6 @@ 4B3A5F531DF96EB4005A53A1 /* zip_ftell.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B3A5F4E1DF96D83005A53A1 /* zip_ftell.c */; }; 4B3FAE802385C5CC00192D6A /* zip_algorithm_xz.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FAE7F2385C5A300192D6A /* zip_algorithm_xz.c */; }; 4B3FAE822385C79200192D6A /* liblzma.5.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B3FAE812385C79200192D6A /* liblzma.5.dylib */; }; - 4B5169A822A7993E00AA4340 /* zip_mkstempm.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B5169A722A7993D00AA4340 /* zip_mkstempm.c */; }; 4B51DDBA1FDAE20A00C5CA85 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B01D70815B2F4CF002D5007 /* libz.dylib */; }; 4B51DDBB1FDAE20A00C5CA85 /* libzip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B01D68B15B2F3F1002D5007 /* libzip.framework */; }; 4B51DDC11FDAE25B00C5CA85 /* ziptool.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BACD57C15BC2AEF00920691 /* ziptool.c */; }; @@ -2015,7 +2014,6 @@ 4BCF302A199A2F820064207B /* zip_source_seek.c in Sources */, 4B01D6C515B2F46B002D5007 /* zip_fopen_index_encrypted.c in Sources */, 4B01D6C615B2F46B002D5007 /* zip_fopen_index.c in Sources */, - 4B5169A822A7993E00AA4340 /* zip_mkstempm.c in Sources */, 4B69E6EE2032F18B0001EEE7 /* zip_winzip_aes.c in Sources */, 4B01D6C715B2F46B002D5007 /* zip_fopen.c in Sources */, 3D9284821C309510001EABA7 /* zip_hash.c in Sources */,
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 5ec2451..1213fa0 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt
@@ -130,7 +130,6 @@ endif() else(WIN32) target_sources(zip PRIVATE - zip_mkstempm.c zip_source_file_stdio_named.c zip_random_unix.c )
diff --git a/lib/zip_mkstempm.c b/lib/zip_mkstempm.c index ede5142..4efcf79 100644 --- a/lib/zip_mkstempm.c +++ b/lib/zip_mkstempm.c
@@ -43,24 +43,28 @@ * or default permissions if there is no previous file */ int -_zip_mkstempm(char *path, int mode) { +_zip_mkstempm(char *path, int mode, bool create_file) { int fd; char *start, *end, *xs; int xcnt = 0; - end = path + strlen(path); - start = end - 1; + end = path + strlen(path) - 1; + while (end >= path) { + if (*end == 'X') { + break; + } + end--; + } + if (end < path) { + errno = EINVAL; + return -1; + } + start = end; while (start >= path && *start == 'X') { xcnt++; start--; } - - if (xcnt == 0) { - errno = EINVAL; - return -1; - } - start++; for (;;) { @@ -68,7 +72,7 @@ xs = start; - while (xs < end) { + while (xs <= end) { char digit = value % 36; if (digit < 10) { *(xs++) = digit + '0'; @@ -79,19 +83,33 @@ value /= 36; } - if ((fd = open(path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, mode == -1 ? 0666 : (mode_t)mode)) >= 0) { - if (mode != -1) { - /* open() honors umask(), which we don't want in this case */ + if (create_file) { + if ((fd = open(path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, mode == -1 ? 0666 : (mode_t)mode)) >= 0) { + if (mode != -1) { + /* open() honors umask(), which we don't want in this case */ #ifdef HAVE_FCHMOD - (void)fchmod(fd, (mode_t)mode); + (void)fchmod(fd, (mode_t)mode); #else - (void)chmod(path, (mode_t)mode); + (void)chmod(path, (mode_t)mode); + } #endif + return fd; } - return fd; + if (errno != EEXIST) { + return -1; + } } - if (errno != EEXIST) { - return -1; + else { + struct stat st; + + if (stat(path, &st) < 0) { + if (errno == ENOENT) { + return 0; + } + else { + return -1; + } + } } } }
diff --git a/lib/zip_source_file_stdio_named.c b/lib/zip_source_file_stdio_named.c index 8027e73..974bf52 100644 --- a/lib/zip_source_file_stdio_named.c +++ b/lib/zip_source_file_stdio_named.c
@@ -36,6 +36,7 @@ #include "zip_source_file.h" #include "zip_source_file_stdio.h" +#include <fcntl.h> #include <stdlib.h> #include <sys/stat.h> #ifdef HAVE_UNISTD_H @@ -53,6 +54,8 @@ #define CAN_CLONE #endif +static int create_temp_file(zip_source_file_context_t *ctx, bool create_file); + static zip_int64_t _zip_stdio_op_commit_write(zip_source_file_context_t *ctx); static zip_int64_t _zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx); #ifdef CAN_CLONE @@ -123,82 +126,51 @@ static zip_int64_t _zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx) { - char *temp; - int tfd; - int mode; - FILE *tfp; - struct stat st; - - if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + int fd = create_temp_file(ctx, true); + + if (fd < 0) { return -1; } - - if (stat(ctx->fname, &st) == 0) { - mode = st.st_mode; - } - else { - mode = -1; - } - - sprintf(temp, "%s.XXXXXX", ctx->fname); - - if ((tfd = _zip_mkstempm(temp, mode)) == -1) { + + if ((ctx->fout = fdopen(fd, "r+b")) == NULL) { zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - free(temp); + close(fd); + (void)remove(ctx->tmpname); + free(ctx->tmpname); + ctx->tmpname = NULL; return -1; } - if ((tfp = fdopen(tfd, "r+b")) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - close(tfd); - (void)remove(temp); - free(temp); - return -1; - } - - ctx->fout = tfp; - ctx->tmpname = temp; - return 0; } #ifdef CAN_CLONE static zip_int64_t _zip_stdio_op_create_temp_output_cloning(zip_source_file_context_t *ctx, zip_uint64_t offset) { - char *temp; FILE *tfp; - + if (offset > ZIP_OFF_MAX) { zip_error_set(&ctx->error, ZIP_ER_SEEK, E2BIG); return -1; } - - if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); - return -1; - } - sprintf(temp, "%s.XXXXXX", ctx->fname); - + #ifdef HAVE_CLONEFILE -#ifndef __clang_analyzer__ - /* we can't use mkstemp, since clonefile insists on creating the file */ - if (mktemp(temp) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - free(temp); + /* clonefile insists on creating the file, so just create a name */ + if (create_temp_file(ctx, false) < 0) { return -1; } -#endif - - if (clonefile(ctx->fname, temp, 0) < 0) { + + if (clonefile(ctx->fname, ctx->tmpname, 0) < 0) { zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - free(temp); + free(ctx->tmpname); + ctx->tmpname = NULL; return -1; } - if ((tfp = _zip_fopen_close_on_exec(temp, true)) == NULL) { + if ((tfp = _zip_fopen_close_on_exec(ctx->tmpname, true)) == NULL) { zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - (void)remove(temp); - free(temp); + (void)remove(ctx->tmpname); + free(ctx->tmpname); + ctx->tmpname = NULL; return -1; } #else @@ -206,19 +178,16 @@ int fd; struct file_clone_range range; struct stat st; - + if (fstat(fileno(ctx->f), &st) < 0) { zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - free(temp); return -1; } - - if ((fd = mkstemp(temp)) < 0) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - free(temp); + + if ((fd = create_temp_file(ctx, true)) < 0) { return -1; } - + range.src_fd = fileno(ctx->f); range.src_offset = 0; range.src_length = ((offset + st.st_blksize - 1) / st.st_blksize) * st.st_blksize; @@ -229,16 +198,18 @@ if (ioctl(fd, FICLONERANGE, &range) < 0) { zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); (void)close(fd); - (void)remove(temp); - free(temp); + (void)remove(ctx->tmpname); + free(ctx->tmpname); + ctx->tmpname = NULL; return -1; } if ((tfp = fdopen(fd, "r+b")) == NULL) { zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); (void)close(fd); - (void)remove(temp); - free(temp); + (void)remove(ctx->tmpname); + free(ctx->tmpname); + ctx->tmpname = NULL; return -1; } } @@ -246,20 +217,21 @@ if (ftruncate(fileno(tfp), (off_t)offset) < 0) { (void)fclose(tfp); - (void)remove(temp); - free(temp); + (void)remove(ctx->tmpname); + free(ctx->tmpname); + ctx->tmpname = NULL; return -1; } if (fseeko(tfp, (off_t)offset, SEEK_SET) < 0) { - (void)fclose(tfp); - (void)remove(temp); - free(temp); zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + (void)fclose(tfp); + (void)remove(ctx->tmpname); + free(ctx->tmpname); + ctx->tmpname = NULL; return -1; } ctx->fout = tfp; - ctx->tmpname = temp; return 0; } @@ -312,3 +284,78 @@ return (zip_int64_t)ret; } + + +static int create_temp_file(zip_source_file_context_t *ctx, bool create_file) { + char *temp; + int mode; + struct stat st; + int fd; + char *start, *end; + + if (stat(ctx->fname, &st) == 0) { + mode = st.st_mode; + } + else { + mode = -1; + } + + if ((temp = (char *)malloc(strlen(ctx->fname) + 13)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + sprintf(temp, "%s.XXXXXX.part", ctx->fname); + end = temp + strlen(temp) - 5; + start = end - 6; + + for (;;) { + zip_uint32_t value = zip_random_uint32(); + char *xs = start; + + while (xs < end) { + char digit = value % 36; + if (digit < 10) { + *(xs++) = digit + '0'; + } + else { + *(xs++) = digit - 10 + 'a'; + } + value /= 36; + } + + if (create_file) { + if ((fd = open(temp, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, mode == -1 ? 0666 : (mode_t)mode)) >= 0) { + if (mode != -1) { + /* open() honors umask(), which we don't want in this case */ +#ifdef HAVE_FCHMOD + (void)fchmod(fd, (mode_t)mode); +#else + (void)chmod(temp, (mode_t)mode); +#endif + } + break; + } + if (errno != EEXIST) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + } + else { + if (stat(temp, &st) < 0) { + if (errno == ENOENT) { + break; + } + else { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + } + } + } + + ctx->tmpname = temp; + + return create_file ? fd : 0; +}
diff --git a/lib/zipint.h b/lib/zipint.h index eab1e3a..6a26dc3 100644 --- a/lib/zipint.h +++ b/lib/zipint.h
@@ -576,7 +576,7 @@ bool _zip_hash_reserve_capacity(zip_hash_t *hash, zip_uint64_t capacity, zip_error_t *error); bool _zip_hash_revert(zip_hash_t *hash, zip_error_t *error); -int _zip_mkstempm(char *path, int mode); +int _zip_mkstempm(char *path, int mode, bool create_file); zip_t *_zip_open(zip_source_t *, unsigned int, zip_error_t *);