Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a923e21

Browse files
committed
Fix detection of unseekable files for fseek() and ftello() with MSVC
Calling fseek() or ftello() on a handle to a non-seeking device such as a pipe or a communications device is not supported. Unfortunately, MSVC's flavor of these routines, _fseeki64() and _ftelli64(), do not return an error when given a pipe as handle. Some of the logic of pg_dump and restore relies on these routines to check if a handle is seekable, causing failures when passing the contents of pg_dump to pg_restore through a pipe, for example. This commit introduces wrappers for fseeko() and ftello() on MSVC so as any callers are able to properly detect the cases of non-seekable handles. This relies mainly on GetFileType(), sharing a bit of code with the MSVC port for fstat(). The code in charge of getting a file type is refactored into a new file called win32common.c, shared by win32stat.c and the new win32fseek.c. It includes the MSVC ports for fseeko() and ftello(). Like 765f5df, this is backpatched down to 14, where the fstat() implementation for MSVC is able to understand about files larger than 4GB in size. Using a TAP test for that is proving to be tricky as IPC::Run handles the pipes by itself, still I have been able to check the fix manually. Reported-by: Daniel Watzinger Author: Juan José Santamaría Flecha, Michael Paquier Discussion: https://postgr.es/m/CAC+AXB26a4EmxM2suXxPpJaGrqAdxracd7hskLg-zxtPB50h7A@mail.gmail.com Backpatch-through: 14
1 parent c03c2ea commit a923e21

File tree

8 files changed

+166
-22
lines changed

8 files changed

+166
-22
lines changed

configure

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16492,6 +16492,12 @@ esac
1649216492
;;
1649316493
esac
1649416494

16495+
case " $LIBOBJS " in
16496+
*" win32common.$ac_objext "* ) ;;
16497+
*) LIBOBJS="$LIBOBJS win32common.$ac_objext"
16498+
;;
16499+
esac
16500+
1649516501
case " $LIBOBJS " in
1649616502
*" win32dlopen.$ac_objext "* ) ;;
1649716503
*) LIBOBJS="$LIBOBJS win32dlopen.$ac_objext"

configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,6 +1894,7 @@ if test "$PORTNAME" = "win32"; then
18941894
AC_LIBOBJ(kill)
18951895
AC_LIBOBJ(open)
18961896
AC_LIBOBJ(system)
1897+
AC_LIBOBJ(win32common)
18971898
AC_LIBOBJ(win32dlopen)
18981899
AC_LIBOBJ(win32env)
18991900
AC_LIBOBJ(win32error)

src/include/port/win32_port.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,21 @@ struct itimerval
204204

205205
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
206206

207+
/* Convenience wrapper for GetFileType() */
208+
extern DWORD pgwin32_get_file_type(HANDLE hFile);
209+
207210
/*
208211
* WIN32 does not provide 64-bit off_t, but does provide the functions operating
209-
* with 64-bit offsets.
212+
* with 64-bit offsets. Also, fseek() might not give an error for unseekable
213+
* streams, so harden that function with our version.
210214
*/
211215
#define pgoff_t __int64
212216

213217
#ifdef _MSC_VER
214-
#define fseeko(stream, offset, origin) _fseeki64(stream, offset, origin)
215-
#define ftello(stream) _ftelli64(stream)
218+
extern int _pgfseeko64(FILE *stream, pgoff_t offset, int origin);
219+
extern pgoff_t _pgftello64(FILE *stream);
220+
#define fseeko(stream, offset, origin) _pgfseeko64(stream, offset, origin)
221+
#define ftello(stream) _pgftello64(stream)
216222
#else
217223
#ifndef fseeko
218224
#define fseeko(stream, offset, origin) fseeko64(stream, offset, origin)

src/port/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ if host_system == 'windows'
2929
'kill.c',
3030
'open.c',
3131
'system.c',
32+
'win32common.c',
3233
'win32dlopen.c',
3334
'win32env.c',
3435
'win32error.c',
3536
'win32fdatasync.c',
37+
'win32fseek.c',
3638
'win32getrusage.c',
3739
'win32link.c',
3840
'win32ntdll.c',

src/port/win32common.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* win32common.c
4+
* Common routines shared among the win32*.c ports.
5+
*
6+
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7+
* Portions Copyright (c) 1994, Regents of the University of California
8+
*
9+
*
10+
* IDENTIFICATION
11+
* src/port/win32common.c
12+
*
13+
*-------------------------------------------------------------------------
14+
*/
15+
16+
#ifdef FRONTEND
17+
#include "postgres_fe.h"
18+
#else
19+
#include "postgres.h"
20+
#endif
21+
22+
#ifdef WIN32
23+
24+
/*
25+
* pgwin32_get_file_type
26+
*
27+
* Convenience wrapper for GetFileType() with specific error handling for all the
28+
* port implementations. Returns the file type associated with a HANDLE.
29+
*
30+
* On error, sets errno with FILE_TYPE_UNKNOWN as file type.
31+
*/
32+
DWORD
33+
pgwin32_get_file_type(HANDLE hFile)
34+
{
35+
DWORD fileType = FILE_TYPE_UNKNOWN;
36+
DWORD lastError;
37+
38+
errno = 0;
39+
40+
/*
41+
* When stdin, stdout, and stderr aren't associated with a stream the
42+
* special value -2 is returned:
43+
* https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle
44+
*/
45+
if (hFile == INVALID_HANDLE_VALUE || hFile == (HANDLE) -2)
46+
{
47+
errno = EINVAL;
48+
return FILE_TYPE_UNKNOWN;
49+
}
50+
51+
fileType = GetFileType(hFile);
52+
lastError = GetLastError();
53+
54+
/*
55+
* Invoke GetLastError in order to distinguish between a "valid" return of
56+
* FILE_TYPE_UNKNOWN and its return due to a calling error. In case of
57+
* success, GetLastError() returns NO_ERROR.
58+
*/
59+
if (fileType == FILE_TYPE_UNKNOWN && lastError != NO_ERROR)
60+
{
61+
_dosmaperr(lastError);
62+
return FILE_TYPE_UNKNOWN;
63+
}
64+
65+
return fileType;
66+
}
67+
68+
#endif /* WIN32 */

