Commit 799bd80d authored by Benjamin Peterson's avatar Benjamin Peterson

expose linux extended file system attributes (closes #12720)

parent ab3bea68
...@@ -751,6 +751,26 @@ as internal buffering of data. ...@@ -751,6 +751,26 @@ as internal buffering of data.
This function is not available on MacOS. This function is not available on MacOS.
.. function:: fgetxattr(fd, attr)
This works exactly like :func:`getxattr` but operates on a file descriptor,
*fd*, instead of a path.
Availability: Linux
.. versionadded:: 3.3
.. function:: flistxattr(fd)
This is exactly like :func:`listxattr` but operates on a file descriptor,
*fd*, instead of a path.
Availability: Linux
.. versionadded:: 3.3
.. function:: fdlistdir(fd) .. function:: fdlistdir(fd)
Like :func:`listdir`, but uses a file descriptor instead and always returns Like :func:`listdir`, but uses a file descriptor instead and always returns
...@@ -836,6 +856,27 @@ as internal buffering of data. ...@@ -836,6 +856,27 @@ as internal buffering of data.
Availability: Unix. Availability: Unix.
.. function:: fremovexattr(fd, attr)
This works exactly like :func:`removexattr` but operates on a file
descriptor, *fd*, instead of a path.
Availability: Linux
.. versionadded:: 3.3
.. function:: fsetxattr(fd, attr, value, flags=0)
This works exactly like :func:`setxattr` but on a file descriptor, *fd*,
instead of a path.
Availability: Linux
.. versionadded:: 3.3
.. function:: futimesat(dirfd, path, (atime, mtime)) .. function:: futimesat(dirfd, path, (atime, mtime))
futimesat(dirfd, path, None) futimesat(dirfd, path, None)
...@@ -1536,6 +1577,17 @@ Files and Directories ...@@ -1536,6 +1577,17 @@ Files and Directories
Availability: Unix. Availability: Unix.
.. function:: getxattr(path, attr)
Return the value of the extended filesystem attribute *attr* for
*path*. *attr* can be bytes or str. If it is str, it is encoded with the
filesystem encoding.
Availability: Linux
.. versionadded:: 3.3
.. function:: lchflags(path, flags) .. function:: lchflags(path, flags)
Set the flags of *path* to the numeric *flags*, like :func:`chflags`, but do not Set the flags of *path* to the numeric *flags*, like :func:`chflags`, but do not
...@@ -1561,6 +1613,15 @@ Files and Directories ...@@ -1561,6 +1613,15 @@ Files and Directories
Availability: Unix. Availability: Unix.
.. function:: lgetxattr(path, attr)
This works exactly like :func:`getxattr` but doesn't follow symlinks.
Availability: Linux
.. versionadded:: 3.3
.. function:: link(source, link_name) .. function:: link(source, link_name)
Create a hard link pointing to *source* named *link_name*. Create a hard link pointing to *source* named *link_name*.
...@@ -1585,6 +1646,44 @@ Files and Directories ...@@ -1585,6 +1646,44 @@ Files and Directories
.. versionchanged:: 3.2 .. versionchanged:: 3.2
The *path* parameter became optional. The *path* parameter became optional.
.. function:: listxattr(path)
Return a list of the extended filesystem attributes on *path*. Attributes are
returned as string decoded with the filesystem encoding.
Availability: Linux
.. versionadded:: 3.3
.. function:: llistxattr(path)
This works exactly like :func:`listxattr` but doesn't follow symlinks.
Availability: Linux
.. versionadded:: 3.3
.. function:: lremoveattr(path, attr)
This works exactly like :func:`removeattr` but doesn't follow symlinks.
Availability: Linux
.. versionadded:: 3.3
.. function:: lsetxattr(path, attr, value, flags=0)
This works exactly like :func:`setxattr` but doesn't follow symlinks.
Availability: Linux
.. versionadded:: 3.3
.. function:: lstat(path) .. function:: lstat(path)
Perform the equivalent of an :c:func:`lstat` system call on the given path. Perform the equivalent of an :c:func:`lstat` system call on the given path.
...@@ -1758,6 +1857,17 @@ Files and Directories ...@@ -1758,6 +1857,17 @@ Files and Directories
successfully removed. successfully removed.
.. function:: removexattr(path, attr)
Removes the extended filesystem attribute *attr* from *path*. *attr* should
be bytes or str. If it is a string, it is encoded with the filesystem
encoding.
Availability: Linux
.. versionadded:: 3.3
.. function:: rename(src, dst) .. function:: rename(src, dst)
Rename the file or directory *src* to *dst*. If *dst* is a directory, Rename the file or directory *src* to *dst*. If *dst* is a directory,
...@@ -1794,6 +1904,44 @@ Files and Directories ...@@ -1794,6 +1904,44 @@ Files and Directories
Availability: Unix, Windows. Availability: Unix, Windows.
.. data:: XATTR_SIZE_MAX
The maximum size the value of an extended attribute can be. Currently, this
is 64 kilobytes on Linux.
.. data:: XATTR_CREATE
This is a possible value for the flags argument in :func:`setxattr`. It
indicates the operation must create an attribute.
.. data:: XATTR_REPLACE
This is a possible value for the flags argument in :func:`setxattr`. It
indicates the operation must replace an existing attribute.
.. function:: setxattr(path, attr, value, flags=0)
Set the extended filesystem attribute *attr* on *path* to *value*. *attr*
must be a bytes or str with no embedded NULs. If it is str, it is encoded
with the filesystem encoding. *flags* may be :data:`XATTR_REPLACE` or
:data:`XATTR_CREATE`. If :data:`XATTR_REPLACE` is given and the attribute
does not exist, ``EEXISTS`` will be raised. If :data:`XATTR_CREATE` is given
and the attribute already exists, the attribute will not be created and
``ENODATA`` will be raised.
Availability: Linux
.. note::
A bug in Linux kernel versions less than 2.6.39 caused the flags argument
to be ignored on some filesystems.
.. versionadded:: 3.3
.. function:: stat(path) .. function:: stat(path)
Perform the equivalent of a :c:func:`stat` system call on the given path. Perform the equivalent of a :c:func:`stat` system call on the given path.
......
...@@ -14,6 +14,8 @@ import shutil ...@@ -14,6 +14,8 @@ import shutil
from test import support from test import support
import contextlib import contextlib
import mmap import mmap
import platform
import re
import uuid import uuid
import asyncore import asyncore
import asynchat import asynchat
...@@ -1506,6 +1508,97 @@ class TestSendfile(unittest.TestCase): ...@@ -1506,6 +1508,97 @@ class TestSendfile(unittest.TestCase):
raise raise
def supports_extended_attributes():
if not hasattr(os, "setxattr"):
return False
try:
with open(support.TESTFN, "wb") as fp:
try:
os.fsetxattr(fp.fileno(), b"user.test", b"")
except OSError as e:
if e.errno != errno.ENOTSUP:
raise
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):
fn = support.TESTFN
open(fn, "wb").close()
with self.assertRaises(OSError) as cm:
getxattr(fn, s("user.test"))
self.assertEqual(cm.exception.errno, errno.ENODATA)
self.assertEqual(listxattr(fn), [])
setxattr(fn, s("user.test"), b"")
self.assertEqual(listxattr(fn), ["user.test"])
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")
with self.assertRaises(OSError) as cm:
setxattr(fn, s("user.test"), b"bye", os.XATTR_CREATE)
self.assertEqual(cm.exception.errno, errno.EEXIST)
with self.assertRaises(OSError) as cm:
setxattr(fn, s("user.test2"), b"bye", os.XATTR_REPLACE)
self.assertEqual(cm.exception.errno, errno.ENODATA)
setxattr(fn, s("user.test2"), b"foo", os.XATTR_CREATE)
self.assertEqual(sorted(listxattr(fn)), ["user.test", "user.test2"])
removexattr(fn, s("user.test"))
with self.assertRaises(OSError) as cm:
getxattr(fn, s("user.test"))
self.assertEqual(cm.exception.errno, errno.ENODATA)
self.assertEqual(listxattr(fn), ["user.test2"])
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"))
many = sorted("user.test{}".format(i) for i in range(100))
for thing in many:
setxattr(fn, thing, b"x")
self.assertEqual(sorted(listxattr(fn)), many)
def _check_xattrs(self, *args):
def make_bytes(s):
return bytes(s, "ascii")
self._check_xattrs_str(str, *args)
support.unlink(support.TESTFN)
self._check_xattrs_str(make_bytes, *args)
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)
def test_fds(self):
def getxattr(path, *args):
with open(path, "rb") as fp:
return os.fgetxattr(fp.fileno(), *args)
def setxattr(path, *args):
with open(path, "wb") as fp:
os.fsetxattr(fp.fileno(), *args)
def removexattr(path, *args):
with open(path, "wb") as fp:
os.fremovexattr(fp.fileno(), *args)
def listxattr(path, *args):
with open(path, "rb") as fp:
return os.flistxattr(fp.fileno(), *args)
self._check_xattrs(getxattr, setxattr, removexattr, listxattr)
@support.reap_threads @support.reap_threads
def test_main(): def test_main():
support.run_unittest( support.run_unittest(
...@@ -1529,6 +1622,7 @@ def test_main(): ...@@ -1529,6 +1622,7 @@ def test_main():
LinkTests, LinkTests,
TestSendfile, TestSendfile,
ProgramPriorityTests, ProgramPriorityTests,
ExtendedAttributeTests,
) )
if __name__ == "__main__": if __name__ == "__main__":
......
This diff is collapsed.
...@@ -6090,7 +6090,7 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h ...@@ -6090,7 +6090,7 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h
fi fi
for ac_header in asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ for ac_header in asm/types.h attr/xattr.h conio.h curses.h direct.h dlfcn.h errno.h \
fcntl.h grp.h \ fcntl.h grp.h \
ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \
sched.h shadow.h signal.h stdint.h stropts.h termios.h \ sched.h shadow.h signal.h stdint.h stropts.h termios.h \
......
...@@ -1299,7 +1299,7 @@ dnl AC_MSG_RESULT($cpp_type) ...@@ -1299,7 +1299,7 @@ dnl AC_MSG_RESULT($cpp_type)
# checks for header files # checks for header files
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADERS(asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ AC_CHECK_HEADERS(asm/types.h attr/xattr.h conio.h curses.h direct.h dlfcn.h errno.h \
fcntl.h grp.h \ fcntl.h grp.h \
ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \
sched.h shadow.h signal.h stdint.h stropts.h termios.h \ sched.h shadow.h signal.h stdint.h stropts.h termios.h \
......
...@@ -64,6 +64,9 @@ ...@@ -64,6 +64,9 @@
/* Define if GCC supports __attribute__((format(PyArg_ParseTuple, 2, 3))) */ /* Define if GCC supports __attribute__((format(PyArg_ParseTuple, 2, 3))) */
#undef HAVE_ATTRIBUTE_FORMAT_PARSETUPLE #undef HAVE_ATTRIBUTE_FORMAT_PARSETUPLE
/* Define to 1 if you have the <attr/xattr.h> header file. */
#undef HAVE_ATTR_XATTR_H
/* Define to 1 if you have the `bind_textdomain_codeset' function. */ /* Define to 1 if you have the `bind_textdomain_codeset' function. */
#undef HAVE_BIND_TEXTDOMAIN_CODESET #undef HAVE_BIND_TEXTDOMAIN_CODESET
......
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