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.
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)
Like :func:`listdir`, but uses a file descriptor instead and always returns
......@@ -836,6 +856,27 @@ as internal buffering of data.
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))
futimesat(dirfd, path, None)
......@@ -1536,6 +1577,17 @@ Files and Directories
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)
Set the flags of *path* to the numeric *flags*, like :func:`chflags`, but do not
......@@ -1561,6 +1613,15 @@ Files and Directories
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)
Create a hard link pointing to *source* named *link_name*.
......@@ -1585,6 +1646,44 @@ Files and Directories
.. versionchanged:: 3.2
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)
Perform the equivalent of an :c:func:`lstat` system call on the given path.
......@@ -1758,6 +1857,17 @@ Files and Directories
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)
Rename the file or directory *src* to *dst*. If *dst* is a directory,
......@@ -1794,6 +1904,44 @@ Files and Directories
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)
Perform the equivalent of a :c:func:`stat` system call on the given path.
......
......@@ -14,6 +14,8 @@ import shutil
from test import support
import contextlib
import mmap
import platform
import re
import uuid
import asyncore
import asynchat
......@@ -1506,6 +1508,97 @@ class TestSendfile(unittest.TestCase):
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
def test_main():
support.run_unittest(
......@@ -1529,6 +1622,7 @@ def test_main():
LinkTests,
TestSendfile,
ProgramPriorityTests,
ExtendedAttributeTests,
)
if __name__ == "__main__":
......
This diff is collapsed.
......@@ -6090,7 +6090,7 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h
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 \
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 \
......
......@@ -1299,7 +1299,7 @@ dnl AC_MSG_RESULT($cpp_type)
# checks for header files
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 \
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 \
......
......@@ -64,6 +64,9 @@
/* Define if GCC supports __attribute__((format(PyArg_ParseTuple, 2, 3))) */
#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. */
#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