Coverage Report

Created: 2025-11-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Modules/_io/fileio.c
Line
Count
Source
1
/* Author: Daniel Stutzbach */
2
3
#include "Python.h"
4
#include "pycore_fileutils.h"     // _Py_BEGIN_SUPPRESS_IPH
5
#include "pycore_object.h"        // _PyObject_GC_UNTRACK()
6
#include "pycore_pyerrors.h"      // _PyErr_ChainExceptions1()
7
#include "pycore_weakref.h"       // FT_CLEAR_WEAKREFS()
8
9
#include <stdbool.h>              // bool
10
#ifdef HAVE_UNISTD_H
11
#  include <unistd.h>             // lseek()
12
#endif
13
#ifdef HAVE_SYS_TYPES_H
14
#  include <sys/types.h>
15
#endif
16
#ifdef HAVE_IO_H
17
#  include <io.h>
18
#endif
19
#ifdef HAVE_FCNTL_H
20
#  include <fcntl.h>              // open()
21
#endif
22
23
#include "_iomodule.h"
24
25
/*
26
 * Known likely problems:
27
 *
28
 * - Files larger then 2**32-1
29
 * - Files with unicode filenames
30
 * - Passing numbers greater than 2**32-1 when an integer is expected
31
 * - Making it work on Windows and other oddball platforms
32
 *
33
 * To Do:
34
 *
35
 * - autoconfify header file inclusion
36
 */
37
38
#ifdef MS_WINDOWS
39
   // can simulate truncate with Win32 API functions; see file_truncate
40
#  define HAVE_FTRUNCATE
41
#  ifndef WIN32_LEAN_AND_MEAN
42
#    define WIN32_LEAN_AND_MEAN
43
#  endif
44
#  include <windows.h>
45
#endif
46
47
#if BUFSIZ < (8*1024)
48
#  define SMALLCHUNK (8*1024)
49
#elif (BUFSIZ >= (2 << 25))
50
#  error "unreasonable BUFSIZ > 64 MiB defined"
51
#else
52
3
#  define SMALLCHUNK BUFSIZ
53
#endif
54
55
/* Size at which a buffer is considered "large" and behavior should change to
56
   avoid excessive memory allocation */
57
1.20k
#define LARGE_BUFFER_CUTOFF_SIZE 65536
58
59
/*[clinic input]
60
module _io
61
class _io.FileIO "fileio *" "clinic_state()->PyFileIO_Type"
62
[clinic start generated code]*/
63
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ac25ec278f4d6703]*/
64
65
typedef struct {
66
    PyObject_HEAD
67
    int fd;
68
    unsigned int created : 1;
69
    unsigned int readable : 1;
70
    unsigned int writable : 1;
71
    unsigned int appending : 1;
72
    signed int seekable : 2; /* -1 means unknown */
73
    unsigned int truncate : 1;
74
    unsigned int closefd : 1;
75
    char finalizing;
76
    /* Stat result which was grabbed at file open, useful for optimizing common
77
       File I/O patterns to be more efficient. This is only guidance / an
78
       estimate, as it is subject to Time-Of-Check to Time-Of-Use (TOCTOU)
79
       issues / bugs. Both the underlying file descriptor and file may be
80
       modified outside of the fileio object / Python (ex. gh-90102, GH-121941,
81
       gh-109523). */
82
    struct _Py_stat_struct *stat_atopen;
83
    PyObject *weakreflist;
84
    PyObject *dict;
85
} fileio;
86
87
#define PyFileIO_Check(state, op) (PyObject_TypeCheck((op), state->PyFileIO_Type))
88
14.6k
#define PyFileIO_CAST(op) ((fileio *)(op))
89
90
/* Forward declarations */
91
static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error);
92
93
int
94
_PyFileIO_closed(PyObject *self)
95
2.42k
{
96
2.42k
    return (PyFileIO_CAST(self)->fd < 0);
97
2.42k
}
98
99
/* Because this can call arbitrary code, it shouldn't be called when
100
   the refcount is 0 (that is, not directly from tp_dealloc unless
101
   the refcount has been temporarily re-incremented). */
