]> granicus.if.org Git - python/commitdiff
Issue #21679: Prevent extraneous fstat() calls during open(). Patch by Bohuslav...
authorAntoine Pitrou <solipsis@pitrou.net>
Mon, 30 Jun 2014 00:07:28 +0000 (20:07 -0400)
committerAntoine Pitrou <solipsis@pitrou.net>
Mon, 30 Jun 2014 00:07:28 +0000 (20:07 -0400)
Lib/test/test_fileio.py
Misc/NEWS
Modules/_io/_iomodule.c
Modules/_io/fileio.c

index c37482ed701b6a6ac17eb5d0c616cc66aad06f20..b87dc07805a8cb831934a5de628c1ab756bd7e8b 100644 (file)
@@ -60,6 +60,15 @@ class AutoFileTests(unittest.TestCase):
             self.assertRaises((AttributeError, TypeError),
                               setattr, f, attr, 'oops')
 
+    def testBlksize(self):
+        # test private _blksize attribute
+        blksize = io.DEFAULT_BUFFER_SIZE
+        # try to get preferred blksize from stat.st_blksize, if available
+        if hasattr(os, 'fstat'):
+            fst = os.fstat(self.f.fileno())
+            blksize = getattr(fst, 'st_blksize', blksize)
+        self.assertEqual(self.f._blksize, blksize)
+
     def testReadinto(self):
         # verify readinto
         self.f.write(bytes([1, 2]))
@@ -141,7 +150,7 @@ class AutoFileTests(unittest.TestCase):
     def testOpendir(self):
         # Issue 3703: opening a directory should fill the errno
         # Windows always returns "[Errno 13]: Permission denied
-        # Unix calls dircheck() and returns "[Errno 21]: Is a directory"
+        # Unix uses fstat and returns "[Errno 21]: Is a directory"
         try:
             _FileIO('.', 'r')
         except OSError as e:
index d7bc2afe4af1d603815687b09b9834dcff297f7c..27effe898bef43791c0a5aefd0340a662538a957 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -103,6 +103,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #21679: Prevent extraneous fstat() calls during open().  Patch by
+  Bohuslav Kabrda.
+
 - Issue #21863: cProfile now displays the module name of C extension functions,
   in addition to their own name.
 
index 660ff1fe58ed6be2f0ecfa13a11bfe9dcea51be4..9e14b4ff40d5831a354ef4fa3a388a8214af914f 100644 (file)
@@ -237,8 +237,8 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
 
     PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL;
 
+    _Py_IDENTIFIER(_blksize);
     _Py_IDENTIFIER(isatty);
-    _Py_IDENTIFIER(fileno);
     _Py_IDENTIFIER(mode);
     _Py_IDENTIFIER(close);
 
@@ -380,24 +380,14 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
         line_buffering = 0;
 
     if (buffering < 0) {
-        buffering = DEFAULT_BUFFER_SIZE;
-#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
-        {
-            struct stat st;
-            long fileno;
-            PyObject *res = _PyObject_CallMethodId(raw, &PyId_fileno, NULL);
-            if (res == NULL)
-                goto error;
-
-            fileno = PyLong_AsLong(res);
-            Py_DECREF(res);
-            if (fileno == -1 && PyErr_Occurred())
-                goto error;
-
-            if (fstat(fileno, &st) >= 0 && st.st_blksize > 1)
-                buffering = st.st_blksize;
-        }
-#endif
+        PyObject *blksize_obj;
+        blksize_obj = _PyObject_GetAttrId(raw, &PyId__blksize);
+        if (blksize_obj == NULL)
+            goto error;
+        buffering = PyLong_AsLong(blksize_obj);
+        Py_DECREF(blksize_obj);
+        if (buffering == -1 && PyErr_Occurred())
+            goto error;
     }
     if (buffering < 0) {
         PyErr_SetString(PyExc_ValueError,
index cbb2daf5c291af71a4a82a410ef0ee21788d4415..038b2c6459544e6234df193de68f757eb6d45d9e 100644 (file)
@@ -53,6 +53,7 @@ typedef struct {
     signed int seekable : 2; /* -1 means unknown */
     unsigned int closefd : 1;
     char finalizing;
+    unsigned int blksize;
     PyObject *weakreflist;
     PyObject *dict;
 } fileio;
@@ -161,6 +162,7 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
         self->writable = 0;
         self->appending = 0;
         self->seekable = -1;
+        self->blksize = 0;
         self->closefd = 1;
         self->weakreflist = NULL;
     }
@@ -168,26 +170,6 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     return (PyObject *) self;
 }
 
-/* On Unix, open will succeed for directories.
-   In Python, there should be no file objects referring to
-   directories, so we need a check.  */
-
-static int
-dircheck(fileio* self, PyObject *nameobj)
-{
-#if defined(HAVE_FSTAT) && defined(S_ISDIR) && defined(EISDIR)
-    struct stat buf;
-    if (self->fd < 0)
-        return 0;
-    if (fstat(self->fd, &buf) == 0 && S_ISDIR(buf.st_mode)) {
-        errno = EISDIR;
-        PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj);
-        return -1;
-    }
-#endif
-    return 0;
-}
-
 static int
 check_fd(int fd)
 {
@@ -233,6 +215,9 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
 #elif !defined(MS_WINDOWS)
     int *atomic_flag_works = NULL;
 #endif
+#ifdef HAVE_FSTAT
+    struct stat fdfstat;
+#endif
 
     assert(PyFileIO_Check(oself));
     if (self->fd >= 0) {
@@ -421,8 +406,26 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
             goto error;
 #endif
     }
-    if (dircheck(self, nameobj) < 0)
+
+    self->blksize = DEFAULT_BUFFER_SIZE;
+#ifdef HAVE_FSTAT
+    if (fstat(self->fd, &fdfstat) < 0)
         goto error;
+#if defined(S_ISDIR) && defined(EISDIR)
+    /* On Unix, open will succeed for directories.
+       In Python, there should be no file objects referring to
+       directories, so we need a check.  */
+    if (S_ISDIR(fdfstat.st_mode)) {
+        errno = EISDIR;
+        PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj);
+        goto error;
+    }
+#endif /* defined(S_ISDIR) */
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+    if (fdfstat.st_blksize > 1)
+        self->blksize = fdfstat.st_blksize;
+#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
+#endif /* HAVE_FSTAT */
 
 #if defined(MS_WINDOWS) || defined(__CYGWIN__)
     /* don't translate newlines (\r\n <=> \n) */
@@ -1216,6 +1219,7 @@ static PyGetSetDef fileio_getsetlist[] = {
 };
 
 static PyMemberDef fileio_members[] = {
+    {"_blksize", T_UINT, offsetof(fileio, blksize), 0},
     {"_finalizing", T_BOOL, offsetof(fileio, finalizing), 0},
     {NULL}
 };