Home | History | Annotate | Download | only in _io
      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
     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
     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 *
    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 *
    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
    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
    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
    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
    419 fileio_traverse(fileio *self, visitproc visit, void *arg)
    420 {
    421     Py_VISIT(self->dict);
    422     return 0;
    423 }
    424 
    425 static int
    426 fileio_clear(fileio *self)
    427 {
    428     Py_CLEAR(self->dict);
    429     return 0;
    430 }
    431 
    432 static void
    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 *
    445 err_closed(void)
    446 {
    447     PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
    448     return NULL;
    449 }
    450 
    451 static PyObject *
    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 *
    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 *
    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 *
    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 *
    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 *
    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
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
   1101 get_closed(fileio *self, void *closure)
   1102 {
   1103     return PyBool_FromLong((long)(self->fd < 0));
   1104 }
   1105 
   1106 static PyObject *
   1107 get_closefd(fileio *self, void *closure)
   1108 {
   1109     return PyBool_FromLong((long)(self->closefd));
   1110 }
   1111 
   1112 static PyObject *
   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