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