2 posix1e - a python module exposing the posix acl functions
4 Copyright (C) 2002-2008 Iustin Pop <iusty@k1024.org>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 #include <sys/types.h>
29 #include <acl/libacl.h>
30 #define get_perm acl_get_perm
32 #define get_perm acl_get_perm_np
35 #if PY_MAJOR_VERSION >= 3
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
49 #define PyBytes_Check PyString_Check
50 #define PyBytes_AS_STRING PyString_AS_STRING
51 #define PyBytes_FromStringAndSize PyString_FromStringAndSize
52 #define PyBytes_FromString PyString_FromString
53 #define PyBytes_FromFormat PyString_FromFormat
54 #define PyBytes_ConcatAndDel PyString_ConcatAndDel
57 static PyTypeObject ACL_Type;
58 static PyObject* ACL_applyto(PyObject* obj, PyObject* args);
59 static PyObject* ACL_valid(PyObject* obj, PyObject* args);
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);
67 static PyTypeObject Entry_Type;
68 static PyTypeObject Permset_Type;
69 static PyObject* Permset_new(PyTypeObject* type, PyObject* args,
73 static acl_perm_t holder_ACL_EXECUTE = ACL_EXECUTE;
74 static acl_perm_t holder_ACL_READ = ACL_READ;
75 static acl_perm_t holder_ACL_WRITE = ACL_WRITE;
89 PyObject *parent_acl; /* The parent acl, so it won't run out on us */
95 PyObject *parent_entry; /* The parent entry, so it won't run out on us */
96 acl_permset_t permset;
101 /* Creation of a new ACL instance */
102 static PyObject* ACL_new(PyTypeObject* type, PyObject* args,
106 newacl = type->tp_alloc(type, 0);
109 ((ACL_Object*)newacl)->acl = NULL;
111 ((ACL_Object*)newacl)->entry_id = ACL_FIRST_ENTRY;
118 /* Initialization of a new ACL instance */
119 static int ACL_init(PyObject* obj, PyObject* args, PyObject *keywds) {
120 ACL_Object* self = (ACL_Object*) obj;
122 static char *kwlist[] = { "file", "fd", "text", "acl", "filedef",
124 char *format = "|sisO!sH";
127 static char *kwlist[] = { "file", "fd", "text", "acl", "filedef", NULL };
128 char *format = "|sisO!s";
131 char *filedef = NULL;
134 ACL_Object* thesrc = NULL;
136 if(!PyTuple_Check(args) || PyTuple_Size(args) != 0 ||
137 (keywds != NULL && PyDict_Check(keywds) && PyDict_Size(keywds) > 1)) {
138 PyErr_SetString(PyExc_ValueError, "a max of one keyword argument"
142 if(!PyArg_ParseTupleAndKeywords(args, keywds, format, kwlist,
143 &file, &fd, &text, &ACL_Type,
151 /* Free the old acl_t without checking for error, we don't
153 if(self->acl != NULL)
157 self->acl = acl_get_file(file, ACL_TYPE_ACCESS);
158 else if(text != NULL)
159 self->acl = acl_from_text(text);
161 self->acl = acl_get_fd(fd);
162 else if(thesrc != NULL)
163 self->acl = acl_dup(thesrc->acl);
164 else if(filedef != NULL)
165 self->acl = acl_get_file(filedef, ACL_TYPE_DEFAULT);
167 else if(PyMapping_HasKeyString(keywds, kwlist[5]))
168 self->acl = acl_from_mode(mode);
171 self->acl = acl_init(0);
173 if(self->acl == NULL) {
174 PyErr_SetFromErrno(PyExc_IOError);
181 /* Standard type functions */
182 static void ACL_dealloc(PyObject* obj) {
183 ACL_Object *self = (ACL_Object*) obj;
184 PyObject *err_type, *err_value, *err_traceback;
185 int have_error = PyErr_Occurred() ? 1 : 0;
188 PyErr_Fetch(&err_type, &err_value, &err_traceback);
189 if(self->acl != NULL && acl_free(self->acl) != 0)
190 PyErr_WriteUnraisable(obj);
192 PyErr_Restore(err_type, err_value, err_traceback);
196 /* Converts the acl to a text format */
197 static PyObject* ACL_str(PyObject *obj) {
199 ACL_Object *self = (ACL_Object*) obj;
202 text = acl_to_text(self->acl, NULL);
204 return PyErr_SetFromErrno(PyExc_IOError);
206 ret = PyBytes_FromString(text);
207 if(acl_free(text) != 0) {
209 return PyErr_SetFromErrno(PyExc_IOError);
215 static char __to_any_text_doc__[] =
216 "Convert the ACL to a custom text format.\n"
218 "This method encapsulates the acl_to_any_text function. It allows a \n"
219 "customized text format to be generated for the ACL. See\n"
220 "acl_to_any_text(3) for more details.\n"
223 " - prefix: if given, this string will be prepended to all lines\n"
224 " - separator: a single character (defaults to '\\n'); this will be\n"
225 " user to separate the entries in the ACL\n"
226 " - options: a bitwise combination of:\n"
227 " - TEXT_ABBREVIATE: use 'u' instead of 'user', 'g' instead of \n"
229 " - TEXT_NUMERIC_IDS: User and group IDs are included as decimal\n"
230 " numbers instead of names\n"
231 " - TEXT_SOME_EFFECTIVE: Include comments denoting the effective\n"
232 " permissions when some are masked\n"
233 " - TEXT_ALL_EFFECTIVE: Include comments after all ACL entries\n"
234 " affected by an ACL_MASK entry\n"
235 " - TEXT_SMART_INDENT: Used in combination with the _EFFECTIVE\n"
236 " options, this will ensure that comments \n"
237 " are alligned to the fourth tab position\n"
238 " (assuming one tab equals eight spaces)\n"
241 /* Converts the acl to a custom text format */
242 static PyObject* ACL_to_any_text(PyObject *obj, PyObject *args,
245 ACL_Object *self = (ACL_Object*) obj;
247 char *arg_prefix = NULL;
248 char arg_separator = '\n';
250 static char *kwlist[] = {"prefix", "separator", "options", NULL};
252 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sci", kwlist, &arg_prefix,
253 &arg_separator, &arg_options))
256 text = acl_to_any_text(self->acl, arg_prefix, arg_separator, arg_options);
258 return PyErr_SetFromErrno(PyExc_IOError);
260 ret = PyBytes_FromString(text);
261 if(acl_free(text) != 0) {
263 return PyErr_SetFromErrno(PyExc_IOError);
268 static char __check_doc__[] =
269 "Check the ACL validity.\n"
271 "This is a non-portable, Linux specific extension that allow more\n"
272 "information to be retrieved in case an ACL is not valid than the\n"
273 "validate() method.\n"
275 "This method will return either False (the ACL is valid), or a tuple\n"
276 "with two elements. The first element is one of the following\n"
278 " - ACL_MULTI_ERROR: The ACL contains multiple entries that have a\n"
279 " tag type that may occur at most once\n"
280 " - ACL_DUPLICATE_ERROR: The ACL contains multiple ACL_USER or \n"
281 " ACL_GROUP entries with the same ID\n"
282 " - ACL_MISS_ERROR: A required entry is missing\n"
283 " - ACL_ENTRY_ERROR: The ACL contains an invalid entry tag type\n"
285 "The second element of the tuple is the index of the entry that is\n"
286 "invalid (in the same order as by iterating over the ACL entry)\n"
289 /* The acl_check method */
290 static PyObject* ACL_check(PyObject* obj, PyObject* args) {
291 ACL_Object *self = (ACL_Object*) obj;
295 if((result = acl_check(self->acl, &eindex)) == -1)
296 return PyErr_SetFromErrno(PyExc_IOError);
301 return PyTuple_Pack(2, PyInt_FromLong(result), PyInt_FromLong(eindex));
304 /* Implementation of the rich compare for ACLs */
305 static PyObject* ACL_richcompare(PyObject* o1, PyObject* o2, int op) {
306 ACL_Object *acl1, *acl2;
310 if(!PyObject_IsInstance(o2, (PyObject*)&ACL_Type)) {
315 PyErr_SetString(PyExc_TypeError, "can only compare to an ACL");
319 acl1 = (ACL_Object*)o1;
320 acl2 = (ACL_Object*)o2;
321 if((n=acl_cmp(acl1->acl, acl2->acl))==-1)
322 return PyErr_SetFromErrno(PyExc_IOError);
325 ret = n == 0 ? Py_True : Py_False;
328 ret = n == 1 ? Py_True : Py_False;
331 ret = Py_NotImplemented;
337 static char __equiv_mode_doc__[] =
338 "Return the octal mode the ACL is equivalent to.\n"
340 "This is a non-portable, Linux specific extension that checks\n"
341 "if the ACL is a basic ACL and returns the corresponding mode.\n"
343 "An IOerror exception will be raised if the ACL is an extended ACL\n"
346 /* The acl_equiv_mode method */
347 static PyObject* ACL_equiv_mode(PyObject* obj, PyObject* args) {
348 ACL_Object *self = (ACL_Object*) obj;
351 if(acl_equiv_mode(self->acl, &mode) == -1)
352 return PyErr_SetFromErrno(PyExc_IOError);
353 return PyInt_FromLong(mode);
357 /* Implementation of the compare for ACLs */
358 static int ACL_nocmp(PyObject* o1, PyObject* o2) {
360 PyErr_SetString(PyExc_TypeError, "cannot compare ACLs using cmp()");
365 static char __applyto_doc__[] =
366 "Apply the ACL to a file or filehandle.\n"
369 " - either a filename or a file-like object or an integer; this\n"
370 " represents the filesystem object on which to act\n"
371 " - optional flag representing the type of ACL to set, either\n"
372 " ACL_TYPE_ACCESS (default) or ACL_TYPE_DEFAULT\n"
375 /* Applyes the ACL to a file */
376 static PyObject* ACL_applyto(PyObject* obj, PyObject* args) {
377 ACL_Object *self = (ACL_Object*) obj;
379 acl_type_t type = ACL_TYPE_ACCESS;
383 if (!PyArg_ParseTuple(args, "O|i", &myarg, &type))
386 if(PyBytes_Check(myarg)) {
387 char *filename = PyBytes_AS_STRING(myarg);
388 nret = acl_set_file(filename, type, self->acl);
389 } else if((fd = PyObject_AsFileDescriptor(myarg)) != -1) {
390 nret = acl_set_fd(fd, self->acl);
392 PyErr_SetString(PyExc_TypeError, "argument 1 must be string, int,"
393 " or file-like object");
397 return PyErr_SetFromErrno(PyExc_IOError);
400 /* Return the result */
405 static char __valid_doc__[] =
406 "Test the ACL for validity.\n"
408 "This method tests the ACL to see if it is a valid ACL\n"
409 "in terms of the filesystem. More precisely, it checks that:\n"
411 "The ACL contains exactly one entry with each of the\n"
412 "ACL_USER_OBJ, ACL_GROUP_OBJ, and ACL_OTHER tag types. Entries\n"
413 "with ACL_USER and ACL_GROUP tag types may appear zero or more\n"
414 "times in an ACL. An ACL that contains entries of ACL_USER or\n"
415 "ACL_GROUP tag types must contain exactly one entry of the \n"
416 "ACL_MASK tag type. If an ACL contains no entries of\n"
417 "ACL_USER or ACL_GROUP tag types, the ACL_MASK entry is optional.\n"
419 "All user ID qualifiers must be unique among all entries of\n"
420 "the ACL_USER tag type, and all group IDs must be unique among all\n"
421 "entries of ACL_GROUP tag type.\n"
423 "The method will return 1 for a valid ACL and 0 for an invalid one.\n"
424 "This has been chosen because the specification for acl_valid in\n"
425 "the POSIX.1e standard documents only one possible value for errno\n"
426 "in case of an invalid ACL, so we can't differentiate between\n"
427 "classes of errors. Other suggestions are welcome.\n"
430 /* Checks the ACL for validity */
431 static PyObject* ACL_valid(PyObject* obj, PyObject* args) {
432 ACL_Object *self = (ACL_Object*) obj;
434 if(acl_valid(self->acl) == -1) {
443 #ifdef HAVE_ACL_COPY_EXT
444 static PyObject* ACL_get_state(PyObject *obj, PyObject* args) {
445 ACL_Object *self = (ACL_Object*) obj;
450 size = acl_size(self->acl);
452 return PyErr_SetFromErrno(PyExc_IOError);
454 if((ret = PyBytes_FromStringAndSize(NULL, size)) == NULL)
456 buf = PyBytes_AsString(ret);
458 if((nsize = acl_copy_ext(buf, self->acl, size)) == -1) {
460 return PyErr_SetFromErrno(PyExc_IOError);
466 static PyObject* ACL_set_state(PyObject *obj, PyObject* args) {
467 ACL_Object *self = (ACL_Object*) obj;
472 /* Parse the argument */
473 if (!PyArg_ParseTuple(args, "s#", &buf, &bufsize))
476 /* Try to import the external representation */
477 if((ptr = acl_copy_int(buf)) == NULL)
478 return PyErr_SetFromErrno(PyExc_IOError);
480 /* Free the old acl. Should we ignore errors here? */
481 if(self->acl != NULL) {
482 if(acl_free(self->acl) == -1)
483 return PyErr_SetFromErrno(PyExc_IOError);
488 /* Return the result */
496 /* tp_iter for the ACL type; since it can be iterated only
497 * destructively, the type is its iterator
499 static PyObject* ACL_iter(PyObject *obj) {
500 ACL_Object *self = (ACL_Object*)obj;
501 self->entry_id = ACL_FIRST_ENTRY;
506 /* the tp_iternext function for the ACL type */
507 static PyObject* ACL_iternext(PyObject *obj) {
508 ACL_Object *self = (ACL_Object*)obj;
509 acl_entry_t the_entry_t;
510 Entry_Object *the_entry_obj;
513 nerr = acl_get_entry(self->acl, self->entry_id, &the_entry_t);
514 self->entry_id = ACL_NEXT_ENTRY;
516 return PyErr_SetFromErrno(PyExc_IOError);
518 /* Docs says this is not needed */
519 /*PyErr_SetObject(PyExc_StopIteration, Py_None);*/
523 the_entry_obj = (Entry_Object*) PyType_GenericNew(&Entry_Type, NULL, NULL);
524 if(the_entry_obj == NULL)
527 the_entry_obj->entry = the_entry_t;
529 the_entry_obj->parent_acl = obj;
530 Py_INCREF(obj); /* For the reference we have in entry->parent */
532 return (PyObject*)the_entry_obj;
535 static char __ACL_delete_entry_doc__[] =
536 "Deletes an entry from the ACL.\n"
538 "Note: Only with level 2\n"
540 " - the Entry object which should be deleted; note that after\n"
541 " this function is called, that object is unusable any longer\n"
542 " and should be deleted\n"
545 /* Deletes an entry from the ACL */
546 static PyObject* ACL_delete_entry(PyObject *obj, PyObject *args) {
547 ACL_Object *self = (ACL_Object*)obj;
550 if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &e))
553 if(acl_delete_entry(self->acl, e->entry) == -1)
554 return PyErr_SetFromErrno(PyExc_IOError);
556 /* Return the result */
561 static char __ACL_calc_mask_doc__[] =
562 "Compute the file group class mask.\n"
564 "The calc_mask() method calculates and sets the permissions \n"
565 "associated with the ACL_MASK Entry of the ACL.\n"
566 "The value of the new permissions is the union of the permissions \n"
567 "granted by all entries of tag type ACL_GROUP, ACL_GROUP_OBJ, or \n"
568 "ACL_USER. If the ACL already contains an ACL_MASK entry, its \n"
569 "permissions are overwritten; if it does not contain an ACL_MASK \n"
570 "Entry, one is added.\n"
572 "The order of existing entries in the ACL is undefined after this \n"
576 /* Updates the mask entry in the ACL */
577 static PyObject* ACL_calc_mask(PyObject *obj, PyObject *args) {
578 ACL_Object *self = (ACL_Object*)obj;
580 if(acl_calc_mask(&self->acl) == -1)
581 return PyErr_SetFromErrno(PyExc_IOError);
583 /* Return the result */
588 static char __ACL_append_doc__[] =
589 "Append a new Entry to the ACL and return it.\n"
591 "This is a convenience function to create a new Entry \n"
592 "and append it to the ACL.\n"
593 "If a parameter of type Entry instance is given, the \n"
594 "entry will be a copy of that one (as if copied with \n"
595 "Entry.copy()), otherwise, the new entry will be empty.\n"
598 /* Convenience method to create a new Entry */
599 static PyObject* ACL_append(PyObject *obj, PyObject *args) {
600 ACL_Object* self = (ACL_Object*) obj;
601 Entry_Object* newentry;
602 Entry_Object* oldentry = NULL;
605 newentry = (Entry_Object*)PyType_GenericNew(&Entry_Type, NULL, NULL);
606 if(newentry == NULL) {
610 if (!PyArg_ParseTuple(args, "|O!", &Entry_Type, &oldentry))
613 nret = acl_create_entry(&self->acl, &newentry->entry);
616 return PyErr_SetFromErrno(PyExc_IOError);
619 if(oldentry != NULL) {
620 nret = acl_copy_entry(newentry->entry, oldentry->entry);
623 return PyErr_SetFromErrno(PyExc_IOError);
627 newentry->parent_acl = obj;
630 return (PyObject*)newentry;
633 /***** Entry type *****/
635 /* Creation of a new Entry instance */
636 static PyObject* Entry_new(PyTypeObject* type, PyObject* args,
640 newentry = PyType_GenericNew(type, args, keywds);
642 if(newentry != NULL) {
643 ((Entry_Object*)newentry)->entry = NULL;
644 ((Entry_Object*)newentry)->parent_acl = NULL;
650 /* Initialization of a new Entry instance */
651 static int Entry_init(PyObject* obj, PyObject* args, PyObject *keywds) {
652 Entry_Object* self = (Entry_Object*) obj;
653 ACL_Object* parent = NULL;
655 if (!PyArg_ParseTuple(args, "O!", &ACL_Type, &parent))
658 if(acl_create_entry(&parent->acl, &self->entry) == -1) {
659 PyErr_SetFromErrno(PyExc_IOError);
663 self->parent_acl = (PyObject*)parent;
669 /* Free the Entry instance */
670 static void Entry_dealloc(PyObject* obj) {
671 Entry_Object *self = (Entry_Object*) obj;
672 PyObject *err_type, *err_value, *err_traceback;
673 int have_error = PyErr_Occurred() ? 1 : 0;
676 PyErr_Fetch(&err_type, &err_value, &err_traceback);
677 if(self->parent_acl != NULL) {
678 Py_DECREF(self->parent_acl);
679 self->parent_acl = NULL;
682 PyErr_Restore(err_type, err_value, err_traceback);
686 /* Converts the entry to a text format */
687 static PyObject* Entry_str(PyObject *obj) {
691 PyObject *format, *kind;
692 Entry_Object *self = (Entry_Object*) obj;
694 if(acl_get_tag_type(self->entry, &tag) == -1) {
695 PyErr_SetFromErrno(PyExc_IOError);
698 if(tag == ACL_USER || tag == ACL_GROUP) {
699 if((p = acl_get_qualifier(self->entry)) == NULL) {
700 PyErr_SetFromErrno(PyExc_IOError);
703 qualifier = *(uid_t*)p;
709 format = PyBytes_FromString("ACL entry for ");
712 if(tag == ACL_UNDEFINED_TAG) {
713 kind = PyBytes_FromString("undefined type");
714 } else if(tag == ACL_USER_OBJ) {
715 kind = PyBytes_FromString("the owner");
716 } else if(tag == ACL_GROUP_OBJ) {
717 kind = PyBytes_FromString("the group");
718 } else if(tag == ACL_OTHER) {
719 kind = PyBytes_FromString("the others");
720 } else if(tag == ACL_USER) {
721 kind = PyBytes_FromFormat("user with uid %d", qualifier);
722 } else if(tag == ACL_GROUP) {
723 kind = PyBytes_FromFormat("group with gid %d", qualifier);
724 } else if(tag == ACL_MASK) {
725 kind = PyBytes_FromString("the mask");
727 kind = PyBytes_FromString("UNKNOWN_TAG_TYPE!");
731 PyBytes_ConcatAndDel(&format, kind);
736 /* Sets the tag type of the entry */
737 static int Entry_set_tag_type(PyObject* obj, PyObject* value, void* arg) {
738 Entry_Object *self = (Entry_Object*) obj;
741 PyErr_SetString(PyExc_TypeError,
742 "tag type deletion is not supported");
746 if(!PyInt_Check(value)) {
747 PyErr_SetString(PyExc_TypeError,
748 "tag type must be integer");
751 if(acl_set_tag_type(self->entry, (acl_tag_t)PyInt_AsLong(value)) == -1) {
752 PyErr_SetFromErrno(PyExc_IOError);
759 /* Returns the tag type of the entry */
760 static PyObject* Entry_get_tag_type(PyObject *obj, void* arg) {
761 Entry_Object *self = (Entry_Object*) obj;
764 if (self->entry == NULL) {
765 PyErr_SetString(PyExc_AttributeError, "entry attribute");
768 if(acl_get_tag_type(self->entry, &value) == -1) {
769 PyErr_SetFromErrno(PyExc_IOError);
773 return PyInt_FromLong(value);
776 /* Sets the qualifier (either uid_t or gid_t) for the entry,
777 * usable only if the tag type if ACL_USER or ACL_GROUP
779 static int Entry_set_qualifier(PyObject* obj, PyObject* value, void* arg) {
780 Entry_Object *self = (Entry_Object*) obj;
784 PyErr_SetString(PyExc_TypeError,
785 "qualifier deletion is not supported");
789 if(!PyInt_Check(value)) {
790 PyErr_SetString(PyExc_TypeError,
791 "tag type must be integer");
794 uidgid = PyInt_AsLong(value);
795 if(acl_set_qualifier(self->entry, (void*)&uidgid) == -1) {
796 PyErr_SetFromErrno(PyExc_IOError);
803 /* Returns the qualifier of the entry */
804 static PyObject* Entry_get_qualifier(PyObject *obj, void* arg) {
805 Entry_Object *self = (Entry_Object*) obj;
809 if (self->entry == NULL) {
810 PyErr_SetString(PyExc_AttributeError, "entry attribute");
813 if((p = acl_get_qualifier(self->entry)) == NULL) {
814 PyErr_SetFromErrno(PyExc_IOError);
820 return PyInt_FromLong(value);
823 /* Returns the parent ACL of the entry */
824 static PyObject* Entry_get_parent(PyObject *obj, void* arg) {
825 Entry_Object *self = (Entry_Object*) obj;
827 Py_INCREF(self->parent_acl);
828 return self->parent_acl;
831 /* Returns the a new Permset representing the permset of the entry
832 * FIXME: Should return a new reference to the same object, which
833 * should be created at init time!
835 static PyObject* Entry_get_permset(PyObject *obj, void* arg) {
836 Entry_Object *self = (Entry_Object*)obj;
840 p = Permset_new(&Permset_Type, NULL, NULL);
843 ps = (Permset_Object*)p;
844 if(acl_get_permset(self->entry, &ps->permset) == -1) {
845 PyErr_SetFromErrno(PyExc_IOError);
848 ps->parent_entry = obj;
854 /* Sets the permset of the entry to the passed Permset */
855 static int Entry_set_permset(PyObject* obj, PyObject* value, void* arg) {
856 Entry_Object *self = (Entry_Object*)obj;
859 if(!PyObject_IsInstance(value, (PyObject*)&Permset_Type)) {
860 PyErr_SetString(PyExc_TypeError, "argument 1 must be posix1e.Permset");
863 p = (Permset_Object*)value;
864 if(acl_set_permset(self->entry, p->permset) == -1) {
865 PyErr_SetFromErrno(PyExc_IOError);
871 static char __Entry_copy_doc__[] =
872 "Copy an ACL entry.\n"
874 "This method sets all the parameters to those of another\n"
875 "entry, even one of another's ACL\n"
877 " - src, instance of type Entry\n"
880 /* Sets all the entry parameters to another's entry */
881 static PyObject* Entry_copy(PyObject *obj, PyObject *args) {
882 Entry_Object *self = (Entry_Object*)obj;
885 if(!PyArg_ParseTuple(args, "O!", &Entry_Type, &other))
888 if(acl_copy_entry(self->entry, other->entry) == -1)
889 return PyErr_SetFromErrno(PyExc_IOError);
895 /**** Permset type *****/
897 /* Creation of a new Permset instance */
898 static PyObject* Permset_new(PyTypeObject* type, PyObject* args,
900 PyObject* newpermset;
902 newpermset = PyType_GenericNew(type, args, keywds);
904 if(newpermset != NULL) {
905 ((Permset_Object*)newpermset)->permset = NULL;
906 ((Permset_Object*)newpermset)->parent_entry = NULL;
912 /* Initialization of a new Permset instance */
913 static int Permset_init(PyObject* obj, PyObject* args, PyObject *keywds) {
914 Permset_Object* self = (Permset_Object*) obj;
915 Entry_Object* parent = NULL;
917 if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &parent))
920 if(acl_get_permset(parent->entry, &self->permset) == -1) {
921 PyErr_SetFromErrno(PyExc_IOError);
925 self->parent_entry = (PyObject*)parent;
931 /* Free the Permset instance */
932 static void Permset_dealloc(PyObject* obj) {
933 Permset_Object *self = (Permset_Object*) obj;
934 PyObject *err_type, *err_value, *err_traceback;
935 int have_error = PyErr_Occurred() ? 1 : 0;
938 PyErr_Fetch(&err_type, &err_value, &err_traceback);
939 if(self->parent_entry != NULL) {
940 Py_DECREF(self->parent_entry);
941 self->parent_entry = NULL;
944 PyErr_Restore(err_type, err_value, err_traceback);
948 /* Permset string representation */
949 static PyObject* Permset_str(PyObject *obj) {
950 Permset_Object *self = (Permset_Object*) obj;
953 pstr[0] = get_perm(self->permset, ACL_READ) ? 'r' : '-';
954 pstr[1] = get_perm(self->permset, ACL_WRITE) ? 'w' : '-';
955 pstr[2] = get_perm(self->permset, ACL_EXECUTE) ? 'x' : '-';
956 return PyBytes_FromStringAndSize(pstr, 3);
959 static char __Permset_clear_doc__[] =
960 "Clear all permissions from the permission set.\n"
963 /* Clears all permissions from the permset */
964 static PyObject* Permset_clear(PyObject* obj, PyObject* args) {
965 Permset_Object *self = (Permset_Object*) obj;
967 if(acl_clear_perms(self->permset) == -1)
968 return PyErr_SetFromErrno(PyExc_IOError);
970 /* Return the result */
975 static PyObject* Permset_get_right(PyObject *obj, void* arg) {
976 Permset_Object *self = (Permset_Object*) obj;
978 if(get_perm(self->permset, *(acl_perm_t*)arg)) {
987 static int Permset_set_right(PyObject* obj, PyObject* value, void* arg) {
988 Permset_Object *self = (Permset_Object*) obj;
992 if(!PyInt_Check(value)) {
993 PyErr_SetString(PyExc_ValueError, "a maximum of one argument must"
997 on = PyInt_AsLong(value);
999 nerr = acl_add_perm(self->permset, *(acl_perm_t*)arg);
1001 nerr = acl_delete_perm(self->permset, *(acl_perm_t*)arg);
1003 PyErr_SetFromErrno(PyExc_IOError);
1009 static char __Permset_add_doc__[] =
1010 "Add a permission to the permission set.\n"
1012 "The add() function adds the permission contained in \n"
1013 "the argument perm to the permission set. An attempt \n"
1014 "to add a permission that is already contained in the \n"
1015 "permission set is not considered an error.\n"
1018 " - perm: a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...)\n"
1020 "Return value: None\n"
1022 "Can raise: IOError\n"
1025 static PyObject* Permset_add(PyObject* obj, PyObject* args) {
1026 Permset_Object *self = (Permset_Object*) obj;
1029 if (!PyArg_ParseTuple(args, "i", &right))
1032 if(acl_add_perm(self->permset, (acl_perm_t) right) == -1)
1033 return PyErr_SetFromErrno(PyExc_IOError);
1035 /* Return the result */
1040 static char __Permset_delete_doc__[] =
1041 "Delete a permission from the permission set.\n"
1043 "The delete() function deletes the permission contained in \n"
1044 "the argument perm from the permission set. An attempt \n"
1045 "to delete a permission that is not contained in the \n"
1046 "permission set is not considered an error.\n"
1048 " - perm a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...)\n"
1049 "Return value: None\n"
1051 "Can raise: IOError\n"
1054 static PyObject* Permset_delete(PyObject* obj, PyObject* args) {
1055 Permset_Object *self = (Permset_Object*) obj;
1058 if (!PyArg_ParseTuple(args, "i", &right))
1061 if(acl_delete_perm(self->permset, (acl_perm_t) right) == -1)
1062 return PyErr_SetFromErrno(PyExc_IOError);
1064 /* Return the result */
1069 static char __Permset_test_doc__[] =
1070 "Test if a permission exists in the permission set.\n"
1072 "The test() function tests if the permission contained in \n"
1073 "the argument perm exits the permission set.\n"
1075 " - perm a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...)\n"
1076 "Return value: Boolean\n"
1078 "Can raise: IOError\n"
1081 static PyObject* Permset_test(PyObject* obj, PyObject* args) {
1082 Permset_Object *self = (Permset_Object*) obj;
1086 if (!PyArg_ParseTuple(args, "i", &right))
1089 ret = get_perm(self->permset, (acl_perm_t) right);
1091 return PyErr_SetFromErrno(PyExc_IOError);
1097 Py_INCREF(Py_False);
1104 static char __ACL_Type_doc__[] =
1105 "Type which represents a POSIX ACL\n"
1107 "Parameters (only one keword parameter should be provided):\n"
1108 " - file=\"...\", meaning create ACL representing\n"
1109 " the access ACL of that file\n"
1110 " - filedef=\"...\", meaning create ACL representing\n"
1111 " the default ACL of that directory\n"
1112 " - fd=<int>, meaning create ACL representing\n"
1113 " the access ACL of that file descriptor\n"
1114 " - text=\"...\", meaning create ACL from a \n"
1115 " textual description\n"
1116 " - acl=<ACL instance>, meaning create a copy\n"
1117 " of an existing ACL instance\n"
1118 " - mode=<int>, meaning create an ACL from a numeric mode\n"
1119 " (e.g. mode=0644) (this is valid only when the C library\n"
1120 " provides the acl_from_mode call)\n"
1122 "If no parameters are passed, create an empty ACL; this\n"
1123 "makes sense only when your OS supports ACL modification\n"
1124 "(i.e. it implements full POSIX.1e support)\n"
1127 /* ACL type methods */
1128 static PyMethodDef ACL_methods[] = {
1129 {"applyto", ACL_applyto, METH_VARARGS, __applyto_doc__},
1130 {"valid", ACL_valid, METH_NOARGS, __valid_doc__},
1132 {"to_any_text", (PyCFunction)ACL_to_any_text, METH_VARARGS | METH_KEYWORDS,
1133 __to_any_text_doc__},
1134 {"check", ACL_check, METH_NOARGS, __check_doc__},
1135 {"equiv_mode", ACL_equiv_mode, METH_NOARGS, __equiv_mode_doc__},
1137 #ifdef HAVE_ACL_COPYEXT
1138 {"__getstate__", ACL_get_state, METH_NOARGS,
1139 "Dumps the ACL to an external format."},
1140 {"__setstate__", ACL_set_state, METH_VARARGS,
1141 "Loads the ACL from an external format."},
1144 {"delete_entry", ACL_delete_entry, METH_VARARGS, __ACL_delete_entry_doc__},
1145 {"calc_mask", ACL_calc_mask, METH_NOARGS, __ACL_calc_mask_doc__},
1146 {"append", ACL_append, METH_VARARGS, __ACL_append_doc__},
1148 {NULL, NULL, 0, NULL}
1152 /* The definition of the ACL Type */
1153 static PyTypeObject ACL_Type = {
1154 PyObject_HEAD_INIT(NULL)
1159 ACL_dealloc, /* tp_dealloc */
1163 ACL_nocmp, /* tp_compare */
1165 0, /* tp_as_number */
1166 0, /* tp_as_sequence */
1167 0, /* tp_as_mapping */
1170 ACL_str, /* tp_str */
1171 0, /* tp_getattro */
1172 0, /* tp_setattro */
1173 0, /* tp_as_buffer */
1174 Py_TPFLAGS_DEFAULT, /* tp_flags */
1175 __ACL_Type_doc__, /* tp_doc */
1176 0, /* tp_traverse */
1179 ACL_richcompare, /* tp_richcompare */
1181 0, /* tp_richcompare */
1183 0, /* tp_weaklistoffset */
1189 0, /* tp_iternext */
1191 ACL_methods, /* tp_methods */
1196 0, /* tp_descr_get */
1197 0, /* tp_descr_set */
1198 0, /* tp_dictoffset */
1199 ACL_init, /* tp_init */
1201 ACL_new, /* tp_new */
1206 /* Entry type methods */
1207 static PyMethodDef Entry_methods[] = {
1208 {"copy", Entry_copy, METH_VARARGS, __Entry_copy_doc__},
1209 {NULL, NULL, 0, NULL}
1212 static char __Entry_tagtype_doc__[] =
1213 "The tag type of the current entry\n"
1216 " - ACL_UNDEFINED_TAG\n"
1219 " - ACL_GROUP_OBJ\n"
1225 static char __Entry_qualifier_doc__[] =
1226 "The qualifier of the current entry\n"
1228 "If the tag type is ACL_USER, this should be a user id.\n"
1229 "If the tag type if ACL_GROUP, this should be a group id.\n"
1230 "Else, it doesn't matter.\n"
1233 static char __Entry_parent_doc__[] =
1234 "The parent ACL of this entry\n"
1237 static char __Entry_permset_doc__[] =
1238 "The permission set of this ACL entry\n"
1242 static PyGetSetDef Entry_getsets[] = {
1243 {"tag_type", Entry_get_tag_type, Entry_set_tag_type,
1244 __Entry_tagtype_doc__},
1245 {"qualifier", Entry_get_qualifier, Entry_set_qualifier,
1246 __Entry_qualifier_doc__},
1247 {"parent", Entry_get_parent, NULL, __Entry_parent_doc__},
1248 {"permset", Entry_get_permset, Entry_set_permset, __Entry_permset_doc__},
1252 static char __Entry_Type_doc__[] =
1253 "Type which represents an entry in an ACL.\n"
1255 "The type exists only if the OS has full support for POSIX.1e\n"
1256 "Can be created either by:\n"
1258 " >>> e = posix1e.Entry(myACL) # this creates a new entry in the ACL\n"
1259 " >>> e = myACL.append() # another way for doing the same thing\n"
1262 " >>> for entry in myACL:\n"
1263 " ... print entry\n"
1265 "Note that the Entry keeps a reference to its ACL, so even if \n"
1266 "you delete the ACL, it won't be cleaned up and will continue to \n"
1267 "exist until its Entry(ies) will be deleted.\n"
1269 /* The definition of the Entry Type */
1270 static PyTypeObject Entry_Type = {
1271 PyObject_HEAD_INIT(NULL)
1274 sizeof(Entry_Object),
1276 Entry_dealloc, /* tp_dealloc */
1282 0, /* tp_as_number */
1283 0, /* tp_as_sequence */
1284 0, /* tp_as_mapping */
1287 Entry_str, /* tp_str */
1288 0, /* tp_getattro */
1289 0, /* tp_setattro */
1290 0, /* tp_as_buffer */
1291 Py_TPFLAGS_DEFAULT, /* tp_flags */
1292 __Entry_Type_doc__, /* tp_doc */
1293 0, /* tp_traverse */
1295 0, /* tp_richcompare */
1296 0, /* tp_weaklistoffset */
1298 0, /* tp_iternext */
1299 Entry_methods, /* tp_methods */
1301 Entry_getsets, /* tp_getset */
1304 0, /* tp_descr_get */
1305 0, /* tp_descr_set */
1306 0, /* tp_dictoffset */
1307 Entry_init, /* tp_init */
1309 Entry_new, /* tp_new */
1312 /* Permset type methods */
1313 static PyMethodDef Permset_methods[] = {
1314 {"clear", Permset_clear, METH_NOARGS, __Permset_clear_doc__, },
1315 {"add", Permset_add, METH_VARARGS, __Permset_add_doc__, },
1316 {"delete", Permset_delete, METH_VARARGS, __Permset_delete_doc__, },
1317 {"test", Permset_test, METH_VARARGS, __Permset_test_doc__, },
1318 {NULL, NULL, 0, NULL}
1321 static char __Permset_execute_doc__[] =
1322 "Execute permsission\n"
1324 "This is a convenience method of access; the \n"
1325 "same effect can be achieved using the functions\n"
1326 "add(), test(), delete(), and those can take any \n"
1327 "permission defined by your platform.\n"
1330 static char __Permset_read_doc__[] =
1331 "Read permsission\n"
1333 "This is a convenience method of access; the \n"
1334 "same effect can be achieved using the functions\n"
1335 "add(), test(), delete(), and those can take any \n"
1336 "permission defined by your platform.\n"
1339 static char __Permset_write_doc__[] =
1340 "Write permsission\n"
1342 "This is a convenience method of access; the \n"
1343 "same effect can be achieved using the functions\n"
1344 "add(), test(), delete(), and those can take any \n"
1345 "permission defined by your platform.\n"
1348 /* Permset getset */
1349 static PyGetSetDef Permset_getsets[] = {
1350 {"execute", Permset_get_right, Permset_set_right,
1351 __Permset_execute_doc__, &holder_ACL_EXECUTE},
1352 {"read", Permset_get_right, Permset_set_right,
1353 __Permset_read_doc__, &holder_ACL_READ},
1354 {"write", Permset_get_right, Permset_set_right,
1355 __Permset_write_doc__, &holder_ACL_WRITE},
1359 static char __Permset_Type_doc__[] =
1360 "Type which represents the permission set in an ACL entry\n"
1362 "The type exists only if the OS has full support for POSIX.1e\n"
1363 "Can be retrieved either by:\n\n"
1364 ">>> perms = myEntry.permset\n"
1367 ">>> perms = posix1e.Permset(myEntry)\n"
1369 "Note that the Permset keeps a reference to its Entry, so even if \n"
1370 "you delete the entry, it won't be cleaned up and will continue to \n"
1371 "exist until its Permset will be deleted.\n"
1374 /* The definition of the Permset Type */
1375 static PyTypeObject Permset_Type = {
1376 PyObject_HEAD_INIT(NULL)
1379 sizeof(Permset_Object),
1381 Permset_dealloc, /* tp_dealloc */
1387 0, /* tp_as_number */
1388 0, /* tp_as_sequence */
1389 0, /* tp_as_mapping */
1392 Permset_str, /* tp_str */
1393 0, /* tp_getattro */
1394 0, /* tp_setattro */
1395 0, /* tp_as_buffer */
1396 Py_TPFLAGS_DEFAULT, /* tp_flags */
1397 __Permset_Type_doc__,/* tp_doc */
1398 0, /* tp_traverse */
1400 0, /* tp_richcompare */
1401 0, /* tp_weaklistoffset */
1403 0, /* tp_iternext */
1404 Permset_methods, /* tp_methods */
1406 Permset_getsets, /* tp_getset */
1409 0, /* tp_descr_get */
1410 0, /* tp_descr_set */
1411 0, /* tp_dictoffset */
1412 Permset_init, /* tp_init */
1414 Permset_new, /* tp_new */
1419 /* Module methods */
1421 static char __deletedef_doc__[] =
1422 "Delete the default ACL from a directory.\n"
1424 "This function deletes the default ACL associated with \n"
1425 "a directory (the ACL which will be ANDed with the mode\n"
1426 "parameter to the open, creat functions).\n"
1428 " - a string representing the directory whose default ACL\n"
1429 " should be deleted\n"
1432 /* Deletes the default ACL from a directory */
1433 static PyObject* aclmodule_delete_default(PyObject* obj, PyObject* args) {
1436 /* Parse the arguments */
1437 if (!PyArg_ParseTuple(args, "s", &filename))
1440 if(acl_delete_def_file(filename) == -1) {
1441 return PyErr_SetFromErrno(PyExc_IOError);
1444 /* Return the result */
1450 static char __has_extended_doc__[] =
1451 "Check if a file or filehandle has an extended ACL.\n"
1454 " - either a filename or a file-like object or an integer; this\n"
1455 " represents the filesystem object on which to act\n"
1458 /* Check for extended ACL a file or fd */
1459 static PyObject* aclmodule_has_extended(PyObject* obj, PyObject* args) {
1464 if (!PyArg_ParseTuple(args, "O", &myarg))
1467 if(PyBytes_Check(myarg)) {
1468 const char *filename = PyBytes_AS_STRING(myarg);
1469 nret = acl_extended_file(filename);
1470 } else if((fd = PyObject_AsFileDescriptor(myarg)) != -1) {
1471 nret = acl_extended_fd(fd);
1473 PyErr_SetString(PyExc_TypeError, "argument 1 must be string, int,"
1474 " or file-like object");
1478 return PyErr_SetFromErrno(PyExc_IOError);
1481 /* Return the result */
1482 return PyBool_FromLong(nret);
1486 /* The module methods */
1487 static PyMethodDef aclmodule_methods[] = {
1488 {"delete_default", aclmodule_delete_default, METH_VARARGS,
1491 {"has_extended", aclmodule_has_extended, METH_VARARGS,
1492 __has_extended_doc__},
1494 {NULL, NULL, 0, NULL}
1497 static char __posix1e_doc__[] =
1498 "POSIX.1e ACLs manipulation\n"
1500 "This module provides support for manipulating POSIX.1e ACLS\n"
1502 "Depending on the operating system support for POSIX.1e, \n"
1503 "the ACL type will have more or less capabilities:\n"
1504 " - level 1, only basic support, you can create\n"
1505 " ACLs from files and text descriptions;\n"
1506 " once created, the type is immutable\n"
1507 " - level 2, complete support, you can alter\n"
1508 " the ACL once it is created\n"
1510 "Also, in level 2, more types are available, corresponding\n"
1511 "to acl_entry_t (the Entry type), acl_permset_t (the Permset type).\n"
1513 "The existence of level 2 support and other extensions can be\n"
1514 "checked by the constants:\n"
1515 " - HAS_ACL_ENTRY for level 2 and the Entry/Permset classes\n"
1516 " - HAS_ACL_FROM_MODE for ACL(mode=...) usage\n"
1517 " - HAS_ACL_CHECK for the ACL().check function\n"
1518 " - HAS_EXTENDED_CHECK for the module-level has_extended function\n"
1519 " - HAS_EQUIV_MODE for the ACL().equiv_mode method\n"
1523 ">>> import posix1e\n"
1524 ">>> acl1 = posix1e.ACL(file=\"file.txt\") \n"
1530 ">>> b = posix1e.ACL(text=\"u::rx,g::-,o::-\")\n"
1536 ">>> b.applyto(\"file.txt\")\n"
1537 ">>> print posix1e.ACL(file=\"file.txt\")\n"
1545 void initposix1e(void) {
1548 ACL_Type.ob_type = &PyType_Type;
1549 if(PyType_Ready(&ACL_Type) < 0)
1553 Entry_Type.ob_type = &PyType_Type;
1554 if(PyType_Ready(&Entry_Type) < 0)
1557 Permset_Type.ob_type = &PyType_Type;
1558 if(PyType_Ready(&Permset_Type) < 0)
1562 m = Py_InitModule3("posix1e", aclmodule_methods, __posix1e_doc__);
1564 d = PyModule_GetDict(m);
1568 Py_INCREF(&ACL_Type);
1569 if (PyDict_SetItemString(d, "ACL",
1570 (PyObject *) &ACL_Type) < 0)
1573 /* 23.3.6 acl_type_t values */
1574 PyModule_AddIntConstant(m, "ACL_TYPE_ACCESS", ACL_TYPE_ACCESS);
1575 PyModule_AddIntConstant(m, "ACL_TYPE_DEFAULT", ACL_TYPE_DEFAULT);
1579 Py_INCREF(&Entry_Type);
1580 if (PyDict_SetItemString(d, "Entry",
1581 (PyObject *) &Entry_Type) < 0)
1584 Py_INCREF(&Permset_Type);
1585 if (PyDict_SetItemString(d, "Permset",
1586 (PyObject *) &Permset_Type) < 0)
1589 /* 23.2.2 acl_perm_t values */
1590 PyModule_AddIntConstant(m, "ACL_READ", ACL_READ);
1591 PyModule_AddIntConstant(m, "ACL_WRITE", ACL_WRITE);
1592 PyModule_AddIntConstant(m, "ACL_EXECUTE", ACL_EXECUTE);
1594 /* 23.2.5 acl_tag_t values */
1595 PyModule_AddIntConstant(m, "ACL_UNDEFINED_TAG", ACL_UNDEFINED_TAG);
1596 PyModule_AddIntConstant(m, "ACL_USER_OBJ", ACL_USER_OBJ);
1597 PyModule_AddIntConstant(m, "ACL_USER", ACL_USER);
1598 PyModule_AddIntConstant(m, "ACL_GROUP_OBJ", ACL_GROUP_OBJ);
1599 PyModule_AddIntConstant(m, "ACL_GROUP", ACL_GROUP);
1600 PyModule_AddIntConstant(m, "ACL_MASK", ACL_MASK);
1601 PyModule_AddIntConstant(m, "ACL_OTHER", ACL_OTHER);
1603 /* Document extended functionality via easy-to-use constants */
1604 PyModule_AddIntConstant(m, "HAS_ACL_ENTRY", 1);
1606 PyModule_AddIntConstant(m, "HAS_ACL_ENTRY", 0);
1610 /* Linux libacl specific acl_to_any_text constants */
1611 PyModule_AddIntConstant(m, "TEXT_ABBREVIATE", TEXT_ABBREVIATE);
1612 PyModule_AddIntConstant(m, "TEXT_NUMERIC_IDS", TEXT_NUMERIC_IDS);
1613 PyModule_AddIntConstant(m, "TEXT_SOME_EFFECTIVE", TEXT_SOME_EFFECTIVE);
1614 PyModule_AddIntConstant(m, "TEXT_ALL_EFFECTIVE", TEXT_ALL_EFFECTIVE);
1615 PyModule_AddIntConstant(m, "TEXT_SMART_INDENT", TEXT_SMART_INDENT);
1617 /* Linux libacl specific acl_check constants */
1618 PyModule_AddIntConstant(m, "ACL_MULTI_ERROR", ACL_MULTI_ERROR);
1619 PyModule_AddIntConstant(m, "ACL_DUPLICATE_ERROR", ACL_DUPLICATE_ERROR);
1620 PyModule_AddIntConstant(m, "ACL_MISS_ERROR", ACL_MISS_ERROR);
1621 PyModule_AddIntConstant(m, "ACL_ENTRY_ERROR", ACL_ENTRY_ERROR);
1623 /* declare the Linux extensions */
1624 PyModule_AddIntConstant(m, "HAS_ACL_FROM_MODE", 1);
1625 PyModule_AddIntConstant(m, "HAS_ACL_CHECK", 1);
1626 PyModule_AddIntConstant(m, "HAS_EXTENDED_CHECK", 1);
1627 PyModule_AddIntConstant(m, "HAS_EQUIV_MODE", 1);
1629 PyModule_AddIntConstant(m, "HAS_ACL_FROM_MODE", 0);
1630 PyModule_AddIntConstant(m, "HAS_ACL_CHECK", 0);
1631 PyModule_AddIntConstant(m, "HAS_EXTENDED_CHECK", 0);
1632 PyModule_AddIntConstant(m, "HAS_EQUIV_MODE", 0);