summaryrefslogtreecommitdiff
path: root/xattr/lib.py
diff options
context:
space:
mode:
Diffstat (limited to 'xattr/lib.py')
-rw-r--r--xattr/lib.py713
1 files changed, 713 insertions, 0 deletions
diff --git a/xattr/lib.py b/xattr/lib.py
new file mode 100644
index 0000000..b72a1ff
--- /dev/null
+++ b/xattr/lib.py
@@ -0,0 +1,713 @@
+import os
+import sys
+
+import cffi
+
+ffi = cffi.FFI()
+ffi.cdef("""
+#define XATTR_NOFOLLOW ...
+#define XATTR_CREATE ...
+#define XATTR_REPLACE ...
+#define XATTR_NOSECURITY ...
+#define XATTR_MAXNAMELEN ...
+
+ssize_t xattr_getxattr(const char *, const char *, void *, ssize_t, uint32_t, int);
+ssize_t xattr_fgetxattr(int, const char *, void *, ssize_t, uint32_t, int);
+
+ssize_t xattr_setxattr(const char *, const char *, void *, ssize_t, uint32_t, int);
+ssize_t xattr_fsetxattr(int, const char *, void *, ssize_t, uint32_t, int);
+
+ssize_t xattr_removexattr(const char *, const char *, int);
+ssize_t xattr_fremovexattr(int, const char *, int);
+
+ssize_t xattr_listxattr(const char *, char *, size_t, int);
+ssize_t xattr_flistxattr(int, char *, size_t, int);
+
+""")
+
+lib = ffi.verify("""
+#include "Python.h"
+#ifdef __FreeBSD__
+#include <sys/extattr.h>
+#elif defined(__SUN__) || defined(__sun__) || defined(sun)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#else
+#include <sys/xattr.h>
+#endif
+
+#ifdef __FreeBSD__
+
+/* FreeBSD compatibility API */
+#define XATTR_XATTR_NOFOLLOW 0x0001
+#define XATTR_XATTR_CREATE 0x0002
+#define XATTR_XATTR_REPLACE 0x0004
+#define XATTR_XATTR_NOSECURITY 0x0008
+
+
+/* Converts a freebsd format attribute list into a NULL terminated list.
+ * While the man page on extattr_list_file says it is NULL terminated,
+ * it is actually the first byte that is the length of the
+ * following attribute.
+ */
+static void convert_bsd_list(char *namebuf, size_t size)
+{
+ size_t offset = 0;
+ while(offset < size) {
+ int length = (int) namebuf[offset];
+ memmove(namebuf+offset, namebuf+offset+1, length);
+ namebuf[offset+length] = '\0';
+ offset += length+1;
+ }
+}
+
+static ssize_t xattr_getxattr(const char *path, const char *name,
+ void *value, ssize_t size, u_int32_t position,
+ int options)
+{
+ if (position != 0 ||
+ !(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return extattr_get_link(path, EXTATTR_NAMESPACE_USER,
+ name, value, size);
+ }
+ else {
+ return extattr_get_file(path, EXTATTR_NAMESPACE_USER,
+ name, value, size);
+ }
+}
+
+static ssize_t xattr_setxattr(const char *path, const char *name,
+ void *value, ssize_t size, u_int32_t position,
+ int options)
+{
+ int rv = 0;
+ int nofollow;
+
+ if (position != 0) {
+ return -1;
+ }
+
+ nofollow = options & XATTR_XATTR_NOFOLLOW;
+ options &= ~XATTR_XATTR_NOFOLLOW;
+
+ if (options == XATTR_XATTR_CREATE ||
+ options == XATTR_XATTR_REPLACE) {
+
+ /* meh. FreeBSD doesn't really have this in it's
+ * API... Oh well.
+ */
+ }
+ else if (options != 0) {
+ return -1;
+ }
+
+ if (nofollow) {
+ rv = extattr_set_link(path, EXTATTR_NAMESPACE_USER,
+ name, value, size);
+ }
+ else {
+ rv = extattr_set_file(path, EXTATTR_NAMESPACE_USER,
+ name, value, size);
+ }
+
+ /* freebsd returns the written length on success, not zero. */
+ if (rv >= 0) {
+ return 0;
+ }
+ else {
+ return rv;
+ }
+}
+
+static ssize_t xattr_removexattr(const char *path, const char *name,
+ int options)
+{
+ if (!(options == 0 ||
+ options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
+ }
+ else {
+ return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name);
+ }
+}
+
+
+static ssize_t xattr_listxattr(const char *path, char *namebuf,
+ size_t size, int options)
+{
+ ssize_t rv = 0;
+ if (!(options == 0 ||
+ options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ rv = extattr_list_link(path, EXTATTR_NAMESPACE_USER, namebuf, size);
+ }
+ else {
+ rv = extattr_list_file(path, EXTATTR_NAMESPACE_USER, namebuf, size);
+ }
+
+ if (rv > 0 && namebuf) {
+ convert_bsd_list(namebuf, rv);
+ }
+
+ return rv;
+}
+
+static ssize_t xattr_fgetxattr(int fd, const char *name, void *value,
+ ssize_t size, u_int32_t position, int options)
+{
+ if (position != 0 ||
+ !(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return -1;
+ }
+ else {
+ return extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, name, value, size);
+ }
+}
+
+static ssize_t xattr_fsetxattr(int fd, const char *name, void *value,
+ ssize_t size, u_int32_t position, int options)
+{
+ int rv = 0;
+ int nofollow;
+
+ if (position != 0) {
+ return -1;
+ }
+
+ nofollow = options & XATTR_XATTR_NOFOLLOW;
+ options &= ~XATTR_XATTR_NOFOLLOW;
+
+ if (options == XATTR_XATTR_CREATE ||
+ options == XATTR_XATTR_REPLACE) {
+ /* freebsd noop */
+ }
+ else if (options != 0) {
+ return -1;
+ }
+
+ if (nofollow) {
+ return -1;
+ }
+ else {
+ rv = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER,
+ name, value, size);
+ }
+
+ /* freebsd returns the written length on success, not zero. */
+ if (rv >= 0) {
+ return 0;
+ }
+ else {
+ return rv;
+ }
+}
+
+static ssize_t xattr_fremovexattr(int fd, const char *name, int options)
+{
+
+ if (!(options == 0 ||
+ options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return -1;
+ }
+ else {
+ return extattr_delete_fd(fd, EXTATTR_NAMESPACE_USER, name);
+ }
+}
+
+
+static ssize_t xattr_flistxattr(int fd, char *namebuf, size_t size, int options)
+{
+ ssize_t rv = 0;
+
+ if (!(options == 0 ||
+ options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return -1;
+ }
+ else {
+ rv = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, namebuf, size);
+ }
+
+ if (rv > 0 && namebuf) {
+ convert_bsd_list(namebuf, rv);
+ }
+
+ return rv;
+}
+
+#elif defined(__SUN__) || defined(__sun__) || defined(sun)
+
+/* Solaris 9 and later compatibility API */
+#define XATTR_XATTR_NOFOLLOW 0x0001
+#define XATTR_XATTR_CREATE 0x0002
+#define XATTR_XATTR_REPLACE 0x0004
+#define XATTR_XATTR_NOSECURITY 0x0008
+
+#ifndef u_int32_t
+#define u_int32_t uint32_t
+#endif
+
+static ssize_t xattr_fgetxattr(int fd, const char *name, void *value,
+ ssize_t size, u_int32_t position, int options)
+{
+ int xfd;
+ ssize_t bytes;
+ struct stat statbuf;
+
+ /* XXX should check that name does not have / characters in it */
+ xfd = openat(fd, name, O_RDONLY | O_XATTR);
+ if (xfd == -1) {
+ return -1;
+ }
+ if (lseek(xfd, position, SEEK_SET) == -1) {
+ close(xfd);
+ return -1;
+ }
+ if (value == NULL) {
+ if (fstat(xfd, &statbuf) == -1) {
+ close(xfd);
+ return -1;
+ }
+ close(xfd);
+ return statbuf.st_size;
+ }
+ /* XXX should keep reading until the buffer is exhausted or EOF */
+ bytes = read(xfd, value, size);
+ close(xfd);
+ return bytes;
+}
+
+static ssize_t xattr_getxattr(const char *path, const char *name,
+ void *value, ssize_t size, u_int32_t position,
+ int options)
+{
+ int fd;
+ ssize_t bytes;
+
+ if (position != 0 ||
+ !(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+
+ fd = open(path,
+ O_RDONLY |
+ ((options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0));
+ if (fd == -1) {
+ return -1;
+ }
+ bytes = xattr_fgetxattr(fd, name, value, size, position, options);
+ close(fd);
+ return bytes;
+}
+
+static ssize_t xattr_fsetxattr(int fd, const char *name, void *value,
+ ssize_t size, u_int32_t position, int options)
+{
+ int xfd;
+ ssize_t bytes = 0;
+
+ /* XXX should check that name does not have / characters in it */
+ xfd = openat(fd, name, O_XATTR | O_TRUNC |
+ ((options & XATTR_XATTR_CREATE) ? O_EXCL : 0) |
+ ((options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0) |
+ ((options & XATTR_XATTR_REPLACE) ? O_RDWR : O_WRONLY|O_CREAT),
+ 0644);
+ if (xfd == -1) {
+ return -1;
+ }
+ while (size > 0) {
+ bytes = write(xfd, value, size);
+ if (bytes == -1) {
+ close(xfd);
+ return -1;
+ }
+ size -= bytes;
+ value += bytes;
+ }
+ close(xfd);
+ return 0;
+}
+
+static ssize_t xattr_setxattr(const char *path, const char *name,
+ void *value, ssize_t size, u_int32_t position,
+ int options)
+{
+ int fd;
+ ssize_t bytes;
+
+ if (position != 0) {
+ return -1;
+ }
+
+ fd = open(path,
+ O_RDONLY | (options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0);
+ if (fd == -1) {
+ return -1;
+ }
+ bytes = xattr_fsetxattr(fd, name, value, size, position, options);
+ close(fd);
+ return bytes;
+}
+
+static ssize_t xattr_fremovexattr(int fd, const char *name, int options)
+{
+ int xfd, status;
+ /* XXX should check that name does not have / characters in it */
+ if (!(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return -1;
+ }
+ xfd = openat(fd, ".", O_XATTR, 0644);
+ if (xfd == -1) {
+ return -1;
+ }
+ status = unlinkat(xfd, name, 0);
+ close(xfd);
+ return status;
+}
+
+static ssize_t xattr_removexattr(const char *path, const char *name,
+ int options)
+{
+ int fd;
+ ssize_t status;
+
+ fd = open(path,
+ O_RDONLY | ((options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0));
+ if (fd == -1) {
+ return -1;
+ }
+ status = xattr_fremovexattr(fd, name, options);
+ close(fd);
+ return status;
+}
+
+static ssize_t xattr_xflistxattr(int xfd, char *namebuf, size_t size, int options)
+{
+ int esize;
+ DIR *dirp;
+ struct dirent *entry;
+ ssize_t nsize = 0;
+
+ dirp = fdopendir(xfd);
+ if (dirp == NULL) {
+ return (-1);
+ }
+ while (entry = readdir(dirp)) {
+ if (strcmp(entry->d_name, ".") == 0 ||
+ strcmp(entry->d_name, "..") == 0)
+ continue;
+ esize = strlen(entry->d_name);
+ if (nsize + esize + 1 <= size) {
+ snprintf((char *)(namebuf + nsize), esize + 1,
+ entry->d_name);
+ }
+ nsize += esize + 1; /* +1 for \0 */
+ }
+ closedir(dirp);
+ return nsize;
+}
+static ssize_t xattr_flistxattr(int fd, char *namebuf, size_t size, int options)
+{
+ int xfd;
+
+ xfd = openat(fd, ".", O_RDONLY);
+ return xattr_xflistxattr(xfd, namebuf, size, options);
+}
+
+static ssize_t xattr_listxattr(const char *path, char *namebuf,
+ size_t size, int options)
+{
+ int xfd;
+
+ xfd = attropen(path, ".", O_RDONLY);
+ return xattr_xflistxattr(xfd, namebuf, size, options);
+}
+
+#elif !defined(XATTR_NOFOLLOW)
+/* Linux compatibility API */
+#define XATTR_XATTR_NOFOLLOW 0x0001
+#define XATTR_XATTR_CREATE 0x0002
+#define XATTR_XATTR_REPLACE 0x0004
+#define XATTR_XATTR_NOSECURITY 0x0008
+static ssize_t xattr_getxattr(const char *path, const char *name, void *value, ssize_t size, u_int32_t position, int options) {
+ if (position != 0 || !(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return lgetxattr(path, name, value, size);
+ } else {
+ return getxattr(path, name, value, size);
+ }
+}
+
+static ssize_t xattr_setxattr(const char *path, const char *name, void *value, ssize_t size, u_int32_t position, int options) {
+ int nofollow;
+ if (position != 0) {
+ return -1;
+ }
+ nofollow = options & XATTR_XATTR_NOFOLLOW;
+ options &= ~XATTR_XATTR_NOFOLLOW;
+ if (options == XATTR_XATTR_CREATE) {
+ options = XATTR_CREATE;
+ } else if (options == XATTR_XATTR_REPLACE) {
+ options = XATTR_REPLACE;
+ } else if (options != 0) {
+ return -1;
+ }
+ if (nofollow) {
+ return lsetxattr(path, name, value, size, options);
+ } else {
+ return setxattr(path, name, value, size, options);
+ }
+}
+
+static ssize_t xattr_removexattr(const char *path, const char *name, int options) {
+ if (!(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return lremovexattr(path, name);
+ } else {
+ return removexattr(path, name);
+ }
+}
+
+
+static ssize_t xattr_listxattr(const char *path, char *namebuf, size_t size, int options) {
+ if (!(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return llistxattr(path, namebuf, size);
+ } else {
+ return listxattr(path, namebuf, size);
+ }
+}
+
+static ssize_t xattr_fgetxattr(int fd, const char *name, void *value, ssize_t size, u_int32_t position, int options) {
+ if (position != 0 || !(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return -1;
+ } else {
+ return fgetxattr(fd, name, value, size);
+ }
+}
+
+static ssize_t xattr_fsetxattr(int fd, const char *name, void *value, ssize_t size, u_int32_t position, int options) {
+ int nofollow;
+ if (position != 0) {
+ return -1;
+ }
+ nofollow = options & XATTR_XATTR_NOFOLLOW;
+ options &= ~XATTR_XATTR_NOFOLLOW;
+ if (options == XATTR_XATTR_CREATE) {
+ options = XATTR_CREATE;
+ } else if (options == XATTR_XATTR_REPLACE) {
+ options = XATTR_REPLACE;
+ } else if (options != 0) {
+ return -1;
+ }
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return -1;
+ } else {
+ return fsetxattr(fd, name, value, size, options);
+ }
+}
+
+static ssize_t xattr_fremovexattr(int fd, const char *name, int options) {
+ if (!(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return -1;
+ } else {
+ return fremovexattr(fd, name);
+ }
+}
+
+
+static ssize_t xattr_flistxattr(int fd, char *namebuf, size_t size, int options) {
+ if (!(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
+ return -1;
+ }
+ if (options & XATTR_XATTR_NOFOLLOW) {
+ return -1;
+ } else {
+ return flistxattr(fd, namebuf, size);
+ }
+}
+
+#else /* Mac OS X assumed */
+#define xattr_getxattr getxattr
+#define xattr_fgetxattr fgetxattr
+#define xattr_removexattr removexattr
+#define xattr_fremovexattr fremovexattr
+#define xattr_setxattr setxattr
+#define xattr_fsetxattr fsetxattr
+#define xattr_listxattr listxattr
+#define xattr_flistxattr flistxattr
+#endif
+""", ext_package='xattr')
+
+XATTR_NOFOLLOW = lib.XATTR_NOFOLLOW
+XATTR_CREATE = lib.XATTR_CREATE
+XATTR_REPLACE = lib.XATTR_REPLACE
+XATTR_NOSECURITY = lib.XATTR_NOSECURITY
+XATTR_MAXNAMELEN = lib.XATTR_MAXNAMELEN
+
+XATTR_FINDERINFO_NAME = "com.apple.FinderInfo"
+XATTR_RESOURCEFORK_NAME = "com.apple.ResourceFork"
+
+
+def fs_encode(val):
+ if isinstance(val, bytes):
+ return val.encode(sys.getfilesystemencoding())
+ else:
+ return val
+
+
+def error(path=None):
+ errno = ffi.errno
+ strerror = os.strerror(ffi.errno)
+ if path:
+ raise IOError(errno, strerror, path)
+ else:
+ raise IOError(errno, strerror)
+
+
+def _getxattr(path, name, size=0, position=0, options=0):
+ """
+ getxattr(path, name, size=0, position=0, options=0) -> str
+ """
+ path = fs_encode(path)
+ name = fs_encode(name)
+ if size == 0:
+ res = lib.xattr_getxattr(path, name, ffi.NULL, 0, position, options)
+ if res == -1:
+ raise error(path)
+ size = res
+ buf = ffi.new("char[]", size)
+ res = lib.xattr_getxattr(path, name, buf, size, position, options)
+ if res == -1:
+ raise error(path)
+ return ffi.buffer(buf)[:res]
+
+
+def _fgetxattr(fd, name, size=0, position=0, options=0):
+ """
+ fgetxattr(fd, name, size=0, position=0, options=0) -> str
+ """
+ name = fs_encode(name)
+ if size == 0:
+ res = lib.xattr_fgetxattr(fd, name, ffi.NULL, 0, position, options)
+ if res == -1:
+ raise error()
+ size = res
+ buf = ffi.new("char[]", size)
+ res = lib.xattr_fgetxattr(fd, name, buf, size, position, options)
+ if res == -1:
+ raise error()
+ return ffi.buffer(buf)[:res]
+
+
+def _setxattr(path, name, value, position=0, options=0):
+ """
+ setxattr(path, name, value, position=0, options=0) -> None
+ """
+ path = fs_encode(path)
+ name = fs_encode(name)
+ res = lib.xattr_setxattr(path, name, value, len(value), position, options)
+ if res:
+ raise error(path)
+
+
+def _fsetxattr(fd, name, value, position=0, options=0):
+ """
+ fsetxattr(fd, name, value, position=0, options=0) -> None
+ """
+ name = fs_encode(name)
+ res = lib.xattr_fsetxattr(fd, name, value, len(value), position, options)
+ if res:
+ raise error()
+
+
+def _removexattr(path, name, options=0):
+ """
+ removexattr(path, name, options=0) -> None
+ """
+ path = fs_encode(path)
+ name = fs_encode(name)
+ res = lib.xattr_removexattr(path, name, options)
+ if res:
+ raise error(path)
+
+
+def _fremovexattr(fd, name, options=0):
+ """
+ fremovexattr(fd, name, options=0) -> None
+ """
+ name = fs_encode(name)
+ res = lib.xattr_fremovexattr(fd, name, options)
+ if res:
+ raise error()
+
+
+def _listxattr(path, options=0):
+ """
+ listxattr(path, options=0) -> str
+ """
+ path = fs_encode(path)
+ res = lib.xattr_listxattr(path, ffi.NULL, 0, options)
+ if res == -1:
+ raise error(path)
+ elif res == 0:
+ return b""
+ buf = ffi.new("char[]", res)
+ res = lib.xattr_listxattr(path, buf, res, options)
+ if res == -1:
+ raise error(path)
+ return ffi.buffer(buf)[:res]
+
+
+def _flistxattr(fd, options=0):
+ """
+ flistxattr(fd, options=0) -> str
+ """
+ res = lib.xattr_flistxattr(fd, ffi.NULL, 0, options)
+ if res == 1:
+ raise error()
+ buf = ffi.new("char[]", res)
+ res = lib.xattr_flistxattr(fd, buf, res, options)
+ if res == -1:
+ raise error()
+ return ffi.buffer(buf)[:res]