102
static PyObject *
103
fileio_dealloc_warn(PyObject *op, PyObject *source)
104
0
{
105
0
    fileio *self = PyFileIO_CAST(op);
106
0
    if (self->fd >= 0 && self->closefd) {
107
0
        PyObject *exc = PyErr_GetRaisedException();
108
0
        if (PyErr_ResourceWarning(source, 1, "unclosed file %R", source)) {
109
            /* Spurious errors can appear at shutdown */
110
0
            if (PyErr_ExceptionMatches(PyExc_Warning)) {
111
0
                PyErr_FormatUnraisable("Exception ignored "
112
0
                                       "while finalizing file %R", self);
113
0
            }
114
0
        }
115
0
        PyErr_SetRaisedException(exc);
116
0
    }
117
0
    Py_RETURN_NONE;
118
0
}
119
120
/* Returns 0 on success, -1 with exception set on failure. */
121
static int
122
internal_close(fileio *self)
123
1.46k
{
124
1.46k
    int err = 0;
125
1.46k
    int save_errno = 0;
126
1.46k
    if (self->fd >= 0) {
127
1.46k
        int fd = self->fd;
128
1.46k
        self->fd = -1;
129
        /* fd is accessible and someone else may have closed it */
130
1.46k
        Py_BEGIN_ALLOW_THREADS
131
1.46k
        _Py_BEGIN_SUPPRESS_IPH
132
1.46k
        err = close(fd);
133
1.46k
        if (err < 0)
134
0
            save_errno = errno;
135
1.46k
        _Py_END_SUPPRESS_IPH
136
1.46k
        Py_END_ALLOW_THREADS
137
1.46k
    }
138
1.46k
    PyMem_Free(self->stat_atopen);
139
1.46k
    self->stat_atopen = NULL;
140
1.46k
    if (err < 0) {
141
0
        errno = save_errno;
142
0
        PyErr_SetFromErrno(PyExc_OSError);
143
0
        return -1;
144
0
    }
145
1.46k
    return 0;
146
1.46k
}
147
148
/*[clinic input]
149
_io.FileIO.close
150
151
    cls: defining_class
152
    /
153
154
Close the file.
155
156
A closed file cannot be used for further I/O operations.  close() may be
157
called more than once without error.
158
[clinic start generated code]*/
159
160
static PyObject *
161
_io_FileIO_close_impl(fileio *self, PyTypeObject *cls)
162
/*[clinic end generated code: output=c30cbe9d1f23ca58 input=70da49e63db7c64d]*/
163
1.46k
{
164
1.46k
    PyObject *res;
165
1.46k
    int rc;
166
1.46k
    _PyIO_State *state = get_io_state_by_cls(cls);
167
1.46k
    res = PyObject_CallMethodOneArg((PyObject*)state->PyRawIOBase_Type,
168
1.46k
                                     &_Py_ID(close), (PyObject *)self);
169
1.46k
    if (!self->closefd) {
170
0
        self->fd = -1;
171
0
        return res;
172
0
    }
173
174
1.46k
    PyObject *exc = NULL;
175
1.46k
    if (res == NULL) {
176
0
        exc = PyErr_GetRaisedException();
177
0
    }
178
1.46k
    if (self->finalizing) {
179
0
        PyObject *r = fileio_dealloc_warn((PyObject*)self, (PyObject *) self);
180
0
        if (r) {
181
0
            Py_DECREF(r);
182
0
        }
183
0
        else {
184
0
            PyErr_Clear();
185
0
        }
186
0
    }
187
1.46k
    rc = internal_close(self);
188
1.46k
    if (res == NULL) {
189
0
        _PyErr_ChainExceptions1(exc);
190
0
    }
191
1.46k
    if (rc < 0) {
192
0
        Py_CLEAR(res);
193
0
    }
194
1.46k
    return res;
195
1.46k
}
196
197
static PyObject *
198
fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
199
1.53k
{
200
1.53k
    assert(type != NULL && type->tp_alloc != NULL);
201
202
1.53k
    fileio *self = (fileio *) type->tp_alloc(type, 0);
203
1.53k
    if (self == NULL) {
204
0
        return NULL;
205
0
    }
206
207
1.53k
    self->fd = -1;
208
1.53k
    self->created = 0;
209
1.53k
    self->readable = 0;
210
1.53k
    self->writable = 0;
211
1.53k
    self->appending = 0;
212
1.53k
    self->seekable = -1;
213
1.53k
    self->truncate = 0;
214
1.53k
    self->stat_atopen = NULL;
215
1.53k
    self->closefd = 1;
216
1.53k
    self->weakreflist = NULL;
217
1.53k
    return (PyObject *) self;
218
1.53k
}
219
220
#ifdef O_CLOEXEC
221
extern int _Py_open_cloexec_works;
222
#endif
223
224
/*[clinic input]
225
_io.FileIO.__init__
226
    file as nameobj: object
227
    mode: str = "r"
228
    closefd: bool = True
229
    opener: object = None
230
231
Open a file.
232
233
The mode can be 'r' (default), 'w', 'x' or 'a' for reading,
234
writing, exclusive creation or appending.  The file will be created if it
235
doesn't exist when opened for writing or appending; it will be truncated
236
when opened for writing.  A FileExistsError will be raised if it already
237
exists when opened for creating. Opening a file for creating implies
238
writing so this mode behaves in a similar way to 'w'.Add a '+' to the mode
239
to allow simultaneous reading and writing. A custom opener can be used by
240
passing a callable as *opener*. The underlying file descriptor for the file
241
object is then obtained by calling opener with (*name*, *flags*).
242
*opener* must return an open file descriptor (passing os.open as *opener*
243
results in functionality similar to passing None).
244
[clinic start generated code]*/
245
246
static int
247
_io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
248
                         int closefd, PyObject *opener)
