From 9eb1b4ed4e43ca8fcabf1e6fb45a3df47b7f0d2b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 24 Apr 2015 21:04:39 +0200 Subject: fix options The options passed to xattr are internally converted from XATTR_XATTR_* to XATTR_* with the exception of NOFOLLOW which is in most cases handled explicitly in the code. In order to make this work properly, however, the correct constants need to actually be used in python - that means the XATTR_XATTR_* ones must be there rather than the OS's XATTR_* ones. --- xattr/lib.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/xattr/lib.py b/xattr/lib.py index 0f29190..d58c7e9 100644 --- a/xattr/lib.py +++ b/xattr/lib.py @@ -5,10 +5,10 @@ import cffi ffi = cffi.FFI() ffi.cdef(""" -#define XATTR_NOFOLLOW ... -#define XATTR_CREATE ... -#define XATTR_REPLACE ... -#define XATTR_NOSECURITY ... +#define XATTR_XATTR_NOFOLLOW ... +#define XATTR_XATTR_CREATE ... +#define XATTR_XATTR_REPLACE ... +#define XATTR_XATTR_NOSECURITY ... #define XATTR_MAXNAMELEN ... ssize_t xattr_getxattr(const char *, const char *, void *, ssize_t, uint32_t, int); @@ -582,25 +582,23 @@ static ssize_t xattr_flistxattr(int fd, char *namebuf, size_t size, int options) #define xattr_fsetxattr fsetxattr #define xattr_listxattr listxattr #define xattr_flistxattr flistxattr + +/* define these for use in python (see below) */ +#define XATTR_XATTR_NOFOLLOW XATTR_NOFOLLOW +#define XATTR_XATTR_CREATE XATTR_CREATE +#define XATTR_XATTR_REPLACE XATTR_REPLACE +#define XATTR_XATTR_NOSECURITY XATTR_NOSECURITY #endif #ifndef XATTR_MAXNAMELEN #define XATTR_MAXNAMELEN 127 #endif - -#ifndef XATTR_NOFOLLOW -#define XATTR_NOFOLLOW 0x0001 -#endif - -#ifndef XATTR_NOSECURITY -#define XATTR_NOSECURITY 0x0008 -#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_NOFOLLOW = lib.XATTR_XATTR_NOFOLLOW +XATTR_CREATE = lib.XATTR_XATTR_CREATE +XATTR_REPLACE = lib.XATTR_XATTR_REPLACE +XATTR_NOSECURITY = lib.XATTR_XATTR_NOSECURITY XATTR_MAXNAMELEN = lib.XATTR_MAXNAMELEN XATTR_FINDERINFO_NAME = "com.apple.FinderInfo" -- cgit v1.2.1 From 082543d4cd08dc84d9cdbef69fef651b25e33fff Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 24 Apr 2015 21:05:14 +0200 Subject: add pyxattr_compat This adds compatibility with pyxattr version 0.5.3. To use it in code written for pyxattr, import it like from xattr import pyxattr_compat as xattr instead of just "import xattr". It's about 100% slower but passes the pyxattr test suite. --- xattr/pyxattr_compat.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 xattr/pyxattr_compat.py diff --git a/xattr/pyxattr_compat.py b/xattr/pyxattr_compat.py new file mode 100644 index 0000000..0ef913b --- /dev/null +++ b/xattr/pyxattr_compat.py @@ -0,0 +1,110 @@ +""" +pyxattr and xattr have differing API, for example xattr assumes +that (like on OSX) attribute keys are valid UTF-8, while pyxattr +just passes through the raw bytestring. + +This module provides compatibility for the pyxattr API. +""" + +import sys + +from .lib import (XATTR_NOFOLLOW, XATTR_CREATE, XATTR_REPLACE, + XATTR_NOSECURITY, XATTR_MAXNAMELEN, XATTR_FINDERINFO_NAME, + XATTR_RESOURCEFORK_NAME, _getxattr, _fgetxattr, _setxattr, _fsetxattr, + _removexattr, _fremovexattr, _listxattr, _flistxattr) + +__all__ = [ + "NS_SECURITY", "NS_USER", "NS_SYSTEM", "NS_TRUSTED", + "getxattr", "get", "get_all", "setxattr", "set", + "removexattr", "remove", "listxattr", "list" +] + +NS_SECURITY = "security" +NS_USER = "user" +NS_SYSTEM = "system" +NS_TRUSTED = "trusted" + +_NO_NS = object() + +_fsencoding = sys.getfilesystemencoding() + +def _call(item, name_func, fd_func, *args): + if isinstance(item, int): + return fd_func(item, *args) + elif hasattr(item, 'fileno'): + return fd_func(item.fileno(), *args) + elif isinstance(item, str): + return name_func(item, *args) + elif isinstance(item, unicode): + item = item.encode(_fsencoding) + return name_func(item, *args) + else: + raise TypeError("argument must be string, int or file object") + +def _add_ns(item, ns): + if ns is None: + raise TypeError("namespace must not be None") + if ns == _NO_NS: + return item + return "%s.%s" % (ns, item) + +def getxattr(item, attribute, nofollow=False): + options = nofollow and XATTR_NOFOLLOW or 0 + return _call(item, _getxattr, _fgetxattr, attribute, 0, 0, options) + +def get(item, name, nofollow=False, namespace=_NO_NS): + name = _add_ns(name, namespace) + return getxattr(item, name, nofollow=nofollow) + +def get_all(item, nofollow=False, namespace=_NO_NS): + if namespace is not None and namespace != _NO_NS: + namespace = '%s.' % namespace + l = listxattr(item, nofollow=nofollow) + result = [] + for name in l: + try: + if namespace is not None and namespace != _NO_NS: + if not name.startswith(namespace): + continue + result.append((name[len(namespace):], + getxattr(item, name, nofollow=nofollow))) + else: + result.append((name, getxattr(item, name, nofollow=nofollow))) + except IOError: + pass + return result + +def setxattr(item, name, value, flags=0, nofollow=False): + options = nofollow and XATTR_NOFOLLOW or 0 + options |= flags + return _call(item, _setxattr, _fsetxattr, name, value, 0, options) + +def set(item, name, value, nofollow=False, flags=0, namespace=_NO_NS): + name = _add_ns(name, namespace) + return setxattr(item, name, value, flags=flags, nofollow=nofollow) + +def removexattr(item, name, nofollow=False): + options = nofollow and XATTR_NOFOLLOW or 0 + return _call(item, _removexattr, _fremovexattr, name, options) + +def remove(item, name, nofollow=False, namespace=_NO_NS): + name = _add_ns(name, namespace) + return removexattr(item, name, nofollow=nofollow) + +def listxattr(item, nofollow=False): + options = nofollow and XATTR_NOFOLLOW or 0 + res = _call(item, _listxattr, _flistxattr, options).split(b'\x00') + res.pop() + return res + +def list(item, nofollow=False, namespace=_NO_NS): + if not namespace or namespace == _NO_NS: + return listxattr(item, nofollow=nofollow) + namespace = "%s." % namespace + l = listxattr(item, nofollow=nofollow) + result = [] + for name in l: + if not name.startswith(namespace): + continue + result.append(name[len(namespace):]) + return result -- cgit v1.2.1 From 7b40e00f1ab859f13b349c40d97f6526a6110a0c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 25 Apr 2015 11:37:27 +0200 Subject: fix nofollow in Linux xattr_flistxattr() The nofollow variable is set, but not used, while the flag is removed from the 'options' and then tested. Fix that by using the nofollow variable instead of the useless test. --- xattr/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xattr/lib.py b/xattr/lib.py index d58c7e9..d9e973a 100644 --- a/xattr/lib.py +++ b/xattr/lib.py @@ -543,7 +543,7 @@ static ssize_t xattr_fsetxattr(int fd, const char *name, void *value, ssize_t si } else if (options != 0) { return -1; } - if (options & XATTR_XATTR_NOFOLLOW) { + if (nofollow) { return -1; } else { return fsetxattr(fd, name, value, size, options); -- cgit v1.2.1