Merge pull request #2686 from stoeckmann/lseek_win_regression

Fix Windows off_t handling
This commit is contained in:
Martin Matuška authored and GitHub committed 2025-09-10 10:24:34 +02:00
commit 3b1100f9a9
9 files changed
+139 -30

No files matched your search

+1
View 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 \
+1
View 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
+45
View File
@@ -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 */
+13 -10
View 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);
+9 -7
View 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);
}
+13 -10
View 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)
@@ -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;
+48
View 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.
*/
+5
View 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. */
@@ -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);