From: Martin Matuška Date: Wed, 10 Sep 2025 08:24:34 +0000 (+0200) Subject: Merge pull request #2686 from stoeckmann/lseek_win_regression X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8055fbd3217d81ae45d47d8bba290b62fcf92b7a;p=thirdparty%2Flibarchive.git Merge pull request #2686 from stoeckmann/lseek_win_regression Fix Windows off_t handling (cherry picked from commit 3b1100f9a9424b266fdb34256adbeb530bc79e5b) --- diff --git a/Makefile.am b/Makefile.am index 0226f2517..735a44ab9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index de69909ed..4fb917132 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -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 index 000000000..5432b2f64 --- /dev/null +++ b/libarchive/archive_platform_stat.h @@ -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 */ diff --git a/libarchive/archive_read_open_fd.c b/libarchive/archive_read_open_fd.c index dc7c9e52c..c85a62a3e 100644 --- a/libarchive/archive_read_open_fd.c +++ b/libarchive/archive_read_open_fd.c @@ -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); diff --git a/libarchive/archive_read_open_file.c b/libarchive/archive_read_open_file.c index 742923abb..6ca2ff191 100644 --- a/libarchive/archive_read_open_file.c +++ b/libarchive/archive_read_open_file.c @@ -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); } diff --git a/libarchive/archive_read_open_filename.c b/libarchive/archive_read_open_filename.c index 0fb72c9e0..a910eefcb 100644 --- a/libarchive/archive_read_open_filename.c +++ b/libarchive/archive_read_open_filename.c @@ -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) diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c index f508a8af1..ded13bee7 100644 --- a/libarchive/archive_read_support_format_mtree.c +++ b/libarchive/archive_read_support_format_mtree.c @@ -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; diff --git a/libarchive/archive_windows.c b/libarchive/archive_windows.c index 1bb73c18d..e55f995c7 100644 --- a/libarchive/archive_windows.c +++ b/libarchive/archive_windows.c @@ -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. */ diff --git a/libarchive/archive_windows.h b/libarchive/archive_windows.h index c528b8f4f..ecb14e010 100644 --- a/libarchive/archive_windows.h +++ b/libarchive/archive_windows.h @@ -72,6 +72,8 @@ #include //#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. */ @@ -262,6 +264,9 @@ #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);