pax_global_header00006660000000000000000000000064135712754630014527gustar00rootroot0000000000000052 comment=0fb162fcab88020547033a31ab05f8dd64f53eb4 xattr-0.9.7/000077500000000000000000000000001357127546300127065ustar00rootroot00000000000000xattr-0.9.7/.gitignore000066400000000000000000000001101357127546300146660ustar00rootroot00000000000000/xattr.egg-info /build /dist /*.egg /*.eggs *.pyc *.so .\#* __pycache__ xattr-0.9.7/.travis.yml000066400000000000000000000025561357127546300150270ustar00rootroot00000000000000language: python cache: directories: - "$HOME/.cache/pip" - "$HOME/.pyenv" matrix: include: - name: 'Ubuntu: 3.8' os: linux python: 3.8 - name: 'Ubuntu: 3.7' os: linux python: 3.7 - name: 'Ubuntu: 3.6' os: linux python: 3.6 - name: 'Ubuntu: 3.5' os: linux python: 3.5 - name: 'Ubuntu: 2.7' os: linux python: 2.7 - name: 'Ubuntu: PyPy 3.5' os: linux env: PYPY_URL=https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux64.tar.bz2 - name: 'Ubuntu: PyPy 2.7' os: linux env: PYPY_URL=https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-linux64.tar.bz2 - name: 'OSX: 3.7' os: osx language: generic env: PYENV_VERSION=3.7.4 BUILD_SDIST=true osx_image: xcode_9.4 - name: 'OSX: 3.6' os: osx language: generic env: PYENV_VERSION=3.6.5 osx_image: xcode_9.4 - name: 'OSX: 2.7' os: osx language: generic env: PYENV_VERSION=2.7.15 osx_image: xcode_9.4 install: - "./.travis/install.sh" script: - "./.travis/run.sh" deploy: provider: releases api_key: secure: ik/Btxv+NMOGjKuNnilYSeATYwL7sHy8nildzQcF+GMCFL8mDcerXRoC1jOF+ETsmSOAZ95NOEUGNiyvCApy4VgYvBvz7mJzdaob034+GXOStEIdBBvV8v9XB9XwQpJUUGvRMSF9WMUGmhyQ9PQEPOHfERgLkdlcY24djCJm/6A= file: - dist/*.whl - dist/*.tar.gz file_glob: true on: repo: xattr/xattr tags: true skip_cleanup: true xattr-0.9.7/.travis/000077500000000000000000000000001357127546300142745ustar00rootroot00000000000000xattr-0.9.7/.travis/install.sh000077500000000000000000000014121357127546300162770ustar00rootroot00000000000000#!/bin/bash set -e set -x install_pypy() { pypy_dir="/home/travis/pypy" curl -L "$PYPY_URL" -o "pypy.tar.bz2" mkdir "$pypy_dir" tar xf "pypy.tar.bz2" -C "$pypy_dir" --strip-components=1 if [ -f "$pypy_dir/bin/pypy" ]; then ln -s "$pypy_dir/bin/pypy" "pypy" elif [ -f "$pypy_dir/bin/pypy3" ]; then ln -s "$pypy_dir/bin/pypy3" "pypy" fi ./pypy -m ensurepip } install_pyenv() { brew update > /dev/null brew upgrade readline openssl pyenv eval "$(pyenv init -)" pyenv install -sv "$PYENV_VERSION" pip install --upgrade pip pyenv rehash python -m pip install wheel } if [[ -n "$PYPY_URL" ]]; then install_pypy elif [[ -n "$PYENV_VERSION" && "$TRAVIS_OS_NAME" == "osx" ]]; then install_pyenv fi xattr-0.9.7/.travis/run.sh000077500000000000000000000010671357127546300154430ustar00rootroot00000000000000#!/bin/bash set -e set -x if [[ -n "$PYENV_VERSION" ]]; then eval "$(pyenv init -)" fi if [[ -n "$PYPY_URL" ]]; then cmd=./pypy else cmd=python fi "$cmd" setup.py build_ext -i "$cmd" -m compileall -f . "$cmd" setup.py test if [[ -n "$PYENV_VERSION" && "$TRAVIS_OS_NAME" == 'osx' ]]; then python setup.py bdist_wheel fi if [[ "$BUILD_SDIST" == 'true' ]]; then "$cmd" setup.py sdist --formats=gztar # Ensure the package installs from tarball correctly. filename=$("$cmd" setup.py --fullname) pip install "dist/$filename.tar.gz" fi xattr-0.9.7/CHANGES.txt000066400000000000000000000062021357127546300145170ustar00rootroot00000000000000Version 0.9.7 released 2019-12-02 * Fix xattr().update() in Python 3 https://github.com/xattr/xattr/pull/87 Version 0.9.6 released 2018-07-31 * Fix release build by including *.[ch] in Manifest.in https://github.com/xattr/xattr/pull/77 https://github.com/xattr/xattr/pull/78 https://github.com/xattr/xattr/pull/79 Version 0.9.4 released 2018-07-30 * Extract inline C code for syntax highlighting and easier maintenance https://github.com/xattr/xattr/pull/75 * Fix Travis build https://github.com/xattr/xattr/pull/74 * Always include sys/types.h (musl compatibility) https://github.com/xattr/xattr/pull/73 Version 0.9.3 released 2018-01-28 * Do not attempt to use surrogateescape unless it is available https://github.com/xattr/xattr/issues/59 Version 0.9.2 released 2017-04-02 * Fix BSD issue w/ lsattr and long attrs https://github.com/xattr/xattr/pull/57 * Remove unreachable code https://github.com/xattr/xattr/pull/56 Version 0.9.1 released 2016-10-23 * Allow (Python 2) long for fd https://github.com/xattr/xattr/pull/51 * Fix Python 3 bytes handling in xattr.tool https://github.com/xattr/xattr/pull/50 * Use cffi 1.X features to build native module for faster import https://github.com/xattr/xattr/pull/47 * NOTE: Version 0.9.0 is the same, was momentarily uploaded with incomplete CHANGES.txt Version 0.8.0 released 2016-02-28 * Use os.fsencode where available to better handle filesystem quirks related to surrogates https://github.com/xattr/xattr/pull/46 * Options bugfix and compatibility module for pyxattr API https://github.com/xattr/xattr/pull/38 Version 0.7.9 released 2016-02-12 * Added xattr/tests/*.py to MANIFEST.in https://github.com/xattr/xattr/issues/43 Version 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.9.7/INSTALLING.txt000066400000000000000000000001401357127546300151060ustar00rootroot00000000000000Installing ========== From the xattr source directory, type: sudo python setup.py install xattr-0.9.7/LICENSE.txt000066400000000000000000000022241357127546300145310ustar00rootroot00000000000000This 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.9.7/MANIFEST.in000066400000000000000000000001541357127546300144440ustar00rootroot00000000000000include *.py include xattr/*.c include xattr/*.h include *.txt include MANIFEST.in include xattr/tests/*.py xattr-0.9.7/README.rst000066400000000000000000000010401357127546300143700ustar00rootroot00000000000000xattr ----- .. image:: https://travis-ci.org/xattr/xattr.svg?branch=master :target: https://travis-ci.org/xattr/xattr xattr 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.9.7/TODO.txt000066400000000000000000000000371357127546300142140ustar00rootroot00000000000000* Write tests * Write examples xattr-0.9.7/requirements.txt000066400000000000000000000000121357127546300161630ustar00rootroot00000000000000cffi>=1.0 xattr-0.9.7/scripts/000077500000000000000000000000001357127546300143755ustar00rootroot00000000000000xattr-0.9.7/scripts/artifacts.py000077500000000000000000000027761357127546300167460ustar00rootroot00000000000000#!/usr/bin/env python3 from urllib.request import urlopen import json import os import subprocess import sys import getpass def get_json(url): return json.loads(urlopen(url).read().decode('utf-8')) def download_file(src_url, dest_path): print(dest_path) subprocess.call( ['curl', '-L', '-#', '-o', dest_path, src_url]) def download_github_artifacts(): release = get_json( 'https://api.github.com/repos/xattr/xattr/releases/latest') for asset in release['assets']: download_file(asset['browser_download_url'], 'dist/{name}'.format(**asset)) def get_version(): return subprocess.check_output( [sys.executable, 'setup.py', '--version'], encoding='utf8' ).strip() def artifact_matcher(version): prefix = 'xattr-{}'.format(version) def matches(fn): return ( fn.startswith(prefix) and os.path.splitext(fn)[1] in ('.exe', '.whl') and not fn.endswith('-none-any.whl') ) or fn == '{}.tar.gz'.format(prefix) return matches def upload_artifacts(version): artifacts = set(os.listdir('dist')) matches = artifact_matcher(version) args = ['twine', 'upload'] for fn in filter(matches, artifacts): args.append(os.path.join('dist', fn)) subprocess.check_call(args) def main(): try: os.makedirs('dist') except OSError: pass download_github_artifacts() version = get_version() upload_artifacts(version) if __name__ == '__main__': main() xattr-0.9.7/setup.py000066400000000000000000000032241357127546300144210ustar00rootroot00000000000000#!/usr/bin/env python import os import sys from setuptools import setup VERSION = '0.9.7' 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>=1.0.0"], setup_requires=["cffi>=1.0.0"], cffi_modules=["xattr/lib_build.py:ffi"], test_suite="xattr.tests.all_tests_suite", zip_safe=False, ) xattr-0.9.7/xattr/000077500000000000000000000000001357127546300140505ustar00rootroot00000000000000xattr-0.9.7/xattr/__init__.py000066400000000000000000000123071357127546300161640ustar00rootroot00000000000000""" 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.9.7' from .compat import integer_types 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, integer_types): 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, integer_types): 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) 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, 'items'): seq = dict(seq) for k, v in seq.items(): 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.9.7/xattr/compat.py000066400000000000000000000012501357127546300157030ustar00rootroot00000000000000"""Python 3 compatibility shims """ import os import sys import codecs if sys.version_info[0] < 3: integer_types = (int, long) text_type = unicode binary_type = str else: integer_types = (int,) text_type = str binary_type = bytes fs_encoding = sys.getfilesystemencoding() fs_errors = 'strict' if fs_encoding != 'mbcs': try: codecs.lookup('surrogateescape') fs_errors = 'surrogateescape' except LookupError: pass try: fs_encode = os.fsencode except AttributeError: def fs_encode(val): if not isinstance(val, bytes): return val.encode(fs_encoding, fs_errors) else: return val xattr-0.9.7/xattr/lib.py000066400000000000000000000072501357127546300151740ustar00rootroot00000000000000import os import sys from .compat import fs_encode try: from ._lib import lib, ffi except ImportError: from .lib_build import ffi, c_source lib = ffi.verify(c_source) 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" XATTR_RESOURCEFORK_NAME = "com.apple.ResourceFork" 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.9.7/xattr/lib_build.c000066400000000000000000000343761357127546300161560ustar00rootroot00000000000000#include "Python.h" #include #ifdef __FreeBSD__ #include #elif defined(__SUN__) || defined(__sun__) || defined(__sun) #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. * The first byte 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) (unsigned char)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 (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 /* 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 xattr-0.9.7/xattr/lib_build.h000066400000000000000000000013041357127546300161440ustar00rootroot00000000000000#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); 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); xattr-0.9.7/xattr/lib_build.py000066400000000000000000000005261357127546300163520ustar00rootroot00000000000000import sys import os from cffi import FFI PATH = os.path.dirname(__file__) with open(os.path.join(PATH, 'lib_build.h')) as hf: c_header = hf.read() with open(os.path.join(PATH, 'lib_build.c')) as cf: c_source = cf.read() ffi = FFI() ffi.cdef(c_header) ffi.set_source('_lib', c_source) if __name__ == '__main__': ffi.compile() xattr-0.9.7/xattr/pyxattr_compat.py000066400000000000000000000072621357127546300175070ustar00rootroot00000000000000""" 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 .compat import (binary_type, integer_types, text_type) 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, integer_types): return fd_func(item, *args) elif hasattr(item, 'fileno'): return fd_func(item.fileno(), *args) elif isinstance(item, binary_type): return name_func(item, *args) elif isinstance(item, text_type): 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 xattr-0.9.7/xattr/tests/000077500000000000000000000000001357127546300152125ustar00rootroot00000000000000xattr-0.9.7/xattr/tests/__init__.py000066400000000000000000000006451357127546300173300ustar00rootroot00000000000000import 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.9.7/xattr/tests/test_xattr.py000066400000000000000000000120471357127546300177710ustar00rootroot00000000000000import os import sys import unittest from unittest import TestCase from tempfile import mkdtemp, NamedTemporaryFile import xattr class BaseTestXattr(object): # TESTDIR for temporary files usually defaults to "/tmp", # which may not have XATTR support (e.g. tmpfs); # manual override here. TESTDIR = None def test_attr_fs_encoding_unicode(self): # Not using setlocale(LC_ALL, ..) to set locale because # sys.getfilesystemencoding() implementation falls back # to user's preferred locale by calling setlocale(LC_ALL, ''). xattr.compat.fs_encoding = 'UTF-8' self._test_attr() def test_attr_fs_encoding_ascii(self): xattr.compat.fs_encoding = 'US-ASCII' if sys.version_info[0] < 3: with self.assertRaises(UnicodeEncodeError): self._test_attr() else: self._test_attr() def test_update(self): x = xattr.xattr(self.tempfile) attrs = { 'user.test.key1': b'test_value1', 'user.test.key2': b'test_value2' } x.update(attrs) for k, v in attrs.items(): self.assertEqual(x[k], v) 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'] # SELinux systems use an attribute which must be accounted for if sys.platform.startswith('linux') and 'security.selinux' in x: d['security.selinux'] = x['security.selinux'] 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): symlinkPath = self.tempfilename + '.link' os.symlink(self.tempfilename, symlinkPath) try: symlink = xattr.xattr(symlinkPath, options=xattr.XATTR_NOFOLLOW) realfile = xattr.xattr(self.tempfilename) try: symlink['user.islink'] = b'true' except IOError: # Solaris, Linux don't support extended attributes on symlinks raise unittest.SkipTest("XATTRs on symlink not allowed" " on filesystem/platform") 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(dir=self.TESTDIR) self.tempfilename = self.tempfile.name def tearDown(self): self.tempfile.close() class TestDir(TestCase, BaseTestXattr): def setUp(self): self.tempfile = mkdtemp(dir=self.TESTDIR) self.tempfilename = self.tempfile def tearDown(self): os.rmdir(self.tempfile) try: # SkipTest is only available in Python 2.7+ unittest.SkipTest except AttributeError: pass else: class TestFileWithSurrogates(TestFile): def setUp(self): if sys.platform not in ('linux', 'linux2'): raise unittest.SkipTest('Files with invalid encoded names are only supported under linux') if sys.version_info[0] < 3: raise unittest.SkipTest('Test is only available on Python3') # surrogateescape not avail in py2 self.tempfile = NamedTemporaryFile(prefix=b'invalid-\xe9'.decode('utf8','surrogateescape'), dir=self.TESTDIR) self.tempfilename = self.tempfile.name xattr-0.9.7/xattr/tool.py000077500000000000000000000152451357127546300154110ustar00rootroot00000000000000#!/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 class NullsInString(Exception): """Nulls in string.""" def usage(e=None): if e: print(e) print("") name = os.path.basename(sys.argv[0]) print("usage: %s [-slz] file [file ...]" % (name,)) print(" %s -p [-slz] attr_name file [file ...]" % (name,)) print(" %s -w [-sz] attr_name attr_value file [file ...]" % (name,)) print(" %s -d [-s] 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(" -s: act on symbolic links themselves rather than their targets") 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) _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:], "hlpwdzs", ["help"]) except getopt.GetoptError as e: usage(e) attr_name = None long_format = False read = False write = False delete = False nofollow = 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 == "-s": nofollow = 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).encode('utf-8') if len(args) > 1: multiple_files = True else: multiple_files = False options = 0 if nofollow: options |= xattr.XATTR_NOFOLLOW 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, options=options) 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] attr_value = attr_value.decode('utf-8') except KeyError: onError("%sNo such xattr: %s" % (file_prefix, attr_name)) continue if long_format: try: if '\0' in attr_value: 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()