249
/*[clinic end generated code: output=23413f68e6484bbd input=588aac967e0ba74b]*/
250
1.53k
{
251
#ifdef MS_WINDOWS
252
    wchar_t *widename = NULL;
253
#else
254
1.53k
    const char *name = NULL;
255
1.53k
#endif
256
1.53k
    PyObject *stringobj = NULL;
257
1.53k
    const char *s;
258
1.53k
    int ret = 0;
259
1.53k
    int rwa = 0, plus = 0;
260
1.53k
    int flags = 0;
261
1.53k
    int fd = -1;
262
1.53k
    int fd_is_own = 0;
263
1.53k
#ifdef O_CLOEXEC
264
1.53k
    int *atomic_flag_works = &_Py_open_cloexec_works;
265
#elif !defined(MS_WINDOWS)
266
    int *atomic_flag_works = NULL;
267
#endif
268
1.53k
    int fstat_result;
269
1.53k
    int async_err = 0;
270
271
#ifdef Py_DEBUG
272
    _PyIO_State *state = find_io_state_by_def(Py_TYPE(self));
273
    assert(PyFileIO_Check(state, self));
274
#endif
275
1.53k
    if (self->fd >= 0) {
276
0
        if (self->closefd) {
277
            /* Have to close the existing file first. */
278
0
            if (internal_close(self) < 0) {
279
0
                return -1;
280
0
            }
281
0
        }
282
0
        else
283
0
            self->fd = -1;
284
0
    }
285
286
1.53k
    if (PyBool_Check(nameobj)) {
287
0
        if (PyErr_WarnEx(PyExc_RuntimeWarning,
288
0
                "bool is used as a file descriptor", 1))
289
0
        {
290
0
            return -1;
291
0
        }
292
0
    }
293
1.53k
    fd = PyLong_AsInt(nameobj);
294
1.53k
    if (fd < 0) {
295
1.20k
        if (!PyErr_Occurred()) {
296
0
            PyErr_SetString(PyExc_ValueError,
297
0
                            "negative file descriptor");
298
0
            return -1;
299
0
        }
300
1.20k
        PyErr_Clear();
301
1.20k
    }
302
303
1.53k
    if (fd < 0) {
304
#ifdef MS_WINDOWS
305
        if (!PyUnicode_FSDecoder(nameobj, &stringobj)) {
306
            return -1;
307
        }
308
        widename = PyUnicode_AsWideCharString(stringobj, NULL);
309
        if (widename == NULL)
310
            return -1;
311
#else
312
1.20k
        if (!PyUnicode_FSConverter(nameobj, &stringobj)) {
313
0
            return -1;
314
0
        }
315
1.20k
        name = PyBytes_AS_STRING(stringobj);
316
1.20k
#endif
317
1.20k
    }
318
319
1.53k
    s = mode;
320
3.32k
    while (*s) {
321
1.79k
        switch (*s++) {
322
0
        case 'x':
323
0
            if (rwa) {
324
0
            bad_mode:
325
0
                PyErr_SetString(PyExc_ValueError,
326
0
                                "Must have exactly one of create/read/write/append "
327
0
                                "mode and at most one plus");
328
0
                goto error;
329
0
            }
330
0
            rwa = 1;
331
0
            self->created = 1;
332
0
            self->writable = 1;
333
0
            flags |= O_EXCL | O_CREAT;
334
0
            break;
335
1.22k
        case 'r':
336
1.22k
            if (rwa)
337
0
                goto bad_mode;
338
1.22k
            rwa = 1;
339
1.22k
            self->readable = 1;
340
1.22k
            break;
341
304
        case 'w':
342
304
            if (rwa)
343
0
                goto bad_mode;
344
304
            rwa = 1;
345
304
            self->writable = 1;
346
304
            self->truncate = 1;
347
304
            flags |= O_CREAT | O_TRUNC;
348
304
            break;
349
0
        case 'a':
350
0
            if (rwa)
351
0
                goto bad_mode;
352
0
            rwa = 1;
353
0
            self->writable = 1;
354
0
            self->appending = 1;
355
0
            flags |= O_APPEND | O_CREAT;
356
0
            break;
357
260
        case 'b':
358
260
            break;
359
0
        case '+':
360
0
            if (plus)
361
0
                goto bad_mode;
362
0
            self->readable = self->writable = 1;
363
0
            plus = 1;
364
0
            break;
365
0
        default:
366
0
            PyErr_Format(PyExc_ValueError,
367
0
                         "invalid mode: %.200s", mode);
368
0
            goto error;
369
1.79k
        }
370
1.79k
    }
371
372
1.53k
    if (!rwa)
373
0
        goto bad_mode;
374
375
1.53k
    if (self->readable && self->writable)
376
0
        flags |= O_RDWR;
377
1.53k
    else if (self->readable)
378
1.22k
        flags |= O_RDONLY;
379
304
    else
380
304
        flags |= O_WRONLY;
381
382
#ifdef O_BINARY
383
    flags |= O_BINARY;
384
#endif
385
386
#ifdef MS_WINDOWS
387
    flags |= O_NOINHERIT;
388
#elif defined(O_CLOEXEC)
389
1.53k
    flags |= O_CLOEXEC;
390
1.53k
#endif
391
392
1.53k
    if (PySys_Audit("open", "Osi", nameobj, mode, flags) < 0) {
393
0
        goto error;
394
0
    }
395
396
1.53k
    if (fd >= 0) {
397
326
        self->fd = fd;
398
326
        self->closefd = closefd;
399
326
    }
400
1.20k
    else {
401
1.20k
        self->closefd = 1;
402
1.20k
        if (!closefd) {
403
0
            PyErr_SetString(PyExc_ValueError,
404
0
                "Cannot use closefd=False with file name");
405
0
            goto error;
406
0
        }
407
408
1.20k
        errno = 0;
409
1.20k
        if (opener == Py_None) {
410
1.20k
            do {
411
1.20k
                Py_BEGIN_ALLOW_THREADS
412
#ifdef MS_WINDOWS
413
                self->fd = _wopen(widename, flags, 0666);
414
#else
415
1.20k
                self->fd = open(name, flags, 0666);
416
1.20k
#endif
417
1.20k
                Py_END_ALLOW_THREADS
418
1.20k
            } while (self->fd < 0 && errno == EINTR &&
419
0
                     !(async_err = PyErr_CheckSignals()));
420
421
1.20k
            if (async_err)
422
0
                goto error;
423
424
1.20k
            if (self->fd < 0) {
425
0
                PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
426
0
                goto error;
427
0
            }
428
1.20k
        }
429
0
        else {
430
0
            PyObject *fdobj;
431
432
0
#ifndef MS_WINDOWS
433
            /* the opener may clear the atomic flag */
434
0
            atomic_flag_works = NULL;
435
0
#endif
436
437
0
            fdobj = PyObject_CallFunction(opener, "Oi", nameobj, flags);
438
0
            if (fdobj == NULL)
439
0
                goto error;
440
0
            if (!PyLong_Check(fdobj)) {
441
0
                Py_DECREF(fdobj);
442
0
                PyErr_SetString(PyExc_TypeError,
443
0
                        "expected integer from opener");
444
0
                goto error;
445
0
            }
446
447
0
            self->fd = PyLong_AsInt(fdobj);
448
0
            Py_DECREF(fdobj);
449
0
            if (self->fd < 0) {
450
0
                if (!PyErr_Occurred()) {
451
                    /* The opener returned a negative but didn't set an
452
                       exception.  See issue #27066 */
453
0
                    PyErr_Format(PyExc_ValueError,
454
0
                                 "opener returned %d", self->fd);
455
0
                }
456
0
                goto error;
457
0
            }
458
0
        }
459
1.20k
        fd_is_own = 1;
460
461
1.20k
#ifndef MS_WINDOWS
462
1.20k
        if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0)
463
0
            goto error;
464
1.20k
#endif
465
1.20k
    }
466
467
1.53k
    PyMem_Free(self->stat_atopen);
468
1.53k
    self->stat_atopen = PyMem_New(struct _Py_stat_struct, 1);
469
1.53k
    if (self->stat_atopen == NULL) {
470
0
        PyErr_NoMemory();
471
0
        goto error;
472
0
    }
473
1.53k
    Py_BEGIN_ALLOW_THREADS
474
1.53k
    fstat_result = _Py_fstat_noraise(self->fd, self->stat_atopen);
475
1.53k
    Py_END_ALLOW_THREADS
