1 /* Author: Daniel Stutzbach */
2 
3 #define PY_SSIZE_T_CLEAN
4 #include "Python.h"
5 #ifdef HAVE_SYS_TYPES_H
6 #include <sys/types.h>
7 #endif
8 #ifdef HAVE_SYS_STAT_H
9 #include <sys/stat.h>
10 #endif
11 #ifdef HAVE_IO_H
12 #include <io.h>
13 #endif
14 #ifdef HAVE_FCNTL_H
15 #include <fcntl.h>
16 #endif
17 #include <stddef.h> /* For offsetof */
18 #include "_iomodule.h"
19 
20 /*
21  * Known likely problems:
22  *
23  * - Files larger then 2**32-1
24  * - Files with unicode filenames
25  * - Passing numbers greater than 2**32-1 when an integer is expected
26  * - Making it work on Windows and other oddball platforms
27  *
28  * To Do:
29  *
30  * - autoconfify header file inclusion
31  */
32 
33 #ifdef MS_WINDOWS
34 /* can simulate truncate with Win32 API functions; see file_truncate */
35 #define HAVE_FTRUNCATE
36 #define WIN32_LEAN_AND_MEAN
37 #include <windows.h>
38 #endif
39 
40 #if BUFSIZ < (8*1024)
41 #define SMALLCHUNK (8*1024)
42 #elif (BUFSIZ >= (2 << 25))
43 #error "unreasonable BUFSIZ > 64MB defined"
44 #else
45 #define SMALLCHUNK BUFSIZ
46 #endif
47 
48 typedef struct {
49     PyObject_HEAD
50     int fd;
51     unsigned int readable : 1;
52     unsigned int writable : 1;
53     unsigned int appending : 1;
54     signed int seekable : 2; /* -1 means unknown */
55     unsigned int closefd : 1;
56     PyObject *weakreflist;
57     PyObject *dict;
58 } fileio;
59 
60 PyTypeObject PyFileIO_Type;
61 
62 #define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type))
63 
64 int
_PyFileIO_closed(PyObject * self)65 _PyFileIO_closed(PyObject *self)
66 {
67     return ((fileio *)self)->fd < 0;
68 }
69 
70 static PyObject *
71 portable_lseek(int fd, PyObject *posobj, int whence);
72 
73 static PyObject *portable_lseek(int fd, PyObject *posobj, int whence);
74 
75 /* Returns 0 on success, -1 with exception set on failure. */
76 static int
internal_close(fileio * self)77 internal_close(fileio *self)
78 {
79     int err = 0;
80     int save_errno = 0;
81     if (self->fd >= 0) {
82         int fd = self->fd;
83         self->fd = -1;
84         /* fd is accessible and someone else may have closed it */
85         if (_PyVerify_fd(fd)) {
86             Py_BEGIN_ALLOW_THREADS
87             err = close(fd);
88             if (err < 0)
89                 save_errno = errno;
90             Py_END_ALLOW_THREADS
91         } else {
92             save_errno = errno;
93             err = -1;
94         }
95     }
96     if (err < 0) {
97         errno = save_errno;
98         PyErr_SetFromErrno(PyExc_IOError);
99         return -1;
100     }
101     return 0;
102 }
103 
104 static PyObject *
fileio_close(fileio * self)105 fileio_close(fileio *self)
106 {
107     PyObject *res;
108     res = PyObject_CallMethod((PyObject*)&PyRawIOBase_Type,
109                               "close", "O", self);
110     if (!self->closefd) {
111         self->fd = -1;
112         return res;
113     }
114     if (internal_close(self) < 0)
115         Py_CLEAR(res);
116     return res;
117 }
118 
119 static PyObject *
fileio_new(PyTypeObject * type,PyObject * args,PyObject * kwds)120 fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
121 {
122     fileio *self;
123 
124     assert(type != NULL && type->tp_alloc != NULL);
125 
126     self = (fileio *) type->tp_alloc(type, 0);
127     if (self != NULL) {
128         self->fd = -1;
129         self->readable = 0;
130         self->writable = 0;
131         self->appending = 0;
132         self->seekable = -1;
133         self->closefd = 1;
134         self->weakreflist = NULL;
135     }
136 
137     return (PyObject *) self;
138 }
139 
140 /* On Unix, open will succeed for directories.
141    In Python, there should be no file objects referring to
142    directories, so we need a check.  */
143 
144 static int
dircheck(fileio * self,PyObject * nameobj)145 dircheck(fileio* self, PyObject *nameobj)
146 {
147 #if defined(HAVE_FSTAT) && defined(S_IFDIR) && defined(EISDIR)
148     struct stat buf;
149     int res;
150     if (self->fd < 0)
151         return 0;
152 
153     Py_BEGIN_ALLOW_THREADS
154     res = fstat(self->fd, &buf);
155     Py_END_ALLOW_THREADS
156 
157     if (res == 0 && S_ISDIR(buf.st_mode)) {
158         errno = EISDIR;
159         PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj);
160         return -1;
161     }
162 #endif
163     return 0;
164 }
165 
166 static int
check_fd(int fd)167 check_fd(int fd)
168 {
169 #if defined(HAVE_FSTAT)
170     struct stat buf;
171     int res;
172     PyObject *exc;
173     char *msg;
174 
175     if (!_PyVerify_fd(fd)) {
176         goto badfd;
177     }
178 
179     Py_BEGIN_ALLOW_THREADS
180     res = fstat(fd, &buf);
181     Py_END_ALLOW_THREADS
182 
183     if (res < 0 && errno == EBADF) {
184         goto badfd;
185     }
186 
187     return 0;
188 
189 badfd:
190     msg = strerror(EBADF);
191     exc = PyObject_CallFunction(PyExc_OSError, "(is)",
192                                 EBADF, msg);
193     PyErr_SetObject(PyExc_OSError, exc);
194     Py_XDECREF(exc);
195     return -1;
196 #else
197     return 0;
198 #endif
199 }
200 
201 
202 static int
fileio_init(PyObject * oself,PyObject * args,PyObject * kwds)203 fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
204 {
205     fileio *self = (fileio *) oself;
206     static char *kwlist[] = {"file", "mode", "closefd", NULL};
207     const char *name = NULL;
208     PyObject *nameobj, *stringobj = NULL;
209     char *mode = "r";
210     char *s;
211 #ifdef MS_WINDOWS
212     Py_UNICODE *widename = NULL;
213 #endif
214     int ret = 0;
215     int rwa = 0, plus = 0;
216     int flags = 0;
217     int fd = -1;
218     int closefd = 1;
219     int fd_is_own = 0;
220 
221     assert(PyFileIO_Check(oself));
222     if (self->fd >= 0) {
223         if (self->closefd) {
224             /* Have to close the existing file first. */
225             if (internal_close(self) < 0)
226                 return -1;
227         }
228         else
229             self->fd = -1;
230     }
231 
232     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|si:fileio",
233                                      kwlist, &nameobj, &mode, &closefd))
234         return -1;
235 
236     if (PyFloat_Check(nameobj)) {
237         PyErr_SetString(PyExc_TypeError,
238                         "integer argument expected, got float");
239         return -1;
240     }
241 
242     fd = _PyLong_AsInt(nameobj);
243     if (fd < 0) {
244         if (!PyErr_Occurred()) {
245             PyErr_SetString(PyExc_ValueError,
246                             "negative file descriptor");
247             return -1;
248         }
249         PyErr_Clear();
250     }
251 
252 #ifdef MS_WINDOWS
253     if (PyUnicode_Check(nameobj)) {
254         widename = PyUnicode_AS_UNICODE(nameobj);
255         if (wcslen(widename) != (size_t)PyUnicode_GET_SIZE(nameobj)) {
256             PyErr_SetString(PyExc_TypeError, "embedded NUL character");
257             return -1;
258         }
259     }
260     if (widename == NULL)
261 #endif
262     if (fd < 0)
263     {
264         if (PyBytes_Check(nameobj) || PyByteArray_Check(nameobj)) {
265             Py_ssize_t namelen;
266             if (PyObject_AsCharBuffer(nameobj, &name, &namelen) < 0)
267                 return -1;
268             if (strlen(name) != (size_t)namelen) {
269                 PyErr_SetString(PyExc_TypeError, "embedded NUL character");
270                 return -1;
271             }
272         }
273         else {
274             PyObject *u = PyUnicode_FromObject(nameobj);
275 
276             if (u == NULL)
277                 return -1;
278 
279             stringobj = PyUnicode_AsEncodedString(
280                 u, Py_FileSystemDefaultEncoding, NULL);
281             Py_DECREF(u);
282             if (stringobj == NULL)
283                 return -1;
284             if (!PyBytes_Check(stringobj)) {
285                 PyErr_SetString(PyExc_TypeError,
286                                 "encoder failed to return bytes");
287                 goto error;
288             }
289             name = PyBytes_AS_STRING(stringobj);
290             if (strlen(name) != (size_t)PyBytes_GET_SIZE(stringobj)) {
291                 PyErr_SetString(PyExc_TypeError, "embedded NUL character");
292                 goto error;
293             }
294         }
295     }
296 
297     s = mode;
298     while (*s) {
299         switch (*s++) {
300         case 'r':
301             if (rwa) {
302             bad_mode:
303                 PyErr_SetString(PyExc_ValueError,
304                                 "Must have exactly one of read/write/append "
305                                 "mode and at most one plus");
306                 goto error;
307             }
308             rwa = 1;
309             self->readable = 1;
310             break;
311         case 'w':
312             if (rwa)
313                 goto bad_mode;
314             rwa = 1;
315             self->writable = 1;
316             flags |= O_CREAT | O_TRUNC;
317             break;
318         case 'a':
319             if (rwa)
320                 goto bad_mode;
321             rwa = 1;
322             self->writable = 1;
323             self->appending = 1;
324             flags |= O_APPEND | O_CREAT;
325             break;
326         case 'b':
327             break;
328         case '+':
329             if (plus)
330                 goto bad_mode;
331             self->readable = self->writable = 1;
332             plus = 1;
333             break;
334         default:
335             PyErr_Format(PyExc_ValueError,
336                          "invalid mode: %.200s", mode);
337             goto error;
338         }
339     }
340 
341     if (!rwa)
342         goto bad_mode;
343 
344     if (self->readable && self->writable)
345         flags |= O_RDWR;
346     else if (self->readable)
347         flags |= O_RDONLY;
348     else
349         flags |= O_WRONLY;
350 
351 #ifdef O_BINARY
352     flags |= O_BINARY;
353 #endif
354 
355     if (fd >= 0) {
356         if (check_fd(fd))
357             goto error;
358         self->fd = fd;
359         self->closefd = closefd;
360     }
361     else {
362         self->closefd = 1;
363         if (!closefd) {
364             PyErr_SetString(PyExc_ValueError,
365                 "Cannot use closefd=False with file name");
366             goto error;
367         }
368 
369         Py_BEGIN_ALLOW_THREADS
370         errno = 0;
371 #ifdef MS_WINDOWS
372         if (widename != NULL)
373             self->fd = _wopen(widename, flags, 0666);
374         else
375 #endif
376             self->fd = open(name, flags, 0666);
377         Py_END_ALLOW_THREADS
378         fd_is_own = 1;
379         if (self->fd < 0) {
380 #ifdef MS_WINDOWS
381             if (widename != NULL)
382                 PyErr_SetFromErrnoWithUnicodeFilename(PyExc_IOError, widename);
383             else
384 #endif
385                 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
386             goto error;
387         }
388     }
389     if (dircheck(self, nameobj) < 0)
390         goto error;
391 
392     if (PyObject_SetAttrString((PyObject *)self, "name", nameobj) < 0)
393         goto error;
394 
395     if (self->appending) {
396         /* For consistent behaviour, we explicitly seek to the
397            end of file (otherwise, it might be done only on the
398            first write()). */
399         PyObject *pos = portable_lseek(self->fd, NULL, 2);
400         if (pos == NULL)
401             goto error;
402         Py_DECREF(pos);
403     }
404 
405     goto done;
406 
407  error:
408     if (!fd_is_own)
409         self->fd = -1;
410 
411     ret = -1;
412 
413  done:
414     Py_CLEAR(stringobj);
415     return ret;
416 }
417 
418 static int
fileio_traverse(fileio * self,visitproc visit,void * arg)419 fileio_traverse(fileio *self, visitproc visit, void *arg)
420 {
421     Py_VISIT(self->dict);
422     return 0;
423 }
424 
425 static int
fileio_clear(fileio * self)426 fileio_clear(fileio *self)
427 {
428     Py_CLEAR(self->dict);
429     return 0;
430 }
431 
432 static void
fileio_dealloc(fileio * self)433 fileio_dealloc(fileio *self)
434 {
435     if (_PyIOBase_finalize((PyObject *) self) < 0)
436         return;
437     _PyObject_GC_UNTRACK(self);
438     if (self->weakreflist != NULL)
439         PyObject_ClearWeakRefs((PyObject *) self);
440     Py_CLEAR(self->dict);
441     Py_TYPE(self)->tp_free((PyObject *)self);
442 }
443 
444 static PyObject *
err_closed(void)445 err_closed(void)
446 {
447     PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
448     return NULL;
449 }
450 
451 static PyObject *
err_mode(char * action)452 err_mode(char *action)
453 {
454     PyErr_Format(PyExc_ValueError, "File not open for %s", action);
455     return NULL;
456 }
457 
458 static PyObject *
fileio_fileno(fileio * self)459 fileio_fileno(fileio *self)
460 {
461     if (self->fd < 0)
462         return err_closed();
463     return PyInt_FromLong((long) self->fd);
464 }
465 
466 static PyObject *
fileio_readable(fileio * self)467 fileio_readable(fileio *self)
468 {
469     if (self->fd < 0)
470         return err_closed();
471     return PyBool_FromLong((long) self->readable);
472 }
473 
474 static PyObject *
fileio_writable(fileio * self)475 fileio_writable(fileio *self)
476 {
477     if (self->fd < 0)
478         return err_closed();
479     return PyBool_FromLong((long) self->writable);
480 }
481 
482 static PyObject *
fileio_seekable(fileio * self)483 fileio_seekable(fileio *self)
484 {
485     if (self->fd < 0)
486         return err_closed();
487     if (self->seekable < 0) {
488         PyObject *pos = portable_lseek(self->fd, NULL, SEEK_CUR);
489         if (pos == NULL) {
490             PyErr_Clear();
491             self->seekable = 0;
492         } else {
493             Py_DECREF(pos);
494             self->seekable = 1;
495         }
496     }
497     return PyBool_FromLong((long) self->seekable);
498 }
499 
500 static PyObject *
fileio_readinto(fileio * self,PyObject * args)501 fileio_readinto(fileio *self, PyObject *args)
502 {
503     Py_buffer pbuf;
504     Py_ssize_t n, len;
505 
506     if (self->fd < 0)
507         return err_closed();
508     if (!self->readable)
509         return err_mode("reading");
510 
511     if (!PyArg_ParseTuple(args, "w*", &pbuf))
512         return NULL;
513 
514     if (_PyVerify_fd(self->fd)) {
515         len = pbuf.len;
516         Py_BEGIN_ALLOW_THREADS
517         errno = 0;
518 #if defined(MS_WIN64) || defined(MS_WINDOWS)
519         if (len > INT_MAX)
520             len = INT_MAX;
521         n = read(self->fd, pbuf.buf, (int)len);
522 #else
523         n = read(self->fd, pbuf.buf, len);
524 #endif
525         Py_END_ALLOW_THREADS
526     } else
527         n = -1;
528     PyBuffer_Release(&pbuf);
529     if (n < 0) {
530         if (errno == EAGAIN)
531             Py_RETURN_NONE;
532         PyErr_SetFromErrno(PyExc_IOError);
533         return NULL;
534     }
535 
536     return PyLong_FromSsize_t(n);
537 }
538 
539 static size_t
new_buffersize(fileio * self,size_t currentsize)540 new_buffersize(fileio *self, size_t currentsize)
541 {
542 #ifdef HAVE_FSTAT
543     off_t pos, end;
544     struct stat st;
545     int res;
546 
547     Py_BEGIN_ALLOW_THREADS
548     res = fstat(self->fd, &st);
549     Py_END_ALLOW_THREADS
550 
551     if (res == 0) {
552         end = st.st_size;
553 
554         Py_BEGIN_ALLOW_THREADS
555         pos = lseek(self->fd, 0L, SEEK_CUR);
556         Py_END_ALLOW_THREADS
557 
558         /* Files claiming a size smaller than SMALLCHUNK may
559            actually be streaming pseudo-files. In this case, we
560            apply the more aggressive algorithm below.
561         */
562         if (end >= SMALLCHUNK && end >= pos && pos >= 0) {
563             /* Add 1 so if the file were to grow we'd notice. */
564             return currentsize + end - pos + 1;
565         }
566     }
567 #endif
568     /* Expand the buffer by an amount proportional to the current size,
569        giving us amortized linear-time behavior. Use a less-than-double
570        growth factor to avoid excessive allocation. */
571     return currentsize + (currentsize >> 3) + 6;
572 }
573 
574 static PyObject *
fileio_readall(fileio * self)575 fileio_readall(fileio *self)
576 {
577     PyObject *result;
578     Py_ssize_t total = 0;
579     Py_ssize_t n;
580 
581     if (self->fd < 0)
582         return err_closed();
583     if (!_PyVerify_fd(self->fd))
584         return PyErr_SetFromErrno(PyExc_IOError);
585 
586     result = PyBytes_FromStringAndSize(NULL, SMALLCHUNK);
587     if (result == NULL)
588         return NULL;
589 
590     while (1) {
591         size_t newsize = new_buffersize(self, total);
592         if (newsize > PY_SSIZE_T_MAX || newsize <= 0) {
593             PyErr_SetString(PyExc_OverflowError,
594                 "unbounded read returned more bytes "
595                 "than a Python string can hold ");
596             Py_DECREF(result);
597             return NULL;
598         }
599 
600         if (PyBytes_GET_SIZE(result) < (Py_ssize_t)newsize) {
601             if (_PyBytes_Resize(&result, newsize) < 0)
602                 return NULL; /* result has been freed */
603         }
604         Py_BEGIN_ALLOW_THREADS
605         errno = 0;
606         n = newsize - total;
607 #if defined(MS_WIN64) || defined(MS_WINDOWS)
608         if (n > INT_MAX)
609             n = INT_MAX;
610         n = read(self->fd,
611                  PyBytes_AS_STRING(result) + total,
612                  (int)n);
613 #else
614         n = read(self->fd,
615                  PyBytes_AS_STRING(result) + total,
616                  n);
617 #endif
618         Py_END_ALLOW_THREADS
619         if (n == 0)
620             break;
621         if (n < 0) {
622             if (errno == EINTR) {
623                 if (PyErr_CheckSignals()) {
624                     Py_DECREF(result);
625                     return NULL;
626                 }
627                 continue;
628             }
629             if (errno == EAGAIN) {
630                 if (total > 0)
631                     break;
632                 Py_DECREF(result);
633                 Py_RETURN_NONE;
634             }
635             Py_DECREF(result);
636             PyErr_SetFromErrno(PyExc_IOError);
637             return NULL;
638         }
639         total += n;
640     }
641 
642     if (PyBytes_GET_SIZE(result) > total) {
643         if (_PyBytes_Resize(&result, total) < 0) {
644             /* This should never happen, but just in case */
645             return NULL;
646         }
647     }
648     return result;
649 }
650 
651 static PyObject *
fileio_read(fileio * self,PyObject * args)652 fileio_read(fileio *self, PyObject *args)
653 {
654     char *ptr;
655     Py_ssize_t n;
656     Py_ssize_t size = -1;
657     PyObject *bytes;
658 
659     if (self->fd < 0)
660         return err_closed();
661     if (!self->readable)
662         return err_mode("reading");
663 
664     if (!PyArg_ParseTuple(args, "|O&", &_PyIO_ConvertSsize_t, &size))
665         return NULL;
666 
667     if (size < 0) {
668         return fileio_readall(self);
669     }
670 
671 #if defined(MS_WIN64) || defined(MS_WINDOWS)
672     if (size > INT_MAX)
673         size = INT_MAX;
674 #endif
675     bytes = PyBytes_FromStringAndSize(NULL, size);
676     if (bytes == NULL)
677         return NULL;
678     ptr = PyBytes_AS_STRING(bytes);
679 
680     if (_PyVerify_fd(self->fd)) {
681         Py_BEGIN_ALLOW_THREADS
682         errno = 0;
683 #if defined(MS_WIN64) || defined(MS_WINDOWS)
684         n = read(self->fd, ptr, (int)size);
685 #else
686         n = read(self->fd, ptr, size);
687 #endif
688         Py_END_ALLOW_THREADS
689     } else
690         n = -1;
691 
692     if (n < 0) {
693         Py_DECREF(bytes);
694         if (errno == EAGAIN)
695             Py_RETURN_NONE;
696         PyErr_SetFromErrno(PyExc_IOError);
697         return NULL;
698     }
699 
700     if (n != size) {
701         if (_PyBytes_Resize(&bytes, n) < 0)
702             return NULL;
703     }
704 
705     return (PyObject *) bytes;
706 }
707 
708 static PyObject *
fileio_write(fileio * self,PyObject * args)709 fileio_write(fileio *self, PyObject *args)
710 {
711     Py_buffer pbuf;
712     Py_ssize_t n, len;
713 
714     if (self->fd < 0)
715         return err_closed();
716     if (!self->writable)
717         return err_mode("writing");
718 
719     if (!PyArg_ParseTuple(args, "s*", &pbuf))
720         return NULL;
721 
722     if (_PyVerify_fd(self->fd)) {
723         Py_BEGIN_ALLOW_THREADS
724         errno = 0;
725         len = pbuf.len;
726 #if defined(MS_WIN64) || defined(MS_WINDOWS)
727         if (len > INT_MAX)
728             len = INT_MAX;
729         n = write(self->fd, pbuf.buf, (int)len);
730 #else
731         n = write(self->fd, pbuf.buf, len);
732 #endif
733         Py_END_ALLOW_THREADS
734     } else
735         n = -1;
736 
737     PyBuffer_Release(&pbuf);
738 
739     if (n < 0) {
740         if (errno == EAGAIN)
741             Py_RETURN_NONE;
742         PyErr_SetFromErrno(PyExc_IOError);
743         return NULL;
744     }
745 
746     return PyLong_FromSsize_t(n);
747 }
748 
749 /* XXX Windows support below is likely incomplete */
750 
751 /* Cribbed from posix_lseek() */
752 static PyObject *
portable_lseek(int fd,PyObject * posobj,int whence)753 portable_lseek(int fd, PyObject *posobj, int whence)
754 {
755     Py_off_t pos, res;
756 
757 #ifdef SEEK_SET
758     /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
759     switch (whence) {
760 #if SEEK_SET != 0
761     case 0: whence = SEEK_SET; break;
762 #endif
763 #if SEEK_CUR != 1
764     case 1: whence = SEEK_CUR; break;
765 #endif
766 #if SEEK_END != 2
767     case 2: whence = SEEK_END; break;
768 #endif
769     }
770 #endif /* SEEK_SET */
771 
772     if (posobj == NULL)
773         pos = 0;
774     else {
775         if(PyFloat_Check(posobj)) {
776             PyErr_SetString(PyExc_TypeError, "an integer is required");
777             return NULL;
778         }
779 #if defined(HAVE_LARGEFILE_SUPPORT)
780         pos = PyLong_AsLongLong(posobj);
781 #else
782         pos = PyLong_AsLong(posobj);
783 #endif
784         if (PyErr_Occurred())
785             return NULL;
786     }
787 
788     if (_PyVerify_fd(fd)) {
789         Py_BEGIN_ALLOW_THREADS
790 #if defined(MS_WIN64) || defined(MS_WINDOWS)
791         res = _lseeki64(fd, pos, whence);
792 #else
793         res = lseek(fd, pos, whence);
794 #endif
795         Py_END_ALLOW_THREADS
796     } else
797         res = -1;
798     if (res < 0)
799         return PyErr_SetFromErrno(PyExc_IOError);
800 
801 #if defined(HAVE_LARGEFILE_SUPPORT)
802     return PyLong_FromLongLong(res);
803 #else
804     return PyLong_FromLong(res);
805 #endif
806 }
807 
808 static PyObject *
fileio_seek(fileio * self,PyObject * args)809 fileio_seek(fileio *self, PyObject *args)
810 {
811     PyObject *posobj;
812     int whence = 0;
813 
814     if (self->fd < 0)
815         return err_closed();
816 
817     if (!PyArg_ParseTuple(args, "O|i", &posobj, &whence))
818         return NULL;
819 
820     return portable_lseek(self->fd, posobj, whence);
821 }
822 
823 static PyObject *
fileio_tell(fileio * self,PyObject * args)824 fileio_tell(fileio *self, PyObject *args)
825 {
826     if (self->fd < 0)
827         return err_closed();
828 
829     return portable_lseek(self->fd, NULL, 1);
830 }
831 
832 #ifdef HAVE_FTRUNCATE
833 static PyObject *
fileio_truncate(fileio * self,PyObject * args)834 fileio_truncate(fileio *self, PyObject *args)
835 {
836     PyObject *posobj = NULL; /* the new size wanted by the user */
837 #ifndef MS_WINDOWS
838     Py_off_t pos;
839 #endif
840     int ret;
841     int fd;
842 
843     fd = self->fd;
844     if (fd < 0)
845         return err_closed();
846     if (!self->writable)
847         return err_mode("writing");
848 
849     if (!PyArg_ParseTuple(args, "|O", &posobj))
850         return NULL;
851 
852     if (posobj == Py_None || posobj == NULL) {
853         /* Get the current position. */
854         posobj = portable_lseek(fd, NULL, 1);
855         if (posobj == NULL)
856             return NULL;
857     }
858     else {
859         Py_INCREF(posobj);
860     }
861 
862 #ifdef MS_WINDOWS
863     /* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
864        so don't even try using it. */
865     {
866         PyObject *oldposobj, *tempposobj;
867         HANDLE hFile;
868 
869         /* we save the file pointer position */
870         oldposobj = portable_lseek(fd, NULL, 1);
871         if (oldposobj == NULL) {
872             Py_DECREF(posobj);
873             return NULL;
874         }
875 
876         /* we then move to the truncation position */
877         tempposobj = portable_lseek(fd, posobj, 0);
878         if (tempposobj == NULL) {
879             Py_DECREF(oldposobj);
880             Py_DECREF(posobj);
881             return NULL;
882         }
883         Py_DECREF(tempposobj);
884 
885         /* Truncate.  Note that this may grow the file! */
886         Py_BEGIN_ALLOW_THREADS
887         errno = 0;
888         hFile = (HANDLE)_get_osfhandle(fd);
889         ret = hFile == (HANDLE)-1; /* testing for INVALID_HANDLE value */
890         if (ret == 0) {
891             ret = SetEndOfFile(hFile) == 0;
892             if (ret)
893                 errno = EACCES;
894         }
895         Py_END_ALLOW_THREADS
896 
897         /* we restore the file pointer position in any case */
898         tempposobj = portable_lseek(fd, oldposobj, 0);
899         Py_DECREF(oldposobj);
900         if (tempposobj == NULL) {
901             Py_DECREF(posobj);
902             return NULL;
903         }
904         Py_DECREF(tempposobj);
905     }
906 #else
907 
908 #if defined(HAVE_LARGEFILE_SUPPORT)
909     pos = PyLong_AsLongLong(posobj);
910 #else
911     pos = PyLong_AsLong(posobj);
912 #endif
913     if (PyErr_Occurred()){
914         Py_DECREF(posobj);
915         return NULL;
916     }
917 
918     Py_BEGIN_ALLOW_THREADS
919     errno = 0;
920     ret = ftruncate(fd, pos);
921     Py_END_ALLOW_THREADS
922 
923 #endif /* !MS_WINDOWS */
924 
925     if (ret != 0) {
926         Py_DECREF(posobj);
927         PyErr_SetFromErrno(PyExc_IOError);
928         return NULL;
929     }
930 
931     return posobj;
932 }
933 #endif /* HAVE_FTRUNCATE */
934 
935 static char *
mode_string(fileio * self)936 mode_string(fileio *self)
937 {
938     if (self->appending) {
939         if (self->readable)
940             return "ab+";
941         else
942             return "ab";
943     }
944     else if (self->readable) {
945         if (self->writable)
946             return "rb+";
947         else
948             return "rb";
949     }
950     else
951         return "wb";
952 }
953 
954 static PyObject *
fileio_repr(fileio * self)955 fileio_repr(fileio *self)
956 {
957     PyObject *nameobj, *res;
958 
959     if (self->fd < 0)
960         return PyString_FromFormat("<_io.FileIO [closed]>");
961 
962     nameobj = PyObject_GetAttrString((PyObject *) self, "name");
963     if (nameobj == NULL) {
964         if (PyErr_ExceptionMatches(PyExc_AttributeError))
965             PyErr_Clear();
966         else
967             return NULL;
968         res = PyString_FromFormat("<_io.FileIO fd=%d mode='%s'>",
969                                    self->fd, mode_string(self));
970     }
971     else {
972         PyObject *repr = PyObject_Repr(nameobj);
973         Py_DECREF(nameobj);
974         if (repr == NULL)
975             return NULL;
976         res = PyString_FromFormat("<_io.FileIO name=%s mode='%s'>",
977                                    PyString_AS_STRING(repr),
978                                    mode_string(self));
979         Py_DECREF(repr);
980     }
981     return res;
982 }
983 
984 static PyObject *
fileio_isatty(fileio * self)985 fileio_isatty(fileio *self)
986 {
987     long res;
988 
989     if (self->fd < 0)
990         return err_closed();
991     Py_BEGIN_ALLOW_THREADS
992     res = isatty(self->fd);
993     Py_END_ALLOW_THREADS
994     return PyBool_FromLong(res);
995 }
996 
997 
998 PyDoc_STRVAR(fileio_doc,
999 "file(name: str[, mode: str]) -> file IO object\n"
1000 "\n"
1001 "Open a file.  The mode can be 'r' (default), 'w' or 'a' for reading,\n"
1002 "writing or appending.  The file will be created if it doesn't exist\n"
1003 "when opened for writing or appending; it will be truncated when\n"
1004 "opened for writing.  Add a '+' to the mode to allow simultaneous\n"
1005 "reading and writing.");
1006 
1007 PyDoc_STRVAR(read_doc,
1008 "read(size: int) -> bytes.  read at most size bytes, returned as bytes.\n"
1009 "\n"
1010 "Only makes one system call, so less data may be returned than requested\n"
1011 "In non-blocking mode, returns None if no data is available.\n"
1012 "On end-of-file, returns ''.");
1013 
1014 PyDoc_STRVAR(readall_doc,
1015 "readall() -> bytes.  read all data from the file, returned as bytes.\n"
1016 "\n"
1017 "In non-blocking mode, returns as much as is immediately available,\n"
1018 "or None if no data is available.  On end-of-file, returns ''.");
1019 
1020 PyDoc_STRVAR(write_doc,
1021 "write(b) -> int.  Write array of bytes b, return number written.\n"
1022 "\n"
1023 "Only makes one system call, so not all of the data may be written.\n"
1024 "The number of bytes actually written is returned.  In non-blocking mode,\n"
1025 "returns None if the write would block."
1026 );
1027 
1028 PyDoc_STRVAR(fileno_doc,
1029 "fileno() -> int.  Return the underlying file descriptor (an integer).");
1030 
1031 PyDoc_STRVAR(seek_doc,
1032 "seek(offset: int[, whence: int]) -> int.  Move to new file position\n"
1033 "and return the file position.\n"
1034 "\n"
1035 "Argument offset is a byte count.  Optional argument whence defaults to\n"
1036 "SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values\n"
1037 "are SEEK_CUR or 1 (move relative to current position, positive or negative),\n"
1038 "and SEEK_END or 2 (move relative to end of file, usually negative, although\n"
1039 "many platforms allow seeking beyond the end of a file).\n"
1040 "\n"
1041 "Note that not all file objects are seekable.");
1042 
1043 #ifdef HAVE_FTRUNCATE
1044 PyDoc_STRVAR(truncate_doc,
1045 "truncate([size: int]) -> int.  Truncate the file to at most size bytes and\n"
1046 "return the truncated size.\n"
1047 "\n"
1048 "Size defaults to the current file position, as returned by tell().\n"
1049 "The current file position is changed to the value of size.");
1050 #endif
1051 
1052 PyDoc_STRVAR(tell_doc,
1053 "tell() -> int.  Current file position.\n"
1054 "\n"
1055 "Can raise OSError for non seekable files."
1056 );
1057 
1058 PyDoc_STRVAR(readinto_doc,
1059 "readinto() -> Same as RawIOBase.readinto().");
1060 
1061 PyDoc_STRVAR(close_doc,
1062 "close() -> None.  Close the file.\n"
1063 "\n"
1064 "A closed file cannot be used for further I/O operations.  close() may be\n"
1065 "called more than once without error.");
1066 
1067 PyDoc_STRVAR(isatty_doc,
1068 "isatty() -> bool.  True if the file is connected to a TTY device.");
1069 
1070 PyDoc_STRVAR(seekable_doc,
1071 "seekable() -> bool.  True if file supports random-access.");
1072 
1073 PyDoc_STRVAR(readable_doc,
1074 "readable() -> bool.  True if file was opened in a read mode.");
1075 
1076 PyDoc_STRVAR(writable_doc,
1077 "writable() -> bool.  True if file was opened in a write mode.");
1078 
1079 static PyMethodDef fileio_methods[] = {
1080     {"read",     (PyCFunction)fileio_read,         METH_VARARGS, read_doc},
1081     {"readall",  (PyCFunction)fileio_readall,  METH_NOARGS,  readall_doc},
1082     {"readinto", (PyCFunction)fileio_readinto, METH_VARARGS, readinto_doc},
1083     {"write",    (PyCFunction)fileio_write,        METH_VARARGS, write_doc},
1084     {"seek",     (PyCFunction)fileio_seek,         METH_VARARGS, seek_doc},
1085     {"tell",     (PyCFunction)fileio_tell,         METH_VARARGS, tell_doc},
1086 #ifdef HAVE_FTRUNCATE
1087     {"truncate", (PyCFunction)fileio_truncate, METH_VARARGS, truncate_doc},
1088 #endif
1089     {"close",    (PyCFunction)fileio_close,        METH_NOARGS,  close_doc},
1090     {"seekable", (PyCFunction)fileio_seekable, METH_NOARGS,      seekable_doc},
1091     {"readable", (PyCFunction)fileio_readable, METH_NOARGS,      readable_doc},
1092     {"writable", (PyCFunction)fileio_writable, METH_NOARGS,      writable_doc},
1093     {"fileno",   (PyCFunction)fileio_fileno,   METH_NOARGS,      fileno_doc},
1094     {"isatty",   (PyCFunction)fileio_isatty,   METH_NOARGS,      isatty_doc},
1095     {NULL,           NULL}             /* sentinel */
1096 };
1097 
1098 /* 'closed' and 'mode' are attributes for backwards compatibility reasons. */
1099 
1100 static PyObject *
get_closed(fileio * self,void * closure)1101 get_closed(fileio *self, void *closure)
1102 {
1103     return PyBool_FromLong((long)(self->fd < 0));
1104 }
1105 
1106 static PyObject *
get_closefd(fileio * self,void * closure)1107 get_closefd(fileio *self, void *closure)
1108 {
1109     return PyBool_FromLong((long)(self->closefd));
1110 }
1111 
1112 static PyObject *
get_mode(fileio * self,void * closure)1113 get_mode(fileio *self, void *closure)
1114 {
1115     return PyUnicode_FromString(mode_string(self));
1116 }
1117 
1118 static PyGetSetDef fileio_getsetlist[] = {
1119     {"closed", (getter)get_closed, NULL, "True if the file is closed"},
1120     {"closefd", (getter)get_closefd, NULL,
1121         "True if the file descriptor will be closed by close()."},
1122     {"mode", (getter)get_mode, NULL, "String giving the file mode"},
1123     {NULL},
1124 };
1125 
1126 PyTypeObject PyFileIO_Type = {
1127     PyVarObject_HEAD_INIT(NULL, 0)
1128     "_io.FileIO",
1129     sizeof(fileio),
1130     0,
1131     (destructor)fileio_dealloc,                 /* tp_dealloc */
1132     0,                                          /* tp_print */
1133     0,                                          /* tp_getattr */
1134     0,                                          /* tp_setattr */
1135     0,                                          /* tp_reserved */
1136     (reprfunc)fileio_repr,                      /* tp_repr */
1137     0,                                          /* tp_as_number */
1138     0,                                          /* tp_as_sequence */
1139     0,                                          /* tp_as_mapping */
1140     0,                                          /* tp_hash */
1141     0,                                          /* tp_call */
1142     0,                                          /* tp_str */
1143     PyObject_GenericGetAttr,                    /* tp_getattro */
1144     0,                                          /* tp_setattro */
1145     0,                                          /* tp_as_buffer */
1146     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
1147                     | Py_TPFLAGS_HAVE_GC,       /* tp_flags */
1148     fileio_doc,                                 /* tp_doc */
1149     (traverseproc)fileio_traverse,              /* tp_traverse */
1150     (inquiry)fileio_clear,                      /* tp_clear */
1151     0,                                          /* tp_richcompare */
1152     offsetof(fileio, weakreflist),      /* tp_weaklistoffset */
1153     0,                                          /* tp_iter */
1154     0,                                          /* tp_iternext */
1155     fileio_methods,                             /* tp_methods */
1156     0,                                          /* tp_members */
1157     fileio_getsetlist,                          /* tp_getset */
1158     0,                                          /* tp_base */
1159     0,                                          /* tp_dict */
1160     0,                                          /* tp_descr_get */
1161     0,                                          /* tp_descr_set */
1162     offsetof(fileio, dict),         /* tp_dictoffset */
1163     fileio_init,                                /* tp_init */
1164     PyType_GenericAlloc,                        /* tp_alloc */
1165     fileio_new,                                 /* tp_new */
1166     PyObject_GC_Del,                            /* tp_free */
1167 };
1168