]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Merge pull request #2686 from stoeckmann/lseek_win_regression
authorMartin Matuška <martin@matuska.de>
Wed, 10 Sep 2025 08:24:34 +0000 (10:24 +0200)
committerMartin Matuska <martin@matuska.de>
Tue, 23 Sep 2025 20:59:21 +0000 (22:59 +0200)
Fix Windows off_t handling

(cherry picked from commit 3b1100f9a9424b266fdb34256adbeb530bc79e5b)

Makefile.am
libarchive/CMakeLists.txt
libarchive/archive_platform_stat.h [new file with mode: 0644]
libarchive/archive_read_open_fd.c
libarchive/archive_read_open_file.c
libarchive/archive_read_open_filename.c
libarchive/archive_read_support_format_mtree.c
libarchive/archive_windows.c
libarchive/archive_windows.h

index 0226f25173e2520f724f058240194fbd32030591..735a44ab91a769656190fbb1d0d64b9b95543f18 100644 (file)
@@ -132,6 +132,7 @@ libarchive_la_SOURCES= \
        libarchive/archive_pathmatch.h \
        libarchive/archive_platform.h \
        libarchive/archive_platform_acl.h \
+       libarchive/archive_platform_stat.h \
        libarchive/archive_platform_xattr.h \
        libarchive/archive_ppmd_private.h \
        libarchive/archive_ppmd7.c \
