mirror of
https://github.com/python/cpython.git
synced 2026-04-13 15:20:52 +00:00
gh-145335: Fix os functions when passing fd -1 as path (#145439)
os.listdir(-1) and os.scandir(-1) now fail with OSError(errno.EBADF) rather than listing the current directory. os.listxattr(-1) now fails with OSError(errno.EBADF) rather than listing extended attributes of the current directory.
This commit is contained in:
parent
db41717cd5
commit
52c8efa87d
4 changed files with 103 additions and 32 deletions
|
|
@ -2409,6 +2409,10 @@ features:
|
|||
.. versionchanged:: 3.6
|
||||
Accepts a :term:`path-like object`.
|
||||
|
||||
.. versionchanged:: next
|
||||
``os.listdir(-1)`` now fails with ``OSError(errno.EBADF)`` rather than
|
||||
listing the current directory.
|
||||
|
||||
|
||||
.. function:: listdrives()
|
||||
|
||||
|
|
@ -2939,6 +2943,10 @@ features:
|
|||
.. versionchanged:: 3.7
|
||||
Added support for :ref:`file descriptors <path_fd>` on Unix.
|
||||
|
||||
.. versionchanged:: next
|
||||
``os.scandir(-1)`` now fails with ``OSError(errno.EBADF)`` rather than
|
||||
listing the current directory.
|
||||
|
||||
|
||||
.. class:: DirEntry
|
||||
|
||||
|
|
@ -4574,6 +4582,10 @@ These functions are all available on Linux only.
|
|||
.. versionchanged:: 3.6
|
||||
Accepts a :term:`path-like object`.
|
||||
|
||||
.. versionchanged:: next
|
||||
``os.listxattr(-1)`` now fails with ``OSError(errno.EBADF)`` rather than
|
||||
listing extended attributes of the current directory.
|
||||
|
||||
|
||||
.. function:: removexattr(path, attribute, *, follow_symlinks=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -2784,10 +2784,61 @@ def test_fpathconf_bad_fd(self):
|
|||
'musl fpathconf ignores the file descriptor and returns a constant',
|
||||
)
|
||||
def test_pathconf_negative_fd_uses_fd_semantics(self):
|
||||
if os.pathconf not in os.supports_fd:
|
||||
self.skipTest('needs fpathconf()')
|
||||
|
||||
with self.assertRaises(OSError) as ctx:
|
||||
os.pathconf(-1, 1)
|
||||
self.assertEqual(ctx.exception.errno, errno.EBADF)
|
||||
|
||||
@support.subTests("fd", [-1, -5])
|
||||
def test_negative_fd_ebadf(self, fd):
|
||||
tests = [(os.stat, fd)]
|
||||
if hasattr(os, "statx"):
|
||||
tests.append((os.statx, fd, 0))
|
||||
if os.chdir in os.supports_fd:
|
||||
tests.append((os.chdir, fd))
|
||||
if os.chmod in os.supports_fd:
|
||||
tests.append((os.chmod, fd, 0o777))
|
||||
if hasattr(os, "chown") and os.chown in os.supports_fd:
|
||||
tests.append((os.chown, fd, 0, 0))
|
||||
if os.listdir in os.supports_fd:
|
||||
tests.append((os.listdir, fd))
|
||||
if os.utime in os.supports_fd:
|
||||
tests.append((os.utime, fd, (0, 0)))
|
||||
if hasattr(os, "truncate") and os.truncate in os.supports_fd:
|
||||
tests.append((os.truncate, fd, 0))
|
||||
if hasattr(os, 'statvfs') and os.statvfs in os.supports_fd:
|
||||
tests.append((os.statvfs, fd))
|
||||
if hasattr(os, "setxattr"):
|
||||
tests.append((os.getxattr, fd, b"user.test"))
|
||||
tests.append((os.setxattr, fd, b"user.test", b"1"))
|
||||
tests.append((os.removexattr, fd, b"user.test"))
|
||||
tests.append((os.listxattr, fd))
|
||||
if os.scandir in os.supports_fd:
|
||||
tests.append((os.scandir, fd))
|
||||
|
||||
for func, *args in tests:
|
||||
with self.subTest(func=func, args=args):
|
||||
with self.assertRaises(OSError) as ctx:
|
||||
func(*args)
|
||||
self.assertEqual(ctx.exception.errno, errno.EBADF)
|
||||
|
||||
if hasattr(os, "execve") and os.execve in os.supports_fd:
|
||||
# glibc fails with EINVAL, musl fails with EBADF
|
||||
with self.assertRaises(OSError) as ctx:
|
||||
os.execve(fd, [sys.executable, "-c", "pass"], os.environ)
|
||||
self.assertIn(ctx.exception.errno, (errno.EBADF, errno.EINVAL))
|
||||
|
||||
if support.MS_WINDOWS:
|
||||
import nt
|
||||
self.assertFalse(nt._path_exists(fd))
|
||||
self.assertFalse(nt._path_lexists(fd))
|
||||
self.assertFalse(nt._path_isdir(fd))
|
||||
self.assertFalse(nt._path_isfile(fd))
|
||||
self.assertFalse(nt._path_islink(fd))
|
||||
self.assertFalse(nt._path_isjunction(fd))
|
||||
|
||||
@unittest.skipUnless(hasattr(os, 'ftruncate'), 'test needs os.ftruncate()')
|
||||
def test_ftruncate(self):
|
||||
self.check(os.truncate, 0)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
``os.listdir(-1)`` and ``os.scandir(-1)`` now fail with
|
||||
``OSError(errno.EBADF)`` rather than listing the current directory.
|
||||
``os.listxattr(-1)`` now fails with ``OSError(errno.EBADF)`` rather than
|
||||
listing extended attributes of the current directory. Patch by Victor
|
||||
Stinner.
|
||||
|
|
@ -1638,10 +1638,10 @@ dir_fd_and_fd_invalid(const char *function_name, int dir_fd, int fd)
|
|||
}
|
||||
|
||||
static int
|
||||
fd_and_follow_symlinks_invalid(const char *function_name, int fd,
|
||||
fd_and_follow_symlinks_invalid(const char *function_name, int is_fd,
|
||||
int follow_symlinks)
|
||||
{
|
||||
if ((fd >= 0) && (!follow_symlinks)) {
|
||||
if (is_fd && (!follow_symlinks)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"%s: cannot use fd and follow_symlinks together",
|
||||
function_name);
|
||||
|
|
@ -2880,12 +2880,13 @@ posix_do_stat(PyObject *module, const char *function_name, path_t *path,
|
|||
|
||||
if (path_and_dir_fd_invalid("stat", path, dir_fd) ||
|
||||
dir_fd_and_fd_invalid("stat", dir_fd, path->fd) ||
|
||||
fd_and_follow_symlinks_invalid("stat", path->fd, follow_symlinks))
|
||||
fd_and_follow_symlinks_invalid("stat", path->is_fd, follow_symlinks))
|
||||
return NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
if (path->fd != -1)
|
||||
if (path->is_fd) {
|
||||
result = FSTAT(path->fd, &st);
|
||||
}
|
||||
#ifdef MS_WINDOWS
|
||||
else if (follow_symlinks)
|
||||
result = win32_stat(path->wide, &st);
|
||||
|
|
@ -3647,7 +3648,7 @@ os_statx_impl(PyObject *module, path_t *path, unsigned int mask, int flags,
|
|||
{
|
||||
if (path_and_dir_fd_invalid("statx", path, dir_fd) ||
|
||||
dir_fd_and_fd_invalid("statx", dir_fd, path->fd) ||
|
||||
fd_and_follow_symlinks_invalid("statx", path->fd, follow_symlinks)) {
|
||||
fd_and_follow_symlinks_invalid("statx", path->is_fd, follow_symlinks)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -3677,7 +3678,7 @@ os_statx_impl(PyObject *module, path_t *path, unsigned int mask, int flags,
|
|||
|
||||
int result;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
if (path->fd != -1) {
|
||||
if (path->is_fd) {
|
||||
result = statx(path->fd, "", flags | AT_EMPTY_PATH, mask, &v->stx);
|
||||
}
|
||||
else {
|
||||
|
|
@ -3934,7 +3935,7 @@ os_chdir_impl(PyObject *module, path_t *path)
|
|||
result = !win32_wchdir(path->wide);
|
||||
#else
|
||||
#ifdef HAVE_FCHDIR
|
||||
if (path->fd != -1)
|
||||
if (path->is_fd)
|
||||
result = fchdir(path->fd);
|
||||
else
|
||||
#endif
|
||||
|
|
@ -4090,7 +4091,7 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
|
|||
#ifdef MS_WINDOWS
|
||||
result = 0;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
if (path->fd != -1) {
|
||||
if (path->is_fd) {
|
||||
result = win32_fchmod(path->fd, mode);
|
||||
}
|
||||
else if (follow_symlinks) {
|
||||
|
|
@ -4113,8 +4114,9 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
|
|||
#else /* MS_WINDOWS */
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
#ifdef HAVE_FCHMOD
|
||||
if (path->fd != -1)
|
||||
if (path->is_fd) {
|
||||
result = fchmod(path->fd, mode);
|
||||
}
|
||||
else
|
||||
#endif /* HAVE_CHMOD */
|
||||
#ifdef HAVE_LCHMOD
|
||||
|
|
@ -4511,7 +4513,7 @@ os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid,
|
|||
return NULL;
|
||||
#endif
|
||||
if (dir_fd_and_fd_invalid("chown", dir_fd, path->fd) ||
|
||||
fd_and_follow_symlinks_invalid("chown", path->fd, follow_symlinks))
|
||||
fd_and_follow_symlinks_invalid("chown", path->is_fd, follow_symlinks))
|
||||
return NULL;
|
||||
|
||||
if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid,
|
||||
|
|
@ -4521,7 +4523,7 @@ os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid,
|
|||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
#ifdef HAVE_FCHOWN
|
||||
if (path->fd != -1)
|
||||
if (path->is_fd)
|
||||
result = fchown(path->fd, uid, gid);
|
||||
else
|
||||
#endif
|
||||
|
|
@ -4999,7 +5001,7 @@ _posix_listdir(path_t *path, PyObject *list)
|
|||
|
||||
errno = 0;
|
||||
#ifdef HAVE_FDOPENDIR
|
||||
if (path->fd != -1) {
|
||||
if (path->is_fd) {
|
||||
if (HAVE_FDOPENDIR_RUNTIME) {
|
||||
/* closedir() closes the FD, so we duplicate it */
|
||||
fd = _Py_dup(path->fd);
|
||||
|
|
@ -5898,7 +5900,7 @@ _testFileExists(path_t *path, BOOL followLinks)
|
|||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
if (path->fd != -1) {
|
||||
if (path->is_fd) {
|
||||
HANDLE hfile = _Py_get_osfhandle_noraise(path->fd);
|
||||
if (hfile != INVALID_HANDLE_VALUE) {
|
||||
if (GetFileType(hfile) != FILE_TYPE_UNKNOWN || !GetLastError()) {
|
||||
|
|
@ -5924,7 +5926,7 @@ _testFileType(path_t *path, int testedType)
|
|||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
if (path->fd != -1) {
|
||||
if (path->is_fd) {
|
||||
HANDLE hfile = _Py_get_osfhandle_noraise(path->fd);
|
||||
if (hfile != INVALID_HANDLE_VALUE) {
|
||||
result = _testFileTypeByHandle(hfile, testedType, TRUE);
|
||||
|
|
@ -7141,7 +7143,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
|
|||
|
||||
if (path_and_dir_fd_invalid("utime", path, dir_fd) ||
|
||||
dir_fd_and_fd_invalid("utime", dir_fd, path->fd) ||
|
||||
fd_and_follow_symlinks_invalid("utime", path->fd, follow_symlinks))
|
||||
fd_and_follow_symlinks_invalid("utime", path->is_fd, follow_symlinks))
|
||||
return NULL;
|
||||
|
||||
#if !defined(HAVE_UTIMENSAT)
|
||||
|
|
@ -7200,7 +7202,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
|
|||
#endif
|
||||
|
||||
#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS)
|
||||
if (path->fd != -1)
|
||||
if (path->is_fd)
|
||||
result = utime_fd(&utime, path->fd);
|
||||
else
|
||||
#endif
|
||||
|
|
@ -7569,7 +7571,7 @@ os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env)
|
|||
|
||||
_Py_BEGIN_SUPPRESS_IPH
|
||||
#ifdef HAVE_FEXECVE
|
||||
if (path->fd > -1)
|
||||
if (path->is_fd)
|
||||
fexecve(path->fd, argvlist, envlist);
|
||||
else
|
||||
#endif
|
||||
|
|
@ -13355,7 +13357,7 @@ os_truncate_impl(PyObject *module, path_t *path, Py_off_t length)
|
|||
int fd;
|
||||
#endif
|
||||
|
||||
if (path->fd != -1)
|
||||
if (path->is_fd)
|
||||
return os_ftruncate_impl(module, path->fd, length);
|
||||
|
||||
if (PySys_Audit("os.truncate", "On", path->object, length) < 0) {
|
||||
|
|
@ -14052,7 +14054,7 @@ os_statvfs_impl(PyObject *module, path_t *path)
|
|||
struct statfs st;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
if (path->fd != -1) {
|
||||
if (path->is_fd) {
|
||||
result = fstatfs(path->fd, &st);
|
||||
}
|
||||
else
|
||||
|
|
@ -14070,7 +14072,7 @@ os_statvfs_impl(PyObject *module, path_t *path)
|
|||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
#ifdef HAVE_FSTATVFS
|
||||
if (path->fd != -1) {
|
||||
if (path->is_fd) {
|
||||
result = fstatvfs(path->fd, &st);
|
||||
}
|
||||
else
|
||||
|
|
@ -15410,7 +15412,7 @@ os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute,
|
|||
int follow_symlinks)
|
||||
/*[clinic end generated code: output=5f2f44200a43cff2 input=025789491708f7eb]*/
|
||||
{
|
||||
if (fd_and_follow_symlinks_invalid("getxattr", path->fd, follow_symlinks))
|
||||
if (fd_and_follow_symlinks_invalid("getxattr", path->is_fd, follow_symlinks))
|
||||
return NULL;
|
||||
|
||||
if (PySys_Audit("os.getxattr", "OO", path->object, attribute->object) < 0) {
|
||||
|
|
@ -15432,7 +15434,7 @@ os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute,
|
|||
void *ptr = PyBytesWriter_GetData(writer);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
if (path->fd >= 0)
|
||||
if (path->is_fd)
|
||||
result = fgetxattr(path->fd, attribute->narrow, ptr, buffer_size);
|
||||
else if (follow_symlinks)
|
||||
result = getxattr(path->narrow, attribute->narrow, ptr, buffer_size);
|
||||
|
|
@ -15481,7 +15483,7 @@ os_setxattr_impl(PyObject *module, path_t *path, path_t *attribute,
|
|||
{
|
||||
ssize_t result;
|
||||
|
||||
if (fd_and_follow_symlinks_invalid("setxattr", path->fd, follow_symlinks))
|
||||
if (fd_and_follow_symlinks_invalid("setxattr", path->is_fd, follow_symlinks))
|
||||
return NULL;
|
||||
|
||||
if (PySys_Audit("os.setxattr", "OOy#i", path->object, attribute->object,
|
||||
|
|
@ -15490,7 +15492,7 @@ os_setxattr_impl(PyObject *module, path_t *path, path_t *attribute,
|
|||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
if (path->fd > -1)
|
||||
if (path->is_fd)
|
||||
result = fsetxattr(path->fd, attribute->narrow,
|
||||
value->buf, value->len, flags);
|
||||
else if (follow_symlinks)
|
||||
|
|
@ -15534,7 +15536,7 @@ os_removexattr_impl(PyObject *module, path_t *path, path_t *attribute,
|
|||
{
|
||||
ssize_t result;
|
||||
|
||||
if (fd_and_follow_symlinks_invalid("removexattr", path->fd, follow_symlinks))
|
||||
if (fd_and_follow_symlinks_invalid("removexattr", path->is_fd, follow_symlinks))
|
||||
return NULL;
|
||||
|
||||
if (PySys_Audit("os.removexattr", "OO", path->object, attribute->object) < 0) {
|
||||
|
|
@ -15542,7 +15544,7 @@ os_removexattr_impl(PyObject *module, path_t *path, path_t *attribute,
|
|||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
if (path->fd > -1)
|
||||
if (path->is_fd)
|
||||
result = fremovexattr(path->fd, attribute->narrow);
|
||||
else if (follow_symlinks)
|
||||
result = removexattr(path->narrow, attribute->narrow);
|
||||
|
|
@ -15584,7 +15586,7 @@ os_listxattr_impl(PyObject *module, path_t *path, int follow_symlinks)
|
|||
const char *name;
|
||||
char *buffer = NULL;
|
||||
|
||||
if (fd_and_follow_symlinks_invalid("listxattr", path->fd, follow_symlinks))
|
||||
if (fd_and_follow_symlinks_invalid("listxattr", path->is_fd, follow_symlinks))
|
||||
goto exit;
|
||||
|
||||
if (PySys_Audit("os.listxattr", "(O)",
|
||||
|
|
@ -15611,7 +15613,7 @@ os_listxattr_impl(PyObject *module, path_t *path, int follow_symlinks)
|
|||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
if (path->fd > -1)
|
||||
if (path->is_fd)
|
||||
length = flistxattr(path->fd, buffer, buffer_size);
|
||||
else if (follow_symlinks)
|
||||
length = listxattr(name, buffer, buffer_size);
|
||||
|
|
@ -16664,7 +16666,7 @@ DirEntry_from_posix_info(PyObject *module, path_t *path, const char *name,
|
|||
entry->stat = NULL;
|
||||
entry->lstat = NULL;
|
||||
|
||||
if (path->fd != -1) {
|
||||
if (path->is_fd) {
|
||||
entry->dir_fd = path->fd;
|
||||
joined_path = NULL;
|
||||
}
|
||||
|
|
@ -16689,7 +16691,7 @@ DirEntry_from_posix_info(PyObject *module, path_t *path, const char *name,
|
|||
if (!entry->name)
|
||||
goto error;
|
||||
|
||||
if (path->fd != -1) {
|
||||
if (path->is_fd) {
|
||||
entry->path = Py_NewRef(entry->name);
|
||||
}
|
||||
else if (!entry->path)
|
||||
|
|
@ -16813,8 +16815,9 @@ ScandirIterator_closedir(ScandirIterator *iterator)
|
|||
iterator->dirp = NULL;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
#ifdef HAVE_FDOPENDIR
|
||||
if (iterator->path.fd != -1)
|
||||
if (iterator->path.is_fd) {
|
||||
rewinddir(dirp);
|
||||
}
|
||||
#endif
|
||||
closedir(dirp);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
|
@ -17034,7 +17037,7 @@ os_scandir_impl(PyObject *module, path_t *path)
|
|||
#else /* POSIX */
|
||||
errno = 0;
|
||||
#ifdef HAVE_FDOPENDIR
|
||||
if (iterator->path.fd != -1) {
|
||||
if (iterator->path.is_fd) {
|
||||
if (HAVE_FDOPENDIR_RUNTIME) {
|
||||
/* closedir() closes the FD, so we duplicate it */
|
||||
fd = _Py_dup(iterator->path.fd);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue