Coverage Report

Created: 2026-06-21 06:15

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