476
1.53k
    if (fstat_result < 0) {
477
        /* Tolerate fstat() errors other than EBADF.  See Issue #25717, where
478
        an anonymous file on a Virtual Box shared folder filesystem would
479
        raise ENOENT. */
480
#ifdef MS_WINDOWS
481
        if (GetLastError() == ERROR_INVALID_HANDLE) {
482
            PyErr_SetFromWindowsErr(0);
483
#else
484
0
        if (errno == EBADF) {
485
0
            PyErr_SetFromErrno(PyExc_OSError);
486
0
#endif
487
0
            goto error;
488
0
        }
489
490
0
        PyMem_Free(self->stat_atopen);
491
0
        self->stat_atopen = NULL;
492
0
    }
493
1.53k
    else {
494
1.53k
#if defined(S_ISDIR) && defined(EISDIR)
495
        /* On Unix, open will succeed for directories.
496
           In Python, there should be no file objects referring to
497
           directories, so we need a check.  */
498
1.53k
        if (S_ISDIR(self->stat_atopen->st_mode)) {
499
0
            errno = EISDIR;
500
0
            PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
501
0
            goto error;
502
0
        }
503
1.53k
#endif /* defined(S_ISDIR) */
504
1.53k
    }
505
506
#if defined(MS_WINDOWS) || defined(__CYGWIN__)
507
    /* don't translate newlines (\r\n <=> \n) */
508
    _setmode(self->fd, O_BINARY);
509
#endif
510
511
1.53k
    if (PyObject_SetAttr((PyObject *)self, &_Py_ID(name), nameobj) < 0)
512
0
        goto error;
513
514
1.53k
    if (self->appending) {
515
        /* For consistent behaviour, we explicitly seek to the
516
           end of file (otherwise, it might be done only on the
517
           first write()). */
518
0
        PyObject *pos = portable_lseek(self, NULL, 2, true);
519
0
        if (pos == NULL)
520
0
            goto error;
521
0
        Py_DECREF(pos);
522
0
    }
523
524
1.53k
    goto done;
525
526
1.53k
 error:
527
0
    ret = -1;
528
0
    if (!fd_is_own)
529
0
        self->fd = -1;
530
0
    if (self->fd >= 0) {
531
0
        PyObject *exc = PyErr_GetRaisedException();
532
0
        internal_close(self);
533
0
        _PyErr_ChainExceptions1(exc);
534
0
    }
535
0
    PyMem_Free(self->stat_atopen);
536
0
    self->stat_atopen = NULL;
537
538
1.53k
 done:
539
#ifdef MS_WINDOWS
540
    PyMem_Free(widename);
541
#endif
542
1.53k
    Py_CLEAR(stringobj);
543
1.53k
    return ret;
544
0
}
545
546
static int
547
fileio_traverse(PyObject *op, visitproc visit, void *arg)
548
2.59k
{
549
2.59k
    fileio *self = PyFileIO_CAST(op);
550
2.59k
    Py_VISIT(Py_TYPE(self));
551
2.59k
    Py_VISIT(self->dict);
552
2.59k
    return 0;
553
2.59k
}
554
555
static int
556
fileio_clear(PyObject *op)
557
1.46k
{
558
1.46k
    fileio *self = PyFileIO_CAST(op);
559
1.46k
    Py_CLEAR(self->dict);
560
1.46k
    return 0;
561
1.46k
}
562
563
static void
564
fileio_dealloc(PyObject *op)
565
1.46k
{
566
1.46k
    fileio *self = PyFileIO_CAST(op);
567
1.46k
    self->finalizing = 1;
568
1.46k
    if (_PyIOBase_finalize(op) < 0) {
569
0
        return;
570
0
    }
571
572
1.46k
    _PyObject_GC_UNTRACK(self);
573
1.46k
    if (self->stat_atopen != NULL) {
574
0
        PyMem_Free(self->stat_atopen);
575
0
        self->stat_atopen = NULL;
576
0
    }
577
1.46k
    FT_CLEAR_WEAKREFS(op, self->weakreflist);
578
1.46k
    (void)fileio_clear(op);
579
580
1.46k
    PyTypeObject *tp = Py_TYPE(op);
581
1.46k
    tp->tp_free(op);
582
1.46k
    Py_DECREF(tp);
583
1.46k
}
584
585
static PyObject *
586
err_closed(void)
587
0
{
588
0
    PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
589
0
    return NULL;
590
0
}
591
592
static PyObject *
593
err_mode(_PyIO_State *state, const char *action)
594
0
{
595
0
    return PyErr_Format(state->unsupported_operation,
596
0
                        "File not open for %s", action);
597
0
}
598
599
/*[clinic input]
600
_io.FileIO.fileno
601
602
Return the underlying file descriptor (an integer).
603
[clinic start generated code]*/
604
605
static PyObject *
606
_io_FileIO_fileno_impl(fileio *self)
607
/*[clinic end generated code: output=a9626ce5398ece90 input=0b9b2de67335ada3]*/
608
0
{
609
0
    if (self->fd < 0)
610
0
        return err_closed();
611
0
    return PyLong_FromLong((long) self->fd);
612
0
}
613
614
/*[clinic input]
615
_io.FileIO.readable
616
617
True if file was opened in a read mode.
618
[clinic start generated code]*/
619
620
static PyObject *
621
_io_FileIO_readable_impl(fileio *self)
622
/*[clinic end generated code: output=640744a6150fe9ba input=a3fdfed6eea721c5]*/
623
1.25k
{
624
1.25k
    if (self->fd < 0)
625
0
        return err_closed();
626
1.25k
    return PyBool_FromLong((long) self->readable);
627
1.25k
}
628
629
/*[clinic input]
630
_io.FileIO.writable
631
632
True if file was opened in a write mode.
633
[clinic start generated code]*/
634
635
static PyObject *
636
_io_FileIO_writable_impl(fileio *self)
637
/*[clinic end generated code: output=96cefc5446e89977 input=c204a808ca2e1748]*/
638
88
{
639
88
    if (self->fd < 0)
640
0
        return err_closed();
641
88
    return PyBool_FromLong((long) self->writable);
642
88
}
643
644
/*[clinic input]
645
_io.FileIO.seekable
646
647
True if file supports random-access.
648
[clinic start generated code]*/
649
650
static PyObject *
651
_io_FileIO_seekable_impl(fileio *self)
652
/*[clinic end generated code: output=47909ca0a42e9287 input=c8e5554d2fd63c7f]*/
653
78
{
654
78
    if (self->fd < 0)
655
0
        return err_closed();
656
78
    if (self->seekable < 0) {
657
        /* portable_lseek() sets the seekable attribute */
658
0
        PyObject *pos = portable_lseek(self, NULL, SEEK_CUR, false);
659
0
        assert(self->seekable >= 0);
660
0
        if (pos == NULL) {
661
0
            PyErr_Clear();
662
0
        }
663
0
        else {
664
0
            Py_DECREF(pos);
665
0
        }
666
0
    }
667
78
    return PyBool_FromLong((long) self->seekable);
668
78
}
669
670
/*[clinic input]
671
_io.FileIO.readinto
672
    cls: defining_class
673
    buffer: Py_buffer(accept={rwbuffer})
674
    /
675
676
Same as RawIOBase.readinto().
677
[clinic start generated code]*/
678
679
static PyObject *
680
_io_FileIO_readinto_impl(fileio *self, PyTypeObject *cls, Py_buffer *buffer)
681
/*[clinic end generated code: output=97f0f3d69534db34 input=fd20323e18ce1ec8]*/
682
8
{
683
8
    Py_ssize_t n;
684
8
    int err;
685
686
8
    if (self->fd < 0)
687
0
        return err_closed();
688
8
    if (!self->readable) {
689
0
        _PyIO_State *state = get_io_state_by_cls(cls);
690
0
        return err_mode(state, "reading");
691
0
    }
692
693
8
    n = _Py_read(self->fd, buffer->buf, buffer->len);
694
    /* copy errno because PyBuffer_Release() can indirectly modify it */
695
8
    err = errno;
696
697
8
    if (n == -1) {
698
0
        if (err == EAGAIN) {
699
0
            PyErr_Clear();
700
0
            Py_RETURN_NONE;
701
0
        }
702
0
        return NULL;
703
0
    }
704
705
8
    return PyLong_FromSsize_t(n);
706
8
}
707
708
static size_t
709
new_buffersize(fileio *self, size_t currentsize)
710
0
{
711
0
    size_t addend;
712
713
    /* Expand the buffer by an amount proportional to the current size,
714
       giving us amortized linear-time behavior.  For bigger sizes, use a
715
       less-than-double growth factor to avoid excessive allocation. */
716
0
    assert(currentsize <= PY_SSIZE_T_MAX);
717
0
    if (currentsize > LARGE_BUFFER_CUTOFF_SIZE)
718
0
        addend = currentsize >> 3;
719
0
    else
720
0
        addend = 256 + currentsize;
721
0
    if (addend < SMALLCHUNK)
722
        /* Avoid tiny read() calls. */
723
0
        addend = SMALLCHUNK;
724
0
    return addend + currentsize;
725
0
}
726
727
/*[clinic input]
728
@permit_long_docstring_body
729
_io.FileIO.readall
730
731
Read all data from the file, returned as bytes.
732
733
Reads until either there is an error or read() returns size 0 (indicates EOF).
734
If the file is already at EOF, returns an empty bytes object.
735
736
In non-blocking mode, returns as much data as could be read before EAGAIN. If no
737
data is available (EAGAIN is returned before bytes are read) returns None.
738
[clinic start generated code]*/
739
740
static PyObject *
741
_io_FileIO_readall_impl(fileio *self)
742
/*[clinic end generated code: output=faa0292b213b4022 input=10d8b2ec403302dc]*/
743
1.20k
{
744
1.20k
    Py_off_t pos, end;
745
1.20k
    PyBytesWriter *writer;
746
1.20k
    Py_ssize_t bytes_read = 0;
747
1.20k
    Py_ssize_t n;
748
1.20k
    size_t bufsize;
749
750
1.20k
    if (self->fd < 0) {
751
0
        return err_closed();
752
0
    }
753
754
1.20k
    if (self->stat_atopen != NULL && self->stat_atopen->st_size < _PY_READ_MAX) {
755
1.20k
        end = (Py_off_t)self->stat_atopen->st_size;
756
1.20k
    }
757
0
    else {
758
0
        end = -1;
759
0
    }
760
1.20k
    if (end <= 0) {
761
        /* Use a default size and resize as needed. */
762
3
        bufsize = SMALLCHUNK;
763
3
    }
764
1.20k
    else {
765
        /* This is probably a real file. */
766
1.20k
        if (end > _PY_READ_MAX - 1) {
767
0
            bufsize = _PY_READ_MAX;
768
0
        }
769
1.20k
        else {
770
            /* In order to detect end of file, need a read() of at
771
               least 1 byte which returns size 0. Oversize the buffer
772
               by 1 byte so the I/O can be completed with two read()
773
               calls (one for all data, one for EOF) without needing
774
               to resize the buffer. */
775
1.20k
            bufsize = (size_t)end + 1;
776
1.20k
        }
777
778
        /* While a lot of code does open().read() to get the whole contents
779
           of a file it is possible a caller seeks/reads a ways into the file
780
           then calls readall() to get the rest, which would result in allocating
781
           more than required. Guard against that for larger files where we expect
782
           the I/O time to dominate anyways while keeping small files fast. */
783
1.20k
        if (bufsize > LARGE_BUFFER_CUTOFF_SIZE) {
784
81
            Py_BEGIN_ALLOW_THREADS
785
81
            _Py_BEGIN_SUPPRESS_IPH
786
#ifdef MS_WINDOWS
787
            pos = _lseeki64(self->fd, 0L, SEEK_CUR);
788
#else
789
81
            pos = lseek(self->fd, 0L, SEEK_CUR);
790
81
#endif
791
81
            _Py_END_SUPPRESS_IPH
792
81
            Py_END_ALLOW_THREADS
793
794
81
            if (end >= pos && pos >= 0 && (end - pos) < (_PY_READ_MAX - 1)) {
795
81
                bufsize = (size_t)(end - pos) + 1;
796
81
            }
797
81
        }
798
1.20k
    }
799
800
1.20k
    writer = PyBytesWriter_Create(bufsize);
801
1.20k
    if (writer == NULL) {
802
0
        return NULL;
803
0
    }
804
805
2.40k
    while (1) {
806
2.40k
        if (bytes_read >= (Py_ssize_t)bufsize) {
807
0
            bufsize = new_buffersize(self, bytes_read);
808
0
            if (bufsize > PY_SSIZE_T_MAX || bufsize <= 0) {
809
0
                PyErr_SetString(PyExc_OverflowError,
810
0
                                "unbounded read returned more bytes "
811
0
                                "than a Python bytes object can hold");
812
0
                PyBytesWriter_Discard(writer);
813
0
                return NULL;
814
0
            }
815
816
0
            if (PyBytesWriter_GetSize(writer) < (Py_ssize_t)bufsize) {
817
0
                if (PyBytesWriter_Resize(writer, bufsize) < 0)
818
0
                    return NULL;
819
0
            }
820
0
        }
821
822
2.40k
        n = _Py_read(self->fd,
823
2.40k
                     (char*)PyBytesWriter_GetData(writer) + bytes_read,
824
2.40k
                     bufsize - bytes_read);
825
826
2.40k
        if (n == 0)
827
1.20k
            break;
828
1.20k
        if (n == -1) {
829
0
            if (errno == EAGAIN) {
830
0
                PyErr_Clear();
831
0
                if (bytes_read > 0)
832
0
                    break;
833
0
                PyBytesWriter_Discard(writer);
834
0
                Py_RETURN_NONE;
835
0
            }
836
0
            PyBytesWriter_Discard(writer);
837
0
            return NULL;
838
0
        }
839
1.20k
        bytes_read += n;
840
1.20k
    }
841
842
1.20k
    return PyBytesWriter_FinishWithSize(writer, bytes_read);
843
1.20k
}
844
845
/*[clinic input]
846
@permit_long_docstring_body
847
_io.FileIO.read
848
    cls: defining_class
849
    size: Py_ssize_t(accept={int, NoneType}) = -1
850
    /
851
852
Read at most size bytes, returned as bytes.
853
854
If size is less than 0, read all bytes in the file making multiple read calls.
855
See ``FileIO.readall``.
856
857
Attempts to make only one system call, retrying only per PEP 475 (EINTR). This
858
means less data may be returned than requested.
859
860
In non-blocking mode, returns None if no data is available. Return an empty
861
bytes object at EOF.
862
[clinic start generated code]*/
863
864
static PyObject *
865
_io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size)
866
/*[clinic end generated code: output=bbd749c7c224143e input=752d1ad3db8564a5]*/
867
0
{
868
0
    if (self->fd < 0)
869
0
        return err_closed();
870
0
    if (!self->readable) {
871
0
        _PyIO_State *state = get_io_state_by_cls(cls);
872
0
        return err_mode(state, "reading");
873
0
    }
874
875
0
    if (size < 0)
876
0
        return _io_FileIO_readall_impl(self);
877
878
0
    if (size > _PY_READ_MAX) {
879
0
        size = _PY_READ_MAX;
880
0
    }
881
882
0
    PyBytesWriter *writer = PyBytesWriter_Create(size);
883
0
    if (writer == NULL) {
884
0
        return NULL;
885
0
    }
886
0
    char *ptr = PyBytesWriter_GetData(writer);
887
888
0
    Py_ssize_t n = _Py_read(self->fd, ptr, size);
889
0
    if (n == -1) {
890
        // copy errno because PyBytesWriter_Discard() can indirectly modify it
891
0
        int err = errno;
892
0
        PyBytesWriter_Discard(writer);
893
0
        if (err == EAGAIN) {
894
0
            PyErr_Clear();
895
0
            Py_RETURN_NONE;
896
0
        }
897
0
        return NULL;
898
0
    }
899
900
0
    return PyBytesWriter_FinishWithSize(writer, n);
901
0
}
902
903
/*[clinic input]
904
_io.FileIO.write
905
    cls: defining_class
906
    b: Py_buffer
907
    /
908
909
Write buffer b to file, return number of bytes written.
910
911
Only makes one system call, so not all of the data may be written.
912
The number of bytes actually written is returned.  In non-blocking mode,
913
returns None if the write would block.
914
[clinic start generated code]*/
915
916
static PyObject *
917
_io_FileIO_write_impl(fileio *self, PyTypeObject *cls, Py_buffer *b)
918
/*[clinic end generated code: output=927e25be80f3b77b input=2776314f043088f5]*/
919
260
{
920
260
    Py_ssize_t n;
921
260
    int err;
922
923
260
    if (self->fd < 0)
924
0
        return err_closed();
925
260
    if (!self->writable) {
926
0
        _PyIO_State *state = get_io_state_by_cls(cls);
927
0
        return err_mode(state, "writing");
928
0
    }
929
930
260
    n = _Py_write(self->fd, b->buf, b->len);
931
    /* copy errno because PyBuffer_Release() can indirectly modify it */
932
260
    err = errno;
933
934
260
    if (n < 0) {
935
0
        if (err == EAGAIN) {
936
0
            PyErr_Clear();
937
0
            Py_RETURN_NONE;
938
0
        }
939
0
        return NULL;
940
0
    }
941
942
260
    return PyLong_FromSsize_t(n);
943
260
}
944
945
/* XXX Windows support below is likely incomplete */
946
947
/* Cribbed from posix_lseek() */
948
static PyObject *
949
portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error)
950
1.33k
{
951
1.33k
    Py_off_t pos, res;
952
1.33k
    int fd = self->fd;
953
954
1.33k
#ifdef SEEK_SET
955
    /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
956
1.33k
    switch (whence) {
957
#if SEEK_SET != 0
958
    case 0: whence = SEEK_SET; break;
959
#endif
960
#if SEEK_CUR != 1
961
    case 1: whence = SEEK_CUR; break;
962
#endif
963
#if SEEK_END != 2
964
    case 2: whence = SEEK_END; break;
965
#endif
966
1.33k
    }
967
1.33k
#endif /* SEEK_SET */
968
969
1.33k
    if (posobj == NULL) {
970
1.32k
        pos = 0;
971
1.32k
    }
972
12
    else {
973
#if defined(HAVE_LARGEFILE_SUPPORT)
974
        pos = PyLong_AsLongLong(posobj);
975
#else
976
12
        pos = PyLong_AsLong(posobj);
977
12
#endif
978
12
        if (PyErr_Occurred())
979
0
            return NULL;
980
12
    }
981
982
1.33k
    Py_BEGIN_ALLOW_THREADS
983
1.33k
    _Py_BEGIN_SUPPRESS_IPH
984
#ifdef MS_WINDOWS
985
    res = _lseeki64(fd, pos, whence);
986
#else
987
1.33k
    res = lseek(fd, pos, whence);
988
1.33k
#endif
989
1.33k
    _Py_END_SUPPRESS_IPH
990
1.33k
    Py_END_ALLOW_THREADS
991
992
1.33k
    if (self->seekable < 0) {
993
1.27k
        self->seekable = (res >= 0);
994
1.27k
    }
995
996
1.33k
    if (res < 0) {
997
0
        if (suppress_pipe_error && errno == ESPIPE) {
998
0
            res = 0;
999
0
        } else {
1000
0
            return PyErr_SetFromErrno(PyExc_OSError);
1001
0
        }
1002
0
    }
1003
1004
#if defined(HAVE_LARGEFILE_SUPPORT)
1005
    return PyLong_FromLongLong(res);
1006
#else
1007
1.33k
    return PyLong_FromLong(res);
1008
1.33k
#endif
1009
1.33k
}
1010
1011
/*[clinic input]
1012
@permit_long_docstring_body
1013
_io.FileIO.seek
1014
    pos: object
1015
    whence: int = 0
1016
    /
1017
1018
Move to new file position and return the file position.
1019
1020
Argument offset is a byte count.  Optional argument whence defaults to
1021
SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values
1022
are SEEK_CUR or 1 (move relative to current position, positive or negative),
1023
and SEEK_END or 2 (move relative to end of file, usually negative, although
1024
many platforms allow seeking beyond the end of a file).
1025
1026
Note that not all file objects are seekable.
1027
[clinic start generated code]*/
1028
1029
static PyObject *
1030
_io_FileIO_seek_impl(fileio *self, PyObject *pos, int whence)
1031
/*[clinic end generated code: output=c976acdf054e6655 input=f077c492a84c9e62]*/
1032
12
{
1033
12
    if (self->fd < 0)
1034
0
        return err_closed();
1035
1036
12
    return portable_lseek(self, pos, whence, false);
1037
12
}
1038
1039
/*[clinic input]
1040
_io.FileIO.tell
1041
1042
Current file position.
1043
1044
Can raise OSError for non seekable files.
1045
[clinic start generated code]*/
1046
1047
static PyObject *
1048
_io_FileIO_tell_impl(fileio *self)
1049
/*[clinic end generated code: output=ffe2147058809d0b input=807e24ead4cec2f9]*/
1050
1.32k
{
1051
1.32k
    if (self->fd < 0)
1052
0
        return err_closed();
1053
1054
1.32k
    return portable_lseek(self, NULL, 1, false);
1055
1.32k
}
1056
1057
#ifdef HAVE_FTRUNCATE
1058
/*[clinic input]
1059
_io.FileIO.truncate
1060
    cls: defining_class
1061
    size as posobj: object = None
1062
    /
1063
1064
Truncate the file to at most size bytes and return the truncated size.
1065
1066
Size defaults to the current file position, as returned by tell().
1067
The current file position is changed to the value of size.
1068
[clinic start generated code]*/
1069
1070
static PyObject *
1071
_io_FileIO_truncate_impl(fileio *self, PyTypeObject *cls, PyObject *posobj)
1072
/*[clinic end generated code: output=d936732a49e8d5a2 input=c367fb45d6bb2c18]*/
1073
0
{
1074
0
    Py_off_t pos;
1075
0
    int ret;
1076
0
    int fd;
1077
1078
0
    fd = self->fd;
1079
0
    if (fd < 0)
1080
0
        return err_closed();
1081
0
    if (!self->writable) {
1082
0
        _PyIO_State *state = get_io_state_by_cls(cls);
1083
0
        return err_mode(state, "writing");
1084
0
    }
1085
1086
0
    if (posobj == Py_None) {
1087
        /* Get the current position. */
1088
0
        posobj = portable_lseek(self, NULL, 1, false);
1089
0
        if (posobj == NULL)
1090
0
            return NULL;
1091
0
    }
1092
0
    else {
1093
0
        Py_INCREF(posobj);
1094
0
    }
1095
1096
#if defined(HAVE_LARGEFILE_SUPPORT)
1097
    pos = PyLong_AsLongLong(posobj);
1098
#else
1099
0
    pos = PyLong_AsLong(posobj);
1100
0
#endif
1101
0
    if (PyErr_Occurred()){
1102
0
        Py_DECREF(posobj);
1103
0
        return NULL;
1104
0
    }
1105
1106
0
    Py_BEGIN_ALLOW_THREADS
1107
0
    _Py_BEGIN_SUPPRESS_IPH
1108
0
    errno = 0;
1109
#ifdef MS_WINDOWS
1110
    ret = _chsize_s(fd, pos);
1111
#else
1112
0
    ret = ftruncate(fd, pos);
1113
0
#endif
1114
0
    _Py_END_SUPPRESS_IPH
1115
0
    Py_END_ALLOW_THREADS
1116
1117
0
    if (ret != 0) {
1118
0
        PyErr_SetFromErrno(PyExc_OSError);
1119
0
        Py_DECREF(posobj);
1120
0
        return NULL;
1121
0
    }
1122
1123
    /* Since the file was truncated, its size at open is no longer accurate
1124
       as an estimate. Clear out the stat result, and rely on dynamic resize
1125
       code if a readall is requested. */
1126
0
    if (self->stat_atopen != NULL) {
1127
0
        PyMem_Free(self->stat_atopen);
1128
0
        self->stat_atopen = NULL;
1129
0
    }
1130
1131
0
    return posobj;
1132
0
}
1133
#endif /* HAVE_FTRUNCATE */
1134
1135
static const char *
1136
mode_string(fileio *self)
1137
0
{
1138
0
    if (self->created) {
1139
0
        if (self->readable)
1140
0
            return "xb+";
1141
0
        else
1142
0
            return "xb";
1143
0
    }
1144
0
    if (self->appending) {
1145
0
        if (self->readable)
1146
0
            return "ab+";
1147
0
        else
1148
0
            return "ab";
1149
0
    }
1150
0
    else if (self->readable) {
1151
0
        if (self->writable) {
1152
0
            if (self->truncate) {
1153
0
                return "wb+";
1154
0
            }
1155
0
            else {
1156
0
                return "rb+";
1157
0
            }
1158
0
        }
1159
0
        else {
1160
0
            return "rb";
1161
0
        }
1162
0
    }
1163
0
    else
1164
0
        return "wb";
1165
0
}
1166
1167
static PyObject *
1168
fileio_repr(PyObject *op)
1169
0
{
1170
0
    fileio *self = PyFileIO_CAST(op);
1171
0
    const char *type_name = Py_TYPE(self)->tp_name;
1172
1173
0
    if (self->fd < 0) {
1174
0
        return PyUnicode_FromFormat("<%.100s [closed]>", type_name);
1175
0
    }
1176
1177
0
    PyObject *nameobj;
1178
0
    if (PyObject_GetOptionalAttr((PyObject *) self, &_Py_ID(name), &nameobj) < 0) {
1179
0
        return NULL;
1180
0
    }
1181
0
    PyObject *res;
1182
0
    if (nameobj == NULL) {
1183
0
        res = PyUnicode_FromFormat(
1184
0
            "<%.100s fd=%d mode='%s' closefd=%s>",
1185
0
            type_name, self->fd, mode_string(self), self->closefd ? "True" : "False");
1186
0
    }
1187
0
    else {
1188
0
        int status = Py_ReprEnter((PyObject *)self);
1189
0
        res = NULL;
1190
0
        if (status == 0) {
1191
0
            res = PyUnicode_FromFormat(
1192
0
                "<%.100s name=%R mode='%s' closefd=%s>",
1193
0
                type_name, nameobj, mode_string(self), self->closefd ? "True" : "False");
1194
0
            Py_ReprLeave((PyObject *)self);
1195
0
        }
1196
0
        else if (status > 0) {
1197
0
            PyErr_Format(PyExc_RuntimeError,
1198
0
                         "reentrant call inside %.100s.__repr__", type_name);
1199
0
        }
1200
0
        Py_DECREF(nameobj);
1201
0
    }
1202
0
    return res;
1203
0
}
1204
1205
/*[clinic input]
1206
_io.FileIO.isatty
1207
1208
True if the file is connected to a TTY device.
1209
[clinic start generated code]*/
1210
1211
static PyObject *
1212
_io_FileIO_isatty_impl(fileio *self)
1213
/*[clinic end generated code: output=932c39924e9a8070 input=cd94ca1f5e95e843]*/
1214
88
{
1215
88
    long res;
1216
1217
88
    if (self->fd < 0)
1218
0
        return err_closed();
1219
88
    Py_BEGIN_ALLOW_THREADS
1220
88
    _Py_BEGIN_SUPPRESS_IPH
1221
88
    res = isatty(self->fd);
1222
88
    _Py_END_SUPPRESS_IPH
1223
88
    Py_END_ALLOW_THREADS
1224
88
    return PyBool_FromLong(res);
1225
88
}
1226
1227
/* Checks whether the file is a TTY using an open-only optimization.
1228
1229
   TTYs are always character devices. If the interpreter knows a file is
1230
   not a character device when it would call ``isatty``, can skip that
1231
   call. Inside ``open()``  there is a fresh stat result that contains that
1232
   information. Use the stat result to skip a system call. Outside of that
1233
   context TOCTOU issues (the fd could be arbitrarily modified by
1234
   surrounding code). */
