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