index de69909edfcebabb1fbd4bfff9783f3ff4ff5e69..4fb917132ca0eb45c02dfdeebffe2b34e252b08e 100644 (file)
@@ -52,6 +52,7 @@ SET(libarchive_SOURCES
   archive_pathmatch.h
   archive_platform.h
   archive_platform_acl.h
+  archive_platform_stat.h
   archive_platform_xattr.h
   archive_ppmd_private.h
   archive_ppmd8.c
diff --git a/libarchive/archive_platform_stat.h b/libarchive/archive_platform_stat.h
new file mode 100644 (file)
index 0000000..5432b2f
--- /dev/null
@@ -0,0 +1,45 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Tobias Stoeckmann
+ * All rights reserved.
+ */
+
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
+
+#ifndef ARCHIVE_PLATFORM_STAT_H_INCLUDED
+#define ARCHIVE_PLATFORM_STAT_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+/* We use _lseeki64() on Windows. */
+typedef int64_t la_seek_t;
+
+struct la_seek_stat {
+       int64_t         st_mtime;
+       ino_t           st_ino;
+       unsigned short  st_mode;
+       uint32_t        st_nlink;
+       gid_t           st_gid;
+       la_seek_t       st_size;
+       uid_t           st_uid;
+       dev_t           st_dev;
+       dev_t           st_rdev;
+};
+typedef struct la_seek_stat la_seek_stat_t;
+
+#define la_seek_fstat(fd, st)  __la_seek_fstat((fd), (st))
+#define la_seek_stat(fd, st)   __la_seek_stat((fd), (st))
+
+#else
+typedef off_t la_seek_t;
+typedef struct stat la_seek_stat_t;
+
+#define la_seek_fstat(fd, st)  fstat((fd), (st))
+#define la_seek_stat(fd, st)   stat((fd), (st))
+#endif
+
+#endif /* !ARCHIVE_PLATFORM_STAT_H_INCLUDED */
index dc7c9e52c6f63a07841b788cd130d23d39cbd863..c85a62a3e2d73f38174663828c22ecbdfe7d81a4 100644 (file)
@@ -48,6 +48,7 @@
 #endif
 
 #include "archive.h"
+#include "archive_platform_stat.h"
 
 struct read_fd_data {
        int      fd;
@@ -65,12 +66,12 @@ static int64_t      file_skip(struct archive *, void *, int64_t request);
 int
 archive_read_open_fd(struct archive *a, int fd, size_t block_size)
 {
-       struct stat st;
+       la_seek_stat_t st;
        struct read_fd_data *mine;
        void *b;
 
        archive_clear_error(a);
-       if (fstat(fd, &st) != 0) {
+       if (la_seek_fstat(fd, &st) != 0) {
                archive_set_error(a, errno, "Can't stat fd %d", fd);
                return (ARCHIVE_FATAL);
        }
@@ -133,7 +134,7 @@ static int64_t
 file_skip(struct archive *a, void *client_data, int64_t request)
 {
        struct read_fd_data *mine = (struct read_fd_data *)client_data;
-       off_t skip = (off_t)request;
+       la_seek_t skip = (la_seek_t)request;
        int64_t old_offset, new_offset;
        int skip_bits = sizeof(skip) * 8 - 1;  /* off_t is a signed type. */
 
@@ -149,7 +150,8 @@ file_skip(struct archive *a, void *client_data, int64_t request)
        }
 
        /* Reduce 'skip' to the next smallest multiple of block_size */
-       skip = (off_t)(((int64_t)skip / mine->block_size) * mine->block_size);
+       skip = (la_seek_t)(((int64_t)skip / mine->block_size) * mine->block_size);
+
        if (skip == 0)
                return (0);
 
@@ -185,27 +187,28 @@ static int64_t
 file_seek(struct archive *a, void *client_data, int64_t request, int whence)
 {
        struct read_fd_data *mine = (struct read_fd_data *)client_data;
-       off_t seek = (off_t)request;
+       la_seek_t seek = (la_seek_t)request;
        int64_t r;
        int seek_bits = sizeof(seek) * 8 - 1;  /* off_t is a signed type. */
 
        /* We use off_t here because lseek() is declared that way. */
 
-       /* Reduce a request that would overflow the 'seek' variable. */
+       /* Do not perform a seek which cannot be fulfilled. */
        if (sizeof(request) > sizeof(seek)) {
                const int64_t max_seek =
                    (((int64_t)1 << (seek_bits - 1)) - 1) * 2 + 1;
                const int64_t min_seek = ~max_seek;
-               if (request > max_seek)
-                       seek = (off_t)max_seek;
-               else if (request < min_seek)
-                       seek = (off_t)min_seek;
+               if (request < min_seek || request > max_seek) {
+                       errno = EOVERFLOW;
+                       goto err;
+               }
        }
 
        r = lseek(mine->fd, seek, whence);
        if (r >= 0)
                return r;
 
+err:
        if (errno == ESPIPE) {
                archive_set_error(a, errno,
                    "A file descriptor(%d) is not seekable(PIPE)", mine->fd);
index 742923abbee99c6560fde6ad4a5727f2f5dd33bb..6ca2ff191aa8daab047acc56c00aa5e074e5072b 100644 (file)
@@ -48,6 +48,7 @@
 #endif
 
 #include "archive.h"
+#include "archive_platform_stat.h"
 
 struct read_FILE_data {
        FILE    *f;
@@ -65,7 +66,7 @@ static int64_t        FILE_skip(struct archive *, void *, int64_t);
 int
 archive_read_open_FILE(struct archive *a, FILE *f)
 {
-       struct stat st;
+       la_seek_stat_t st;
        struct read_FILE_data *mine;
        size_t block_size = 128 * 1024;
        void *b;
@@ -88,7 +89,7 @@ archive_read_open_FILE(struct archive *a, FILE *f)
         * streams that don't support fileno()).  As a result, fileno()
         * should be used cautiously.)
         */
-       if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) {
+       if (la_seek_fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) {
                archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
                /* Enable the seek optimization only for regular files. */
                mine->can_skip = 1;
@@ -205,15 +206,15 @@ FILE_seek(struct archive *a, void *client_data, int64_t request, int whence)
        int seek_bits = sizeof(seek) * 8 - 1;
        (void)a; /* UNUSED */
 
-       /* Reduce a request that would overflow the 'seek' variable. */
+       /* Do not perform a seek which cannot be fulfilled. */
        if (sizeof(request) > sizeof(seek)) {
                const int64_t max_seek =
                    (((int64_t)1 << (seek_bits - 1)) - 1) * 2 + 1;
                const int64_t min_seek = ~max_seek;
-               if (request > max_seek)
-                       seek = max_seek;
-               else if (request < min_seek)
-                       seek = min_seek;
+               if (request < min_seek || request > max_seek) {
+                       errno = EOVERFLOW;
+                       goto err;
+               }
        }
 
 #ifdef __ANDROID__
@@ -236,6 +237,7 @@ FILE_seek(struct archive *a, void *client_data, int64_t request, int whence)
        }
 #endif
        /* If we arrive here, the input is corrupted or truncated so fail. */
+err:
        archive_set_error(a, errno, "Error seeking in FILE* pointer");
        return (ARCHIVE_FATAL);
 }
index 0fb72c9e04ac720739fe7af69c082b36a8c5469e..a910eefcbfd23243fccb049ed7336aafd5ad1890 100644 (file)
@@ -59,6 +59,7 @@
 #endif
 
 #include "archive.h"
+#include "archive_platform_stat.h"
 #include "archive_private.h"
 #include "archive_string.h"
 
@@ -252,7 +253,7 @@ archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename,
 static int
 file_open(struct archive *a, void *client_data)
 {
-       struct stat st;
+       la_seek_stat_t st;
        struct read_file_data *mine = (struct read_file_data *)client_data;
        void *buffer;
        const char *filename = NULL;
@@ -317,7 +318,7 @@ file_open(struct archive *a, void *client_data)
                goto fail;
 #endif
        }
-       if (fstat(fd, &st) != 0) {
+       if (la_seek_fstat(fd, &st) != 0) {
 #if defined(_WIN32) && !defined(__CYGWIN__)
                if (mine->filename_type == FNT_WCS)
                        archive_set_error(a, errno, "Can't stat '%ls'",
@@ -486,10 +487,11 @@ file_skip_lseek(struct archive *a, void *client_data, int64_t request)
        struct read_file_data *mine = (struct read_file_data *)client_data;
 #if defined(_WIN32) && !defined(__CYGWIN__)
        /* We use _lseeki64() on Windows. */
-       int64_t old_offset, new_offset, skip = request;
+       int64_t old_offset, new_offset;
 #else
-       off_t old_offset, new_offset, skip = (off_t)request;
+       off_t old_offset, new_offset;
 #endif
+       la_seek_t skip = (la_seek_t)request;
        int skip_bits = sizeof(skip) * 8 - 1;
 
        /* We use off_t here because lseek() is declared that way. */
@@ -556,21 +558,21 @@ static int64_t
 file_seek(struct archive *a, void *client_data, int64_t request, int whence)
 {
        struct read_file_data *mine = (struct read_file_data *)client_data;
-       off_t seek = (off_t)request;
+       la_seek_t seek = (la_seek_t)request;
        int64_t r;
        int seek_bits = sizeof(seek) * 8 - 1;
 
        /* We use off_t here because lseek() is declared that way. */
 
-       /* Reduce a request that would overflow the 'seek' variable. */
+       /* Do not perform a seek which cannot be fulfilled. */
        if (sizeof(request) > sizeof(seek)) {
                const int64_t max_seek =
                    (((int64_t)1 << (seek_bits - 1)) - 1) * 2 + 1;
                const int64_t min_seek = ~max_seek;
-               if (request > max_seek)
-                       seek = (off_t)max_seek;
-               else if (request < min_seek)
-                       seek = (off_t)min_seek;
+               if (request < min_seek || request > max_seek) {
+                       errno = EOVERFLOW;
+                       goto err;
+               }
        }
 
        r = lseek(mine->fd, seek, whence);
@@ -578,6 +580,7 @@ file_seek(struct archive *a, void *client_data, int64_t request, int whence)
                return r;
 
        /* If the input is corrupted or truncated, fail. */
+err:
        if (mine->filename_type == FNT_STDIN)
                archive_set_error(a, errno, "Error seeking in stdin");
        else if (mine->filename_type == FNT_MBS)
index f508a8af153d98adb21a7b5d24974469beef4486..ded13bee79a3247d3060b1b6d89d76c3fb203c8c 100644 (file)
@@ -51,6 +51,7 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_entry_private.h"
+#include "archive_platform_stat.h"
 #include "archive_private.h"
 #include "archive_rb.h"
 #include "archive_read_private.h"
@@ -1177,7 +1178,7 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
     struct mtree *mtree, struct mtree_entry *mentry, int *use_next)
 {
        const char *path;
-       struct stat st_storage, *st;
+       la_seek_stat_t st_storage, *st;
        struct mtree_entry *mp;
        struct archive_entry *sparse_entry;
        int r = ARCHIVE_OK, r1, parsed_kws;
@@ -1272,7 +1273,7 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
 
                st = &st_storage;
                if (mtree->fd >= 0) {
-                       if (fstat(mtree->fd, st) == -1) {
+                       if (la_seek_fstat(mtree->fd, st) == -1) {
                                archive_set_error(&a->archive, errno,
                                                "Could not fstat %s", path);
                                r = ARCHIVE_WARN;
@@ -1285,7 +1286,7 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
 #ifdef HAVE_LSTAT
                else if (lstat(path, st) == -1)
 #else
-               else if (la_stat(path, st) == -1)
+               else if (la_seek_stat(path, st) == -1)
 #endif
                {
                        st = NULL;
index 1bb73c18d6fecc1e612fb7bd7def604180a71a89..e55f995c77023bfaa58bf26746536d81decf292f 100644 (file)
@@ -45,6 +45,7 @@
 #if defined(_WIN32) && !defined(__CYGWIN__)
 
 #include "archive_platform.h"
+#include "archive_platform_stat.h"
 #include "archive_private.h"
 #include "archive_entry.h"
 #include "archive_time_private.h"
@@ -723,6 +724,53 @@ __la_stat(const char *path, struct stat *st)
        return (ret);
 }
 
+static void
+copy_seek_stat(la_seek_stat_t *st, struct ustat *us)
+{
+       st->st_mtime = us->st_mtime;
+       st->st_gid = us->st_gid;
+       st->st_ino = getino(us);
+       st->st_mode = us->st_mode;
+       st->st_nlink = us->st_nlink;
+       st->st_size = (la_seek_t)us->st_size;
+       if (st->st_size < 0 || (uint64_t)st->st_size != us->st_size)
+               st->st_size = -1;
+       st->st_uid = us->st_uid;
+       st->st_dev = us->st_dev;
+       st->st_rdev = us->st_rdev;
+}
+
+int
+__la_seek_fstat(int fd, la_seek_stat_t *st)
+{
+       struct ustat u;
+       int ret;
+
+       ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
+       copy_seek_stat(st, &u);
+       return (ret);
+}
+
+int
+__la_seek_stat(const char *path, la_seek_stat_t *st)
+{
+       HANDLE handle;
+       struct ustat u;
+       int ret;
+
+       handle = la_CreateFile(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+               FILE_FLAG_BACKUP_SEMANTICS,
+               NULL);
+       if (handle == INVALID_HANDLE_VALUE) {
+               la_dosmaperr(GetLastError());
+               return (-1);
+       }
+       ret = __hstat(handle, &u);
+       CloseHandle(handle);
+       copy_seek_stat(st, &u);
+       return (ret);
+}
+
 /*
  * This waitpid is limited implementation.
  */
index c528b8f4fad54dd1e74a3462787eadf1d9d15c50..ecb14e01087b37670ba3b6587cb0d11b54a48fef 100644 (file)
@@ -72,6 +72,8 @@
 #include <windows.h>
 //#define      EFTYPE 7
 
+#include "archive_platform_stat.h"
+
 #if defined(__BORLANDC__)
 #pragma warn -8068     /* Constant out of range in comparison. */
 #pragma warn -8072     /* Suspicious pointer arithmetic. */
     #define    F_OK    0       /*  Test for existence of file  */
 #endif
 
+/* Functions to circumvent off_t limitations */
+int __la_seek_fstat(int fd, la_seek_stat_t *st);
+int __la_seek_stat(const char *path, la_seek_stat_t *st);
 
 /* Replacement POSIX function */
 extern int      __la_fstat(int fd, struct stat *st);