2 posix1e - a python module exposing the posix acl functions
4 Copyright (C) 2002-2008 Iustin Pop <iusty@k1024.org>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 #include <sys/types.h>
29 #include <acl/libacl.h>
30 #define get_perm acl_get_perm
32 #define get_perm acl_get_perm_np
35 #if PY_MAJOR_VERSION >= 3
38 #define PyBytes_Check PyString_Check
39 #define PyBytes_AS_STRING PyString_AS_STRING
40 #define PyBytes_FromStringAndSize PyString_FromStringAndSize
41 #define PyBytes_FromString PyString_FromString
42 #define PyBytes_FromFormat PyString_FromFormat
43 #define PyBytes_ConcatAndDel PyString_ConcatAndDel
46 static PyTypeObject ACL_Type;
47 static PyObject* ACL_applyto(PyObject* obj, PyObject* args);
48 static PyObject* ACL_valid(PyObject* obj, PyObject* args);
50 #ifdef HAVE_ACL_COPY_EXT
51 static PyObject* ACL_get_state(PyObject *obj, PyObject* args);
52 static PyObject* ACL_set_state(PyObject *obj, PyObject* args);
56 static PyTypeObject Entry_Type;
57 static PyTypeObject Permset_Type;
58 static PyObject* Permset_new(PyTypeObject* type, PyObject* args,
62 static acl_perm_t holder_ACL_EXECUTE = ACL_EXECUTE;
63 static acl_perm_t holder_ACL_READ = ACL_READ;
64 static acl_perm_t holder_ACL_WRITE = ACL_WRITE;
78 PyObject *parent_acl; /* The parent acl, so it won't run out on us */
84 PyObject *parent_entry; /* The parent entry, so it won't run out on us */
85 acl_permset_t permset;
90 /* Creation of a new ACL instance */
91 static PyObject* ACL_new(PyTypeObject* type, PyObject* args,
95 newacl = type->tp_alloc(type, 0);
98 ((ACL_Object*)newacl)->acl = NULL;
100 ((ACL_Object*)newacl)->entry_id = ACL_FIRST_ENTRY;
107 /* Initialization of a new ACL instance */
108 static int ACL_init(PyObject* obj, PyObject* args, PyObject *keywds) {
109 ACL_Object* self = (ACL_Object*) obj;
111 static char *kwlist[] = { "file", "fd", "text", "acl", "filedef",
113 char *format = "|sisO!sH";
116 static char *kwlist[] = { "file", "fd", "text", "acl", "filedef", NULL };
117 char *format = "|sisO!s";
120 char *filedef = NULL;
123 ACL_Object* thesrc = NULL;
125 if(!PyTuple_Check(args) || PyTuple_Size(args) != 0 ||
126 (keywds != NULL && PyDict_Check(keywds) && PyDict_Size(keywds) > 1)) {
127 PyErr_SetString(PyExc_ValueError, "a max of one keyword argument"
131 if(!PyArg_ParseTupleAndKeywords(args, keywds, format, kwlist,
132 &file, &fd, &text, &ACL_Type,
140 /* Free the old acl_t without checking for error, we don't
142 if(self->acl != NULL)
146 self->acl = acl_get_file(file, ACL_TYPE_ACCESS);
147 else if(text != NULL)
148 self->acl = acl_from_text(text);
150 self->acl = acl_get_fd(fd);
151 else if(thesrc != NULL)
152 self->acl = acl_dup(thesrc->acl);
153 else if(filedef != NULL)
154 self->acl = acl_get_file(filedef, ACL_TYPE_DEFAULT);
156 else if(PyMapping_HasKeyString(keywds, kwlist[5]))
157 self->acl = acl_from_mode(mode);
160 self->acl = acl_init(0);
162 if(self->acl == NULL) {
163 PyErr_SetFromErrno(PyExc_IOError);
170 /* Standard type functions */
171 static void ACL_dealloc(PyObject* obj) {
172 ACL_Object *self = (ACL_Object*) obj;
173 PyObject *err_type, *err_value, *err_traceback;
174 int have_error = PyErr_Occurred() ? 1 : 0;
177 PyErr_Fetch(&err_type, &err_value, &err_traceback);
178 if(self->acl != NULL && acl_free(self->acl) != 0)
179 PyErr_WriteUnraisable(obj);
181 PyErr_Restore(err_type, err_value, err_traceback);
185 /* Converts the acl to a text format */
186 static PyObject* ACL_str(PyObject *obj) {
188 ACL_Object *self = (ACL_Object*) obj;
191 text = acl_to_text(self->acl, NULL);
193 return PyErr_SetFromErrno(PyExc_IOError);
195 ret = PyBytes_FromString(text);
196 if(acl_free(text) != 0) {
198 return PyErr_SetFromErrno(PyExc_IOError);
204 static char __to_any_text_doc__[] =
205 "Convert the ACL to a custom text format.\n"
207 "This method encapsulates the acl_to_any_text function. It allows a \n"
208 "customized text format to be generated for the ACL. See\n"
209 "acl_to_any_text(3) for more details.\n"
212 " - prefix: if given, this string will be prepended to all lines\n"
213 " - separator: a single character (defaults to '\\n'); this will be\n"
214 " user to separate the entries in the ACL\n"
215 " - options: a bitwise combination of:\n"
216 " - TEXT_ABBREVIATE: use 'u' instead of 'user', 'g' instead of \n"
218 " - TEXT_NUMERIC_IDS: User and group IDs are included as decimal\n"
219 " numbers instead of names\n"
220 " - TEXT_SOME_EFFECTIVE: Include comments denoting the effective\n"
221 " permissions when some are masked\n"
222 " - TEXT_ALL_EFFECTIVE: Include comments after all ACL entries\n"
223 " affected by an ACL_MASK entry\n"
224 " - TEXT_SMART_INDENT: Used in combination with the _EFFECTIVE\n"
225 " options, this will ensure that comments \n"
226 " are alligned to the fourth tab position\n"
227 " (assuming one tab equals eight spaces)\n"
230 /* Converts the acl to a custom text format */
231 static PyObject* ACL_to_any_text(PyObject *obj, PyObject *args,
234 ACL_Object *self = (ACL_Object*) obj;
236 char *arg_prefix = NULL;
237 char arg_separator = '\n';
239 static char *kwlist[] = {"prefix", "separator", "options", NULL};
241 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sci", kwlist, &arg_prefix,
242 &arg_separator, &arg_options))
245 text = acl_to_any_text(self->acl, arg_prefix, arg_separator, arg_options);
247 return PyErr_SetFromErrno(PyExc_IOError);
249 ret = PyBytes_FromString(text);
250 if(acl_free(text) != 0) {
252 return PyErr_SetFromErrno(PyExc_IOError);
257 static char __check_doc__[] =
258 "Check the ACL validity.\n"
260 "This is a non-portable, Linux specific extension that allow more\n"
261 "information to be retrieved in case an ACL is not valid than the\n"
262 "validate() method.\n"
264 "This method will return either False (the ACL is valid), or a tuple\n"
265 "with two elements. The first element is one of the following\n"
267 " - ACL_MULTI_ERROR: The ACL contains multiple entries that have a\n"
268 " tag type that may occur at most once\n"
269 " - ACL_DUPLICATE_ERROR: The ACL contains multiple ACL_USER or \n"
270 " ACL_GROUP entries with the same ID\n"
271 " - ACL_MISS_ERROR: A required entry is missing\n"
272 " - ACL_ENTRY_ERROR: The ACL contains an invalid entry tag type\n"
274 "The second element of the tuple is the index of the entry that is\n"
275 "invalid (in the same order as by iterating over the ACL entry)\n"
278 /* The acl_check method */
279 static PyObject* ACL_check(PyObject* obj, PyObject* args) {
280 ACL_Object *self = (ACL_Object*) obj;
284 if((result = acl_check(self->acl, &eindex)) == -1)
285 return PyErr_SetFromErrno(PyExc_IOError);
290 return PyTuple_Pack(2, PyInt_FromLong(result), PyInt_FromLong(eindex));
293 /* Implementation of the rich compare for ACLs */
294 static PyObject* ACL_richcompare(PyObject* o1, PyObject* o2, int op) {
295 ACL_Object *acl1, *acl2;
299 if(!PyObject_IsInstance(o2, (PyObject*)&ACL_Type)) {
304 PyErr_SetString(PyExc_TypeError, "can only compare to an ACL");
308 acl1 = (ACL_Object*)o1;
309 acl2 = (ACL_Object*)o2;
310 if((n=acl_cmp(acl1->acl, acl2->acl))==-1)
311 return PyErr_SetFromErrno(PyExc_IOError);
314 ret = n == 0 ? Py_True : Py_False;
317 ret = n == 1 ? Py_True : Py_False;
320 ret = Py_NotImplemented;
326 static char __equiv_mode_doc__[] =
327 "Return the octal mode the ACL is equivalent to.\n"
329 "This is a non-portable, Linux specific extension that checks\n"
330 "if the ACL is a basic ACL and returns the corresponding mode.\n"
332 "An IOerror exception will be raised if the ACL is an extended ACL\n"
335 /* The acl_equiv_mode method */
336 static PyObject* ACL_equiv_mode(PyObject* obj, PyObject* args) {
337 ACL_Object *self = (ACL_Object*) obj;
340 if(acl_equiv_mode(self->acl, &mode) == -1)
341 return PyErr_SetFromErrno(PyExc_IOError);
342 return PyInt_FromLong(mode);
346 /* Implementation of the compare for ACLs */
347 static int ACL_nocmp(PyObject* o1, PyObject* o2) {
349 PyErr_SetString(PyExc_TypeError, "cannot compare ACLs using cmp()");
354 static char __applyto_doc__[] =
355 "Apply the ACL to a file or filehandle.\n"
358 " - either a filename or a file-like object or an integer; this\n"
359 " represents the filesystem object on which to act\n"
360 " - optional flag representing the type of ACL to set, either\n"
361 " ACL_TYPE_ACCESS (default) or ACL_TYPE_DEFAULT\n"
364 /* Applyes the ACL to a file */
365 static PyObject* ACL_applyto(PyObject* obj, PyObject* args) {
366 ACL_Object *self = (ACL_Object*) obj;
368 acl_type_t type = ACL_TYPE_ACCESS;
372 if (!PyArg_ParseTuple(args, "O|i", &myarg, &type))
375 if(PyBytes_Check(myarg)) {
376 char *filename = PyBytes_AS_STRING(myarg);
377 nret = acl_set_file(filename, type, self->acl);
378 } else if((fd = PyObject_AsFileDescriptor(myarg)) != -1) {
379 nret = acl_set_fd(fd, self->acl);
381 PyErr_SetString(PyExc_TypeError, "argument 1 must be string, int,"
382 " or file-like object");
386 return PyErr_SetFromErrno(PyExc_IOError);
389 /* Return the result */
394 static char __valid_doc__[] =
395 "Test the ACL for validity.\n"
397 "This method tests the ACL to see if it is a valid ACL\n"
398 "in terms of the filesystem. More precisely, it checks that:\n"
400 "The ACL contains exactly one entry with each of the\n"
401 "ACL_USER_OBJ, ACL_GROUP_OBJ, and ACL_OTHER tag types. Entries\n"
402 "with ACL_USER and ACL_GROUP tag types may appear zero or more\n"
403 "times in an ACL. An ACL that contains entries of ACL_USER or\n"
404 "ACL_GROUP tag types must contain exactly one entry of the \n"
405 "ACL_MASK tag type. If an ACL contains no entries of\n"
406 "ACL_USER or ACL_GROUP tag types, the ACL_MASK entry is optional.\n"
408 "All user ID qualifiers must be unique among all entries of\n"
409 "the ACL_USER tag type, and all group IDs must be unique among all\n"
410 "entries of ACL_GROUP tag type.\n"
412 "The method will return 1 for a valid ACL and 0 for an invalid one.\n"
413 "This has been chosen because the specification for acl_valid in\n"
414 "the POSIX.1e standard documents only one possible value for errno\n"
415 "in case of an invalid ACL, so we can't differentiate between\n"
416 "classes of errors. Other suggestions are welcome.\n"
419 /* Checks the ACL for validity */
420 static PyObject* ACL_valid(PyObject* obj, PyObject* args) {
421 ACL_Object *self = (ACL_Object*) obj;
423 if(acl_valid(self->acl) == -1) {
432 #ifdef HAVE_ACL_COPY_EXT
433 static PyObject* ACL_get_state(PyObject *obj, PyObject* args) {
434 ACL_Object *self = (ACL_Object*) obj;
439 size = acl_size(self->acl);
441 return PyErr_SetFromErrno(PyExc_IOError);
443 if((ret = PyBytes_FromStringAndSize(NULL, size)) == NULL)
445 buf = PyBytes_AsString(ret);
447 if((nsize = acl_copy_ext(buf, self->acl, size)) == -1) {
449 return PyErr_SetFromErrno(PyExc_IOError);
455 static PyObject* ACL_set_state(PyObject *obj, PyObject* args) {
456 ACL_Object *self = (ACL_Object*) obj;
461 /* Parse the argument */
462 if (!PyArg_ParseTuple(args, "s#", &buf, &bufsize))
465 /* Try to import the external representation */
466 if((ptr = acl_copy_int(buf)) == NULL)
467 return PyErr_SetFromErrno(PyExc_IOError);
469 /* Free the old acl. Should we ignore errors here? */
470 if(self->acl != NULL) {
471 if(acl_free(self->acl) == -1)
472 return PyErr_SetFromErrno(PyExc_IOError);
477 /* Return the result */
485 /* tp_iter for the ACL type; since it can be iterated only
486 * destructively, the type is its iterator
488 static PyObject* ACL_iter(PyObject *obj) {
489 ACL_Object *self = (ACL_Object*)obj;
490 self->entry_id = ACL_FIRST_ENTRY;
495 /* the tp_iternext function for the ACL type */
496 static PyObject* ACL_iternext(PyObject *obj) {
497 ACL_Object *self = (ACL_Object*)obj;
498 acl_entry_t the_entry_t;
499 Entry_Object *the_entry_obj;
502 nerr = acl_get_entry(self->acl, self->entry_id, &the_entry_t);
503 self->entry_id = ACL_NEXT_ENTRY;
505 return PyErr_SetFromErrno(PyExc_IOError);
507 /* Docs says this is not needed */
508 /*PyErr_SetObject(PyExc_StopIteration, Py_None);*/
512 the_entry_obj = (Entry_Object*) PyType_GenericNew(&Entry_Type, NULL, NULL);
513 if(the_entry_obj == NULL)
516 the_entry_obj->entry = the_entry_t;
518 the_entry_obj->parent_acl = obj;
519 Py_INCREF(obj); /* For the reference we have in entry->parent */
521 return (PyObject*)the_entry_obj;
524 static char __ACL_delete_entry_doc__[] =
525 "Deletes an entry from the ACL.\n"
527 "Note: Only with level 2\n"
529 " - the Entry object which should be deleted; note that after\n"
530 " this function is called, that object is unusable any longer\n"
531 " and should be deleted\n"
534 /* Deletes an entry from the ACL */
535 static PyObject* ACL_delete_entry(PyObject *obj, PyObject *args) {
536 ACL_Object *self = (ACL_Object*)obj;
539 if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &e))
542 if(acl_delete_entry(self->acl, e->entry) == -1)
543 return PyErr_SetFromErrno(PyExc_IOError);
545 /* Return the result */
550 static char __ACL_calc_mask_doc__[] =
551 "Compute the file group class mask.\n"
553 "The calc_mask() method calculates and sets the permissions \n"
554 "associated with the ACL_MASK Entry of the ACL.\n"
555 "The value of the new permissions is the union of the permissions \n"
556 "granted by all entries of tag type ACL_GROUP, ACL_GROUP_OBJ, or \n"
557 "ACL_USER. If the ACL already contains an ACL_MASK entry, its \n"
558 "permissions are overwritten; if it does not contain an ACL_MASK \n"
559 "Entry, one is added.\n"
561 "The order of existing entries in the ACL is undefined after this \n"
565 /* Updates the mask entry in the ACL */
566 static PyObject* ACL_calc_mask(PyObject *obj, PyObject *args) {
567 ACL_Object *self = (ACL_Object*)obj;
569 if(acl_calc_mask(&self->acl) == -1)
570 return PyErr_SetFromErrno(PyExc_IOError);
572 /* Return the result */
577 static char __ACL_append_doc__[] =
578 "Append a new Entry to the ACL and return it.\n"
580 "This is a convenience function to create a new Entry \n"
581 "and append it to the ACL.\n"
582 "If a parameter of type Entry instance is given, the \n"
583 "entry will be a copy of that one (as if copied with \n"
584 "Entry.copy()), otherwise, the new entry will be empty.\n"
587 /* Convenience method to create a new Entry */
588 static PyObject* ACL_append(PyObject *obj, PyObject *args) {
589 ACL_Object* self = (ACL_Object*) obj;
590 Entry_Object* newentry;
591 Entry_Object* oldentry = NULL;
594 newentry = (Entry_Object*)PyType_GenericNew(&Entry_Type, NULL, NULL);
595 if(newentry == NULL) {
599 if (!PyArg_ParseTuple(args, "|O!", &Entry_Type, &oldentry))
602 nret = acl_create_entry(&self->acl, &newentry->entry);
605 return PyErr_SetFromErrno(PyExc_IOError);
608 if(oldentry != NULL) {
609 nret = acl_copy_entry(newentry->entry, oldentry->entry);
612 return PyErr_SetFromErrno(PyExc_IOError);
616 newentry->parent_acl = obj;
619 return (PyObject*)newentry;
622 /***** Entry type *****/
624 /* Creation of a new Entry instance */
625 static PyObject* Entry_new(PyTypeObject* type, PyObject* args,
629 newentry = PyType_GenericNew(type, args, keywds);
631 if(newentry != NULL) {
632 ((Entry_Object*)newentry)->entry = NULL;
633 ((Entry_Object*)newentry)->parent_acl = NULL;
639 /* Initialization of a new Entry instance */
640 static int Entry_init(PyObject* obj, PyObject* args, PyObject *keywds) {
641 Entry_Object* self = (Entry_Object*) obj;
642 ACL_Object* parent = NULL;
644 if (!PyArg_ParseTuple(args, "O!", &ACL_Type, &parent))
647 if(acl_create_entry(&parent->acl, &self->entry) == -1) {
648 PyErr_SetFromErrno(PyExc_IOError);
652 self->parent_acl = (PyObject*)parent;
658 /* Free the Entry instance */
659 static void Entry_dealloc(PyObject* obj) {
660 Entry_Object *self = (Entry_Object*) obj;
661 PyObject *err_type, *err_value, *err_traceback;
662 int have_error = PyErr_Occurred() ? 1 : 0;
665 PyErr_Fetch(&err_type, &err_value, &err_traceback);
666 if(self->parent_acl != NULL) {
667 Py_DECREF(self->parent_acl);
668 self->parent_acl = NULL;
671 PyErr_Restore(err_type, err_value, err_traceback);
675 /* Converts the entry to a text format */
676 static PyObject* Entry_str(PyObject *obj) {
680 PyObject *format, *kind;
681 Entry_Object *self = (Entry_Object*) obj;
683 if(acl_get_tag_type(self->entry, &tag) == -1) {
684 PyErr_SetFromErrno(PyExc_IOError);
687 if(tag == ACL_USER || tag == ACL_GROUP) {
688 if((p = acl_get_qualifier(self->entry)) == NULL) {
689 PyErr_SetFromErrno(PyExc_IOError);
692 qualifier = *(uid_t*)p;
698 format = PyBytes_FromString("ACL entry for ");
701 if(tag == ACL_UNDEFINED_TAG) {
702 kind = PyBytes_FromString("undefined type");
703 } else if(tag == ACL_USER_OBJ) {
704 kind = PyBytes_FromString("the owner");
705 } else if(tag == ACL_GROUP_OBJ) {
706 kind = PyBytes_FromString("the group");
707 } else if(tag == ACL_OTHER) {
708 kind = PyBytes_FromString("the others");
709 } else if(tag == ACL_USER) {
710 kind = PyBytes_FromFormat("user with uid %d", qualifier);
711 } else if(tag == ACL_GROUP) {
712 kind = PyBytes_FromFormat("group with gid %d", qualifier);
713 } else if(tag == ACL_MASK) {
714 kind = PyBytes_FromString("the mask");
716 kind = PyBytes_FromString("UNKNOWN_TAG_TYPE!");
720 PyBytes_ConcatAndDel(&format, kind);
725 /* Sets the tag type of the entry */
726 static int Entry_set_tag_type(PyObject* obj, PyObject* value, void* arg) {
727 Entry_Object *self = (Entry_Object*) obj;
730 PyErr_SetString(PyExc_TypeError,
731 "tag type deletion is not supported");
735 if(!PyInt_Check(value)) {
736 PyErr_SetString(PyExc_TypeError,
737 "tag type must be integer");
740 if(acl_set_tag_type(self->entry, (acl_tag_t)PyInt_AsLong(value)) == -1) {
741 PyErr_SetFromErrno(PyExc_IOError);
748 /* Returns the tag type of the entry */
749 static PyObject* Entry_get_tag_type(PyObject *obj, void* arg) {
750 Entry_Object *self = (Entry_Object*) obj;
753 if (self->entry == NULL) {
754 PyErr_SetString(PyExc_AttributeError, "entry attribute");
757 if(acl_get_tag_type(self->entry, &value) == -1) {
758 PyErr_SetFromErrno(PyExc_IOError);
762 return PyInt_FromLong(value);
765 /* Sets the qualifier (either uid_t or gid_t) for the entry,
766 * usable only if the tag type if ACL_USER or ACL_GROUP
768 static int Entry_set_qualifier(PyObject* obj, PyObject* value, void* arg) {
769 Entry_Object *self = (Entry_Object*) obj;
773 PyErr_SetString(PyExc_TypeError,
774 "qualifier deletion is not supported");
778 if(!PyInt_Check(value)) {
779 PyErr_SetString(PyExc_TypeError,
780 "tag type must be integer");
783 uidgid = PyInt_AsLong(value);
784 if(acl_set_qualifier(self->entry, (void*)&uidgid) == -1) {
785 PyErr_SetFromErrno(PyExc_IOError);
792 /* Returns the qualifier of the entry */
793 static PyObject* Entry_get_qualifier(PyObject *obj, void* arg) {
794 Entry_Object *self = (Entry_Object*) obj;
798 if (self->entry == NULL) {
799 PyErr_SetString(PyExc_AttributeError, "entry attribute");
802 if((p = acl_get_qualifier(self->entry)) == NULL) {
803 PyErr_SetFromErrno(PyExc_IOError);
809 return PyInt_FromLong(value);
812 /* Returns the parent ACL of the entry */
813 static PyObject* Entry_get_parent(PyObject *obj, void* arg) {
814 Entry_Object *self = (Entry_Object*) obj;
816 Py_INCREF(self->parent_acl);
817 return self->parent_acl;
820 /* Returns the a new Permset representing the permset of the entry
821 * FIXME: Should return a new reference to the same object, which
822 * should be created at init time!
824 static PyObject* Entry_get_permset(PyObject *obj, void* arg) {
825 Entry_Object *self = (Entry_Object*)obj;
829 p = Permset_new(&Permset_Type, NULL, NULL);
832 ps = (Permset_Object*)p;
833 if(acl_get_permset(self->entry, &ps->permset) == -1) {
834 PyErr_SetFromErrno(PyExc_IOError);
837 ps->parent_entry = obj;
843 /* Sets the permset of the entry to the passed Permset */
844 static int Entry_set_permset(PyObject* obj, PyObject* value, void* arg) {
845 Entry_Object *self = (Entry_Object*)obj;
848 if(!PyObject_IsInstance(value, (PyObject*)&Permset_Type)) {
849 PyErr_SetString(PyExc_TypeError, "argument 1 must be posix1e.Permset");
852 p = (Permset_Object*)value;
853 if(acl_set_permset(self->entry, p->permset) == -1) {
854 PyErr_SetFromErrno(PyExc_IOError);
860 static char __Entry_copy_doc__[] =
861 "Copy an ACL entry.\n"
863 "This method sets all the parameters to those of another\n"
864 "entry, even one of another's ACL\n"
866 " - src, instance of type Entry\n"
869 /* Sets all the entry parameters to another's entry */
870 static PyObject* Entry_copy(PyObject *obj, PyObject *args) {
871 Entry_Object *self = (Entry_Object*)obj;
874 if(!PyArg_ParseTuple(args, "O!", &Entry_Type, &other))
877 if(acl_copy_entry(self->entry, other->entry) == -1)
878 return PyErr_SetFromErrno(PyExc_IOError);
884 /**** Permset type *****/
886 /* Creation of a new Permset instance */
887 static PyObject* Permset_new(PyTypeObject* type, PyObject* args,
889 PyObject* newpermset;
891 newpermset = PyType_GenericNew(type, args, keywds);
893 if(newpermset != NULL) {
894 ((Permset_Object*)newpermset)->permset = NULL;
895 ((Permset_Object*)newpermset)->parent_entry = NULL;
901 /* Initialization of a new Permset instance */
902 static int Permset_init(PyObject* obj, PyObject* args, PyObject *keywds) {
903 Permset_Object* self = (Permset_Object*) obj;
904 Entry_Object* parent = NULL;
906 if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &parent))
909 if(acl_get_permset(parent->entry, &self->permset) == -1) {
910 PyErr_SetFromErrno(PyExc_IOError);
914 self->parent_entry = (PyObject*)parent;
920 /* Free the Permset instance */
921 static void Permset_dealloc(PyObject* obj) {
922 Permset_Object *self = (Permset_Object*) obj;
923 PyObject *err_type, *err_value, *err_traceback;
924 int have_error = PyErr_Occurred() ? 1 : 0;
927 PyErr_Fetch(&err_type, &err_value, &err_traceback);
928 if(self->parent_entry != NULL) {
929 Py_DECREF(self->parent_entry);
930 self->parent_entry = NULL;
933 PyErr_Restore(err_type, err_value, err_traceback);
937 /* Permset string representation */
938 static PyObject* Permset_str(PyObject *obj) {
939 Permset_Object *self = (Permset_Object*) obj;
942 pstr[0] = get_perm(self->permset, ACL_READ) ? 'r' : '-';
943 pstr[1] = get_perm(self->permset, ACL_WRITE) ? 'w' : '-';
944 pstr[2] = get_perm(self->permset, ACL_EXECUTE) ? 'x' : '-';
945 return PyBytes_FromStringAndSize(pstr, 3);
948 static char __Permset_clear_doc__[] =
949 "Clear all permissions from the permission set.\n"
952 /* Clears all permissions from the permset */
953 static PyObject* Permset_clear(PyObject* obj, PyObject* args) {
954 Permset_Object *self = (Permset_Object*) obj;
956 if(acl_clear_perms(self->permset) == -1)
957 return PyErr_SetFromErrno(PyExc_IOError);
959 /* Return the result */
964 static PyObject* Permset_get_right(PyObject *obj, void* arg) {
965 Permset_Object *self = (Permset_Object*) obj;
967 if(get_perm(self->permset, *(acl_perm_t*)arg)) {
976 static int Permset_set_right(PyObject* obj, PyObject* value, void* arg) {
977 Permset_Object *self = (Permset_Object*) obj;
981 if(!PyInt_Check(value)) {
982 PyErr_SetString(PyExc_ValueError, "a maximum of one argument must"
986 on = PyInt_AsLong(value);
988 nerr = acl_add_perm(self->permset, *(acl_perm_t*)arg);
990 nerr = acl_delete_perm(self->permset, *(acl_perm_t*)arg);
992 PyErr_SetFromErrno(PyExc_IOError);
998 static char __Permset_add_doc__[] =
999 "Add a permission to the permission set.\n"
1001 "The add() function adds the permission contained in \n"
1002 "the argument perm to the permission set. An attempt \n"
1003 "to add a permission that is already contained in the \n"
1004 "permission set is not considered an error.\n"
1007 " - perm: a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...)\n"
1009 "Return value: None\n"
1011 "Can raise: IOError\n"
1014 static PyObject* Permset_add(PyObject* obj, PyObject* args) {
1015 Permset_Object *self = (Permset_Object*) obj;
1018 if (!PyArg_ParseTuple(args, "i", &right))
1021 if(acl_add_perm(self->permset, (acl_perm_t) right) == -1)
1022 return PyErr_SetFromErrno(PyExc_IOError);
1024 /* Return the result */
1029 static char __Permset_delete_doc__[] =
1030 "Delete a permission from the permission set.\n"
1032 "The delete() function deletes the permission contained in \n"
1033 "the argument perm from the permission set. An attempt \n"
1034 "to delete a permission that is not contained in the \n"
1035 "permission set is not considered an error.\n"
1037 " - perm a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...)\n"
1038 "Return value: None\n"
1040 "Can raise: IOError\n"
1043 static PyObject* Permset_delete(PyObject* obj, PyObject* args) {
1044 Permset_Object *self = (Permset_Object*) obj;
1047 if (!PyArg_ParseTuple(args, "i", &right))
1050 if(acl_delete_perm(self->permset, (acl_perm_t) right) == -1)
1051 return PyErr_SetFromErrno(PyExc_IOError);
1053 /* Return the result */
1058 static char __Permset_test_doc__[] =
1059 "Test if a permission exists in the permission set.\n"
1061 "The test() function tests if the permission contained in \n"
1062 "the argument perm exits the permission set.\n"
1064 " - perm a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...)\n"
1065 "Return value: Boolean\n"
1067 "Can raise: IOError\n"
1070 static PyObject* Permset_test(PyObject* obj, PyObject* args) {
1071 Permset_Object *self = (Permset_Object*) obj;
1075 if (!PyArg_ParseTuple(args, "i", &right))
1078 ret = get_perm(self->permset, (acl_perm_t) right);
1080 return PyErr_SetFromErrno(PyExc_IOError);
1086 Py_INCREF(Py_False);
1093 static char __ACL_Type_doc__[] =
1094 "Type which represents a POSIX ACL\n"
1096 "Parameters (only one keword parameter should be provided):\n"
1097 " - file=\"...\", meaning create ACL representing\n"
1098 " the access ACL of that file\n"
1099 " - filedef=\"...\", meaning create ACL representing\n"
1100 " the default ACL of that directory\n"
1101 " - fd=<int>, meaning create ACL representing\n"
1102 " the access ACL of that file descriptor\n"
1103 " - text=\"...\", meaning create ACL from a \n"
1104 " textual description\n"
1105 " - acl=<ACL instance>, meaning create a copy\n"
1106 " of an existing ACL instance\n"
1107 " - mode=<int>, meaning create an ACL from a numeric mode\n"
1108 " (e.g. mode=0644) (this is valid only when the C library\n"
1109 " provides the acl_from_mode call)\n"
1111 "If no parameters are passed, create an empty ACL; this\n"
1112 "makes sense only when your OS supports ACL modification\n"
1113 "(i.e. it implements full POSIX.1e support)\n"
1116 /* ACL type methods */
1117 static PyMethodDef ACL_methods[] = {
1118 {"applyto", ACL_applyto, METH_VARARGS, __applyto_doc__},
1119 {"valid", ACL_valid, METH_NOARGS, __valid_doc__},
1121 {"to_any_text", (PyCFunction)ACL_to_any_text, METH_VARARGS | METH_KEYWORDS,
1122 __to_any_text_doc__},
1123 {"check", ACL_check, METH_NOARGS, __check_doc__},
1124 {"equiv_mode", ACL_equiv_mode, METH_NOARGS, __equiv_mode_doc__},
1126 #ifdef HAVE_ACL_COPYEXT
1127 {"__getstate__", ACL_get_state, METH_NOARGS,
1128 "Dumps the ACL to an external format."},
1129 {"__setstate__", ACL_set_state, METH_VARARGS,
1130 "Loads the ACL from an external format."},
1133 {"delete_entry", ACL_delete_entry, METH_VARARGS, __ACL_delete_entry_doc__},
1134 {"calc_mask", ACL_calc_mask, METH_NOARGS, __ACL_calc_mask_doc__},
1135 {"append", ACL_append, METH_VARARGS, __ACL_append_doc__},
1137 {NULL, NULL, 0, NULL}
1141 /* The definition of the ACL Type */
1142 static PyTypeObject ACL_Type = {
1143 PyObject_HEAD_INIT(NULL)
1148 ACL_dealloc, /* tp_dealloc */
1152 ACL_nocmp, /* tp_compare */
1154 0, /* tp_as_number */
1155 0, /* tp_as_sequence */
1156 0, /* tp_as_mapping */
1159 ACL_str, /* tp_str */
1160 0, /* tp_getattro */
1161 0, /* tp_setattro */
1162 0, /* tp_as_buffer */
1163 Py_TPFLAGS_DEFAULT, /* tp_flags */
1164 __ACL_Type_doc__, /* tp_doc */
1165 0, /* tp_traverse */
1168 ACL_richcompare, /* tp_richcompare */
1170 0, /* tp_richcompare */
1172 0, /* tp_weaklistoffset */
1178 0, /* tp_iternext */
1180 ACL_methods, /* tp_methods */
1185 0, /* tp_descr_get */
1186 0, /* tp_descr_set */
1187 0, /* tp_dictoffset */
1188 ACL_init, /* tp_init */
1190 ACL_new, /* tp_new */
1195 /* Entry type methods */
1196 static PyMethodDef Entry_methods[] = {
1197 {"copy", Entry_copy, METH_VARARGS, __Entry_copy_doc__},
1198 {NULL, NULL, 0, NULL}
1201 static char __Entry_tagtype_doc__[] =
1202 "The tag type of the current entry\n"
1205 " - ACL_UNDEFINED_TAG\n"
1208 " - ACL_GROUP_OBJ\n"
1214 static char __Entry_qualifier_doc__[] =
1215 "The qualifier of the current entry\n"
1217 "If the tag type is ACL_USER, this should be a user id.\n"
1218 "If the tag type if ACL_GROUP, this should be a group id.\n"
1219 "Else, it doesn't matter.\n"
1222 static char __Entry_parent_doc__[] =
1223 "The parent ACL of this entry\n"
1226 static char __Entry_permset_doc__[] =
1227 "The permission set of this ACL entry\n"
1231 static PyGetSetDef Entry_getsets[] = {
1232 {"tag_type", Entry_get_tag_type, Entry_set_tag_type,
1233 __Entry_tagtype_doc__},
1234 {"qualifier", Entry_get_qualifier, Entry_set_qualifier,
1235 __Entry_qualifier_doc__},
1236 {"parent", Entry_get_parent, NULL, __Entry_parent_doc__},
1237 {"permset", Entry_get_permset, Entry_set_permset, __Entry_permset_doc__},
1241 static char __Entry_Type_doc__[] =
1242 "Type which represents an entry in an ACL.\n"
1244 "The type exists only if the OS has full support for POSIX.1e\n"
1245 "Can be created either by:\n"
1247 " >>> e = posix1e.Entry(myACL) # this creates a new entry in the ACL\n"
1248 " >>> e = myACL.append() # another way for doing the same thing\n"
1251 " >>> for entry in myACL:\n"
1252 " ... print entry\n"
1254 "Note that the Entry keeps a reference to its ACL, so even if \n"
1255 "you delete the ACL, it won't be cleaned up and will continue to \n"
1256 "exist until its Entry(ies) will be deleted.\n"
1258 /* The definition of the Entry Type */
1259 static PyTypeObject Entry_Type = {
1260 PyObject_HEAD_INIT(NULL)
1263 sizeof(Entry_Object),
1265 Entry_dealloc, /* tp_dealloc */
1271 0, /* tp_as_number */
1272 0, /* tp_as_sequence */
1273 0, /* tp_as_mapping */
1276 Entry_str, /* tp_str */
1277 0, /* tp_getattro */
1278 0, /* tp_setattro */
1279 0, /* tp_as_buffer */
1280 Py_TPFLAGS_DEFAULT, /* tp_flags */
1281 __Entry_Type_doc__, /* tp_doc */
1282 0, /* tp_traverse */
1284 0, /* tp_richcompare */
1285 0, /* tp_weaklistoffset */
1287 0, /* tp_iternext */
1288 Entry_methods, /* tp_methods */
1290 Entry_getsets, /* tp_getset */
1293 0, /* tp_descr_get */
1294 0, /* tp_descr_set */
1295 0, /* tp_dictoffset */
1296 Entry_init, /* tp_init */
1298 Entry_new, /* tp_new */
1301 /* Permset type methods */
1302 static PyMethodDef Permset_methods[] = {
1303 {"clear", Permset_clear, METH_NOARGS, __Permset_clear_doc__, },
1304 {"add", Permset_add, METH_VARARGS, __Permset_add_doc__, },
1305 {"delete", Permset_delete, METH_VARARGS, __Permset_delete_doc__, },
1306 {"test", Permset_test, METH_VARARGS, __Permset_test_doc__, },
1307 {NULL, NULL, 0, NULL}
1310 static char __Permset_execute_doc__[] =
1311 "Execute permsission\n"
1313 "This is a convenience method of access; the \n"
1314 "same effect can be achieved using the functions\n"
1315 "add(), test(), delete(), and those can take any \n"
1316 "permission defined by your platform.\n"
1319 static char __Permset_read_doc__[] =
1320 "Read permsission\n"
1322 "This is a convenience method of access; the \n"
1323 "same effect can be achieved using the functions\n"
1324 "add(), test(), delete(), and those can take any \n"
1325 "permission defined by your platform.\n"
1328 static char __Permset_write_doc__[] =
1329 "Write permsission\n"
1331 "This is a convenience method of access; the \n"
1332 "same effect can be achieved using the functions\n"
1333 "add(), test(), delete(), and those can take any \n"
1334 "permission defined by your platform.\n"
1337 /* Permset getset */
1338 static PyGetSetDef Permset_getsets[] = {
1339 {"execute", Permset_get_right, Permset_set_right,
1340 __Permset_execute_doc__, &holder_ACL_EXECUTE},
1341 {"read", Permset_get_right, Permset_set_right,
1342 __Permset_read_doc__, &holder_ACL_READ},
1343 {"write", Permset_get_right, Permset_set_right,
1344 __Permset_write_doc__, &holder_ACL_WRITE},
1348 static char __Permset_Type_doc__[] =
1349 "Type which represents the permission set in an ACL entry\n"
1351 "The type exists only if the OS has full support for POSIX.1e\n"
1352 "Can be retrieved either by:\n\n"
1353 ">>> perms = myEntry.permset\n"
1356 ">>> perms = posix1e.Permset(myEntry)\n"
1358 "Note that the Permset keeps a reference to its Entry, so even if \n"
1359 "you delete the entry, it won't be cleaned up and will continue to \n"
1360 "exist until its Permset will be deleted.\n"
1363 /* The definition of the Permset Type */
1364 static PyTypeObject Permset_Type = {
1365 PyObject_HEAD_INIT(NULL)
1368 sizeof(Permset_Object),
1370 Permset_dealloc, /* tp_dealloc */
1376 0, /* tp_as_number */
1377 0, /* tp_as_sequence */
1378 0, /* tp_as_mapping */
1381 Permset_str, /* tp_str */
1382 0, /* tp_getattro */
1383 0, /* tp_setattro */
1384 0, /* tp_as_buffer */
1385 Py_TPFLAGS_DEFAULT, /* tp_flags */
1386 __Permset_Type_doc__,/* tp_doc */
1387 0, /* tp_traverse */
1389 0, /* tp_richcompare */
1390 0, /* tp_weaklistoffset */
1392 0, /* tp_iternext */
1393 Permset_methods, /* tp_methods */
1395 Permset_getsets, /* tp_getset */
1398 0, /* tp_descr_get */
1399 0, /* tp_descr_set */
1400 0, /* tp_dictoffset */
1401 Permset_init, /* tp_init */
1403 Permset_new, /* tp_new */
1408 /* Module methods */
1410 static char __deletedef_doc__[] =
1411 "Delete the default ACL from a directory.\n"
1413 "This function deletes the default ACL associated with \n"
1414 "a directory (the ACL which will be ANDed with the mode\n"
1415 "parameter to the open, creat functions).\n"
1417 " - a string representing the directory whose default ACL\n"
1418 " should be deleted\n"
1421 /* Deletes the default ACL from a directory */
1422 static PyObject* aclmodule_delete_default(PyObject* obj, PyObject* args) {
1425 /* Parse the arguments */
1426 if (!PyArg_ParseTuple(args, "s", &filename))
1429 if(acl_delete_def_file(filename) == -1) {
1430 return PyErr_SetFromErrno(PyExc_IOError);
1433 /* Return the result */
1439 static char __has_extended_doc__[] =
1440 "Check if a file or filehandle has an extended ACL.\n"
1443 " - either a filename or a file-like object or an integer; this\n"
1444 " represents the filesystem object on which to act\n"
1447 /* Check for extended ACL a file or fd */
1448 static PyObject* aclmodule_has_extended(PyObject* obj, PyObject* args) {
1453 if (!PyArg_ParseTuple(args, "O", &myarg))
1456 if(PyBytes_Check(myarg)) {
1457 const char *filename = PyBytes_AS_STRING(myarg);
1458 nret = acl_extended_file(filename);
1459 } else if((fd = PyObject_AsFileDescriptor(myarg)) != -1) {
1460 nret = acl_extended_fd(fd);
1462 PyErr_SetString(PyExc_TypeError, "argument 1 must be string, int,"
1463 " or file-like object");
1467 return PyErr_SetFromErrno(PyExc_IOError);
1470 /* Return the result */
1471 return PyBool_FromLong(nret);
1475 /* The module methods */
1476 static PyMethodDef aclmodule_methods[] = {
1477 {"delete_default", aclmodule_delete_default, METH_VARARGS,
1480 {"has_extended", aclmodule_has_extended, METH_VARARGS,
1481 __has_extended_doc__},
1483 {NULL, NULL, 0, NULL}
1486 static char __posix1e_doc__[] =
1487 "POSIX.1e ACLs manipulation\n"
1489 "This module provides support for manipulating POSIX.1e ACLS\n"
1491 "Depending on the operating system support for POSIX.1e, \n"
1492 "the ACL type will have more or less capabilities:\n"
1493 " - level 1, only basic support, you can create\n"
1494 " ACLs from files and text descriptions;\n"
1495 " once created, the type is immutable\n"
1496 " - level 2, complete support, you can alter\n"
1497 " the ACL once it is created\n"
1499 "Also, in level 2, more types are available, corresponding\n"
1500 "to acl_entry_t (the Entry type), acl_permset_t (the Permset type).\n"
1502 "The existence of level 2 support and other extensions can be\n"
1503 "checked by the constants:\n"
1504 " - HAS_ACL_ENTRY for level 2 and the Entry/Permset classes\n"
1505 " - HAS_ACL_FROM_MODE for ACL(mode=...) usage\n"
1506 " - HAS_ACL_CHECK for the ACL().check function\n"
1507 " - HAS_EXTENDED_CHECK for the module-level has_extended function\n"
1508 " - HAS_EQUIV_MODE for the ACL().equiv_mode method\n"
1512 ">>> import posix1e\n"
1513 ">>> acl1 = posix1e.ACL(file=\"file.txt\") \n"
1519 ">>> b = posix1e.ACL(text=\"u::rx,g::-,o::-\")\n"
1525 ">>> b.applyto(\"file.txt\")\n"
1526 ">>> print posix1e.ACL(file=\"file.txt\")\n"
1534 void initposix1e(void) {
1537 ACL_Type.ob_type = &PyType_Type;
1538 if(PyType_Ready(&ACL_Type) < 0)
1542 Entry_Type.ob_type = &PyType_Type;
1543 if(PyType_Ready(&Entry_Type) < 0)
1546 Permset_Type.ob_type = &PyType_Type;
1547 if(PyType_Ready(&Permset_Type) < 0)
1551 m = Py_InitModule3("posix1e", aclmodule_methods, __posix1e_doc__);
1553 d = PyModule_GetDict(m);
1557 Py_INCREF(&ACL_Type);
1558 if (PyDict_SetItemString(d, "ACL",
1559 (PyObject *) &ACL_Type) < 0)
1562 /* 23.3.6 acl_type_t values */
1563 PyModule_AddIntConstant(m, "ACL_TYPE_ACCESS", ACL_TYPE_ACCESS);
1564 PyModule_AddIntConstant(m, "ACL_TYPE_DEFAULT", ACL_TYPE_DEFAULT);
1568 Py_INCREF(&Entry_Type);
1569 if (PyDict_SetItemString(d, "Entry",
1570 (PyObject *) &Entry_Type) < 0)
1573 Py_INCREF(&Permset_Type);
1574 if (PyDict_SetItemString(d, "Permset",
1575 (PyObject *) &Permset_Type) < 0)
1578 /* 23.2.2 acl_perm_t values */
1579 PyModule_AddIntConstant(m, "ACL_READ", ACL_READ);
1580 PyModule_AddIntConstant(m, "ACL_WRITE", ACL_WRITE);
1581 PyModule_AddIntConstant(m, "ACL_EXECUTE", ACL_EXECUTE);
1583 /* 23.2.5 acl_tag_t values */
1584 PyModule_AddIntConstant(m, "ACL_UNDEFINED_TAG", ACL_UNDEFINED_TAG);
1585 PyModule_AddIntConstant(m, "ACL_USER_OBJ", ACL_USER_OBJ);
1586 PyModule_AddIntConstant(m, "ACL_USER", ACL_USER);
1587 PyModule_AddIntConstant(m, "ACL_GROUP_OBJ", ACL_GROUP_OBJ);
1588 PyModule_AddIntConstant(m, "ACL_GROUP", ACL_GROUP);
1589 PyModule_AddIntConstant(m, "ACL_MASK", ACL_MASK);
1590 PyModule_AddIntConstant(m, "ACL_OTHER", ACL_OTHER);
1592 /* Document extended functionality via easy-to-use constants */
1593 PyModule_AddIntConstant(m, "HAS_ACL_ENTRY", 1);
1595 PyModule_AddIntConstant(m, "HAS_ACL_ENTRY", 0);
1599 /* Linux libacl specific acl_to_any_text constants */
1600 PyModule_AddIntConstant(m, "TEXT_ABBREVIATE", TEXT_ABBREVIATE);
1601 PyModule_AddIntConstant(m, "TEXT_NUMERIC_IDS", TEXT_NUMERIC_IDS);
1602 PyModule_AddIntConstant(m, "TEXT_SOME_EFFECTIVE", TEXT_SOME_EFFECTIVE);
1603 PyModule_AddIntConstant(m, "TEXT_ALL_EFFECTIVE", TEXT_ALL_EFFECTIVE);
1604 PyModule_AddIntConstant(m, "TEXT_SMART_INDENT", TEXT_SMART_INDENT);
1606 /* Linux libacl specific acl_check constants */
1607 PyModule_AddIntConstant(m, "ACL_MULTI_ERROR", ACL_MULTI_ERROR);
1608 PyModule_AddIntConstant(m, "ACL_DUPLICATE_ERROR", ACL_DUPLICATE_ERROR);
1609 PyModule_AddIntConstant(m, "ACL_MISS_ERROR", ACL_MISS_ERROR);
1610 PyModule_AddIntConstant(m, "ACL_ENTRY_ERROR", ACL_ENTRY_ERROR);
1612 /* declare the Linux extensions */
1613 PyModule_AddIntConstant(m, "HAS_ACL_FROM_MODE", 1);
1614 PyModule_AddIntConstant(m, "HAS_ACL_CHECK", 1);
1615 PyModule_AddIntConstant(m, "HAS_EXTENDED_CHECK", 1);
1616 PyModule_AddIntConstant(m, "HAS_EQUIV_MODE", 1);
1618 PyModule_AddIntConstant(m, "HAS_ACL_FROM_MODE", 0);
1619 PyModule_AddIntConstant(m, "HAS_ACL_CHECK", 0);
1620 PyModule_AddIntConstant(m, "HAS_EXTENDED_CHECK", 0);
1621 PyModule_AddIntConstant(m, "HAS_EQUIV_MODE", 0);