@@ -11161,6 +11161,7 @@ typedef struct {
1116111161 unsigned char d_type ;
1116211162#endif
1116311163 ino_t d_ino ;
11164+ int dir_fd ;
1116411165#endif
1116511166} DirEntry ;
1116611167
@@ -11210,19 +11211,31 @@ DirEntry_fetch_stat(DirEntry *self, int follow_symlinks)
1121011211 PyObject * ub ;
1121111212
1121211213#ifdef MS_WINDOWS
11213- if (PyUnicode_FSDecoder (self -> path , & ub )) {
11214- const wchar_t * path = PyUnicode_AsUnicode (ub );
11214+ if (!PyUnicode_FSDecoder (self -> path , & ub ))
11215+ return NULL ;
11216+ const wchar_t * path = PyUnicode_AsUnicode (ub );
1121511217#else /* POSIX */
11216- if (PyUnicode_FSConverter (self -> path , & ub )) {
11217- const char * path = PyBytes_AS_STRING (ub );
11218+ if (!PyUnicode_FSConverter (self -> path , & ub ))
11219+ return NULL ;
11220+ const char * path = PyBytes_AS_STRING (ub );
11221+ if (self -> dir_fd != DEFAULT_DIR_FD ) {
11222+ #ifdef HAVE_FSTATAT
11223+ result = fstatat (self -> dir_fd , path , & st ,
11224+ follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW );
11225+ #else
11226+ PyErr_SetString (PyExc_NotImplementedError , "can't fetch stat" );
11227+ return NULL ;
11228+ #endif /* HAVE_FSTATAT */
11229+ }
11230+ else
1121811231#endif
11232+ {
1121911233 if (follow_symlinks )
1122011234 result = STAT (path , & st );
1122111235 else
1122211236 result = LSTAT (path , & st );
11223- Py_DECREF (ub );
11224- } else
11225- return NULL ;
11237+ }
11238+ Py_DECREF (ub );
1122611239
1122711240 if (result != 0 )
1122811241 return path_object_error (self -> path );
@@ -11633,20 +11646,36 @@ DirEntry_from_posix_info(path_t *path, const char *name, Py_ssize_t name_len,
1163311646 entry -> stat = NULL ;
1163411647 entry -> lstat = NULL ;
1163511648
11636- joined_path = join_path_filename (path -> narrow , name , name_len );
11637- if (!joined_path )
11638- goto error ;
11649+ if (path -> fd != -1 ) {
11650+ entry -> dir_fd = path -> fd ;
11651+ joined_path = NULL ;
11652+ }
11653+ else {
11654+ entry -> dir_fd = DEFAULT_DIR_FD ;
11655+ joined_path = join_path_filename (path -> narrow , name , name_len );
11656+ if (!joined_path )
11657+ goto error ;
11658+ }
1163911659
1164011660 if (!path -> narrow || !PyBytes_Check (path -> object )) {
1164111661 entry -> name = PyUnicode_DecodeFSDefaultAndSize (name , name_len );
11642- entry -> path = PyUnicode_DecodeFSDefault (joined_path );
11662+ if (joined_path )
11663+ entry -> path = PyUnicode_DecodeFSDefault (joined_path );
1164311664 }
1164411665 else {
1164511666 entry -> name = PyBytes_FromStringAndSize (name , name_len );
11646- entry -> path = PyBytes_FromString (joined_path );
11667+ if (joined_path )
11668+ entry -> path = PyBytes_FromString (joined_path );
1164711669 }
1164811670 PyMem_Free (joined_path );
11649- if (!entry -> name || !entry -> path )
11671+ if (!entry -> name )
11672+ goto error ;
11673+
11674+ if (path -> fd != -1 ) {
11675+ entry -> path = entry -> name ;
11676+ Py_INCREF (entry -> path );
11677+ }
11678+ else if (!entry -> path )
1165011679 goto error ;
1165111680
1165211681#ifdef HAVE_DIRENT_D_TYPE
@@ -11674,6 +11703,9 @@ typedef struct {
1167411703#else /* POSIX */
1167511704 DIR * dirp ;
1167611705#endif
11706+ #ifdef HAVE_FDOPENDIR
11707+ int fd ;
11708+ #endif
1167711709} ScandirIterator ;
1167811710
1167911711#ifdef MS_WINDOWS
@@ -11758,6 +11790,10 @@ ScandirIterator_closedir(ScandirIterator *iterator)
1175811790
1175911791 iterator -> dirp = NULL ;
1176011792 Py_BEGIN_ALLOW_THREADS
11793+ #ifdef HAVE_FDOPENDIR
11794+ if (iterator -> path .fd != -1 )
11795+ rewinddir (dirp );
11796+ #endif
1176111797 closedir (dirp );
1176211798 Py_END_ALLOW_THREADS
1176311799 return ;
@@ -11933,7 +11969,7 @@ static PyTypeObject ScandirIteratorType = {
1193311969/*[clinic input]
1193411970os.scandir
1193511971
11936- path : path_t(nullable=True) = None
11972+ path : path_t(nullable=True, allow_fd='PATH_HAVE_FDOPENDIR' ) = None
1193711973
1193811974Return an iterator of DirEntry objects for given path.
1193911975
@@ -11946,13 +11982,16 @@ If path is None, uses the path='.'.
1194611982
1194711983static PyObject *
1194811984os_scandir_impl (PyObject * module , path_t * path )
11949- /*[clinic end generated code: output=6eb2668b675ca89e input=e62b08b3cd41f604 ]*/
11985+ /*[clinic end generated code: output=6eb2668b675ca89e input=b139dc1c57f60846 ]*/
1195011986{
1195111987 ScandirIterator * iterator ;
1195211988#ifdef MS_WINDOWS
1195311989 wchar_t * path_strW ;
1195411990#else
1195511991 const char * path_str ;
11992+ #ifdef HAVE_FDOPENDIR
11993+ int fd = -1 ;
11994+ #endif
1195611995#endif
1195711996
1195811997 iterator = PyObject_New (ScandirIterator , & ScandirIteratorType );
@@ -11988,18 +12027,40 @@ os_scandir_impl(PyObject *module, path_t *path)
1198812027 goto error ;
1198912028 }
1199012029#else /* POSIX */
11991- if (iterator -> path .narrow )
11992- path_str = iterator -> path .narrow ;
12030+ errno = 0 ;
12031+ #ifdef HAVE_FDOPENDIR
12032+ if (path -> fd != -1 ) {
12033+ /* closedir() closes the FD, so we duplicate it */
12034+ fd = _Py_dup (path -> fd );
12035+ if (fd == -1 )
12036+ goto error ;
12037+
12038+ Py_BEGIN_ALLOW_THREADS
12039+ iterator -> dirp = fdopendir (fd );
12040+ Py_END_ALLOW_THREADS
12041+ }
1199312042 else
11994- path_str = "." ;
12043+ #endif
12044+ {
12045+ if (iterator -> path .narrow )
12046+ path_str = iterator -> path .narrow ;
12047+ else
12048+ path_str = "." ;
1199512049
11996- errno = 0 ;
11997- Py_BEGIN_ALLOW_THREADS
11998- iterator -> dirp = opendir ( path_str );
11999- Py_END_ALLOW_THREADS
12050+ Py_BEGIN_ALLOW_THREADS
12051+ iterator -> dirp = opendir ( path_str );
12052+ Py_END_ALLOW_THREADS
12053+ }
1200012054
1200112055 if (!iterator -> dirp ) {
1200212056 path_error (& iterator -> path );
12057+ #ifdef HAVE_FDOPENDIR
12058+ if (fd != -1 ) {
12059+ Py_BEGIN_ALLOW_THREADS
12060+ close (fd );
12061+ Py_END_ALLOW_THREADS
12062+ }
12063+ #endif
1200312064 goto error ;
1200412065 }
1200512066#endif
0 commit comments