Skip to content

Commit e134a7f

Browse files
committed
Issue python#23752: _Py_fstat() is now responsible to raise the Python exception
Add _Py_fstat_noraise() function when a Python exception is not welcome.
1 parent 2e1c4e5 commit e134a7f

File tree

12 files changed

+89
-57
lines changed

12 files changed

+89
-57
lines changed

Include/fileutils.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,16 @@ struct _Py_stat_struct {
4141

4242
PyAPI_FUNC(int) _Py_fstat(
4343
int fd,
44-
struct _Py_stat_struct *stat);
44+
struct _Py_stat_struct *status);
45+
46+
PyAPI_FUNC(int) _Py_fstat_noraise(
47+
int fd,
48+
struct _Py_stat_struct *status);
4549
#endif /* Py_LIMITED_API */
4650

4751
PyAPI_FUNC(int) _Py_stat(
4852
PyObject *path,
49-
struct stat *statbuf);
53+
struct stat *status);
5054

5155
#ifndef Py_LIMITED_API
5256
PyAPI_FUNC(int) _Py_open(

Modules/_io/fileio.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,8 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
399399
}
400400

401401
self->blksize = DEFAULT_BUFFER_SIZE;
402-
if (_Py_fstat(self->fd, &fdfstat) < 0) {
403-
PyErr_SetFromErrno(PyExc_OSError);
402+
if (_Py_fstat(self->fd, &fdfstat) < 0)
404403
goto error;
405-
}
406404
#if defined(S_ISDIR) && defined(EISDIR)
407405
/* On Unix, open will succeed for directories.
408406
In Python, there should be no file objects referring to
@@ -589,7 +587,7 @@ new_buffersize(fileio *self, size_t currentsize)
589587
static PyObject *
590588
fileio_readall(fileio *self)
591589
{
592-
struct _Py_stat_struct st;
590+
struct _Py_stat_struct status;
593591
Py_off_t pos, end;
594592
PyObject *result;
595593
Py_ssize_t bytes_read = 0;
@@ -606,8 +604,8 @@ fileio_readall(fileio *self)
606604
#else
607605
pos = lseek(self->fd, 0L, SEEK_CUR);
608606
#endif
609-
if (_Py_fstat(self->fd, &st) == 0)
610-
end = st.st_size;
607+
if (_Py_fstat_noraise(self->fd, &status) == 0)
608+
end = status.st_size;
611609
else
612610
end = (Py_off_t)-1;
613611

Modules/main.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -753,9 +753,11 @@ Py_Main(int argc, wchar_t **argv)
753753
}
754754
{
755755
struct _Py_stat_struct sb;
756-
if (_Py_fstat(fileno(fp), &sb) == 0 &&
756+
if (_Py_fstat_noraise(fileno(fp), &sb) == 0 &&
757757
S_ISDIR(sb.st_mode)) {
758-
fprintf(stderr, "%ls: '%ls' is a directory, cannot continue\n", argv[0], filename);
758+
fprintf(stderr,
759+
"%ls: '%ls' is a directory, cannot continue\n",
760+
argv[0], filename);
759761
fclose(fp);
760762
return 1;
761763
}

Modules/mmapmodule.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -465,15 +465,13 @@ mmap_size_method(mmap_object *self,
465465

466466
#ifdef UNIX
467467
{
468-
struct _Py_stat_struct buf;
469-
if (-1 == _Py_fstat(self->fd, &buf)) {
470-
PyErr_SetFromErrno(PyExc_OSError);
468+
struct _Py_stat_struct status;
469+
if (_Py_fstat(self->fd, &status) == -1)
471470
return NULL;
472-
}
473471
#ifdef HAVE_LARGEFILE_SUPPORT
474-
return PyLong_FromLongLong(buf.st_size);
472+
return PyLong_FromLongLong(status.st_size);
475473
#else
476-
return PyLong_FromLong(buf.st_size);
474+
return PyLong_FromLong(status.st_size);
477475
#endif
478476
}
479477
#endif /* UNIX */
@@ -1112,7 +1110,7 @@ _GetMapSize(PyObject *o, const char* param)
11121110
static PyObject *
11131111
new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
11141112
{
1115-
struct _Py_stat_struct st;
1113+
struct _Py_stat_struct status;
11161114
mmap_object *m_obj;
11171115
PyObject *map_size_obj = NULL;
11181116
Py_ssize_t map_size;
@@ -1177,25 +1175,26 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
11771175
if (fd != -1)
11781176
(void)fcntl(fd, F_FULLFSYNC);
11791177
#endif
1180-
if (fd != -1 && _Py_fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
1178+
if (fd != -1 && _Py_fstat_noraise(fd, &status) == 0
1179+
&& S_ISREG(status.st_mode)) {
11811180
if (map_size == 0) {
1182-
if (st.st_size == 0) {
1181+
if (status.st_size == 0) {
11831182
PyErr_SetString(PyExc_ValueError,
11841183
"cannot mmap an empty file");
11851184
return NULL;
11861185
}
1187-
if (offset >= st.st_size) {
1186+
if (offset >= status.st_size) {
11881187
PyErr_SetString(PyExc_ValueError,
11891188
"mmap offset is greater than file size");
11901189
return NULL;
11911190
}
1192-
if (st.st_size - offset > PY_SSIZE_T_MAX) {
1191+
if (status.st_size - offset > PY_SSIZE_T_MAX) {
11931192
PyErr_SetString(PyExc_ValueError,
11941193
"mmap length is too large");
11951194
return NULL;
11961195
}
1197-
map_size = (Py_ssize_t) (st.st_size - offset);
1198-
} else if (offset + map_size > st.st_size) {
1196+
map_size = (Py_ssize_t) (status.st_size - offset);
1197+
} else if (offset + map_size > status.st_size) {
11991198
PyErr_SetString(PyExc_ValueError,
12001199
"mmap length is greater than file size");
12011200
return NULL;

Modules/posixmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ static int win32_can_symlink = 0;
351351
#ifdef MS_WINDOWS
352352
# define STAT win32_stat
353353
# define LSTAT win32_lstat
354-
# define FSTAT _Py_fstat
354+
# define FSTAT _Py_fstat_noraise
355355
# define STRUCT_STAT struct _Py_stat_struct
356356
#else
357357
# define STAT stat

Modules/signalmodule.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ signal_siginterrupt(PyObject *self, PyObject *args)
503503
static PyObject *
504504
signal_set_wakeup_fd(PyObject *self, PyObject *args)
505505
{
506-
struct _Py_stat_struct st;
506+
struct _Py_stat_struct status;
507507
#ifdef MS_WINDOWS
508508
PyObject *fdobj;
509509
SOCKET_T sockfd, old_sockfd;
@@ -559,10 +559,8 @@ signal_set_wakeup_fd(PyObject *self, PyObject *args)
559559
return NULL;
560560
}
561561

562-
if (_Py_fstat(fd, &st) != 0) {
563-
PyErr_SetExcFromWindowsErr(PyExc_OSError, GetLastError());
562+
if (_Py_fstat(fd, &status) != 0)
564563
return NULL;
565-
}
566564

567565
/* on Windows, a file cannot be set to non-blocking mode */
568566
}
@@ -591,10 +589,8 @@ signal_set_wakeup_fd(PyObject *self, PyObject *args)
591589
return NULL;
592590
}
593591

594-
if (_Py_fstat(fd, &st) != 0) {
595-
PyErr_SetFromErrno(PyExc_OSError);
592+
if (_Py_fstat(fd, &status) != 0)
596593
return NULL;
597-
}
598594

599595
blocking = _Py_get_blocking(fd);
600596
if (blocking < 0)

Programs/_freeze_importlib.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ main(int argc, char *argv[])
3535
{
3636
char *inpath, *outpath;
3737
FILE *infile = NULL, *outfile = NULL;
38-
struct _Py_stat_struct st;
38+
struct _Py_stat_struct status;
3939
size_t text_size, data_size, n;
4040
char *text = NULL;
4141
unsigned char *data;
@@ -54,11 +54,11 @@ main(int argc, char *argv[])
5454
fprintf(stderr, "cannot open '%s' for reading\n", inpath);
5555
goto error;
5656
}
57-
if (_Py_fstat(fileno(infile), &st)) {
57+
if (_Py_fstat_noraise(fileno(infile), &status)) {
5858
fprintf(stderr, "cannot fstat '%s'\n", inpath);
5959
goto error;
6060
}
61-
text_size = st.st_size;
61+
text_size = status.st_size;
6262
text = (char *) malloc(text_size + 1);
6363
if (text == NULL) {
6464
fprintf(stderr, "could not allocate %ld bytes\n", (long) text_size);

Python/dynload_shlib.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,20 @@ dl_funcptr _PyImport_GetDynLoadFunc(const char *shortname,
7171

7272
if (fp != NULL) {
7373
int i;
74-
struct _Py_stat_struct statb;
75-
if (_Py_fstat(fileno(fp), &statb) == -1) {
76-
PyErr_SetFromErrno(PyExc_IOError);
74+
struct _Py_stat_struct status;
75+
if (_Py_fstat(fileno(fp), &status) == -1)
7776
return NULL;
78-
}
7977
for (i = 0; i < nhandles; i++) {
80-
if (statb.st_dev == handles[i].dev &&
81-
statb.st_ino == handles[i].ino) {
78+
if (status.st_dev == handles[i].dev &&
79+
status.st_ino == handles[i].ino) {
8280
p = (dl_funcptr) dlsym(handles[i].handle,
8381
funcname);
8482
return p;
8583
}
8684
}
8785
if (nhandles < 128) {
88-
handles[nhandles].dev = statb.st_dev;
89-
handles[nhandles].ino = statb.st_ino;
86+
handles[nhandles].dev = status.st_dev;
87+
handles[nhandles].ino = status.st_ino;
9088
}
9189
}
9290

Python/fileutils.c

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,8 @@ attributes_to_mode(DWORD attr)
565565
}
566566

567567
void
568-
_Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag, struct _Py_stat_struct *result)
568+
_Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
569+
struct _Py_stat_struct *result)
569570
{
570571
memset(result, 0, sizeof(*result));
571572
result->st_mode = attributes_to_mode(info->dwFileAttributes);
@@ -595,9 +596,12 @@ _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
595596
files larger than 2 GB. fstat() may fail with EOVERFLOW on files larger
596597
than 2 GB because the file size type is an signed 32-bit integer: see issue
597598
#23152.
598-
*/
599+
600+
On Windows, set the last Windows error and return nonzero on error. On
601+
POSIX, set errno and return nonzero on error. Fill status and return 0 on
602+
success. */
599603
int
600-
_Py_fstat(int fd, struct _Py_stat_struct *result)
604+
_Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
601605
{
602606
#ifdef MS_WINDOWS
603607
BY_HANDLE_FILE_INFORMATION info;
@@ -619,38 +623,70 @@ _Py_fstat(int fd, struct _Py_stat_struct *result)
619623
SetLastError(ERROR_INVALID_HANDLE);
620624
return -1;
621625
}
622-
memset(result, 0, sizeof(*result));
626+
memset(status, 0, sizeof(*status));
623627

624628
type = GetFileType(h);
625629
if (type == FILE_TYPE_UNKNOWN) {
626630
DWORD error = GetLastError();
627-
if (error != 0) {
631+
if (error != 0)
628632
return -1;
629-
}
630633
/* else: valid but unknown file */
631634
}
632635

633636
if (type != FILE_TYPE_DISK) {
634637
if (type == FILE_TYPE_CHAR)
635-
result->st_mode = _S_IFCHR;
638+
status->st_mode = _S_IFCHR;
636639
else if (type == FILE_TYPE_PIPE)
637-
result->st_mode = _S_IFIFO;
640+
status->st_mode = _S_IFIFO;
638641
return 0;
639642
}
640643

641644
if (!GetFileInformationByHandle(h, &info)) {
642645
return -1;
643646
}
644647

645-
_Py_attribute_data_to_stat(&info, 0, result);
648+
_Py_attribute_data_to_stat(&info, 0, status);
646649
/* specific to fstat() */
647-
result->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow;
650+
status->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow;
648651
return 0;
649652
#else
650-
return fstat(fd, result);
653+
return fstat(fd, status);
651654
#endif
652655
}
653656

657+
/* Return information about a file.
658+
659+
On POSIX, use fstat().
660+
661+
On Windows, use GetFileType() and GetFileInformationByHandle() which support
662+
files larger than 2 GB. fstat() may fail with EOVERFLOW on files larger
663+
than 2 GB because the file size type is an signed 32-bit integer: see issue
664+
#23152.
665+
666+
Raise an exception and return -1 on error. On Windows, set the last Windows
667+
error on error. On POSIX, set errno on error. Fill status and return 0 on
668+
success.
669+
670+
The GIL must be held. */
671+
int
672+
_Py_fstat(int fd, struct _Py_stat_struct *status)
673+
{
674+
int res;
675+
676+
Py_BEGIN_ALLOW_THREADS
677+
res = _Py_fstat_noraise(fd, status);
678+
Py_END_ALLOW_THREADS
679+
680+
if (res != 0) {
681+
#ifdef MS_WINDOWS
682+
PyErr_SetFromWindowsErr(0);
683+
#else
684+
PyErr_SetFromErrno(PyExc_OSError);
685+
#endif
686+
return -1;
687+
}
688+
return 0;
689+
}
654690

655691
/* Call _wstat() on Windows, or encode the path to the filesystem encoding and
656692
call stat() otherwise. Only fill st_mode attribute on Windows.

Python/marshal.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1486,7 +1486,7 @@ static off_t
14861486
getfilesize(FILE *fp)
14871487
{
14881488
struct _Py_stat_struct st;
1489-
if (_Py_fstat(fileno(fp), &st) != 0)
1489+
if (_Py_fstat_noraise(fileno(fp), &st) != 0)
14901490
return -1;
14911491
#if SIZEOF_OFF_T == 4
14921492
else if (st.st_size >= INT_MAX)

0 commit comments

Comments
 (0)