mirror of
https://github.com/zlib-ng/minizip-ng
synced 2026-08-25 14:26:08 -04:00
minizip: place erase temp on same filesystem when TMPDIR differs
minizip_erase rewrites the archive to a temp file under TMPDIR and then renames it onto the source. rename(2) returns EXDEV across filesystems, so when TMPDIR (e.g. /tmp) and the source live on different mounts (a common conda-forge pattern), the rename fails after the original was already moved aside to .bak — the next operation that opens the archive sees no file and reports -111. Add mz_os_path_same_fs() and have minizip_erase fall back on POSIX to a same-dir temp when TMPDIR is on a different filesystem. POSIX uses stat() st_dev comparison; Win32 uses _wstati64 for the parallel check but the caller skips it under #ifndef _WIN32 because MoveFileExW with MOVEFILE_COPY_ALLOWED already absorbs cross-volume rename internally. Make the same-dir tmp filename unique (mz_ms_time suffix) and refuse to clobber an existing leftover from a prior crash. Refs #943.
This commit is contained in:
parent
ece5b06dcf
commit
3f23ed7a11
4 changed files with 66 additions and 0 deletions
22
minizip.c
22
minizip.c
|
|
@ -444,6 +444,28 @@ int32_t minizip_erase(const char *src_path, const char *target_path, int32_t arg
|
|||
/* Construct temporary zip name with random suffix */
|
||||
if (mz_os_get_temp_path(tmp_path, sizeof(tmp_path), "mz_") != MZ_OK)
|
||||
return MZ_INTERNAL_ERROR;
|
||||
|
||||
#ifndef _WIN32
|
||||
/* POSIX rename(2) returns EXDEV across mounts; fall back to a same-dir tmp when
|
||||
TMPDIR is on a different filesystem. MoveFileExW absorbs cross-volume natively
|
||||
on Win32, so the check is skipped there. */
|
||||
{
|
||||
char tmp_dir[256];
|
||||
|
||||
strncpy(tmp_dir, tmp_path, sizeof(tmp_dir) - 1);
|
||||
tmp_dir[sizeof(tmp_dir) - 1] = 0;
|
||||
mz_path_remove_filename(tmp_dir);
|
||||
|
||||
if (mz_os_path_same_fs(tmp_dir, src_path) != MZ_OK) {
|
||||
int32_t result = snprintf(tmp_path, sizeof(tmp_path), "%s.mz_tmp.%llu", src_path,
|
||||
(unsigned long long)mz_os_ms_time());
|
||||
if (result < 0 || result >= (int32_t)sizeof(tmp_path))
|
||||
return MZ_BUF_ERROR;
|
||||
if (mz_os_file_exists(tmp_path) == MZ_OK)
|
||||
return MZ_EXIST_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
target_path_ptr = tmp_path;
|
||||
}
|
||||
|
||||
|
|
|
|||
3
mz_os.h
3
mz_os.h
|
|
@ -154,6 +154,9 @@ int32_t mz_os_set_file_attribs(const char *path, uint32_t attributes);
|
|||
int32_t mz_os_get_temp_path(char *path, int32_t max_path, const char *prefix);
|
||||
/* Gets a unique temporary file path */
|
||||
|
||||
int32_t mz_os_path_same_fs(const char *path_a, const char *path_b);
|
||||
/* Checks if both paths are on the same filesystem */
|
||||
|
||||
int32_t mz_os_make_dir(const char *path);
|
||||
/* Recursively creates a directory */
|
||||
|
||||
|
|
|
|||
|
|
@ -165,6 +165,15 @@ int32_t mz_os_rename(const char *source_path, const char *target_path) {
|
|||
return MZ_OK;
|
||||
}
|
||||
|
||||
int32_t mz_os_path_same_fs(const char *path_a, const char *path_b) {
|
||||
struct stat sa, sb;
|
||||
if (!path_a || !path_b)
|
||||
return MZ_PARAM_ERROR;
|
||||
if (stat(path_a, &sa) != 0 || stat(path_b, &sb) != 0)
|
||||
return MZ_EXIST_ERROR;
|
||||
return (sa.st_dev == sb.st_dev) ? MZ_OK : MZ_EXIST_ERROR;
|
||||
}
|
||||
|
||||
int32_t mz_os_unlink(const char *path) {
|
||||
if (unlink(path) == -1)
|
||||
return MZ_EXIST_ERROR;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
#include "mz_os.h"
|
||||
#include "mz_strm_os.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
|
|
@ -114,6 +117,35 @@ int32_t mz_os_rand(uint8_t *buf, int32_t size) {
|
|||
return len;
|
||||
}
|
||||
|
||||
int32_t mz_os_path_same_fs(const char *path_a, const char *path_b) {
|
||||
wchar_t *path_a_wide = NULL;
|
||||
wchar_t *path_b_wide = NULL;
|
||||
struct _stati64 sa;
|
||||
struct _stati64 sb;
|
||||
int32_t err = MZ_OK;
|
||||
|
||||
if (!path_a || !path_b)
|
||||
return MZ_PARAM_ERROR;
|
||||
|
||||
path_a_wide = mz_os_unicode_string_create(path_a, MZ_ENCODING_UTF8);
|
||||
if (!path_a_wide)
|
||||
return MZ_PARAM_ERROR;
|
||||
path_b_wide = mz_os_unicode_string_create(path_b, MZ_ENCODING_UTF8);
|
||||
if (!path_b_wide) {
|
||||
mz_os_unicode_string_delete(&path_a_wide);
|
||||
return MZ_PARAM_ERROR;
|
||||
}
|
||||
|
||||
if (_wstati64(path_a_wide, &sa) != 0 || _wstati64(path_b_wide, &sb) != 0)
|
||||
err = MZ_EXIST_ERROR;
|
||||
else if (sa.st_dev != sb.st_dev)
|
||||
err = MZ_EXIST_ERROR;
|
||||
|
||||
mz_os_unicode_string_delete(&path_a_wide);
|
||||
mz_os_unicode_string_delete(&path_b_wide);
|
||||
return err;
|
||||
}
|
||||
|
||||
int32_t mz_os_rename(const char *source_path, const char *target_path) {
|
||||
wchar_t *source_path_wide = NULL;
|
||||
wchar_t *target_path_wide = NULL;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue