Merge pull request #2686 from stoeckmann/lseek_win_regression
Fix Windows off_t handling
This commit is contained in:
9 files changed
+139
-30
No files matched your search
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user