pax_global_header00006660000000000000000000000064125430346260014517gustar00rootroot0000000000000052 comment=02f54b688faf496033303f00433a37850100c4b5 xattr-0.7.8/000077500000000000000000000000001254303462600126755ustar00rootroot00000000000000xattr-0.7.8/.gitignore000066400000000000000000000001001254303462600146540ustar00rootroot00000000000000/xattr.egg-info /build /dist /*.egg *.pyc *.so .\#* __pycache__ xattr-0.7.8/.travis.yml000066400000000000000000000003031254303462600150020ustar00rootroot00000000000000language: objective-c script: - ARCHFLAGS="-Wno-error=unused-command-line-argument-hard-error-in-future" python setup.py build_ext -i - python -m compileall -f xattr - python setup.py test xattr-0.7.8/CHANGES.txt000066400000000000000000000027011254303462600145060ustar00rootroot00000000000000Version 0.7.8 released 2015-06-25 * Added MANIFEST.in to ensure that the .txt files are included https://github.com/xattr/xattr/issues/40 Version 0.7.7 released 2015-06-19 * Fixed FreeBSD build https://github.com/xattr/xattr/pull/32 Version 0.7.6 released 2014-03-27 * Fixed Solaris & Solaris Studio support and Python 2.6 compatibility https://github.com/xattr/xattr/pull/29 Version 0.7.5 released 2014-03-23 * Improved error message when str/unicode is passed where bytes is required https://github.com/xattr/xattr/pull/24 Version 0.7.4 released 2014-03-03 * Improved Python 3 compatibility https://github.com/xattr/xattr/pull/22 Version 0.7.3 released 2014-01-06 * Added some unicode-specific tests Version 0.7.2 released 2013-07-22 * Added Python 3 support. https://github.com/xattr/xattr/commit/14795a47b1dc4bb994faf520888c51c5886b8187 Version 0.7.1 released 2013-07-19 * Fixed compilation on some platforms https://github.com/xattr/xattr/issues/12 Version 0.7.0 released 2013-07-19 * Rewritten to use cffi https://github.com/xattr/xattr/pull/11 Version 0.6.4 released 2012-02-01 * Updated README.txt to match setup.py description https://github.com/xattr/xattr/issues/5 * Bug fixes for Solaris port https://github.com/xattr/xattr/pull/2 Version 0.6.3 released 2012-01-23 * Fix tests for Linux, allow xattr on symlinks https://github.com/xattr/xattr/pull/4 Version 0.6.2 released 2011-08-17 * Bug fix in Solaris support xattr-0.7.8/INSTALLING.txt000066400000000000000000000001401254303462600150750ustar00rootroot00000000000000Installing ========== From the xattr source directory, type: sudo python setup.py install xattr-0.7.8/LICENSE.txt000066400000000000000000000022241254303462600145200ustar00rootroot00000000000000This is the MIT license. This software may also be distributed under the same terms as Python (the PSF license). Copyright (c) 2004 Bob Ippolito. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. xattr-0.7.8/MANIFEST.in000066400000000000000000000000571254303462600144350ustar00rootroot00000000000000include *.py include *.txt include MANIFEST.in xattr-0.7.8/README.txt000066400000000000000000000006441254303462600143770ustar00rootroot00000000000000xattr is a Python wrapper for extended filesystem attributes. Extended attributes extend the basic attributes of files and directories in the file system. They are stored as name:data pairs associated with file system objects (files, directories, symlinks, etc). Extended attributes are currently only available on Darwin 8.0+ (Mac OS X 10.4) and Linux 2.6+. Experimental support is included for Solaris and FreeBSD. xattr-0.7.8/TODO.txt000066400000000000000000000000371254303462600142030ustar00rootroot00000000000000* Write tests * Write examples xattr-0.7.8/requirements.txt000066400000000000000000000000121254303462600161520ustar00rootroot00000000000000cffi>=0.4 xattr-0.7.8/setup.py000066400000000000000000000040441254303462600144110ustar00rootroot00000000000000#!/usr/bin/env python import os import sys from setuptools import setup from distutils.command.build import build class cffi_build(build): """This is a shameful hack to ensure that cffi is present when we specify ext_modules. We can't do this eagerly because setup_requires hasn't run yet. """ def finalize_options(self): from xattr.lib import ffi self.distribution.ext_modules = [ffi.verifier.get_extension()] build.finalize_options(self) VERSION = '0.7.8' DESCRIPTION = "Python wrapper for extended filesystem attributes" LONG_DESCRIPTION = """ Extended attributes extend the basic attributes of files and directories in the file system. They are stored as name:data pairs associated with file system objects (files, directories, symlinks, etc). Extended attributes are currently only available on Darwin 8.0+ (Mac OS X 10.4) and Linux 2.6+. Experimental support is included for Solaris and FreeBSD. """ CLASSIFIERS = filter(bool, map(str.strip, """ Environment :: Console Intended Audience :: Developers License :: OSI Approved :: MIT License Natural Language :: English Operating System :: MacOS :: MacOS X Operating System :: POSIX :: Linux Operating System :: POSIX :: BSD :: FreeBSD Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 3 Topic :: Software Development :: Libraries :: Python Modules """.splitlines())) setup( name="xattr", version=VERSION, description=DESCRIPTION, long_description=LONG_DESCRIPTION, classifiers=CLASSIFIERS, author="Bob Ippolito", author_email="bob@redivi.com", url="http://github.com/xattr/xattr", license="MIT License", packages=['xattr'], ext_package='xattr', platforms=['MacOS X', 'Linux', 'FreeBSD', 'Solaris'], entry_points={ 'console_scripts': [ "xattr = xattr.tool:main", ], }, install_requires=["cffi>=0.4"], setup_requires=["cffi>=0.4"], test_suite="xattr.tests.all_tests_suite", zip_safe=False, cmdclass={'build': cffi_build}, ) xattr-0.7.8/xattr/000077500000000000000000000000001254303462600140375ustar00rootroot00000000000000xattr-0.7.8/xattr/__init__.py000066400000000000000000000123141254303462600161510ustar00rootroot00000000000000""" Extended attributes extend the basic attributes of files and directories in the file system. They are stored as name:data pairs associated with file system objects (files, directories, symlinks, etc). The xattr type wraps a path or file descriptor with a dict-like interface that exposes these extended attributes. """ __version__ = '0.7.8' 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__ = [ "XATTR_NOFOLLOW", "XATTR_CREATE", "XATTR_REPLACE", "XATTR_NOSECURITY", "XATTR_MAXNAMELEN", "XATTR_FINDERINFO_NAME", "XATTR_RESOURCEFORK_NAME", "xattr", "listxattr", "getxattr", "setxattr", "removexattr" ] class xattr(object): """ A wrapper for paths or file descriptors to access their extended attributes with a dict-like interface """ def __init__(self, obj, options=0): """ obj should be a path, a file descriptor, or an object that implements fileno() and returns a file descriptor. options should be 0 or XATTR_NOFOLLOW. If set, it will be OR'ed with the options passed to getxattr, setxattr, etc. """ self.obj = obj self.options = options fileno = getattr(obj, 'fileno', None) if fileno is not None: self.value = fileno() else: self.value = obj def __repr__(self): if isinstance(self.value, int): flavor = "fd" else: flavor = "file" return "<%s %s=%r>" % (type(self).__name__, flavor, self.value) def _call(self, name_func, fd_func, *args): if isinstance(self.value, int): return fd_func(self.value, *args) else: return name_func(self.value, *args) def get(self, name, options=0): """ Retrieve the extended attribute ``name`` as a ``str``. Raises ``IOError`` on failure. See x-man-page://2/getxattr for options and possible errors. """ return self._call(_getxattr, _fgetxattr, name, 0, 0, options | self.options) def set(self, name, value, options=0): """ Set the extended attribute ``name`` to ``value`` Raises ``IOError`` on failure. See x-man-page://2/setxattr for options and possible errors. """ return self._call(_setxattr, _fsetxattr, name, value, 0, options | self.options) def remove(self, name, options=0): """ Remove the extended attribute ``name`` Raises ``IOError`` on failure. See x-man-page://2/removexattr for options and possible errors. """ return self._call(_removexattr, _fremovexattr, name, options | self.options) self._remove(name, options | self.options) def list(self, options=0): """ Retrieves the extended attributes currently set as a list of strings. Raises ``IOError`` on failure. See x-man-page://2/listxattr for options and possible errors. """ res = self._call(_listxattr, _flistxattr, options | self.options).split(b'\x00') res.pop() return [s.decode('utf-8') for s in res] # dict-like methods def __len__(self): return len(self.list()) def __delitem__(self, item): try: self.remove(item) except IOError: raise KeyError(item) def __setitem__(self, item, value): self.set(item, value) def __getitem__(self, item): try: return self.get(item) except IOError: raise KeyError(item) def iterkeys(self): return iter(self.list()) __iter__ = iterkeys def has_key(self, item): try: self.get(item) except IOError: return False else: return True __contains__ = has_key def clear(self): for k in self.keys(): del self[k] def update(self, seq): if not hasattr(seq, 'iteritems'): seq = dict(seq) for k, v in seq.iteritems(): self[k] = v def copy(self): return dict(self.iteritems()) def setdefault(self, k, d=''): try: d = self.get(k) except IOError: self[k] = d return d def keys(self): return self.list() def itervalues(self): for k, v in self.iteritems(): yield v def values(self): return list(self.itervalues()) def iteritems(self): for k in self.list(): yield k, self.get(k) def items(self): return list(self.iteritems()) def listxattr(f, symlink=False): return tuple(xattr(f).list(options=symlink and XATTR_NOFOLLOW or 0)) def getxattr(f, attr, symlink=False): return xattr(f).get(attr, options=symlink and XATTR_NOFOLLOW or 0) def setxattr(f, attr, value, options=0, symlink=False): if symlink: options |= XATTR_NOFOLLOW return xattr(f).set(attr, value, options=options) def removexattr(f, attr, symlink=False): options = symlink and XATTR_NOFOLLOW or 0 return xattr(f).remove(attr, options=options) xattr-0.7.8/xattr/lib.py000066400000000000000000000452161254303462600151670ustar00rootroot00000000000000import 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 #elif defined(__SUN__) || defined(__sun__) || defined(__sun) #include #include #include #include #include #include #else #include #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 #define XATTR_CREATE 0x1 #define XATTR_REPLACE 0x2 /* 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 its * 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 #define XATTR_CREATE 0x1 #define XATTR_REPLACE 0x2 #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 | O_XATTR); 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 #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_MAXNAMELEN = lib.XATTR_MAXNAMELEN XATTR_FINDERINFO_NAME = "com.apple.FinderInfo" XATTR_RESOURCEFORK_NAME = "com.apple.ResourceFork" def fs_encode(val): if not isinstance(val, bytes): return val.encode(sys.getfilesystemencoding()) else: return val def _check_bytes(val): if not isinstance(val, bytes): raise TypeError( "Value must be bytes, %s was passed." % type(val).__name__ ) 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 """ _check_bytes(value) 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 """ _check_bytes(value) 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] xattr-0.7.8/xattr/tests/000077500000000000000000000000001254303462600152015ustar00rootroot00000000000000xattr-0.7.8/xattr/tests/__init__.py000066400000000000000000000006451254303462600173170ustar00rootroot00000000000000import os import sys import unittest def all_tests_suite(): suite = unittest.TestLoader().loadTestsFromNames([ 'xattr.tests.test_xattr', ]) return suite def main(): runner = unittest.TextTestRunner() suite = all_tests_suite() runner.run(suite) if __name__ == '__main__': sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) main() xattr-0.7.8/xattr/tests/test_xattr.py000066400000000000000000000057761254303462600177730ustar00rootroot00000000000000import os import sys from unittest import TestCase from tempfile import mkdtemp, NamedTemporaryFile import xattr class BaseTestXattr(object): def test_attr(self): x = xattr.xattr(self.tempfile) # Solaris 11 and forward contain system attributes (file flags) in # extended attributes present on all files, so cons them up into a # comparison dict. d = {} if sys.platform == 'sunos5' and 'SUNWattr_ro' in x: d['SUNWattr_ro'] = x['SUNWattr_ro'] d['SUNWattr_rw'] = x['SUNWattr_rw'] self.assertEqual(list(x.keys()), list(d.keys())) self.assertEqual(list(x.list()), list(d.keys())) self.assertEqual(dict(x), d) x['user.sopal'] = b'foo' x['user.sop.foo'] = b'bar' x[u'user.\N{SNOWMAN}'] = b'not a snowman' del x x = xattr.xattr(self.tempfile) attrs = set(x.list()) self.assertTrue('user.sopal' in x) self.assertTrue(u'user.sopal' in attrs) self.assertEqual(x['user.sopal'], b'foo') self.assertTrue('user.sop.foo' in x) self.assertTrue(u'user.sop.foo' in attrs) self.assertEqual(x['user.sop.foo'], b'bar') self.assertTrue(u'user.\N{SNOWMAN}' in x) self.assertTrue(u'user.\N{SNOWMAN}' in attrs) self.assertEqual(x[u'user.\N{SNOWMAN}'], b'not a snowman') del x[u'user.\N{SNOWMAN}'] del x['user.sop.foo'] del x x = xattr.xattr(self.tempfile) self.assertTrue('user.sop.foo' not in x) def test_setxattr_unicode_error(self): x = xattr.xattr(self.tempfile) def assign(): x['abc'] = u'abc' self.assertRaises(TypeError, assign) if sys.version_info[0] >= 3: msg = "Value must be bytes, str was passed." else: msg = "Value must be bytes, unicode was passed." try: assign() except TypeError: e = sys.exc_info()[1] self.assertEqual(str(e), msg) def test_symlink_attrs(self): # Solaris doesn't support extended attributes on symlinks if sys.platform == 'sunos5': return symlinkPath = self.tempfilename + '.link' os.symlink(self.tempfilename, symlinkPath) try: symlink = xattr.xattr(symlinkPath, options=xattr.XATTR_NOFOLLOW) realfile = xattr.xattr(self.tempfilename) symlink['user.islink'] = b'true' self.assertEqual(dict(realfile), {}) self.assertEqual(symlink['user.islink'], b'true') finally: os.remove(symlinkPath) class TestFile(TestCase, BaseTestXattr): def setUp(self): self.tempfile = NamedTemporaryFile() self.tempfilename = self.tempfile.name def tearDown(self): self.tempfile.close() class TestDir(TestCase, BaseTestXattr): def setUp(self): self.tempfile = mkdtemp() self.tempfilename = self.tempfile def tearDown(self): os.rmdir(self.tempfile) xattr-0.7.8/xattr/tool.py000077500000000000000000000145451254303462600154020ustar00rootroot00000000000000#!/usr/bin/env python ## # Copyright (c) 2007 Apple Inc. # # This is the MIT license. This software may also be distributed under the # same terms as Python (the PSF license). # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. ## from __future__ import print_function import sys import os import getopt import zlib import xattr def usage(e=None): if e: print(e) print("") name = os.path.basename(sys.argv[0]) print("usage: %s [-lz] file [file ...]" % (name,)) print(" %s -p [-lz] attr_name file [file ...]" % (name,)) print(" %s -w [-z] attr_name attr_value file [file ...]" % (name,)) print(" %s -d attr_name file [file ...]" % (name,)) print("") print("The first form lists the names of all xattrs on the given file(s).") print("The second form (-p) prints the value of the xattr attr_name.") print("The third form (-w) sets the value of the xattr attr_name to attr_value.") print("The fourth form (-d) deletes the xattr attr_name.") print("") print("options:") print(" -h: print this help") print(" -l: print long format (attr_name: attr_value)") print(" -z: compress or decompress (if compressed) attribute value in zip format") if e: sys.exit(64) else: sys.exit(0) class NullsInString(Exception): """Nulls in string.""" _FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)]) def _dump(src, length=16): result = [] for i in range(0, len(src), length): s = src[i:i+length] hexa = ' '.join(["%02X" % ord(x) for x in s]) printable = s.translate(_FILTER) result.append("%04X %-*s %s\n" % (i, length*3, hexa, printable)) return ''.join(result) def main(): try: (optargs, args) = getopt.getopt(sys.argv[1:], "hlpwdz", ["help"]) except getopt.GetoptError as e: usage(e) attr_name = None long_format = False read = False write = False delete = False compress = lambda x: x decompress = compress status = 0 for opt, arg in optargs: if opt in ("-h", "--help"): usage() elif opt == "-l": long_format = True elif opt == "-p": read = True if write or delete: usage("-p not allowed with -w or -d") elif opt == "-w": write = True if read or delete: usage("-w not allowed with -p or -d") elif opt == "-d": delete = True if read or write: usage("-d not allowed with -p or -w") elif opt == "-z": compress = zlib.compress decompress = zlib.decompress if write or delete: if long_format: usage("-l not allowed with -w or -p") if read or write or delete: if not args: usage("No attr_name") attr_name = args.pop(0) if write: if not args: usage("No attr_value") attr_value = args.pop(0) if len(args) > 1: multiple_files = True else: multiple_files = False for filename in args: def onError(e): if not os.path.exists(filename): sys.stderr.write("No such file: %s\n" % (filename,)) else: sys.stderr.write(str(e) + "\n") try: attrs = xattr.xattr(filename) except (IOError, OSError) as e: onError(e) continue if write: try: attrs[attr_name] = compress(attr_value) except (IOError, OSError) as e: onError(e) continue elif delete: try: del attrs[attr_name] except (IOError, OSError) as e: onError(e) continue except KeyError: onError("No such xattr: %s" % (attr_name,)) continue else: try: if read: attr_names = (attr_name,) else: attr_names = attrs.keys() except (IOError, OSError) as e: onError(e) continue if multiple_files: file_prefix = "%s: " % (filename,) else: file_prefix = "" for attr_name in attr_names: try: try: attr_value = decompress(attrs[attr_name]) except zlib.error: attr_value = attrs[attr_name] except KeyError: onError("%sNo such xattr: %s" % (file_prefix, attr_name)) continue if long_format: try: if attr_value.find('\0') >= 0: raise NullsInString print("".join((file_prefix, "%s: " % (attr_name,), attr_value))) except (UnicodeDecodeError, NullsInString): print("".join((file_prefix, "%s:" % (attr_name,)))) print(_dump(attr_value)) else: if read: print("".join((file_prefix, attr_value))) else: print("".join((file_prefix, attr_name))) sys.exit(status) if __name__ == "__main__": main()