1235
static PyObject *
1236
_io_FileIO_isatty_open_only(PyObject *op, PyObject *Py_UNUSED(dummy))
1237
1.27k
{
1238
1.27k
    fileio *self = PyFileIO_CAST(op);
1239
1.27k
    if (self->stat_atopen != NULL && !S_ISCHR(self->stat_atopen->st_mode)) {
1240
1.25k
        Py_RETURN_FALSE;
1241
1.25k
    }
1242
22
    return _io_FileIO_isatty_impl(self);
1243
1.27k
}
1244
1245
#include "clinic/fileio.c.h"
1246
1247
static PyMethodDef fileio_methods[] = {
1248
    _IO_FILEIO_READ_METHODDEF
1249
    _IO_FILEIO_READALL_METHODDEF
1250
    _IO_FILEIO_READINTO_METHODDEF
1251
    _IO_FILEIO_WRITE_METHODDEF
1252
    _IO_FILEIO_SEEK_METHODDEF
1253
    _IO_FILEIO_TELL_METHODDEF
1254
    _IO_FILEIO_TRUNCATE_METHODDEF
1255
    _IO_FILEIO_CLOSE_METHODDEF
1256
    _IO_FILEIO_SEEKABLE_METHODDEF
1257
    _IO_FILEIO_READABLE_METHODDEF
1258
    _IO_FILEIO_WRITABLE_METHODDEF
1259
    _IO_FILEIO_FILENO_METHODDEF
1260
    _IO_FILEIO_ISATTY_METHODDEF
1261
    {"_isatty_open_only", _io_FileIO_isatty_open_only, METH_NOARGS},
1262
    {"_dealloc_warn", fileio_dealloc_warn, METH_O, NULL},
1263
    {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
1264
    {NULL,           NULL}             /* sentinel */
1265
};
1266
1267
/* 'closed' and 'mode' are attributes for backwards compatibility reasons. */
1268
1269
static PyObject *
1270
fileio_get_closed(PyObject *op, void *closure)
1271
4.14k
{
1272
4.14k
    fileio *self = PyFileIO_CAST(op);
1273
4.14k
    return PyBool_FromLong((long)(self->fd < 0));
1274
4.14k
}
1275
1276
static PyObject *
1277
fileio_get_closefd(PyObject *op, void *closure)
1278
0
{
1279
0
    fileio *self = PyFileIO_CAST(op);
1280
0
    return PyBool_FromLong((long)(self->closefd));
1281
0
}
1282
1283
static PyObject *
1284
fileio_get_mode(PyObject *op, void *closure)
1285
0
{
1286
0
    fileio *self = PyFileIO_CAST(op);
1287
0
    return PyUnicode_FromString(mode_string(self));
1288
0
}
1289
1290
static PyObject *
1291
fileio_get_blksize(PyObject *op, void *closure)
1292
1.27k
{
1293
1.27k
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1294
1.27k
    fileio *self = PyFileIO_CAST(op);
1295
1.27k
    if (self->stat_atopen != NULL && self->stat_atopen->st_blksize > 1) {
1296
1.27k
        return PyLong_FromLong(self->stat_atopen->st_blksize);
1297
1.27k
    }
1298
0
#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1299
0
    return PyLong_FromLong(DEFAULT_BUFFER_SIZE);
1300
1.27k
}
1301
1302
static PyGetSetDef fileio_getsetlist[] = {
1303
    {"closed", fileio_get_closed, NULL, "True if the file is closed"},
1304
    {"closefd", fileio_get_closefd, NULL,
1305
        "True if the file descriptor will be closed by close()."},
1306
    {"mode", fileio_get_mode, NULL, "String giving the file mode"},
1307
    {"_blksize", fileio_get_blksize, NULL, "Stat st_blksize if available"},
1308
    {NULL},
1309
};
1310
1311
static PyMemberDef fileio_members[] = {
1312
    {"_finalizing", Py_T_BOOL, offsetof(fileio, finalizing), 0},
1313
    {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(fileio, weakreflist), Py_READONLY},
1314
    {"__dictoffset__", Py_T_PYSSIZET, offsetof(fileio, dict), Py_READONLY},
1315
    {NULL}
1316
};
1317
1318
static PyType_Slot fileio_slots[] = {
1319
    {Py_tp_dealloc, fileio_dealloc},
1320
    {Py_tp_repr, fileio_repr},
1321
    {Py_tp_doc, (void *)_io_FileIO___init____doc__},
1322
    {Py_tp_traverse, fileio_traverse},
1323
    {Py_tp_clear, fileio_clear},
1324
    {Py_tp_methods, fileio_methods},
1325
    {Py_tp_members, fileio_members},
1326
    {Py_tp_getset, fileio_getsetlist},
1327
    {Py_tp_init, _io_FileIO___init__},
1328
    {Py_tp_new, fileio_new},
1329
    {0, NULL},
1330
};
1331
1332
PyType_Spec fileio_spec = {
1333
    .name = "_io.FileIO",
1334
    .basicsize = sizeof(fileio),
1335
    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
1336
              Py_TPFLAGS_IMMUTABLETYPE),
1337
    .slots = fileio_slots,
1338
};