diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -57,6 +57,8 @@ import posixpath as path import posix + if hasattr(posix, "_HAVE_OPENAT"): + _HAVE_OPENAT = posix._HAVE_OPENAT __all__.extend(_get_exports_list(posix)) del posix @@ -305,7 +307,7 @@ __all__.append("walk") -if _exists("openat"): +if _exists("_HAVE_OPENAT"): def fwalk(top, topdown=True, onerror=None, followlinks=False): """Directory tree generator. @@ -330,7 +332,7 @@ import os for root, dirs, files, rootfd in os.fwalk('python/Lib/email'): print(root, "consumes", end="") - print(sum([os.fstatat(rootfd, name).st_size for name in files]), + print(sum([os.stat(dir_fd=rootfd, path=name).st_size for name in files]), end="") print("bytes in", len(files), "non-directory files") if 'CVS' in dirs: @@ -352,10 +354,7 @@ # necessary, it can be adapted to only require O(1) FDs, see issue # #13734. - # whether to follow symlinks - flag = 0 if followlinks else AT_SYMLINK_NOFOLLOW - - names = flistdir(topfd) + names = listdir(fd=topfd) dirs, nondirs = [], [] for name in names: try: @@ -363,14 +362,14 @@ # walk() which reports symlinks to directories as directories. # We do however check for symlinks before recursing into # a subdirectory. - if st.S_ISDIR(fstatat(topfd, name).st_mode): + if st.S_ISDIR(stat(dir_fd=topfd, path=name).st_mode): dirs.append(name) else: nondirs.append(name) except FileNotFoundError: try: # Add dangling symlinks, ignore disappeared files - if st.S_ISLNK(fstatat(topfd, name, AT_SYMLINK_NOFOLLOW) + if st.S_ISLNK(stat(dir_fd=topfd, path=name, follow_symlinks=False) .st_mode): nondirs.append(name) except FileNotFoundError: @@ -381,8 +380,8 @@ for name in dirs: try: - orig_st = fstatat(topfd, name, flag) - dirfd = openat(topfd, name, O_RDONLY) + orig_st = stat(dir_fd=topfd, path=name, follow_symlinks=followlinks) + dirfd = open(name, O_RDONLY, dir_fd=topfd) except error as err: if onerror is not None: onerror(err) diff --git a/Lib/shutil.py b/Lib/shutil.py --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -141,20 +141,21 @@ def _nop(*args, ns=None): pass + utime_func = os.utime if hasattr(os, 'utime') else _nop if symlinks and os.path.islink(src) and os.path.islink(dst): stat_func = os.lstat - utime_func = os.lutimes if hasattr(os, 'lutimes') else _nop chmod_func = os.lchmod if hasattr(os, 'lchmod') else _nop + utime_follow = False chflags_func = os.lchflags if hasattr(os, 'lchflags') else _nop else: stat_func = os.stat - utime_func = os.utime if hasattr(os, 'utime') else _nop chmod_func = os.chmod if hasattr(os, 'chmod') else _nop + utime_follow = True chflags_func = os.chflags if hasattr(os, 'chflags') else _nop st = stat_func(src) mode = stat.S_IMODE(st.st_mode) - utime_func(dst, ns=(st.st_atime_ns, st.st_mtime_ns)) + utime_func(dst, ns=(st.st_atime_ns, st.st_mtime_ns), follow_symlinks=utime_follow) chmod_func(dst, mode) if hasattr(st, 'st_flags'): try: @@ -175,20 +176,10 @@ If the optional flag `symlinks` is set, symlinks won't be followed. """ - if symlinks: - listxattr = os.llistxattr - removexattr = os.lremovexattr - setxattr = os.lsetxattr - getxattr = os.lgetxattr - else: - listxattr = os.listxattr - removexattr = os.removexattr - setxattr = os.setxattr - getxattr = os.getxattr - for attr in listxattr(src): + for attr in os.listxattr(src, follow_symlinks=symlinks): try: - setxattr(dst, attr, getxattr(src, attr)) + os.setxattr(dst, attr, os.getxattr(src, attr, follow_symlinks=symlinks), follow_symlinks=symlinks) except OSError as e: if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA): raise diff --git a/Lib/test/support.py b/Lib/test/support.py --- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -1703,8 +1703,8 @@ try: # TESTFN & tempfile may use different file systems with # different capabilities - os.fsetxattr(tmp_fp, b"user.test", b"") - os.fsetxattr(fp.fileno(), b"user.test", b"") + os.setxattr(None, b"user.test", b"", fd=tmp_fp) + os.setxattr(None, b"user.test", b"", fd=fp.fileno()) # Kernels < 2.6.39 don't respect setxattr flags. kernel_version = platform.release() m = re.match("2.6.(\d{1,2})", kernel_version) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -29,6 +29,7 @@ except ImportError: threading = None from test.script_helper import assert_python_ok +import sysconfig os.stat_float_times(True) st = os.stat(__file__) @@ -343,40 +344,39 @@ return os.utime(file, ns=times) self._test_utime_ns(utime_ns) - requires_lutimes = unittest.skipUnless(hasattr(os, 'lutimes'), - "os.lutimes required for this test.") - requires_futimes = unittest.skipUnless(hasattr(os, 'futimes'), - "os.futimes required for this test.") + requires_utime_dir_fd = unittest.skipUnless( + sysconfig.get_config_var('HAVE_FUTIMESAT') or + sysconfig.get_config_var('HAVE_UTIMENSAT'), + "dir_fd support for utime required for this test.") + requires_utime_fd = unittest.skipUnless( + sysconfig.get_config_var('HAVE_FUTIMES') or + sysconfig.get_config_var('HAVE_FUTIMENS'), + "fd support for utime required for this test.") + requires_utime_nofollow_symlinks = unittest.skipUnless( + sysconfig.get_config_var('HAVE_UTIMENSAT') or + sysconfig.get_config_var('HAVE_LUTIMES'), + "follow_symlinks support for utime required for this test.") - @requires_lutimes + @requires_utime_nofollow_symlinks def test_lutimes_ns(self): def lutimes_ns(file, times): - return os.lutimes(file, ns=times) + return os.utime(file, ns=times, follow_symlinks=False) self._test_utime_ns(lutimes_ns) - @requires_futimes + @requires_utime_fd def test_futimes_ns(self): def futimes_ns(file, times): with open(file, "wb") as f: - os.futimes(f.fileno(), ns=times) + os.utime(None, fd=f.fileno(), ns=times) self._test_utime_ns(futimes_ns, test_dir=False) def _utime_invalid_arguments(self, name, arg): - with self.assertRaises(RuntimeError): + with self.assertRaises(ValueError): getattr(os, name)(arg, (5, 5), ns=(5, 5)) def test_utime_invalid_arguments(self): self._utime_invalid_arguments('utime', self.fname) - @requires_lutimes - def test_lutimes_invalid_arguments(self): - self._utime_invalid_arguments('lutimes', self.fname) - - @requires_futimes - def test_futimes_invalid_arguments(self): - with open(self.fname, "wb") as f: - self._utime_invalid_arguments('futimes', f.fileno()) - @unittest.skipUnless(stat_supports_subsecond, "os.stat() doesn't has a subsecond resolution") @@ -398,64 +398,47 @@ os.utime(filename, (atime, mtime)) self._test_utime_subsecond(set_time) - @requires_futimes + @requires_utime_fd def test_futimes_subsecond(self): def set_time(filename, atime, mtime): with open(filename, "wb") as f: - os.futimes(f.fileno(), (atime, mtime)) + os.utime(None, fd=f.fileno(), times=(atime, mtime)) self._test_utime_subsecond(set_time) - @unittest.skipUnless(hasattr(os, 'futimens'), - "os.futimens required for this test.") + @requires_utime_fd def test_futimens_subsecond(self): def set_time(filename, atime, mtime): with open(filename, "wb") as f: - asec, ansec = divmod(atime, 1.0) - asec = int(asec) - ansec = int(ansec * 1e9) - msec, mnsec = divmod(mtime, 1.0) - msec = int(msec) - mnsec = int(mnsec * 1e9) - os.futimens(f.fileno(), - (asec, ansec), - (msec, mnsec)) + os.utime(fd=f.fileno(), path=None, + times=(atime, mtime)) self._test_utime_subsecond(set_time) - @unittest.skipUnless(hasattr(os, 'futimesat'), - "os.futimesat required for this test.") + @requires_utime_dir_fd def test_futimesat_subsecond(self): def set_time(filename, atime, mtime): dirname = os.path.dirname(filename) dirfd = os.open(dirname, os.O_RDONLY) try: - os.futimesat(dirfd, os.path.basename(filename), - (atime, mtime)) + os.utime(dir_fd=dirfd, path=os.path.basename(filename), + times=(atime, mtime)) finally: os.close(dirfd) self._test_utime_subsecond(set_time) - @requires_lutimes + @requires_utime_nofollow_symlinks def test_lutimes_subsecond(self): def set_time(filename, atime, mtime): - os.lutimes(filename, (atime, mtime)) + os.utime(filename, (atime, mtime), follow_symlinks=False) self._test_utime_subsecond(set_time) - @unittest.skipUnless(hasattr(os, 'utimensat'), - "os.utimensat required for this test.") + @requires_utime_dir_fd def test_utimensat_subsecond(self): def set_time(filename, atime, mtime): dirname = os.path.dirname(filename) dirfd = os.open(dirname, os.O_RDONLY) try: - asec, ansec = divmod(atime, 1.0) - asec = int(asec) - ansec = int(ansec * 1e9) - msec, mnsec = divmod(mtime, 1.0) - msec = int(msec) - mnsec = int(mnsec * 1e9) - os.utimensat(dirfd, os.path.basename(filename), - (asec, ansec), - (msec, mnsec)) + os.utime(dir_fd=dirfd, path=os.path.basename(filename), + times=(atime, mtime)) finally: os.close(dirfd) self._test_utime_subsecond(set_time) @@ -778,8 +761,10 @@ for root, dirs, files, rootfd in os.fwalk(*args): # check that the FD is valid os.fstat(rootfd) - # check that flistdir() returns consistent information - self.assertEqual(set(os.flistdir(rootfd)), set(dirs) | set(files)) + # redundant check + os.stat(fd=rootfd) + # check that listdir() returns consistent information + self.assertEqual(set(os.listdir(fd=rootfd)), set(dirs) | set(files)) def test_fd_leak(self): # Since we're opening a lot of FDs, we must be careful to avoid leaks: @@ -798,13 +783,10 @@ # cleanup for root, dirs, files, rootfd in os.fwalk(support.TESTFN, topdown=False): for name in files: - os.unlinkat(rootfd, name) + os.unlink(dir_fd=rootfd, path=name) for name in dirs: - st = os.fstatat(rootfd, name, os.AT_SYMLINK_NOFOLLOW) - if stat.S_ISDIR(st.st_mode): - os.unlinkat(rootfd, name, os.AT_REMOVEDIR) - else: - os.unlinkat(rootfd, name) + st = os.stat(dir_fd=rootfd, path=name, follow_symlinks=False) + os.unlink(dir_fd=rootfd, path=name, remove_dir=stat.S_ISDIR(st.st_mode)) os.rmdir(support.TESTFN) @@ -1817,79 +1799,97 @@ raise -@support.skip_unless_xattr +def supports_extended_attributes(): + if not hasattr(os, "setxattr"): + return False + try: + with open(support.TESTFN, "wb") as fp: + try: + os.setxattr(None, b"user.test", b"", fd=fp.fileno()) + except OSError: + return False + finally: + support.unlink(support.TESTFN) + # Kernels < 2.6.39 don't respect setxattr flags. + kernel_version = platform.release() + m = re.match("2.6.(\d{1,2})", kernel_version) + return m is None or int(m.group(1)) >= 39 + + +@unittest.skipUnless(supports_extended_attributes(), + "no non-broken extended attribute support") class ExtendedAttributeTests(unittest.TestCase): def tearDown(self): support.unlink(support.TESTFN) - def _check_xattrs_str(self, s, getxattr, setxattr, removexattr, listxattr): + def _check_xattrs_str(self, s, getxattr, setxattr, removexattr, listxattr, **kwargs): fn = support.TESTFN open(fn, "wb").close() with self.assertRaises(OSError) as cm: - getxattr(fn, s("user.test")) + getxattr(fn, s("user.test"), **kwargs) self.assertEqual(cm.exception.errno, errno.ENODATA) init_xattr = listxattr(fn) self.assertIsInstance(init_xattr, list) - setxattr(fn, s("user.test"), b"") + setxattr(fn, s("user.test"), b"", **kwargs) xattr = set(init_xattr) xattr.add("user.test") self.assertEqual(set(listxattr(fn)), xattr) - self.assertEqual(getxattr(fn, b"user.test"), b"") - setxattr(fn, s("user.test"), b"hello", os.XATTR_REPLACE) - self.assertEqual(getxattr(fn, b"user.test"), b"hello") + self.assertEqual(getxattr(fn, b"user.test", **kwargs), b"") + setxattr(fn, s("user.test"), b"hello", os.XATTR_REPLACE, **kwargs) + self.assertEqual(getxattr(fn, b"user.test", **kwargs), b"hello") with self.assertRaises(OSError) as cm: - setxattr(fn, s("user.test"), b"bye", os.XATTR_CREATE) + setxattr(fn, s("user.test"), b"bye", os.XATTR_CREATE, **kwargs) self.assertEqual(cm.exception.errno, errno.EEXIST) with self.assertRaises(OSError) as cm: - setxattr(fn, s("user.test2"), b"bye", os.XATTR_REPLACE) + setxattr(fn, s("user.test2"), b"bye", os.XATTR_REPLACE, **kwargs) self.assertEqual(cm.exception.errno, errno.ENODATA) - setxattr(fn, s("user.test2"), b"foo", os.XATTR_CREATE) + setxattr(fn, s("user.test2"), b"foo", os.XATTR_CREATE, **kwargs) xattr.add("user.test2") self.assertEqual(set(listxattr(fn)), xattr) - removexattr(fn, s("user.test")) + removexattr(fn, s("user.test"), **kwargs) with self.assertRaises(OSError) as cm: - getxattr(fn, s("user.test")) + getxattr(fn, s("user.test"), **kwargs) self.assertEqual(cm.exception.errno, errno.ENODATA) xattr.remove("user.test") self.assertEqual(set(listxattr(fn)), xattr) - self.assertEqual(getxattr(fn, s("user.test2")), b"foo") - setxattr(fn, s("user.test"), b"a"*1024) - self.assertEqual(getxattr(fn, s("user.test")), b"a"*1024) - removexattr(fn, s("user.test")) + self.assertEqual(getxattr(fn, s("user.test2"), **kwargs), b"foo") + setxattr(fn, s("user.test"), b"a"*1024, **kwargs) + self.assertEqual(getxattr(fn, s("user.test"), **kwargs), b"a"*1024) + removexattr(fn, s("user.test"), **kwargs) many = sorted("user.test{}".format(i) for i in range(100)) for thing in many: - setxattr(fn, thing, b"x") + setxattr(fn, thing, b"x", **kwargs) self.assertEqual(set(listxattr(fn)), set(init_xattr) | set(many)) - def _check_xattrs(self, *args): + def _check_xattrs(self, *args, **kwargs): def make_bytes(s): return bytes(s, "ascii") - self._check_xattrs_str(str, *args) + self._check_xattrs_str(str, *args, **kwargs) support.unlink(support.TESTFN) - self._check_xattrs_str(make_bytes, *args) + self._check_xattrs_str(make_bytes, *args, **kwargs) def test_simple(self): self._check_xattrs(os.getxattr, os.setxattr, os.removexattr, os.listxattr) def test_lpath(self): - self._check_xattrs(os.lgetxattr, os.lsetxattr, os.lremovexattr, - os.llistxattr) + self._check_xattrs(os.getxattr, os.setxattr, os.removexattr, + os.listxattr, follow_symlinks=False) def test_fds(self): def getxattr(path, *args): with open(path, "rb") as fp: - return os.fgetxattr(fp.fileno(), *args) + return os.getxattr(None, *args, fd=fp.fileno()) def setxattr(path, *args): with open(path, "wb") as fp: - os.fsetxattr(fp.fileno(), *args) + os.setxattr(None, *args, fd=fp.fileno()) def removexattr(path, *args): with open(path, "wb") as fp: - os.fremovexattr(fp.fileno(), *args) + os.removexattr(None, *args, fd=fp.fileno()) def listxattr(path, *args): with open(path, "rb") as fp: - return os.flistxattr(fp.fileno(), *args) + return os.listxattr(None, fd=fp.fileno(), *args) self._check_xattrs(getxattr, setxattr, removexattr, listxattr) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -14,6 +14,7 @@ import pwd import shutil import stat +import sysconfig import tempfile import unittest import warnings @@ -129,6 +130,7 @@ fp = open(support.TESTFN) try: self.assertTrue(posix.fstatvfs(fp.fileno())) + self.assertTrue(posix.statvfs(fd=fp.fileno())) finally: fp.close() @@ -150,7 +152,7 @@ fp.flush() posix.truncate(support.TESTFN, 0) - @unittest.skipUnless(hasattr(posix, 'fexecve'), "test needs posix.fexecve()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_FEXECVE'), "test needs execve() to support the fd parameter") @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()") def test_fexecve(self): @@ -159,7 +161,7 @@ pid = os.fork() if pid == 0: os.chdir(os.path.split(sys.executable)[0]) - posix.fexecve(fp, [sys.executable, '-c', 'pass'], os.environ) + posix.execve(None, [sys.executable, '-c', 'pass'], os.environ, fd=fp) else: self.assertEqual(os.waitpid(pid, 0), (pid, 0)) finally: @@ -234,43 +236,43 @@ finally: os.close(fd) - @unittest.skipUnless(hasattr(posix, 'futimes'), "test needs posix.futimes()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_FUTIMES'), "test needs posix futimes()") def test_futimes(self): now = time.time() fd = os.open(support.TESTFN, os.O_RDONLY) try: - posix.futimes(fd, None) - posix.futimes(fd) - self.assertRaises(TypeError, posix.futimes, fd, (None, None)) - self.assertRaises(TypeError, posix.futimes, fd, (now, None)) - self.assertRaises(TypeError, posix.futimes, fd, (None, now)) - posix.futimes(fd, (int(now), int(now))) - posix.futimes(fd, (now, now)) + posix.utime(None, None, fd=fd) + posix.utime(None, fd=fd) + self.assertRaises(TypeError, posix.utime, None, (None, None), fd=fd) + self.assertRaises(TypeError, posix.utime, None, (now, None), fd=fd) + self.assertRaises(TypeError, posix.utime, None, (None, now), fd=fd) + posix.utime(None, (int(now), int(now)), fd=fd) + posix.utime(None, (now, now), fd=fd) finally: os.close(fd) - @unittest.skipUnless(hasattr(posix, 'lutimes'), "test needs posix.lutimes()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_LUTIMES'), "test needs posix lutimes()") def test_lutimes(self): now = time.time() - posix.lutimes(support.TESTFN, None) - self.assertRaises(TypeError, posix.lutimes, support.TESTFN, (None, None)) - self.assertRaises(TypeError, posix.lutimes, support.TESTFN, (now, None)) - self.assertRaises(TypeError, posix.lutimes, support.TESTFN, (None, now)) - posix.lutimes(support.TESTFN, (int(now), int(now))) - posix.lutimes(support.TESTFN, (now, now)) - posix.lutimes(support.TESTFN) + posix.utime(support.TESTFN, None, follow_symlinks=False) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None), follow_symlinks=False) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None), follow_symlinks=False) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now), follow_symlinks=False) + posix.utime(support.TESTFN, (int(now), int(now)), follow_symlinks=False) + posix.utime(support.TESTFN, (now, now), follow_symlinks=False) + posix.utime(support.TESTFN, follow_symlinks=False) - @unittest.skipUnless(hasattr(posix, 'futimens'), "test needs posix.futimens()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_FUTIMENS'), "test needs posix futimens()") def test_futimens(self): now = time.time() fd = os.open(support.TESTFN, os.O_RDONLY) try: - self.assertRaises(TypeError, posix.futimens, fd, (None, None), (None, None)) - self.assertRaises(TypeError, posix.futimens, fd, (now, 0), None) - self.assertRaises(TypeError, posix.futimens, fd, None, (now, 0)) - posix.futimens(fd, (int(now), int((now - int(now)) * 1e9)), - (int(now), int((now - int(now)) * 1e9))) - posix.futimens(fd) + self.assertRaises(ValueError, posix.utime, None, (now, now), ns=(now, now), fd=fd) + self.assertRaises(ValueError, posix.utime, None, (now, 0), ns=(None, None), fd=fd) + self.assertRaises(ValueError, posix.utime, None, (None, None), ns=(now, 0), fd=fd) + posix.utime(None, (int(now), int((now - int(now)) * 1e9)), fd=fd) + posix.utime(None, ns=(int(now), int((now - int(now)) * 1e9)), fd=fd) + posix.utime(None, fd=fd) finally: os.close(fd) @@ -364,6 +366,7 @@ fp = open(support.TESTFN) try: self.assertTrue(posix.fstat(fp.fileno())) + self.assertTrue(posix.stat(fd=fp.fileno())) finally: fp.close() @@ -462,18 +465,18 @@ if hasattr(posix, 'listdir'): self.assertTrue(support.TESTFN in posix.listdir()) - @unittest.skipUnless(hasattr(posix, 'flistdir'), "test needs posix.flistdir()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_FDOPENDIR'), "test needs posix. istdir() to support fd") def test_flistdir(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) self.addCleanup(posix.close, f) self.assertEqual( sorted(posix.listdir('.')), - sorted(posix.flistdir(f)) + sorted(posix.listdir(fd=f)) ) # Check that the fd offset was reset (issue #13739) self.assertEqual( sorted(posix.listdir('.')), - sorted(posix.flistdir(f)) + sorted(posix.listdir(fd=f)) ) def test_access(self): @@ -532,10 +535,10 @@ posix.utime(support.TESTFN, (int(now), int(now))) posix.utime(support.TESTFN, (now, now)) - def _test_chflags_regular_file(self, chflags_func, target_file): + def _test_chflags_regular_file(self, chflags_func, target_file, kwargs): st = os.stat(target_file) self.assertTrue(hasattr(st, 'st_flags')) - chflags_func(target_file, st.st_flags | stat.UF_IMMUTABLE) + chflags_func(target_file, st.st_flags | stat.UF_IMMUTABLE, **kwargs) try: new_st = os.stat(target_file) self.assertEqual(st.st_flags | stat.UF_IMMUTABLE, new_st.st_flags) @@ -548,11 +551,12 @@ @unittest.skipUnless(hasattr(posix, 'chflags'), 'test needs os.chflags()') def test_chflags(self): - self._test_chflags_regular_file(posix.chflags, support.TESTFN) + self._test_chflags_regular_file(posix.chflags, support.TESTFN, {}) @unittest.skipUnless(hasattr(posix, 'lchflags'), 'test needs os.lchflags()') def test_lchflags_regular_file(self): - self._test_chflags_regular_file(posix.lchflags, support.TESTFN) + self._test_chflags_regular_file(posix.lchflags, support.TESTFN, {}) + self._test_chflags_regular_file(posix.chflags, support.TESTFN, {"follow_symlinks" : False}) @unittest.skipUnless(hasattr(posix, 'lchflags'), 'test needs os.lchflags()') def test_lchflags_symlink(self): @@ -564,17 +568,21 @@ self.teardown_files.append(_DUMMY_SYMLINK) dummy_symlink_st = os.lstat(_DUMMY_SYMLINK) - posix.lchflags(_DUMMY_SYMLINK, - dummy_symlink_st.st_flags | stat.UF_IMMUTABLE) - try: - new_testfn_st = os.stat(support.TESTFN) - new_dummy_symlink_st = os.lstat(_DUMMY_SYMLINK) + def chflags_nofollow(path, flags): + return posix.chflags(path, flags, follow_symlinks=False) - self.assertEqual(testfn_st.st_flags, new_testfn_st.st_flags) - self.assertEqual(dummy_symlink_st.st_flags | stat.UF_IMMUTABLE, - new_dummy_symlink_st.st_flags) - finally: - posix.lchflags(_DUMMY_SYMLINK, dummy_symlink_st.st_flags) + for fn in (posix.lchflags, chflags_nofollow): + fn(_DUMMY_SYMLINK, + dummy_symlink_st.st_flags | stat.UF_IMMUTABLE) + try: + new_testfn_st = os.stat(support.TESTFN) + new_dummy_symlink_st = os.lstat(_DUMMY_SYMLINK) + + self.assertEqual(testfn_st.st_flags, new_testfn_st.st_flags) + self.assertEqual(dummy_symlink_st.st_flags | stat.UF_IMMUTABLE, + new_dummy_symlink_st.st_flags) + finally: + fn(_DUMMY_SYMLINK, dummy_symlink_st.st_flags) def test_environ(self): if os.name == "nt": @@ -657,39 +665,39 @@ # tests for the posix *at functions follow - @unittest.skipUnless(hasattr(posix, 'faccessat'), "test needs posix.faccessat()") + @unittest.skipUnless(sysconfig.get_config_var("HAVE_FACCESSAT"), "test needs posix faccessat()") def test_faccessat(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - self.assertTrue(posix.faccessat(f, support.TESTFN, os.R_OK)) + self.assertTrue(posix.access(support.TESTFN, os.R_OK, dir_fd=f)) finally: posix.close(f) - @unittest.skipUnless(hasattr(posix, 'fchmodat'), "test needs posix.fchmodat()") + @unittest.skipUnless(sysconfig.get_config_var("HAVE_FCHMODAT"), "test needs posix fchmodat()") def test_fchmodat(self): os.chmod(support.TESTFN, stat.S_IRUSR) f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.fchmodat(f, support.TESTFN, stat.S_IRUSR | stat.S_IWUSR) + posix.chmod(support.TESTFN, stat.S_IRUSR | stat.S_IWUSR, dir_fd=f) s = posix.stat(support.TESTFN) self.assertEqual(s[0] & stat.S_IRWXU, stat.S_IRUSR | stat.S_IWUSR) finally: posix.close(f) - @unittest.skipUnless(hasattr(posix, 'fchownat'), "test needs posix.fchownat()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_FCHOWNAT'), "test needs posix fchownat()") def test_fchownat(self): support.unlink(support.TESTFN) support.create_empty_file(support.TESTFN) f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.fchownat(f, support.TESTFN, os.getuid(), os.getgid()) + posix.chown(support.TESTFN, os.getuid(), os.getgid(), dir_fd=f) finally: posix.close(f) - @unittest.skipUnless(hasattr(posix, 'fstatat'), "test needs posix.fstatat()") + @unittest.skipUnless(sysconfig.get_config_var("HAS_FSTATAT"), "test needs posix fstatat()") def test_fstatat(self): support.unlink(support.TESTFN) with open(support.TESTFN, 'w') as outfile: @@ -698,31 +706,31 @@ f = posix.open(posix.getcwd(), posix.O_RDONLY) try: s1 = posix.stat(support.TESTFN) - s2 = posix.fstatat(f, support.TESTFN) + s2 = posix.stat(dir_fd=f, path=support.TESTFN) self.assertEqual(s1, s2) finally: posix.close(f) - @unittest.skipUnless(hasattr(posix, 'futimesat'), "test needs posix.futimesat()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_FUTIMESAT'), "test needs posix futimesat()") def test_futimesat(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: now = time.time() - posix.futimesat(f, support.TESTFN, None) - posix.futimesat(f, support.TESTFN) - self.assertRaises(TypeError, posix.futimesat, f, support.TESTFN, (None, None)) - self.assertRaises(TypeError, posix.futimesat, f, support.TESTFN, (now, None)) - self.assertRaises(TypeError, posix.futimesat, f, support.TESTFN, (None, now)) - posix.futimesat(f, support.TESTFN, (int(now), int(now))) - posix.futimesat(f, support.TESTFN, (now, now)) + posix.utime(support.TESTFN, None, dir_fd=f) + posix.utime(support.TESTFN, dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None), dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None), dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now), dir_fd=f) + posix.utime(support.TESTFN, (int(now), int(now)), dir_fd=f) + posix.utime(support.TESTFN, (now, now), dir_fd=f) finally: posix.close(f) - @unittest.skipUnless(hasattr(posix, 'linkat'), "test needs posix.linkat()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_LINKAT'), "test needs posix linkat()") def test_linkat(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.linkat(f, support.TESTFN, f, support.TESTFN + 'link') + posix.link(src_dir_fd=f, src=support.TESTFN, dst_dir_fd=f, dst=support.TESTFN + 'link') # should have same inodes self.assertEqual(posix.stat(support.TESTFN)[1], posix.stat(support.TESTFN + 'link')[1]) @@ -730,17 +738,17 @@ posix.close(f) support.unlink(support.TESTFN + 'link') - @unittest.skipUnless(hasattr(posix, 'mkdirat'), "test needs posix.mkdirat()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_MKDIRAT'), "test needs posix mkdirat()") def test_mkdirat(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.mkdirat(f, support.TESTFN + 'dir') + posix.mkdir(dir_fd=f, path=support.TESTFN + 'dir') posix.stat(support.TESTFN + 'dir') # should not raise exception finally: posix.close(f) support.rmtree(support.TESTFN + 'dir') - @unittest.skipUnless(hasattr(posix, 'mknodat') and hasattr(stat, 'S_IFIFO'), + @unittest.skipUnless(sysconfig.get_config_var('HAVE_MKNODAT') and hasattr(stat, 'S_IFIFO'), "don't have mknodat()/S_IFIFO") def test_mknodat(self): # Test using mknodat() to create a FIFO (the only use specified @@ -749,7 +757,7 @@ mode = stat.S_IFIFO | stat.S_IRUSR | stat.S_IWUSR f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.mknodat(f, support.TESTFN, mode, 0) + posix.mknod(support.TESTFN, mode, 0, dir_fd=f) except OSError as e: # Some old systems don't allow unprivileged users to use # mknod(), or only support creating device nodes. @@ -759,13 +767,13 @@ finally: posix.close(f) - @unittest.skipUnless(hasattr(posix, 'openat'), "test needs posix.openat()") + @unittest.skipUnless(sysconfig.get_config_var("HAVE_OPENAT"), "test needs posix openat()") def test_openat(self): support.unlink(support.TESTFN) with open(support.TESTFN, 'w') as outfile: outfile.write("testline\n") a = posix.open(posix.getcwd(), posix.O_RDONLY) - b = posix.openat(a, support.TESTFN, posix.O_RDONLY) + b = posix.open(support.TESTFN, posix.O_RDONLY, dir_fd=a) try: res = posix.read(b, 9).decode(encoding="utf-8") self.assertEqual("testline\n", res) @@ -773,24 +781,24 @@ posix.close(a) posix.close(b) - @unittest.skipUnless(hasattr(posix, 'readlinkat'), "test needs posix.readlinkat()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_READLINKAT'), "test needs posix readlinkat()") def test_readlinkat(self): os.symlink(support.TESTFN, support.TESTFN + 'link') f = posix.open(posix.getcwd(), posix.O_RDONLY) try: self.assertEqual(posix.readlink(support.TESTFN + 'link'), - posix.readlinkat(f, support.TESTFN + 'link')) + posix.readlink(support.TESTFN + 'link', dir_fd=f)) finally: support.unlink(support.TESTFN + 'link') posix.close(f) - @unittest.skipUnless(hasattr(posix, 'renameat'), "test needs posix.renameat()") + @unittest.skipUnless(sysconfig.get_config_var("HAVE_RENAMEAT"), "test needs posix renameat()") def test_renameat(self): support.unlink(support.TESTFN) support.create_empty_file(support.TESTFN + 'ren') f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.renameat(f, support.TESTFN + 'ren', f, support.TESTFN) + posix.rename(src_dir_fd=f, src=support.TESTFN + 'ren', dst_dir_fd=f, dst=support.TESTFN) except: posix.rename(support.TESTFN + 'ren', support.TESTFN) raise @@ -799,23 +807,23 @@ finally: posix.close(f) - @unittest.skipUnless(hasattr(posix, 'symlinkat'), "test needs posix.symlinkat()") + @unittest.skipUnless(sysconfig.get_config_var("HAVE_SYMLINKAT"), "test needs posix symlinkat()") def test_symlinkat(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.symlinkat(support.TESTFN, f, support.TESTFN + 'link') + posix.symlink(support.TESTFN, support.TESTFN + 'link', dir_fd=f) self.assertEqual(posix.readlink(support.TESTFN + 'link'), support.TESTFN) finally: posix.close(f) support.unlink(support.TESTFN + 'link') - @unittest.skipUnless(hasattr(posix, 'unlinkat'), "test needs posix.unlinkat()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_UNLINKAT'), "test needs posix unlinkat()") def test_unlinkat(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) support.create_empty_file(support.TESTFN + 'del') posix.stat(support.TESTFN + 'del') # should not throw exception try: - posix.unlinkat(f, support.TESTFN + 'del') + posix.unlink(dir_fd=f, path=support.TESTFN + 'del') except: support.unlink(support.TESTFN + 'del') raise @@ -824,31 +832,30 @@ finally: posix.close(f) - @unittest.skipUnless(hasattr(posix, 'utimensat'), "test needs posix.utimensat()") + @unittest.skipUnless(sysconfig.get_config_var('HAVE_UTIMENSAT'), "test needs posix utimensat()") def test_utimensat(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: now = time.time() - posix.utimensat(f, support.TESTFN, None, None) - posix.utimensat(f, support.TESTFN) - posix.utimensat(f, support.TESTFN, flags=os.AT_SYMLINK_NOFOLLOW) - self.assertRaises(TypeError, posix.utimensat, f, support.TESTFN, (None, None), (None, None)) - self.assertRaises(TypeError, posix.utimensat, f, support.TESTFN, (now, 0), None) - self.assertRaises(TypeError, posix.utimensat, f, support.TESTFN, None, (now, 0)) - posix.utimensat(f, support.TESTFN, (int(now), int((now - int(now)) * 1e9)), - (int(now), int((now - int(now)) * 1e9))) - posix.utimensat(dirfd=f, path=support.TESTFN, - atime=(int(now), int((now - int(now)) * 1e9)), - mtime=(int(now), int((now - int(now)) * 1e9))) + posix.utime(support.TESTFN, (now, now), dir_fd=f) + posix.utime(support.TESTFN, dir_fd=f) + posix.utime(support.TESTFN, follow_symlinks=False, dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None), dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, "x"), dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, now, dir_fd=f) + posix.utime(support.TESTFN, + (int(now), int((now - int(now)) * 1e9)), dir_fd=f) + posix.utime(dir_fd=f, path=support.TESTFN, + times=(int(now), int((now - int(now)) * 1e9))) finally: posix.close(f) - @unittest.skipUnless(hasattr(posix, 'mkfifoat'), "don't have mkfifoat()") + @unittest.skipUnless(sysconfig.get_config_var("HAVE_MKFIFOAT"), "don't have posix mkfifoat()") def test_mkfifoat(self): support.unlink(support.TESTFN) f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.mkfifoat(f, support.TESTFN, stat.S_IRUSR | stat.S_IWUSR) + posix.mkfifo(support.TESTFN, stat.S_IRUSR | stat.S_IWUSR, dir_fd=f) self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) finally: posix.close(f) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -19,6 +19,7 @@ get_archive_formats, Error, unpack_archive, register_unpack_format, RegistryError, unregister_unpack_format, get_unpack_formats) +import sysconfig import tarfile import warnings @@ -268,7 +269,7 @@ # don't follow shutil.copystat(src_link, dst_link, symlinks=True) dst_link_stat = os.lstat(dst_link) - if hasattr(os, 'lutimes'): + if sysconfig.get_config_var('HAVE_LUTIMES'): for attr in 'st_atime', 'st_mtime': # The modification times may be truncated in the new file. self.assertLessEqual(getattr(src_link_stat, attr), @@ -334,11 +335,11 @@ write_file(dst, 'bar') os_error = OSError(errno.EPERM, 'EPERM') - def _raise_on_user_foo(fname, attr, val): + def _raise_on_user_foo(fname, attr, val, **kwargs): if attr == 'user.foo': raise os_error else: - orig_setxattr(fname, attr, val) + orig_setxattr(fname, attr, val, **kwargs) try: orig_setxattr = os.setxattr os.setxattr = _raise_on_user_foo @@ -361,13 +362,13 @@ write_file(src, 'foo') os.symlink(src, src_link) os.setxattr(src, 'trusted.foo', b'42') - os.lsetxattr(src_link, 'trusted.foo', b'43') + os.setxattr(src_link, 'trusted.foo', b'43', follow_symlinks=False) dst = os.path.join(tmp_dir, 'bar') dst_link = os.path.join(tmp_dir, 'qux') write_file(dst, 'bar') os.symlink(dst, dst_link) shutil._copyxattr(src_link, dst_link, symlinks=True) - self.assertEqual(os.lgetxattr(dst_link, 'trusted.foo'), b'43') + self.assertEqual(os.getxattr(dst_link, 'trusted.foo', follow_symlinks=False), b'43') self.assertRaises(OSError, os.getxattr, dst, 'trusted.foo') shutil._copyxattr(src_link, dst, symlinks=True) self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43') @@ -419,7 +420,7 @@ self.assertTrue(os.path.islink(dst)) self.assertEqual(os.readlink(dst), os.readlink(src_link)) dst_stat = os.lstat(dst) - if hasattr(os, 'lutimes'): + if sysconfig.get_config_var('HAVE_LUTIMES'): for attr in 'st_atime', 'st_mtime': # The modification times may be truncated in the new file. self.assertLessEqual(getattr(src_link_stat, attr), diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -379,10 +379,12 @@ #undef STRUCT_STAT #if defined(MS_WIN64) || defined(MS_WINDOWS) # define STAT win32_stat +# define LSTAT win32_lstat # define FSTAT win32_fstat # define STRUCT_STAT struct win32_stat #else # define STAT stat +# define LSTAT lstat # define FSTAT fstat # define STRUCT_STAT struct stat #endif @@ -398,6 +400,275 @@ #endif #endif +/* + * A PyArg_ParseTuple "converter" function + * that handles filesystem paths in the manner + * preferred by the os module. + * + * path_converter accepts (Unicode) strings and their + * subclasses, and bytes and their subclasses. What + * it does with the argument depends on the platform: + * + * * On Windows, if we get a (Unicode) string we + * extract the wchar_t * and return it; if we get + * bytes we extract the char * and return that. + * + * * On all other platforms, strings are encoded + * to bytes using PyUnicode_FSConverter, then we + * extract the char * from the bytes object and + * return that. + * + * Input fields: + * path.nullable + * If nonzero, the path is permitted to be None. + * path.function_name + * If non-NULL, path_converter will use that as the name + * of the function in error messages. + * (If path.argument_name is NULL it omits the function name.) + * path.argument_name + * If non-NULL, path_converter will use that as the name + * of the parameter in error messages. + * (If path.argument_name is NULL it uses "path".) + * + * Output fields: + * path.wide + * Points to the path if it was expressed as Unicode + * and was not encoded. (Only used on Windows.) + * path.narrow + * Points to the path if it was expressed as bytes, + * or it was Unicode and was encoded to bytes. + * path.length + * The length of the path in characters. + * path.object + * The original object passed in. + * path.cleanup + * For internal use only. May point to a temporary object. + * (Pay no attention to the man behind the curtain.) + * + * At most one of path.wide or path.narrow will be non-NULL. + * If path was None and path.nullable was set, + * both path.wide and path.narrow will be NULL, + * and path.length will be 0. + * + * path_converter takes care to not write to the path_t + * unless it's successful. However it must reset the + * "cleanup" field each time it's called. + * + * Use as follows: + * path_t path; + * memset(&path, 0, sizeof(path)); + * PyArg_ParseTuple(args, "O&", path_converter, &path); + * // ... use values from path ... + * path_cleanup(&path); + * + * (Note that if PyArg_Parse fails you don't need to call + * path_cleanup(). However it is safe to do so.) + */ +typedef struct { + char *function_name; + char *argument_name; + int nullable; + wchar_t *wide; + char *narrow; + Py_ssize_t length; + PyObject *object; + PyObject *cleanup; +} path_t; + +static void path_cleanup(path_t *path) { + if (path->cleanup) { + Py_DECREF(path->cleanup); + path->cleanup = NULL; + } +} + +static int path_converter(PyObject *o, void *p) { + path_t *path = (path_t *)p; + PyObject *unicode, *bytes; + Py_ssize_t length; + char *narrow; + +#define FORMAT_EXCEPTION(exc, fmt) \ + PyErr_Format(exc, "%s%s" fmt, \ + path->function_name ? path->function_name : "", \ + path->function_name ? ": " : "", \ + path->argument_name ? path->argument_name : "path") + + /* Py_CLEANUP_SUPPORTED support */ + if (o == NULL) { + path_cleanup(path); + return 1; + } + + /* ensure it's always safe to call path_cleanup() */ + path->cleanup = NULL; + + if (o == Py_None) { + if (!path->nullable) { + FORMAT_EXCEPTION(PyExc_TypeError, "can't specify None for %s argument"); + return 0; + } + path->wide = NULL; + path->narrow = NULL; + path->length = 0; + path->object = o; + return 1; + } + + unicode = PyUnicode_FromObject(o); + if (unicode) { +#ifdef MS_WINDOWS + wchar_t *wide; + length = PyUnicode_GET_SIZE(unicode); + if (length > 32767) { + FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows"); + Py_DECREF(unicode); + return 0; + } + + wide = PyUnicode_AsUnicode(unicode); + if (!wide) { + Py_DECREF(unicode); + return 0; + } + + path->wide = wide; + path->narrow = NULL; + path->length = length; + path->object = o; + path->cleanup = unicode; + return Py_CLEANUP_SUPPORTED; +#else + int converted = PyUnicode_FSConverter(unicode, &bytes); + Py_DECREF(unicode); + if (!converted) + bytes = NULL; +#endif + } + else { + PyErr_Clear(); + bytes = PyBytes_FromObject(o); + if (!bytes) + PyErr_Clear(); + } + + if (!bytes) { + if (!PyErr_Occurred()) + FORMAT_EXCEPTION(PyExc_TypeError, "illegal type for %s parameter"); + return 0; + } + +#ifdef MS_WINDOWS + if (win32_warn_bytes_api()) { + Py_DECREF(bytes); + return 0; + } +#endif + + length = PyBytes_GET_SIZE(bytes); +#ifdef MS_WINDOWS + if (length > MAX_PATH) { + FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows"); + Py_DECREF(bytes); + return 0; + } +#endif + + narrow = PyBytes_AS_STRING(bytes); + if (length != strlen(narrow)) { + FORMAT_EXCEPTION(PyExc_ValueError, "embedded NUL character in %s"); + Py_DECREF(bytes); + return 0; + } + + path->wide = NULL; + path->narrow = narrow; + path->length = length; + path->object = o; + path->cleanup = bytes; + return Py_CLEANUP_SUPPORTED; +} + +static void +argument_unavailable_error(char *function_name, char *argument_name) { + PyErr_Format(PyExc_NotImplementedError, + "%s: %s unavailable on this platform", + function_name, argument_name); +} + +#ifdef AT_FDCWD +#define DEFAULT_DIR_FD AT_FDCWD +#else +#define DEFAULT_DIR_FD (-100) +#endif + +static int +dir_fd_specified(char *function_name, int dir_fd) { + if (dir_fd == DEFAULT_DIR_FD) + return 0; + + argument_unavailable_error(function_name, "dir_fd"); + return 1; +} + +static int +fd_specified(char *function_name, int fd) { + if (fd == -1) + return 0; + + argument_unavailable_error(function_name, "fd"); + return 1; +} + +static int +follow_symlinks_specified(char *function_name, int follow_symlinks) { + if (follow_symlinks) + return 0; + + argument_unavailable_error(function_name, "follow_symlinks"); + return 1; +} + +static int +path_and_fd_invalid(char *function_name, path_t *path, int fd) { + if ((path->narrow == NULL) && (fd < 0)) { + PyErr_Format(PyExc_ValueError, "%s: must specify either valid path or valid fd", function_name); + return 1; + } + if ((path->narrow != NULL) && (fd > 0)) { + PyErr_Format(PyExc_ValueError, "%s: can't specify both path and fd", function_name); + return 1; + } + return 0; +} + +static int +path_and_dir_fd_invalid(char *function_name, path_t *path, int dir_fd) { + if ((path->narrow == NULL) && (dir_fd != DEFAULT_DIR_FD)) { + PyErr_Format(PyExc_ValueError, "%s: can't specify dir_fd without matching path", function_name); + return 1; + } + return 0; +} + +static int +dir_fd_and_fd_invalid(char *function_name, int dir_fd, int fd) { + if ((dir_fd != DEFAULT_DIR_FD) && (fd != -1)) { + PyErr_Format(PyExc_ValueError, "%s: can't specify both dir_fd and fd", function_name); + return 1; + } + return 0; +} + +static int +fd_and_follow_symlinks_invalid(char *function_name, int fd, int follow_symlinks) { + if ((fd > 0) && (!follow_symlinks)) { + PyErr_Format(PyExc_ValueError, "%s: cannot use fd and follow_symlinks together", function_name); + return 1; + } + return 0; +} + /* A helper used by a number of POSIX-only functions */ #ifndef MS_WINDOWS static int @@ -575,24 +846,6 @@ 1); } -static PyObject* -win32_decode_filename(PyObject *obj) -{ - PyObject *unicode; - if (PyUnicode_Check(obj)) { - if (PyUnicode_READY(obj)) - return NULL; - Py_INCREF(obj); - return obj; - } - if (!PyUnicode_FSDecoder(obj, &unicode)) - return NULL; - if (win32_warn_bytes_api()) { - Py_DECREF(unicode); - return NULL; - } - return unicode; -} #endif /* MS_WINDOWS */ /* Return a dictionary corresponding to the POSIX environment table */ @@ -774,6 +1027,19 @@ #endif /* MS_WINDOWS */ +static PyObject * +path_error(char *function_name, path_t *path) { +#ifdef MS_WINDOWS + if (path->narrow || path->wide) + return win32_error_object(function_name, path->object); + return win32_error(function_name); +#else + if (path->narrow) + return posix_error_with_filename(path->narrow); + return posix_error(); +#endif +} + #if defined(PYOS_OS2) /********************************************************************** * Helper Function to Trim and Format OS/2 Messages @@ -892,32 +1158,6 @@ return Py_None; } -static PyObject * -posix_2str(PyObject *args, - char *format, - int (*func)(const char *, const char *)) -{ - PyObject *opath1 = NULL, *opath2 = NULL; - char *path1, *path2; - int res; - if (!PyArg_ParseTuple(args, format, - PyUnicode_FSConverter, &opath1, - PyUnicode_FSConverter, &opath2)) { - return NULL; - } - path1 = PyBytes_AsString(opath1); - path2 = PyBytes_AsString(opath2); - Py_BEGIN_ALLOW_THREADS - res = (*func)(path1, path2); - Py_END_ALLOW_THREADS - Py_DECREF(opath1); - Py_DECREF(opath2); - if (res != 0) - /* XXX how to report both path1 and path2??? */ - return posix_error(); - Py_INCREF(Py_None); - return Py_None; -} #ifdef MS_WINDOWS static PyObject* @@ -1875,132 +2115,206 @@ return v; } -static PyObject * -posix_do_stat(PyObject *self, PyObject *args, - char *format, -#ifdef __VMS - int (*statfunc)(const char *, STRUCT_STAT *, ...), -#else - int (*statfunc)(const char *, STRUCT_STAT *), -#endif - char *wformat, - int (*wstatfunc)(const wchar_t *, STRUCT_STAT *)) +/* POSIX methods */ + + +static PyObject * +posix_do_stat(char *function_name, path_t *path, int dir_fd, int fd, int follow_symlinks) { STRUCT_STAT st; - PyObject *opath; - char *path; - int res; - PyObject *result; - + int result; + +#ifndef HAVE_FSTATAT + if (dir_fd_specified(function_name, dir_fd)) + return NULL; +#endif + +#if !defined(MS_WINDOWS) && !defined(HAVE_FSTATAT) && !defined(HAVE_LSTAT) + if (follow_symlinks_specified(function_name, follow_symlinks)) + return NULL; +#endif + + if (path_and_dir_fd_invalid("stat", path, dir_fd) || + path_and_fd_invalid("stat", path, fd) || + dir_fd_and_fd_invalid("stat", dir_fd, fd) || + fd_and_follow_symlinks_invalid("stat", fd, follow_symlinks)) + return NULL; + + Py_BEGIN_ALLOW_THREADS #ifdef MS_WINDOWS - PyObject *po; - if (PyArg_ParseTuple(args, wformat, &po)) { - wchar_t *wpath = PyUnicode_AsUnicode(po); - if (wpath == NULL) - return NULL; - - Py_BEGIN_ALLOW_THREADS - res = wstatfunc(wpath, &st); - Py_END_ALLOW_THREADS - - if (res != 0) - return win32_error_object("stat", po); - return _pystat_fromstructstat(&st); - } - /* Drop the argument parsing error as narrow strings - are also valid. */ - PyErr_Clear(); -#endif - - if (!PyArg_ParseTuple(args, format, - PyUnicode_FSConverter, &opath)) - return NULL; -#ifdef MS_WINDOWS - if (win32_warn_bytes_api()) { - Py_DECREF(opath); - return NULL; - } -#endif - path = PyBytes_AsString(opath); - Py_BEGIN_ALLOW_THREADS - res = (*statfunc)(path, &st); + if (path->wide) { + if (follow_symlinks) + result = win32_stat_w(path->wide, &st); + else + result = win32_lstat_w(path->wide, &st); + } + else +#endif +#ifdef HAVE_FSTATAT + if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) + result = fstatat(dir_fd, path->narrow, &st, follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); + else +#endif + if (fd != -1) + result = FSTAT(fd, &st); + else +#if defined(HAVE_LSTAT) || defined(MS_WINDOWS) + if (!follow_symlinks) + result = LSTAT(path->narrow, &st); + else +#endif + result = STAT(path->narrow, &st); Py_END_ALLOW_THREADS - if (res != 0) { -#ifdef MS_WINDOWS - result = win32_error("stat", path); -#else - result = posix_error_with_filename(path); -#endif - } - else - result = _pystat_fromstructstat(&st); - - Py_DECREF(opath); - return result; -} - -/* POSIX methods */ + if (result != 0) + return path_error("stat", path); + + return _pystat_fromstructstat(&st); +} + +PyDoc_STRVAR(posix_stat__doc__, +"stat(path, *, dir_fd=AT_FDCWD, fd=-1, follow_symlinks=True) -> stat result\n\n\ +Perform a stat system call on the given path."); + +static PyObject * +posix_stat(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *keywords[] = {"path", "dir_fd", "fd", "follow_symlinks", NULL}; + path_t path; + int dir_fd = DEFAULT_DIR_FD; + int fd = -1; + int follow_symlinks = 1; + PyObject *return_value; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&$iip:stat", keywords, + path_converter, &path, &dir_fd, &fd, &follow_symlinks)) + return NULL; + return_value = posix_do_stat("stat", &path, dir_fd, fd, follow_symlinks); + path_cleanup(&path); + return return_value; +} + +PyDoc_STRVAR(posix_lstat__doc__, +"lstat(path, *, dir_fd=AT_FDCWD) -> stat result\n\n\ +Like stat(), but do not follow symbolic links."); + +static PyObject * +posix_lstat(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *keywords[] = {"path", "dir_fd", NULL}; + path_t path; + int dir_fd = DEFAULT_DIR_FD; + int fd = -1; + int follow_symlinks = 0; + PyObject *return_value; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$i:lstat", keywords, + path_converter, &path, &dir_fd)) + return NULL; + return_value = posix_do_stat("stat", &path, dir_fd, fd, follow_symlinks); + path_cleanup(&path); + return return_value; +} PyDoc_STRVAR(posix_access__doc__, -"access(path, mode) -> True if granted, False otherwise\n\n\ -Use the real uid/gid to test for access to a path. Note that most\n\ -operations will use the effective uid/gid, therefore this routine can\n\ -be used in a suid/sgid environment to test if the invoking user has the\n\ -specified access to the path. The mode argument can be F_OK to test\n\ +"access(path, mode, *, dir_fd=AT_FDCWD, effective_ids=0, follow_symlinks=True)\n\n\ +Use the real uid/gid to test for access to a path. Returns True if granted,\n\ +False otherwise.\n\ +\n\ +Note that most operations will use the effective uid/gid, therefore this\n\ +routine can be used in a suid/sgid environment to test if the invoking user\n\ +has the specified access to the path.\n\ +The mode argument can be F_OK to test\n\ existence, or the inclusive-OR of R_OK, W_OK, and X_OK."); static PyObject * -posix_access(PyObject *self, PyObject *args) -{ - const char *path; +posix_access(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *keywords[] = { "path", "mode", "dir_fd", "effective_ids", "follow_symlinks", NULL }; + path_t path; int mode; + int dir_fd = DEFAULT_DIR_FD; + int effective_ids = 0; + int follow_symlinks = 1; + PyObject *return_value = NULL; #ifdef MS_WINDOWS DWORD attr; - PyObject *po; - if (PyArg_ParseTuple(args, "Ui:access", &po, &mode)) { - wchar_t* wpath = PyUnicode_AsUnicode(po); - if (wpath == NULL) - return NULL; - Py_BEGIN_ALLOW_THREADS - attr = GetFileAttributesW(wpath); - Py_END_ALLOW_THREADS - goto finish; - } - /* Drop the argument parsing error as narrow strings - are also valid. */ - PyErr_Clear(); - if (!PyArg_ParseTuple(args, "yi:access", &path, &mode)) - return NULL; - if (win32_warn_bytes_api()) - return NULL; + int accessible; + const int allow_none_for_path = 0; +#else + int result; + const int allow_none_for_path = 1; +#endif + + memset(&path, 0, sizeof(path)); + path.nullable = allow_none_for_path; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&i|$ipp:access", keywords, + path_converter, &path, &mode, &dir_fd, &effective_ids, &follow_symlinks)) + return NULL; + +#ifndef HAVE_FACCESSAT + if (dir_fd_specified("access", dir_fd)) + goto exit; + + if (follow_symlinks_specified("access", follow_symlinks)) + goto exit; + + if (effective_ids) { + argument_unavailable_error("access", "effective_ids"); + goto exit; + } +#endif + +#ifdef MS_WINDOWS Py_BEGIN_ALLOW_THREADS - attr = GetFileAttributesA(path); + if (wide != NULL) + attr = GetFileAttributesW(path.narrow); + else + attr = GetFileAttributesA(path.wide); Py_END_ALLOW_THREADS -finish: - if (attr == 0xFFFFFFFF) - /* File does not exist, or cannot read attributes */ - return PyBool_FromLong(0); - /* Access is possible if either write access wasn't requested, or - the file isn't read-only, or if it's a directory, as there are - no read-only directories on Windows. */ - return PyBool_FromLong(!(mode & 2) - || !(attr & FILE_ATTRIBUTE_READONLY) - || (attr & FILE_ATTRIBUTE_DIRECTORY)); + + /* + * Access is possible if + * * we didn't get a -1, and + * * write access wasn't requested, + * * or the file isn't read-only, + * * or it's a directory. + * (Directories cannot be read-only on Windows.) + */ + return_value = PyBool_FromLong( + (attr != 0xFFFFFFFF) && + ((mode & 2) || + !(attr & FILE_ATTRIBUTE_READONLY) || + (attr & FILE_ATTRIBUTE_DIRECTORY))); #else - PyObject *opath; - int res; - if (!PyArg_ParseTuple(args, "O&i:access", - PyUnicode_FSConverter, &opath, &mode)) - return NULL; - path = PyBytes_AsString(opath); + Py_BEGIN_ALLOW_THREADS - res = access(path, mode); +#ifdef HAVE_FACCESSAT + if ((dir_fd != DEFAULT_DIR_FD) || + effective_ids || + !follow_symlinks) { + int flags = 0; + if (!follow_symlinks) + flags |= AT_SYMLINK_NOFOLLOW; + if (effective_ids) + flags |= AT_EACCESS; + result = faccessat(dir_fd, path.narrow, mode, flags); + } + else +#endif + result = access(path.narrow, mode); Py_END_ALLOW_THREADS - Py_DECREF(opath); - return PyBool_FromLong(res == 0); -#endif + return_value = PyBool_FromLong(!result); +#endif + +#ifndef HAVE_FACCESSAT +exit: +#endif + path_cleanup(&path); + return return_value; } #ifndef F_OK @@ -2070,21 +2384,62 @@ #endif PyDoc_STRVAR(posix_chdir__doc__, -"chdir(path)\n\n\ +"chdir(path=None, *, fd=-1)\n\n\ Change the current working directory to the specified path."); static PyObject * -posix_chdir(PyObject *self, PyObject *args) -{ +posix_chdir(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + int fd = -1; + int result; + PyObject *return_value = NULL; + static char *keywords[] = {"path", "fd", NULL}; + + memset(&path, 0, sizeof(path)); + path.nullable = 1; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&$i:chdir", keywords, + path_converter, &path, + &fd)) + return NULL; + +#ifdef HAVE_FCHDIR + if (path_and_fd_invalid("chdir", &path, fd)) + goto exit; +#else + if (fd_specified("chdir", fd)) + goto exit; +#endif + + Py_BEGIN_ALLOW_THREADS #ifdef MS_WINDOWS - return win32_1str(args, "chdir", "y:chdir", win32_chdir, "U:chdir", win32_wchdir); + if (path.wide) + result = win32_wchdir(path.wide); + else + result = win32_chdir(path.narrow); #elif defined(PYOS_OS2) && defined(PYCC_GCC) - return posix_1str(args, "O&:chdir", _chdir2); -#elif defined(__VMS) - return posix_1str(args, "O&:chdir", (int (*)(const char *))chdir); + result = _chdir2(path.narrow); #else - return posix_1str(args, "O&:chdir", chdir); -#endif +#ifdef HAVE_FCHDIR + if (fd != -1) + result = fchdir(fd); + else +#endif + result = chdir(path.narrow); +#endif + Py_END_ALLOW_THREADS + + if (result) { + return_value = path_error("chdir", &path); + goto exit; + } + + return_value = Py_None; + Py_INCREF(Py_None); + +exit: + path_cleanup(&path); + return return_value; } #ifdef HAVE_FCHDIR @@ -2102,81 +2457,103 @@ PyDoc_STRVAR(posix_chmod__doc__, -"chmod(path, mode)\n\n\ +"chmod(path, mode, *, fd=-1, dir_fd=AT_FDCWD, follow_symlinks=True)\n\n\ Change the access permissions of a file."); static PyObject * -posix_chmod(PyObject *self, PyObject *args) -{ - PyObject *opath = NULL; - const char *path = NULL; - int i; - int res; +posix_chmod(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + int mode; + int dir_fd = DEFAULT_DIR_FD; + int fd = -1; + int follow_symlinks = 1; + int result; + PyObject *return_value = NULL; + static char *keywords[] = {"path", "mode", "dir_fd", "follow_symlinks", NULL}; + #ifdef MS_WINDOWS DWORD attr; - PyObject *po; - if (PyArg_ParseTuple(args, "Ui|:chmod", &po, &i)) { - wchar_t *wpath = PyUnicode_AsUnicode(po); - if (wpath == NULL) - return NULL; - Py_BEGIN_ALLOW_THREADS - attr = GetFileAttributesW(wpath); - if (attr != 0xFFFFFFFF) { - if (i & _S_IWRITE) - attr &= ~FILE_ATTRIBUTE_READONLY; - else - attr |= FILE_ATTRIBUTE_READONLY; - res = SetFileAttributesW(wpath, attr); - } - else - res = 0; - Py_END_ALLOW_THREADS - if (!res) - return win32_error_object("chmod", po); - Py_INCREF(Py_None); - return Py_None; - } - /* Drop the argument parsing error as narrow strings - are also valid. */ - PyErr_Clear(); - - if (!PyArg_ParseTuple(args, "yi:chmod", &path, &i)) - return NULL; - if (win32_warn_bytes_api()) - return NULL; +#endif + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&i|$ip:chmod", keywords, + path_converter, &path, + &mode, &dir_fd, &follow_symlinks)) + return NULL; + +#ifndef HAVE_FCHMODAT + if (dir_fd_specified("chmod", dir_fd)) + goto exit; +#endif + +#ifndef HAVE_FCHMOD + if (fd_specified("chmod", fd)) + goto exit; +#endif + +#if !(defined(HAVE_FCHMODAT) || defined(HAVE_LCHMOD)) + if (follow_symlinks_specified("chmod", follow_symlinks)) + goto exit; +#endif + +#ifdef MS_WINDOWS Py_BEGIN_ALLOW_THREADS - attr = GetFileAttributesA(path); - if (attr != 0xFFFFFFFF) { - if (i & _S_IWRITE) + if (path.wide) + attr = GetFileAttributesW(path.wide); + else + attr = GetFileAttributesA(path.narrow); + if (attr == 0xFFFFFFFF) + result = 0; + else { + if (mode & _S_IWRITE) attr &= ~FILE_ATTRIBUTE_READONLY; else attr |= FILE_ATTRIBUTE_READONLY; - res = SetFileAttributesA(path, attr); - } + if (path.wide) + result = SetFileAttributesW(path.wide, attr); + else + result = SetFileAttributesA(path.narrow, attr); + } + Py_END_ALLOW_THREADS + + if (!result) { + return_value = win32_error_object("chmod", path.object); + goto exit; + } +#else /* MS_WINDOWS */ + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_FCHMODAT + if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) + result = fchmodat(dir_fd, path.narrow, mode, follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); else - res = 0; +#endif +#ifdef HAVE_FCHMOD + if (fd != -1) + result = fchmod(fd, mode); + else +#endif +#ifdef HAVE_LCHMOD + if (!follow_symlinks) + result = lchmod(fd, mode); + else +#endif + result = chmod(path.narrow, mode); Py_END_ALLOW_THREADS - if (!res) { - win32_error("chmod", path); - return NULL; - } + + if (result) { + return_value = path_error("chmod", &path); + goto exit; + } +#endif + Py_INCREF(Py_None); - return Py_None; -#else /* MS_WINDOWS */ - if (!PyArg_ParseTuple(args, "O&i:chmod", PyUnicode_FSConverter, - &opath, &i)) - return NULL; - path = PyBytes_AsString(opath); - Py_BEGIN_ALLOW_THREADS - res = chmod(path, i); - Py_END_ALLOW_THREADS - if (res < 0) - return posix_error_with_allocated_filename(opath); - Py_DECREF(opath); - Py_INCREF(Py_None); - return Py_None; -#endif -} + return_value = Py_None; +exit: + path_cleanup(&path); + return return_value; +} + #ifdef HAVE_FCHMOD PyDoc_STRVAR(posix_fchmod__doc__, @@ -2233,24 +2610,46 @@ Set file flags."); static PyObject * -posix_chflags(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *path; +posix_chflags(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; unsigned long flags; - int res; - if (!PyArg_ParseTuple(args, "O&k:chflags", - PyUnicode_FSConverter, &opath, &flags)) - return NULL; - path = PyBytes_AsString(opath); + int follow_symlinks = 1; + int result; + PyObject *return_value; + static char *keywords[] = {"path", "flags", "follow_symlinks", NULL}; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&k|$i:chflags", keywords, + path_converter, &path, + &flags, &follow_symlinks)) + return NULL; + +#ifndef HAVE_LCHFLAGS + if (follow_symlinks_specified("chflags", follow_symlinks)) + goto exit; +#endif + Py_BEGIN_ALLOW_THREADS - res = chflags(path, flags); +#ifdef HAVE_LCHFLAGS + if (!follow_symlinks) + result = lchflags(path.narrow, flags); + else +#endif + result = chflags(path.narrow, flags); Py_END_ALLOW_THREADS - if (res < 0) - return posix_error_with_allocated_filename(opath); - Py_DECREF(opath); + + if (result) { + return_value = path_error("chflags", &path); + goto exit; + } + + return_value = Py_None; Py_INCREF(Py_None); - return Py_None; + +exit: + path_cleanup(&path); + return return_value; } #endif /* HAVE_CHFLAGS */ @@ -2342,29 +2741,76 @@ #ifdef HAVE_CHOWN PyDoc_STRVAR(posix_chown__doc__, -"chown(path, uid, gid)\n\n\ +"chown(path, uid, gid, *, dir_fd=AT_FDCWD, fd=-1, follow_symlinks=True)\n\n\ Change the owner and group id of path to the numeric uid and gid."); static PyObject * -posix_chown(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *path; - long uid, gid; - int res; - if (!PyArg_ParseTuple(args, "O&ll:chown", - PyUnicode_FSConverter, &opath, - &uid, &gid)) - return NULL; - path = PyBytes_AsString(opath); +posix_chown(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + long uid_l, gid_l; + uid_t uid; + gid_t gid; + int dir_fd = DEFAULT_DIR_FD; + int fd = -1; + int follow_symlinks = 1; + int result; + PyObject *return_value; + static char *keywords[] = {"path", "uid", "gid", "dir_fd", "fd", "follow_symlinks", NULL}; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&ll|$iip:chown", keywords, + path_converter, &path, + &uid_l, &gid_l, &dir_fd, &fd, &follow_symlinks)) + return NULL; + +#ifndef HAVE_FCHOWNAT + if (dir_fd_specified("chown", dir_fd)) + goto exit; +#endif + +#ifndef HAVE_FCHOWN + if (fd_specified("chown", fd)) + goto exit; +#endif + +#if !(defined(HAVE_LCHOWN) || defined(HAVE_FCHOWNAT)) + if (follow_symlinks_specified("chown", follow_symlinks)) + goto exit; +#endif + Py_BEGIN_ALLOW_THREADS - res = chown(path, (uid_t) uid, (gid_t) gid); + uid = (uid_t)uid_l; + gid = (uid_t)gid_l; +#ifdef HAVE_FCHOWN + if ((dir_fd != -1) || (!follow_symlinks)) + result = fchownat(dir_fd, path.narrow, uid, gid, follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); + else +#endif +#ifdef HAVE_FCHOWN + if (fd != -1) + result = fchown(fd, uid, gid); + else +#endif +#ifdef HAVE_LCHOWN + if (!follow_symlinks) + result = lchown(path.narrow, uid, gid); + else +#endif + result = chown(path.narrow, uid, gid); Py_END_ALLOW_THREADS - if (res < 0) - return posix_error_with_allocated_filename(opath); - Py_DECREF(opath); + + if (result) { + return_value = path_error("chown", &path); + goto exit; + } + + return_value = Py_None; Py_INCREF(Py_None); - return Py_None; + +exit: + path_cleanup(&path); + return return_value; } #endif /* HAVE_CHOWN */ @@ -2497,83 +2943,97 @@ } #endif +#if ((!defined(HAVE_LINK)) && defined(MS_WINDOWS)) +#define HAVE_LINK 1 +#endif #ifdef HAVE_LINK PyDoc_STRVAR(posix_link__doc__, -"link(src, dst)\n\n\ +"link(src, dst, src_dir_fd=AT_FDCWD, dst_dir_fd=AT_FDCWD, follow_symlinks=True)\n\n\ Create a hard link to a file."); static PyObject * -posix_link(PyObject *self, PyObject *args) -{ - return posix_2str(args, "O&O&:link", link); -} -#endif /* HAVE_LINK */ - +posix_link(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t src, dst; + int src_dir_fd, dst_dir_fd; + int follow_symlinks = 1; + PyObject *return_value = NULL; + static char *keywords[] = {"src", "dst", "src_dir_fd", "dst_dir_fd", "follow_symlinks", NULL}; #ifdef MS_WINDOWS -PyDoc_STRVAR(win32_link__doc__, -"link(src, dst)\n\n\ -Create a hard link to a file."); - -static PyObject * -win32_link(PyObject *self, PyObject *args) -{ - PyObject *src, *dst; - BOOL ok; - - if (PyArg_ParseTuple(args, "UU:link", &src, &dst)) - { - wchar_t *wsrc, *wdst; - - wsrc = PyUnicode_AsUnicode(src); - if (wsrc == NULL) - goto error; - wdst = PyUnicode_AsUnicode(dst); - if (wdst == NULL) - goto error; - - Py_BEGIN_ALLOW_THREADS - ok = CreateHardLinkW(wdst, wsrc, NULL); - Py_END_ALLOW_THREADS - - if (!ok) - return win32_error("link", NULL); - Py_RETURN_NONE; - } - else { - PyErr_Clear(); - if (!PyArg_ParseTuple(args, "O&O&:link", - PyUnicode_FSConverter, &src, - PyUnicode_FSConverter, &dst)) - return NULL; - - if (win32_warn_bytes_api()) - goto error; - - Py_BEGIN_ALLOW_THREADS - ok = CreateHardLinkA(PyBytes_AS_STRING(dst), - PyBytes_AS_STRING(src), - NULL); - Py_END_ALLOW_THREADS - - Py_XDECREF(src); - Py_XDECREF(dst); - - if (!ok) - return win32_error("link", NULL); - Py_RETURN_NONE; - - error: - Py_XDECREF(src); - Py_XDECREF(dst); - return NULL; - } -} -#endif /* MS_WINDOWS */ + BOOL result; +#else + int result; +#endif + + memset(&src, 0, sizeof(src)); + memset(&dst, 0, sizeof(dst)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&|iip:link", keywords, + path_converter, &src, + path_converter, &dst, + &src_dir_fd, + &dst_dir_fd, + &follow_symlinks)) + return NULL; + +#ifndef HAVE_LINKAT + if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) { + argument_unavailable_error("link", "src_dir_fd and dst_dir_fd"); + goto exit; + } +#endif + + if (src.narrow && !dst.narrow) { + PyErr_SetString(PyExc_NotImplementedError, "link: src and dst must be the same type"); + goto exit; + } + +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + if (src.wide) + result = CreateHardLinkW(src.wide, dst.wide, NULL); + else + result = CreateHardLinkA(src.narrow, dst.narrow, NULL); + Py_END_ALLOW_THREADS + + if (!result) { + return_value = win32_error_object("link", dst.object); + goto exit; + } +#else + Py_BEGIN_ALLOW_THREADS +#ifndef HAVE_LINKAT + if ((src_dir_fd != DEFAULT_DIR_FD) || + (dst_dir_fd != DEFAULT_DIR_FD) || + (!follow_symlinks)) + result = linkat(src_dir_fd, src.narrow, + dst_dir_fd, dst.narrow, + follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); + else +#endif + result = link(src.narrow, dst.narrow); + Py_END_ALLOW_THREADS + + if (result) { + return_value = path_error("link", &dst); + goto exit; + } +#endif + + return_value = Py_None; + Py_INCREF(Py_None); + +exit: + path_cleanup(&src); + path_cleanup(&dst); + return return_value; +} +#endif + PyDoc_STRVAR(posix_listdir__doc__, -"listdir([path]) -> list_of_strings\n\n\ +"listdir([path], *, fd=-1) -> list_of_strings\n\n\ Return a list containing the names of the entries in the directory.\n\ \n\ path: path of directory to list (default: '.')\n\ @@ -2582,40 +3042,79 @@ entries '.' and '..' even if they are present in the directory."); static PyObject * -posix_listdir(PyObject *self, PyObject *args) -{ - /* XXX Should redo this putting the (now four) versions of opendir - in separate files instead of having them all here... */ +posix_listdir(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + int fd = -1; + PyObject *list = NULL; + static char *keywords[] = {"path", "fd", NULL}; + #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR) - - PyObject *d, *v; - HANDLE hFindFile; + PyObject *v; + HANDLE hFindFile = INVALID_HANDLE_VALUE; BOOL result; WIN32_FIND_DATA FileData; + Py_ssize_t len; const char *path; Py_ssize_t pathlen; char namebuf[MAX_PATH+5]; /* Overallocate for \\*.*\0 */ char *bufptr = namebuf; Py_ssize_t len = sizeof(namebuf)-5; /* only claim to have space for MAX_PATH */ - PyObject *po = NULL; - if (PyArg_ParseTuple(args, "|U:listdir", &po)) { + wchar_t *wnamebuf = NULL; +#elif defined(PYOS_OS2) +#ifndef MAX_PATH +#define MAX_PATH CCHMAXPATH +#endif + char *name; + char *pt; + PyObject *v; + char namebuf[MAX_PATH+5]; + HDIR hdir = 1; + ULONG srchcnt = 1; + FILEFINDBUF3 ep; + APIRET rc; +#else + char *name; + PyObject *v; + DIR *dirp = NULL; + struct dirent *ep; + int arg_is_unicode = 1; +#endif + + memset(&path, 0, sizeof(path)); + path.nullable = 1; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&$i:listdir", keywords, + path_converter, &path, + &fd)) + return NULL; + +#ifndef HAVE_FDOPENDIR + if (fd_specified("listdir", fd)) + goto exit; +#endif + + /* XXX Should redo this putting the (now four) versions of opendir + in separate files instead of having them all here... */ +#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR) + if (!path.narrow) { WIN32_FIND_DATAW wFileData; - wchar_t *wnamebuf, *po_wchars; - - if (po == NULL) { /* Default arg: "." */ + wchar_t *po_wchars; + + if (!path.wide) { /* Default arg: "." */ po_wchars = L"."; len = 1; } else { - po_wchars = PyUnicode_AsUnicodeAndSize(po, &len); + len = sizeof(namebuf)-5; + po_wchars = PyUnicode_AsUnicodeAndSize(path.wide, &len); if (po_wchars == NULL) - return NULL; + goto exit; } /* Overallocate for \\*.*\0 */ wnamebuf = malloc((len + 5) * sizeof(wchar_t)); if (!wnamebuf) { PyErr_NoMemory(); - return NULL; + goto exit; } wcscpy(wnamebuf, po_wchars); if (len > 0) { @@ -2624,23 +3123,20 @@ wnamebuf[len++] = L'\\'; wcscpy(wnamebuf + len, L"*.*"); } - if ((d = PyList_New(0)) == NULL) { - free(wnamebuf); - return NULL; + if ((list = PyList_New(0)) == NULL) { + goto exit; } Py_BEGIN_ALLOW_THREADS hFindFile = FindFirstFileW(wnamebuf, &wFileData); Py_END_ALLOW_THREADS if (hFindFile == INVALID_HANDLE_VALUE) { int error = GetLastError(); - if (error == ERROR_FILE_NOT_FOUND) { - free(wnamebuf); - return d; - } - Py_DECREF(d); + if (error == ERROR_FILE_NOT_FOUND) + goto exit; + Py_DECREF(list); + list = NULL; win32_error_unicode("FindFirstFileW", wnamebuf); - free(wnamebuf); - return NULL; + goto exit; } do { /* Skip over . and .. */ @@ -2648,14 +3144,14 @@ wcscmp(wFileData.cFileName, L"..") != 0) { v = PyUnicode_FromWideChar(wFileData.cFileName, wcslen(wFileData.cFileName)); if (v == NULL) { - Py_DECREF(d); - d = NULL; + Py_DECREF(list); + list = NULL; break; } - if (PyList_Append(d, v) != 0) { + if (PyList_Append(list, v) != 0) { Py_DECREF(v); - Py_DECREF(d); - d = NULL; + Py_DECREF(list); + list = NULL; break; } Py_DECREF(v); @@ -2666,37 +3162,16 @@ /* FindNextFile sets error to ERROR_NO_MORE_FILES if it got to the end of the directory. */ if (!result && GetLastError() != ERROR_NO_MORE_FILES) { - Py_DECREF(d); - win32_error_unicode("FindNextFileW", wnamebuf); - FindClose(hFindFile); - free(wnamebuf); - return NULL; + Py_DECREF(list); + list = win32_error_unicode("FindNextFileW", wnamebuf); + goto exit; } } while (result == TRUE); - if (FindClose(hFindFile) == FALSE) { - Py_DECREF(d); - win32_error_unicode("FindClose", wnamebuf); - free(wnamebuf); - return NULL; - } - free(wnamebuf); - return d; - } - /* Drop the argument parsing error as narrow strings - are also valid. */ - PyErr_Clear(); - - if (!PyArg_ParseTuple(args, "y#:listdir", &path, &pathlen)) - return NULL; - if (win32_warn_bytes_api()) - return NULL; - if (pathlen+1 > MAX_PATH) { - PyErr_SetString(PyExc_ValueError, "path too long"); - return NULL; - } - strcpy(namebuf, path); - len = pathlen; + goto exit; + } + strcpy(namebuf, path.narrow); + len = path.length; if (len > 0) { char ch = namebuf[len-1]; if (ch != SEP && ch != ALTSEP && ch != ':') @@ -2704,7 +3179,7 @@ strcpy(namebuf + len, "*.*"); } - if ((d = PyList_New(0)) == NULL) + if ((list = PyList_New(0)) == NULL) return NULL; Py_BEGIN_ALLOW_THREADS @@ -2713,9 +3188,10 @@ if (hFindFile == INVALID_HANDLE_VALUE) { int error = GetLastError(); if (error == ERROR_FILE_NOT_FOUND) - return d; - Py_DECREF(d); - return win32_error("FindFirstFile", namebuf); + goto exit; + Py_DECREF(list); + list = win32_error("FindFirstFile", namebuf); + goto exit; } do { /* Skip over . and .. */ @@ -2723,14 +3199,14 @@ strcmp(FileData.cFileName, "..") != 0) { v = PyBytes_FromString(FileData.cFileName); if (v == NULL) { - Py_DECREF(d); - d = NULL; + Py_DECREF(list); + list = NULL; break; } - if (PyList_Append(d, v) != 0) { + if (PyList_Append(list, v) != 0) { Py_DECREF(v); - Py_DECREF(d); - d = NULL; + Py_DECREF(list); + list = NULL; break; } Py_DECREF(v); @@ -2741,46 +3217,33 @@ /* FindNextFile sets error to ERROR_NO_MORE_FILES if it got to the end of the directory. */ if (!result && GetLastError() != ERROR_NO_MORE_FILES) { - Py_DECREF(d); - win32_error("FindNextFile", namebuf); - FindClose(hFindFile); - return NULL; + Py_DECREF(list); + list = win32_error("FindNextFile", namebuf); + goto exit; } } while (result == TRUE); - if (FindClose(hFindFile) == FALSE) { - Py_DECREF(d); - return win32_error("FindClose", namebuf); - } - - return d; +exit: + if (hFindFile != INVALID_HANDLE_VALUE) { + if (FindClose(hFindFile) == FALSE) { + if (list != NULL) { + Py_DECREF(list); + list = win32_error_object("FindClose", path.object); + } + } + } + if (wnamebuf) + free(wnamebuf); + path_cleanup(&path); + + return list; #elif defined(PYOS_OS2) - -#ifndef MAX_PATH -#define MAX_PATH CCHMAXPATH -#endif - PyObject *oname; - char *name, *pt; - Py_ssize_t len; - PyObject *d, *v; - char namebuf[MAX_PATH+5]; - HDIR hdir = 1; - ULONG srchcnt = 1; - FILEFINDBUF3 ep; - APIRET rc; - - if (!PyArg_ParseTuple(args, "O&:listdir", - PyUnicode_FSConverter, &oname)) - return NULL; - name = PyBytes_AsString(oname); - len = PyBytes_GET_SIZE(oname); - if (len >= MAX_PATH) { - Py_DECREF(oname); + if (path.length >= MAX_PATH) { PyErr_SetString(PyExc_ValueError, "path too long"); - return NULL; - } - strcpy(namebuf, name); + goto exit; + } + strcpy(namebuf, path.narrow); for (pt = namebuf; *pt; pt++) if (*pt == ALTSEP) *pt = SEP; @@ -2788,9 +3251,8 @@ namebuf[len++] = SEP; strcpy(namebuf + len, "*.*"); - if ((d = PyList_New(0)) == NULL) { - Py_DECREF(oname); - return NULL; + if ((list = PyList_New(0)) == NULL) { + goto exit; } rc = DosFindFirst(namebuf, /* Wildcard Pattern to Match */ @@ -2802,7 +3264,9 @@ if (rc != NO_ERROR) { errno = ENOENT; - return posix_error_with_allocated_filename(oname); + Py_DECREF(list); + list = posix_error_with_filename(path.narrow); + goto exit; } if (srchcnt > 0) { /* If Directory is NOT Totally Empty, */ @@ -2818,29 +3282,25 @@ v = PyBytes_FromString(namebuf); if (v == NULL) { - Py_DECREF(d); - d = NULL; + Py_DECREF(list); + list = NULL; break; } - if (PyList_Append(d, v) != 0) { + if (PyList_Append(list, v) != 0) { Py_DECREF(v); - Py_DECREF(d); - d = NULL; + Py_DECREF(list); + list = NULL; break; } Py_DECREF(v); } while (DosFindNext(hdir, &ep, sizeof(ep), &srchcnt) == NO_ERROR && srchcnt > 0); } - Py_DECREF(oname); - return d; +exit: + path_cleanup(&path); + + return list; #else - PyObject *oname; - char *name; - PyObject *d, *v; - DIR *dirp; - struct dirent *ep; - int arg_is_unicode = 1; errno = 0; /* v is never read, so it does not need to be initialized yet. */ @@ -2848,25 +3308,37 @@ arg_is_unicode = 0; PyErr_Clear(); } - oname = NULL; - if (!PyArg_ParseTuple(args, "|O&:listdir", PyUnicode_FSConverter, &oname)) - return NULL; - if (oname == NULL) { /* Default arg: "." */ - oname = PyBytes_FromString("."); - } - name = PyBytes_AsString(oname); - Py_BEGIN_ALLOW_THREADS - dirp = opendir(name); - Py_END_ALLOW_THREADS + name = path.narrow ? path.narrow : "."; +#ifdef HAVE_FDOPENDIR + if (fd != -1) { + /* closedir() closes the FD, so we duplicate it */ + Py_BEGIN_ALLOW_THREADS + fd = dup(fd); + Py_END_ALLOW_THREADS + + if (fd == -1) { + list = posix_error(); + goto exit; + } + + Py_BEGIN_ALLOW_THREADS + dirp = fdopendir(fd); + Py_END_ALLOW_THREADS + } + else +#endif + { + Py_BEGIN_ALLOW_THREADS + dirp = opendir(name); + Py_END_ALLOW_THREADS + } + if (dirp == NULL) { - return posix_error_with_allocated_filename(oname); - } - if ((d = PyList_New(0)) == NULL) { - Py_BEGIN_ALLOW_THREADS - closedir(dirp); - Py_END_ALLOW_THREADS - Py_DECREF(oname); - return NULL; + list = path_error("listdir", &path); + goto exit; + } + if ((list = PyList_New(0)) == NULL) { + goto exit; } for (;;) { errno = 0; @@ -2877,11 +3349,9 @@ if (errno == 0) { break; } else { - Py_BEGIN_ALLOW_THREADS - closedir(dirp); - Py_END_ALLOW_THREADS - Py_DECREF(d); - return posix_error_with_allocated_filename(oname); + Py_DECREF(list); + list = path_error("listdir", &path); + goto exit; } } if (ep->d_name[0] == '.' && @@ -2893,101 +3363,33 @@ else v = PyBytes_FromStringAndSize(ep->d_name, NAMLEN(ep)); if (v == NULL) { - Py_CLEAR(d); + Py_CLEAR(list); break; } - if (PyList_Append(d, v) != 0) { + if (PyList_Append(list, v) != 0) { Py_DECREF(v); - Py_CLEAR(d); + Py_CLEAR(list); break; } Py_DECREF(v); } - Py_BEGIN_ALLOW_THREADS - closedir(dirp); - Py_END_ALLOW_THREADS - Py_DECREF(oname); - - return d; + +exit: + if (dirp != NULL) { + Py_BEGIN_ALLOW_THREADS + if (fd > -1) + rewinddir(dirp); + closedir(dirp); + Py_END_ALLOW_THREADS + } + + path_cleanup(&path); + + return list; #endif /* which OS */ } /* end of posix_listdir */ -#ifdef HAVE_FDOPENDIR -PyDoc_STRVAR(posix_flistdir__doc__, -"flistdir(fd) -> list_of_strings\n\n\ -Like listdir(), but uses a file descriptor instead."); - -static PyObject * -posix_flistdir(PyObject *self, PyObject *args) -{ - PyObject *d, *v; - DIR *dirp; - struct dirent *ep; - int fd; - - errno = 0; - if (!PyArg_ParseTuple(args, "i:flistdir", &fd)) - return NULL; - /* closedir() closes the FD, so we duplicate it */ - fd = dup(fd); - if (fd < 0) - return posix_error(); - Py_BEGIN_ALLOW_THREADS - dirp = fdopendir(fd); - Py_END_ALLOW_THREADS - if (dirp == NULL) { - close(fd); - return posix_error(); - } - if ((d = PyList_New(0)) == NULL) { - Py_BEGIN_ALLOW_THREADS - closedir(dirp); - Py_END_ALLOW_THREADS - return NULL; - } - for (;;) { - errno = 0; - Py_BEGIN_ALLOW_THREADS - ep = readdir(dirp); - Py_END_ALLOW_THREADS - if (ep == NULL) { - if (errno == 0) { - break; - } else { - Py_BEGIN_ALLOW_THREADS - rewinddir(dirp); - closedir(dirp); - Py_END_ALLOW_THREADS - Py_DECREF(d); - return posix_error(); - } - } - if (ep->d_name[0] == '.' && - (NAMLEN(ep) == 1 || - (ep->d_name[1] == '.' && NAMLEN(ep) == 2))) - continue; - v = PyUnicode_DecodeFSDefaultAndSize(ep->d_name, NAMLEN(ep)); - if (v == NULL) { - Py_CLEAR(d); - break; - } - if (PyList_Append(d, v) != 0) { - Py_DECREF(v); - Py_CLEAR(d); - break; - } - Py_DECREF(v); - } - Py_BEGIN_ALLOW_THREADS - rewinddir(dirp); - closedir(dirp); - Py_END_ALLOW_THREADS - - return d; -} -#endif - #ifdef MS_WINDOWS /* A helper function for abspath on win32 */ static PyObject * @@ -3178,68 +3580,64 @@ #endif /* MS_WINDOWS */ PyDoc_STRVAR(posix_mkdir__doc__, -"mkdir(path [, mode=0777])\n\n\ +"mkdir(path [, mode=0777, *, dir_fd=AT_FDCWD])\n\n\ Create a directory."); static PyObject * -posix_mkdir(PyObject *self, PyObject *args) -{ - int res; - const char *path; +posix_mkdir(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; int mode = 0777; + int dir_fd = DEFAULT_DIR_FD; + static char *keywords[] = {"path", "mode", "dir_fd", NULL}; + PyObject *return_value = NULL; + int result; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|i$i:mkdir", keywords, + path_converter, &path, &mode, &dir_fd)) + return NULL; + +#ifndef HAVE_MKDIRAT + if (dir_fd_specified("mkdir", dir_fd)) + goto exit; +#endif #ifdef MS_WINDOWS - PyObject *po; - if (PyArg_ParseTuple(args, "U|i:mkdir", &po, &mode)) - { - wchar_t *wpath = PyUnicode_AsUnicode(po); - if (wpath == NULL) - return NULL; - - Py_BEGIN_ALLOW_THREADS - res = CreateDirectoryW(wpath, NULL); - Py_END_ALLOW_THREADS - if (!res) - return win32_error_object("mkdir", po); - Py_INCREF(Py_None); - return Py_None; - } - /* Drop the argument parsing error as narrow strings - are also valid. */ - PyErr_Clear(); - if (!PyArg_ParseTuple(args, "y|i:mkdir", &path, &mode)) - return NULL; - if (win32_warn_bytes_api()) - return NULL; + Py_END_ALLOW_THREADS + if (path.wide) + result = CreateDirectoryW(path.wide, NULL); + else + result = CreateDirectoryA(path.narrow, NULL); + Py_END_ALLOW_THREADS + + if (!result) { + return_value = win32_error_object("mkdir", path.object); + goto exit; + } +#else Py_BEGIN_ALLOW_THREADS - res = CreateDirectoryA(path, NULL); +#if HAVE_MKDIRAT + if (dir_fd != DEFAULT_DIR_FD) + result = mkdirat(dir_fd, path.narrow, mode); + else +#endif +#if ( defined(__WATCOMC__) || defined(PYCC_VACPP) ) && !defined(__QNX__) + result = mkdir(path.narrow); +#else + result = mkdir(path.narrow, mode); +#endif Py_END_ALLOW_THREADS - if (!res) { - win32_error("mkdir", path); - return NULL; - } + if (result < 0) { + return_value = path_error("mkdir", &path); + goto exit; + } +#endif + return_value = Py_None; Py_INCREF(Py_None); - return Py_None; -#else - PyObject *opath; - - if (!PyArg_ParseTuple(args, "O&|i:mkdir", - PyUnicode_FSConverter, &opath, &mode)) - return NULL; - path = PyBytes_AsString(opath); - Py_BEGIN_ALLOW_THREADS -#if ( defined(__WATCOMC__) || defined(PYCC_VACPP) ) && !defined(__QNX__) - res = mkdir(path); -#else - res = mkdir(path, mode); -#endif - Py_END_ALLOW_THREADS - if (res < 0) - return posix_error_with_allocated_filename(opath); - Py_DECREF(opath); - Py_INCREF(Py_None); - return Py_None; -#endif +exit: + path_cleanup(&path); + return return_value; } @@ -3328,85 +3726,101 @@ static PyObject * -internal_rename(PyObject *self, PyObject *args, int is_replace) -{ +internal_rename(PyObject *args, PyObject *kwargs, int is_replace) +{ + char *function_name = is_replace ? "replace" : "rename"; + path_t src; + path_t dst; + int src_dir_fd = DEFAULT_DIR_FD; + int dst_dir_fd = DEFAULT_DIR_FD; + PyObject *return_value = NULL; + char format[24]; + static char *keywords[] = {"src", "dst", "src_dir_fd", "dst_dir_fd", NULL}; + #ifdef MS_WINDOWS - PyObject *src, *dst; BOOL result; int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0; - if (PyArg_ParseTuple(args, - is_replace ? "UU:replace" : "UU:rename", - &src, &dst)) - { - wchar_t *wsrc, *wdst; - - wsrc = PyUnicode_AsUnicode(src); - if (wsrc == NULL) - return NULL; - wdst = PyUnicode_AsUnicode(dst); - if (wdst == NULL) - return NULL; - Py_BEGIN_ALLOW_THREADS - result = MoveFileExW(wsrc, wdst, flags); - Py_END_ALLOW_THREADS - if (!result) - return win32_error(is_replace ? "replace" : "rename", NULL); - Py_INCREF(Py_None); - return Py_None; - } - else { - PyErr_Clear(); - if (!PyArg_ParseTuple(args, - is_replace ? "O&O&:replace" : "O&O&:rename", - PyUnicode_FSConverter, &src, - PyUnicode_FSConverter, &dst)) - return NULL; - - if (win32_warn_bytes_api()) - goto error; - - Py_BEGIN_ALLOW_THREADS - result = MoveFileExA(PyBytes_AS_STRING(src), - PyBytes_AS_STRING(dst), flags); - Py_END_ALLOW_THREADS - - Py_XDECREF(src); - Py_XDECREF(dst); - - if (!result) - return win32_error(is_replace ? "replace" : "rename", NULL); - Py_INCREF(Py_None); - return Py_None; - -error: - Py_XDECREF(src); - Py_XDECREF(dst); - return NULL; - } #else - return posix_2str(args, - is_replace ? "O&O&:replace" : "O&O&:rename", rename); -#endif + int result; +#endif + + memset(&src, 0, sizeof(src)); + memset(&dst, 0, sizeof(dst)); + strcpy(format, "O&O&|$ii:"); + strcat(format, function_name); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, format, keywords, + path_converter, &src, + path_converter, &dst, + &src_dir_fd, &dst_dir_fd)) + return NULL; + +#ifndef HAVE_RENAMEAT + if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) { + argument_unavailable_error(function_name, "src_dir_fd and dst_dir_fd"); + goto exit; + } +#endif + + if (dst.wide && !src.wide) { + PyErr_Format(PyExc_ValueError, "%s: src and dst must be of the same type", function_name); + goto exit; + } + +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + if (src.wide) + result = MoveFileExW(src.wide, dst.wide, flags); + else + result = MoveFileExA(src.narrow, dst.narrow, flags); + Py_END_ALLOW_THREADS + + if (!result) { + return_value = win32_error_object(function_name, dst.object); + goto exit; + } + +#else + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_RENAMEAT + if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) + result = renameat(src_dir_fd, src.narrow, dst_dir_fd, dst.narrow); + else +#endif + result = rename(src.narrow, dst.narrow); + Py_END_ALLOW_THREADS + + if (result) { + return_value = path_error(function_name, &dst); + goto exit; + } +#endif + + Py_INCREF(Py_None); + return_value = Py_None; +exit: + path_cleanup(&src); + path_cleanup(&dst); + return return_value; } PyDoc_STRVAR(posix_rename__doc__, -"rename(old, new)\n\n\ +"rename(src, dst, *, src_dir_fd=AT_FDCWD, dst_dir_df=AT_FDCWD)\n\n\ Rename a file or directory."); static PyObject * -posix_rename(PyObject *self, PyObject *args) -{ - return internal_rename(self, args, 0); +posix_rename(PyObject *self, PyObject *args, PyObject *kwargs) +{ + return internal_rename(args, kwargs, 0); } PyDoc_STRVAR(posix_replace__doc__, -"replace(old, new)\n\n\ +"replace(src, dst, *, src_dir_fd=AT_FDCWD, dst_dir_df=AT_FDCWD)\n\n\ Rename a file or directory, overwriting the destination."); static PyObject * -posix_replace(PyObject *self, PyObject *args) -{ - return internal_rename(self, args, 1); +posix_replace(PyObject *self, PyObject *args, PyObject *kwargs) +{ + return internal_rename(args, kwargs, 1); } PyDoc_STRVAR(posix_rmdir__doc__, @@ -3424,21 +3838,6 @@ } -PyDoc_STRVAR(posix_stat__doc__, -"stat(path) -> stat result\n\n\ -Perform a stat system call on the given path."); - -static PyObject * -posix_stat(PyObject *self, PyObject *args) -{ -#ifdef MS_WINDOWS - return posix_do_stat(self, args, "O&:stat", STAT, "U:stat", win32_stat_w); -#else - return posix_do_stat(self, args, "O&:stat", STAT, NULL, NULL); -#endif -} - - #ifdef HAVE_SYSTEM PyDoc_STRVAR(posix_system__doc__, "system(command) -> exit_status\n\n\ @@ -3527,22 +3926,68 @@ #endif /* MS_WINDOWS */ PyDoc_STRVAR(posix_unlink__doc__, -"unlink(path)\n\n\ -Remove a file (same as remove(path))."); +"unlink(path=None, *, dir_fd=AT_FDCWD, remove_dir=False)\n\n\ +Remove a file (same as remove())."); PyDoc_STRVAR(posix_remove__doc__, -"remove(path)\n\n\ -Remove a file (same as unlink(path))."); - -static PyObject * -posix_unlink(PyObject *self, PyObject *args) -{ +"remove(path=None, *, dir_fd=AT_FDCWD, remove_dir=False)\n\n\ +Remove a file (same as unlink())."); + +static PyObject * +posix_unlink(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + int dir_fd = DEFAULT_DIR_FD; + int remove_dir = 0; + static char *keywords[] = {"path", "dir_fd", "remove_dir", NULL}; + int result; + PyObject *return_value = NULL; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$ip:unlink", keywords, + path_converter, &path, + &dir_fd, &remove_dir)) + return NULL; + +#ifndef HAVE_UNLINKAT + if (dir_fd_specified("unlink", dir_fd)) + goto exit; + if (remove_dir) { + argument_unavailable_error("unlink", "remove_dir"); + goto exit; + } +#endif + #ifdef MS_WINDOWS - return win32_1str(args, "remove", "y:remove", DeleteFileA, - "U:remove", Py_DeleteFileW); + Py_BEGIN_ALLOW_THREADS + if (path.wide) + result = Py_DeleteFileW(path.wide); + else + result = DeleteFileA(path.narrow); + result = !result; /* Windows, success=1, UNIX, success=0 */ + Py_END_ALLOW_THREADS #else - return posix_1str(args, "O&:remove", unlink); -#endif + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_UNLINKAT + if ((dir_fd != DEFAULT_DIR_FD) || remove_dir) + result = unlinkat(dir_fd, path.narrow, remove_dir ? AT_REMOVEDIR : 0); + else +#endif /* HAVE_UNLINKAT */ + result = unlink(path.narrow); + Py_END_ALLOW_THREADS +#endif + + if (result) { + return_value = path_error("unlink", &path); + goto exit; + } + + return_value = Py_None; + Py_INCREF(Py_None); + +exit: + path_cleanup(&path); + return return_value; } @@ -3572,6 +4017,153 @@ #endif /* HAVE_UNAME */ +PyDoc_STRVAR(posix_utime__doc__, +"utime(path[, times=(atime, mtime), *, ns=(atime_ns, mtime_ns),\n\ + dir_fd=AT_FDCWD, fd=-1, follow_symlinks=True])\n\ +Set the access and modified time of the file.\n\ +If the second argument ('times') is specified,\n\ + the values should be expressed as float seconds since the epoch.\n\ +If the keyword argument 'ns' is specified,\n\ + the values should be expressed as integer nanoseconds since the epoch.\n\ +If neither the second nor the 'ns' argument is specified,\n\ + utime uses the current time.\n\ +Specifying both 'times' and 'ns' is an error."); + +typedef struct { + int now; + time_t atime_s; + long atime_ns; + time_t mtime_s; + long mtime_ns; +} utime_t; + +/* + * these macros assume that "utime" is a pointer to a utime_t + * they also intentionally leak the declaration of a pointer named "time" + */ +#define UTIME_TO_TIMESPEC \ + struct timespec ts[2]; \ + struct timespec *time; \ + if (utime->now) \ + time = NULL; \ + else { \ + ts[0].tv_sec = utime->atime_s; \ + ts[0].tv_nsec = utime->atime_ns; \ + ts[1].tv_sec = utime->mtime_s; \ + ts[1].tv_nsec = utime->mtime_ns; \ + time = ts; \ + } \ + +#define UTIME_TO_TIMEVAL \ + struct timeval tv[2]; \ + struct timeval *time; \ + if (utime->now) \ + time = NULL; \ + else { \ + tv[0].tv_sec = utime->atime_s; \ + tv[0].tv_usec = utime->atime_ns / 1000; \ + tv[1].tv_sec = utime->mtime_s; \ + tv[1].tv_usec = utime->mtime_ns / 1000; \ + time = tv; \ + } \ + +#define UTIME_TO_UTIMBUF \ + struct utimbuf u[2]; \ + struct utimbuf *time; \ + if (utime->now) \ + time = NULL; \ + else { \ + u.actime = utime->atime_s; \ + u.modtime = utime->mtime_s; \ + time = u; \ + } + +#define UTIME_TO_TIME_T \ + time_t timet[2]; \ + struct timet time; \ + if (utime->now) \ + time = NULL; \ + else { \ + timet[0] = utime->atime_s; \ + timet[1] = utime->mtime_s; \ + time = &timet; \ + } \ + + +#define UTIME_HAVE_DIR_FD (defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMENSAT)) + +#if UTIME_HAVE_DIR_FD + +static int +utime_dir_fd(utime_t *utime, int dir_fd, char *path, int follow_symlinks) { +#ifdef HAVE_UTIMENSAT + int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW; + UTIME_TO_TIMESPEC; + return utimensat(dir_fd, path, time, flags); +#elif defined(HAVE_FUTIMESAT) + UTIME_TO_TIMEVAL; + return futimesat(dir_fd, path, time); +#endif +} + +#endif + +#define UTIME_HAVE_FD (defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS)) + +#if UTIME_HAVE_FD + +static int +utime_fd(utime_t *utime, int fd) { +#ifdef HAVE_FUTIMENS + UTIME_TO_TIMESPEC; + return futimens(fd, time); +#else + UTIME_TO_TIMEVAL; + return futimes(fd, time); +#endif +} + +#endif + + +#define UTIME_HAVE_NOFOLLOW_SYMLINKS (defined(HAVE_UTIMENSAT) || defined(HAVE_LUTIMES)) + +#if UTIME_HAVE_NOFOLLOW_SYMLINKS + +static int +utime_nofollow_symlinks(utime_t *utime, char *path) { +#ifdef HAVE_UTIMENSAT + UTIME_TO_TIMESPEC; + return utimensat(DEFAULT_DIR_FD, path, time, AT_SYMLINK_NOFOLLOW); +#else + UTIME_TO_TIMEVAL; + return lutimes(path, time); +#endif +} + +#endif + +#ifndef MS_WINDOWS + +static int +utime_default(utime_t *utime, char *path) { +#ifdef HAVE_UTIMENSAT + UTIME_TO_TIMESPEC; + return utimensat(DEFAULT_DIR_FD, path, time, 0); +#elif defined(HAVE_UTIMES) + UTIME_TO_TIMEVAL; + return utimes(path, time); +#elif defined(HAVE_UTIME_H) + UTIME_TO_UTIMBUF; + return utime(path, time); +#else + UTIME_TO_TIME_T; + return utime(path, time); +#endif +} + +#endif + static int split_py_long_to_s_and_ns(PyObject *py_long, time_t *s, long *ns) { @@ -3593,250 +4185,136 @@ return result; } - -typedef int (*parameter_converter_t)(PyObject *, void *); - -typedef struct { - /* input only */ - char path_format; - parameter_converter_t converter; - char *function_name; - char *first_argument_name; - PyObject *args; - PyObject *kwargs; - - /* input/output */ - PyObject **path; - - /* output only */ - int now; - time_t atime_s; - long atime_ns; - time_t mtime_s; - long mtime_ns; -} utime_arguments; - -#define DECLARE_UA(ua, fname) \ - utime_arguments ua; \ - memset(&ua, 0, sizeof(ua)); \ - ua.function_name = fname; \ - ua.args = args; \ - ua.kwargs = kwargs; \ - ua.first_argument_name = "path"; \ - -/* UA_TO_FILETIME doesn't declare atime and mtime for you */ -#define UA_TO_FILETIME(ua, atime, mtime) \ - time_t_to_FILE_TIME(ua.atime_s, ua.atime_ns, &atime); \ - time_t_to_FILE_TIME(ua.mtime_s, ua.mtime_ns, &mtime) - -/* the rest of these macros declare the output variable for you */ -#define UA_TO_TIMESPEC(ua, ts) \ - struct timespec ts[2]; \ - ts[0].tv_sec = ua.atime_s; \ - ts[0].tv_nsec = ua.atime_ns; \ - ts[1].tv_sec = ua.mtime_s; \ - ts[1].tv_nsec = ua.mtime_ns - -#define UA_TO_TIMEVAL(ua, tv) \ - struct timeval tv[2]; \ - tv[0].tv_sec = ua.atime_s; \ - tv[0].tv_usec = ua.atime_ns / 1000; \ - tv[1].tv_sec = ua.mtime_s; \ - tv[1].tv_usec = ua.mtime_ns / 1000 - -#define UA_TO_UTIMBUF(ua, u) \ - struct utimbuf u; \ - utimbuf.actime = ua.atime_s; \ - utimbuf.modtime = ua.mtime_s - -#define UA_TO_TIME_T(ua, timet) \ - time_t timet[2]; \ - timet[0] = ua.atime_s; \ - timet[1] = ua.mtime_s - - -/* - * utime_read_time_arguments() processes arguments for the utime - * family of functions. - */ - -typedef enum { - UTIME_SUCCESS = 0, - UTIME_PARSE_FAILURE = 1, - UTIME_TIMES_AND_NS_COLLISION = 2, - UTIME_TIMES_CONVERSION_FAILURE = 3, - UTIME_NS_CONVERSION_FAILURE = 4, -} utime_status; - -static utime_status -utime_read_time_arguments(utime_arguments *ua) -{ +static PyObject * +posix_utime(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; PyObject *times = NULL; PyObject *ns = NULL; - char format[24]; - char *kwlist[4]; - char **kw = kwlist; - utime_status return_value; - int parse_result; - - *kw++ = ua->first_argument_name; - *kw++ = "times"; - *kw++ = "ns"; - *kw = NULL; - - sprintf(format, "%c%s|O$O:%s", - ua->path_format, - ua->converter ? "&" : "", - ua->function_name); - - if (ua->converter) - parse_result = PyArg_ParseTupleAndKeywords(ua->args, ua->kwargs, - format, kwlist, ua->converter, ua->path, ×, &ns); - else - parse_result = PyArg_ParseTupleAndKeywords(ua->args, ua->kwargs, - format, kwlist, ua->path, ×, &ns); - - if (!parse_result) - return UTIME_PARSE_FAILURE; - - if (times && ns) { - PyErr_Format(PyExc_RuntimeError, - "%s: you may specify either 'times'" - " or 'ns' but not both", - ua->function_name); - return_value = UTIME_TIMES_AND_NS_COLLISION; - goto fail; + int dir_fd = DEFAULT_DIR_FD; + int fd = -1; + int follow_symlinks = 1; + char *keywords[] = {"path", "times", "ns", "dir_fd", "fd", "follow_symlinks", NULL}; + + utime_t utime; + +#ifdef MS_WINDOWS + HANDLE hFile; + FILETIME atime, mtime; +#else + int result; +#endif + + PyObject *return_value = NULL; + + memset(&path, 0, sizeof(path)); + path.nullable = 1; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "O&|O$Oiip:utime", keywords, + path_converter, &path, + ×, &ns, + &dir_fd, &fd, &follow_symlinks + )) + return NULL; + + if (times && (times != Py_None) && ns) { + PyErr_SetString(PyExc_ValueError, + "utime: you may specify either 'times'" + " or 'ns' but not both"); + goto exit; } if (times && (times != Py_None)) { if (!PyTuple_CheckExact(times) || (PyTuple_Size(times) != 2)) { - PyErr_Format(PyExc_TypeError, - "%s: 'times' must be either" - " a tuple of two ints or None", - ua->function_name); - return_value = UTIME_TIMES_CONVERSION_FAILURE; - goto fail; + PyErr_SetString(PyExc_TypeError, + "utime: 'times' must be either" + " a tuple of two ints or None"); + goto exit; } - ua->now = 0; + utime.now = 0; if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0), - &ua->atime_s, &ua->atime_ns) == -1 || + &utime.atime_s, &utime.atime_ns) == -1 || _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1), - &ua->mtime_s, &ua->mtime_ns) == -1) { - return_value = UTIME_TIMES_CONVERSION_FAILURE; - goto fail; + &utime.mtime_s, &utime.mtime_ns) == -1) { + goto exit; } - return UTIME_SUCCESS; - } - - if (ns) { + } + else if (ns) { if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) { - PyErr_Format(PyExc_TypeError, - "%s: 'ns' must be a tuple of two ints", - ua->function_name); - return_value = UTIME_NS_CONVERSION_FAILURE; - goto fail; + PyErr_SetString(PyExc_TypeError, + "utime: 'ns' must be a tuple of two ints"); + goto exit; } - ua->now = 0; + utime.now = 0; if (!split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 0), - &ua->atime_s, &ua->atime_ns) || + &utime.atime_s, &utime.atime_ns) || !split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 1), - &ua->mtime_s, &ua->mtime_ns)) { - return_value = UTIME_NS_CONVERSION_FAILURE; - goto fail; + &utime.mtime_s, &utime.mtime_ns)) { + goto exit; } - return UTIME_SUCCESS; - } - - /* either times=None, or neither times nor ns was specified. use "now". */ - ua->now = 1; - return UTIME_SUCCESS; - - fail: - if (ua->converter) - Py_DECREF(*ua->path); - return return_value; -} - - -PyDoc_STRVAR(posix_utime__doc__, -"utime(path[, times=(atime, mtime), *, ns=(atime_ns, mtime_ns)])\n\ -Set the access and modified time of the file.\n\ -If the second argument ('times') is specified,\n\ - the values should be expressed as float seconds since the epoch.\n\ -If the keyword argument 'ns' is specified,\n\ - the values should be expressed as integer nanoseconds since the epoch.\n\ -If neither the second nor the 'ns' argument is specified,\n\ - utime uses the current time.\n\ -Specifying both 'times' and 'ns' is an error."); - -static PyObject * -posix_utime(PyObject *self, PyObject *args, PyObject *kwargs) -{ + } + else { + /* either times=None, or neither times nor ns was specified. use "now". */ + utime.now = 1; + } + +#if !UTIME_HAVE_DIR_FD + if (dir_fd_specified("utime", dir_fd)) + goto exit; +#endif + +#if !UTIME_HAVE_FD + if (fd_specified("utime", fd)) + goto exit; +#endif + +#if !UTIME_HAVE_NOFOLLOW_SYMLINKS + if (follow_symlinks_specified("utime", follow_symlinks)) + goto exit; +#endif + + if (path_and_dir_fd_invalid("utime", &path, dir_fd) || + path_and_fd_invalid("utime", &path, fd) || + dir_fd_and_fd_invalid("utime", dir_fd, fd) || + fd_and_follow_symlinks_invalid("utime", fd, follow_symlinks)) + goto exit; + +#if !defined(HAVE_UTIMENSAT) + if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) { + PyErr_SetString(PyExc_RuntimeError, + "utime: cannot use dir_fd and follow_symlinks together on this platform"); + goto exit; + } +#endif + #ifdef MS_WINDOWS - PyObject *upath; - HANDLE hFile; - PyObject *result = NULL; - FILETIME atime, mtime; - - DECLARE_UA(ua, "utime"); - - ua.path_format = 'U'; - ua.path = &upath; - - switch (utime_read_time_arguments(&ua)) { - default: - return NULL; - case UTIME_SUCCESS: { - wchar_t *wpath = PyUnicode_AsUnicode(upath); - if (wpath == NULL) - return NULL; - Py_BEGIN_ALLOW_THREADS - hFile = CreateFileW(wpath, FILE_WRITE_ATTRIBUTES, 0, + Py_BEGIN_ALLOW_THREADS + if (path.wide) + hFile = CreateFileW(path.wide, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - Py_END_ALLOW_THREADS - if (hFile == INVALID_HANDLE_VALUE) - return win32_error_object("utime", upath); - break; - } - case UTIME_PARSE_FAILURE: { - const char *apath; - /* Drop the argument parsing error as narrow strings - are also valid. */ - PyErr_Clear(); - - ua.path_format = 'y'; - ua.path = (PyObject **)&apath; - if (utime_read_time_arguments(&ua) != UTIME_SUCCESS) - return NULL; - if (win32_warn_bytes_api()) - return NULL; - - Py_BEGIN_ALLOW_THREADS - hFile = CreateFileA(apath, FILE_WRITE_ATTRIBUTES, 0, + else + hFile = CreateFileA(path.narrow, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - Py_END_ALLOW_THREADS - if (hFile == INVALID_HANDLE_VALUE) { - win32_error("utime", apath); - return NULL; - } - break; - } - - } - - if (ua.now) { + Py_END_ALLOW_THREADS + if (hFile == INVALID_HANDLE_VALUE) { + win32_error_object("utime", path.object); + goto exit; + } + + if (now) { SYSTEMTIME now; GetSystemTime(&now); if (!SystemTimeToFileTime(&now, &mtime) || !SystemTimeToFileTime(&now, &atime)) { win32_error("utime", NULL); - goto done; + goto exit; } } else { - UA_TO_FILETIME(ua, atime, mtime); + time_t_to_FILE_TIME(atime_s, atime_ns, &atime); + time_t_to_FILE_TIME(mtime_s, mtime_ns, &mtime); } if (!SetFileTime(hFile, NULL, &atime, &mtime)) { /* Avoid putting the file name into the error here, @@ -3844,151 +4322,52 @@ something is wrong with the file, when it also could be the time stamp that gives a problem. */ win32_error("utime", NULL); - goto done; - } + goto exit; + } +#else /* MS_WINDOWS */ + Py_BEGIN_ALLOW_THREADS + +#if UTIME_HAVE_DIR_FD + if (dir_fd != DEFAULT_DIR_FD) + result = utime_dir_fd(&utime, dir_fd, path.narrow, follow_symlinks); + else +#endif + +#if UTIME_HAVE_FD + if (fd != -1) + result = utime_fd(&utime, fd); + else +#endif + +#if UTIME_HAVE_NOFOLLOW_SYMLINKS + if (!follow_symlinks) + result = utime_nofollow_symlinks(&utime, path.narrow); + else +#endif + + result = utime_default(&utime, path.narrow); + + Py_END_ALLOW_THREADS + + if (result < 0) { + /* see previous comment about not putting filename in error here */ + return_value = posix_error(); + goto exit; + } + +#endif /* MS_WINDOWS */ + Py_INCREF(Py_None); - result = Py_None; -done: - CloseHandle(hFile); - return result; -#else /* MS_WINDOWS */ - PyObject *opath; - char *path; - int res; - - DECLARE_UA(ua, "utime"); - - ua.path_format = 'O'; - ua.path = &opath; - ua.converter = PyUnicode_FSConverter; - - if (utime_read_time_arguments(&ua) != UTIME_SUCCESS) - return NULL; - path = PyBytes_AsString(opath); - if (ua.now) { - Py_BEGIN_ALLOW_THREADS - res = utime(path, NULL); - Py_END_ALLOW_THREADS - } - else { - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_UTIMENSAT - UA_TO_TIMESPEC(ua, buf); - res = utimensat(AT_FDCWD, path, buf, 0); -#elif defined(HAVE_UTIMES) - UA_TO_TIMEVAL(ua, buf); - res = utimes(path, buf); -#elif defined(HAVE_UTIME_H) - /* XXX should define struct utimbuf instead, above */ - UA_TO_UTIMBUF(ua, buf); - res = utime(path, &buf); -#else - UA_TO_TIME_T(ua, buf); - res = utime(path, buf); -#endif - Py_END_ALLOW_THREADS - } - - if (res < 0) { - return posix_error_with_allocated_filename(opath); - } - Py_DECREF(opath); - Py_RETURN_NONE; -#undef UTIME_EXTRACT -#endif /* MS_WINDOWS */ -} - -#ifdef HAVE_FUTIMES -PyDoc_STRVAR(posix_futimes__doc__, -"futimes(fd[, times=(atime, mtime), *, ns=(atime_ns, mtime_ns)])\n\ -Set the access and modified time of the file specified by the file\n\ -descriptor fd. See utime for the semantics of the times and ns parameters."); - -static PyObject * -posix_futimes(PyObject *self, PyObject *args, PyObject *kwargs) -{ - int res, fd; - - DECLARE_UA(ua, "futimes"); - - ua.path_format = 'i'; - ua.path = (PyObject **)&fd; - ua.first_argument_name = "fd"; - - if (utime_read_time_arguments(&ua) != UTIME_SUCCESS) - return NULL; - - if (ua.now) { - Py_BEGIN_ALLOW_THREADS - res = futimes(fd, NULL); - Py_END_ALLOW_THREADS - } - else { - Py_BEGIN_ALLOW_THREADS - { -#ifdef HAVE_FUTIMENS - UA_TO_TIMESPEC(ua, buf); - res = futimens(fd, buf); -#else - UA_TO_TIMEVAL(ua, buf); - res = futimes(fd, buf); -#endif - } - Py_END_ALLOW_THREADS - } - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif - -#ifdef HAVE_LUTIMES -PyDoc_STRVAR(posix_lutimes__doc__, -"lutimes(path[, times=(atime, mtime), *, ns=(atime_ns, mtime_ns)])\n\ -Like utime(), but if path is a symbolic link, it is not dereferenced."); - -static PyObject * -posix_lutimes(PyObject *self, PyObject *args, PyObject *kwargs) -{ - PyObject *opath; - const char *path; - int res; - - DECLARE_UA(ua, "lutimes"); - - ua.path_format = 'O'; - ua.path = &opath; - ua.converter = PyUnicode_FSConverter; - - if (utime_read_time_arguments(&ua) != UTIME_SUCCESS) - return NULL; - path = PyBytes_AsString(opath); - - if (ua.now) { - /* optional time values not given */ - Py_BEGIN_ALLOW_THREADS - res = lutimes(path, NULL); - Py_END_ALLOW_THREADS - } - else { - Py_BEGIN_ALLOW_THREADS - { -#ifdef HAVE_UTIMENSAT - UA_TO_TIMESPEC(ua, buf); - res = utimensat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); -#else - UA_TO_TIMEVAL(ua, buf); - res = lutimes(path, buf); -#endif - } - Py_END_ALLOW_THREADS - } - Py_DECREF(opath); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif + return_value = Py_None; + +exit: + path_cleanup(&path); +#ifdef MS_WINDOWS + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); +#endif + return return_value; +} /* Process operations */ @@ -4205,118 +4584,78 @@ env: dictionary of strings mapping to strings"); static PyObject * -posix_execve(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *path; +posix_execve(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; PyObject *argv, *env; - char **argvlist; + int fd = -1; + char **argvlist = NULL; char **envlist; Py_ssize_t argc, envc; + static char *keywords[] = {"path", "argv", "environment", "fd", NULL}; /* execve has three arguments: (path, argv, env), where argv is a list or tuple of strings and env is a dictionary like posix.environ. */ - if (!PyArg_ParseTuple(args, "O&OO:execve", - PyUnicode_FSConverter, - &opath, &argv, &env)) - return NULL; - path = PyBytes_AsString(opath); + memset(&path, 0, sizeof(path)); + path.nullable = 1; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&OO|$i:execve", keywords, + path_converter, &path, + &argv, &env, &fd)) + return NULL; + +#ifndef HAVE_FEXECVE + if (fd_specified("execve", fd)) + goto fail; +#endif + + if (path_and_fd_invalid("exeve", &path, fd)) + goto fail; + if (!PyList_Check(argv) && !PyTuple_Check(argv)) { PyErr_SetString(PyExc_TypeError, - "execve() arg 2 must be a tuple or list"); - goto fail_0; + "execve: argv must be a tuple or list"); + goto fail; } argc = PySequence_Size(argv); if (!PyMapping_Check(env)) { PyErr_SetString(PyExc_TypeError, - "execve() arg 3 must be a mapping object"); - goto fail_0; + "execve: environment must be a mapping object"); + goto fail; } argvlist = parse_arglist(argv, &argc); if (argvlist == NULL) { - goto fail_0; - } - - envlist = parse_envlist(env, &envc); - if (envlist == NULL) - goto fail_1; - - execve(path, argvlist, envlist); - - /* If we get here it's definitely an error */ - - (void) posix_error(); - - while (--envc >= 0) - PyMem_DEL(envlist[envc]); - PyMem_DEL(envlist); - fail_1: - free_string_array(argvlist, argc); - fail_0: - Py_DECREF(opath); - return NULL; -} -#endif /* HAVE_EXECV */ - -#ifdef HAVE_FEXECVE -PyDoc_STRVAR(posix_fexecve__doc__, -"fexecve(fd, args, env)\n\n\ -Execute the program specified by a file descriptor with arguments and\n\ -environment, replacing the current process.\n\ -\n\ - fd: file descriptor of executable\n\ - args: tuple or list of arguments\n\ - env: dictionary of strings mapping to strings"); - -static PyObject * -posix_fexecve(PyObject *self, PyObject *args) -{ - int fd; - PyObject *argv, *env; - char **argvlist; - char **envlist; - Py_ssize_t argc, envc; - - if (!PyArg_ParseTuple(args, "iOO:fexecve", - &fd, &argv, &env)) - return NULL; - if (!PyList_Check(argv) && !PyTuple_Check(argv)) { - PyErr_SetString(PyExc_TypeError, - "fexecve() arg 2 must be a tuple or list"); - return NULL; - } - argc = PySequence_Size(argv); - if (!PyMapping_Check(env)) { - PyErr_SetString(PyExc_TypeError, - "fexecve() arg 3 must be a mapping object"); - return NULL; - } - - argvlist = parse_arglist(argv, &argc); - if (argvlist == NULL) - return NULL; + goto fail; + } envlist = parse_envlist(env, &envc); if (envlist == NULL) goto fail; - fexecve(fd, argvlist, envlist); +#ifdef HAVE_FEXECVE + if (fd > -1) + fexecve(fd, argvlist, envlist); + else +#endif + execve(path.narrow, argvlist, envlist); /* If we get here it's definitely an error */ - (void) posix_error(); + path_error("execve", &path); while (--envc >= 0) PyMem_DEL(envlist[envc]); PyMem_DEL(envlist); fail: - free_string_array(argvlist, argc); + if (argvlist) + free_string_array(argvlist, argc); + path_cleanup(&path); return NULL; } -#endif /* HAVE_FEXECVE */ +#endif /* HAVE_EXECV */ + #ifdef HAVE_SPAWNV PyDoc_STRVAR(posix_spawnv__doc__, @@ -6429,90 +6768,182 @@ #endif -PyDoc_STRVAR(posix_lstat__doc__, -"lstat(path) -> stat result\n\n\ -Like stat(path), but do not follow symbolic links."); - -static PyObject * -posix_lstat(PyObject *self, PyObject *args) -{ -#ifdef HAVE_LSTAT - return posix_do_stat(self, args, "O&:lstat", lstat, NULL, NULL); -#else /* !HAVE_LSTAT */ -#ifdef MS_WINDOWS - return posix_do_stat(self, args, "O&:lstat", win32_lstat, "U:lstat", - win32_lstat_w); -#else - return posix_do_stat(self, args, "O&:lstat", STAT, NULL, NULL); -#endif -#endif /* !HAVE_LSTAT */ -} - - #ifdef HAVE_READLINK PyDoc_STRVAR(posix_readlink__doc__, -"readlink(path) -> path\n\n\ +"readlink(path, *, dir_fd=AT_FDCWD) -> path\n\n\ Return a string representing the path to which the symbolic link points."); static PyObject * -posix_readlink(PyObject *self, PyObject *args) -{ - PyObject* v; - char buf[MAXPATHLEN]; - PyObject *opath; - char *path; - int n; - int arg_is_unicode = 0; - - if (!PyArg_ParseTuple(args, "O&:readlink", - PyUnicode_FSConverter, &opath)) - return NULL; - path = PyBytes_AsString(opath); - v = PySequence_GetItem(args, 0); - if (v == NULL) { - Py_DECREF(opath); - return NULL; - } - - if (PyUnicode_Check(v)) { - arg_is_unicode = 1; - } - Py_DECREF(v); +posix_readlink(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + int dir_fd = DEFAULT_DIR_FD; + char buffer[MAXPATHLEN]; + ssize_t length; + PyObject *return_value = NULL; + static char *keywords[] = {"path", "dir_fd", NULL}; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$i:readlink", keywords, + path_converter, &path, &dir_fd)) + return NULL; + +#ifndef HAVE_READLINKAT + if (dir_fd_specified("readlink", dir_fd)) + goto exit; +#endif Py_BEGIN_ALLOW_THREADS - n = readlink(path, buf, (int) sizeof buf); +#ifdef HAVE_READLINKAT + if (dir_fd != DEFAULT_DIR_FD) + length = readlinkat(dir_fd, path.narrow, buffer, sizeof(buffer)); + else +#endif + length = readlink(path.narrow, buffer, sizeof(buffer)); Py_END_ALLOW_THREADS - if (n < 0) - return posix_error_with_allocated_filename(opath); - - Py_DECREF(opath); - if (arg_is_unicode) - return PyUnicode_DecodeFSDefaultAndSize(buf, n); + + if (length < 0) { + return_value = path_error("readlink", &path); + goto exit; + } + + if (PyUnicode_Check(path.object)) + return_value = PyUnicode_DecodeFSDefaultAndSize(buffer, length); else - return PyBytes_FromStringAndSize(buf, n); -} + return_value = PyBytes_FromStringAndSize(buffer, length); +exit: + path_cleanup(&path); + return return_value; +} + + #endif /* HAVE_READLINK */ -#if defined(HAVE_SYMLINK) && !defined(MS_WINDOWS) +#ifdef HAVE_SYMLINK PyDoc_STRVAR(posix_symlink__doc__, -"symlink(src, dst)\n\n\ -Create a symbolic link pointing to src named dst."); - -static PyObject * -posix_symlink(PyObject *self, PyObject *args) -{ - return posix_2str(args, "O&O&:symlink", symlink); -} +"symlink(src, dst, target_is_directory=False, *, dir_fd=AT_FDCWD)\n\n\ +Create a symbolic link pointing to src named dst.\n\n\ +target_is_directory is required on Windows\n\ +if the target is to be interpreted as a directory.\n\ +(On Windows, symlink requires Windows 6.0 or greater, and raises a\n\ +NotImplementedError otherwise.)"); + +#if defined(MS_WINDOWS) + +/* Grab CreateSymbolicLinkW dynamically from kernel32 */ +static DWORD (CALLBACK *Py_CreateSymbolicLinkW)(LPWSTR, LPWSTR, DWORD) = NULL; +static DWORD (CALLBACK *Py_CreateSymbolicLinkA)(LPSTR, LPSTR, DWORD) = NULL; +static int +check_CreateSymbolicLink() +{ + HINSTANCE hKernel32; + /* only recheck */ + if (Py_CreateSymbolicLinkW && Py_CreateSymbolicLinkA) + return 1; + hKernel32 = GetModuleHandleW(L"KERNEL32"); + *(FARPROC*)&Py_CreateSymbolicLinkW = GetProcAddress(hKernel32, + "CreateSymbolicLinkW"); + *(FARPROC*)&Py_CreateSymbolicLinkA = GetProcAddress(hKernel32, + "CreateSymbolicLinkA"); + return (Py_CreateSymbolicLinkW && Py_CreateSymbolicLinkA); +} + +#endif + +static PyObject * +posix_symlink(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t src; + path_t dst; + int dir_fd = DEFAULT_DIR_FD; + int target_is_directory = 0; + static char *keywords[] = {"src", "dst", "target_is_directory", "dir_fd", NULL}; + PyObject *return_value; +#ifdef MS_WINDOWS + DWORD result; +#else + int result; +#endif + + memset(&src, 0, sizeof(src)); + src.argument_name = "src"; + memset(&dst, 0, sizeof(dst)); + dst.argument_name = "dst"; + +#ifdef MS_WINDOWS + if (!check_CreateSymbolicLink()) + return PyErr_SetString(PyExc_NotImplementedError, + "CreateSymbolicLink functions not found"); + if (!win32_can_symlink) + return PyErr_SetString(PyExc_OSError, "symbolic link privilege not held"); +#endif + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&|i$i:symlink", keywords, + path_converter, &src, + path_converter, &dst, + &target_is_directory, &dir_fd)) + return NULL; + +#ifndef HAVE_SYMLINKAT + if (dir_fd_specified("symlink", dir_fd)) + goto exit; +#endif + + if (dst.wide && !src.wide) { + PyErr_SetString(PyExc_ValueError, "symlink: src and dst must be of the same type"); + return_value = NULL; + goto exit; + } + +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + if (dst.wide) + result = Py_CreateSymbolicLinkW(dst.wide, src.wide, target_is_directory); + else + result = Py_CreateSymbolicLinkA(dst.narrow, src.narrow, target_is_directory); + Py_END_ALLOW_THREADS + + if (!result) { + return_value = win32_error_object("symlink", src.object); + goto exit; + } + +#else + + Py_BEGIN_ALLOW_THREADS +#if HAVE_SYMLINKAT + if (dir_fd != DEFAULT_DIR_FD) + result = symlinkat(src.narrow, dir_fd, dst.narrow); + else +#endif + result = symlink(src.narrow, dst.narrow); + Py_END_ALLOW_THREADS + + if (result) { + return_value = path_error("symlink", &dst); + goto exit; + } +#endif + + return_value = Py_None; + Py_INCREF(Py_None); + goto exit; /* silence "unused label" warning */ +exit: + path_cleanup(&src); + path_cleanup(&dst); + return return_value; +} + #endif /* HAVE_SYMLINK */ + #if !defined(HAVE_READLINK) && defined(MS_WINDOWS) PyDoc_STRVAR(win_readlink__doc__, "readlink(path) -> path\n\n\ Return a string representing the path to which the symbolic link points."); -/* Windows readlink implementation */ static PyObject * win_readlink(PyObject *self, PyObject *args) { @@ -6581,90 +7012,6 @@ #endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */ -#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS) - -/* Grab CreateSymbolicLinkW dynamically from kernel32 */ -static int has_CreateSymbolicLinkW = 0; -static DWORD (CALLBACK *Py_CreateSymbolicLinkW)(LPWSTR, LPWSTR, DWORD); -static int -check_CreateSymbolicLinkW() -{ - HINSTANCE hKernel32; - /* only recheck */ - if (has_CreateSymbolicLinkW) - return has_CreateSymbolicLinkW; - hKernel32 = GetModuleHandleW(L"KERNEL32"); - *(FARPROC*)&Py_CreateSymbolicLinkW = GetProcAddress(hKernel32, - "CreateSymbolicLinkW"); - if (Py_CreateSymbolicLinkW) - has_CreateSymbolicLinkW = 1; - return has_CreateSymbolicLinkW; -} - -PyDoc_STRVAR(win_symlink__doc__, -"symlink(src, dst, target_is_directory=False)\n\n\ -Create a symbolic link pointing to src named dst.\n\ -target_is_directory is required if the target is to be interpreted as\n\ -a directory.\n\ -This function requires Windows 6.0 or greater, and raises a\n\ -NotImplementedError otherwise."); - -static PyObject * -win_symlink(PyObject *self, PyObject *args, PyObject *kwargs) -{ - static char *kwlist[] = {"src", "dest", "target_is_directory", NULL}; - PyObject *osrc, *odest; - PyObject *usrc = NULL, *udest = NULL; - wchar_t *wsrc, *wdest; - int target_is_directory = 0; - DWORD res; - - if (!check_CreateSymbolicLinkW()) - { - /* raise NotImplementedError */ - return PyErr_Format(PyExc_NotImplementedError, - "CreateSymbolicLinkW not found"); - } - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "OO|i:symlink", kwlist, - &osrc, &odest, &target_is_directory)) - return NULL; - - usrc = win32_decode_filename(osrc); - if (!usrc) - return NULL; - udest = win32_decode_filename(odest); - if (!udest) - goto error; - - if (win32_can_symlink == 0) - return PyErr_Format(PyExc_OSError, "symbolic link privilege not held"); - - wsrc = PyUnicode_AsUnicode(usrc); - if (wsrc == NULL) - goto error; - wdest = PyUnicode_AsUnicode(udest); - if (wsrc == NULL) - goto error; - - Py_BEGIN_ALLOW_THREADS - res = Py_CreateSymbolicLinkW(wdest, wsrc, target_is_directory); - Py_END_ALLOW_THREADS - - Py_DECREF(usrc); - Py_DECREF(udest); - if (!res) - return win32_error_object("symlink", osrc); - - Py_INCREF(Py_None); - return Py_None; - -error: - Py_XDECREF(usrc); - Py_XDECREF(udest); - return NULL; -} -#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */ #ifdef HAVE_TIMES #if defined(PYCC_VACPP) && defined(PYOS_OS2) @@ -6844,57 +7191,56 @@ /* Functions acting on file descriptors */ PyDoc_STRVAR(posix_open__doc__, -"open(filename, flag [, mode=0777]) -> fd\n\n\ -Open a file (for low level IO)."); - -static PyObject * -posix_open(PyObject *self, PyObject *args) -{ - PyObject *ofile; - char *file; - int flag; +"open(path, flags [, mode=0777, *, dir_fd=AT_FDCWD])\n\n\ +Open a file for low level IO. Returns a file handle (integer)."); + +static PyObject * +posix_open(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + int flags; int mode = 0777; + int dir_fd = DEFAULT_DIR_FD; int fd; - + PyObject *return_value = NULL; + static char *keywords[] = {"path", "flags", "mode", "dir_fd", NULL}; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&i|i$i:open", keywords, + path_converter, &path, + &flags, &mode, &dir_fd)) + return NULL; + +#ifndef HAVE_OPENAT + if (dir_fd_specified("open", dir_fd)) + goto exit; +#endif + + Py_BEGIN_ALLOW_THREADS #ifdef MS_WINDOWS - PyObject *po; - if (PyArg_ParseTuple(args, "Ui|i:open", &po, &flag, &mode)) { - wchar_t *wpath = PyUnicode_AsUnicode(po); - if (wpath == NULL) - return NULL; - - Py_BEGIN_ALLOW_THREADS - fd = _wopen(wpath, flag, mode); - Py_END_ALLOW_THREADS - if (fd < 0) - return posix_error(); - return PyLong_FromLong((long)fd); - } - /* Drop the argument parsing error as narrow strings - are also valid. */ - PyErr_Clear(); -#endif - - if (!PyArg_ParseTuple(args, "O&i|i:open", - PyUnicode_FSConverter, &ofile, - &flag, &mode)) - return NULL; -#ifdef MS_WINDOWS - if (win32_warn_bytes_api()) { - Py_DECREF(ofile); - return NULL; - } -#endif - file = PyBytes_AsString(ofile); - Py_BEGIN_ALLOW_THREADS - fd = open(file, flag, mode); + if (path.wide) + fd = _wopen(path.wide, flags, mode); + else +#endif +#ifdef HAVE_OPENAT + if (dir_fd != DEFAULT_DIR_FD) + fd = openat(dir_fd, path.narrow, flags, mode); + else +#endif + fd = open(path.narrow, flags, mode); Py_END_ALLOW_THREADS - if (fd < 0) - return posix_error_with_allocated_filename(ofile); - Py_DECREF(ofile); - return PyLong_FromLong((long)fd); -} - + + if (fd < 0) { + return_value = path_error("open", &path); + goto exit; + } + + return_value = PyLong_FromLong((long)fd); + +exit: + path_cleanup(&path); + return return_value; +} PyDoc_STRVAR(posix_close__doc__, "close(fd)\n\n\ @@ -7585,35 +7931,56 @@ #ifdef HAVE_MKFIFO PyDoc_STRVAR(posix_mkfifo__doc__, -"mkfifo(filename [, mode=0666])\n\n\ +"mkfifo(filename [, mode=0666, *, dir_fd=AT_FDCWD])\n\n\ Create a FIFO (a POSIX named pipe)."); static PyObject * -posix_mkfifo(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *filename; +posix_mkfifo(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; int mode = 0666; - int res; - if (!PyArg_ParseTuple(args, "O&|i:mkfifo", PyUnicode_FSConverter, &opath, - &mode)) - return NULL; - filename = PyBytes_AS_STRING(opath); + int dir_fd = DEFAULT_DIR_FD; + int result; + PyObject *return_value = NULL; + static char *keywords[] = {"path", "mode", "dir_fd", NULL}; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|i$i:mkfifo", keywords, + path_converter, &path, + &mode, &dir_fd)) + return NULL; + +#ifndef HAVE_MKFIFOAT + if (dir_fd_specified("mkfifo", dir_fd)) + goto exit; +#endif + Py_BEGIN_ALLOW_THREADS - res = mkfifo(filename, mode); +#ifdef HAVE_MKFIFOAT + if (dir_fd != DEFAULT_DIR_FD) + result = mkfifoat(dir_fd, path.narrow, mode); + else +#endif + result = mkfifo(path.narrow, mode); Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (res < 0) - return posix_error(); + + if (result < 0) { + return_value = path_error("mkfifo", &path); + goto exit; + } + + return_value = Py_None; Py_INCREF(Py_None); - return Py_None; -} -#endif - + +exit: + path_cleanup(&path); + return return_value; +} +#endif #if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) PyDoc_STRVAR(posix_mknod__doc__, -"mknod(filename [, mode=0600, device])\n\n\ +"mknod(filename [, mode=0600, device, *, dir_fd=AT_FDCWD])\n\n\ Create a filesystem node (file, device special file or named pipe)\n\ named filename. mode specifies both the permissions to use and the\n\ type of node to be created, being combined (bitwise OR) with one of\n\ @@ -7623,25 +7990,47 @@ static PyObject * -posix_mknod(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *filename; - int mode = 0600; +posix_mknod(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + int mode = 0666; int device = 0; - int res; - if (!PyArg_ParseTuple(args, "O&|ii:mknod", PyUnicode_FSConverter, &opath, - &mode, &device)) - return NULL; - filename = PyBytes_AS_STRING(opath); + int dir_fd = DEFAULT_DIR_FD; + int result; + PyObject *return_value = NULL; + static char *keywords[] = {"path", "mode", "device", "dir_fd", NULL}; + + memset(&path, 0, sizeof(path)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|ii$i:mknod", keywords, + path_converter, &path, + &mode, &device, &dir_fd)) + return NULL; + +#ifndef HAVE_MKNODAT + if (dir_fd_specified("mknod", dir_fd)) + goto exit; +#endif + Py_BEGIN_ALLOW_THREADS - res = mknod(filename, mode, device); +#ifdef HAVE_MKNODAT + if (dir_fd != DEFAULT_DIR_FD) + result = mknodat(dir_fd, path.narrow, mode, device); + else +#endif + result = mknod(path.narrow, mode, device); Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (res < 0) - return posix_error(); + + if (result < 0) { + return_value = path_error("mknod", &path); + goto exit; + } + + return_value = Py_None; Py_INCREF(Py_None); - return Py_None; + +exit: + path_cleanup(&path); + return return_value; } #endif @@ -8188,24 +8577,48 @@ Perform a statvfs system call on the given path."); static PyObject * -posix_statvfs(PyObject *self, PyObject *args) -{ - PyObject *path; - int res; +posix_statvfs(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *keywords[] = {"path", "fd", NULL}; + path_t path; + int fd = -1; + int result; + PyObject *return_value = NULL; struct statvfs st; - if (!PyArg_ParseTuple(args, "O&:statvfs", PyUnicode_FSConverter, &path)) - return NULL; + + memset(&path, 0, sizeof(path)); + path.nullable = 1; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&$i:statvfs", keywords, + path_converter, &path, &fd)) + return NULL; + +#ifndef HAVE_FSTATVFS + if (fd_specified("statvfs", fd)) + goto exit; +#endif + + if (path_and_fd_invalid("statvfs", &path, fd)) + goto exit; + Py_BEGIN_ALLOW_THREADS - res = statvfs(PyBytes_AS_STRING(path), &st); +#ifdef HAVE_FSTATVFS + if (fd != -1) + result = fstatvfs(fd, &st); + else +#endif + result = statvfs(path.narrow, &st); Py_END_ALLOW_THREADS - if (res != 0) { - posix_error_with_filename(PyBytes_AS_STRING(path)); - Py_DECREF(path); - return NULL; - } - Py_DECREF(path); - - return _pystatvfs_fromstructstatvfs(st); + + if (result) { + return_value = path_error("statvfs", &path); + goto exit; + } + + return_value = _pystatvfs_fromstructstatvfs(st); + +exit: + path_cleanup(&path); + return return_value; } #endif /* HAVE_STATVFS */ @@ -9513,946 +9926,285 @@ } #endif -/* Posix *at family of functions: - faccessat, fchmodat, fchownat, fstatat, futimesat, - linkat, mkdirat, mknodat, openat, readlinkat, renameat, symlinkat, - unlinkat, utimensat, mkfifoat */ - -#ifdef HAVE_FACCESSAT -PyDoc_STRVAR(posix_faccessat__doc__, -"faccessat(dirfd, path, mode, flags=0) -> True if granted, False otherwise\n\n\ -Like access() but if path is relative, it is taken as relative to dirfd.\n\ -flags is optional and can be constructed by ORing together zero or more\n\ -of these values: AT_SYMLINK_NOFOLLOW, AT_EACCESS.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_faccessat(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *path; - int mode; - int res; - int dirfd, flags = 0; - if (!PyArg_ParseTuple(args, "iO&i|i:faccessat", - &dirfd, PyUnicode_FSConverter, &opath, &mode, &flags)) - return NULL; - path = PyBytes_AsString(opath); - Py_BEGIN_ALLOW_THREADS - res = faccessat(dirfd, path, mode, flags); - Py_END_ALLOW_THREADS - Py_DECREF(opath); - return PyBool_FromLong(res == 0); -} -#endif - -#ifdef HAVE_FCHMODAT -PyDoc_STRVAR(posix_fchmodat__doc__, -"fchmodat(dirfd, path, mode, flags=0)\n\n\ -Like chmod() but if path is relative, it is taken as relative to dirfd.\n\ -flags is optional and may be 0 or AT_SYMLINK_NOFOLLOW.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_fchmodat(PyObject *self, PyObject *args) -{ - int dirfd, mode, res; +#ifdef USE_XATTRS + +PyDoc_STRVAR(posix_getxattr__doc__, +"getxattr(path, attribute, *, fd=-1, follow_symlinks=True) -> value\n\n\ +Return the value of extended attribute *attribute* on *path*.\n\ +If fd is specified, use fd instead of path.\n\ +If follow_symlinks is false, do not follow symlinks for path"); + +static PyObject * +posix_getxattr(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + path_t attribute; + int fd = -1; + int follow_symlinks = 1; + PyObject *buffer = NULL; + int i; + static char *keywords[] = {"path", "attribute", "fd", "follow_symlinks", NULL}; + + memset(&path, 0, sizeof(path)); + memset(&attribute, 0, sizeof(attribute)); + path.nullable = 1; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&|$ip:getxattr", keywords, + path_converter, &path, + path_converter, &attribute, + &fd, &follow_symlinks)) + return NULL; + + if (path_and_fd_invalid("getxattr", &path, fd) || + fd_and_follow_symlinks_invalid("getxattr", fd, follow_symlinks)) + goto exit; + + for (i = 0; ; i++) { + void *ptr; + ssize_t result; + static Py_ssize_t buffer_sizes[] = {128, XATTR_SIZE_MAX, 0}; + Py_ssize_t buffer_size = buffer_sizes[i]; + if (!buffer_size) { + path_error("getxattr", &path); + goto exit; + } + buffer = PyBytes_FromStringAndSize(NULL, buffer_size); + if (!buffer) + goto exit; + ptr = PyBytes_AS_STRING(buffer); + + Py_BEGIN_ALLOW_THREADS; + if (fd >= 0) + result = fgetxattr(fd, attribute.narrow, ptr, buffer_size); + else if (follow_symlinks) + result = getxattr(path.narrow, attribute.narrow, ptr, buffer_size); + else + result = lgetxattr(path.narrow, attribute.narrow, ptr, buffer_size); + Py_END_ALLOW_THREADS; + + if (result < 0) { + Py_DECREF(buffer); + buffer = NULL; + if (errno == ERANGE) + continue; + path_error("getxattr", &path); + goto exit; + } + + if (result != buffer_size) { + /* Can only shrink. */ + _PyBytes_Resize(&buffer, result); + } + break; + } + +exit: + path_cleanup(&path); + path_cleanup(&attribute); + return buffer; +} + +PyDoc_STRVAR(posix_setxattr__doc__, +"setxattr(path, attribute, value, flags=0, *, fd=-1, follow_symlinks=True)\n\n\ +Set extended attribute *attr* on *path* to *value*."); + +static PyObject * +posix_setxattr(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + path_t attribute; + Py_buffer value; int flags = 0; - PyObject *opath; - char *path; - - if (!PyArg_ParseTuple(args, "iO&i|i:fchmodat", - &dirfd, PyUnicode_FSConverter, &opath, &mode, &flags)) - return NULL; - - path = PyBytes_AsString(opath); - - Py_BEGIN_ALLOW_THREADS - res = fchmodat(dirfd, path, mode, flags); - Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_FCHMODAT */ - -#ifdef HAVE_FCHOWNAT -PyDoc_STRVAR(posix_fchownat__doc__, -"fchownat(dirfd, path, uid, gid, flags=0)\n\n\ -Like chown() but if path is relative, it is taken as relative to dirfd.\n\ -flags is optional and may be 0 or AT_SYMLINK_NOFOLLOW.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_fchownat(PyObject *self, PyObject *args) -{ - PyObject *opath; - int dirfd, res; - long uid, gid; - int flags = 0; - char *path; - - if (!PyArg_ParseTuple(args, "iO&ll|i:fchownat", - &dirfd, PyUnicode_FSConverter, &opath, &uid, &gid, &flags)) - return NULL; - - path = PyBytes_AsString(opath); - - Py_BEGIN_ALLOW_THREADS - res = fchownat(dirfd, path, (uid_t) uid, (gid_t) gid, flags); - Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_FCHOWNAT */ - -#ifdef HAVE_FSTATAT -PyDoc_STRVAR(posix_fstatat__doc__, -"fstatat(dirfd, path, flags=0) -> stat result\n\n\ -Like stat() but if path is relative, it is taken as relative to dirfd.\n\ -flags is optional and may be 0 or AT_SYMLINK_NOFOLLOW.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_fstatat(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *path; - STRUCT_STAT st; - int dirfd, res, flags = 0; - - if (!PyArg_ParseTuple(args, "iO&|i:fstatat", - &dirfd, PyUnicode_FSConverter, &opath, &flags)) - return NULL; - path = PyBytes_AsString(opath); - - Py_BEGIN_ALLOW_THREADS - res = fstatat(dirfd, path, &st, flags); - Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (res != 0) - return posix_error(); - - return _pystat_fromstructstat(&st); -} -#endif - -#ifdef HAVE_FUTIMESAT -PyDoc_STRVAR(posix_futimesat__doc__, -"futimesat(dirfd, path[, (atime, mtime)])\n\ -Like utime() but if path is relative, it is taken as relative to dirfd.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_futimesat(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *path; - int res, dirfd; - PyObject* arg = Py_None; - time_t atime, mtime; - long ansec, mnsec; - - if (!PyArg_ParseTuple(args, "iO&|O:futimesat", - &dirfd, PyUnicode_FSConverter, &opath, &arg)) - return NULL; - path = PyBytes_AsString(opath); - if (arg == Py_None) { - /* optional time values not given */ - Py_BEGIN_ALLOW_THREADS - res = futimesat(dirfd, path, NULL); - Py_END_ALLOW_THREADS - } - else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { - PyErr_SetString(PyExc_TypeError, - "futimesat() arg 3 must be a tuple (atime, mtime)"); - Py_DECREF(opath); - return NULL; - } - else { - if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 0), - &atime, &ansec) == -1) { - Py_DECREF(opath); - return NULL; + int fd = -1; + int follow_symlinks = 1; + int result; + PyObject *return_value = NULL; + static char *keywords[] = {"path", "attribute", "value", "flags", "fd", "follow_symlinks", NULL}; + + memset(&path, 0, sizeof(path)); + path.nullable = 1; + memset(&attribute, 0, sizeof(attribute)); + memset(&value, 0, sizeof(value)); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&y*|i$ip:setxattr", keywords, + path_converter, &path, + path_converter, &attribute, + &value, &flags, + &fd, &follow_symlinks)) + return NULL; + + if (path_and_fd_invalid("setxattr", &path, fd) || + fd_and_follow_symlinks_invalid("setxattr", fd, follow_symlinks)) + goto exit; + + Py_BEGIN_ALLOW_THREADS; + if (fd > -1) + result = fsetxattr(fd, attribute.narrow, + value.buf, value.len, flags); + else if (follow_symlinks) + result = setxattr(path.narrow, attribute.narrow, + value.buf, value.len, flags); + else + result = lsetxattr(path.narrow, attribute.narrow, + value.buf, value.len, flags); + Py_END_ALLOW_THREADS; + + if (result) { + return_value = path_error("setxattr", &path); + goto exit; + } + + return_value = Py_None; + Py_INCREF(return_value); + +exit: + path_cleanup(&path); + path_cleanup(&attribute); + PyBuffer_Release(&value); + + return return_value; +} + +PyDoc_STRVAR(posix_removexattr__doc__, +"removexattr(path, attr, *, fd=-1, follow_symlinks=True)\n\n\ +Remove extended attribute *attr* on *path*."); + +static PyObject * +posix_removexattr(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + path_t attribute; + int fd = -1; + int follow_symlinks = 1; + int result; + PyObject *return_value = NULL; + static char *keywords[] = {"path", "attribute", "fd", "follow_symlinks", NULL}; + + memset(&path, 0, sizeof(path)); + memset(&attribute, 0, sizeof(attribute)); + path.nullable = 1; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&|$ip:removexattr", keywords, + path_converter, &path, + path_converter, &attribute, + &fd, &follow_symlinks)) + return NULL; + + if (path_and_fd_invalid("removexattr", &path, fd) || + fd_and_follow_symlinks_invalid("removexattr", fd, follow_symlinks)) + goto exit; + + Py_BEGIN_ALLOW_THREADS; + if (fd > -1) + result = fremovexattr(fd, attribute.narrow); + else if (follow_symlinks) + result = removexattr(path.narrow, attribute.narrow); + else + result = lremovexattr(path.narrow, attribute.narrow); + Py_END_ALLOW_THREADS; + + if (result) { + return_value = path_error("removexattr", &path); + goto exit; + } + + return_value = Py_None; + Py_INCREF(return_value); + +exit: + path_cleanup(&path); + path_cleanup(&attribute); + + return return_value; +} + +PyDoc_STRVAR(posix_listxattr__doc__, +"listxattr(path=None, *, fd=-1, follow_symlinks=True)\n\n\ +Return a list of extended attributes on *path*."); + +static PyObject * +posix_listxattr(PyObject *self, PyObject *args, PyObject *kwargs) +{ + path_t path; + int fd = -1; + int follow_symlinks = 1; + Py_ssize_t i; + PyObject *result = NULL; + char *buffer = NULL; + static char *keywords[] = {"path", "fd", "follow_symlinks", NULL}; + + memset(&path, 0, sizeof(path)); + path.nullable = 1; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&$ip:listxattr", keywords, + path_converter, &path, + &fd, &follow_symlinks)) + return NULL; + + if (path_and_fd_invalid("listxattr", &path, fd) || + fd_and_follow_symlinks_invalid("listxattr", fd, follow_symlinks)) + goto exit; + + for (i = 0; ; i++) { + char *start, *trace, *end; + ssize_t length; + static Py_ssize_t buffer_sizes[] = { 256, XATTR_LIST_MAX, 0 }; + Py_ssize_t buffer_size = buffer_sizes[i]; + if (!buffer_size) { + // ERANGE + path_error("listxattr", &path); + break; } - if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 1), - &mtime, &mnsec) == -1) { - Py_DECREF(opath); - return NULL; + buffer = PyMem_MALLOC(buffer_size); + if (!buffer) { + PyErr_NoMemory(); + break; } - Py_BEGIN_ALLOW_THREADS - { -#ifdef HAVE_UTIMENSAT - struct timespec buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_nsec = ansec; - buf[1].tv_sec = mtime; - buf[1].tv_nsec = mnsec; - res = utimensat(dirfd, path, buf, 0); -#else - struct timeval buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_usec = ansec / 1000; - buf[1].tv_sec = mtime; - buf[1].tv_usec = mnsec / 1000; - res = futimesat(dirfd, path, buf); -#endif + Py_BEGIN_ALLOW_THREADS; + if (fd > -1) + length = flistxattr(fd, buffer, buffer_size); + else if (follow_symlinks) + length = listxattr(path.narrow, buffer, buffer_size); + else + length = llistxattr(path.narrow, buffer, buffer_size); + Py_END_ALLOW_THREADS; + + if (length < 0) { + if (errno == ERANGE) + continue; + path_error("listxattr", &path); + break; } - Py_END_ALLOW_THREADS - } - Py_DECREF(opath); - if (res < 0) { - return posix_error(); - } - Py_RETURN_NONE; -} -#endif - -#ifdef HAVE_LINKAT -PyDoc_STRVAR(posix_linkat__doc__, -"linkat(srcfd, srcpath, dstfd, dstpath, flags=0)\n\n\ -Like link() but if srcpath is relative, it is taken as relative to srcfd\n\ -and if dstpath is relative, it is taken as relative to dstfd.\n\ -flags is optional and may be 0 or AT_SYMLINK_FOLLOW.\n\ -If srcpath is relative and srcfd is the special value AT_FDCWD, then\n\ -srcpath is interpreted relative to the current working directory. This\n\ -also applies for dstpath."); - -static PyObject * -posix_linkat(PyObject *self, PyObject *args) -{ - PyObject *osrc, *odst; - char *src, *dst; - int res, srcfd, dstfd; - int flags = 0; - - if (!PyArg_ParseTuple(args, "iO&iO&|i:linkat", - &srcfd, PyUnicode_FSConverter, &osrc, &dstfd, PyUnicode_FSConverter, &odst, &flags)) - return NULL; - src = PyBytes_AsString(osrc); - dst = PyBytes_AsString(odst); - Py_BEGIN_ALLOW_THREADS - res = linkat(srcfd, src, dstfd, dst, flags); - Py_END_ALLOW_THREADS - Py_DECREF(osrc); - Py_DECREF(odst); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_LINKAT */ - -#ifdef HAVE_MKDIRAT -PyDoc_STRVAR(posix_mkdirat__doc__, -"mkdirat(dirfd, path, mode=0o777)\n\n\ -Like mkdir() but if path is relative, it is taken as relative to dirfd.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_mkdirat(PyObject *self, PyObject *args) -{ - int res, dirfd; - PyObject *opath; - char *path; - int mode = 0777; - - if (!PyArg_ParseTuple(args, "iO&|i:mkdirat", - &dirfd, PyUnicode_FSConverter, &opath, &mode)) - return NULL; - path = PyBytes_AsString(opath); - Py_BEGIN_ALLOW_THREADS - res = mkdirat(dirfd, path, mode); - Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif - -#if defined(HAVE_MKNODAT) && defined(HAVE_MAKEDEV) -PyDoc_STRVAR(posix_mknodat__doc__, -"mknodat(dirfd, path, mode=0o600, device=0)\n\n\ -Like mknod() but if path is relative, it is taken as relative to dirfd.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_mknodat(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *filename; - int mode = 0600; - int device = 0; - int res, dirfd; - if (!PyArg_ParseTuple(args, "iO&|ii:mknodat", &dirfd, - PyUnicode_FSConverter, &opath, &mode, &device)) - return NULL; - filename = PyBytes_AS_STRING(opath); - Py_BEGIN_ALLOW_THREADS - res = mknodat(dirfd, filename, mode, device); - Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif - -#ifdef HAVE_OPENAT -PyDoc_STRVAR(posix_openat__doc__, -"openat(dirfd, path, flag, mode=0o777) -> fd\n\n\ -Like open() but if path is relative, it is taken as relative to dirfd.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_openat(PyObject *self, PyObject *args) -{ - PyObject *ofile; - char *file; - int flag, dirfd, fd; - int mode = 0777; - - if (!PyArg_ParseTuple(args, "iO&i|i:openat", - &dirfd, PyUnicode_FSConverter, &ofile, - &flag, &mode)) - return NULL; - file = PyBytes_AsString(ofile); - Py_BEGIN_ALLOW_THREADS - fd = openat(dirfd, file, flag, mode); - Py_END_ALLOW_THREADS - Py_DECREF(ofile); - if (fd < 0) - return posix_error(); - return PyLong_FromLong((long)fd); -} -#endif - -#ifdef HAVE_READLINKAT -PyDoc_STRVAR(posix_readlinkat__doc__, -"readlinkat(dirfd, path) -> path\n\n\ -Like readlink() but if path is relative, it is taken as relative to dirfd.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_readlinkat(PyObject *self, PyObject *args) -{ - PyObject *v, *opath; - char buf[MAXPATHLEN]; - char *path; - int n, dirfd; - int arg_is_unicode = 0; - - if (!PyArg_ParseTuple(args, "iO&:readlinkat", - &dirfd, PyUnicode_FSConverter, &opath)) - return NULL; - path = PyBytes_AsString(opath); - v = PySequence_GetItem(args, 1); - if (v == NULL) { - Py_DECREF(opath); - return NULL; - } - - if (PyUnicode_Check(v)) { - arg_is_unicode = 1; - } - Py_DECREF(v); - - Py_BEGIN_ALLOW_THREADS - n = readlinkat(dirfd, path, buf, (int) sizeof buf); - Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (n < 0) - return posix_error(); - - if (arg_is_unicode) - return PyUnicode_DecodeFSDefaultAndSize(buf, n); - else - return PyBytes_FromStringAndSize(buf, n); -} -#endif /* HAVE_READLINKAT */ - -#ifdef HAVE_RENAMEAT -PyDoc_STRVAR(posix_renameat__doc__, -"renameat(olddirfd, oldpath, newdirfd, newpath)\n\n\ -Like rename() but if oldpath is relative, it is taken as relative to\n\ -olddirfd and if newpath is relative, it is taken as relative to newdirfd.\n\ -If oldpath is relative and olddirfd is the special value AT_FDCWD, then\n\ -oldpath is interpreted relative to the current working directory. This\n\ -also applies for newpath."); - -static PyObject * -posix_renameat(PyObject *self, PyObject *args) -{ - int res; - PyObject *opathold, *opathnew; - char *opath, *npath; - int oldfd, newfd; - - if (!PyArg_ParseTuple(args, "iO&iO&:renameat", - &oldfd, PyUnicode_FSConverter, &opathold, &newfd, PyUnicode_FSConverter, &opathnew)) - return NULL; - opath = PyBytes_AsString(opathold); - npath = PyBytes_AsString(opathnew); - Py_BEGIN_ALLOW_THREADS - res = renameat(oldfd, opath, newfd, npath); - Py_END_ALLOW_THREADS - Py_DECREF(opathold); - Py_DECREF(opathnew); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif - -#if HAVE_SYMLINKAT -PyDoc_STRVAR(posix_symlinkat__doc__, -"symlinkat(src, dstfd, dst)\n\n\ -Like symlink() but if dst is relative, it is taken as relative to dstfd.\n\ -If dst is relative and dstfd is the special value AT_FDCWD, then dst\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_symlinkat(PyObject *self, PyObject *args) -{ - int res, dstfd; - PyObject *osrc, *odst; - char *src, *dst; - - if (!PyArg_ParseTuple(args, "O&iO&:symlinkat", - PyUnicode_FSConverter, &osrc, &dstfd, PyUnicode_FSConverter, &odst)) - return NULL; - src = PyBytes_AsString(osrc); - dst = PyBytes_AsString(odst); - Py_BEGIN_ALLOW_THREADS - res = symlinkat(src, dstfd, dst); - Py_END_ALLOW_THREADS - Py_DECREF(osrc); - Py_DECREF(odst); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SYMLINKAT */ - -#ifdef HAVE_UNLINKAT -PyDoc_STRVAR(posix_unlinkat__doc__, -"unlinkat(dirfd, path, flags=0)\n\n\ -Like unlink() but if path is relative, it is taken as relative to dirfd.\n\ -flags is optional and may be 0 or AT_REMOVEDIR. If AT_REMOVEDIR is\n\ -specified, unlinkat() behaves like rmdir().\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_unlinkat(PyObject *self, PyObject *args) -{ - int dirfd, res, flags = 0; - PyObject *opath; - char *path; - - if (!PyArg_ParseTuple(args, "iO&|i:unlinkat", - &dirfd, PyUnicode_FSConverter, &opath, &flags)) - return NULL; - path = PyBytes_AsString(opath); - Py_BEGIN_ALLOW_THREADS - res = unlinkat(dirfd, path, flags); - Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif - -#ifdef HAVE_UTIMENSAT -PyDoc_STRVAR(posix_utimensat__doc__, -"utimensat(dirfd, path[, atime=(atime_sec, atime_nsec),\n\ - mtime=(mtime_sec, mtime_nsec), flags=0])\n\ -utimensat(dirfd, path, None, None, flags)\n\n\ -Updates the timestamps of a file with nanosecond precision. If path is\n\ -relative, it is taken as relative to dirfd.\n\ -If atime and mtime are both None, which is the default, set atime and\n\ -mtime to the current time.\n\ -flags is optional and may be 0 or AT_SYMLINK_NOFOLLOW.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory.\n\ -If *_nsec is specified as UTIME_NOW, the timestamp is updated to the\n\ -current time.\n\ -If *_nsec is specified as UTIME_OMIT, the timestamp is not updated."); - -static PyObject * -posix_utimensat(PyObject *self, PyObject *args, PyObject *kwargs) -{ - PyObject *opath; - char *path; - int res, dirfd, flags = 0; - PyObject *atime = Py_None; - PyObject *mtime = Py_None; - - static char *kwlist[] = {"dirfd", "path", "atime", "mtime", "flags", NULL}; - - struct timespec buf[2]; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&|OOi:utimensat", kwlist, - &dirfd, PyUnicode_FSConverter, &opath, &atime, &mtime, &flags)) - return NULL; - path = PyBytes_AsString(opath); - if (atime == Py_None && mtime == Py_None) { - /* optional time values not given */ - Py_BEGIN_ALLOW_THREADS - res = utimensat(dirfd, path, NULL, flags); - Py_END_ALLOW_THREADS - } - else if (!PyTuple_Check(atime) || PyTuple_Size(atime) != 2) { - PyErr_SetString(PyExc_TypeError, - "utimensat() arg 3 must be a tuple (atime_sec, atime_nsec)"); - Py_DECREF(opath); - return NULL; - } - else if (!PyTuple_Check(mtime) || PyTuple_Size(mtime) != 2) { - PyErr_SetString(PyExc_TypeError, - "utimensat() arg 4 must be a tuple (mtime_sec, mtime_nsec)"); - Py_DECREF(opath); - return NULL; - } - else { - if (!PyArg_ParseTuple(atime, "ll:utimensat", - &(buf[0].tv_sec), &(buf[0].tv_nsec))) { - Py_DECREF(opath); - return NULL; + + result = PyList_New(0); + if (!result) { + goto exit; } - if (!PyArg_ParseTuple(mtime, "ll:utimensat", - &(buf[1].tv_sec), &(buf[1].tv_nsec))) { - Py_DECREF(opath); - return NULL; + + end = buffer + length; + for (trace = start = buffer; trace != end; trace++) { + if (!*trace) { + int error; + PyObject *attribute = PyUnicode_DecodeFSDefaultAndSize(start, trace - start); + if (!attribute) { + Py_DECREF(result); + result = NULL; + goto exit; + } + error = PyList_Append(result, attribute); + Py_DECREF(attribute); + if (error) { + Py_DECREF(result); + result = NULL; + goto exit; + } + start = trace + 1; + } } - Py_BEGIN_ALLOW_THREADS - res = utimensat(dirfd, path, buf, flags); - Py_END_ALLOW_THREADS - } - Py_DECREF(opath); - if (res < 0) { - return posix_error(); - } - Py_RETURN_NONE; -} -#endif - -#ifdef HAVE_MKFIFOAT -PyDoc_STRVAR(posix_mkfifoat__doc__, -"mkfifoat(dirfd, path, mode=0o666)\n\n\ -Like mkfifo() but if path is relative, it is taken as relative to dirfd.\n\ -If path is relative and dirfd is the special value AT_FDCWD, then path\n\ -is interpreted relative to the current working directory."); - -static PyObject * -posix_mkfifoat(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *filename; - int mode = 0666; - int res, dirfd; - if (!PyArg_ParseTuple(args, "iO&|i:mkfifoat", - &dirfd, PyUnicode_FSConverter, &opath, &mode)) - return NULL; - filename = PyBytes_AS_STRING(opath); - Py_BEGIN_ALLOW_THREADS - res = mkfifoat(dirfd, filename, mode); - Py_END_ALLOW_THREADS - Py_DECREF(opath); - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif - -#ifdef USE_XATTRS - -static int -try_getxattr(const char *path, const char *name, - ssize_t (*get)(const char *, const char *, void *, size_t), - Py_ssize_t buf_size, PyObject **res) -{ - PyObject *value; - Py_ssize_t len; - - assert(buf_size <= XATTR_SIZE_MAX); - value = PyBytes_FromStringAndSize(NULL, buf_size); - if (!value) - return 0; - Py_BEGIN_ALLOW_THREADS; - len = get(path, name, PyBytes_AS_STRING(value), buf_size); - Py_END_ALLOW_THREADS; - if (len < 0) { - Py_DECREF(value); - if (errno == ERANGE) { - value = NULL; - } - else { - posix_error(); - return 0; - } - } - else if (len != buf_size) { - /* Can only shrink. */ - _PyBytes_Resize(&value, len); - } - *res = value; - return 1; -} - -static PyObject * -getxattr_common(const char *path, PyObject *name_obj, - ssize_t (*get)(const char *, const char *, void *, size_t)) -{ - PyObject *value; - const char *name = PyBytes_AS_STRING(name_obj); - - /* Try a small value first. */ - if (!try_getxattr(path, name, get, 128, &value)) - return NULL; - if (value) - return value; - /* Now the maximum possible one. */ - if (!try_getxattr(path, name, get, XATTR_SIZE_MAX, &value)) - return NULL; - assert(value); - return value; -} - -PyDoc_STRVAR(posix_getxattr__doc__, -"getxattr(path, attr) -> value\n\n\ -Return the value of extended attribute *name* on *path*."); - -static PyObject * -posix_getxattr(PyObject *self, PyObject *args) -{ - PyObject *path, *res, *name; - - if (!PyArg_ParseTuple(args, "O&O&:getxattr", PyUnicode_FSConverter, &path, - PyUnicode_FSConverter, &name)) - return NULL; - res = getxattr_common(PyBytes_AS_STRING(path), name, getxattr); - Py_DECREF(path); - Py_DECREF(name); - return res; -} - -PyDoc_STRVAR(posix_lgetxattr__doc__, -"lgetxattr(path, attr) -> value\n\n\ -Like getxattr but don't follow symlinks."); - -static PyObject * -posix_lgetxattr(PyObject *self, PyObject *args) -{ - PyObject *path, *res, *name; - - if (!PyArg_ParseTuple(args, "O&O&:lgetxattr", PyUnicode_FSConverter, &path, - PyUnicode_FSConverter, &name)) - return NULL; - res = getxattr_common(PyBytes_AS_STRING(path), name, lgetxattr); - Py_DECREF(path); - Py_DECREF(name); - return res; -} - -static ssize_t -wrap_fgetxattr(const char *path, const char *name, void *value, size_t size) -{ - /* Hack to share code. */ - return fgetxattr((int)(Py_uintptr_t)path, name, value, size); -} - -PyDoc_STRVAR(posix_fgetxattr__doc__, -"fgetxattr(fd, attr) -> value\n\n\ -Like getxattr but operate on a fd instead of a path."); - -static PyObject * -posix_fgetxattr(PyObject *self, PyObject *args) -{ - PyObject *res, *name; - int fd; - - if (!PyArg_ParseTuple(args, "iO&:fgetxattr", &fd, PyUnicode_FSConverter, &name)) - return NULL; - res = getxattr_common((const char *)(Py_uintptr_t)fd, name, wrap_fgetxattr); - Py_DECREF(name); - return res; -} - -PyDoc_STRVAR(posix_setxattr__doc__, -"setxattr(path, attr, value, flags=0)\n\n\ -Set extended attribute *attr* on *path* to *value*."); - -static PyObject * -posix_setxattr(PyObject *self, PyObject *args) -{ - PyObject *path, *name; - Py_buffer data; - int flags = 0, err; - - if (!PyArg_ParseTuple(args, "O&O&y*|i:setxattr", PyUnicode_FSConverter, - &path, PyUnicode_FSConverter, &name, &data, &flags)) - return NULL; - Py_BEGIN_ALLOW_THREADS; - err = setxattr(PyBytes_AS_STRING(path), PyBytes_AS_STRING(name), - data.buf, data.len, flags); - Py_END_ALLOW_THREADS; - Py_DECREF(path); - Py_DECREF(name); - PyBuffer_Release(&data); - if (err) - return posix_error(); - Py_RETURN_NONE; -} - -PyDoc_STRVAR(posix_lsetxattr__doc__, -"lsetxattr(path, attr, value, flags=0)\n\n\ -Like setxattr but don't follow symlinks."); - -static PyObject * -posix_lsetxattr(PyObject *self, PyObject *args) -{ - PyObject *path, *name; - Py_buffer data; - int flags = 0, err; - - if (!PyArg_ParseTuple(args, "O&O&y*|i:lsetxattr", PyUnicode_FSConverter, - &path, PyUnicode_FSConverter, &name, &data, &flags)) - return NULL; - Py_BEGIN_ALLOW_THREADS; - err = lsetxattr(PyBytes_AS_STRING(path), PyBytes_AS_STRING(name), - data.buf, data.len, flags); - Py_END_ALLOW_THREADS; - Py_DECREF(path); - Py_DECREF(name); - PyBuffer_Release(&data); - if (err) - return posix_error(); - Py_RETURN_NONE; -} - -PyDoc_STRVAR(posix_fsetxattr__doc__, -"fsetxattr(fd, attr, value, flags=0)\n\n\ -Like setxattr but operates on *fd* instead of a path."); - -static PyObject * -posix_fsetxattr(PyObject *self, PyObject *args) -{ - Py_buffer data; - const char *name; - int fd, flags = 0, err; - - if (!PyArg_ParseTuple(args, "iO&y*|i:fsetxattr", &fd, PyUnicode_FSConverter, - &name, &data, &flags)) - return NULL; - Py_BEGIN_ALLOW_THREADS; - err = fsetxattr(fd, PyBytes_AS_STRING(name), data.buf, data.len, flags); - Py_END_ALLOW_THREADS; - Py_DECREF(name); - PyBuffer_Release(&data); - if (err) - return posix_error(); - Py_RETURN_NONE; -} - -PyDoc_STRVAR(posix_removexattr__doc__, -"removexattr(path, attr)\n\n\ -Remove extended attribute *attr* on *path*."); - -static PyObject * -posix_removexattr(PyObject *self, PyObject *args) -{ - PyObject *path, *name; - int err; - - if (!PyArg_ParseTuple(args, "O&O&:removexattr", PyUnicode_FSConverter, &path, - PyUnicode_FSConverter, &name)) - return NULL; - Py_BEGIN_ALLOW_THREADS; - err = removexattr(PyBytes_AS_STRING(path), PyBytes_AS_STRING(name)); - Py_END_ALLOW_THREADS; - Py_DECREF(path); - Py_DECREF(name); - if (err) - return posix_error(); - Py_RETURN_NONE; -} - -PyDoc_STRVAR(posix_lremovexattr__doc__, -"lremovexattr(path, attr)\n\n\ -Like removexattr but don't follow symlinks."); - -static PyObject * -posix_lremovexattr(PyObject *self, PyObject *args) -{ - PyObject *path, *name; - int err; - - if (!PyArg_ParseTuple(args, "O&O&:lremovexattr", PyUnicode_FSConverter, &path, - PyUnicode_FSConverter, &name)) - return NULL; - Py_BEGIN_ALLOW_THREADS; - err = lremovexattr(PyBytes_AS_STRING(path), PyBytes_AS_STRING(name)); - Py_END_ALLOW_THREADS; - Py_DECREF(path); - Py_DECREF(name); - if (err) - return posix_error(); - Py_RETURN_NONE; -} - -PyDoc_STRVAR(posix_fremovexattr__doc__, -"fremovexattr(fd, attr)\n\n\ -Like removexattr but operates on a file descriptor."); - -static PyObject * -posix_fremovexattr(PyObject *self, PyObject *args) -{ - PyObject *name; - int fd, err; - - if (!PyArg_ParseTuple(args, "iO&:fremovexattr", &fd, - PyUnicode_FSConverter, &name)) - return NULL; - Py_BEGIN_ALLOW_THREADS; - err = fremovexattr(fd, PyBytes_AS_STRING(name)); - Py_END_ALLOW_THREADS; - Py_DECREF(name); - if (err) - return posix_error(); - Py_RETURN_NONE; -} - -static Py_ssize_t -try_listxattr(const char *path, ssize_t (*list)(const char *, char *, size_t), - Py_ssize_t buf_size, char **buf) -{ - Py_ssize_t len; - - *buf = PyMem_MALLOC(buf_size); - if (!*buf) { - PyErr_NoMemory(); - return -1; - } - Py_BEGIN_ALLOW_THREADS; - len = list(path, *buf, buf_size); - Py_END_ALLOW_THREADS; - if (len < 0) { - PyMem_FREE(*buf); - if (errno != ERANGE) - posix_error(); - return -1; - } - return len; -} - -static PyObject * -listxattr_common(const char *path, ssize_t (*list)(const char *, char *, size_t)) -{ - PyObject *res, *attr; - Py_ssize_t len, err, start, i; - char *buf; - - len = try_listxattr(path, list, 256, &buf); - if (len < 0) { - if (PyErr_Occurred()) - return NULL; - len = try_listxattr(path, list, XATTR_LIST_MAX, &buf); - if (len < 0) - return NULL; - } - res = PyList_New(0); - if (!res) { - PyMem_FREE(buf); - return NULL; - } - for (start = i = 0; i < len; i++) { - if (!buf[i]) { - attr = PyUnicode_DecodeFSDefaultAndSize(&buf[start], i - start); - if (!attr) { - Py_DECREF(res); - PyMem_FREE(buf); - return NULL; - } - err = PyList_Append(res, attr); - Py_DECREF(attr); - if (err) { - Py_DECREF(res); - PyMem_FREE(buf); - return NULL; - } - start = i + 1; - } - } - PyMem_FREE(buf); - return res; -} - -PyDoc_STRVAR(posix_listxattr__doc__, -"listxattr(path)\n\n\ -Return a list of extended attributes on *path*."); - -static PyObject * -posix_listxattr(PyObject *self, PyObject *args) -{ - PyObject *path, *res; - - if (!PyArg_ParseTuple(args, "O&:listxattr", PyUnicode_FSConverter, &path)) - return NULL; - res = listxattr_common(PyBytes_AS_STRING(path), listxattr); - Py_DECREF(path); - return res; -} - -PyDoc_STRVAR(posix_llistxattr__doc__, -"llistxattr(path)\n\n\ -Like listxattr but don't follow symlinks.."); - -static PyObject * -posix_llistxattr(PyObject *self, PyObject *args) -{ - PyObject *path, *res; - - if (!PyArg_ParseTuple(args, "O&:llistxattr", PyUnicode_FSConverter, &path)) - return NULL; - res = listxattr_common(PyBytes_AS_STRING(path), llistxattr); - Py_DECREF(path); - return res; -} - -static ssize_t -wrap_flistxattr(const char *path, char *buf, size_t len) -{ - /* Hack to share code. */ - return flistxattr((int)(Py_uintptr_t)path, buf, len); -} - -PyDoc_STRVAR(posix_flistxattr__doc__, -"flistxattr(path)\n\n\ -Like flistxattr but operates on a file descriptor."); - -static PyObject * -posix_flistxattr(PyObject *self, PyObject *args) -{ - long fd; - - if (!PyArg_ParseTuple(args, "i:flistxattr", &fd)) - return NULL; - return listxattr_common((const char *)(Py_uintptr_t)fd, wrap_flistxattr); + break; + } +exit: + path_cleanup(&path); + if (buffer) + PyMem_FREE(buffer); + return result; } #endif /* USE_XATTRS */ @@ -10596,20 +10348,30 @@ static PyMethodDef posix_methods[] = { - {"access", posix_access, METH_VARARGS, posix_access__doc__}, + {"access", (PyCFunction)posix_access, + METH_VARARGS | METH_KEYWORDS, + posix_access__doc__}, #ifdef HAVE_TTYNAME {"ttyname", posix_ttyname, METH_VARARGS, posix_ttyname__doc__}, #endif - {"chdir", posix_chdir, METH_VARARGS, posix_chdir__doc__}, + {"chdir", (PyCFunction)posix_chdir, + METH_VARARGS | METH_KEYWORDS, + posix_chdir__doc__}, #ifdef HAVE_CHFLAGS - {"chflags", posix_chflags, METH_VARARGS, posix_chflags__doc__}, + {"chflags", (PyCFunction)posix_chflags, + METH_VARARGS | METH_KEYWORDS, + posix_chflags__doc__}, #endif /* HAVE_CHFLAGS */ - {"chmod", posix_chmod, METH_VARARGS, posix_chmod__doc__}, + {"chmod", (PyCFunction)posix_chmod, + METH_VARARGS | METH_KEYWORDS, + posix_chmod__doc__}, #ifdef HAVE_FCHMOD {"fchmod", posix_fchmod, METH_VARARGS, posix_fchmod__doc__}, #endif /* HAVE_FCHMOD */ #ifdef HAVE_CHOWN - {"chown", posix_chown, METH_VARARGS, posix_chown__doc__}, + {"chown", (PyCFunction)posix_chown, + METH_VARARGS | METH_KEYWORDS, + posix_chown__doc__}, #endif /* HAVE_CHOWN */ #ifdef HAVE_LCHMOD {"lchmod", posix_lchmod, METH_VARARGS, posix_lchmod__doc__}, @@ -10636,14 +10398,19 @@ METH_NOARGS, posix_getcwdb__doc__}, #endif #ifdef HAVE_LINK - {"link", posix_link, METH_VARARGS, posix_link__doc__}, + {"link", (PyCFunction)posix_link, + METH_VARARGS | METH_KEYWORDS, + posix_link__doc__}, #endif /* HAVE_LINK */ - {"listdir", posix_listdir, METH_VARARGS, posix_listdir__doc__}, -#ifdef HAVE_FDOPENDIR - {"flistdir", posix_flistdir, METH_VARARGS, posix_flistdir__doc__}, -#endif - {"lstat", posix_lstat, METH_VARARGS, posix_lstat__doc__}, - {"mkdir", posix_mkdir, METH_VARARGS, posix_mkdir__doc__}, + {"listdir", (PyCFunction)posix_listdir, + METH_VARARGS | METH_KEYWORDS, + posix_listdir__doc__}, + {"lstat", (PyCFunction)posix_lstat, + METH_VARARGS | METH_KEYWORDS, + posix_lstat__doc__}, + {"mkdir", (PyCFunction)posix_mkdir, + METH_VARARGS | METH_KEYWORDS, + posix_mkdir__doc__}, #ifdef HAVE_NICE {"nice", posix_nice, METH_VARARGS, posix_nice__doc__}, #endif /* HAVE_NICE */ @@ -10654,23 +10421,29 @@ {"setpriority", posix_setpriority, METH_VARARGS, posix_setpriority__doc__}, #endif /* HAVE_SETPRIORITY */ #ifdef HAVE_READLINK - {"readlink", posix_readlink, METH_VARARGS, posix_readlink__doc__}, + {"readlink", (PyCFunction)posix_readlink, + METH_VARARGS | METH_KEYWORDS, + posix_readlink__doc__}, #endif /* HAVE_READLINK */ #if !defined(HAVE_READLINK) && defined(MS_WINDOWS) {"readlink", win_readlink, METH_VARARGS, win_readlink__doc__}, #endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */ - {"rename", posix_rename, METH_VARARGS, posix_rename__doc__}, - {"replace", posix_replace, METH_VARARGS, posix_replace__doc__}, + {"rename", (PyCFunction)posix_rename, + METH_VARARGS | METH_KEYWORDS, + posix_rename__doc__}, + {"replace", (PyCFunction)posix_replace, + METH_VARARGS | METH_KEYWORDS, + posix_replace__doc__}, {"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__}, - {"stat", posix_stat, METH_VARARGS, posix_stat__doc__}, + {"stat", (PyCFunction)posix_stat, + METH_VARARGS | METH_KEYWORDS, + posix_stat__doc__}, {"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__}, -#if defined(HAVE_SYMLINK) && !defined(MS_WINDOWS) - {"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__}, +#if defined(HAVE_SYMLINK) + {"symlink", (PyCFunction)posix_symlink, + METH_VARARGS | METH_KEYWORDS, + posix_symlink__doc__}, #endif /* HAVE_SYMLINK */ -#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS) - {"symlink", (PyCFunction)win_symlink, METH_VARARGS | METH_KEYWORDS, - win_symlink__doc__}, -#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */ #ifdef HAVE_SYSTEM {"system", posix_system, METH_VARARGS, posix_system__doc__}, #endif @@ -10678,29 +10451,24 @@ #ifdef HAVE_UNAME {"uname", posix_uname, METH_NOARGS, posix_uname__doc__}, #endif /* HAVE_UNAME */ - {"unlink", posix_unlink, METH_VARARGS, posix_unlink__doc__}, - {"remove", posix_unlink, METH_VARARGS, posix_remove__doc__}, + {"unlink", (PyCFunction)posix_unlink, + METH_VARARGS | METH_KEYWORDS, + posix_unlink__doc__}, + {"remove", (PyCFunction)posix_unlink, + METH_VARARGS | METH_KEYWORDS, + posix_remove__doc__}, {"utime", (PyCFunction)posix_utime, METH_VARARGS | METH_KEYWORDS, posix_utime__doc__}, -#ifdef HAVE_FUTIMES - {"futimes", (PyCFunction)posix_futimes, - METH_VARARGS | METH_KEYWORDS, posix_futimes__doc__}, -#endif -#ifdef HAVE_LUTIMES - {"lutimes", (PyCFunction)posix_lutimes, - METH_VARARGS | METH_KEYWORDS, posix_lutimes__doc__}, -#endif #ifdef HAVE_TIMES {"times", posix_times, METH_NOARGS, posix_times__doc__}, #endif /* HAVE_TIMES */ {"_exit", posix__exit, METH_VARARGS, posix__exit__doc__}, #ifdef HAVE_EXECV {"execv", posix_execv, METH_VARARGS, posix_execv__doc__}, - {"execve", posix_execve, METH_VARARGS, posix_execve__doc__}, + {"execve", (PyCFunction)posix_execve, + METH_VARARGS | METH_KEYWORDS, + posix_execve__doc__}, #endif /* HAVE_EXECV */ -#ifdef HAVE_FEXECVE - {"fexecve", posix_fexecve, METH_VARARGS, posix_fexecve__doc__}, -#endif #ifdef HAVE_SPAWNV {"spawnv", posix_spawnv, METH_VARARGS, posix_spawnv__doc__}, {"spawnve", posix_spawnve, METH_VARARGS, posix_spawnve__doc__}, @@ -10849,7 +10617,9 @@ #ifdef HAVE_TCSETPGRP {"tcsetpgrp", posix_tcsetpgrp, METH_VARARGS, posix_tcsetpgrp__doc__}, #endif /* HAVE_TCSETPGRP */ - {"open", posix_open, METH_VARARGS, posix_open__doc__}, + {"open", (PyCFunction)posix_open,\ + METH_VARARGS | METH_KEYWORDS, + posix_open__doc__}, {"close", posix_close, METH_VARARGS, posix_close__doc__}, {"closerange", posix_closerange, METH_VARARGS, posix_closerange__doc__}, {"device_encoding", device_encoding, METH_VARARGS, device_encoding__doc__}, @@ -10886,10 +10656,14 @@ {"pipe2", posix_pipe2, METH_O, posix_pipe2__doc__}, #endif #ifdef HAVE_MKFIFO - {"mkfifo", posix_mkfifo, METH_VARARGS, posix_mkfifo__doc__}, + {"mkfifo", (PyCFunction)posix_mkfifo, + METH_VARARGS | METH_KEYWORDS, + posix_mkfifo__doc__}, #endif #if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) - {"mknod", posix_mknod, METH_VARARGS, posix_mknod__doc__}, + {"mknod", (PyCFunction)posix_mknod, + METH_VARARGS | METH_KEYWORDS, + posix_mknod__doc__}, #endif #ifdef HAVE_DEVICE_MACROS {"major", posix_major, METH_VARARGS, posix_major__doc__}, @@ -10957,7 +10731,9 @@ {"fstatvfs", posix_fstatvfs, METH_VARARGS, posix_fstatvfs__doc__}, #endif #if defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H) - {"statvfs", posix_statvfs, METH_VARARGS, posix_statvfs__doc__}, + {"statvfs", (PyCFunction)posix_statvfs, + METH_VARARGS | METH_KEYWORDS, + posix_statvfs__doc__}, #endif #ifdef HAVE_CONFSTR {"confstr", posix_confstr, METH_VARARGS, posix_confstr__doc__}, @@ -10996,67 +10772,19 @@ {"getresgid", posix_getresgid, METH_NOARGS, posix_getresgid__doc__}, #endif -/* posix *at family of functions */ -#ifdef HAVE_FACCESSAT - {"faccessat", posix_faccessat, METH_VARARGS, posix_faccessat__doc__}, -#endif -#ifdef HAVE_FCHMODAT - {"fchmodat", posix_fchmodat, METH_VARARGS, posix_fchmodat__doc__}, -#endif /* HAVE_FCHMODAT */ -#ifdef HAVE_FCHOWNAT - {"fchownat", posix_fchownat, METH_VARARGS, posix_fchownat__doc__}, -#endif /* HAVE_FCHOWNAT */ -#ifdef HAVE_FSTATAT - {"fstatat", posix_fstatat, METH_VARARGS, posix_fstatat__doc__}, -#endif -#ifdef HAVE_FUTIMESAT - {"futimesat", posix_futimesat, METH_VARARGS, posix_futimesat__doc__}, -#endif -#ifdef HAVE_LINKAT - {"linkat", posix_linkat, METH_VARARGS, posix_linkat__doc__}, -#endif /* HAVE_LINKAT */ -#ifdef HAVE_MKDIRAT - {"mkdirat", posix_mkdirat, METH_VARARGS, posix_mkdirat__doc__}, -#endif -#if defined(HAVE_MKNODAT) && defined(HAVE_MAKEDEV) - {"mknodat", posix_mknodat, METH_VARARGS, posix_mknodat__doc__}, -#endif -#ifdef HAVE_OPENAT - {"openat", posix_openat, METH_VARARGS, posix_openat__doc__}, -#endif -#ifdef HAVE_READLINKAT - {"readlinkat", posix_readlinkat, METH_VARARGS, posix_readlinkat__doc__}, -#endif /* HAVE_READLINKAT */ -#ifdef HAVE_RENAMEAT - {"renameat", posix_renameat, METH_VARARGS, posix_renameat__doc__}, -#endif -#if HAVE_SYMLINKAT - {"symlinkat", posix_symlinkat, METH_VARARGS, posix_symlinkat__doc__}, -#endif /* HAVE_SYMLINKAT */ -#ifdef HAVE_UNLINKAT - {"unlinkat", posix_unlinkat, METH_VARARGS, posix_unlinkat__doc__}, -#endif -#ifdef HAVE_UTIMENSAT - {"utimensat", (PyCFunction)posix_utimensat, - METH_VARARGS | METH_KEYWORDS, - posix_utimensat__doc__}, -#endif -#ifdef HAVE_MKFIFOAT - {"mkfifoat", posix_mkfifoat, METH_VARARGS, posix_mkfifoat__doc__}, -#endif #ifdef USE_XATTRS - {"setxattr", posix_setxattr, METH_VARARGS, posix_setxattr__doc__}, - {"lsetxattr", posix_lsetxattr, METH_VARARGS, posix_lsetxattr__doc__}, - {"fsetxattr", posix_fsetxattr, METH_VARARGS, posix_fsetxattr__doc__}, - {"getxattr", posix_getxattr, METH_VARARGS, posix_getxattr__doc__}, - {"lgetxattr", posix_lgetxattr, METH_VARARGS, posix_lgetxattr__doc__}, - {"fgetxattr", posix_fgetxattr, METH_VARARGS, posix_fgetxattr__doc__}, - {"removexattr", posix_removexattr, METH_VARARGS, posix_removexattr__doc__}, - {"lremovexattr", posix_lremovexattr, METH_VARARGS, posix_lremovexattr__doc__}, - {"fremovexattr", posix_fremovexattr, METH_VARARGS, posix_fremovexattr__doc__}, - {"listxattr", posix_listxattr, METH_VARARGS, posix_listxattr__doc__}, - {"llistxattr", posix_llistxattr, METH_VARARGS, posix_llistxattr__doc__}, - {"flistxattr", posix_flistxattr, METH_VARARGS, posix_flistxattr__doc__}, + {"setxattr", (PyCFunction)posix_setxattr, + METH_VARARGS | METH_KEYWORDS, + posix_setxattr__doc__}, + {"getxattr", (PyCFunction)posix_getxattr, + METH_VARARGS | METH_KEYWORDS, + posix_getxattr__doc__}, + {"removexattr", (PyCFunction)posix_removexattr, + METH_VARARGS | METH_KEYWORDS, + posix_removexattr__doc__}, + {"listxattr", (PyCFunction)posix_listxattr, + METH_VARARGS | METH_KEYWORDS, + posix_listxattr__doc__}, #endif #if defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) {"get_terminal_size", get_terminal_size, METH_VARARGS, termsize__doc__}, @@ -11280,12 +11008,6 @@ #ifdef AT_SYMLINK_FOLLOW if (ins(d, "AT_SYMLINK_FOLLOW", (long)AT_SYMLINK_FOLLOW)) return -1; #endif -#ifdef UTIME_NOW - if (ins(d, "UTIME_NOW", (long)UTIME_NOW)) return -1; -#endif -#ifdef UTIME_OMIT - if (ins(d, "UTIME_OMIT", (long)UTIME_OMIT)) return -1; -#endif /* MS Windows */ @@ -11472,14 +11194,6 @@ if (ins(d, "F_TEST", (long)F_TEST)) return -1; #endif - /* constants for futimens */ -#ifdef UTIME_NOW - if (ins(d, "UTIME_NOW", (long)UTIME_NOW)) return -1; -#endif -#ifdef UTIME_OMIT - if (ins(d, "UTIME_OMIT", (long)UTIME_OMIT)) return -1; -#endif - #ifdef HAVE_SPAWNV #if defined(PYOS_OS2) && defined(PYCC_GCC) if (ins(d, "P_WAIT", (long)P_WAIT)) return -1; @@ -11541,6 +11255,10 @@ #endif #endif +#ifdef HAVE_OPENAT + if (ins(d, "_HAVE_OPENAT", (long)1)) return -1; +#endif + #ifdef USE_XATTRS if (ins(d, "XATTR_CREATE", (long)XATTR_CREATE)) return -1; if (ins(d, "XATTR_REPLACE", (long)XATTR_REPLACE)) return -1; @@ -11739,6 +11457,11 @@ if (!billion) return NULL; + /* suppress "function not used" warnings */ + dir_fd_specified("", DEFAULT_DIR_FD); + fd_specified("", -1); + follow_symlinks_specified("", 1); + return m; }