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