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