From 6d6ba57224698c64b05bf3b1dfe553d216a88886 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 23 Apr 2023 22:35:38 +0200 Subject: [PATCH] New upstream version 0.7.0 --- MANIFEST.in | 4 +- Makefile | 15 +- NEWS => NEWS.md | 99 +++++++------ PKG-INFO | 15 +- README.md | 19 ++- SECURITY.md | 16 +++ acl.c | 253 +++++++++++++--------------------- doc/conf.py | 12 +- doc/contributing.md | 65 +++++++++ doc/index.rst | 4 +- doc/news.md | 191 +++++++++++++++++++++++++ doc/readme.md | 72 ++++++++++ doc/security.md | 16 +++ pylibacl.egg-info/PKG-INFO | 15 +- pylibacl.egg-info/SOURCES.txt | 7 +- setup.py | 4 +- tests/test_acls.py | 56 +++++++- 17 files changed, 609 insertions(+), 254 deletions(-) rename NEWS => NEWS.md (75%) create mode 100644 SECURITY.md create mode 100644 doc/contributing.md create mode 100644 doc/news.md create mode 100644 doc/readme.md create mode 100644 doc/security.md diff --git a/MANIFEST.in b/MANIFEST.in index 9c9a87d..a0863f6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,11 +1,13 @@ include COPYING include Makefile -include NEWS +include NEWS.md include README.rst +include SECURITY.md include acl.c include setup.cfg include doc/conf.py include doc/*.rst +include doc/*.md include tests/*.py include py.typed include posix1e.pyi diff --git a/Makefile b/Makefile index 979f428..ffa6398 100644 --- a/Makefile +++ b/Makefile @@ -5,30 +5,24 @@ DOCDIR = doc DOCHTML = $(DOCDIR)/html DOCTREES = $(DOCDIR)/doctrees ALLSPHINXOPTS = -d $(DOCTREES) $(SPHINXOPTS) $(DOCDIR) -VERSION = 0.6.0 +VERSION = 0.7.0 FULLVER = pylibacl-$(VERSION) DISTFILE = $(FULLVER).tar.gz MODNAME = posix1e.so -RSTFILES = doc/index.rst doc/module.rst doc/news.rst doc/readme.md doc/conf.py +DOCFILES = doc/index.rst doc/module.rst doc/news.md doc/readme.md doc/conf.py all: doc test $(MODNAME): acl.c $(PYTHON) ./setup.py build_ext --inplace -$(DOCHTML)/index.html: $(MODNAME) $(RSTFILES) acl.c +$(DOCHTML)/index.html: $(MODNAME) $(DOCFILES) acl.c $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(DOCHTML) touch $@ doc: $(DOCHTML)/index.html -doc/readme.md: README.md - ln -s ../README.md doc/readme.md - -doc/news.rst: NEWS - ln -s ../NEWS doc/news.rst - dist: fakeroot $(PYTHON) ./setup.py sdist @@ -42,7 +36,7 @@ distcheck: dist test: @set -e; \ - for ver in 3.4 3.5 3.6 3.7 3.8 3.9; do \ + for ver in 3.7 3.8 3.9 3.10 3.11 3.12; do \ for flavour in "" "-dbg"; do \ if type python$$ver$$flavour >/dev/null; then \ echo Testing with python$$ver$$flavour; \ @@ -76,7 +70,6 @@ coverage: clean: rm -rf $(DOCHTML) $(DOCTREES) - rm -f doc/readme.md doc/news.rst rm -f $(MODNAME) rm -f *.so rm -rf build diff --git a/NEWS b/NEWS.md similarity index 75% rename from NEWS rename to NEWS.md index 2e21058..ca55b2d 100644 --- a/NEWS +++ b/NEWS.md @@ -1,10 +1,25 @@ -News -==== +# News -Version 0.6.0 -------------- +## Version 0.7.0 -*Sun, 29 Nov 2020* +*released Sun, 23 Apr 2023* + +Important: Python 3.7 is the minimum supported version, due to +difficulty of testing old releases, and the fact that everything older +has been deprecated a long time ago (e.g. 3.6 at the end of 2021). + +Otherwise, a minor release: + +- Improve error handling in some corner cases (not expected to have + any real-life impact, but who knows). +- Improved testing coverage and test infrastructure. +- Modernise parts of the C code based on recent Python version + guidelines. +- Add a simple security policy and contribution guidelines. + +## Version 0.6.0 + +*released Sun, 29 Nov 2020* Major release removing Python 2 support. This allow both code cleanup and new features, such as: @@ -52,8 +67,7 @@ Minor improvements: - The test suite has changed to `pytest`, which allows increased coverage via parameterisation. -Version 0.5.4 -------------- +## Version 0.5.4 *released Thu, 14 Nov 2019* @@ -69,8 +83,7 @@ Maintenance release: - Drop support (well, drop testing) for Python lower than 2.7. - Minor documentation improvements (closes #9, #12). -Version 0.5.3 -------------- +## Version 0.5.3 *released Thu, 30 Apr 2015* @@ -82,15 +95,13 @@ FreeBSD fixes: with regards to invalid ACLs (which we do exercise in the test suite), thanks again to Garret for the bug reports. -Version 0.5.2 -------------- +## Version 0.5.2 *released Sat, 24 May 2014* No visible changes release: just fix tests when running under pypy. -Version 0.5.1 -------------- +## Version 0.5.1 *released Sun, 13 May 2012* @@ -106,26 +117,23 @@ docstrings. Project reorganisation: the project home page has been moved from SourceForge to GitHub. -Version 0.5 ------------ +## Version 0.5 *released Sun, 27 Dec 2009* Added support for Python 3.x and improved support for Unicode filenames. -Version 0.4 ------------ +## Version 0.4 *released Sat, 28 Jun 2008* -License -~~~~~~~ +### License + Starting with this version, pylibacl is licensed under LGPL 2.1, Febryary 1999 or any later versions (see README.rst and COPYING). -Linux support -~~~~~~~~~~~~~ +### Linux support A few more Linux-specific functions: @@ -136,57 +144,48 @@ A few more Linux-specific functions: - add the acl_extended(...) function, which will check if an fd or path has an extended ACL -FreeBSD support -~~~~~~~~~~~~~~~ +### FreeBSD support FreeBSD 7.x will have almost all the acl manipulation functions that Linux has, with the exception of __getstate__/__setstate__. As a workaround, use the str() and ACL(text=...) methods to pass around textual representations. -Interface -~~~~~~~~~ +### Interface At module level there are now a few constants exported for easy-checking at runtime what features have been compiled in: -- HAS_ACL_FROM_MODE, denoting whether the ACL constructor supports the - mode=0xxx parameter +- `HAS_ACL_FROM_MODE`, denoting whether the ACL constructor supports + the `mode=0xxx` parameter -- HAS_ACL_CHECK, denoting whether ACL instances support the check() - method +- `HAS_ACL_CHECK`, denoting whether ACL instances support the + `check()` method -- HAS_ACL_ENTRY, denoting whether ACL manipulation is possible and the - Entry and Permset classes are available +- `HAS_ACL_ENTRY`, denoting whether ACL manipulation is possible and + the Entry and Permset classes are available -- HAS_EXTENEDED_CHECK, denoting whether the acl_extended function is - supported +- `HAS_EXTENEDED_CHECK`, denoting whether the `acl_extended()` + function is supported -- HAS_EQUIV_MODE, denoting whether ACL instances support the - equiv_mode() method +- `HAS_EQUIV_MODE`, denoting whether ACL instances support the + `equiv_mode()` method -Internals -~~~~~~~~~ +### Internals Many functions have now unittests, which is a good thing. -Version 0.3 ------------ +## Version 0.3 *released Sun, 21 Oct 2007* -Linux support -~~~~~~~~~~~~~ +### Linux support Under Linux, implement more functions from libacl: -- add ACL(mode=...), implementing acl_from_mode -- add ACL().to_any_text, implementing acl_to_any_text -- add ACL comparison, using acl_cmp -- add ACL().check, which is a more descriptive function than validate - -.. Local Variables: -.. mode: rst -.. fill-column: 72 -.. End: +- add `ACL(mode=...)`, implementing `acl_from_mode`. +- add `ACL.to_any_text()`, implementing `acl_to_any_text`. +- add ACL comparison, using `acl_cmp`. +- add `ACL.check()`, which is a more descriptive function than + validate. diff --git a/PKG-INFO b/PKG-INFO index 3f83b9c..8fe963f 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,16 +1,12 @@ -Metadata-Version: 1.2 +Metadata-Version: 2.1 Name: pylibacl -Version: 0.6.0 +Version: 0.7.0 Summary: POSIX.1e ACLs for python Home-page: https://pylibacl.k1024.org/ Author: Iustin Pop Author-email: iustin@k1024.org License: LGPL Project-URL: Bug Tracker, https://github.com/iustin/pylibacl/issues -Description: This is a C extension module for Python which - implements POSIX ACLs manipulation. It is a wrapper on top - of the systems's acl C library - see acl(5). -Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) @@ -21,4 +17,9 @@ Classifier: Operating System :: POSIX :: BSD :: FreeBSD Classifier: Operating System :: POSIX :: Linux Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Filesystems -Requires-Python: >=3.4 +Requires-Python: >=3.7 +License-File: COPYING + +This is a C extension module for Python which +implements POSIX ACLs manipulation. It is a wrapper on top +of the systems's acl C library - see acl(5). diff --git a/README.md b/README.md index bd4c37c..c8e32c3 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,20 @@ # pylibacl -This is a Python 3.4+ extension module allows you to manipulate the +This is a Python 3.7+ extension module allows you to manipulate the POSIX.1e Access Control Lists present in some OS/file-systems combinations. -Downloads: go to . Latest version -is 0.6.0. The source repository is either at +Downloads: go to . Latest +version is 0.7.0. The source repository is either at or at . For any issues, please file bugs at . -[![Travis](https://img.shields.io/travis/iustin/pylibacl)](https://travis-ci.org/iustin/pylibacl) +See the `CONTRIBUTING.md` file for details on how to contribute. + +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/iustin/pylibacl/ci.yml?branch=main)](https://github.com/iustin/pylibacl/actions/workflows/ci.yml) [![Codecov](https://img.shields.io/codecov/c/github/iustin/pylibacl)](https://codecov.io/gh/iustin/pylibacl) [![Read the Docs](https://img.shields.io/readthedocs/pylibacl)](http://pylibacl.readthedocs.io/en/latest/?badge=latest) [![GitHub issues](https://img.shields.io/github/issues/iustin/pylibacl)](https://github.com/iustin/pylibacl/issues) @@ -33,7 +35,8 @@ FreeBSD 7 also has quite good support. If any other platform implements the POSIX.1e draft, pylibacl can be used. I heard that Solaris does, but I can't test it. -- Python 3.4 or newer. Python 2.4+ was supported in the 0.5.x branch. +- Python 3.7 or newer. Python 2.4+ was supported in the 0.5.x branch, + Python 3.4+ in the 0.6 branch. - Operating system: - Linux, kernel v2.4 or newer, and the libacl library and development packages (all modern distributions should have this, @@ -55,9 +58,11 @@ or: - `pkg install py37-setuptools` +## Security + +For reporting security vulnerabilities, please see `SECURITY.md`. -License -------- +## License pylibacl is Copyright (C) 2002-2009, 2012, 2014, 2015 Iustin Pop. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..dbd4a7c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,16 @@ +# Security Policy + +To report a (potential or confirmed) security issue, please email + with a description of the issue, steps to reproduce +it, affected versions, and if known, mitigations for the issue. + +Since this is a small project, there's no list of supported +versions. I will attempt to reply to reports within a working week, +and to fix and disclose vulnerabilities within 90 days, but this is +not a guarantee. + +Optionally, you can encrypt the email with my GPG key, see for details +. + +Alternatively, you can use the GitHub "Private vulnerability +reporting" functionality (but note this is beta). diff --git a/acl.c b/acl.c index f38bada..fed3eb7 100644 --- a/acl.c +++ b/acl.c @@ -116,9 +116,11 @@ static PyObject* ACL_new(PyTypeObject* type, PyObject* args, acl->acl = acl_init(0); if (acl->acl == NULL) { + /* LCOV_EXCL_START */ PyErr_SetFromErrno(PyExc_IOError); Py_DECREF(newacl); return NULL; + /* LCOV_EXCL_STOP */ } #ifdef HAVE_LEVEL2 acl->entry_id = ACL_FIRST_ENTRY; @@ -243,15 +245,12 @@ static int ACL_init(PyObject* obj, PyObject* args, PyObject *keywds) { static void ACL_dealloc(PyObject* obj) { ACL_Object *self = (ACL_Object*) obj; PyObject *err_type, *err_value, *err_traceback; - int have_error = PyErr_Occurred() ? 1 : 0; - if (have_error) - PyErr_Fetch(&err_type, &err_value, &err_traceback); + PyErr_Fetch(&err_type, &err_value, &err_traceback); if(self->acl != NULL && acl_free(self->acl) != 0) - PyErr_WriteUnraisable(obj); - if (have_error) - PyErr_Restore(err_type, err_value, err_traceback); - PyObject_DEL(self); + PyErr_WriteUnraisable(obj); /* LCOV_EXCL_LINE */ + PyErr_Restore(err_type, err_value, err_traceback); + Py_TYPE(obj)->tp_free(obj); } /* Converts the acl to a text format */ @@ -262,12 +261,16 @@ static PyObject* ACL_str(PyObject *obj) { text = acl_to_text(self->acl, NULL); if(text == NULL) { + /* LCOV_EXCL_START */ return PyErr_SetFromErrno(PyExc_IOError); + /* LCOV_EXCL_STOP */ } ret = PyUnicode_FromString(text); if(acl_free(text) != 0) { + /* LCOV_EXCL_START */ Py_XDECREF(ret); return PyErr_SetFromErrno(PyExc_IOError); + /* LCOV_EXCL_STOP */ } return ret; } @@ -317,12 +320,14 @@ static PyObject* ACL_to_any_text(PyObject *obj, PyObject *args, text = acl_to_any_text(self->acl, arg_prefix, arg_separator, arg_options); if(text == NULL) { - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ } ret = PyBytes_FromString(text); if(acl_free(text) != 0) { + /* LCOV_EXCL_START */ Py_XDECREF(ret); return PyErr_SetFromErrno(PyExc_IOError); + /* LCOV_EXCL_STOP */ } return ret; } @@ -357,7 +362,7 @@ static PyObject* ACL_check(PyObject* obj, PyObject* args) { int eindex; if((result = acl_check(self->acl, &eindex)) == -1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ if(result == 0) { Py_RETURN_FALSE; } @@ -382,7 +387,7 @@ static PyObject* ACL_richcompare(PyObject* o1, PyObject* o2, int op) { acl1 = (ACL_Object*)o1; acl2 = (ACL_Object*)o2; if((n=acl_cmp(acl1->acl, acl2->acl))==-1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ switch(op) { case Py_EQ: ret = n == 0 ? Py_True : Py_False; @@ -415,7 +420,7 @@ static PyObject* ACL_equiv_mode(PyObject* obj, PyObject* args) { mode_t mode; if(acl_equiv_mode(self->acl, &mode) == -1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ return PyLong_FromLong(mode); } #endif @@ -520,15 +525,17 @@ static PyObject* ACL_get_state(PyObject *obj, PyObject* args) { size = acl_size(self->acl); if(size == -1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ if((ret = PyBytes_FromStringAndSize(NULL, size)) == NULL) return NULL; buf = PyBytes_AsString(ret); if((nsize = acl_copy_ext(buf, self->acl, size)) == -1) { + /* LCOV_EXCL_START */ Py_DECREF(ret); return PyErr_SetFromErrno(PyExc_IOError); + /* LCOV_EXCL_STOP */ } return ret; @@ -548,10 +555,13 @@ static PyObject* ACL_set_state(PyObject *obj, PyObject* args) { if((ptr = acl_copy_int(buf)) == NULL) return PyErr_SetFromErrno(PyExc_IOError); - /* Free the old acl. Should we ignore errors here? */ if(self->acl != NULL) { - if(acl_free(self->acl) == -1) - return PyErr_SetFromErrno(PyExc_IOError); + /* Ignore errors in freeing the previous acl. We already + allocated the new acl, and the state of the previous one is + suspect if freeing failed (in Linux's libacl, deallocating + a valid ACL can't actually happen, so this path is + unlikely. */ + acl_free(self->acl); /* LCOV_EXCL_LINE */ } self->acl = ptr; @@ -582,7 +592,7 @@ static PyObject* ACL_iternext(PyObject *obj) { nerr = acl_get_entry(self->acl, self->entry_id, &the_entry_t); self->entry_id = ACL_NEXT_ENTRY; if(nerr == -1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ else if(nerr == 0) { /* Docs says this is not needed */ /*PyErr_SetObject(PyExc_StopIteration, Py_None);*/ @@ -695,8 +705,10 @@ static PyObject* ACL_append(PyObject *obj, PyObject *args) { if(oldentry != NULL) { nret = acl_copy_entry(newentry->entry, oldentry->entry); if(nret == -1) { + /* LCOV_EXCL_START */ Py_DECREF(newentry); return PyErr_SetFromErrno(PyExc_IOError); + /* LCOV_EXCL_STOP */ } } @@ -731,13 +743,17 @@ static int get_tag_qualifier(acl_entry_t entry, tag_qual *tq) { void *p; if(acl_get_tag_type(entry, &tq->tag) == -1) { + /* LCOV_EXCL_START */ PyErr_SetFromErrno(PyExc_IOError); return -1; + /* LCOV_EXCL_STOP */ } if (tq->tag == ACL_USER || tq->tag == ACL_GROUP) { if((p = acl_get_qualifier(entry)) == NULL) { + /* LCOV_EXCL_START */ PyErr_SetFromErrno(PyExc_IOError); return -1; + /* LCOV_EXCL_STOP */ } if (tq->tag == ACL_USER) { tq->uid = *(uid_t*)p; @@ -775,9 +791,11 @@ static PyObject* Entry_new(PyTypeObject* type, PyObject* args, entry = (Entry_Object*)newentry; if(acl_create_entry(&parent->acl, &entry->entry) == -1) { + /* LCOV_EXCL_START */ PyErr_SetFromErrno(PyExc_IOError); Py_DECREF(newentry); return NULL; + /* LCOV_EXCL_STOP */ } Py_INCREF(parent); entry->parent_acl = (PyObject*)parent; @@ -804,17 +822,14 @@ static int Entry_init(PyObject* obj, PyObject* args, PyObject *keywds) { static void Entry_dealloc(PyObject* obj) { Entry_Object *self = (Entry_Object*) obj; PyObject *err_type, *err_value, *err_traceback; - int have_error = PyErr_Occurred() ? 1 : 0; - if (have_error) - PyErr_Fetch(&err_type, &err_value, &err_traceback); + PyErr_Fetch(&err_type, &err_value, &err_traceback); if(self->parent_acl != NULL) { Py_DECREF(self->parent_acl); self->parent_acl = NULL; } - if (have_error) - PyErr_Restore(err_type, err_value, err_traceback); - PyObject_DEL(self); + PyErr_Restore(err_type, err_value, err_traceback); + Py_TYPE(obj)->tp_free(obj); } /* Converts the entry to a text format */ @@ -856,13 +871,16 @@ static PyObject* Entry_str(PyObject *obj) { case ACL_MASK: kind = PyUnicode_FromString("the mask"); break; - default: + default: /* LCOV_EXCL_START */ kind = PyUnicode_FromString("UNKNOWN_TAG_TYPE!"); break; + /* LCOV_EXCL_STOP */ } if (kind == NULL) { + /* LCOV_EXCL_START */ Py_DECREF(format); return NULL; + /* LCOV_EXCL_STOP */ } PyObject *ret = PyUnicode_Concat(format, kind); Py_DECREF(format); @@ -895,8 +913,10 @@ static PyObject* Entry_get_tag_type(PyObject *obj, void* arg) { acl_tag_t value; if(acl_get_tag_type(self->entry, &value) == -1) { + /* LCOV_EXCL_START */ PyErr_SetFromErrno(PyExc_IOError); return NULL; + /* LCOV_EXCL_STOP */ } return PyLong_FromLong(value); @@ -932,8 +952,10 @@ static int Entry_set_qualifier(PyObject* obj, PyObject* value, void* arg) { this ugly dance with two variables and a pointer that will point to one of them. */ if(acl_get_tag_type(self->entry, &tag) == -1) { + /* LCOV_EXCL_START */ PyErr_SetFromErrno(PyExc_IOError); return -1; + /* LCOV_EXCL_STOP */ } uid = uidgid; gid = uidgid; @@ -962,8 +984,10 @@ static int Entry_set_qualifier(PyObject* obj, PyObject* value, void* arg) { return -1; } if(acl_set_qualifier(self->entry, p) == -1) { + /* LCOV_EXCL_START */ PyErr_SetFromErrno(PyExc_IOError); return -1; + /* LCOV_EXCL_STOP */ } return 0; @@ -1028,8 +1052,10 @@ static int Entry_set_permset(PyObject* obj, PyObject* value, void* arg) { } p = (Permset_Object*)value; if(acl_set_permset(self->entry, p->permset) == -1) { + /* LCOV_EXCL_START */ PyErr_SetFromErrno(PyExc_IOError); return -1; + /* LCOV_EXCL_STOP */ } return 0; } @@ -1053,7 +1079,7 @@ static PyObject* Entry_copy(PyObject *obj, PyObject *args) { return NULL; if(acl_copy_entry(self->entry, other->entry) == -1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ Py_RETURN_NONE; } @@ -1112,17 +1138,14 @@ static int Permset_init(PyObject* obj, PyObject* args, PyObject *keywds) { static void Permset_dealloc(PyObject* obj) { Permset_Object *self = (Permset_Object*) obj; PyObject *err_type, *err_value, *err_traceback; - int have_error = PyErr_Occurred() ? 1 : 0; - if (have_error) - PyErr_Fetch(&err_type, &err_value, &err_traceback); + PyErr_Fetch(&err_type, &err_value, &err_traceback); if(self->parent_entry != NULL) { Py_DECREF(self->parent_entry); self->parent_entry = NULL; } - if (have_error) - PyErr_Restore(err_type, err_value, err_traceback); - PyObject_DEL(self); + PyErr_Restore(err_type, err_value, err_traceback); + Py_TYPE(obj)->tp_free((PyObject *)obj); } /* Permset string representation */ @@ -1145,7 +1168,7 @@ static PyObject* Permset_clear(PyObject* obj, PyObject* args) { Permset_Object *self = (Permset_Object*) obj; if(acl_clear_perms(self->permset) == -1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ Py_RETURN_NONE; } @@ -1176,8 +1199,10 @@ static int Permset_set_right(PyObject* obj, PyObject* value, void* arg) { else nerr = acl_delete_perm(self->permset, *(acl_perm_t*)arg); if(nerr == -1) { + /* LCOV_EXCL_START */ PyErr_SetFromErrno(PyExc_IOError); return -1; + /* LCOV_EXCL_STOP */ } return 0; } @@ -1204,7 +1229,7 @@ static PyObject* Permset_add(PyObject* obj, PyObject* args) { return NULL; if(acl_add_perm(self->permset, (acl_perm_t) right) == -1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ Py_RETURN_NONE; } @@ -1231,7 +1256,7 @@ static PyObject* Permset_delete(PyObject* obj, PyObject* args) { return NULL; if(acl_delete_perm(self->permset, (acl_perm_t) right) == -1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ Py_RETURN_NONE; } @@ -1259,7 +1284,7 @@ static PyObject* Permset_test(PyObject* obj, PyObject* args) { ret = get_perm(self->permset, (acl_perm_t) right); if(ret == -1) - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_IOError); /* LCOV_EXCL_LINE */ if(ret) { Py_RETURN_TRUE; @@ -1327,53 +1352,23 @@ static PyMethodDef ACL_methods[] = { /* The definition of the ACL Type */ static PyTypeObject ACL_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "posix1e.ACL", - sizeof(ACL_Object), - 0, - ACL_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* formerly tp_compare, in 3.0 deprecated, in - 3.5 tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - ACL_str, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - __ACL_Type_doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ + .tp_name = "posix1e.ACL", + .tp_basicsize = sizeof(ACL_Object), + .tp_itemsize = 0, + .tp_dealloc = ACL_dealloc, + .tp_str = ACL_str, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = __ACL_Type_doc__, #ifdef HAVE_LINUX - ACL_richcompare, /* tp_richcompare */ -#else - 0, /* tp_richcompare */ + .tp_richcompare = ACL_richcompare, #endif - 0, /* tp_weaklistoffset */ #ifdef HAVE_LEVEL2 - ACL_iter, - ACL_iternext, -#else - 0, /* tp_iter */ - 0, /* tp_iternext */ + .tp_iter = ACL_iter, + .tp_iternext = ACL_iternext, #endif - ACL_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - ACL_init, /* tp_init */ - 0, /* tp_alloc */ - ACL_new, /* tp_new */ + .tp_methods = ACL_methods, + .tp_init = ACL_init, + .tp_new = ACL_new, }; #ifdef HAVE_LEVEL2 @@ -1445,43 +1440,17 @@ static char __Entry_Type_doc__[] = /* The definition of the Entry Type */ static PyTypeObject Entry_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "posix1e.Entry", - sizeof(Entry_Object), - 0, - Entry_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - Entry_str, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - __Entry_Type_doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Entry_methods, /* tp_methods */ - 0, /* tp_members */ - Entry_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - Entry_init, /* tp_init */ - 0, /* tp_alloc */ - Entry_new, /* tp_new */ + .tp_name = "posix1e.Entry", + .tp_basicsize = sizeof(Entry_Object), + .tp_itemsize = 0, + .tp_dealloc = Entry_dealloc, + .tp_str = Entry_str, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = __Entry_Type_doc__, + .tp_methods = Entry_methods, + .tp_getset = Entry_getsets, + .tp_init = Entry_init, + .tp_new = Entry_new }; /* Permset type methods */ @@ -1552,43 +1521,17 @@ static char __Permset_Type_doc__[] = /* The definition of the Permset Type */ static PyTypeObject Permset_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "posix1e.Permset", - sizeof(Permset_Object), - 0, - Permset_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - Permset_str, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - __Permset_Type_doc__,/* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Permset_methods, /* tp_methods */ - 0, /* tp_members */ - Permset_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - Permset_init, /* tp_init */ - 0, /* tp_alloc */ - Permset_new, /* tp_new */ + .tp_name = "posix1e.Permset", + .tp_basicsize = sizeof(Permset_Object), + .tp_itemsize = 0, + .tp_dealloc = Permset_dealloc, + .tp_str = Permset_str, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = __Permset_Type_doc__, + .tp_methods = Permset_methods, + .tp_getset = Permset_getsets, + .tp_init = Permset_init, + .tp_new = Permset_new, }; #endif @@ -1782,10 +1725,10 @@ static char __posix1e_doc__[] = static struct PyModuleDef posix1emodule = { PyModuleDef_HEAD_INIT, - "posix1e", - __posix1e_doc__, - 0, - aclmodule_methods, + .m_name = "posix1e", + .m_doc = __posix1e_doc__, + .m_size = 0, + .m_methods = aclmodule_methods, }; PyMODINIT_FUNC diff --git a/doc/conf.py b/doc/conf.py index 0d2504a..1f80083 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -25,7 +25,7 @@ sys.path.insert(0, os.path.abspath('../')) # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo'] +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'recommonmark'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -48,9 +48,9 @@ copyright = u'2002-2009, 2012, 2014, 2015, Iustin Pop' # built documents. # # The short X.Y version. -version = '0.6.0' +version = '0.7.0' # The full version, including alpha/beta/rc tags. -release = '0.6.0' +release = '0.7.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -90,12 +90,6 @@ pygments_style = 'sphinx' keep_warnings = True -# Note: this is still needed in Sphinx 1.8 with recommonmark 0.4.0 -# (https://github.com/readthedocs/recommonmark/issues/119): -source_parsers = { - '.md': 'recommonmark.parser.CommonMarkParser', -} - # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for diff --git a/doc/contributing.md b/doc/contributing.md new file mode 100644 index 0000000..ae6a645 --- /dev/null +++ b/doc/contributing.md @@ -0,0 +1,65 @@ +# Contributing to pylibacl + +Hi, and thanks for any and all contributions! + +## Bugs and patches + +This is a small project, so let's keep things simple: + +- Please file all bug reports on github + (), as this allows + archival and discovery by other people; +- Send patches as pull requests; for larger changes, would be good to + first open a bug to discuss the plans; + +Due to simplicity, there are no old branches being kept alive, but if +it ever happens that a bug is found in older versions and there is +needed to support older Python versions, it is possible to do so. + +## Code standards + +There are no formal standards, but: + +- Code should be tested - this is why there's a [Codecov + integration](https://app.codecov.io/gh/iustin/pylibacl/tree/main). +- New functions should have good docstrings (in the C code). +- New functions/constants should be listed in the documentation, see + `doc/module.rst` for how to include them. +- All non-trivial changes should be listed in `NEWS.md` for further + inclusion in new releases documentation. Add an "unreleased" section + (if one doesn't exist yet) to list the changes. + +## Release process + +Right now, due to GPG signing, I'm doing releases and signing them +manually (offline, I mean). Basically, once GitHub workflows are fine: + +- Bump the version in all places - use `git grep -F $OLD_VER` and + update as needed. +- Ensure that `setup.py` has the right Python versions listed (bit me + more than once). +- Update the `NEWS.md` file is up to date (contents), and use the + right date. +- Check that the generated documentation (`make doc`) looks right. + +Then run these steps: + +``` +$ make clean +$ make distcheck # this leaves things in dist/ +$ git tag -m 'Release pylibacl-0.0.1' --sign v0.0.1 +$ gpg --sign -b -a dist/pylibacl-0.0.1.tar.gz +$ python3 -m twine upload dist/* +``` + +Separately: + +* Upload the `dist/` contents to GitHub and tag a new release. +* Upload the `dist/` contents to the old-style download area, + . + +Hopefully one day all this can be more automated. + +## Signing key + +The releases are currently signed by my key, see . diff --git a/doc/index.rst b/doc/index.rst index b47a5b6..55bc2c3 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -12,8 +12,10 @@ Contents :maxdepth: 2 readme.md + contributing.md + security.md module.rst implementation.rst - news.rst + news.md Also see the :ref:`search`. diff --git a/doc/news.md b/doc/news.md new file mode 100644 index 0000000..ca55b2d --- /dev/null +++ b/doc/news.md @@ -0,0 +1,191 @@ +# News + +## Version 0.7.0 + +*released Sun, 23 Apr 2023* + +Important: Python 3.7 is the minimum supported version, due to +difficulty of testing old releases, and the fact that everything older +has been deprecated a long time ago (e.g. 3.6 at the end of 2021). + +Otherwise, a minor release: + +- Improve error handling in some corner cases (not expected to have + any real-life impact, but who knows). +- Improved testing coverage and test infrastructure. +- Modernise parts of the C code based on recent Python version + guidelines. +- Add a simple security policy and contribution guidelines. + +## Version 0.6.0 + +*released Sun, 29 Nov 2020* + +Major release removing Python 2 support. This allow both code cleanup +and new features, such as: + +- Support for pathlib objects in `apply_to` and `has_extended` + functions when running with Python 3.6 and newer. +- Use of built-in C API functions for bytes/unicode/pathlib conversion + when dealing with file names, removing custom code (with the + associated benefits). + +Important API changes/bug fixes: + +- Initialisation protocol has been changed, to disallow uninitialised + objects; this means that `__new__` will always create valid objects, + to prevent the need for checking initialisation status in all code + paths; this also (implicitly) fixes memory leaks on re-initialisation + (calling `__init__(…)` on an existing object) and segfaults (!) on + non-initialised object attribute access. Note ACL re-initialisation is + tricky and (still) leads to undefined behaviour of existing Entry + objects pointing to it. +- Fix another bug in ACL re-initialisation where failures would result + in invalid objects; now failed re-initialisation does not touch the + original object. +- Restore `__setstate__`/`__getstate__` support on Linux; this was + inadvertently removed due a typo(!) when adding support for it in + FreeBSD. Pickle should work again for ACL instances, although not sure + how stable this serialisation format actually is. +- Additionally, slightly change `__setstate__()` input to not allow + Unicode, since the serialisation format is an opaque binary format. +- Fix (and change) entry qualifier (which is a user/group ID) behaviour: + assume/require that uid_t/gid_t are unsigned types (they are with + glibc, MacOS and FreeBSD at least; the standard doesn't document the + signedness), and convert parsing and returning the qualifier to behave + accordingly. The breakage was most apparent on 32-bit architectures, + in which context the problem was originally reported (see issue #13). + +Minor improvements: + +- Added a `data` keyword argument to `ACL()`, which allows restoring an + ACL directly from a serialised form (as given by `__getstate__()`), + which should simplify some uses cases (`a = ACL(); a.__set + state__(…)`). +- When available, add the file path to I/O error messages, which should + lead to easier debugging. +- The test suite has changed to `pytest`, which allows increased + coverage via parameterisation. + +## Version 0.5.4 + +*released Thu, 14 Nov 2019* + +Maintenance release: + +- Switch build system to Python 3 by default (can be overridden if + needed). +- Internal improvements for better cpychecker support. +- Fix compatibility with PyPy. +- Test improvements (both local and on Travis), testing more variations + (debug, PyPy). +- Improve test coverage, and allow gathering test coverage results. +- Drop support (well, drop testing) for Python lower than 2.7. +- Minor documentation improvements (closes #9, #12). + +## Version 0.5.3 + +*released Thu, 30 Apr 2015* + +FreeBSD fixes: + +- Enable all FreeBSD versions after 7.x at level 2 (thanks to Garrett + Cooper). +- Make test suite pass under FreeBSD, which has a stricter behaviour + with regards to invalid ACLs (which we do exercise in the test suite), + thanks again to Garret for the bug reports. + +## Version 0.5.2 + +*released Sat, 24 May 2014* + +No visible changes release: just fix tests when running under pypy. + +## Version 0.5.1 + +*released Sun, 13 May 2012* + +A bug-fix only release. Critical bugs (memory leaks and possible +segmentation faults) have been fixed thanks to Dave Malcolm and his +``cpychecker`` tool. Additionally, some compatibility issues with Python +3.x have been fixed (str() methods returning bytes). + +The documentation has been improved and changed from epydoc to sphinx; +note however that the documentation is still auto-generated from the +docstrings. + +Project reorganisation: the project home page has been moved from +SourceForge to GitHub. + +## Version 0.5 + +*released Sun, 27 Dec 2009* + +Added support for Python 3.x and improved support for Unicode filenames. + +## Version 0.4 + +*released Sat, 28 Jun 2008* + +### License + + +Starting with this version, pylibacl is licensed under LGPL 2.1, +Febryary 1999 or any later versions (see README.rst and COPYING). + +### Linux support + +A few more Linux-specific functions: + +- add the ACL.equiv_mode() method, which will return the equivalent + octal mode if this is a basic ACL and raise an IOError exception + otherwise + +- add the acl_extended(...) function, which will check if an fd or path + has an extended ACL + +### FreeBSD support + +FreeBSD 7.x will have almost all the acl manipulation functions that +Linux has, with the exception of __getstate__/__setstate__. As a +workaround, use the str() and ACL(text=...) methods to pass around +textual representations. + +### Interface + +At module level there are now a few constants exported for easy-checking +at runtime what features have been compiled in: + +- `HAS_ACL_FROM_MODE`, denoting whether the ACL constructor supports + the `mode=0xxx` parameter + +- `HAS_ACL_CHECK`, denoting whether ACL instances support the + `check()` method + +- `HAS_ACL_ENTRY`, denoting whether ACL manipulation is possible and + the Entry and Permset classes are available + +- `HAS_EXTENEDED_CHECK`, denoting whether the `acl_extended()` + function is supported + +- `HAS_EQUIV_MODE`, denoting whether ACL instances support the + `equiv_mode()` method + +### Internals + +Many functions have now unittests, which is a good thing. + + +## Version 0.3 + +*released Sun, 21 Oct 2007* + +### Linux support + +Under Linux, implement more functions from libacl: + +- add `ACL(mode=...)`, implementing `acl_from_mode`. +- add `ACL.to_any_text()`, implementing `acl_to_any_text`. +- add ACL comparison, using `acl_cmp`. +- add `ACL.check()`, which is a more descriptive function than + validate. diff --git a/doc/readme.md b/doc/readme.md new file mode 100644 index 0000000..c8e32c3 --- /dev/null +++ b/doc/readme.md @@ -0,0 +1,72 @@ +# pylibacl + +This is a Python 3.7+ extension module allows you to manipulate the +POSIX.1e Access Control Lists present in some OS/file-systems +combinations. + +Downloads: go to . Latest +version is 0.7.0. The source repository is either at + or at +. + +For any issues, please file bugs at +. + +See the `CONTRIBUTING.md` file for details on how to contribute. + +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/iustin/pylibacl/ci.yml?branch=main)](https://github.com/iustin/pylibacl/actions/workflows/ci.yml) +[![Codecov](https://img.shields.io/codecov/c/github/iustin/pylibacl)](https://codecov.io/gh/iustin/pylibacl) +[![Read the Docs](https://img.shields.io/readthedocs/pylibacl)](http://pylibacl.readthedocs.io/en/latest/?badge=latest) +[![GitHub issues](https://img.shields.io/github/issues/iustin/pylibacl)](https://github.com/iustin/pylibacl/issues) +![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/iustin/pylibacl) +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/iustin/pylibacl)](https://github.com/iustin/pylibacl/releases) +[![PyPI](https://img.shields.io/pypi/v/pylibacl)](https://pypi.org/project/pylibacl/) +![Debian package](https://img.shields.io/debian/v/python-pylibacl) +![Ubuntu package](https://img.shields.io/ubuntu/v/python-pylibacl) +![GitHub Release Date](https://img.shields.io/github/release-date/iustin/pylibacl) +![GitHub commits since latest release](https://img.shields.io/github/commits-since/iustin/pylibacl/latest) +![GitHub last commit](https://img.shields.io/github/last-commit/iustin/pylibacl) + +## Requirements + +pylibacl has been written and tested on Linux, kernel v2.4 or newer, +with XFS filesystems; ext2/ext3 should also work. Since release 0.4.0, +FreeBSD 7 also has quite good support. If any other platform +implements the POSIX.1e draft, pylibacl can be used. I heard that +Solaris does, but I can't test it. + +- Python 3.7 or newer. Python 2.4+ was supported in the 0.5.x branch, + Python 3.4+ in the 0.6 branch. +- Operating system: + - Linux, kernel v2.4 or newer, and the libacl library and + development packages (all modern distributions should have this, + under various names); also the file-systems you use must have + ACLs turned on, either as a compile or mount option. + - FreeBSD 7.0 or newer. +- The sphinx python module, for your python version, if building the + documentation. + +## FreeBSD + +Note that on FreeBSD, ACLs are not enabled by default (at least on UFS +file systems). To enable them, run `tunefs -a enabled` on the file +system in question (after mounting it read-only). Then install: + +- `pkg install py36-setuptools py36-sphinx` + +or: + +- `pkg install py37-setuptools` + +## Security + +For reporting security vulnerabilities, please see `SECURITY.md`. + +## License + +pylibacl is Copyright (C) 2002-2009, 2012, 2014, 2015 Iustin Pop. + +pylibacl is free software; you can redistribute it and/or modify it under the +terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 2.1 of the License, or (at your option) any +later version. See the COPYING file for the full license terms. diff --git a/doc/security.md b/doc/security.md new file mode 100644 index 0000000..dbd4a7c --- /dev/null +++ b/doc/security.md @@ -0,0 +1,16 @@ +# Security Policy + +To report a (potential or confirmed) security issue, please email + with a description of the issue, steps to reproduce +it, affected versions, and if known, mitigations for the issue. + +Since this is a small project, there's no list of supported +versions. I will attempt to reply to reports within a working week, +and to fix and disclose vulnerabilities within 90 days, but this is +not a guarantee. + +Optionally, you can encrypt the email with my GPG key, see for details +. + +Alternatively, you can use the GitHub "Private vulnerability +reporting" functionality (but note this is beta). diff --git a/pylibacl.egg-info/PKG-INFO b/pylibacl.egg-info/PKG-INFO index 3f83b9c..8fe963f 100644 --- a/pylibacl.egg-info/PKG-INFO +++ b/pylibacl.egg-info/PKG-INFO @@ -1,16 +1,12 @@ -Metadata-Version: 1.2 +Metadata-Version: 2.1 Name: pylibacl -Version: 0.6.0 +Version: 0.7.0 Summary: POSIX.1e ACLs for python Home-page: https://pylibacl.k1024.org/ Author: Iustin Pop Author-email: iustin@k1024.org License: LGPL Project-URL: Bug Tracker, https://github.com/iustin/pylibacl/issues -Description: This is a C extension module for Python which - implements POSIX ACLs manipulation. It is a wrapper on top - of the systems's acl C library - see acl(5). -Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) @@ -21,4 +17,9 @@ Classifier: Operating System :: POSIX :: BSD :: FreeBSD Classifier: Operating System :: POSIX :: Linux Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Filesystems -Requires-Python: >=3.4 +Requires-Python: >=3.7 +License-File: COPYING + +This is a C extension module for Python which +implements POSIX ACLs manipulation. It is a wrapper on top +of the systems's acl C library - see acl(5). diff --git a/pylibacl.egg-info/SOURCES.txt b/pylibacl.egg-info/SOURCES.txt index ca9fffd..48723b7 100644 --- a/pylibacl.egg-info/SOURCES.txt +++ b/pylibacl.egg-info/SOURCES.txt @@ -1,17 +1,22 @@ COPYING MANIFEST.in Makefile -NEWS +NEWS.md README.md +SECURITY.md acl.c posix1e.pyi py.typed setup.cfg setup.py doc/conf.py +doc/contributing.md doc/implementation.rst doc/index.rst doc/module.rst +doc/news.md +doc/readme.md +doc/security.md pylibacl.egg-info/PKG-INFO pylibacl.egg-info/SOURCES.txt pylibacl.egg-info/dependency_links.txt diff --git a/setup.py b/setup.py index bb69a61..5afc738 100755 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ long_desc = """This is a C extension module for Python which implements POSIX ACLs manipulation. It is a wrapper on top of the systems's acl C library - see acl(5).""" -version = "0.6.0" +version = "0.7.0" setup(name="pylibacl", version=version, @@ -45,7 +45,7 @@ setup(name="pylibacl", libraries=libs, define_macros=macros, )], - python_requires = ">=3.4", + python_requires = ">=3.7", # Note: doesn't work since it's not a package. Sigh. package_data = { '': ['py.typed', 'posix1e.pyi'], diff --git a/tests/test_acls.py b/tests/test_acls.py index 2272d05..5bb4837 100644 --- a/tests/test_acls.py +++ b/tests/test_acls.py @@ -220,6 +220,19 @@ FD_D = [ "file io stream", ] +DIR_D = [ + "directory", + "directory (bytes)", + "directory (path object)", +] + +DIR_P = [ + get_dir, + as_bytes(get_dir), + pytest.param(as_fspath(get_dir), + marks=[NOT_BEFORE_36, NOT_PYPY]), +] + ALL_P = FILE_P + FD_P ALL_D = FILE_D + FD_D @@ -233,6 +246,11 @@ def fd_subject(testdir, request): with request.param(testdir) as value: yield value +@pytest.fixture(params=DIR_P, ids=DIR_D) +def dir_subject(testdir, request): + with request.param(testdir) as value: + yield value + @pytest.fixture(params=ALL_P, ids=ALL_D) def subject(testdir, request): with request.param(testdir) as value: @@ -246,10 +264,9 @@ class TestLoad: acl = posix1e.ACL(file=file_subject) assert acl.valid() - def test_from_dir(self, testdir): + def test_from_dir(self, dir_subject): """Test loading ACLs from a directory""" - with get_dir(testdir) as dname: - acl2 = posix1e.ACL(filedef=dname) + acl2 = posix1e.ACL(filedef=dir_subject) # default ACLs might or might not be valid; missing ones are # not valid, so we don't test acl2 for validity @@ -506,6 +523,39 @@ class TestAclExtensions: assert a == b assert b != c + @require_copy_ext + def test_acl_copy_ext_failure(self): + a = posix1e.ACL() + state = a.__getstate__() + # This is a dangerous test. The acl_copy_int() C function gets + # a void * buffer, and then casts that to an ACL structure, + # irrespective of buffer length; this can lead to segfaults + # (via unallocated memory indexing) + # + # To mitigate this, pass same buffer size as returned from the + # state, just nulled out - in the Linux version of the + # library, the first byte is the structure size and is tested + # for correct size, and a null byte will cause failure. + nulled = b'\x00' * len(state) + with pytest.raises(IOError): + a.__setstate__(nulled) + + @require_copy_ext + def test_acl_copy_ext_failure(self): + a = posix1e.ACL(text=BASIC_ACL_TEXT) + b = posix1e.ACL() + c = posix1e.ACL(acl=a) + assert a == c + assert a != b + state = b.__getstate__() + # See notes in the test_acl_copy_ext_failure() for how tricky this is. + nulled = b'\x00' * len(state) + with pytest.raises(IOError): + a.__setstate__(nulled) + # Assert that 'a' didn't change in the attempt to restore + # invalid state. + assert a == c + @require_copy_ext def test_acl_copy_ext_args(self): a = posix1e.ACL() -- 2.39.2