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