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