From c0017b94e911ff5bea03ccb87cd1c52f8020bfc7 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 15 May 2016 00:12:07 +0200 Subject: [PATCH 01/16] Fixup python-pyxattr-doc interaction with old python-pyxattr I thought that as the html files are in a new location, we don't need replaces/breaks; but there's the doc base file, with the same name :( Fix things up (fortunately re-reading the policy shows that just installing the new -doc package won't remove the old python-pyxattr, but upgrade it to newer one), and also add the -doc package to suggests on the -pyxattr ones. --- debian/control | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index 5223a6e..596182f 100644 --- a/debian/control +++ b/debian/control @@ -18,7 +18,7 @@ Package: python-pyxattr Architecture: any Depends: ${python:Depends}, ${shlibs:Depends}, ${misc:Depends} Provides: ${python:Provides} -Suggests: python-pyxattr-dbg +Suggests: python-pyxattr-dbg, python-pyxattr-doc Description: module for manipulating filesystem extended attributes pyxattr is a Python interface to the libattr library. It allows manipulation of the filesystem extended attributes present in some @@ -44,7 +44,7 @@ Package: python3-pyxattr Architecture: any Depends: ${python3:Depends}, ${shlibs:Depends}, ${misc:Depends} Provides: ${python3:Provides} -Suggests: python3-pyxattr-dbg +Suggests: python3-pyxattr-dbg, python-pyxattr-doc Description: module for manipulating filesystem extended attributes (Python3) pyxattr is a Python interface to the libattr library. It allows manipulation of the filesystem extended attributes present in some @@ -69,6 +69,8 @@ Description: module for manipulating filesystem extended attributes (Python3 deb Package: python-pyxattr-doc Architecture: all Section: doc +Replaces: python-pyxattr (<< 0.5.6-1) +Breaks: python-pyxattr (<< 0.5.6-1) Depends: ${misc:Depends}, ${sphinxdoc:Depends} Description: module for manipulating filesystem extended attributes (documentation) pyxattr is a Python interface to the libattr library. It allows -- 2.39.2 From 8c42fbdfa727fd216f713ed9efa8bc8c925369d0 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Mon, 23 Jan 2017 22:56:03 +0100 Subject: [PATCH 02/16] New upstream version 0.6.0 --- Makefile | 18 +- NEWS | 21 ++ PKG-INFO | 2 +- README.rst | 2 +- doc/conf.py | 4 +- doc/module.rst | 11 +- doc/news.rst | 183 ++++++++++++++- pyxattr.egg-info/PKG-INFO | 2 +- setup.cfg | 1 - setup.py | 8 +- test/test_xattr.py | 63 +++++- xattr.c | 466 ++++++++++++++++++++------------------ 12 files changed, 541 insertions(+), 240 deletions(-) mode change 120000 => 100644 doc/news.rst diff --git a/Makefile b/Makefile index a41a077..905791d 100644 --- a/Makefile +++ b/Makefile @@ -24,20 +24,28 @@ dist: test: @for ver in 2.4 2.5 2.6 2.7 3.0 3.1 3.2 3.3 3.4 3.5; do \ - if type python$$ver >/dev/null; then \ - echo Testing with python$$ver; \ - python$$ver ./setup.py test -q; \ - fi; \ + for flavour in "" "-dbg"; do \ + if type python$$ver$$flavour >/dev/null; then \ + echo Testing with python$$ver$$flavour; \ + python$$ver$$flavour ./setup.py test -q; \ + fi; \ + done; \ done; @if type pypy >/dev/null; then \ echo Testing with pypy; \ pypy ./setup.py test -q; \ fi +coverage: + $(MAKE) clean + $(MAKE) test CFLAGS="-coverage" + lcov --capture --directory . --output-file coverage.info + genhtml coverage.info --output-directory out + clean: rm -rf $(DOCHTML) $(DOCTREES) rm -f $(MODNAME) rm -f *.so rm -rf build -.PHONY: doc test clean dist +.PHONY: doc test clean dist coverage diff --git a/NEWS b/NEWS index 526c085..f9002c7 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,27 @@ News ==== +Version 0.6.0 +------------- + +*released Mon, 23 Jan 2017* + +Bugfix and feature release (hence the version bump). + +The main change is to the implementation of how attributes are listed +and read. This was done due to existing race issues when attributes are +modified while being read (github issue #12), but basically all various +internal paths that dealt with retrieving an attribute value or listing +attributes were unified in a single helper function that does handle +such concurrent modifications. As a side effect, the size of the buffers +used for such reads have changed, which (depending on attribute value) +might change the trade-off between number of syscalls done and memory +usage. + +As feature release, OSX support was contributed by Adam Knight +, thanks a lot! I don't have access to OSX so the testing +for it is done via Travis builds; please report any issues. + Version 0.5.6 ------------- diff --git a/PKG-INFO b/PKG-INFO index da37e9b..0e2792c 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyxattr -Version: 0.5.6 +Version: 0.6.0 Summary: Filesystem extended attributes for python Home-page: http://pyxattr.k1024.org/ Author: Iustin Pop diff --git a/README.rst b/README.rst index b3b314c..2844a61 100644 --- a/README.rst +++ b/README.rst @@ -6,7 +6,7 @@ to the extended attributes for filesystem objects available in some operating systems. Downloads: go to http://pyxattr.k1024.org/downloads/. Latest -version is 0.5.6. The source repository is either at +version is 0.6.0. The source repository is either at http://git.k1024.org/pyxattr.git or at https://github.com/iustin/pyxattr. diff --git a/doc/conf.py b/doc/conf.py index 77f6b68..e6c994a 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -48,9 +48,9 @@ copyright = u'2002, 2003, 2006, 2008, 2012, 2013, 2014, 2015, Iustin Pop' # built documents. # # The short X.Y version. -version = '0.5.6' +version = '0.6.0' # The full version, including alpha/beta/rc tags. -release = '0.5.6' +release = '0.6.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/module.rst b/doc/module.rst index 0231605..d7146a8 100644 --- a/doc/module.rst +++ b/doc/module.rst @@ -18,21 +18,22 @@ Constants .. data:: NS_SECURITY - The security namespace, used by kernel security modules. + The security name space, used by kernel security modules to store + (for example) capabilities information. .. data:: NS_SYSTEM - The system namespace, used by the kernel to store things such as - ACLs and capabilities. + The system name space, used by the kernel to store (for example) + ACLs. .. data:: NS_TRUSTED - The trusted namespace, visible and accessibly only to trusted + The trusted name space, visible and accessibly only to trusted processes, used to implement mechanisms in user space. .. data:: NS_USER - The user namespace; this is the namespace accessible to + The user name space; this is the name space accessible to non-privileged processes. Functions diff --git a/doc/news.rst b/doc/news.rst deleted file mode 120000 index 0fae0f8..0000000 --- a/doc/news.rst +++ /dev/null @@ -1 +0,0 @@ -../NEWS \ No newline at end of file diff --git a/doc/news.rst b/doc/news.rst new file mode 100644 index 0000000..f9002c7 --- /dev/null +++ b/doc/news.rst @@ -0,0 +1,182 @@ +News +==== + +Version 0.6.0 +------------- + +*released Mon, 23 Jan 2017* + +Bugfix and feature release (hence the version bump). + +The main change is to the implementation of how attributes are listed +and read. This was done due to existing race issues when attributes are +modified while being read (github issue #12), but basically all various +internal paths that dealt with retrieving an attribute value or listing +attributes were unified in a single helper function that does handle +such concurrent modifications. As a side effect, the size of the buffers +used for such reads have changed, which (depending on attribute value) +might change the trade-off between number of syscalls done and memory +usage. + +As feature release, OSX support was contributed by Adam Knight +, thanks a lot! I don't have access to OSX so the testing +for it is done via Travis builds; please report any issues. + +Version 0.5.6 +------------- + +*released Sat, 09 Apr 2016* + +Small bugfix release: + +* Fixes some sign-compare warnings +* Fixes potential name truncation in merge_ns() +* Fixes building on systems which don't have ENODATA + +Tested with Python 2.7.11, Python 3.5.1 and PyPy 5.0.1. + +Version 0.5.5 +------------- + +*released Fri, 01 May 2015* + +Bugfix release: + +* fixes some more memory leaks when handling out-of-memory in get_all() + function +* improve error reporting when an attribute disappears after we asked + for its length but before we managed to read it +* fix int/size_t issues found by RedHat/Fedora, + https://bugzilla.redhat.com/show_bug.cgi?id=1127310; the fix is + different than their fix, but it should accomplish the same thing +* convert all code to only do explicit casts after checking boundaries, + making the code `-Wconversion`-clean (although that warning is not + enabled by default) + +Version 0.5.4 +------------- + +*released Thu, 30 Apr 2015* + +Fix memory leaks on some of the error-handling paths of the `get()` +function. + +Version 0.5.3 +------------- + +*released Fri, 23 May 2014* + +Small optimisations release: + +* ari edelkind contributed a speed-up optimisation for handling of files + without xattrs (which is, in general, the expected case) +* Jonas Borgström contributed a behaviour change to the handling of file + names: under Python 3 and up, unicode paths are encoded/decoded using + the 'surogatee' handler, instead of the 'strict' handler; while this + can hide encoding errors, it mirrors what Python libraries do + (e.g. see os.fsencode/fsdecode) +* Sean Patrick Santos contributed improvements to the test suite so that + it can be used even on files systems which have built-in attributes + (e.g. when using SELinux, or NFSv4); to enable this, define the + attributes in the TEST_IGNORE_XATTRS environment variable + +Version 0.5.2 +------------- + +*released Thu, 03 Jan 2013* + +Bug-fix release. Thanks to Michał Górny, it looked like the library had +problem running under pypy, but actually there was a bug in the +PyArg_ParseTuple use of et# (signed vs. unsigned, and lack of compiler +warnings). This was fixed, and now the test suite passed with many +CPython versions and PyPy (version 1.9). + +Version 0.5.1 +------------- + +*released Wed, 16 May 2012* + +Bug-fix release. Thanks to Dave Malcolm and his cpychecker tool, a +number of significant bugs (refcount leaks and potential NULL-pointer +dereferences) have been fixed. + +Furthermore, compatibility with Python 3 has been improved; this however +required changing the meaning of the ``namespace`` argument to the +functions: if passed, None is no longer a valid value; pass an empty +string if (due to the structure of your program) you have to pass this +argument but want to specify no namespace. + +Also, the project home page has changed from SourceForge to GitHub, and +the documentation has been converted from epydoc-based to sphinx. + + +Version 0.5 +----------- + +*released Sun, 27 Dec 2009* + +Implemented support for Python 3. This required a significant change to +the C module, hence the new version number. + +Version 0.4 +----------- + +*released Mon, 30 Jun 2008* + +API +~~~ + +The old functions ({get,set,list,remove}xattr) are deprecated and replaced with +a new API that is namespace-aware and hopefully will allow other OSes (e.g. +FreeBSD) to be supported more naturally. + +Both the old and the new API are supported in the 0.4 versions, however users +are encouraged to migrate to the new API. + +New features +~~~~~~~~~~~~ + +A new bulk get function called get_all() has been added that should be somewhat +faster in case of querying files which have many attributes. + +License +~~~~~~~ + +Since LGPLv3 is not compatible with GPLv2 (which unfortunately I didn't realize +before), the license was changed to LGPLv2.1 or later. + +Internals +~~~~~~~~~ + +Unittest coverage was improved. + +Version 0.3 +----------- + +*released Sun, 09 Mar 2008* + +* changed licence from GPL to LGPL (3 or later) +* changed listxattr return type from tuple to a list +* developer-related: added unittests + +Version 0.2.2 +------------- + +*released Sun, 01 Jul 2007* + +* fixed listing symlink xattrs + +Version 0.2.1 +------------- + +*released Sat, 11 Feb 2006* + +* fixed a bug when reading symlink EAs (you weren't able to + do it, actually) +* fixed a possible memory leak when the actual read of the EA + failed but the call to get the length of the EA didn't + +.. Local Variables: +.. mode: rst +.. fill-column: 72 +.. End: diff --git a/pyxattr.egg-info/PKG-INFO b/pyxattr.egg-info/PKG-INFO index da37e9b..0e2792c 100644 --- a/pyxattr.egg-info/PKG-INFO +++ b/pyxattr.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyxattr -Version: 0.5.6 +Version: 0.6.0 Summary: Filesystem extended attributes for python Home-page: http://pyxattr.k1024.org/ Author: Iustin Pop diff --git a/setup.cfg b/setup.cfg index a68b051..4e0e866 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,5 +5,4 @@ requires = libattr [egg_info] tag_build = tag_date = 0 -tag_svn_revision = 0 diff --git a/setup.py b/setup.py index f580b19..54259db 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ #!/usr/bin/python import distutils +import platform try: from setuptools import setup, Extension except ImportError: @@ -9,9 +10,12 @@ except ImportError: long_desc = """This is a C extension module for Python which implements extended attributes manipulation. It is a wrapper on top of the attr C library - see attr(5).""" -version = "0.5.6" +version = "0.6.0" author = "Iustin Pop" author_email = "iustin@k1024.org" +libraries = [] +if platform.system() == 'Linux': + libraries.append("attr") macros = [ ("_XATTR_VERSION", '"%s"' % version), ("_XATTR_AUTHOR", '"%s"' % author), @@ -27,7 +31,7 @@ setup(name = "pyxattr", download_url = "http://pyxattr.k1024.org/downloads/", license = "LGPL", ext_modules = [Extension("xattr", ["xattr.c"], - libraries=["attr"], + libraries=libraries, define_macros=macros, extra_compile_args=["-Wall", "-Werror", "-Wsign-compare"], )], diff --git a/test/test_xattr.py b/test/test_xattr.py index 69e939e..084b831 100644 --- a/test/test_xattr.py +++ b/test/test_xattr.py @@ -31,12 +31,16 @@ class xattrTest(unittest.TestCase): USER_NN = "test" USER_ATTR = NS_USER.decode() + "." + USER_NN USER_VAL = "abc" + EMPTY_VAL = "" + LARGE_VAL = "x" * 2048 MANYOPS_COUNT = 131072 if PY3K: USER_NN = USER_NN.encode() USER_VAL = USER_VAL.encode() USER_ATTR = USER_ATTR.encode() + EMPTY_VAL = EMPTY_VAL.encode() + LARGE_VAL = LARGE_VAL.encode() @staticmethod def _ignore_tuples(attrs): @@ -51,11 +55,11 @@ class xattrTest(unittest.TestCase): if attr not in TEST_IGNORE_XATTRS] def checkList(self, attrs, value): - """Helper to check attribute list equivalence, skipping TEST_IGNORE_XATTRS.""" + """Helper to check list equivalence, skipping TEST_IGNORE_XATTRS.""" self.assertEqual(self._ignore(attrs), value) def checkTuples(self, attrs, value): - """Helper to check attribute list equivalence, skipping TEST_IGNORE_XATTRS.""" + """Helper to check list equivalence, skipping TEST_IGNORE_XATTRS.""" self.assertEqual(self._ignore_tuples(attrs), value) def setUp(self): @@ -109,9 +113,11 @@ class xattrTest(unittest.TestCase): xattr.setxattr(item, self.USER_ATTR, self.USER_VAL, 0, symlink) except IOError: err = sys.exc_info()[1] - if err.errno == errno.EPERM and symlink: + if symlink and (err.errno == errno.EPERM or + err.errno == errno.ENOENT): # symlinks may fail, in which case we abort the rest - # of the test for this case + # of the test for this case (Linux returns EPERM; OS X + # returns ENOENT) return raise self.assertRaises(EnvironmentError, xattr.setxattr, item, @@ -146,9 +152,11 @@ class xattrTest(unittest.TestCase): nofollow=symlink) except IOError: err = sys.exc_info()[1] - if err.errno == errno.EPERM and symlink: + if symlink and (err.errno == errno.EPERM or + err.errno == errno.ENOENT): # symlinks may fail, in which case we abort the rest - # of the test for this case + # of the test for this case (Linux returns EPERM; OS X + # returns ENOENT) return raise self.assertRaises(EnvironmentError, xattr.set, item, @@ -188,12 +196,19 @@ class xattrTest(unittest.TestCase): fh, fname = self._getfile() self.checkList(xattr.listxattr(fname), []) self.checkTuples(xattr.get_all(fname), []) + self.assertRaises(EnvironmentError, xattr.getxattr, fname, + self.USER_ATTR) dname = self._getdir() self.checkList(xattr.listxattr(dname), []) self.checkTuples(xattr.get_all(dname), []) + self.assertRaises(EnvironmentError, xattr.getxattr, dname, + self.USER_ATTR) _, sname = self._getsymlink() self.checkList(xattr.listxattr(sname, True), []) self.checkTuples(xattr.get_all(sname, nofollow=True), []) + self.assertRaises(EnvironmentError, xattr.getxattr, fname, + self.USER_ATTR, True) + def testNoXattr(self): """test no attributes""" @@ -202,11 +217,15 @@ class xattrTest(unittest.TestCase): self.assertEqual(xattr.list(fname, namespace=NS_USER), []) self.checkTuples(xattr.get_all(fname), []) self.assertEqual(xattr.get_all(fname, namespace=NS_USER), []) + self.assertRaises(EnvironmentError, xattr.get, fname, + self.USER_NN, namespace=NS_USER) dname = self._getdir() self.checkList(xattr.list(dname), []) self.assertEqual(xattr.list(dname, namespace=NS_USER), []) self.checkTuples(xattr.get_all(dname), []) self.assertEqual(xattr.get_all(dname, namespace=NS_USER), []) + self.assertRaises(EnvironmentError, xattr.get, dname, + self.USER_NN, namespace=NS_USER) _, sname = self._getsymlink() self.checkList(xattr.list(sname, nofollow=True), []) self.assertEqual(xattr.list(sname, nofollow=True, @@ -214,6 +233,8 @@ class xattrTest(unittest.TestCase): self.checkTuples(xattr.get_all(sname, nofollow=True), []) self.assertEqual(xattr.get_all(sname, nofollow=True, namespace=NS_USER), []) + self.assertRaises(EnvironmentError, xattr.get, sname, + self.USER_NN, namespace=NS_USER, nofollow=True) def testFileByNameDeprecated(self): """test set and retrieve one attribute by file name (deprecated)""" @@ -397,6 +418,36 @@ class xattrTest(unittest.TestCase): self.assertRaises(TypeError, xattr.get, fh, self.USER_ATTR, namespace=None) + def testEmptyValue(self): + fh, fname = self._getfile() + xattr.set(fh, self.USER_ATTR, self.EMPTY_VAL) + self.assertEqual(xattr.get(fh, self.USER_ATTR), self.EMPTY_VAL) + + def testWrongCall(self): + for call in [xattr.get, + xattr.list, xattr.listxattr, + xattr.remove, xattr.removexattr, + xattr.set, xattr.setxattr, + xattr.get, xattr.getxattr]: + self.assertRaises(TypeError, call) + + def testWrongType(self): + self.assertRaises(TypeError, xattr.get, object(), self.USER_ATTR) + for call in [xattr.listxattr, xattr.list]: + self.assertRaises(TypeError, call, object()) + for call in [xattr.remove, xattr.removexattr, + xattr.get, xattr.getxattr]: + self.assertRaises(TypeError, call, object(), self.USER_ATTR) + for call in [xattr.set, xattr.setxattr]: + self.assertRaises(TypeError, call, object(), self.USER_ATTR, self.USER_VAL) + + + def testLargeAttribute(self): + fh, fname = self._getfile() + + xattr.set(fh, self.USER_ATTR, self.LARGE_VAL) + self.assertEqual(xattr.get(fh, self.USER_ATTR), self.LARGE_VAL) + if __name__ == "__main__": unittest.main() diff --git a/xattr.c b/xattr.c index c792df3..111cec1 100644 --- a/xattr.c +++ b/xattr.c @@ -23,7 +23,11 @@ #define PY_SSIZE_T_CLEAN #include +#if defined(__APPLE__) +#include +#elif defined(__linux__) #include +#endif #include /* Compatibility with python 2.4 regarding python size type (PEP 353) */ @@ -36,8 +40,10 @@ typedef int Py_ssize_t; #if PY_MAJOR_VERSION >= 3 #define IS_PY3K #define BYTES_CHAR "y" +#define BYTES_TUPLE "yy#" #else #define BYTES_CHAR "s" +#define BYTES_TUPLE "ss#" #define PyBytes_Check PyString_Check #define PyBytes_AS_STRING PyString_AS_STRING #define PyBytes_FromStringAndSize PyString_FromStringAndSize @@ -94,9 +100,14 @@ typedef int Py_ssize_t; " string (byte string under Python 3)." -/* the estimated (startup) attribute buffer size in - multi-operations */ -#define ESTIMATE_ATTR_SIZE 256 +/* The initial I/O buffer size for list and get operations; if the + * actual values will be smaller than this, we save a syscall out of + * two and allocate more memory upfront than needed, otherwise we + * incur three syscalls (get with ENORANGE, get with 0 to compute + * actual size, final get). The test suite is marginally faster (5%) + * with this, so it seems worth doing. +*/ +#define ESTIMATE_ATTR_SIZE 1024 typedef enum {T_FD, T_PATH, T_LINK} target_e; @@ -163,6 +174,8 @@ static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow) { tgt->fd = fd; } else { PyErr_SetString(PyExc_TypeError, "argument must be string or int"); + tgt->type = T_PATH; + tgt->name = NULL; return -1; } return 0; @@ -196,42 +209,203 @@ static int merge_ns(const char *ns, const char *name, return 0; } -static ssize_t _list_obj(target_t *tgt, char *list, size_t size) { +#if defined(__APPLE__) +static inline ssize_t _listxattr(const char *path, char *namebuf, size_t size) { + return listxattr(path, namebuf, size, 0); +} +static inline ssize_t _llistxattr(const char *path, char *namebuf, size_t size) { + return listxattr(path, namebuf, size, XATTR_NOFOLLOW); +} +static inline ssize_t _flistxattr(int fd, char *namebuf, size_t size) { + return flistxattr(fd, namebuf, size, 0); +} + +static inline ssize_t _getxattr (const char *path, const char *name, void *value, size_t size) { + return getxattr(path, name, value, size, 0, 0); +} +static inline ssize_t _lgetxattr (const char *path, const char *name, void *value, size_t size) { + return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); +} +static inline ssize_t _fgetxattr (int filedes, const char *name, void *value, size_t size) { + return fgetxattr(filedes, name, value, size, 0, 0); +} + +// [fl]setxattr: Both OS X and Linux define XATTR_CREATE and XATTR_REPLACE for the last option. +static inline int _setxattr(const char *path, const char *name, const void *value, size_t size, int flags) { + return setxattr(path, name, value, size, 0, flags); +} +static inline int _lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) { + return setxattr(path, name, value, size, 0, flags & XATTR_NOFOLLOW); +} +static inline int _fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) { + return fsetxattr(filedes, name, value, size, 0, flags); +} + +static inline int _removexattr(const char *path, const char *name) { + return removexattr(path, name, 0); +} +static inline int _lremovexattr(const char *path, const char *name) { + return removexattr(path, name, XATTR_NOFOLLOW); +} +static inline int _fremovexattr(int filedes, const char *name) { + return fremovexattr(filedes, name, 0); +} + +#elif defined(__linux__) +#define _listxattr(path, list, size) listxattr(path, list, size) +#define _llistxattr(path, list, size) llistxattr(path, list, size) +#define _flistxattr(fd, list, size) flistxattr(fd, list, size) + +#define _getxattr(path, name, value, size) getxattr(path, name, value, size) +#define _lgetxattr(path, name, value, size) lgetxattr(path, name, value, size) +#define _fgetxattr(fd, name, value, size) fgetxattr(fd, name, value, size) + +#define _setxattr(path, name, value, size, flags) setxattr(path, name, value, size, flags) +#define _lsetxattr(path, name, value, size, flags) lsetxattr(path, name, value, size, flags) +#define _fsetxattr(fd, name, value, size, flags) fsetxattr(fd, name, value, size, flags) + +#define _removexattr(path, name) removexattr(path, name) +#define _lremovexattr(path, name) lremovexattr(path, name) +#define _fremovexattr(fd, name) fremovexattr(fd, name) + +#endif + +typedef ssize_t (*buf_getter)(target_t *tgt, const char *name, + void *output, size_t size); + +static ssize_t _list_obj(target_t *tgt, const char *unused, void *list, + size_t size) { if(tgt->type == T_FD) - return flistxattr(tgt->fd, list, size); + return _flistxattr(tgt->fd, list, size); else if (tgt->type == T_LINK) - return llistxattr(tgt->name, list, size); + return _llistxattr(tgt->name, list, size); else - return listxattr(tgt->name, list, size); + return _listxattr(tgt->name, list, size); } static ssize_t _get_obj(target_t *tgt, const char *name, void *value, size_t size) { if(tgt->type == T_FD) - return fgetxattr(tgt->fd, name, value, size); + return _fgetxattr(tgt->fd, name, value, size); else if (tgt->type == T_LINK) - return lgetxattr(tgt->name, name, value, size); + return _lgetxattr(tgt->name, name, value, size); else - return getxattr(tgt->name, name, value, size); + return _getxattr(tgt->name, name, value, size); } static int _set_obj(target_t *tgt, const char *name, const void *value, size_t size, int flags) { if(tgt->type == T_FD) - return fsetxattr(tgt->fd, name, value, size, flags); + return _fsetxattr(tgt->fd, name, value, size, flags); else if (tgt->type == T_LINK) - return lsetxattr(tgt->name, name, value, size, flags); + return _lsetxattr(tgt->name, name, value, size, flags); else - return setxattr(tgt->name, name, value, size, flags); + return _setxattr(tgt->name, name, value, size, flags); } static int _remove_obj(target_t *tgt, const char *name) { if(tgt->type == T_FD) - return fremovexattr(tgt->fd, name); + return _fremovexattr(tgt->fd, name); else if (tgt->type == T_LINK) - return lremovexattr(tgt->name, name); + return _lremovexattr(tgt->name, name); else - return removexattr(tgt->name, name); + return _removexattr(tgt->name, name); +} + +/* Perform a get/list operation with appropriate buffer size, + * determined dynamically. + * + * Arguments: + * - getter: the function that actually does the I/O. + * - tgt, name: passed to the getter. + * - buffer: pointer to either an already allocated memory area (in + * which case size contains its current size), or NULL to + * allocate. In all cases (success or failure), the caller should + * deallocate the buffer, using PyMem_Free(). Note that if size is + * zero but buffer already points to allocate memory, it will be + * ignored/leaked. + * - size: either size of current buffer (if non-NULL), or size for + * initial allocation (if non-zero), or a zero value which means + * auto-allocate buffer with automatically queried size. Value will + * be updated upon return with the current buffer size. + * - io_errno: if non-NULL, the actual errno will be recorded here; if + * zero, the call was successful and the output/size/nval are valid. + * + * Return value: if positive or zero, buffer will contain the read + * value. Otherwise, io_errno will contain the I/O errno, or zero + * to signify a Python-level error. In all cases, the Python-level + * error is set to the appropriate value. + */ +static ssize_t _generic_get(buf_getter getter, target_t *tgt, + const char *name, + char **buffer, + size_t *size, + int *io_errno) + CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; + +static ssize_t _generic_get(buf_getter getter, target_t *tgt, + const char *name, + char **buffer, + size_t *size, + int *io_errno) { + ssize_t res; + /* Clear errno for now, will only set it when it fails in I/O. */ + if (io_errno != NULL) { + *io_errno = 0; + } + +#define EXIT_IOERROR() \ + { \ + if (io_errno != NULL) { \ + *io_errno = errno; \ + } \ + PyErr_SetFromErrno(PyExc_IOError); \ + return -1; \ + } + + /* Initialize the buffer, if needed. */ + if (*size == 0 || *buffer == NULL) { + if (*size == 0) { + ssize_t nalloc; + if ((nalloc = getter(tgt, name, NULL, 0)) == -1) { + EXIT_IOERROR(); + } + if (nalloc == 0) { + /* Empty, so no need to retrieve it. */ + return 0; + } + *size = nalloc; + } + if((*buffer = PyMem_Malloc(*size)) == NULL) { + PyErr_NoMemory(); + return -1; + } + } + // Try to get the value, while increasing the buffer if too small. + while((res = getter(tgt, name, *buffer, *size)) == -1) { + if(errno == ERANGE) { + ssize_t realloc_size_s = getter(tgt, name, NULL, 0); + /* ERANGE + proper size _should_ not fail, but... */ + if(realloc_size_s == -1) { + EXIT_IOERROR(); + } + size_t realloc_size = (size_t) realloc_size_s; + char *tmp_buf; + if((tmp_buf = PyMem_Realloc(*buffer, realloc_size)) == NULL) { + PyErr_NoMemory(); + return -1; + } + *buffer = tmp_buf; + *size = realloc_size; + continue; + } else { + /* else we're dealing with a different error, which we + don't know how to handle nicely, so we return */ + EXIT_IOERROR(); + } + } + return res; +#undef EXIT_IOERROR } /* @@ -277,9 +451,9 @@ pygetxattr(PyObject *self, PyObject *args) target_t tgt; int nofollow = 0; char *attrname = NULL; - char *buf; - ssize_t nalloc_s, nret; - size_t nalloc; + char *buf = NULL; + ssize_t nret; + size_t nalloc = ESTIMATE_ATTR_SIZE; PyObject *res; /* Parse the arguments */ @@ -290,33 +464,17 @@ pygetxattr(PyObject *self, PyObject *args) goto free_arg; } - /* Find out the needed size of the buffer */ - if((nalloc_s = _get_obj(&tgt, attrname, NULL, 0)) == -1) { - res = PyErr_SetFromErrno(PyExc_IOError); - goto free_tgt; - } - - nalloc = (size_t) nalloc_s; - - /* Try to allocate the memory, using Python's allocator */ - if((buf = PyMem_Malloc(nalloc)) == NULL) { - res = PyErr_NoMemory(); - goto free_tgt; - } - - /* Now retrieve the attribute value */ - if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) { - res = PyErr_SetFromErrno(PyExc_IOError); - goto free_buf; + nret = _generic_get(_get_obj, &tgt, attrname, &buf, &nalloc, NULL); + if (nret == -1) { + res = NULL; + goto free_buf; } - /* Create the string which will hold the result */ res = PyBytes_FromStringAndSize(buf, nret); free_buf: /* Free the buffer, now it is no longer needed */ PyMem_Free(buf); - free_tgt: free_tgt(&tgt); free_arg: PyMem_Free(attrname); @@ -356,45 +514,29 @@ xattr_get(PyObject *self, PyObject *args, PyObject *keywds) int nofollow = 0; char *attrname = NULL, *namebuf; const char *fullname; - char *buf; + char *buf = NULL; const char *ns = NULL; - ssize_t nalloc_s, nret; - size_t nalloc; - PyObject *res; + ssize_t nret; + size_t nalloc = ESTIMATE_ATTR_SIZE; + PyObject *res = NULL; static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL}; /* Parse the arguments */ if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist, &myarg, NULL, &attrname, &nofollow, &ns)) return NULL; + res = NULL; if(convert_obj(myarg, &tgt, nofollow) < 0) { - res = NULL; goto free_arg; } if(merge_ns(ns, attrname, &fullname, &namebuf) < 0) { - res = NULL; goto free_tgt; } - /* Find out the needed size of the buffer */ - if((nalloc_s = _get_obj(&tgt, fullname, NULL, 0)) == -1) { - res = PyErr_SetFromErrno(PyExc_IOError); - goto free_name_buf; - } - - nalloc = (size_t) nalloc_s; - - /* Try to allocate the memory, using Python's allocator */ - if((buf = PyMem_Malloc(nalloc)) == NULL) { - res = PyErr_NoMemory(); - goto free_name_buf; - } - - /* Now retrieve the attribute value */ - if((nret = _get_obj(&tgt, fullname, buf, nalloc)) == -1) { - res = PyErr_SetFromErrno(PyExc_IOError); - goto free_buf; + nret = _generic_get(_get_obj, &tgt, fullname, &buf, &nalloc, NULL); + if(nret == -1) { + goto free_buf; } /* Create the string which will hold the result */ @@ -403,7 +545,6 @@ xattr_get(PyObject *self, PyObject *args, PyObject *keywds) /* Free the buffers, they are no longer needed */ free_buf: PyMem_Free(buf); - free_name_buf: PyMem_Free(namebuf); free_tgt: free_tgt(&tgt); @@ -458,13 +599,14 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) PyObject *myarg, *res; int nofollow=0; const char *ns = NULL; - char *buf_list, *buf_val, *buf_val_tmp; + char *buf_list = NULL, *buf_val = NULL; const char *s; - ssize_t nalloc_s, nlist, nval_s; - size_t nalloc, nval; + size_t nalloc = ESTIMATE_ATTR_SIZE; + ssize_t nlist, nval; PyObject *mylist; target_t tgt; static char *kwlist[] = {"item", "nofollow", "namespace", NULL}; + int io_errno; /* Parse the arguments */ if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist, @@ -473,114 +615,48 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) if(convert_obj(myarg, &tgt, nofollow) < 0) return NULL; + res = NULL; /* Compute first the list of attributes */ - - /* Find out the needed size of the buffer for the attribute list */ - nalloc_s = _list_obj(&tgt, NULL, 0); - - if(nalloc_s == -1) { - res = PyErr_SetFromErrno(PyExc_IOError); - goto free_tgt; - } - - if(nalloc_s == 0) { - res = PyList_New(0); - goto free_tgt; - } - - nalloc = (size_t) nalloc_s; - - /* Try to allocate the memory, using Python's allocator */ - if((buf_list = PyMem_Malloc(nalloc)) == NULL) { - res = PyErr_NoMemory(); - goto free_tgt; - } - - /* Now retrieve the list of attributes */ - nlist = _list_obj(&tgt, buf_list, nalloc); - - if(nlist == -1) { - res = PyErr_SetFromErrno(PyExc_IOError); - goto free_buf_list; + nlist = _generic_get(_list_obj, &tgt, NULL, &buf_list, + &nalloc, &io_errno); + if (nlist == -1) { + /* We can't handle any errors, and the Python error is already + set, just bail out. */ + goto free_tgt; } - /* Create the list which will hold the result */ + /* Create the list which will hold the result. */ mylist = PyList_New(0); if(mylist == NULL) { - res = NULL; goto free_buf_list; } nalloc = ESTIMATE_ATTR_SIZE; - if((buf_val = PyMem_Malloc(nalloc)) == NULL) { - Py_DECREF(mylist); - res = PyErr_NoMemory(); - goto free_buf_list; - } - /* Create and insert the attributes as strings in the list */ for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) { PyObject *my_tuple; - int missing; const char *name; - if((name=matches_ns(ns, s))==NULL) + if((name = matches_ns(ns, s)) == NULL) continue; /* Now retrieve the attribute value */ - missing = 0; - while(1) { - nval_s = _get_obj(&tgt, s, buf_val, nalloc); - - if(nval_s == -1) { - if(errno == ERANGE) { - ssize_t realloc_size_s = _get_obj(&tgt, s, NULL, 0); - /* ERANGE + proper size should not fail, but it - still can, so let's check first */ - if(realloc_size_s == -1) { - res = PyErr_SetFromErrno(PyExc_IOError); - Py_DECREF(mylist); - goto free_buf_val; - } - size_t realloc_size = (size_t) realloc_size_s; - if((buf_val_tmp = PyMem_Realloc(buf_val, realloc_size)) - == NULL) { - res = PyErr_NoMemory(); - Py_DECREF(mylist); - goto free_buf_val; - } - buf_val = buf_val_tmp; - nalloc = realloc_size; - continue; - } else if( + nval = _generic_get(_get_obj, &tgt, s, &buf_val, &nalloc, &io_errno); + if (nval == -1) { + if ( #ifdef ENODATA - errno == ENODATA || + io_errno == ENODATA || #endif - errno == ENOATTR) { - /* this attribute has gone away since we queried - the attribute list */ - missing = 1; - break; - } - /* else we're dealing with a different error, which we - don't know how to handle nicely, so we abort */ - Py_DECREF(mylist); - res = PyErr_SetFromErrno(PyExc_IOError); - goto free_buf_val; - } else { - nval = (size_t) nval_s; - } - break; - } - if(missing) + io_errno == ENOATTR) { + PyErr_Clear(); continue; -#ifdef IS_PY3K - my_tuple = Py_BuildValue("yy#", name, buf_val, nval); -#else - my_tuple = Py_BuildValue("ss#", name, buf_val, nval); -#endif + } else { + Py_DECREF(mylist); + goto free_buf_val; + } + } + my_tuple = Py_BuildValue(BYTES_TUPLE, name, buf_val, nval); if (my_tuple == NULL) { Py_DECREF(mylist); - res = NULL; goto free_buf_val; } PyList_Append(mylist, my_tuple); @@ -902,10 +978,10 @@ static char __pylistxattr_doc__[] = static PyObject * pylistxattr(PyObject *self, PyObject *args) { - char *buf; - int nofollow=0; - ssize_t nalloc_s, nret; - size_t nalloc; + char *buf = NULL; + int nofollow = 0; + ssize_t nret; + size_t nalloc = ESTIMATE_ATTR_SIZE; PyObject *myarg; PyObject *mylist; Py_ssize_t nattrs; @@ -918,29 +994,10 @@ pylistxattr(PyObject *self, PyObject *args) if(convert_obj(myarg, &tgt, nofollow) < 0) return NULL; - /* Find out the needed size of the buffer */ - if((nalloc_s = _list_obj(&tgt, NULL, 0)) == -1) { - mylist = PyErr_SetFromErrno(PyExc_IOError); - goto free_tgt; - } - - if(nalloc_s == 0) { - mylist = PyList_New(0); - goto free_tgt; - } - - nalloc = (size_t) nalloc_s; - - /* Try to allocate the memory, using Python's allocator */ - if((buf = PyMem_Malloc(nalloc)) == NULL) { - mylist = PyErr_NoMemory(); - goto free_tgt; - } - - /* Now retrieve the list of attributes */ - if((nret = _list_obj(&tgt, buf, nalloc)) == -1) { - mylist = PyErr_SetFromErrno(PyExc_IOError); - goto free_buf; + nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL); + if (nret == -1) { + mylist = NULL; + goto free_buf; } /* Compute the number of attributes in the list */ @@ -950,8 +1007,9 @@ pylistxattr(PyObject *self, PyObject *args) /* Create the list which will hold the result */ mylist = PyList_New(nattrs); - if(mylist == NULL) + if(mylist == NULL) { goto free_buf; + } /* Create and insert the attributes as strings in the list */ for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) { @@ -968,8 +1026,6 @@ pylistxattr(PyObject *self, PyObject *args) free_buf: /* Free the buffer, now it is no longer needed */ PyMem_Free(buf); - - free_tgt: free_tgt(&tgt); /* Return the result */ @@ -1005,10 +1061,10 @@ static char __list_doc__[] = static PyObject * xattr_list(PyObject *self, PyObject *args, PyObject *keywds) { - char *buf; + char *buf = NULL; int nofollow = 0; - ssize_t nalloc_s, nret; - size_t nalloc; + ssize_t nret; + size_t nalloc = ESTIMATE_ATTR_SIZE; PyObject *myarg; PyObject *res; const char *ns = NULL; @@ -1021,34 +1077,13 @@ xattr_list(PyObject *self, PyObject *args, PyObject *keywds) if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist, &myarg, &nofollow, &ns)) return NULL; + res = NULL; if(convert_obj(myarg, &tgt, nofollow) < 0) { - res = NULL; goto free_arg; } - - /* Find out the needed size of the buffer */ - if((nalloc_s = _list_obj(&tgt, NULL, 0)) == -1) { - res = PyErr_SetFromErrno(PyExc_IOError); - goto free_tgt; - } - - if(nalloc_s == 0) { - res = PyList_New(0); - goto free_tgt; - } - - nalloc = (size_t) nalloc_s; - - /* Try to allocate the memory, using Python's allocator */ - if((buf = PyMem_Malloc(nalloc)) == NULL) { - res = PyErr_NoMemory(); - goto free_tgt; - } - - /* Now retrieve the list of attributes */ - if((nret = _list_obj(&tgt, buf, nalloc)) == -1) { - res = PyErr_SetFromErrno(PyExc_IOError); - goto free_buf; + nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL); + if (nret == -1) { + goto free_tgt; } /* Compute the number of attributes in the list */ @@ -1056,15 +1091,16 @@ xattr_list(PyObject *self, PyObject *args, PyObject *keywds) if(matches_ns(ns, s) != NULL) nattrs++; } + /* Create the list which will hold the result */ - res = PyList_New(nattrs); - if(res == NULL) + if((res = PyList_New(nattrs)) == NULL) { goto free_buf; + } /* Create and insert the attributes as strings in the list */ for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) { const char *name = matches_ns(ns, s); - if(name!=NULL) { + if(name != NULL) { PyObject *item = PyBytes_FromString(name); if(item == NULL) { Py_DECREF(res); -- 2.39.2 From d4ab58d38a0e4eb6ee04a3c69a817daa2fc1af86 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Mon, 23 Jan 2017 23:25:32 +0100 Subject: [PATCH 03/16] Fixup simple autopkgtest when building the binaries --- debian/tests/simple | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/tests/simple b/debian/tests/simple index 534fc2f..a68a0b9 100755 --- a/debian/tests/simple +++ b/debian/tests/simple @@ -1,5 +1,11 @@ #!/bin/sh +# switch directory into temp dir to ensure we don't try to use the +# 'xattr.so' from the source package (if autopkgtest has built the +# package): +cd "$ADTTMP/" + +# and now simply import the modules: python2 -c 'import xattr; print xattr; print xattr.__version__' python2-dbg -c 'import xattr; print xattr; print xattr.__version__' python3 -c 'import xattr; print(xattr); print(xattr.__version__)' -- 2.39.2 From 0b14678a652f9439b8b0b3f65a609d08da9a13c0 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Mon, 23 Jan 2017 23:00:15 +0100 Subject: [PATCH 04/16] Bump version for new upstream release --- debian/changelog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/debian/changelog b/debian/changelog index a23fc0d..7401c3d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +python-pyxattr (0.6.0-1) UNRELEASED; urgency=medium + + * New upstream release, mainly fixing a race condition when + listing/reading attributes during concurrent modifications (which + required internal changes, see the NEWS file), and OSX support (not + impacting Debian) + * Fixup autopkgtests to also work when building the packages + + -- Iustin Pop Mon, 23 Jan 2017 23:24:49 +0100 + python-pyxattr (0.5.6-1) unstable; urgency=medium * Imported Upstream versions 0.5.4, 0.5.5 and 0.5.6; main changes: -- 2.39.2 From ef47f72a9c654a4ae6cead658ca6395cb6ebfe13 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Mon, 23 Jan 2017 23:35:52 +0100 Subject: [PATCH 05/16] Fixup distribution line :( --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 7401c3d..d88bbe3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -python-pyxattr (0.6.0-1) UNRELEASED; urgency=medium +python-pyxattr (0.6.0-1) unstable; urgency=medium * New upstream release, mainly fixing a race condition when listing/reading attributes during concurrent modifications (which @@ -6,7 +6,7 @@ python-pyxattr (0.6.0-1) UNRELEASED; urgency=medium impacting Debian) * Fixup autopkgtests to also work when building the packages - -- Iustin Pop Mon, 23 Jan 2017 23:24:49 +0100 + -- Iustin Pop Mon, 23 Jan 2017 23:35:45 +0100 python-pyxattr (0.5.6-1) unstable; urgency=medium -- 2.39.2 From 49bd681fa7c67ac5ecefd480a4b5db8b71c102d6 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Thu, 26 Jan 2017 00:33:05 +0100 Subject: [PATCH 06/16] 6.0.0-2 release fixing kfreebsd/hurd builds --- debian/changelog | 9 +++++++ debian/patches/fixup-non-linux.patch | 35 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 45 insertions(+) create mode 100644 debian/patches/fixup-non-linux.patch diff --git a/debian/changelog b/debian/changelog index d88bbe3..d7160ec 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +python-pyxattr (0.6.0-2) unstable; urgency=medium + + * Fixup building on hurd and kfreebsd; while neither of these + architectures have proper xattr support, the package was present and + importable on them previously (and hurd has some very basic support + for xattrs) + + -- Iustin Pop Thu, 26 Jan 2017 00:30:55 +0100 + python-pyxattr (0.6.0-1) unstable; urgency=medium * New upstream release, mainly fixing a race condition when diff --git a/debian/patches/fixup-non-linux.patch b/debian/patches/fixup-non-linux.patch new file mode 100644 index 0000000..2834c83 --- /dev/null +++ b/debian/patches/fixup-non-linux.patch @@ -0,0 +1,35 @@ +Author: iustin@debian.org +Subject: Fix building on hurd and kfreebsd +Description: Even though both of these arches don't have proper xattr support, + the package previously built on them. +--- a/setup.py ++++ b/setup.py +@@ -14,7 +14,7 @@ + author = "Iustin Pop" + author_email = "iustin@k1024.org" + libraries = [] +-if platform.system() == 'Linux': ++if platform.system() != 'Darwin': + libraries.append("attr") + macros = [ + ("_XATTR_VERSION", '"%s"' % version), +--- a/xattr.c ++++ b/xattr.c +@@ -25,7 +25,7 @@ + #include + #if defined(__APPLE__) + #include +-#elif defined(__linux__) ++#else + #include + #endif + #include +@@ -251,7 +251,7 @@ + return fremovexattr(filedes, name, 0); + } + +-#elif defined(__linux__) ++#else + #define _listxattr(path, list, size) listxattr(path, list, size) + #define _llistxattr(path, list, size) llistxattr(path, list, size) + #define _flistxattr(fd, list, size) flistxattr(fd, list, size) diff --git a/debian/patches/series b/debian/patches/series index 6712e7b..3f233c4 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1 +1,2 @@ sphinx-1.3.patch +fixup-non-linux.patch -- 2.39.2 From 9f2a63b26883474d4625157f539375f4d4d27c9e Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Fri, 26 Oct 2018 00:53:06 +0200 Subject: [PATCH 07/16] Switch Vcs-* fields to Salsa --- debian/control | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index 596182f..905e7e1 100644 --- a/debian/control +++ b/debian/control @@ -9,8 +9,8 @@ Build-Depends: debhelper (>= 9), dh-python, python-sphinx (>= 1.0.7+dfsg) | python3-sphinx Standards-Version: 3.9.8 Homepage: http://pyxattr.k1024.org/ -Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/python-pyxattr.git -Vcs-Git: https://anonscm.debian.org/git/collab-maint/python-pyxattr.git +Vcs-Browser: https://salsa.debian.org/debian/python-pyxattr +Vcs-Git: https://salsa.debian.org/debian/python-pyxattr.git X-Python-Version: >= 2.4 X-Python3-Version: >= 3.0 -- 2.39.2 From 91f27cb441cb6a6050830b87793ef29b4ce97b89 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 28 Oct 2018 19:02:48 +0100 Subject: [PATCH 08/16] New upstream version 0.6.1 --- Makefile | 23 +++++++++- NEWS | 22 ++++++++++ PKG-INFO | 2 +- README.rst | 9 ++-- doc/conf.py | 4 +- doc/news.rst | 22 ++++++++++ pyxattr.egg-info/PKG-INFO | 2 +- setup.py | 4 +- test/test_xattr.py | 88 ++++++++++++++++++++++----------------- xattr.c | 56 +++++++++++++++---------- 10 files changed, 157 insertions(+), 75 deletions(-) diff --git a/Makefile b/Makefile index 905791d..141685f 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ ALLSPHINXOPTS = -d $(DOCTREES) $(SPHINXOPTS) $(DOCDIR) MODNAME = xattr.so RSTFILES = doc/index.rst doc/module.rst NEWS README.rst doc/conf.py +PYVERS = 2.4 2.5 2.6 2.7 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 +REPS = 5 all: doc test @@ -23,7 +25,7 @@ dist: fakeroot ./setup.py sdist test: - @for ver in 2.4 2.5 2.6 2.7 3.0 3.1 3.2 3.3 3.4 3.5; do \ + @for ver in $(PYVERS); do \ for flavour in "" "-dbg"; do \ if type python$$ver$$flavour >/dev/null; then \ echo Testing with python$$ver$$flavour; \ @@ -36,6 +38,25 @@ test: pypy ./setup.py test -q; \ fi +benchmark: $(MODNAME) + @set -e; \ + TESTFILE=`mktemp`;\ + trap 'rm $$TESTFILE' EXIT; \ + for ver in $(PYVERS) ; do \ + if type python$$ver >/dev/null; then \ + echo Benchmarking with python$$ver; \ + python$$ver ./setup.py build -q; \ + echo " - set (with override)"; \ + python$$ver -m timeit -r $(REPS) -s 'import xattr' "xattr.set('$$TESTFILE', 'user.comment', 'hello')"; \ + echo " - list"; \ + python$$ver -m timeit -r $(REPS) -s 'import xattr' "xattr.list('$$TESTFILE')"; \ + echo " - get"; \ + python$$ver -m timeit -r $(REPS) -s 'import xattr' "xattr.get('$$TESTFILE', 'user.comment')"; \ + echo " - set + remove"; \ + python$$ver -m timeit -r $(REPS) -s 'import xattr' "xattr.set('$$TESTFILE', 'user.comment', 'hello'); xattr.remove('$$TESTFILE', 'user.comment')"; \ + fi; \ + done; + coverage: $(MAKE) clean $(MAKE) test CFLAGS="-coverage" diff --git a/NEWS b/NEWS index f9002c7..6bee508 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,28 @@ News ==== +Version 0.6.1 +------------- + +*released Tue, 24 Jul 2018* + +Minor bugfix, performance and compatibility release. + +* Minor compatibility fix: on Linux, drop the use of the `attr` library, + and instead switch to the glibc header `sys/xattr.h`, which is + provided for a really long time (since glibc 2.3). The formerly used + header `attr/xattr.h` has been removed from the `attr` library in + version 2.4.48. Fix provided by Lars Wendler, many thanks! +* Release the GIL when performing I/O. Patch proposed by xwhuang, many + thanks. I tested this a long while back it seemed to impact + performance on local filesystems, but upon further inspection, the + downsides are minor (between 0 and 5%, in many cases negligible). For + remote or slow filesystems, this should allow much increased + parallelism. +* Fix symlink set operation on MacOS X; bugfix provided by adamlin, much + appreciated! This also uncovered testing problems related to symlinks, + which are now fixed (the bug would be caught by the updated tests). + Version 0.6.0 ------------- diff --git a/PKG-INFO b/PKG-INFO index 0e2792c..525c3b3 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyxattr -Version: 0.6.0 +Version: 0.6.1 Summary: Filesystem extended attributes for python Home-page: http://pyxattr.k1024.org/ Author: Iustin Pop diff --git a/README.rst b/README.rst index 2844a61..033d597 100644 --- a/README.rst +++ b/README.rst @@ -5,8 +5,8 @@ This is the pyxattr module, a Python extension module which gives access to the extended attributes for filesystem objects available in some operating systems. -Downloads: go to http://pyxattr.k1024.org/downloads/. Latest -version is 0.6.0. The source repository is either at +Downloads: go to https://pyxattr.k1024.org/downloads/. Latest +version is 0.6.1. The source repository is either at http://git.k1024.org/pyxattr.git or at https://github.com/iustin/pyxattr. @@ -17,9 +17,8 @@ pyxattr has been written and tested on Linux, kernel v2.4 or later, with XFS filesystems; ext2/ext3 should work also. If any other platform implements the same behavior, pyxattr could be used. -You need to have the attr library (including development headers; most -distributions should have this, under various names) and setuptools -installed in order to build and install the module. +You need to have the setuptools tool installed in order to build and +install the module. License ------- diff --git a/doc/conf.py b/doc/conf.py index e6c994a..a2ac56a 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -48,9 +48,9 @@ copyright = u'2002, 2003, 2006, 2008, 2012, 2013, 2014, 2015, Iustin Pop' # built documents. # # The short X.Y version. -version = '0.6.0' +version = '0.6.1' # The full version, including alpha/beta/rc tags. -release = '0.6.0' +release = '0.6.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/news.rst b/doc/news.rst index f9002c7..6bee508 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -1,6 +1,28 @@ News ==== +Version 0.6.1 +------------- + +*released Tue, 24 Jul 2018* + +Minor bugfix, performance and compatibility release. + +* Minor compatibility fix: on Linux, drop the use of the `attr` library, + and instead switch to the glibc header `sys/xattr.h`, which is + provided for a really long time (since glibc 2.3). The formerly used + header `attr/xattr.h` has been removed from the `attr` library in + version 2.4.48. Fix provided by Lars Wendler, many thanks! +* Release the GIL when performing I/O. Patch proposed by xwhuang, many + thanks. I tested this a long while back it seemed to impact + performance on local filesystems, but upon further inspection, the + downsides are minor (between 0 and 5%, in many cases negligible). For + remote or slow filesystems, this should allow much increased + parallelism. +* Fix symlink set operation on MacOS X; bugfix provided by adamlin, much + appreciated! This also uncovered testing problems related to symlinks, + which are now fixed (the bug would be caught by the updated tests). + Version 0.6.0 ------------- diff --git a/pyxattr.egg-info/PKG-INFO b/pyxattr.egg-info/PKG-INFO index 0e2792c..525c3b3 100644 --- a/pyxattr.egg-info/PKG-INFO +++ b/pyxattr.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyxattr -Version: 0.6.0 +Version: 0.6.1 Summary: Filesystem extended attributes for python Home-page: http://pyxattr.k1024.org/ Author: Iustin Pop diff --git a/setup.py b/setup.py index 54259db..4cd944b 100755 --- a/setup.py +++ b/setup.py @@ -10,12 +10,10 @@ except ImportError: long_desc = """This is a C extension module for Python which implements extended attributes manipulation. It is a wrapper on top of the attr C library - see attr(5).""" -version = "0.6.0" +version = "0.6.1" author = "Iustin Pop" author_email = "iustin@k1024.org" libraries = [] -if platform.system() == 'Linux': - libraries.append("attr") macros = [ ("_XATTR_VERSION", '"%s"' % version), ("_XATTR_AUTHOR", '"%s"' % author), diff --git a/test/test_xattr.py b/test/test_xattr.py index 084b831..7b14eec 100644 --- a/test/test_xattr.py +++ b/test/test_xattr.py @@ -10,6 +10,8 @@ import errno import xattr from xattr import NS_USER, XATTR_CREATE, XATTR_REPLACE +NAMESPACE = os.environ.get("NAMESPACE", NS_USER) + if sys.hexversion >= 0x03000000: PY3K = True EMPTY_NS = bytes() @@ -29,7 +31,7 @@ else: class xattrTest(unittest.TestCase): USER_NN = "test" - USER_ATTR = NS_USER.decode() + "." + USER_NN + USER_ATTR = NAMESPACE.decode() + "." + USER_NN USER_VAL = "abc" EMPTY_VAL = "" LARGE_VAL = "x" * 2048 @@ -108,7 +110,7 @@ class xattrTest(unittest.TestCase): self.checkList(xattr.listxattr(item, symlink), []) self.assertRaises(EnvironmentError, xattr.setxattr, item, self.USER_ATTR, self.USER_VAL, - XATTR_REPLACE) + XATTR_REPLACE, symlink) try: xattr.setxattr(item, self.USER_ATTR, self.USER_VAL, 0, symlink) except IOError: @@ -121,31 +123,35 @@ class xattrTest(unittest.TestCase): return raise self.assertRaises(EnvironmentError, xattr.setxattr, item, - self.USER_ATTR, self.USER_VAL, XATTR_CREATE) + self.USER_ATTR, self.USER_VAL, XATTR_CREATE, symlink) self.checkList(xattr.listxattr(item, symlink), [self.USER_ATTR]) self.assertEqual(xattr.getxattr(item, self.USER_ATTR, symlink), self.USER_VAL) self.checkTuples(xattr.get_all(item, nofollow=symlink), [(self.USER_ATTR, self.USER_VAL)]) - xattr.removexattr(item, self.USER_ATTR) + xattr.removexattr(item, self.USER_ATTR, symlink) self.checkList(xattr.listxattr(item, symlink), []) self.checkTuples(xattr.get_all(item, nofollow=symlink), []) self.assertRaises(EnvironmentError, xattr.removexattr, - item, self.USER_ATTR) + item, self.USER_ATTR, symlink) def _checkListSetGet(self, item, symlink=False, use_ns=False): """check list, set, get operations against an item""" self.checkList(xattr.list(item, symlink), []) self.assertRaises(EnvironmentError, xattr.set, item, - self.USER_ATTR, self.USER_VAL, flags=XATTR_REPLACE) + self.USER_ATTR, self.USER_VAL, + flags=XATTR_REPLACE, + nofollow=symlink) self.assertRaises(EnvironmentError, xattr.set, item, - self.USER_NN, self.USER_VAL, flags=XATTR_REPLACE, - namespace=NS_USER) + self.USER_NN, self.USER_VAL, + flags=XATTR_REPLACE, + namespace=NAMESPACE, + nofollow=symlink) try: if use_ns: xattr.set(item, self.USER_NN, self.USER_VAL, - namespace=NS_USER, + namespace=NAMESPACE, nofollow=symlink) else: xattr.set(item, self.USER_ATTR, self.USER_VAL, @@ -160,36 +166,40 @@ class xattrTest(unittest.TestCase): return raise self.assertRaises(EnvironmentError, xattr.set, item, - self.USER_ATTR, self.USER_VAL, flags=XATTR_CREATE) + self.USER_ATTR, self.USER_VAL, + flags=XATTR_CREATE, + nofollow=symlink) self.assertRaises(EnvironmentError, xattr.set, item, self.USER_NN, self.USER_VAL, - flags=XATTR_CREATE, namespace=NS_USER) + flags=XATTR_CREATE, + namespace=NAMESPACE, + nofollow=symlink) self.checkList(xattr.list(item, nofollow=symlink), [self.USER_ATTR]) self.checkList(xattr.list(item, nofollow=symlink, - namespace=EMPTY_NS), - [self.USER_ATTR]) - self.assertEqual(xattr.list(item, namespace=NS_USER, nofollow=symlink), + namespace=EMPTY_NS), + [self.USER_ATTR]) + self.assertEqual(xattr.list(item, namespace=NAMESPACE, nofollow=symlink), [self.USER_NN]) self.assertEqual(xattr.get(item, self.USER_ATTR, nofollow=symlink), self.USER_VAL) self.assertEqual(xattr.get(item, self.USER_NN, nofollow=symlink, - namespace=NS_USER), self.USER_VAL) + namespace=NAMESPACE), self.USER_VAL) self.checkTuples(xattr.get_all(item, nofollow=symlink), [(self.USER_ATTR, self.USER_VAL)]) self.assertEqual(xattr.get_all(item, nofollow=symlink, - namespace=NS_USER), + namespace=NAMESPACE), [(self.USER_NN, self.USER_VAL)]) if use_ns: - xattr.remove(item, self.USER_NN, namespace=NS_USER) + xattr.remove(item, self.USER_NN, namespace=NAMESPACE, nofollow=symlink) else: - xattr.remove(item, self.USER_ATTR) - self.checkList(xattr.list(item, symlink), []) + xattr.remove(item, self.USER_ATTR, nofollow=symlink) + self.checkList(xattr.list(item, nofollow=symlink), []) self.checkTuples(xattr.get_all(item, nofollow=symlink), []) self.assertRaises(EnvironmentError, xattr.remove, item, self.USER_ATTR, nofollow=symlink) self.assertRaises(EnvironmentError, xattr.remove, item, - self.USER_NN, namespace=NS_USER, nofollow=symlink) + self.USER_NN, namespace=NAMESPACE, nofollow=symlink) def testNoXattrDeprecated(self): """test no attributes (deprecated functions)""" @@ -214,27 +224,27 @@ class xattrTest(unittest.TestCase): """test no attributes""" fh, fname = self._getfile() self.checkList(xattr.list(fname), []) - self.assertEqual(xattr.list(fname, namespace=NS_USER), []) + self.assertEqual(xattr.list(fname, namespace=NAMESPACE), []) self.checkTuples(xattr.get_all(fname), []) - self.assertEqual(xattr.get_all(fname, namespace=NS_USER), []) + self.assertEqual(xattr.get_all(fname, namespace=NAMESPACE), []) self.assertRaises(EnvironmentError, xattr.get, fname, - self.USER_NN, namespace=NS_USER) + self.USER_NN, namespace=NAMESPACE) dname = self._getdir() self.checkList(xattr.list(dname), []) - self.assertEqual(xattr.list(dname, namespace=NS_USER), []) + self.assertEqual(xattr.list(dname, namespace=NAMESPACE), []) self.checkTuples(xattr.get_all(dname), []) - self.assertEqual(xattr.get_all(dname, namespace=NS_USER), []) + self.assertEqual(xattr.get_all(dname, namespace=NAMESPACE), []) self.assertRaises(EnvironmentError, xattr.get, dname, - self.USER_NN, namespace=NS_USER) + self.USER_NN, namespace=NAMESPACE) _, sname = self._getsymlink() self.checkList(xattr.list(sname, nofollow=True), []) self.assertEqual(xattr.list(sname, nofollow=True, - namespace=NS_USER), []) + namespace=NAMESPACE), []) self.checkTuples(xattr.get_all(sname, nofollow=True), []) self.assertEqual(xattr.get_all(sname, nofollow=True, - namespace=NS_USER), []) + namespace=NAMESPACE), []) self.assertRaises(EnvironmentError, xattr.get, sname, - self.USER_NN, namespace=NS_USER, nofollow=True) + self.USER_NN, namespace=NAMESPACE, nofollow=True) def testFileByNameDeprecated(self): """test set and retrieve one attribute by file name (deprecated)""" @@ -297,17 +307,17 @@ class xattrTest(unittest.TestCase): self.checkList(xattr.list(fname), []) xattr.set(fname, self.USER_ATTR, self.USER_VAL) self.checkList(xattr.list(fh), [self.USER_ATTR]) - self.assertEqual(xattr.list(fh, namespace=NS_USER), [self.USER_NN]) + self.assertEqual(xattr.list(fh, namespace=NAMESPACE), [self.USER_NN]) self.assertEqual(xattr.get(fo, self.USER_ATTR), self.USER_VAL) - self.assertEqual(xattr.get(fo, self.USER_NN, namespace=NS_USER), + self.assertEqual(xattr.get(fo, self.USER_NN, namespace=NAMESPACE), self.USER_VAL) self.checkTuples(xattr.get_all(fo), [(self.USER_ATTR, self.USER_VAL)]) - self.assertEqual(xattr.get_all(fo, namespace=NS_USER), + self.assertEqual(xattr.get_all(fo, namespace=NAMESPACE), [(self.USER_NN, self.USER_VAL)]) self.checkTuples(xattr.get_all(fname), [(self.USER_ATTR, self.USER_VAL)]) - self.assertEqual(xattr.get_all(fname, namespace=NS_USER), + self.assertEqual(xattr.get_all(fname, namespace=NAMESPACE), [(self.USER_NN, self.USER_VAL)]) fo.close() @@ -371,12 +381,12 @@ class xattrTest(unittest.TestCase): BINVAL = BINVAL.encode() xattr.set(fname, self.USER_ATTR, BINVAL) self.checkList(xattr.list(fname), [self.USER_ATTR]) - self.assertEqual(xattr.list(fname, namespace=NS_USER), [self.USER_NN]) + self.assertEqual(xattr.list(fname, namespace=NAMESPACE), [self.USER_NN]) self.assertEqual(xattr.get(fname, self.USER_ATTR), BINVAL) self.assertEqual(xattr.get(fname, self.USER_NN, - namespace=NS_USER), BINVAL) + namespace=NAMESPACE), BINVAL) self.checkTuples(xattr.get_all(fname), [(self.USER_ATTR, BINVAL)]) - self.assertEqual(xattr.get_all(fname, namespace=NS_USER), + self.assertEqual(xattr.get_all(fname, namespace=NAMESPACE), [(self.USER_NN, BINVAL)]) xattr.remove(fname, self.USER_ATTR) @@ -402,15 +412,15 @@ class xattrTest(unittest.TestCase): for i in range(self.MANYOPS_COUNT): self.checkList(xattr.list(fh), VL) self.checkList(xattr.list(fh, namespace=EMPTY_NS), VL) - self.assertEqual(xattr.list(fh, namespace=NS_USER), VN) + self.assertEqual(xattr.list(fh, namespace=NAMESPACE), VN) for i in range(self.MANYOPS_COUNT): self.assertEqual(xattr.get(fh, self.USER_ATTR), self.USER_VAL) - self.assertEqual(xattr.get(fh, self.USER_NN, namespace=NS_USER), + self.assertEqual(xattr.get(fh, self.USER_NN, namespace=NAMESPACE), self.USER_VAL) for i in range(self.MANYOPS_COUNT): self.checkTuples(xattr.get_all(fh), [(self.USER_ATTR, self.USER_VAL)]) - self.assertEqual(xattr.get_all(fh, namespace=NS_USER), + self.assertEqual(xattr.get_all(fh, namespace=NAMESPACE), [(self.USER_NN, self.USER_VAL)]) def testNoneNamespace(self): diff --git a/xattr.c b/xattr.c index 111cec1..0087b7e 100644 --- a/xattr.c +++ b/xattr.c @@ -23,10 +23,8 @@ #define PY_SSIZE_T_CLEAN #include -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(__linux__) #include -#elif defined(__linux__) -#include #endif #include @@ -235,7 +233,7 @@ static inline int _setxattr(const char *path, const char *name, const void *valu return setxattr(path, name, value, size, 0, flags); } static inline int _lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) { - return setxattr(path, name, value, size, 0, flags & XATTR_NOFOLLOW); + return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW); } static inline int _fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) { return fsetxattr(filedes, name, value, size, 0, flags); @@ -275,41 +273,58 @@ typedef ssize_t (*buf_getter)(target_t *tgt, const char *name, static ssize_t _list_obj(target_t *tgt, const char *unused, void *list, size_t size) { + ssize_t ret; + + Py_BEGIN_ALLOW_THREADS; if(tgt->type == T_FD) - return _flistxattr(tgt->fd, list, size); + ret = _flistxattr(tgt->fd, list, size); else if (tgt->type == T_LINK) - return _llistxattr(tgt->name, list, size); + ret = _llistxattr(tgt->name, list, size); else - return _listxattr(tgt->name, list, size); + ret = _listxattr(tgt->name, list, size); + Py_END_ALLOW_THREADS; + return ret; } static ssize_t _get_obj(target_t *tgt, const char *name, void *value, size_t size) { + ssize_t ret; + Py_BEGIN_ALLOW_THREADS; if(tgt->type == T_FD) - return _fgetxattr(tgt->fd, name, value, size); + ret = _fgetxattr(tgt->fd, name, value, size); else if (tgt->type == T_LINK) - return _lgetxattr(tgt->name, name, value, size); + ret = _lgetxattr(tgt->name, name, value, size); else - return _getxattr(tgt->name, name, value, size); + ret = _getxattr(tgt->name, name, value, size); + Py_END_ALLOW_THREADS; + return ret; } static int _set_obj(target_t *tgt, const char *name, const void *value, size_t size, int flags) { + int ret; + Py_BEGIN_ALLOW_THREADS; if(tgt->type == T_FD) - return _fsetxattr(tgt->fd, name, value, size, flags); + ret = _fsetxattr(tgt->fd, name, value, size, flags); else if (tgt->type == T_LINK) - return _lsetxattr(tgt->name, name, value, size, flags); + ret = _lsetxattr(tgt->name, name, value, size, flags); else - return _setxattr(tgt->name, name, value, size, flags); + ret = _setxattr(tgt->name, name, value, size, flags); + Py_END_ALLOW_THREADS; + return ret; } static int _remove_obj(target_t *tgt, const char *name) { + int ret; + Py_BEGIN_ALLOW_THREADS; if(tgt->type == T_FD) - return _fremovexattr(tgt->fd, name); + ret = _fremovexattr(tgt->fd, name); else if (tgt->type == T_LINK) - return _lremovexattr(tgt->name, name); + ret = _lremovexattr(tgt->name, name); else - return _removexattr(tgt->name, name); + ret = _removexattr(tgt->name, name); + Py_END_ALLOW_THREADS; + return ret; } /* Perform a get/list operation with appropriate buffer size, @@ -642,11 +657,7 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) /* Now retrieve the attribute value */ nval = _generic_get(_get_obj, &tgt, s, &buf_val, &nalloc, &io_errno); if (nval == -1) { - if ( -#ifdef ENODATA - io_errno == ENODATA || -#endif - io_errno == ENOATTR) { + if (io_errno == ENODATA) { PyErr_Clear(); continue; } else { @@ -1173,8 +1184,7 @@ static char __xattr_doc__[] = \ " a :exc:`EnvironmentError`; under\n" " Linux, the following ``errno`` values are used:\n" "\n" - " - ``ENOATTR`` and ``ENODATA`` mean that the attribute name is\n" - " invalid\n" + " - ``ENODATA`` means that the attribute name is\n invalid\n" " - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n" " support extended attributes, or that the namespace is invalid\n" " - ``E2BIG`` mean that the attribute value is too big\n" -- 2.39.2 From 6a9d6a4dad1d42d6683d1bcc1156f8c960d0d258 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 28 Oct 2018 19:13:53 +0100 Subject: [PATCH 09/16] Drop the fix-non-linux-patch, no longer needed MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit … as the attr library is not needed anymore (at all). --- debian/patches/fixup-non-linux.patch | 35 ---------------------------- debian/patches/series | 1 - 2 files changed, 36 deletions(-) delete mode 100644 debian/patches/fixup-non-linux.patch diff --git a/debian/patches/fixup-non-linux.patch b/debian/patches/fixup-non-linux.patch deleted file mode 100644 index 2834c83..0000000 --- a/debian/patches/fixup-non-linux.patch +++ /dev/null @@ -1,35 +0,0 @@ -Author: iustin@debian.org -Subject: Fix building on hurd and kfreebsd -Description: Even though both of these arches don't have proper xattr support, - the package previously built on them. ---- a/setup.py -+++ b/setup.py -@@ -14,7 +14,7 @@ - author = "Iustin Pop" - author_email = "iustin@k1024.org" - libraries = [] --if platform.system() == 'Linux': -+if platform.system() != 'Darwin': - libraries.append("attr") - macros = [ - ("_XATTR_VERSION", '"%s"' % version), ---- a/xattr.c -+++ b/xattr.c -@@ -25,7 +25,7 @@ - #include - #if defined(__APPLE__) - #include --#elif defined(__linux__) -+#else - #include - #endif - #include -@@ -251,7 +251,7 @@ - return fremovexattr(filedes, name, 0); - } - --#elif defined(__linux__) -+#else - #define _listxattr(path, list, size) listxattr(path, list, size) - #define _llistxattr(path, list, size) llistxattr(path, list, size) - #define _flistxattr(fd, list, size) flistxattr(fd, list, size) diff --git a/debian/patches/series b/debian/patches/series index 3f233c4..6712e7b 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,2 +1 @@ sphinx-1.3.patch -fixup-non-linux.patch -- 2.39.2 From d7ef529272cb22fe7f0d5c8b7002cb7a329c040e Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 28 Oct 2018 19:16:45 +0100 Subject: [PATCH 10/16] Drop the attr library --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 905e7e1..c3f4ecc 100644 --- a/debian/control +++ b/debian/control @@ -5,7 +5,7 @@ Maintainer: Iustin Pop Build-Depends: debhelper (>= 9), dh-python, python (>= 2.6.6-3~), python-all-dev (>= 2.6.6-3~), python-all-dbg, python3-all, python3-all-dev, python3-all-dbg, - libattr1-dev, python-setuptools, python3-setuptools, + python-setuptools, python3-setuptools, python-sphinx (>= 1.0.7+dfsg) | python3-sphinx Standards-Version: 3.9.8 Homepage: http://pyxattr.k1024.org/ -- 2.39.2 From d83a7463c97692da71deedac4e1327e39015dab9 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 28 Oct 2018 19:47:18 +0100 Subject: [PATCH 11/16] Switch from -dbg to -dbgsym packages Yay for simplicity! --- debian/control | 30 +----------------------------- debian/rules | 6 ++---- 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/debian/control b/debian/control index c3f4ecc..4e0f4b6 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: python-pyxattr Section: python Priority: optional Maintainer: Iustin Pop -Build-Depends: debhelper (>= 9), dh-python, +Build-Depends: debhelper (>= 9.20160114), dh-python, python (>= 2.6.6-3~), python-all-dev (>= 2.6.6-3~), python-all-dbg, python3-all, python3-all-dev, python3-all-dbg, python-setuptools, python3-setuptools, @@ -26,20 +26,6 @@ Description: module for manipulating filesystem extended attributes . This package contains the library for Python 2. -Package: python-pyxattr-dbg -Section: debug -Priority: extra -Architecture: any -Depends: python-pyxattr (= ${binary:Version}), python-dbg, ${shlibs:Depends}, - ${misc:Depends}, ${python:Depends} -Provides: ${python:Provides} -Description: module for manipulating filesystem extended attributes (debug extension) - pyxattr is a Python interface to the libattr library. It allows - manipulation of the filesystem extended attributes present in some - operating systems (GNU/Linux included). - . - This package contains the library for the Python 2 debug interpreter. - Package: python3-pyxattr Architecture: any Depends: ${python3:Depends}, ${shlibs:Depends}, ${misc:Depends} @@ -52,20 +38,6 @@ Description: module for manipulating filesystem extended attributes (Python3) . This package contains the library for Python 3. -Package: python3-pyxattr-dbg -Section: debug -Priority: extra -Architecture: any -Depends: python3-pyxattr (= ${binary:Version}), python3-dbg, - ${shlibs:Depends}, ${misc:Depends}, ${python3:Depends} -Provides: ${python3:Provides} -Description: module for manipulating filesystem extended attributes (Python3 debug version) - pyxattr is a Python interface to the libattr library. It allows - manipulation of the filesystem extended attributes present in some - operating systems (GNU/Linux included). - . - This package contains the library for the Python 3 debug interpreter. - Package: python-pyxattr-doc Architecture: all Section: doc diff --git a/debian/rules b/debian/rules index 0efa0b6..39bc3c1 100755 --- a/debian/rules +++ b/debian/rules @@ -27,12 +27,10 @@ override_dh_auto_clean: $(MAKE) clean override_dh_strip: - dh_strip -ppython-pyxattr --dbg-package=python-pyxattr-dbg - dh_strip -ppython3-pyxattr --dbg-package=python3-pyxattr-dbg + dh_strip -ppython-pyxattr --dbgsym-migration='python-pyxattr-dbg (<<0.6.1-1~)' + dh_strip -ppython3-pyxattr --dbgsym-migration='python3-pyxattr-dbg (<<0.6.1-1~)' override_dh_installdocs: - dh_installdocs --link-doc=python-pyxattr -ppython-pyxattr-dbg - dh_installdocs --link-doc=python3-pyxattr -ppython3-pyxattr-dbg dh_installdocs -A --remaining-packages README.rst override_dh_installchangelogs: -- 2.39.2 From 5ad89a181dc7d284a4b9a5d100457072daf638e8 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 28 Oct 2018 19:48:47 +0100 Subject: [PATCH 12/16] Switch watch file to https --- debian/watch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/watch b/debian/watch index a11cc77..166f285 100644 --- a/debian/watch +++ b/debian/watch @@ -3,4 +3,4 @@ version=3 # the package is located on my own site, github deprecated downloads opts=pgpsigurlmangle=s/$/.asc/ \ - http://pyxattr.k1024.org/downloads/pyxattr-(.+).tar.gz + https://pyxattr.k1024.org/downloads/pyxattr-(.+).tar.gz -- 2.39.2 From 4bbcbb6050a9b99ccb62a0d93540363bb6dabcec Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 28 Oct 2018 19:49:07 +0100 Subject: [PATCH 13/16] Switch homepage field to https --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 4e0f4b6..480a66d 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ Build-Depends: debhelper (>= 9.20160114), dh-python, python-setuptools, python3-setuptools, python-sphinx (>= 1.0.7+dfsg) | python3-sphinx Standards-Version: 3.9.8 -Homepage: http://pyxattr.k1024.org/ +Homepage: https://pyxattr.k1024.org/ Vcs-Browser: https://salsa.debian.org/debian/python-pyxattr Vcs-Git: https://salsa.debian.org/debian/python-pyxattr.git X-Python-Version: >= 2.4 -- 2.39.2 From b419fe96c2b89f9df2d70452e86b2da55b48a8b9 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 28 Oct 2018 19:58:36 +0100 Subject: [PATCH 14/16] Drop obsolete X-Python-Version fields Py2 is obsolete (only one supported anyway), and upstream supports all Py3. --- debian/control | 2 -- 1 file changed, 2 deletions(-) diff --git a/debian/control b/debian/control index 480a66d..34351a7 100644 --- a/debian/control +++ b/debian/control @@ -11,8 +11,6 @@ Standards-Version: 3.9.8 Homepage: https://pyxattr.k1024.org/ Vcs-Browser: https://salsa.debian.org/debian/python-pyxattr Vcs-Git: https://salsa.debian.org/debian/python-pyxattr.git -X-Python-Version: >= 2.4 -X-Python3-Version: >= 3.0 Package: python-pyxattr Architecture: any -- 2.39.2 From 3a30b86d5f6f8b383ee98bfbc112d50f30d88a9d Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 28 Oct 2018 20:10:47 +0100 Subject: [PATCH 15/16] Bump standards version --- debian/control | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 34351a7..43cb788 100644 --- a/debian/control +++ b/debian/control @@ -7,10 +7,11 @@ Build-Depends: debhelper (>= 9.20160114), dh-python, python3-all, python3-all-dev, python3-all-dbg, python-setuptools, python3-setuptools, python-sphinx (>= 1.0.7+dfsg) | python3-sphinx -Standards-Version: 3.9.8 +Standards-Version: 4.2.1 Homepage: https://pyxattr.k1024.org/ Vcs-Browser: https://salsa.debian.org/debian/python-pyxattr Vcs-Git: https://salsa.debian.org/debian/python-pyxattr.git +Rules-Requires-Root: no Package: python-pyxattr Architecture: any -- 2.39.2 From 395432d4627e28a309925bed47e75fa2411569b0 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 28 Oct 2018 20:10:58 +0100 Subject: [PATCH 16/16] Remove suggests on now-removed dbg package MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Oops… --- debian/control | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index 43cb788..6f5287d 100644 --- a/debian/control +++ b/debian/control @@ -17,7 +17,7 @@ Package: python-pyxattr Architecture: any Depends: ${python:Depends}, ${shlibs:Depends}, ${misc:Depends} Provides: ${python:Provides} -Suggests: python-pyxattr-dbg, python-pyxattr-doc +Suggests: python-pyxattr-doc Description: module for manipulating filesystem extended attributes pyxattr is a Python interface to the libattr library. It allows manipulation of the filesystem extended attributes present in some @@ -29,7 +29,7 @@ Package: python3-pyxattr Architecture: any Depends: ${python3:Depends}, ${shlibs:Depends}, ${misc:Depends} Provides: ${python3:Provides} -Suggests: python3-pyxattr-dbg, python-pyxattr-doc +Suggests: python-pyxattr-doc Description: module for manipulating filesystem extended attributes (Python3) pyxattr is a Python interface to the libattr library. It allows manipulation of the filesystem extended attributes present in some -- 2.39.2