src/port/win32fseek.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* win32fseek.c
4+
* Replacements for fseeko() and ftello().
5+
*
6+
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7+
*
8+
* IDENTIFICATION
9+
* src/port/win32fseek.c
10+
*
11+
*-------------------------------------------------------------------------
12+
*/
13+
14+
#ifdef FRONTEND
15+
#include "postgres_fe.h"
16+
#else
17+
#include "postgres.h"
18+
#endif
19+
20+
#if defined(WIN32) && defined(_MSC_VER)
21+
22+
/*
23+
* _pgfseeko64
24+
*
25+
* Calling fseek() on a handle to a non-seeking device such as a pipe or
26+
* a communications device is not supported, and fseek() may not return
27+
* an error. This wrapper relies on the file type to check which cases
28+
* are supported.
29+
*/
30+
int
31+
_pgfseeko64(FILE *stream, pgoff_t offset, int origin)
32+
{
33+
DWORD fileType;
34+
HANDLE hFile = (HANDLE) _get_osfhandle(_fileno(stream));
35+
36+
fileType = pgwin32_get_file_type(hFile);
37+
if (errno != 0)
38+
return -1;
39+
40+
if (fileType == FILE_TYPE_DISK)
41+
return _fseeki64(stream, offset, origin);
42+
else if (fileType == FILE_TYPE_CHAR || fileType == FILE_TYPE_PIPE)
43+
errno = ESPIPE;
44+
else
45+
errno = EINVAL;
46+
47+
return -1;
48+
}
49+
50+
/*
51+
* _pgftello64
52+
*
53+
* Same as _pgfseeko64().
54+
*/
55+
pgoff_t
56+
_pgftello64(FILE *stream)
57+
{
58+
DWORD fileType;
59+
HANDLE hFile = (HANDLE) _get_osfhandle(_fileno(stream));
60+
61+
fileType = pgwin32_get_file_type(hFile);
62+
if (errno != 0)
63+
return -1;
64+
65+
if (fileType == FILE_TYPE_DISK)
66+
return _ftelli64(stream);
67+
else if (fileType == FILE_TYPE_CHAR || fileType == FILE_TYPE_PIPE)
68+
errno = ESPIPE;
69+
else
70+
errno = EINVAL;
71+
72+
return -1;
73+
}
74+
75+
#endif /* defined(WIN32) && defined(_MSC_VER) */

src/port/win32stat.c

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -258,33 +258,17 @@ _pgfstat64(int fileno, struct stat *buf)
258258
{
259259
HANDLE hFile = (HANDLE) _get_osfhandle(fileno);
260260
DWORD fileType = FILE_TYPE_UNKNOWN;
261-
DWORD lastError;
262261
unsigned short st_mode;
263262

264-
/*
265-
* When stdin, stdout, and stderr aren't associated with a stream the
266-
* special value -2 is returned:
267-
* https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle
268-
*/
269-
if (hFile == INVALID_HANDLE_VALUE || hFile == (HANDLE) -2 || buf == NULL)
263+
if (buf == NULL)
270264
{
271265
errno = EINVAL;
272266
return -1;
273267
}
274268

275-
fileType = GetFileType(hFile);
276-
lastError = GetLastError();
277-
278-
/*
279-
* Invoke GetLastError in order to distinguish between a "valid" return of
280-
* FILE_TYPE_UNKNOWN and its return due to a calling error. In case of
281-
* success, GetLastError returns NO_ERROR.
282-
*/
283-
if (fileType == FILE_TYPE_UNKNOWN && lastError != NO_ERROR)
284-
{
285-
_dosmaperr(lastError);
269+
fileType = pgwin32_get_file_type(hFile);
270+
if (errno != 0)
286271
return -1;
287-
}
288272

289273
switch (fileType)
290274
{

src/tools/msvc/Mkvcbuild.pm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,11 @@ sub mkvcbuild
108108
pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
109109
pqsignal.c mkdtemp.c qsort.c qsort_arg.c bsearch_arg.c quotes.c system.c
110110
strerror.c tar.c
111+
win32common.c
111112
win32dlopen.c
112113
win32env.c win32error.c
113114
win32fdatasync.c
115+
win32fseek.c
114116
win32getrusage.c
115117
win32gettimeofday.c
116118
win32link.c

0 commit comments

Comments
 (0)