Kaydet (Commit) f65132de authored tarafından Antoine Pitrou's avatar Antoine Pitrou

Issue #4761: Add the *at() family of functions (openat(), etc.) to the posix

module.  Patch by Ross Lagerwall.
üst 68e5c044
......@@ -606,6 +606,21 @@ associated with a :term:`file object` when required. Note that using the file
descriptor directly will bypass the file object methods, ignoring aspects such
as internal buffering of data.
.. data:: AT_SYMLINK_NOFOLLOW
AT_EACCESS
AT_FDCWD
AT_REMOVEDIR
AT_SYMLINK_FOLLOW
UTIME_NOW
UTIME_OMIT
These parameters are used as flags to the \*at family of functions.
Availability: Unix.
.. versionadded:: 3.3
.. function:: close(fd)
Close file descriptor *fd*.
......@@ -654,6 +669,19 @@ as internal buffering of data.
Availability: Unix, Windows.
.. function:: faccessat(dirfd, path, mode, flags=0)
Like :func:`access` but if *path* is relative, it is taken as relative to *dirfd*.
*flags* is optional and can be constructed by ORing together zero or more
of these values: :data:`AT_SYMLINK_NOFOLLOW`, :data:`AT_EACCESS`.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: fchmod(fd, mode)
Change the mode of the file given by *fd* to the numeric *mode*. See the docs
......@@ -662,6 +690,18 @@ as internal buffering of data.
Availability: Unix.
.. function:: fchmodat(dirfd, path, mode, flags=0)
Like :func:`chmod` but if *path* is relative, it is taken as relative to *dirfd*.
*flags* is optional and may be 0 or :data:`AT_SYMLINK_NOFOLLOW`.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: fchown(fd, uid, gid)
Change the owner and group id of the file given by *fd* to the numeric *uid*
......@@ -670,6 +710,18 @@ as internal buffering of data.
Availability: Unix.
.. function:: fchownat(dirfd, path, uid, gid, flags=0)
Like :func:`chown` but if *path* is relative, it is taken as relative to *dirfd*.
*flags* is optional and may be 0 or :data:`AT_SYMLINK_NOFOLLOW`.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: fdatasync(fd)
Force write of file with filedescriptor *fd* to disk. Does not force update of
......@@ -705,6 +757,17 @@ as internal buffering of data.
Availability: Unix, Windows.
.. function:: fstatat(dirfd, path, flags=0)
Like :func:`stat` but if *path* is relative, it is taken as relative to *dirfd*.
*flags* is optional and may be 0 or :data:`AT_SYMLINK_NOFOLLOW`.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: fstatvfs(fd)
......@@ -734,6 +797,18 @@ as internal buffering of data.
Availability: Unix.
.. function:: futimesat(dirfd, path, (atime, mtime))
futimesat(dirfd, path, None)
Like :func:`utime` but if *path* is relative, it is taken as relative to *dirfd*.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: isatty(fd)
Return ``True`` if the file descriptor *fd* is open and connected to a
......@@ -742,6 +817,20 @@ as internal buffering of data.
Availability: Unix.
.. function:: linkat(srcfd, srcpath, dstfd, dstpath, flags=0)
Like :func:`link` but if *srcpath* is relative, it is taken as relative to *srcfd*
and if *dstpath* is relative, it is taken as relative to *dstfd*.
*flags* is optional and may be 0 or :data:`AT_SYMLINK_FOLLOW`.
If *srcpath* is relative and *srcfd* is the special value :data:`AT_FDCWD`, then
*srcpath* is interpreted relative to the current working directory. This
also applies for *dstpath*.
Availability: Unix.
.. versionadded:: 3.3
.. function:: lseek(fd, pos, how)
Set the current position of file descriptor *fd* to position *pos*, modified
......@@ -761,6 +850,39 @@ as internal buffering of data.
respectively. Availability: Windows, Unix.
.. function:: mkdirat(dirfd, path, mode=0o777)
Like :func:`mkdir` but if *path* is relative, it is taken as relative to *dirfd*.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: mkfifoat(dirfd, path, mode=0o666)
Like :func:`mkfifo` but if *path* is relative, it is taken as relative to *dirfd*.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: mknodat(dirfd, path, mode=0o600, device=0)
Like :func:`mknod` but if *path* is relative, it is taken as relative to *dirfd*.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: open(file, flags[, mode])
Open the file *file* and set various flags according to *flags* and possibly
......@@ -783,6 +905,17 @@ as internal buffering of data.
wrap a file descriptor in a file object, use :func:`fdopen`.
.. function:: openat(dirfd, path, flags, mode=0o777)
Like :func:`open` but if *path* is relative, it is taken as relative to *dirfd*.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: openpty()
.. index:: module: pty
......@@ -860,6 +993,41 @@ as internal buffering of data.
.. versionadded:: 3.3
.. function:: readlinkat(dirfd, path)
Like :func:`readlink` but if *path* is relative, it is taken as relative to *dirfd*.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: renameat(olddirfd, oldpath, newdirfd, newpath)
Like :func:`rename` but if *oldpath* is relative, it is taken as relative to
*olddirfd* and if *newpath* is relative, it is taken as relative to *newdirfd*.
If *oldpath* is relative and *olddirfd* is the special value :data:`AT_FDCWD`, then
*oldpath* is interpreted relative to the current working directory. This
also applies for *newpath*.
Availability: Unix.
.. versionadded:: 3.3
.. function:: symlinkat(src, dstfd, dst)
Like :func:`symlink` but if *dst* is relative, it is taken as relative to *dstfd*.
If *dst* is relative and *dstfd* is the special value :data:`AT_FDCWD`, then *dst*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: tcgetpgrp(fd)
Return the process group associated with the terminal given by *fd* (an open
......@@ -885,6 +1053,38 @@ as internal buffering of data.
Availability: Unix.
.. function:: unlinkat(dirfd, path, flags=0)
Like :func:`unlink` but if *path* is relative, it is taken as relative to *dirfd*.
*flags* is optional and may be 0 or :data:`AT_REMOVEDIR`. If :data:`AT_REMOVEDIR` is
specified, :func:`unlinkat` behaves like :func:`rmdir`.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: utimensat(dirfd, path, (atime_sec, atime_nsec), (mtime_sec, mtime_nsec), flags)
utimensat(dirfd, path, None, None, flags)
Updates the timestamps of a file with nanosecond precision.
The second form sets *atime* and *mtime* to the current time.
If *atime_nsec* or *mtime_nsec* is specified as :data:`UTIME_NOW`, the corresponding
timestamp is updated to the current time.
If *atime_nsec* or *mtime_nsec* is specified as :data:`UTIME_OMIT`, the corresponding
timestamp is not updated.
If *path* is relative, it is taken as relative to *dirfd*.
*flags* is optional and may be 0 or :data:`AT_SYMLINK_NOFOLLOW`.
If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
is interpreted relative to the current working directory.
Availability: Unix.
.. versionadded:: 3.3
.. function:: write(fd, str)
Write the bytestring in *str* to file descriptor *fd*. Return the number of
......
......@@ -389,6 +389,198 @@ class PosixTester(unittest.TestCase):
set([int(x) for x in groups.split()]),
set(posix.getgroups() + [posix.getegid()]))
# tests for the posix *at functions follow
@unittest.skipUnless(hasattr(posix, '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))
finally:
posix.close(f)
@unittest.skipUnless(hasattr(posix, '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)
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()")
def test_fchownat(self):
support.unlink(support.TESTFN)
open(support.TESTFN, 'w').close()
f = posix.open(posix.getcwd(), posix.O_RDONLY)
try:
posix.fchownat(f, support.TESTFN, os.getuid(), os.getgid())
finally:
posix.close(f)
@unittest.skipUnless(hasattr(posix, 'fstatat'), "test needs posix.fstatat()")
def test_fstatat(self):
support.unlink(support.TESTFN)
with open(support.TESTFN, 'w') as outfile:
outfile.write("testline\n")
f = posix.open(posix.getcwd(), posix.O_RDONLY)
try:
s1 = posix.stat(support.TESTFN)
s2 = posix.fstatat(f, support.TESTFN)
self.assertEqual(s1, s2)
finally:
posix.close(f)
@unittest.skipUnless(hasattr(posix, '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)
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))
finally:
posix.close(f)
@unittest.skipUnless(hasattr(posix, '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')
# should have same inodes
self.assertEqual(posix.stat(support.TESTFN)[1],
posix.stat(support.TESTFN + 'link')[1])
finally:
posix.close(f)
support.unlink(support.TESTFN + 'link')
@unittest.skipUnless(hasattr(posix, '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.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'),
"don't have mknodat()/S_IFIFO")
def test_mknodat(self):
# Test using mknodat() to create a FIFO (the only use specified
# by POSIX).
support.unlink(support.TESTFN)
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)
except OSError as e:
# Some old systems don't allow unprivileged users to use
# mknod(), or only support creating device nodes.
self.assertIn(e.errno, (errno.EPERM, errno.EINVAL))
else:
self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode))
finally:
posix.close(f)
@unittest.skipUnless(hasattr(posix, '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)
try:
res = posix.read(b, 9).decode(encoding="utf-8")
self.assertEqual("testline\n", res)
finally:
posix.close(a)
posix.close(b)
@unittest.skipUnless(hasattr(posix, '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'))
finally:
support.unlink(support.TESTFN + 'link')
posix.close(f)
@unittest.skipUnless(hasattr(posix, 'renameat'), "test needs posix.renameat()")
def test_renameat(self):
support.unlink(support.TESTFN)
open(support.TESTFN + 'ren', 'w').close()
f = posix.open(posix.getcwd(), posix.O_RDONLY)
try:
posix.renameat(f, support.TESTFN + 'ren', f, support.TESTFN)
except:
posix.rename(support.TESTFN + 'ren', support.TESTFN)
raise
else:
posix.stat(support.TESTFN) # should not throw exception
finally:
posix.close(f)
@unittest.skipUnless(hasattr(posix, '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')
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()")
def test_unlinkat(self):
f = posix.open(posix.getcwd(), posix.O_RDONLY)
open(support.TESTFN + 'del', 'w').close()
posix.stat(support.TESTFN + 'del') # should not throw exception
try:
posix.unlinkat(f, support.TESTFN + 'del')
except:
support.unlink(support.TESTFN + 'del')
raise
else:
self.assertRaises(OSError, posix.stat, support.TESTFN + 'link')
finally:
posix.close(f)
@unittest.skipUnless(hasattr(posix, '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)
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)))
finally:
posix.close(f)
@unittest.skipUnless(hasattr(posix, 'mkfifoat'), "don't have 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)
self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode))
finally:
posix.close(f)
class PosixGroupsTester(unittest.TestCase):
def setUp(self):
......
......@@ -35,6 +35,9 @@ Core and Builtins
Library
-------
- Issue #4761: Add the *at() family of functions (openat(), etc.) to the posix
module. Patch by Ross Lagerwall.
- Issue #7322: Trying to read from a socket's file-like object after a timeout
occurred now raises an error instead of silently losing data.
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2534,18 +2534,19 @@ AC_MSG_RESULT(MACHDEP_OBJS)
# checks for library functions
AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
clock confstr ctermid execv faccessat fchmod fchmodat fchown fchownat fork \
fpathconf fstatat ftime ftruncate futimesat \
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
initgroups kill killpg lchmod lchown lstat mbrtowc mkfifo mknod mktime \
mremap nice pathconf pause plock poll pthread_init \
putenv readlink realpath \
initgroups kill killpg lchmod lchown linkat lstat mbrtowc mkdirat mkfifo \
mkfifoat mknod mknodat mktime mremap nice openat pathconf pause plock poll \
pthread_init putenv readlink readlinkat realpath renameat \
select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \
setgid \
setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \
sigaction siginterrupt sigrelse snprintf strftime strlcpy \
sigaction siginterrupt sigrelse snprintf strftime strlcpy symlinkat \
sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \
truncate uname unsetenv utimes waitpid wait3 wait4 \
truncate uname unlinkat unsetenv utimensat utimes waitpid wait3 wait4 \
wcscoll wcsftime wcsxfrm _getpty)
# For some functions, having a definition is not sufficient, since
......
......@@ -205,15 +205,24 @@
/* Define to 1 if you have the `expm1' function. */
#undef HAVE_EXPM1
/* Define to 1 if you have the `faccessat' function. */
#undef HAVE_FACCESSAT
/* Define if you have the 'fchdir' function. */
#undef HAVE_FCHDIR
/* Define to 1 if you have the `fchmod' function. */
#undef HAVE_FCHMOD
/* Define to 1 if you have the `fchmodat' function. */
#undef HAVE_FCHMODAT
/* Define to 1 if you have the `fchown' function. */
#undef HAVE_FCHOWN
/* Define to 1 if you have the `fchownat' function. */
#undef HAVE_FCHOWNAT
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
......@@ -241,6 +250,9 @@
/* Define to 1 if you have the `fseeko' function. */
#undef HAVE_FSEEKO
/* Define to 1 if you have the `fstatat' function. */
#undef HAVE_FSTATAT
/* Define to 1 if you have the `fstatvfs' function. */
#undef HAVE_FSTATVFS
......@@ -259,6 +271,9 @@
/* Define to 1 if you have the `ftruncate' function. */
#undef HAVE_FTRUNCATE
/* Define to 1 if you have the `futimesat' function. */
#undef HAVE_FUTIMESAT
/* Define to 1 if you have the `gai_strerror' function. */
#undef HAVE_GAI_STRERROR
......@@ -431,6 +446,9 @@
/* Define if you have the 'link' function. */
#undef HAVE_LINK
/* Define to 1 if you have the `linkat' function. */
#undef HAVE_LINKAT
/* Define to 1 if you have the <linux/netlink.h> header file. */
#undef HAVE_LINUX_NETLINK_H
......@@ -461,12 +479,21 @@
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `mkdirat' function. */
#undef HAVE_MKDIRAT
/* Define to 1 if you have the `mkfifo' function. */
#undef HAVE_MKFIFO
/* Define to 1 if you have the `mkfifoat' function. */
#undef HAVE_MKFIFOAT
/* Define to 1 if you have the `mknod' function. */
#undef HAVE_MKNOD
/* Define to 1 if you have the `mknodat' function. */
#undef HAVE_MKNODAT
/* Define to 1 if you have the `mktime' function. */
#undef HAVE_MKTIME
......@@ -485,6 +512,9 @@
/* Define to 1 if you have the `nice' function. */
#undef HAVE_NICE
/* Define to 1 if you have the `openat' function. */
#undef HAVE_OPENAT
/* Define to 1 if you have the `openpty' function. */
#undef HAVE_OPENPTY
......@@ -500,6 +530,9 @@
/* Define if the OS supports pipe2() */
#undef HAVE_PIPE2
/* Define if the OS supports pipe2() */
#undef HAVE_PIPE2
/* Define to 1 if you have the `plock' function. */
#undef HAVE_PLOCK
......@@ -536,9 +569,15 @@
/* Define to 1 if you have the `readlink' function. */
#undef HAVE_READLINK
/* Define to 1 if you have the `readlinkat' function. */
#undef HAVE_READLINKAT
/* Define to 1 if you have the `realpath' function. */
#undef HAVE_REALPATH
/* Define to 1 if you have the `renameat' function. */
#undef HAVE_RENAMEAT
/* Define if you have readline 2.1 */
#undef HAVE_RL_CALLBACK
......@@ -724,6 +763,9 @@
/* Define if you have the 'symlink' function. */
#undef HAVE_SYMLINK
/* Define to 1 if you have the `symlinkat' function. */
#undef HAVE_SYMLINKAT
/* Define to 1 if you have the `sysconf' function. */
#undef HAVE_SYSCONF
......@@ -866,6 +908,9 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the `unlinkat' function. */
#undef HAVE_UNLINKAT
/* Define to 1 if you have the `unsetenv' function. */
#undef HAVE_UNSETENV
......@@ -877,6 +922,9 @@
/* Define to 1 if you have the <util.h> header file. */
#undef HAVE_UTIL_H
/* Define to 1 if you have the `utimensat' function. */
#undef HAVE_UTIMENSAT
/* Define to 1 if you have the `utimes' function. */
#undef HAVE_UTIMES
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment