summaryrefslogtreecommitdiff
path: root/Modules/_io/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_io/fileio.c')
-rw-r--r--Modules/_io/fileio.c198
1 files changed, 110 insertions, 88 deletions
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
index f3ce776993..27995e543b 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -52,7 +52,7 @@ typedef struct {
unsigned int appending : 1;
signed int seekable : 2; /* -1 means unknown */
unsigned int closefd : 1;
- unsigned int deallocating: 1;
+ char finalizing;
PyObject *weakreflist;
PyObject *dict;
} fileio;
@@ -129,7 +129,7 @@ fileio_close(fileio *self)
self->fd = -1;
Py_RETURN_NONE;
}
- if (self->deallocating) {
+ if (self->finalizing) {
PyObject *r = fileio_dealloc_warn(self, (PyObject *) self);
if (r)
Py_DECREF(r);
@@ -204,6 +204,9 @@ check_fd(int fd)
return 0;
}
+#ifdef O_CLOEXEC
+extern int _Py_open_cloexec_works;
+#endif
static int
fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
@@ -223,6 +226,11 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
int fd = -1;
int closefd = 1;
int fd_is_own = 0;
+#ifdef O_CLOEXEC
+ int *atomic_flag_works = &_Py_open_cloexec_works;
+#elif !defined(MS_WINDOWS)
+ int *atomic_flag_works = NULL;
+#endif
assert(PyFileIO_Check(oself));
if (self->fd >= 0) {
@@ -343,6 +351,12 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
flags |= O_BINARY;
#endif
+#ifdef MS_WINDOWS
+ flags |= O_NOINHERIT;
+#elif defined(O_CLOEXEC)
+ flags |= O_CLOEXEC;
+#endif
+
if (fd >= 0) {
if (check_fd(fd))
goto error;
@@ -366,10 +380,18 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
else
#endif
self->fd = open(name, flags, 0666);
+
Py_END_ALLOW_THREADS
- } else {
- PyObject *fdobj = PyObject_CallFunction(
- opener, "Oi", nameobj, flags);
+ }
+ else {
+ PyObject *fdobj;
+
+#ifndef MS_WINDOWS
+ /* the opener may clear the atomic flag */
+ atomic_flag_works = NULL;
+#endif
+
+ fdobj = PyObject_CallFunction(opener, "Oi", nameobj, flags);
if (fdobj == NULL)
goto error;
if (!PyLong_Check(fdobj)) {
@@ -388,14 +410,14 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
fd_is_own = 1;
if (self->fd < 0) {
-#ifdef MS_WINDOWS
- if (widename != NULL)
- PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj);
- else
-#endif
- PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
+ PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
goto error;
}
+
+#ifndef MS_WINDOWS
+ if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0)
+ goto error;
+#endif
}
if (dircheck(self, nameobj) < 0)
goto error;
@@ -449,7 +471,7 @@ fileio_clear(fileio *self)
static void
fileio_dealloc(fileio *self)
{
- self->deallocating = 1;
+ self->finalizing = 1;
if (_PyIOBase_finalize((PyObject *) self) < 0)
return;
_PyObject_GC_UNTRACK(self);
@@ -535,7 +557,7 @@ fileio_readinto(fileio *self, PyObject *args)
len = pbuf.len;
Py_BEGIN_ALLOW_THREADS
errno = 0;
-#if defined(MS_WIN64) || defined(MS_WINDOWS)
+#ifdef MS_WINDOWS
if (len > INT_MAX)
len = INT_MAX;
n = read(self->fd, pbuf.buf, (int)len);
@@ -558,33 +580,27 @@ fileio_readinto(fileio *self, PyObject *args)
return PyLong_FromSsize_t(n);
}
+#ifndef HAVE_FSTAT
+
+static PyObject *
+fileio_readall(fileio *self)
+{
+ _Py_IDENTIFIER(readall);
+ return _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
+ &PyId_readall, "O", self);
+}
+
+#else
+
static size_t
-new_buffersize(fileio *self, size_t currentsize
-#ifdef HAVE_FSTAT
- , Py_off_t pos, Py_off_t end
-#endif
- )
+new_buffersize(fileio *self, size_t currentsize)
{
size_t addend;
-#ifdef HAVE_FSTAT
- if (end != (Py_off_t)-1) {
- /* Files claiming a size smaller than SMALLCHUNK may
- actually be streaming pseudo-files. In this case, we
- apply the more aggressive algorithm below.
- */
- if (end >= SMALLCHUNK && end >= pos && pos >= 0) {
- /* Add 1 so if the file were to grow we'd notice. */
- Py_off_t bufsize = currentsize + end - pos + 1;
- if (bufsize < PY_SSIZE_T_MAX)
- return (size_t)bufsize;
- else
- return PY_SSIZE_T_MAX;
- }
- }
-#endif
+
/* Expand the buffer by an amount proportional to the current size,
giving us amortized linear-time behavior. For bigger sizes, use a
less-than-double growth factor to avoid excessive allocation. */
+ assert(currentsize <= PY_SSIZE_T_MAX);
if (currentsize > 65536)
addend = currentsize >> 3;
else
@@ -598,26 +614,19 @@ new_buffersize(fileio *self, size_t currentsize
static PyObject *
fileio_readall(fileio *self)
{
-#ifdef HAVE_FSTAT
struct stat st;
Py_off_t pos, end;
-#endif
PyObject *result;
- Py_ssize_t total = 0;
+ Py_ssize_t bytes_read = 0;
Py_ssize_t n;
- size_t newsize;
+ size_t bufsize;
if (self->fd < 0)
return err_closed();
if (!_PyVerify_fd(self->fd))
return PyErr_SetFromErrno(PyExc_IOError);
- result = PyBytes_FromStringAndSize(NULL, SMALLCHUNK);
- if (result == NULL)
- return NULL;
-
-#ifdef HAVE_FSTAT
-#if defined(MS_WIN64) || defined(MS_WINDOWS)
+#ifdef MS_WINDOWS
pos = _lseeki64(self->fd, 0L, SEEK_CUR);
#else
pos = lseek(self->fd, 0L, SEEK_CUR);
@@ -626,44 +635,46 @@ fileio_readall(fileio *self)
end = st.st_size;
else
end = (Py_off_t)-1;
-#endif
+
+ if (end > 0 && end >= pos && pos >= 0 && end - pos < PY_SSIZE_T_MAX) {
+ /* This is probably a real file, so we try to allocate a
+ buffer one byte larger than the rest of the file. If the
+ calculation is right then we should get EOF without having
+ to enlarge the buffer. */
+ bufsize = (size_t)(end - pos + 1);
+ } else {
+ bufsize = SMALLCHUNK;
+ }
+
+ result = PyBytes_FromStringAndSize(NULL, bufsize);
+ if (result == NULL)
+ return NULL;
+
while (1) {
-#ifdef HAVE_FSTAT
- newsize = new_buffersize(self, total, pos, end);
-#else
- newsize = new_buffersize(self, total);
-#endif
- if (newsize > PY_SSIZE_T_MAX || newsize <= 0) {
- PyErr_SetString(PyExc_OverflowError,
- "unbounded read returned more bytes "
- "than a Python string can hold ");
- Py_DECREF(result);
- return NULL;
- }
+ if (bytes_read >= (Py_ssize_t)bufsize) {
+ bufsize = new_buffersize(self, bytes_read);
+ if (bufsize > PY_SSIZE_T_MAX || bufsize <= 0) {
+ PyErr_SetString(PyExc_OverflowError,
+ "unbounded read returned more bytes "
+ "than a Python string can hold");
+ Py_DECREF(result);
+ return NULL;
+ }
- if (PyBytes_GET_SIZE(result) < (Py_ssize_t)newsize) {
- if (_PyBytes_Resize(&result, newsize) < 0) {
- if (total == 0) {
- Py_DECREF(result);
+ if (PyBytes_GET_SIZE(result) < (Py_ssize_t)bufsize) {
+ if (_PyBytes_Resize(&result, bufsize) < 0)
return NULL;
- }
- PyErr_Clear();
- break;
}
}
Py_BEGIN_ALLOW_THREADS
errno = 0;
- n = newsize - total;
-#if defined(MS_WIN64) || defined(MS_WINDOWS)
+ n = bufsize - bytes_read;
+#ifdef MS_WINDOWS
if (n > INT_MAX)
n = INT_MAX;
- n = read(self->fd,
- PyBytes_AS_STRING(result) + total,
- (int)n);
+ n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, (int)n);
#else
- n = read(self->fd,
- PyBytes_AS_STRING(result) + total,
- n);
+ n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, n);
#endif
Py_END_ALLOW_THREADS
if (n == 0)
@@ -676,7 +687,7 @@ fileio_readall(fileio *self)
}
continue;
}
- if (total > 0)
+ if (bytes_read > 0)
break;
if (errno == EAGAIN) {
Py_DECREF(result);
@@ -686,22 +697,19 @@ fileio_readall(fileio *self)
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
- total += n;
-#ifdef HAVE_FSTAT
+ bytes_read += n;
pos += n;
-#endif
}
- if (PyBytes_GET_SIZE(result) > total) {
- if (_PyBytes_Resize(&result, total) < 0) {
- /* This should never happen, but just in case */
- Py_DECREF(result);
+ if (PyBytes_GET_SIZE(result) > bytes_read) {
+ if (_PyBytes_Resize(&result, bytes_read) < 0)
return NULL;
- }
}
return result;
}
+#endif /* HAVE_FSTAT */
+
static PyObject *
fileio_read(fileio *self, PyObject *args)
{
@@ -722,7 +730,7 @@ fileio_read(fileio *self, PyObject *args)
return fileio_readall(self);
}
-#if defined(MS_WIN64) || defined(MS_WINDOWS)
+#ifdef MS_WINDOWS
if (size > INT_MAX)
size = INT_MAX;
#endif
@@ -734,7 +742,7 @@ fileio_read(fileio *self, PyObject *args)
if (_PyVerify_fd(self->fd)) {
Py_BEGIN_ALLOW_THREADS
errno = 0;
-#if defined(MS_WIN64) || defined(MS_WINDOWS)
+#ifdef MS_WINDOWS
n = read(self->fd, ptr, (int)size);
#else
n = read(self->fd, ptr, size);
@@ -755,7 +763,7 @@ fileio_read(fileio *self, PyObject *args)
if (n != size) {
if (_PyBytes_Resize(&bytes, n) < 0) {
- Py_DECREF(bytes);
+ Py_CLEAR(bytes);
return NULL;
}
}
@@ -782,7 +790,7 @@ fileio_write(fileio *self, PyObject *args)
Py_BEGIN_ALLOW_THREADS
errno = 0;
len = pbuf.len;
-#if defined(MS_WIN64) || defined(MS_WINDOWS)
+#ifdef MS_WINDOWS
if (len > 32767 && isatty(self->fd)) {
/* Issue #11395: the Windows console returns an error (12: not
enough space error) on writing into stdout if stdout mode is
@@ -855,7 +863,7 @@ portable_lseek(int fd, PyObject *posobj, int whence)
if (_PyVerify_fd(fd)) {
Py_BEGIN_ALLOW_THREADS
-#if defined(MS_WIN64) || defined(MS_WINDOWS)
+#ifdef MS_WINDOWS
res = _lseeki64(fd, pos, whence);
#else
res = lseek(fd, pos, whence);
@@ -1204,6 +1212,11 @@ static PyGetSetDef fileio_getsetlist[] = {
{NULL},
};
+static PyMemberDef fileio_members[] = {
+ {"_finalizing", T_BOOL, offsetof(fileio, finalizing), 0},
+ {NULL}
+};
+
PyTypeObject PyFileIO_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_io.FileIO",
@@ -1225,7 +1238,7 @@ PyTypeObject PyFileIO_Type = {
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
fileio_doc, /* tp_doc */
(traverseproc)fileio_traverse, /* tp_traverse */
(inquiry)fileio_clear, /* tp_clear */
@@ -1234,7 +1247,7 @@ PyTypeObject PyFileIO_Type = {
0, /* tp_iter */
0, /* tp_iternext */
fileio_methods, /* tp_methods */
- 0, /* tp_members */
+ fileio_members, /* tp_members */
fileio_getsetlist, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
@@ -1245,4 +1258,13 @@ PyTypeObject PyFileIO_Type = {
PyType_GenericAlloc, /* tp_alloc */
fileio_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
};