]> git.k1024.org Git - pylibacl.git/blob - acl.c
Minor change on how error handling style in ACL_init
[pylibacl.git] / acl.c
1 /*
2     posix1e - a python module exposing the posix acl functions
3
4     Copyright (C) 2002-2009, 2012, 2014, 2015 Iustin Pop <iustin@k1024.org>
5
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.
10
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.
15
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
19     02110-1301  USA
20
21 */
22
23 #include <Python.h>
24
25 #include <sys/types.h>
26 #include <sys/acl.h>
27
28 #ifdef HAVE_LINUX
29 #include <acl/libacl.h>
30 #define get_perm acl_get_perm
31 #elif HAVE_FREEBSD
32 #define get_perm acl_get_perm_np
33 #endif
34
35 /* Used for cpychecker: */
36 /* The checker automatically defines this preprocessor name when creating
37    the custom attribute: */
38 #if defined(WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE)
39 #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename) \
40   __attribute__((cpychecker_type_object_for_typedef(typename)))
41 #else
42 /* This handles the case where we're compiling with a "vanilla"
43    compiler that doesn't supply this attribute: */
44 #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename)
45 #endif
46
47 /* The checker automatically defines this preprocessor name when creating
48    the custom attribute: */
49 #if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
50    #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION \
51 __attribute__((cpychecker_negative_result_sets_exception))
52    #else
53    #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
54 #endif
55
56 static PyTypeObject ACL_Type
57   CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF("ACL_Object");
58 static PyObject* ACL_applyto(PyObject* obj, PyObject* args);
59 static PyObject* ACL_valid(PyObject* obj, PyObject* args);
60
61 #ifdef HAVE_ACL_COPY_EXT
62 static PyObject* ACL_get_state(PyObject *obj, PyObject* args);
63 static PyObject* ACL_set_state(PyObject *obj, PyObject* args);
64 #endif
65
66 #ifdef HAVE_LEVEL2
67 static PyTypeObject Entry_Type
68   CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF("Entry_Object");
69 static PyTypeObject Permset_Type
70   CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF("Permset_Object");
71 static PyObject* Permset_new(PyTypeObject* type, PyObject* args,
72                              PyObject *keywds);
73 #endif
74
75 static acl_perm_t holder_ACL_EXECUTE = ACL_EXECUTE;
76 static acl_perm_t holder_ACL_READ = ACL_READ;
77 static acl_perm_t holder_ACL_WRITE = ACL_WRITE;
78
79 typedef struct {
80     PyObject_HEAD
81     acl_t acl;
82 #ifdef HAVE_LEVEL2
83     int entry_id;
84 #endif
85 } ACL_Object;
86
87 #ifdef HAVE_LEVEL2
88
89 typedef struct {
90     PyObject_HEAD
91     PyObject *parent_acl; /* The parent acl, so it won't run out on us */
92     acl_entry_t entry;
93 } Entry_Object;
94
95 typedef struct {
96     PyObject_HEAD
97     PyObject *parent_entry; /* The parent entry, so it won't run out on us */
98     acl_permset_t permset;
99 } Permset_Object;
100
101 #endif
102
103 /* Creation of a new ACL instance */
104 static PyObject* ACL_new(PyTypeObject* type, PyObject* args,
105                          PyObject *keywds) {
106     PyObject* newacl;
107     ACL_Object *acl;
108
109     newacl = type->tp_alloc(type, 0);
110
111     if(newacl == NULL) {
112         return NULL;
113     }
114     acl = (ACL_Object*) newacl;
115
116     acl->acl = acl_init(0);
117     if (acl->acl == NULL) {
118         PyErr_SetFromErrno(PyExc_IOError);
119         Py_DECREF(newacl);
120         return NULL;
121     }
122 #ifdef HAVEL_LEVEL2
123     acl->entry_id = ACL_FIRST_ENTRY;
124 #endif
125
126     return newacl;
127 }
128
129 /* Initialization of a new ACL instance */
130 static int ACL_init(PyObject* obj, PyObject* args, PyObject *keywds) {
131     ACL_Object* self = (ACL_Object*) obj;
132 #ifdef HAVE_LINUX
133     static char *kwlist[] = { "file", "fd", "text", "acl", "filedef",
134                               "mode", NULL };
135     char *format = "|O&OsO!O&i";
136     int mode = -1;
137 #else
138     static char *kwlist[] = { "file", "fd", "text", "acl", "filedef", NULL };
139     char *format = "|O&OsO!O&";
140 #endif
141     PyObject *file = NULL;
142     PyObject *filedef = NULL;
143     char *text = NULL;
144     PyObject *fd = NULL;
145     ACL_Object* thesrc = NULL;
146
147     if(!PyTuple_Check(args) || PyTuple_Size(args) != 0 ||
148        (keywds != NULL && PyDict_Check(keywds) && PyDict_Size(keywds) > 1)) {
149         PyErr_SetString(PyExc_ValueError, "a max of one keyword argument"
150                         " must be passed");
151         return -1;
152     }
153     if(!PyArg_ParseTupleAndKeywords(args, keywds, format, kwlist,
154                                     PyUnicode_FSConverter, &file,
155                                     &fd, &text, &ACL_Type, &thesrc,
156                                     PyUnicode_FSConverter, &filedef
157 #ifdef HAVE_LINUX
158                                     , &mode
159 #endif
160                                     ))
161         return -1;
162
163     /* Free the old acl_t without checking for error, we don't
164      * care right now */
165     if(self->acl != NULL)
166         acl_free(self->acl);
167
168     self->acl = NULL;
169
170     if(file != NULL) {
171         fprintf(stderr, "foobar!\n");
172         char *path = PyBytes_AS_STRING(file);
173         self->acl = acl_get_file(path, ACL_TYPE_ACCESS);
174         Py_DECREF(file);
175     } else if(text != NULL)
176         self->acl = acl_from_text(text);
177     else if(fd != NULL) {
178         int fdval;
179         if ((fdval = PyObject_AsFileDescriptor(fd)) != -1) {
180             self->acl = acl_get_fd(fdval);
181         }
182     } else if(thesrc != NULL)
183         self->acl = acl_dup(thesrc->acl);
184     else if(filedef != NULL) {
185         char *path = PyBytes_AS_STRING(filedef);
186         self->acl = acl_get_file(path, ACL_TYPE_DEFAULT);
187         Py_DECREF(path);
188     }
189 #ifdef HAVE_LINUX
190     else if(mode != -1)
191         self->acl = acl_from_mode(mode);
192 #endif
193     else
194         self->acl = acl_init(0);
195
196     if(self->acl == NULL) {
197         PyErr_SetFromErrno(PyExc_IOError);
198         return -1;
199     }
200
201     return 0;
202 }
203
204 /* Standard type functions */
205 static void ACL_dealloc(PyObject* obj) {
206     ACL_Object *self = (ACL_Object*) obj;
207     PyObject *err_type, *err_value, *err_traceback;
208     int have_error = PyErr_Occurred() ? 1 : 0;
209
210     if (have_error)
211         PyErr_Fetch(&err_type, &err_value, &err_traceback);
212     if(self->acl != NULL && acl_free(self->acl) != 0)
213         PyErr_WriteUnraisable(obj);
214     if (have_error)
215         PyErr_Restore(err_type, err_value, err_traceback);
216     PyObject_DEL(self);
217 }
218
219 /* Converts the acl to a text format */
220 static PyObject* ACL_str(PyObject *obj) {
221     char *text;
222     ACL_Object *self = (ACL_Object*) obj;
223     PyObject *ret;
224
225     text = acl_to_text(self->acl, NULL);
226     if(text == NULL) {
227         return PyErr_SetFromErrno(PyExc_IOError);
228     }
229     ret = PyUnicode_FromString(text);
230     if(acl_free(text) != 0) {
231         Py_XDECREF(ret);
232         return PyErr_SetFromErrno(PyExc_IOError);
233     }
234     return ret;
235 }
236
237 #ifdef HAVE_LINUX
238 static char __to_any_text_doc__[] =
239   "to_any_text([prefix='', separator='n', options=0])\n"
240   "Convert the ACL to a custom text format.\n"
241   "\n"
242   "This method encapsulates the ``acl_to_any_text()`` function.\n"
243   "It allows a customized text format to be generated for the ACL. See\n"
244   ":manpage:`acl_to_any_text(3)` for more details.\n"
245   "\n"
246   ":param string prefix: if given, this string will be pre-pended to\n"
247   "   all lines\n"
248   ":param string separator: a single character (defaults to '\\n'); this will"
249     " be used to separate the entries in the ACL\n"
250   ":param options: a bitwise combination of:\n\n"
251   "    - :py:data:`TEXT_ABBREVIATE`: use 'u' instead of 'user', 'g' \n"
252   "      instead of 'group', etc.\n"
253   "    - :py:data:`TEXT_NUMERIC_IDS`: User and group IDs are included as\n"
254   "      decimal numbers instead of names\n"
255   "    - :py:data:`TEXT_SOME_EFFECTIVE`: Include comments denoting the\n"
256   "      effective permissions when some are masked\n"
257   "    - :py:data:`TEXT_ALL_EFFECTIVE`: Include comments after all ACL\n"
258   "      entries affected by an ACL_MASK entry\n"
259   "    - :py:data:`TEXT_SMART_INDENT`: Used in combination with the\n"
260   "      _EFFECTIVE options, this will ensure that comments are aligned\n"
261   "      to the fourth tab position (assuming one tab equals eight spaces)\n"
262   ":rtype: string\n"
263   ;
264
265 /* Converts the acl to a custom text format */
266 static PyObject* ACL_to_any_text(PyObject *obj, PyObject *args,
267                                  PyObject *kwds) {
268     char *text;
269     ACL_Object *self = (ACL_Object*) obj;
270     PyObject *ret;
271     const char *arg_prefix = NULL;
272     char arg_separator = '\n';
273     int arg_options = 0;
274     static char *kwlist[] = {"prefix", "separator", "options", NULL};
275
276     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sci", kwlist, &arg_prefix,
277                                      &arg_separator, &arg_options))
278       return NULL;
279
280     text = acl_to_any_text(self->acl, arg_prefix, arg_separator, arg_options);
281     if(text == NULL) {
282         return PyErr_SetFromErrno(PyExc_IOError);
283     }
284     ret = PyBytes_FromString(text);
285     if(acl_free(text) != 0) {
286         Py_XDECREF(ret);
287         return PyErr_SetFromErrno(PyExc_IOError);
288     }
289     return ret;
290 }
291
292 static char __check_doc__[] =
293     "Check the ACL validity.\n"
294     "\n"
295     "This is a non-portable, Linux specific extension that allow more\n"
296     "information to be retrieved in case an ACL is not valid than via the\n"
297     ":py:func:`valid` method.\n"
298     "\n"
299     "This method will return either False (the ACL is valid), or a tuple\n"
300     "with two elements. The first element is one of the following\n"
301     "constants:\n\n"
302     "  - :py:data:`ACL_MULTI_ERROR`: The ACL contains multiple entries that\n"
303     "    have a tag type that may occur at most once\n"
304     "  - :py:data:`ACL_DUPLICATE_ERROR`: The ACL contains multiple \n"
305     "    :py:data:`ACL_USER` or :py:data:`ACL_GROUP` entries with the\n"
306     "    same ID\n"
307     "  - :py:data:`ACL_MISS_ERROR`: A required entry is missing\n"
308     "  - :py:data:`ACL_ENTRY_ERROR`: The ACL contains an invalid entry\n"
309     "    tag type\n"
310     "\n"
311     "The second element of the tuple is the index of the entry that is\n"
312     "invalid (in the same order as by iterating over the ACL entry)\n"
313     ;
314
315 /* The acl_check method */
316 static PyObject* ACL_check(PyObject* obj, PyObject* args) {
317     ACL_Object *self = (ACL_Object*) obj;
318     int result;
319     int eindex;
320
321     if((result = acl_check(self->acl, &eindex)) == -1)
322         return PyErr_SetFromErrno(PyExc_IOError);
323     if(result == 0) {
324         Py_RETURN_FALSE;
325     }
326     return Py_BuildValue("(ii)", result, eindex);
327 }
328
329 /* Implementation of the rich compare for ACLs */
330 static PyObject* ACL_richcompare(PyObject* o1, PyObject* o2, int op) {
331     ACL_Object *acl1, *acl2;
332     int n;
333     PyObject *ret;
334
335     if(!PyObject_IsInstance(o2, (PyObject*)&ACL_Type)) {
336         if(op == Py_EQ)
337             Py_RETURN_FALSE;
338         if(op == Py_NE)
339             Py_RETURN_TRUE;
340         PyErr_SetString(PyExc_TypeError, "can only compare to an ACL");
341         return NULL;
342     }
343
344     acl1 = (ACL_Object*)o1;
345     acl2 = (ACL_Object*)o2;
346     if((n=acl_cmp(acl1->acl, acl2->acl))==-1)
347         return PyErr_SetFromErrno(PyExc_IOError);
348     switch(op) {
349     case Py_EQ:
350         ret = n == 0 ? Py_True : Py_False;
351         break;
352     case Py_NE:
353         ret = n == 1 ? Py_True : Py_False;
354         break;
355     default:
356         PyErr_SetString(PyExc_TypeError, "ACLs are not orderable");
357         return NULL;
358     }
359     Py_INCREF(ret);
360     return ret;
361 }
362
363 static char __equiv_mode_doc__[] =
364     "Return the octal mode the ACL is equivalent to.\n"
365     "\n"
366     "This is a non-portable, Linux specific extension that checks\n"
367     "if the ACL is a basic ACL and returns the corresponding mode.\n"
368     "\n"
369     ":rtype: integer\n"
370     ":raise IOError: An IOerror exception will be raised if the ACL is\n"
371     "    an extended ACL.\n"
372     ;
373
374 /* The acl_equiv_mode method */
375 static PyObject* ACL_equiv_mode(PyObject* obj, PyObject* args) {
376     ACL_Object *self = (ACL_Object*) obj;
377     mode_t mode;
378
379     if(acl_equiv_mode(self->acl, &mode) == -1)
380         return PyErr_SetFromErrno(PyExc_IOError);
381     return PyLong_FromLong(mode);
382 }
383 #endif
384
385 /* Custom methods */
386 static char __applyto_doc__[] =
387     "applyto(item[, flag=ACL_TYPE_ACCESS])\n"
388     "Apply the ACL to a file or filehandle.\n"
389     "\n"
390     ":param item: either a filename or a file-like object or an integer;\n"
391     "    this represents the filesystem object on which to act\n"
392     ":param flag: optional flag representing the type of ACL to set, either\n"
393     "    :py:data:`ACL_TYPE_ACCESS` (default) or :py:data:`ACL_TYPE_DEFAULT`\n"
394     ;
395
396 /* Applies the ACL to a file */
397 static PyObject* ACL_applyto(PyObject* obj, PyObject* args) {
398     ACL_Object *self = (ACL_Object*) obj;
399     PyObject *target, *tmp;
400     acl_type_t type = ACL_TYPE_ACCESS;
401     int nret;
402     int fd;
403
404     if (!PyArg_ParseTuple(args, "O|I", &target, &type))
405         return NULL;
406     if ((fd = PyObject_AsFileDescriptor(target)) != -1) {
407         if((nret = acl_set_fd(fd, self->acl)) == -1) {
408           PyErr_SetFromErrno(PyExc_IOError);
409         }
410     } else {
411       // PyObject_AsFileDescriptor sets an error when failing, so clear
412       // it such that further code works; some method lookups fail if an
413       // error already occured when called, which breaks at least
414       // PyOS_FSPath (called by FSConverter).
415       PyErr_Clear();
416       if(PyUnicode_FSConverter(target, &tmp)) {
417         char *filename = PyBytes_AS_STRING(tmp);
418         if ((nret = acl_set_file(filename, type, self->acl)) == -1) {
419             PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
420         }
421         Py_DECREF(tmp);
422       } else {
423         nret = -1;
424       }
425     }
426     if (nret < 0) {
427         return NULL;
428     } else {
429         Py_RETURN_NONE;
430     }
431 }
432
433 static char __valid_doc__[] =
434     "Test the ACL for validity.\n"
435     "\n"
436     "This method tests the ACL to see if it is a valid ACL\n"
437     "in terms of the file-system. More precisely, it checks that:\n"
438     "\n"
439     "The ACL contains exactly one entry with each of the\n"
440     ":py:data:`ACL_USER_OBJ`, :py:data:`ACL_GROUP_OBJ`, and \n"
441     ":py:data:`ACL_OTHER` tag types. Entries\n"
442     "with :py:data:`ACL_USER` and :py:data:`ACL_GROUP` tag types may\n"
443     "appear zero or more\n"
444     "times in an ACL. An ACL that contains entries of :py:data:`ACL_USER` or\n"
445     ":py:data:`ACL_GROUP` tag types must contain exactly one entry of the \n"
446     ":py:data:`ACL_MASK` tag type. If an ACL contains no entries of\n"
447     ":py:data:`ACL_USER` or :py:data:`ACL_GROUP` tag types, the\n"
448     ":py:data:`ACL_MASK` entry is optional.\n"
449     "\n"
450     "All user ID qualifiers must be unique among all entries of\n"
451     "the :py:data:`ACL_USER` tag type, and all group IDs must be unique\n"
452     "among all entries of :py:data:`ACL_GROUP` tag type.\n"
453     "\n"
454     "The method will return 1 for a valid ACL and 0 for an invalid one.\n"
455     "This has been chosen because the specification for\n"
456     ":manpage:`acl_valid(3)`\n"
457     "in the POSIX.1e standard documents only one possible value for errno\n"
458     "in case of an invalid ACL, so we can't differentiate between\n"
459     "classes of errors. Other suggestions are welcome.\n"
460     "\n"
461     ":return: 0 or 1\n"
462     ":rtype: integer\n"
463     ;
464
465 /* Checks the ACL for validity */
466 static PyObject* ACL_valid(PyObject* obj, PyObject* args) {
467     ACL_Object *self = (ACL_Object*) obj;
468
469     if(acl_valid(self->acl) == -1) {
470         Py_RETURN_FALSE;
471     } else {
472         Py_RETURN_TRUE;
473     }
474 }
475
476 #ifdef HAVE_ACL_COPY_EXT
477 static PyObject* ACL_get_state(PyObject *obj, PyObject* args) {
478     ACL_Object *self = (ACL_Object*) obj;
479     PyObject *ret;
480     ssize_t size, nsize;
481     char *buf;
482
483     size = acl_size(self->acl);
484     if(size == -1)
485         return PyErr_SetFromErrno(PyExc_IOError);
486
487     if((ret = PyBytes_FromStringAndSize(NULL, size)) == NULL)
488         return NULL;
489     buf = PyBytes_AsString(ret);
490
491     if((nsize = acl_copy_ext(buf, self->acl, size)) == -1) {
492         Py_DECREF(ret);
493         return PyErr_SetFromErrno(PyExc_IOError);
494     }
495
496     return ret;
497 }
498
499 static PyObject* ACL_set_state(PyObject *obj, PyObject* args) {
500     ACL_Object *self = (ACL_Object*) obj;
501     const void *buf;
502     int bufsize;
503     acl_t ptr;
504
505     /* Parse the argument */
506     if (!PyArg_ParseTuple(args, "s#", &buf, &bufsize))
507         return NULL;
508
509     /* Try to import the external representation */
510     if((ptr = acl_copy_int(buf)) == NULL)
511         return PyErr_SetFromErrno(PyExc_IOError);
512
513     /* Free the old acl. Should we ignore errors here? */
514     if(self->acl != NULL) {
515         if(acl_free(self->acl) == -1)
516             return PyErr_SetFromErrno(PyExc_IOError);
517     }
518
519     self->acl = ptr;
520
521     Py_RETURN_NONE;
522 }
523 #endif
524
525 #ifdef HAVE_LEVEL2
526
527 /* tp_iter for the ACL type; since it can be iterated only
528  * destructively, the type is its iterator
529  */
530 static PyObject* ACL_iter(PyObject *obj) {
531     ACL_Object *self = (ACL_Object*)obj;
532     self->entry_id = ACL_FIRST_ENTRY;
533     Py_INCREF(obj);
534     return obj;
535 }
536
537 /* the tp_iternext function for the ACL type */
538 static PyObject* ACL_iternext(PyObject *obj) {
539     ACL_Object *self = (ACL_Object*)obj;
540     acl_entry_t the_entry_t;
541     Entry_Object *the_entry_obj;
542     int nerr;
543
544     nerr = acl_get_entry(self->acl, self->entry_id, &the_entry_t);
545     self->entry_id = ACL_NEXT_ENTRY;
546     if(nerr == -1)
547         return PyErr_SetFromErrno(PyExc_IOError);
548     else if(nerr == 0) {
549         /* Docs says this is not needed */
550         /*PyErr_SetObject(PyExc_StopIteration, Py_None);*/
551         return NULL;
552     }
553
554     the_entry_obj = (Entry_Object*) PyType_GenericNew(&Entry_Type, NULL, NULL);
555     if(the_entry_obj == NULL)
556         return NULL;
557
558     the_entry_obj->entry = the_entry_t;
559
560     the_entry_obj->parent_acl = obj;
561     Py_INCREF(obj); /* For the reference we have in entry->parent */
562
563     return (PyObject*)the_entry_obj;
564 }
565
566 static char __ACL_delete_entry_doc__[] =
567     "delete_entry(entry)\n"
568     "Deletes an entry from the ACL.\n"
569     "\n"
570     ".. note:: Only available with level 2.\n"
571     "\n"
572     ":param entry: the Entry object which should be deleted; note that after\n"
573     "    this function is called, that object is unusable any longer\n"
574     "    and should be deleted\n"
575     ;
576
577 /* Deletes an entry from the ACL */
578 static PyObject* ACL_delete_entry(PyObject *obj, PyObject *args) {
579     ACL_Object *self = (ACL_Object*)obj;
580     Entry_Object *e;
581
582     if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &e))
583         return NULL;
584
585     if (e->parent_acl != obj) {
586         PyErr_SetString(PyExc_ValueError,
587                         "Can't remove un-owned entry");
588         return NULL;
589     }
590     if(acl_delete_entry(self->acl, e->entry) == -1)
591         return PyErr_SetFromErrno(PyExc_IOError);
592
593     Py_RETURN_NONE;
594 }
595
596 static char __ACL_calc_mask_doc__[] =
597     "Compute the file group class mask.\n"
598     "\n"
599     "The calc_mask() method calculates and sets the permissions \n"
600     "associated with the :py:data:`ACL_MASK` Entry of the ACL.\n"
601     "The value of the new permissions is the union of the permissions \n"
602     "granted by all entries of tag type :py:data:`ACL_GROUP`, \n"
603     ":py:data:`ACL_GROUP_OBJ`, or \n"
604     ":py:data:`ACL_USER`. If the ACL already contains an :py:data:`ACL_MASK`\n"
605     "entry, its \n"
606     "permissions are overwritten; if it does not contain an \n"
607     ":py:data:`ACL_MASK` Entry, one is added.\n"
608     "\n"
609     "The order of existing entries in the ACL is undefined after this \n"
610     "function.\n"
611     ;
612
613 /* Updates the mask entry in the ACL */
614 static PyObject* ACL_calc_mask(PyObject *obj, PyObject *args) {
615     ACL_Object *self = (ACL_Object*)obj;
616
617     if(acl_calc_mask(&self->acl) == -1)
618         return PyErr_SetFromErrno(PyExc_IOError);
619
620     Py_RETURN_NONE;
621 }
622
623 static char __ACL_append_doc__[] =
624     "append([entry])\n"
625     "Append a new Entry to the ACL and return it.\n"
626     "\n"
627     "This is a convenience function to create a new Entry \n"
628     "and append it to the ACL.\n"
629     "If a parameter of type Entry instance is given, the \n"
630     "entry will be a copy of that one (as if copied with \n"
631     ":py:func:`Entry.copy`), otherwise, the new entry will be empty.\n"
632     "\n"
633     ":rtype: :py:class:`Entry`\n"
634     ":returns: the newly created entry\n"
635     ;
636
637 /* Convenience method to create a new Entry */
638 static PyObject* ACL_append(PyObject *obj, PyObject *args) {
639     Entry_Object* newentry;
640     Entry_Object* oldentry = NULL;
641     int nret;
642
643     if (!PyArg_ParseTuple(args, "|O!", &Entry_Type, &oldentry)) {
644         return NULL;
645     }
646
647     PyObject *new_arglist = Py_BuildValue("(O)", obj);
648     if (new_arglist == NULL) {
649         return NULL;
650     }
651     newentry = (Entry_Object*) PyObject_CallObject((PyObject*)&Entry_Type, new_arglist);
652     Py_DECREF(new_arglist);
653     if(newentry == NULL) {
654         return NULL;
655     }
656
657     if(oldentry != NULL) {
658         nret = acl_copy_entry(newentry->entry, oldentry->entry);
659         if(nret == -1) {
660             Py_DECREF(newentry);
661             return PyErr_SetFromErrno(PyExc_IOError);
662         }
663     }
664
665     return (PyObject*)newentry;
666 }
667
668 /***** Entry type *****/
669
670 typedef struct {
671     acl_tag_t tag;
672     union {
673         uid_t uid;
674         gid_t gid;
675     };
676 } tag_qual;
677
678 /* Pre-declaring the function is more friendly to cpychecker, sigh. */
679 static int get_tag_qualifier(acl_entry_t entry, tag_qual *tq)
680   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
681
682 /* Helper function to get the tag and qualifier of an Entry at the
683    same time. This is "needed" because the acl_get_qualifier function
684    returns a pointer to different types, based on the tag value, and
685    thus it's not straightforward to get the right type.
686
687    It sets a Python exception if an error occurs, and returns -1 in
688    this case. If successful, the tag is set to the tag type, the
689    qualifier (if any) to either the uid or the gid entry in the
690    tag_qual structure, and the return value is 0.
691 */
692 static int get_tag_qualifier(acl_entry_t entry, tag_qual *tq) {
693     void *p;
694
695     if(acl_get_tag_type(entry, &tq->tag) == -1) {
696         PyErr_SetFromErrno(PyExc_IOError);
697         return -1;
698     }
699     if (tq->tag == ACL_USER || tq->tag == ACL_GROUP) {
700         if((p = acl_get_qualifier(entry)) == NULL) {
701             PyErr_SetFromErrno(PyExc_IOError);
702             return -1;
703         }
704         if (tq->tag == ACL_USER) {
705             tq->uid = *(uid_t*)p;
706         } else {
707             tq->gid = *(gid_t*)p;
708         }
709         acl_free(p);
710     }
711     return 0;
712 }
713
714 #define ENTRY_SET_CHECK(self, attr, value)         \
715     if (value == NULL) { \
716         PyErr_SetString(PyExc_TypeError, \
717                         attr " deletion is not supported"); \
718         return -1; \
719     }
720
721 /* Creation of a new Entry instance */
722 static PyObject* Entry_new(PyTypeObject* type, PyObject* args,
723                            PyObject *keywds) {
724     PyObject* newentry;
725     Entry_Object* entry;
726     ACL_Object* parent = NULL;
727
728     if (!PyArg_ParseTuple(args, "O!", &ACL_Type, &parent))
729         return NULL;
730
731     newentry = PyType_GenericNew(type, args, keywds);
732
733     if(newentry == NULL) {
734         return NULL;
735     }
736
737     entry = (Entry_Object*)newentry;
738
739     if(acl_create_entry(&parent->acl, &entry->entry) == -1) {
740         PyErr_SetFromErrno(PyExc_IOError);
741         Py_DECREF(newentry);
742         return NULL;
743     }
744     Py_INCREF(parent);
745     entry->parent_acl = (PyObject*)parent;
746     return newentry;
747 }
748
749 /* Initialization of a new Entry instance */
750 static int Entry_init(PyObject* obj, PyObject* args, PyObject *keywds) {
751     Entry_Object* self = (Entry_Object*) obj;
752     ACL_Object* parent = NULL;
753
754     if (!PyArg_ParseTuple(args, "O!", &ACL_Type, &parent))
755         return -1;
756
757     if ((PyObject*)parent != self->parent_acl) {
758         PyErr_SetString(PyExc_ValueError,
759                         "Can't reinitialize with a different parent");
760         return -1;
761     }
762     return 0;
763 }
764
765 /* Free the Entry instance */
766 static void Entry_dealloc(PyObject* obj) {
767     Entry_Object *self = (Entry_Object*) obj;
768     PyObject *err_type, *err_value, *err_traceback;
769     int have_error = PyErr_Occurred() ? 1 : 0;
770
771     if (have_error)
772         PyErr_Fetch(&err_type, &err_value, &err_traceback);
773     if(self->parent_acl != NULL) {
774         Py_DECREF(self->parent_acl);
775         self->parent_acl = NULL;
776     }
777     if (have_error)
778         PyErr_Restore(err_type, err_value, err_traceback);
779     PyObject_DEL(self);
780 }
781
782 /* Converts the entry to a text format */
783 static PyObject* Entry_str(PyObject *obj) {
784     PyObject *format, *kind;
785     Entry_Object *self = (Entry_Object*) obj;
786     tag_qual tq;
787
788     if(get_tag_qualifier(self->entry, &tq) < 0) {
789         return NULL;
790     }
791
792     format = PyUnicode_FromString("ACL entry for ");
793     if(format == NULL)
794         return NULL;
795     switch(tq.tag) {
796     case ACL_UNDEFINED_TAG:
797         kind = PyUnicode_FromString("undefined type");
798         break;
799     case ACL_USER_OBJ:
800         kind = PyUnicode_FromString("the owner");
801         break;
802     case ACL_GROUP_OBJ:
803         kind = PyUnicode_FromString("the group");
804         break;
805     case ACL_OTHER:
806         kind = PyUnicode_FromString("the others");
807         break;
808     case ACL_USER:
809         /* FIXME: here and in the group case, we're formatting with
810            unsigned, because there's no way to automatically determine
811            the signed-ness of the types; on Linux(glibc) they're
812            unsigned, so we'll go along with that */
813         kind = PyUnicode_FromFormat("user with uid %u", tq.uid);
814         break;
815     case ACL_GROUP:
816         kind = PyUnicode_FromFormat("group with gid %u", tq.gid);
817         break;
818     case ACL_MASK:
819         kind = PyUnicode_FromString("the mask");
820         break;
821     default:
822         kind = PyUnicode_FromString("UNKNOWN_TAG_TYPE!");
823         break;
824     }
825     if (kind == NULL) {
826         Py_DECREF(format);
827         return NULL;
828     }
829     PyObject *ret = PyUnicode_Concat(format, kind);
830     Py_DECREF(format);
831     Py_DECREF(kind);
832     return ret;
833 }
834
835 /* Sets the tag type of the entry */
836 static int Entry_set_tag_type(PyObject* obj, PyObject* value, void* arg) {
837     Entry_Object *self = (Entry_Object*) obj;
838
839     ENTRY_SET_CHECK(self, "tag type", value);
840
841     if(!PyLong_Check(value)) {
842         PyErr_SetString(PyExc_TypeError,
843                         "tag type must be integer");
844         return -1;
845     }
846     if(acl_set_tag_type(self->entry, (acl_tag_t)PyLong_AsLong(value)) == -1) {
847         PyErr_SetFromErrno(PyExc_IOError);
848         return -1;
849     }
850
851     return 0;
852 }
853
854 /* Returns the tag type of the entry */
855 static PyObject* Entry_get_tag_type(PyObject *obj, void* arg) {
856     Entry_Object *self = (Entry_Object*) obj;
857     acl_tag_t value;
858
859     if(acl_get_tag_type(self->entry, &value) == -1) {
860         PyErr_SetFromErrno(PyExc_IOError);
861         return NULL;
862     }
863
864     return PyLong_FromLong(value);
865 }
866
867 /* Sets the qualifier (either uid_t or gid_t) for the entry,
868  * usable only if the tag type if ACL_USER or ACL_GROUP
869  */
870 static int Entry_set_qualifier(PyObject* obj, PyObject* value, void* arg) {
871     Entry_Object *self = (Entry_Object*) obj;
872     long uidgid;
873     uid_t uid;
874     gid_t gid;
875     void *p;
876     acl_tag_t tag;
877
878     ENTRY_SET_CHECK(self, "qualifier", value);
879
880     if(!PyLong_Check(value)) {
881         PyErr_SetString(PyExc_TypeError,
882                         "qualifier must be integer");
883         return -1;
884     }
885     if((uidgid = PyLong_AsLong(value)) == -1) {
886         if(PyErr_Occurred() != NULL) {
887             return -1;
888         }
889     }
890     /* Due to how acl_set_qualifier takes its argument, we have to do
891        this ugly dance with two variables and a pointer that will
892        point to one of them. */
893     if(acl_get_tag_type(self->entry, &tag) == -1) {
894         PyErr_SetFromErrno(PyExc_IOError);
895         return -1;
896     }
897     uid = uidgid;
898     gid = uidgid;
899     switch(tag) {
900     case ACL_USER:
901       if((long)uid != uidgid) {
902         PyErr_SetString(PyExc_OverflowError, "Can't assign given qualifier");
903         return -1;
904       } else {
905         p = &uid;
906       }
907       break;
908     case ACL_GROUP:
909       if((long)gid != uidgid) {
910         PyErr_SetString(PyExc_OverflowError, "Can't assign given qualifier");
911         return -1;
912       } else {
913         p = &gid;
914       }
915       break;
916     default:
917       PyErr_SetString(PyExc_TypeError,
918                       "Can only set qualifiers on ACL_USER or ACL_GROUP entries");
919       return -1;
920     }
921     if(acl_set_qualifier(self->entry, p) == -1) {
922         PyErr_SetFromErrno(PyExc_IOError);
923         return -1;
924     }
925
926     return 0;
927 }
928
929 /* Returns the qualifier of the entry */
930 static PyObject* Entry_get_qualifier(PyObject *obj, void* arg) {
931     Entry_Object *self = (Entry_Object*) obj;
932     long value;
933     tag_qual tq;
934
935     if (self->entry == NULL) {
936         PyErr_SetString(PyExc_ValueError, "Can't get qualifier on uninitalized Entry object");
937         return NULL;
938     }
939     if(get_tag_qualifier(self->entry, &tq) < 0) {
940         return NULL;
941     }
942     if (tq.tag == ACL_USER) {
943         value = tq.uid;
944     } else if (tq.tag == ACL_GROUP) {
945         value = tq.gid;
946     } else {
947         PyErr_SetString(PyExc_TypeError,
948                         "Given entry doesn't have an user or"
949                         " group tag");
950         return NULL;
951     }
952     return PyLong_FromLong(value);
953 }
954
955 /* Returns the parent ACL of the entry */
956 static PyObject* Entry_get_parent(PyObject *obj, void* arg) {
957     Entry_Object *self = (Entry_Object*) obj;
958
959     Py_INCREF(self->parent_acl);
960     return self->parent_acl;
961 }
962
963 /* Returns the a new Permset representing the permset of the entry
964  * FIXME: Should return a new reference to the same object, which
965  * should be created at init time!
966  */
967 static PyObject* Entry_get_permset(PyObject *obj, void* arg) {
968     PyObject *p;
969
970     PyObject *perm_arglist = Py_BuildValue("(O)", obj);
971     if (perm_arglist == NULL) {
972         return NULL;
973     }
974     p = PyObject_CallObject((PyObject*)&Permset_Type, perm_arglist);
975     Py_DECREF(perm_arglist);
976     return p;
977 }
978
979 /* Sets the permset of the entry to the passed Permset */
980 static int Entry_set_permset(PyObject* obj, PyObject* value, void* arg) {
981     Entry_Object *self = (Entry_Object*)obj;
982     Permset_Object *p;
983
984     ENTRY_SET_CHECK(self, "permset", value);
985
986     if(!PyObject_IsInstance(value, (PyObject*)&Permset_Type)) {
987         PyErr_SetString(PyExc_TypeError, "argument 1 must be posix1e.Permset");
988         return -1;
989     }
990     p = (Permset_Object*)value;
991     if(acl_set_permset(self->entry, p->permset) == -1) {
992         PyErr_SetFromErrno(PyExc_IOError);
993         return -1;
994     }
995     return 0;
996 }
997
998 static char __Entry_copy_doc__[] =
999     "copy(src)\n"
1000     "Copies an ACL entry.\n"
1001     "\n"
1002     "This method sets all the parameters to those of another\n"
1003     "entry (either of the same ACL or belonging to another ACL).\n"
1004     "\n"
1005     ":param Entry src: instance of type Entry\n"
1006     ;
1007
1008 /* Sets all the entry parameters to another entry */
1009 static PyObject* Entry_copy(PyObject *obj, PyObject *args) {
1010     Entry_Object *self = (Entry_Object*)obj;
1011     Entry_Object *other;
1012
1013     if(!PyArg_ParseTuple(args, "O!", &Entry_Type, &other))
1014         return NULL;
1015
1016     if(acl_copy_entry(self->entry, other->entry) == -1)
1017         return PyErr_SetFromErrno(PyExc_IOError);
1018
1019     Py_RETURN_NONE;
1020 }
1021
1022 /**** Permset type *****/
1023
1024 /* Creation of a new Permset instance */
1025 static PyObject* Permset_new(PyTypeObject* type, PyObject* args,
1026                              PyObject *keywds) {
1027     PyObject* newpermset;
1028     Permset_Object* permset;
1029     Entry_Object* parent = NULL;
1030
1031     if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &parent)) {
1032         return NULL;
1033     }
1034
1035     newpermset = PyType_GenericNew(type, args, keywds);
1036
1037     if(newpermset == NULL) {
1038         return NULL;
1039     }
1040
1041     permset = (Permset_Object*)newpermset;
1042
1043     if(acl_get_permset(parent->entry, &permset->permset) == -1) {
1044         PyErr_SetFromErrno(PyExc_IOError);
1045         Py_DECREF(newpermset);
1046         return NULL;
1047     }
1048
1049     permset->parent_entry = (PyObject*)parent;
1050     Py_INCREF(parent);
1051
1052     return newpermset;
1053 }
1054
1055 /* Initialization of a new Permset instance */
1056 static int Permset_init(PyObject* obj, PyObject* args, PyObject *keywds) {
1057     Permset_Object* self = (Permset_Object*) obj;
1058     Entry_Object* parent = NULL;
1059
1060     if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &parent))
1061         return -1;
1062
1063     if ((PyObject*)parent != self->parent_entry) {
1064         PyErr_SetString(PyExc_ValueError,
1065                         "Can't reinitialize with a different parent");
1066         return -1;
1067     }
1068
1069     return 0;
1070 }
1071
1072 /* Free the Permset instance */
1073 static void Permset_dealloc(PyObject* obj) {
1074     Permset_Object *self = (Permset_Object*) obj;
1075     PyObject *err_type, *err_value, *err_traceback;
1076     int have_error = PyErr_Occurred() ? 1 : 0;
1077
1078     if (have_error)
1079         PyErr_Fetch(&err_type, &err_value, &err_traceback);
1080     if(self->parent_entry != NULL) {
1081         Py_DECREF(self->parent_entry);
1082         self->parent_entry = NULL;
1083     }
1084     if (have_error)
1085         PyErr_Restore(err_type, err_value, err_traceback);
1086     PyObject_DEL(self);
1087 }
1088
1089 /* Permset string representation */
1090 static PyObject* Permset_str(PyObject *obj) {
1091     Permset_Object *self = (Permset_Object*) obj;
1092     char pstr[3];
1093
1094     pstr[0] = get_perm(self->permset, ACL_READ) ? 'r' : '-';
1095     pstr[1] = get_perm(self->permset, ACL_WRITE) ? 'w' : '-';
1096     pstr[2] = get_perm(self->permset, ACL_EXECUTE) ? 'x' : '-';
1097     return PyUnicode_FromStringAndSize(pstr, 3);
1098 }
1099
1100 static char __Permset_clear_doc__[] =
1101     "Clears all permissions from the permission set.\n"
1102     ;
1103
1104 /* Clears all permissions from the permset */
1105 static PyObject* Permset_clear(PyObject* obj, PyObject* args) {
1106     Permset_Object *self = (Permset_Object*) obj;
1107
1108     if(acl_clear_perms(self->permset) == -1)
1109         return PyErr_SetFromErrno(PyExc_IOError);
1110
1111     Py_RETURN_NONE;
1112 }
1113
1114 static PyObject* Permset_get_right(PyObject *obj, void* arg) {
1115     Permset_Object *self = (Permset_Object*) obj;
1116
1117     if(get_perm(self->permset, *(acl_perm_t*)arg)) {
1118         Py_RETURN_TRUE;
1119     } else {
1120         Py_RETURN_FALSE;
1121     }
1122 }
1123
1124 static int Permset_set_right(PyObject* obj, PyObject* value, void* arg) {
1125     Permset_Object *self = (Permset_Object*) obj;
1126     int on;
1127     int nerr;
1128
1129     if(!PyLong_Check(value)) {
1130         PyErr_SetString(PyExc_ValueError, "invalid argument, an integer"
1131                         " is expected");
1132         return -1;
1133     }
1134     on = PyLong_AsLong(value);
1135     if(on)
1136         nerr = acl_add_perm(self->permset, *(acl_perm_t*)arg);
1137     else
1138         nerr = acl_delete_perm(self->permset, *(acl_perm_t*)arg);
1139     if(nerr == -1) {
1140         PyErr_SetFromErrno(PyExc_IOError);
1141         return -1;
1142     }
1143     return 0;
1144 }
1145
1146 static char __Permset_add_doc__[] =
1147     "add(perm)\n"
1148     "Add a permission to the permission set.\n"
1149     "\n"
1150     "This function adds the permission contained in \n"
1151     "the argument perm to the permission set.  An attempt \n"
1152     "to add a permission that is already contained in the \n"
1153     "permission set is not considered an error.\n"
1154     "\n"
1155     ":param perm: a permission (:py:data:`ACL_WRITE`, :py:data:`ACL_READ`,\n"
1156     "   :py:data:`ACL_EXECUTE`, ...)\n"
1157     ":raises IOError: in case the argument is not a valid descriptor\n"
1158     ;
1159
1160 static PyObject* Permset_add(PyObject* obj, PyObject* args) {
1161     Permset_Object *self = (Permset_Object*) obj;
1162     int right;
1163
1164     if (!PyArg_ParseTuple(args, "i", &right))
1165         return NULL;
1166
1167     if(acl_add_perm(self->permset, (acl_perm_t) right) == -1)
1168         return PyErr_SetFromErrno(PyExc_IOError);
1169
1170     Py_RETURN_NONE;
1171 }
1172
1173 static char __Permset_delete_doc__[] =
1174     "delete(perm)\n"
1175     "Delete a permission from the permission set.\n"
1176     "\n"
1177     "This function deletes the permission contained in \n"
1178     "the argument perm from the permission set. An attempt \n"
1179     "to delete a permission that is not contained in the \n"
1180     "permission set is not considered an error.\n"
1181     "\n"
1182     ":param perm: a permission (:py:data:`ACL_WRITE`, :py:data:`ACL_READ`,\n"
1183     "   :py:data:`ACL_EXECUTE`, ...)\n"
1184     ":raises IOError: in case the argument is not a valid descriptor\n"
1185     ;
1186
1187 static PyObject* Permset_delete(PyObject* obj, PyObject* args) {
1188     Permset_Object *self = (Permset_Object*) obj;
1189     int right;
1190
1191     if (!PyArg_ParseTuple(args, "i", &right))
1192         return NULL;
1193
1194     if(acl_delete_perm(self->permset, (acl_perm_t) right) == -1)
1195         return PyErr_SetFromErrno(PyExc_IOError);
1196
1197     Py_RETURN_NONE;
1198 }
1199
1200 static char __Permset_test_doc__[] =
1201     "test(perm)\n"
1202     "Test if a permission exists in the permission set.\n"
1203     "\n"
1204     "The test() function tests if the permission represented by\n"
1205     "the argument perm exists in the permission set.\n"
1206     "\n"
1207     ":param perm: a permission (:py:data:`ACL_WRITE`, :py:data:`ACL_READ`,\n"
1208     "   :py:data:`ACL_EXECUTE`, ...)\n"
1209     ":rtype: Boolean\n"
1210     ":raises IOError: in case the argument is not a valid descriptor\n"
1211     ;
1212
1213 static PyObject* Permset_test(PyObject* obj, PyObject* args) {
1214     Permset_Object *self = (Permset_Object*) obj;
1215     int right;
1216     int ret;
1217
1218     if (!PyArg_ParseTuple(args, "i", &right))
1219         return NULL;
1220
1221     ret = get_perm(self->permset, (acl_perm_t) right);
1222     if(ret == -1)
1223         return PyErr_SetFromErrno(PyExc_IOError);
1224
1225     if(ret) {
1226         Py_RETURN_TRUE;
1227     } else {
1228         Py_RETURN_FALSE;
1229     }
1230 }
1231
1232 #endif
1233
1234 static char __ACL_Type_doc__[] =
1235     "Type which represents a POSIX ACL\n"
1236     "\n"
1237     ".. note:: only one keyword parameter should be provided\n"
1238     "\n"
1239     ":param string/bytes/path-like file: creates an ACL representing\n"
1240     "    the access ACL of the specified file or directory.\n"
1241     ":param string/bytes/path-like filedef: creates an ACL representing\n"
1242     "    the default ACL of the given directory.\n"
1243     ":param int/iostream fd: creates an ACL representing\n"
1244     "    the access ACL of the given file descriptor.\n"
1245     ":param string text: creates an ACL from a \n"
1246     "    textual description; note the ACL must be valid, which\n"
1247     "    means including a mask for extended ACLs, similar to\n"
1248     "    ``setfacl --no-mask``\n"
1249     ":param ACL acl: creates a copy of an existing ACL instance.\n"
1250     ":param int mode: creates an ACL from a numeric mode\n"
1251     "    (e.g. ``mode=0644``); this is valid only when the C library\n"
1252     "    provides the ``acl_from_mode call``, and\n"
1253     "    note that no validation is done on the given value.\n"
1254     "\n"
1255     "If no parameters are passed, an empty ACL will be created; this\n"
1256     "makes sense only when your OS supports ACL modification\n"
1257     "(i.e. it implements full POSIX.1e support), otherwise the ACL won't\n"
1258     "be useful.\n"
1259     ;
1260
1261 /* ACL type methods */
1262 static PyMethodDef ACL_methods[] = {
1263     {"applyto", ACL_applyto, METH_VARARGS, __applyto_doc__},
1264     {"valid", ACL_valid, METH_NOARGS, __valid_doc__},
1265 #ifdef HAVE_LINUX
1266     {"to_any_text", (PyCFunction)ACL_to_any_text, METH_VARARGS | METH_KEYWORDS,
1267      __to_any_text_doc__},
1268     {"check", ACL_check, METH_NOARGS, __check_doc__},
1269     {"equiv_mode", ACL_equiv_mode, METH_NOARGS, __equiv_mode_doc__},
1270 #endif
1271 #ifdef HAVE_ACL_COPY_EXT
1272     {"__getstate__", ACL_get_state, METH_NOARGS,
1273      "Dumps the ACL to an external format."},
1274     {"__setstate__", ACL_set_state, METH_VARARGS,
1275      "Loads the ACL from an external format."},
1276 #endif
1277 #ifdef HAVE_LEVEL2
1278     {"delete_entry", ACL_delete_entry, METH_VARARGS, __ACL_delete_entry_doc__},
1279     {"calc_mask", ACL_calc_mask, METH_NOARGS, __ACL_calc_mask_doc__},
1280     {"append", ACL_append, METH_VARARGS, __ACL_append_doc__},
1281 #endif
1282     {NULL, NULL, 0, NULL}
1283 };
1284
1285
1286 /* The definition of the ACL Type */
1287 static PyTypeObject ACL_Type = {
1288     PyVarObject_HEAD_INIT(NULL, 0)
1289     "posix1e.ACL",
1290     sizeof(ACL_Object),
1291     0,
1292     ACL_dealloc,        /* tp_dealloc */
1293     0,                  /* tp_print */
1294     0,                  /* tp_getattr */
1295     0,                  /* tp_setattr */
1296     0,                  /* formerly tp_compare, in 3.0 deprecated, in
1297                            3.5 tp_as_async */
1298     0,                  /* tp_repr */
1299     0,                  /* tp_as_number */
1300     0,                  /* tp_as_sequence */
1301     0,                  /* tp_as_mapping */
1302     0,                  /* tp_hash */
1303     0,                  /* tp_call */
1304     ACL_str,            /* tp_str */
1305     0,                  /* tp_getattro */
1306     0,                  /* tp_setattro */
1307     0,                  /* tp_as_buffer */
1308     Py_TPFLAGS_DEFAULT, /* tp_flags */
1309     __ACL_Type_doc__,   /* tp_doc */
1310     0,                  /* tp_traverse */
1311     0,                  /* tp_clear */
1312 #ifdef HAVE_LINUX
1313     ACL_richcompare,    /* tp_richcompare */
1314 #else
1315     0,                  /* tp_richcompare */
1316 #endif
1317     0,                  /* tp_weaklistoffset */
1318 #ifdef HAVE_LEVEL2
1319     ACL_iter,
1320     ACL_iternext,
1321 #else
1322     0,                  /* tp_iter */
1323     0,                  /* tp_iternext */
1324 #endif
1325     ACL_methods,        /* tp_methods */
1326     0,                  /* tp_members */
1327     0,                  /* tp_getset */
1328     0,                  /* tp_base */
1329     0,                  /* tp_dict */
1330     0,                  /* tp_descr_get */
1331     0,                  /* tp_descr_set */
1332     0,                  /* tp_dictoffset */
1333     ACL_init,           /* tp_init */
1334     0,                  /* tp_alloc */
1335     ACL_new,            /* tp_new */
1336 };
1337
1338 #ifdef HAVE_LEVEL2
1339
1340 /* Entry type methods */
1341 static PyMethodDef Entry_methods[] = {
1342     {"copy", Entry_copy, METH_VARARGS, __Entry_copy_doc__},
1343     {NULL, NULL, 0, NULL}
1344 };
1345
1346 static char __Entry_tagtype_doc__[] =
1347     "The tag type of the current entry\n"
1348     "\n"
1349     "This is one of:\n"
1350     " - :py:data:`ACL_UNDEFINED_TAG`\n"
1351     " - :py:data:`ACL_USER_OBJ`\n"
1352     " - :py:data:`ACL_USER`\n"
1353     " - :py:data:`ACL_GROUP_OBJ`\n"
1354     " - :py:data:`ACL_GROUP`\n"
1355     " - :py:data:`ACL_MASK`\n"
1356     " - :py:data:`ACL_OTHER`\n"
1357     ;
1358
1359 static char __Entry_qualifier_doc__[] =
1360     "The qualifier of the current entry\n"
1361     "\n"
1362     "If the tag type is :py:data:`ACL_USER`, this should be a user id.\n"
1363     "If the tag type if :py:data:`ACL_GROUP`, this should be a group id.\n"
1364     "Else it doesn't matter.\n"
1365     ;
1366
1367 static char __Entry_parent_doc__[] =
1368     "The parent ACL of this entry\n"
1369     ;
1370
1371 static char __Entry_permset_doc__[] =
1372     "The permission set of this ACL entry\n"
1373     ;
1374
1375 /* Entry getset */
1376 static PyGetSetDef Entry_getsets[] = {
1377     {"tag_type", Entry_get_tag_type, Entry_set_tag_type,
1378      __Entry_tagtype_doc__},
1379     {"qualifier", Entry_get_qualifier, Entry_set_qualifier,
1380      __Entry_qualifier_doc__},
1381     {"parent", Entry_get_parent, NULL, __Entry_parent_doc__},
1382     {"permset", Entry_get_permset, Entry_set_permset, __Entry_permset_doc__},
1383     {NULL}
1384 };
1385
1386 static char __Entry_Type_doc__[] =
1387     "Type which represents an entry in an ACL.\n"
1388     "\n"
1389     "The type exists only if the OS has full support for POSIX.1e\n"
1390     "Can be created either by:\n"
1391     "\n"
1392     "  >>> e = posix1e.Entry(myACL) # this creates a new entry in the ACL\n"
1393     "  >>> e = myACL.append() # another way for doing the same thing\n"
1394     "\n"
1395     "or by:\n"
1396     "\n"
1397     "  >>> for entry in myACL:\n"
1398     "  ...     print entry\n"
1399     "\n"
1400     "Note that the Entry keeps a reference to its ACL, so even if \n"
1401     "you delete the ACL, it won't be cleaned up and will continue to \n"
1402     "exist until its Entry(ies) will be deleted.\n"
1403     ;
1404 /* The definition of the Entry Type */
1405 static PyTypeObject Entry_Type = {
1406     PyVarObject_HEAD_INIT(NULL, 0)
1407     "posix1e.Entry",
1408     sizeof(Entry_Object),
1409     0,
1410     Entry_dealloc,      /* tp_dealloc */
1411     0,                  /* tp_print */
1412     0,                  /* tp_getattr */
1413     0,                  /* tp_setattr */
1414     0,                  /* tp_compare */
1415     0,                  /* tp_repr */
1416     0,                  /* tp_as_number */
1417     0,                  /* tp_as_sequence */
1418     0,                  /* tp_as_mapping */
1419     0,                  /* tp_hash */
1420     0,                  /* tp_call */
1421     Entry_str,          /* tp_str */
1422     0,                  /* tp_getattro */
1423     0,                  /* tp_setattro */
1424     0,                  /* tp_as_buffer */
1425     Py_TPFLAGS_DEFAULT, /* tp_flags */
1426     __Entry_Type_doc__, /* tp_doc */
1427     0,                  /* tp_traverse */
1428     0,                  /* tp_clear */
1429     0,                  /* tp_richcompare */
1430     0,                  /* tp_weaklistoffset */
1431     0,                  /* tp_iter */
1432     0,                  /* tp_iternext */
1433     Entry_methods,      /* tp_methods */
1434     0,                  /* tp_members */
1435     Entry_getsets,      /* tp_getset */
1436     0,                  /* tp_base */
1437     0,                  /* tp_dict */
1438     0,                  /* tp_descr_get */
1439     0,                  /* tp_descr_set */
1440     0,                  /* tp_dictoffset */
1441     Entry_init,         /* tp_init */
1442     0,                  /* tp_alloc */
1443     Entry_new,          /* tp_new */
1444 };
1445
1446 /* Permset type methods */
1447 static PyMethodDef Permset_methods[] = {
1448     {"clear", Permset_clear, METH_NOARGS, __Permset_clear_doc__, },
1449     {"add", Permset_add, METH_VARARGS, __Permset_add_doc__, },
1450     {"delete", Permset_delete, METH_VARARGS, __Permset_delete_doc__, },
1451     {"test", Permset_test, METH_VARARGS, __Permset_test_doc__, },
1452     {NULL, NULL, 0, NULL}
1453 };
1454
1455 static char __Permset_execute_doc__[] =
1456     "Execute permission property\n"
1457     "\n"
1458     "This is a convenience method of retrieving and setting the execute\n"
1459     "permission in the permission set; the \n"
1460     "same effect can be achieved using the functions\n"
1461     "add(), test(), delete(), and those can take any \n"
1462     "permission defined by your platform.\n"
1463     ;
1464
1465 static char __Permset_read_doc__[] =
1466     "Read permission property\n"
1467     "\n"
1468     "This is a convenience method of retrieving and setting the read\n"
1469     "permission in the permission set; the \n"
1470     "same effect can be achieved using the functions\n"
1471     "add(), test(), delete(), and those can take any \n"
1472     "permission defined by your platform.\n"
1473     ;
1474
1475 static char __Permset_write_doc__[] =
1476     "Write permission property\n"
1477     "\n"
1478     "This is a convenience method of retrieving and setting the write\n"
1479     "permission in the permission set; the \n"
1480     "same effect can be achieved using the functions\n"
1481     "add(), test(), delete(), and those can take any \n"
1482     "permission defined by your platform.\n"
1483     ;
1484
1485 /* Permset getset */
1486 static PyGetSetDef Permset_getsets[] = {
1487     {"execute", Permset_get_right, Permset_set_right,
1488      __Permset_execute_doc__, &holder_ACL_EXECUTE},
1489     {"read", Permset_get_right, Permset_set_right,
1490      __Permset_read_doc__, &holder_ACL_READ},
1491     {"write", Permset_get_right, Permset_set_right,
1492      __Permset_write_doc__, &holder_ACL_WRITE},
1493     {NULL}
1494 };
1495
1496 static char __Permset_Type_doc__[] =
1497     "Type which represents the permission set in an ACL entry\n"
1498     "\n"
1499     "The type exists only if the OS has full support for POSIX.1e\n"
1500     "Can be retrieved either by:\n\n"
1501     ">>> perms = myEntry.permset\n"
1502     "\n"
1503     "or by:\n\n"
1504     ">>> perms = posix1e.Permset(myEntry)\n"
1505     "\n"
1506     "Note that the Permset keeps a reference to its Entry, so even if \n"
1507     "you delete the entry, it won't be cleaned up and will continue to \n"
1508     "exist until its Permset will be deleted.\n"
1509     ;
1510
1511 /* The definition of the Permset Type */
1512 static PyTypeObject Permset_Type = {
1513     PyVarObject_HEAD_INIT(NULL, 0)
1514     "posix1e.Permset",
1515     sizeof(Permset_Object),
1516     0,
1517     Permset_dealloc,    /* tp_dealloc */
1518     0,                  /* tp_print */
1519     0,                  /* tp_getattr */
1520     0,                  /* tp_setattr */
1521     0,                  /* tp_compare */
1522     0,                  /* tp_repr */
1523     0,                  /* tp_as_number */
1524     0,                  /* tp_as_sequence */
1525     0,                  /* tp_as_mapping */
1526     0,                  /* tp_hash */
1527     0,                  /* tp_call */
1528     Permset_str,        /* tp_str */
1529     0,                  /* tp_getattro */
1530     0,                  /* tp_setattro */
1531     0,                  /* tp_as_buffer */
1532     Py_TPFLAGS_DEFAULT, /* tp_flags */
1533     __Permset_Type_doc__,/* tp_doc */
1534     0,                  /* tp_traverse */
1535     0,                  /* tp_clear */
1536     0,                  /* tp_richcompare */
1537     0,                  /* tp_weaklistoffset */
1538     0,                  /* tp_iter */
1539     0,                  /* tp_iternext */
1540     Permset_methods,    /* tp_methods */
1541     0,                  /* tp_members */
1542     Permset_getsets,    /* tp_getset */
1543     0,                  /* tp_base */
1544     0,                  /* tp_dict */
1545     0,                  /* tp_descr_get */
1546     0,                  /* tp_descr_set */
1547     0,                  /* tp_dictoffset */
1548     Permset_init,       /* tp_init */
1549     0,                  /* tp_alloc */
1550     Permset_new,        /* tp_new */
1551 };
1552
1553 #endif
1554
1555 /* Module methods */
1556
1557 static char __deletedef_doc__[] =
1558     "delete_default(path)\n"
1559     "Delete the default ACL from a directory.\n"
1560     "\n"
1561     "This function deletes the default ACL associated with\n"
1562     "a directory (the ACL which will be ANDed with the mode\n"
1563     "parameter to the open, creat functions).\n"
1564     "\n"
1565     ":param string path: the directory whose default ACL should be deleted\n"
1566     ;
1567
1568 /* Deletes the default ACL from a directory */
1569 static PyObject* aclmodule_delete_default(PyObject* obj, PyObject* args) {
1570     char *filename;
1571
1572     /* Parse the arguments */
1573     if (!PyArg_ParseTuple(args, "et", NULL, &filename))
1574         return NULL;
1575
1576     if(acl_delete_def_file(filename) == -1) {
1577         return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
1578     }
1579
1580     Py_RETURN_NONE;
1581 }
1582
1583 #ifdef HAVE_LINUX
1584 static char __has_extended_doc__[] =
1585     "has_extended(item)\n"
1586     "Check if a file or file handle has an extended ACL.\n"
1587     "\n"
1588     ":param item: either a file name or a file-like object or an integer;\n"
1589     "  it represents the file-system object on which to act\n"
1590     ;
1591
1592 /* Check for extended ACL a file or fd */
1593 static PyObject* aclmodule_has_extended(PyObject* obj, PyObject* args) {
1594     PyObject *item, *tmp;
1595     int nret;
1596     int fd;
1597
1598     if (!PyArg_ParseTuple(args, "O", &item))
1599         return NULL;
1600
1601     if((fd = PyObject_AsFileDescriptor(item)) != -1) {
1602         if((nret = acl_extended_fd(fd)) == -1) {
1603             PyErr_SetFromErrno(PyExc_IOError);
1604         }
1605     } else {
1606       // PyObject_AsFileDescriptor sets an error when failing, so clear
1607       // it such that further code works; some method lookups fail if an
1608       // error already occured when called, which breaks at least
1609       // PyOS_FSPath (called by FSConverter).
1610       PyErr_Clear();
1611       if(PyUnicode_FSConverter(item, &tmp)) {
1612         char *filename = PyBytes_AS_STRING(tmp);
1613         if ((nret = acl_extended_file(filename)) == -1) {
1614             PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
1615         }
1616         Py_DECREF(tmp);
1617       } else {
1618           nret = -1;
1619       }
1620     }
1621
1622     if (nret < 0) {
1623         return NULL;
1624     } else {
1625         return PyBool_FromLong(nret);
1626     }
1627 }
1628 #endif
1629
1630 /* The module methods */
1631 static PyMethodDef aclmodule_methods[] = {
1632     {"delete_default", aclmodule_delete_default, METH_VARARGS,
1633      __deletedef_doc__},
1634 #ifdef HAVE_LINUX
1635     {"has_extended", aclmodule_has_extended, METH_VARARGS,
1636      __has_extended_doc__},
1637 #endif
1638     {NULL, NULL, 0, NULL}
1639 };
1640
1641 static char __posix1e_doc__[] =
1642     "POSIX.1e ACLs manipulation\n"
1643     "==========================\n"
1644     "\n"
1645     "This module provides support for manipulating POSIX.1e ACLS\n"
1646     "\n"
1647     "Depending on the operating system support for POSIX.1e, \n"
1648     "the ACL type will have more or less capabilities:\n\n"
1649     "  - level 1, only basic support, you can create\n"
1650     "    ACLs from files and text descriptions;\n"
1651     "    once created, the type is immutable\n"
1652     "  - level 2, complete support, you can alter\n"
1653     "    the ACL once it is created\n"
1654     "\n"
1655     "Also, in level 2, more types are available, corresponding\n"
1656     "to acl_entry_t (the Entry type), acl_permset_t (the Permset type).\n"
1657     "\n"
1658     "The existence of level 2 support and other extensions can be\n"
1659     "checked by the constants:\n\n"
1660     "  - :py:data:`HAS_ACL_ENTRY` for level 2 and the Entry/Permset classes\n"
1661     "  - :py:data:`HAS_ACL_FROM_MODE` for ``ACL(mode=...)`` usage\n"
1662     "  - :py:data:`HAS_ACL_CHECK` for the :py:func:`ACL.check` function\n"
1663     "  - :py:data:`HAS_EXTENDED_CHECK` for the module-level\n"
1664     "    :py:func:`has_extended` function\n"
1665     "  - :py:data:`HAS_EQUIV_MODE` for the :py:func:`ACL.equiv_mode` method\n"
1666     "  - :py:data:`HAS_COPY_EXT` for the :py:func:`ACL.__getstate__` and\n"
1667     "    :py:func:`ACL.__setstate__` functions (pickle protocol)\n"
1668     "\n"
1669     "Example:\n"
1670     "\n"
1671     ">>> import posix1e\n"
1672     ">>> acl1 = posix1e.ACL(file=\"file.txt\") \n"
1673     ">>> print acl1\n"
1674     "user::rw-\n"
1675     "group::rw-\n"
1676     "other::r--\n"
1677     ">>>\n"
1678     ">>> b = posix1e.ACL(text=\"u::rx,g::-,o::-\")\n"
1679     ">>> print b\n"
1680     "user::r-x\n"
1681     "group::---\n"
1682     "other::---\n"
1683     ">>>\n"
1684     ">>> b.applyto(\"file.txt\")\n"
1685     ">>> print posix1e.ACL(file=\"file.txt\")\n"
1686     "user::r-x\n"
1687     "group::---\n"
1688     "other::---\n"
1689     ">>>\n"
1690     "\n"
1691     ".. py:data:: ACL_USER\n\n"
1692     "   Denotes a specific user entry in an ACL.\n"
1693     "\n"
1694     ".. py:data:: ACL_USER_OBJ\n\n"
1695     "   Denotes the user owner entry in an ACL.\n"
1696     "\n"
1697     ".. py:data:: ACL_GROUP\n\n"
1698     "   Denotes the a group entry in an ACL.\n"
1699     "\n"
1700     ".. py:data:: ACL_GROUP_OBJ\n\n"
1701     "   Denotes the group owner entry in an ACL.\n"
1702     "\n"
1703     ".. py:data:: ACL_OTHER\n\n"
1704     "   Denotes the 'others' entry in an ACL.\n"
1705     "\n"
1706     ".. py:data:: ACL_MASK\n\n"
1707     "   Denotes the mask entry in an ACL, representing the maximum\n"
1708     "   access granted other users, the owner group and other groups.\n"
1709     "\n"
1710     ".. py:data:: ACL_UNDEFINED_TAG\n\n"
1711     "   An undefined tag in an ACL.\n"
1712     "\n"
1713     ".. py:data:: ACL_READ\n\n"
1714     "   Read permission in a permission set.\n"
1715     "\n"
1716     ".. py:data:: ACL_WRITE\n\n"
1717     "   Write permission in a permission set.\n"
1718     "\n"
1719     ".. py:data:: ACL_EXECUTE\n\n"
1720     "   Execute permission in a permission set.\n"
1721     "\n"
1722     ".. py:data:: HAS_ACL_ENTRY\n\n"
1723     "   denotes support for level 2 and the Entry/Permset classes\n"
1724     "\n"
1725     ".. py:data:: HAS_ACL_FROM_MODE\n\n"
1726     "   denotes support for building an ACL from an octal mode\n"
1727     "\n"
1728     ".. py:data:: HAS_ACL_CHECK\n\n"
1729     "   denotes support for extended checks of an ACL's validity\n"
1730     "\n"
1731     ".. py:data:: HAS_EXTENDED_CHECK\n\n"
1732     "   denotes support for checking whether an ACL is basic or extended\n"
1733     "\n"
1734     ".. py:data:: HAS_EQUIV_MODE\n\n"
1735     "   denotes support for the equiv_mode function\n"
1736     "\n"
1737     ".. py:data:: HAS_COPY_EXT\n\n"
1738     "   denotes support for __getstate__()/__setstate__() on an ACL\n"
1739     "\n"
1740     ;
1741
1742 static struct PyModuleDef posix1emodule = {
1743     PyModuleDef_HEAD_INIT,
1744     "posix1e",
1745     __posix1e_doc__,
1746     0,
1747     aclmodule_methods,
1748 };
1749
1750 PyMODINIT_FUNC
1751 PyInit_posix1e(void)
1752 {
1753     PyObject *m, *d;
1754
1755     Py_TYPE(&ACL_Type) = &PyType_Type;
1756     if(PyType_Ready(&ACL_Type) < 0)
1757         return NULL;
1758
1759 #ifdef HAVE_LEVEL2
1760     Py_TYPE(&Entry_Type) = &PyType_Type;
1761     if(PyType_Ready(&Entry_Type) < 0)
1762         return NULL;
1763
1764     Py_TYPE(&Permset_Type) = &PyType_Type;
1765     if(PyType_Ready(&Permset_Type) < 0)
1766         return NULL;
1767 #endif
1768
1769     m = PyModule_Create(&posix1emodule);
1770     if (m==NULL)
1771         return NULL;
1772
1773     d = PyModule_GetDict(m);
1774     if (d == NULL)
1775         return NULL;
1776
1777     Py_INCREF(&ACL_Type);
1778     if (PyDict_SetItemString(d, "ACL",
1779                              (PyObject *) &ACL_Type) < 0)
1780         return NULL;
1781
1782     /* 23.3.6 acl_type_t values */
1783     PyModule_AddIntConstant(m, "ACL_TYPE_ACCESS", ACL_TYPE_ACCESS);
1784     PyModule_AddIntConstant(m, "ACL_TYPE_DEFAULT", ACL_TYPE_DEFAULT);
1785
1786
1787 #ifdef HAVE_LEVEL2
1788     Py_INCREF(&Entry_Type);
1789     if (PyDict_SetItemString(d, "Entry",
1790                              (PyObject *) &Entry_Type) < 0)
1791         return NULL;
1792
1793     Py_INCREF(&Permset_Type);
1794     if (PyDict_SetItemString(d, "Permset",
1795                              (PyObject *) &Permset_Type) < 0)
1796         return NULL;
1797
1798     /* 23.2.2 acl_perm_t values */
1799     PyModule_AddIntConstant(m, "ACL_READ", ACL_READ);
1800     PyModule_AddIntConstant(m, "ACL_WRITE", ACL_WRITE);
1801     PyModule_AddIntConstant(m, "ACL_EXECUTE", ACL_EXECUTE);
1802
1803     /* 23.2.5 acl_tag_t values */
1804     PyModule_AddIntConstant(m, "ACL_UNDEFINED_TAG", ACL_UNDEFINED_TAG);
1805     PyModule_AddIntConstant(m, "ACL_USER_OBJ", ACL_USER_OBJ);
1806     PyModule_AddIntConstant(m, "ACL_USER", ACL_USER);
1807     PyModule_AddIntConstant(m, "ACL_GROUP_OBJ", ACL_GROUP_OBJ);
1808     PyModule_AddIntConstant(m, "ACL_GROUP", ACL_GROUP);
1809     PyModule_AddIntConstant(m, "ACL_MASK", ACL_MASK);
1810     PyModule_AddIntConstant(m, "ACL_OTHER", ACL_OTHER);
1811
1812     /* Document extended functionality via easy-to-use constants */
1813     PyModule_AddIntConstant(m, "HAS_ACL_ENTRY", 1);
1814 #else
1815     PyModule_AddIntConstant(m, "HAS_ACL_ENTRY", 0);
1816 #endif
1817
1818 #ifdef HAVE_LINUX
1819     /* Linux libacl specific acl_to_any_text constants */
1820     PyModule_AddIntConstant(m, "TEXT_ABBREVIATE", TEXT_ABBREVIATE);
1821     PyModule_AddIntConstant(m, "TEXT_NUMERIC_IDS", TEXT_NUMERIC_IDS);
1822     PyModule_AddIntConstant(m, "TEXT_SOME_EFFECTIVE", TEXT_SOME_EFFECTIVE);
1823     PyModule_AddIntConstant(m, "TEXT_ALL_EFFECTIVE", TEXT_ALL_EFFECTIVE);
1824     PyModule_AddIntConstant(m, "TEXT_SMART_INDENT", TEXT_SMART_INDENT);
1825
1826     /* Linux libacl specific acl_check constants */
1827     PyModule_AddIntConstant(m, "ACL_MULTI_ERROR", ACL_MULTI_ERROR);
1828     PyModule_AddIntConstant(m, "ACL_DUPLICATE_ERROR", ACL_DUPLICATE_ERROR);
1829     PyModule_AddIntConstant(m, "ACL_MISS_ERROR", ACL_MISS_ERROR);
1830     PyModule_AddIntConstant(m, "ACL_ENTRY_ERROR", ACL_ENTRY_ERROR);
1831
1832 #define LINUX_EXT_VAL 1
1833 #else
1834 #define LINUX_EXT_VAL 0
1835 #endif
1836     /* declare the Linux extensions */
1837     PyModule_AddIntConstant(m, "HAS_ACL_FROM_MODE", LINUX_EXT_VAL);
1838     PyModule_AddIntConstant(m, "HAS_ACL_CHECK", LINUX_EXT_VAL);
1839     PyModule_AddIntConstant(m, "HAS_EXTENDED_CHECK", LINUX_EXT_VAL);
1840     PyModule_AddIntConstant(m, "HAS_EQUIV_MODE", LINUX_EXT_VAL);
1841
1842     PyModule_AddIntConstant(m, "HAS_COPY_EXT",
1843 #ifdef HAVE_ACL_COPY_EXT
1844                             1
1845 #else
1846                             0
1847 #endif
1848                             );
1849     return m;
1850 }