]> git.k1024.org Git - pylibacl.git/blob - acl.c
Add support for building on GNU/kFreeBSD
[pylibacl.git] / acl.c
1 #include <sys/types.h>
2 #include <sys/acl.h>
3
4 #include <Python.h>
5
6 #ifdef HAVE_LINUX
7 #include "os_linux.c"
8 #endif
9
10 staticforward PyTypeObject ACL_Type;
11 static PyObject* ACL_applyto(PyObject* obj, PyObject* args);
12 static PyObject* ACL_valid(PyObject* obj, PyObject* args);
13
14 #ifdef HAVE_LEVEL2
15 static PyObject* ACL_get_state(PyObject *obj, PyObject* args);
16 static PyObject* ACL_set_state(PyObject *obj, PyObject* args);
17
18 staticforward PyTypeObject Entry_Type;
19 staticforward PyTypeObject Permset_Type;
20 static PyObject* Permset_new(PyTypeObject* type, PyObject* args, PyObject *keywds);
21 #endif
22
23 typedef struct {
24     PyObject_HEAD
25     acl_t acl;
26 #ifdef HAVE_LEVEL2
27     int entry_id;
28 #endif
29 } ACL_Object;
30
31 #ifdef HAVE_LEVEL2
32
33 typedef struct {
34     PyObject_HEAD
35     PyObject *parent_acl; /* The parent acl, so it won't run out on us */
36     acl_entry_t entry;
37 } Entry_Object;
38
39 typedef struct {
40     PyObject_HEAD
41     PyObject *parent_entry; /* The parent entry, so it won't run out on us */
42     acl_permset_t permset;
43 } Permset_Object;
44
45 #endif
46
47 /* Creation of a new ACL instance */
48 static PyObject* ACL_new(PyTypeObject* type, PyObject* args, PyObject *keywds) {
49     PyObject* newacl;
50
51     newacl = type->tp_alloc(type, 0);
52
53     if(newacl != NULL) {
54         ((ACL_Object*)newacl)->acl = NULL;
55 #ifdef HAVEL_LEVEL2
56         ((ACL_Object*)newacl)->entry_id = ACL_FIRST_ENTRY;
57 #endif
58     }
59
60     return newacl;
61 }
62
63 /* Initialization of a new ACL instance */
64 static int ACL_init(PyObject* obj, PyObject* args, PyObject *keywds) {
65     ACL_Object* self = (ACL_Object*) obj;
66     static char *kwlist[] = { "file", "fd", "text", "acl", "filedef", NULL };
67     char *file = NULL;
68     char *filedef = NULL;
69     char *text = NULL;
70     int fd = -1;
71     ACL_Object* thesrc = NULL;
72
73     if(!PyTuple_Check(args) || PyTuple_Size(args) != 0 ||
74        (keywds != NULL && PyDict_Check(keywds) && PyDict_Size(keywds) > 1)) {
75         PyErr_SetString(PyExc_ValueError, "a max of one keyword argument must be passed");
76         return -1;
77     }
78     if(!PyArg_ParseTupleAndKeywords(args, keywds, "|sisO!s", kwlist,
79                                      &file, &fd, &text, &ACL_Type, &thesrc, &filedef))
80         return -1;
81
82     /* Free the old acl_t without checking for error, we don't
83      * care right now */
84     if(self->acl != NULL)
85         acl_free(self->acl);
86
87     if(file != NULL)
88         self->acl = acl_get_file(file, ACL_TYPE_ACCESS);
89     else if(text != NULL)
90         self->acl = acl_from_text(text);
91     else if(fd != -1)
92         self->acl = acl_get_fd(fd);
93     else if(thesrc != NULL)
94         self->acl = acl_dup(thesrc->acl);
95     else if(filedef != NULL)
96         self->acl = acl_get_file(filedef, ACL_TYPE_DEFAULT);
97     else
98         self->acl = acl_init(0);
99
100     if(self->acl == NULL) {
101         PyErr_SetFromErrno(PyExc_IOError);
102         return -1;
103     }
104
105     return 0;
106 }
107
108 /* Standard type functions */
109 static void ACL_dealloc(PyObject* obj) {
110     ACL_Object *self = (ACL_Object*) obj;
111     PyObject *err_type, *err_value, *err_traceback;
112     int have_error = PyErr_Occurred() ? 1 : 0;
113
114     if (have_error)
115         PyErr_Fetch(&err_type, &err_value, &err_traceback);
116     if(self->acl != NULL && acl_free(self->acl) != 0)
117         PyErr_WriteUnraisable(obj);
118     if (have_error)
119         PyErr_Restore(err_type, err_value, err_traceback);
120     PyObject_DEL(self);
121 }
122
123 /* Converts the acl to a text format */
124 static PyObject* ACL_str(PyObject *obj) {
125     char *text;
126     ACL_Object *self = (ACL_Object*) obj;
127     PyObject *ret;
128
129     text = acl_to_text(self->acl, NULL);
130     if(text == NULL) {
131         return PyErr_SetFromErrno(PyExc_IOError);
132     }
133     ret = PyString_FromString(text);
134     if(acl_free(text) != 0) {
135         Py_DECREF(ret);
136         return PyErr_SetFromErrno(PyExc_IOError);
137     }
138     return ret;
139 }
140
141 /* Custom methods */
142 static char __applyto_doc__[] = \
143 "Apply the ACL to a file or filehandle.\n" \
144 "\n" \
145 "Parameters:\n" \
146 "  - either a filename or a file-like object or an integer; this\n" \
147 "    represents the filesystem object on which to act\n" \
148 "  - optional flag representing the type of ACL to set, either\n" \
149 "    ACL_TYPE_ACCESS (default) or ACL_TYPE_DEFAULT\n" \
150 ;
151
152 /* Applyes the ACL to a file */
153 static PyObject* ACL_applyto(PyObject* obj, PyObject* args) {
154     ACL_Object *self = (ACL_Object*) obj;
155     PyObject *myarg;
156     acl_type_t type = ACL_TYPE_ACCESS;
157     int nret;
158     int fd;
159
160     if (!PyArg_ParseTuple(args, "O|i", &myarg, &type))
161         return NULL;
162
163     if(PyString_Check(myarg)) {
164         char *filename = PyString_AS_STRING(myarg);
165         nret = acl_set_file(filename, type, self->acl);
166     } else if((fd = PyObject_AsFileDescriptor(myarg)) != -1) {
167         nret = acl_set_fd(fd, self->acl);
168     } else {
169         PyErr_SetString(PyExc_TypeError, "argument 1 must be string, int, or file-like object");
170         return 0;
171     }
172     if(nret == -1) {
173         return PyErr_SetFromErrno(PyExc_IOError);
174     }
175
176     /* Return the result */
177     Py_INCREF(Py_None);
178     return Py_None;
179 }
180
181 static char __valid_doc__[] = \
182 "Test the ACL for validity.\n" \
183 "\n" \
184 "This method tests the ACL to see if it is a valid ACL\n" \
185 "in terms of the filesystem. More precisely, it checks:\n" \
186 "A valid ACL contains exactly one entry with each of the ACL_USER_OBJ,\n" \
187 "ACL_GROUP_OBJ, and ACL_OTHER tag types. Entries with ACL_USER and\n" \
188 "ACL_GROUP tag types may appear zero or more times in an ACL. An ACL that\n" \
189 "contains entries of ACL_USER or ACL_GROUP tag types must contain exactly\n" \
190 "one entry of the ACL_MASK tag type. If an ACL contains no entries of\n" \
191 "ACL_USER or ACL_GROUP tag types, the ACL_MASK entry is optional.\n" \
192 "\n" \
193 "All user ID qualifiers must be unique among all entries of ACL_USER tag\n" \
194 "type, and all group IDs must be unique among all entries of ACL_GROUP tag\n" \
195 "type.\n" \
196 "\n" \
197 "The method will return 1 for a valid ACL and 0 for an invalid one.\n" \
198 "This has been chosen because the specification for acl_valid in POSIX.1e\n" \
199 "documents only one possible value for errno in case of an invalid ACL, \n" \
200 "so we can't differentiate between classes of errors. Other suggestions \n" \
201 "are welcome.\n" \
202 ;
203
204 /* Checks the ACL for validity */
205 static PyObject* ACL_valid(PyObject* obj, PyObject* args) {
206     ACL_Object *self = (ACL_Object*) obj;
207
208     if(acl_valid(self->acl) == -1) {
209         Py_INCREF(Py_False);
210         return Py_False;
211     } else {
212         Py_INCREF(Py_True);
213         return Py_True;
214     }
215 }
216
217 #ifdef HAVE_LEVEL2
218
219 static PyObject* ACL_get_state(PyObject *obj, PyObject* args) {
220     ACL_Object *self = (ACL_Object*) obj;
221     PyObject *ret;
222     ssize_t size, nsize;
223     char *buf;
224
225     size = acl_size(self->acl);
226     if(size == -1)
227         return PyErr_SetFromErrno(PyExc_IOError);
228
229     if((ret = PyString_FromStringAndSize(NULL, size)) == NULL)
230         return NULL;
231     buf = PyString_AsString(ret);
232     
233     if((nsize = acl_copy_ext(buf, self->acl, size)) == -1) {
234         Py_DECREF(ret);
235         return PyErr_SetFromErrno(PyExc_IOError);
236     }
237     
238     return ret;
239 }
240
241 static PyObject* ACL_set_state(PyObject *obj, PyObject* args) {
242     ACL_Object *self = (ACL_Object*) obj;
243     const void *buf;
244     int bufsize;
245     acl_t ptr;
246
247     /* Parse the argument */
248     if (!PyArg_ParseTuple(args, "s#", &buf, &bufsize))
249         return NULL;
250
251     /* Try to import the external representation */
252     if((ptr = acl_copy_int(buf)) == NULL)
253         return PyErr_SetFromErrno(PyExc_IOError);
254         
255     /* Free the old acl. Should we ignore errors here? */
256     if(self->acl != NULL) {
257         if(acl_free(self->acl) == -1)
258             return PyErr_SetFromErrno(PyExc_IOError);
259     }
260
261     self->acl = ptr;
262
263     /* Return the result */
264     Py_INCREF(Py_None);
265     return Py_None;
266 }
267
268 /* tp_iter for the ACL type; since it can be iterated only
269  * destructively, the type is its iterator
270  */
271 static PyObject* ACL_iter(PyObject *obj) {
272     ACL_Object *self = (ACL_Object*)obj;
273     self->entry_id = ACL_FIRST_ENTRY;
274     Py_INCREF(obj);
275     return obj;
276 }
277
278 /* the tp_iternext function for the ACL type */
279 static PyObject* ACL_iternext(PyObject *obj) {
280     ACL_Object *self = (ACL_Object*)obj;
281     acl_entry_t the_entry_t;
282     Entry_Object *the_entry_obj;
283     int nerr;
284     
285     nerr = acl_get_entry(self->acl, self->entry_id, &the_entry_t);
286     self->entry_id = ACL_NEXT_ENTRY;
287     if(nerr == -1)
288         return PyErr_SetFromErrno(PyExc_IOError);
289     else if(nerr == 0) {
290         /* Docs says this is not needed */
291         /*PyErr_SetObject(PyExc_StopIteration, Py_None);*/
292         return NULL;
293     }
294
295     the_entry_obj = (Entry_Object*) PyType_GenericNew(&Entry_Type, NULL, NULL);
296     if(the_entry_obj == NULL)
297         return NULL;
298     
299     the_entry_obj->entry = the_entry_t;
300
301     the_entry_obj->parent_acl = obj;
302     Py_INCREF(obj); /* For the reference we have in entry->parent */
303
304     return (PyObject*)the_entry_obj;
305 }
306
307 static char __ACL_delete_entry_doc__[] = \
308 "Deletes an entry from the ACL.\n" \
309 "\n" \
310 "Note: Only with level 2\n" \
311 "Parameters:\n" \
312 " - the Entry object which should be deleted; note that after\n" \
313 "   this function is called, that object is unusable any longer\n" \
314 "   and should be deleted\n" \
315 ;
316
317 /* Deletes an entry from the ACL */
318 static PyObject* ACL_delete_entry(PyObject *obj, PyObject *args) {
319     ACL_Object *self = (ACL_Object*)obj;
320     Entry_Object *e;
321
322     if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &e))
323         return NULL;
324
325     if(acl_delete_entry(self->acl, e->entry) == -1)
326         return PyErr_SetFromErrno(PyExc_IOError);
327
328     /* Return the result */
329     Py_INCREF(Py_None);
330     return Py_None;
331 }
332
333 static char __ACL_calc_mask_doc__[] = \
334 "Compute the file group class mask.\n" \
335 "\n" \
336 "The calc_mask() method calculates and sets the permissions \n" \
337 "associated with the ACL_MASK Entry of the ACL.\n" \
338 "The value of the new permissions is the union of the permissions \n" \
339 "granted by all entries of tag type ACL_GROUP, ACL_GROUP_OBJ, or \n" \
340 "ACL_USER.  If the ACL already contains an ACL_MASK entry, its \n" \
341 "permissions are overwritten; if it does not contain an ACL_MASK \n" \
342 "Entry, one is added.\n" \
343 "\n" \
344 "The order of existing entries in the ACL is undefined after this \n" \
345 "function.\n" \
346 ;
347
348 /* Updates the mask entry in the ACL */
349 static PyObject* ACL_calc_mask(PyObject *obj, PyObject *args) {
350     ACL_Object *self = (ACL_Object*)obj;
351     
352     if(acl_calc_mask(&self->acl) == -1)
353         return PyErr_SetFromErrno(PyExc_IOError);
354
355     /* Return the result */
356     Py_INCREF(Py_None);
357     return Py_None;
358 }
359
360 static char __ACL_append_doc__[] = \
361 "Append a new Entry to the ACL and return it.\n" \
362 "\n" \
363 "This is a convenience function to create a new Entry \n" \
364 "and append it to the ACL.\n" \
365 "If a parameter of type Entry instance is given, the \n" \
366 "entry will be a copy of that one (as if copied with \n" \
367 "Entry.copy()), otherwise, the new entry will be empty.\n" \
368 ;
369
370 /* Convenience method to create a new Entry */
371 static PyObject* ACL_append(PyObject *obj, PyObject *args) {
372     ACL_Object* self = (ACL_Object*) obj;
373     Entry_Object* newentry;
374     Entry_Object* oldentry = NULL;
375     int nret;
376
377     newentry = (Entry_Object*)PyType_GenericNew(&Entry_Type, NULL, NULL);
378     if(newentry == NULL) {
379         return NULL;
380     }
381
382     if (!PyArg_ParseTuple(args, "|O!", &Entry_Type, &oldentry))
383         return NULL;
384
385     nret = acl_create_entry(&self->acl, &newentry->entry);
386     if(nret == -1) {
387         Py_DECREF(newentry);
388         return PyErr_SetFromErrno(PyExc_IOError);
389     }
390
391     if(oldentry != NULL) {
392         nret = acl_copy_entry(newentry->entry, oldentry->entry);
393         if(nret == -1) {
394             Py_DECREF(newentry);
395             return PyErr_SetFromErrno(PyExc_IOError);
396         }
397     }
398
399     newentry->parent_acl = obj;
400     Py_INCREF(obj);
401     
402     return (PyObject*)newentry;
403 }
404
405 /***** Entry type *****/
406
407 /* Creation of a new Entry instance */
408 static PyObject* Entry_new(PyTypeObject* type, PyObject* args, PyObject *keywds) {
409     PyObject* newentry;
410
411     newentry = PyType_GenericNew(type, args, keywds);
412
413     if(newentry != NULL) {
414         ((Entry_Object*)newentry)->entry = NULL;
415         ((Entry_Object*)newentry)->parent_acl = NULL;
416     }
417
418     return newentry;
419 }
420
421 /* Initialization of a new Entry instance */
422 static int Entry_init(PyObject* obj, PyObject* args, PyObject *keywds) {
423     Entry_Object* self = (Entry_Object*) obj;
424     ACL_Object* parent = NULL;
425
426     if (!PyArg_ParseTuple(args, "O!", &ACL_Type, &parent))
427         return -1;
428
429     if(acl_create_entry(&parent->acl, &self->entry) == -1) {
430         PyErr_SetFromErrno(PyExc_IOError);
431         return -1;
432     }
433
434     self->parent_acl = (PyObject*)parent;
435     Py_INCREF(parent);
436
437     return 0;
438 }
439
440 /* Free the Entry instance */
441 static void Entry_dealloc(PyObject* obj) {
442     Entry_Object *self = (Entry_Object*) obj;
443     PyObject *err_type, *err_value, *err_traceback;
444     int have_error = PyErr_Occurred() ? 1 : 0;
445
446     if (have_error)
447         PyErr_Fetch(&err_type, &err_value, &err_traceback);
448     if(self->parent_acl != NULL) {
449         Py_DECREF(self->parent_acl);
450         self->parent_acl = NULL;
451     }
452     if (have_error)
453         PyErr_Restore(err_type, err_value, err_traceback);
454     PyObject_DEL(self);
455 }
456
457 /* Converts the entry to a text format */
458 static PyObject* Entry_str(PyObject *obj) {
459     acl_tag_t tag;
460     uid_t qualifier;
461     void *p;
462     PyObject *ret;
463     PyObject *format, *list;
464     Entry_Object *self = (Entry_Object*) obj;
465
466     if(acl_get_tag_type(self->entry, &tag) == -1) {
467         PyErr_SetFromErrno(PyExc_IOError);
468         return NULL;
469     }
470     if(tag == ACL_USER || tag == ACL_GROUP) {
471         if((p = acl_get_qualifier(self->entry)) == NULL) {
472             PyErr_SetFromErrno(PyExc_IOError);
473             return NULL;
474         }
475         qualifier = *(uid_t*)p;
476         acl_free(p);
477     } else {
478         qualifier = 0;
479     }
480     
481     format = PyString_FromString("ACL entry for %s");
482     if(format == NULL)
483         return NULL;
484     list = PyTuple_New(1);
485     if(tag == ACL_UNDEFINED_TAG) {
486         PyTuple_SetItem(list, 0, PyString_FromString("undefined type"));
487     } else if(tag == ACL_USER_OBJ) {
488         PyTuple_SetItem(list, 0, PyString_FromString("the owner"));
489     } else if(tag == ACL_GROUP_OBJ) {
490         PyTuple_SetItem(list, 0, PyString_FromString("the group"));
491     } else if(tag == ACL_OTHER) {
492         PyTuple_SetItem(list, 0, PyString_FromString("the others"));
493     } else if(tag == ACL_USER) {
494         PyTuple_SetItem(list, 0, PyString_FromFormat("user with uid %d", qualifier));
495     } else if(tag == ACL_GROUP) {
496         PyTuple_SetItem(list, 0, PyString_FromFormat("group with gid %d", qualifier));
497     } else if(tag == ACL_MASK) {
498         PyTuple_SetItem(list, 0, PyString_FromString("the mask"));
499     } else {
500         PyTuple_SetItem(list, 0, PyString_FromString("UNKNOWN_TAG_TYPE!"));
501     }
502     ret = PyString_Format(format, list);
503     Py_DECREF(format);
504     Py_DECREF(list);
505     return ret;
506 }
507
508 /* Sets the tag type of the entry */
509 static int Entry_set_tag_type(PyObject* obj, PyObject* value, void* arg) {
510     Entry_Object *self = (Entry_Object*) obj;
511
512     if(value == NULL) {
513         PyErr_SetString(PyExc_TypeError,
514                         "tag type deletion is not supported");
515         return -1;
516     }
517
518     if(!PyInt_Check(value)) {
519         PyErr_SetString(PyExc_TypeError,
520                         "tag type must be integer");
521         return -1;
522     }
523     if(acl_set_tag_type(self->entry, (acl_tag_t)PyInt_AsLong(value)) == -1) {
524         PyErr_SetFromErrno(PyExc_IOError);
525         return -1;
526     }
527
528     return 0;
529 }
530
531 /* Returns the tag type of the entry */
532 static PyObject* Entry_get_tag_type(PyObject *obj, void* arg) {
533     Entry_Object *self = (Entry_Object*) obj;
534     acl_tag_t value;
535
536     if (self->entry == NULL) {
537         PyErr_SetString(PyExc_AttributeError, "entry attribute");
538         return NULL;
539     }
540     if(acl_get_tag_type(self->entry, &value) == -1) {
541         PyErr_SetFromErrno(PyExc_IOError);
542         return NULL;
543     }
544
545     return PyInt_FromLong(value);
546 }
547
548 /* Sets the qualifier (either uid_t or gid_t) for the entry,
549  * usable only if the tag type if ACL_USER or ACL_GROUP
550  */
551 static int Entry_set_qualifier(PyObject* obj, PyObject* value, void* arg) {
552     Entry_Object *self = (Entry_Object*) obj;
553     int uidgid;
554
555     if(value == NULL) {
556         PyErr_SetString(PyExc_TypeError,
557                         "qualifier deletion is not supported");
558         return -1;
559     }
560
561     if(!PyInt_Check(value)) {
562         PyErr_SetString(PyExc_TypeError,
563                         "tag type must be integer");
564         return -1;
565     }
566     uidgid = PyInt_AsLong(value);
567     if(acl_set_qualifier(self->entry, (void*)&uidgid) == -1) {
568         PyErr_SetFromErrno(PyExc_IOError);
569         return -1;
570     }
571
572     return 0;
573 }
574
575 /* Returns the qualifier of the entry */
576 static PyObject* Entry_get_qualifier(PyObject *obj, void* arg) {
577     Entry_Object *self = (Entry_Object*) obj;
578     void *p;
579     int value;
580
581     if (self->entry == NULL) {
582         PyErr_SetString(PyExc_AttributeError, "entry attribute");
583         return NULL;
584     }
585     if((p = acl_get_qualifier(self->entry)) == NULL) {
586         PyErr_SetFromErrno(PyExc_IOError);
587         return NULL;
588     }
589     value = *(uid_t*)p;
590     acl_free(p);
591     
592     return PyInt_FromLong(value);
593 }
594
595 /* Returns the parent ACL of the entry */
596 static PyObject* Entry_get_parent(PyObject *obj, void* arg) {
597     Entry_Object *self = (Entry_Object*) obj;
598     
599     Py_INCREF(self->parent_acl);
600     return self->parent_acl;
601 }
602
603 /* Returns the a new Permset representing the permset of the entry 
604  * FIXME: Should return a new reference to the same object, which
605  * should be created at init time!
606 */
607 static PyObject* Entry_get_permset(PyObject *obj, void* arg) {
608     Entry_Object *self = (Entry_Object*)obj;
609     PyObject *p;
610     Permset_Object *ps;
611
612     p = Permset_new(&Permset_Type, NULL, NULL);
613     if(p == NULL)
614         return NULL;
615     ps = (Permset_Object*)p;
616     if(acl_get_permset(self->entry, &ps->permset) == -1) {
617         PyErr_SetFromErrno(PyExc_IOError);
618         return NULL;
619     }
620     ps->parent_entry = obj;
621     Py_INCREF(obj);
622
623     return (PyObject*)p;
624 }
625
626 /* Sets the permset of the entry to the passed Permset */
627 static int Entry_set_permset(PyObject* obj, PyObject* value, void* arg) {
628     Entry_Object *self = (Entry_Object*)obj;
629     Permset_Object *p;
630
631     if(!PyObject_IsInstance(value, (PyObject*)&Permset_Type)) {
632         PyErr_SetString(PyExc_TypeError, "argument 1 must be posix1e.Permset");
633         return -1;
634     }
635     p = (Permset_Object*)value;
636     if(acl_set_permset(self->entry, p->permset) == -1) {
637         PyErr_SetFromErrno(PyExc_IOError);
638         return -1;
639     }
640     return 0;
641 }
642
643 static char __Entry_copy_doc__[] = \
644 "Copy an ACL entry.\n" \
645 "\n" \
646 "This method sets all the parameters to those of another\n" \
647 "entry, even one of another's ACL\n" \
648 "Parameters:\n" \
649 " - src, instance of type Entry\n" \
650 ;
651
652 /* Sets all the entry parameters to another's entry */
653 static PyObject* Entry_copy(PyObject *obj, PyObject *args) {
654     Entry_Object *self = (Entry_Object*)obj;
655     Entry_Object *other;
656     
657     if(!PyArg_ParseTuple(args, "O!", &Entry_Type, &other))
658         return NULL;
659
660     if(acl_copy_entry(self->entry, other->entry) == -1)
661         return PyErr_SetFromErrno(PyExc_IOError);
662
663     Py_INCREF(Py_None);
664     return Py_None;
665 }
666
667 /**** Permset type *****/
668
669 /* Creation of a new Permset instance */
670 static PyObject* Permset_new(PyTypeObject* type, PyObject* args, PyObject *keywds) {
671     PyObject* newpermset;
672
673     newpermset = PyType_GenericNew(type, args, keywds);
674
675     if(newpermset != NULL) {
676         ((Permset_Object*)newpermset)->permset = NULL;
677         ((Permset_Object*)newpermset)->parent_entry = NULL;
678     }
679
680     return newpermset;
681 }
682
683 /* Initialization of a new Permset instance */
684 static int Permset_init(PyObject* obj, PyObject* args, PyObject *keywds) {
685     Permset_Object* self = (Permset_Object*) obj;
686     Entry_Object* parent = NULL;
687
688     if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &parent))
689         return -1;
690
691     if(acl_get_permset(parent->entry, &self->permset) == -1) {
692         PyErr_SetFromErrno(PyExc_IOError);
693         return -1;
694     }
695
696     self->parent_entry = (PyObject*)parent;
697     Py_INCREF(parent);
698
699     return 0;
700 }
701
702 /* Free the Permset instance */
703 static void Permset_dealloc(PyObject* obj) {
704     Permset_Object *self = (Permset_Object*) obj;
705     PyObject *err_type, *err_value, *err_traceback;
706     int have_error = PyErr_Occurred() ? 1 : 0;
707
708     if (have_error)
709         PyErr_Fetch(&err_type, &err_value, &err_traceback);
710     if(self->parent_entry != NULL) {
711         Py_DECREF(self->parent_entry);
712         self->parent_entry = NULL;
713     }
714     if (have_error)
715         PyErr_Restore(err_type, err_value, err_traceback);
716     PyObject_DEL(self);
717 }
718
719 /* Permset string representation */
720 static PyObject* Permset_str(PyObject *obj) {
721     Permset_Object *self = (Permset_Object*) obj;
722     char pstr[3];
723
724     pstr[0] = get_perm(self->permset, ACL_READ) ? 'r' : '-';
725     pstr[1] = get_perm(self->permset, ACL_WRITE) ? 'w' : '-';
726     pstr[2] = get_perm(self->permset, ACL_EXECUTE) ? 'x' : '-';
727     return PyString_FromStringAndSize(pstr, 3);
728 }
729
730 static char __Permset_clear_doc__[] = \
731 "Clear all permissions from the permission set.\n" \
732 ;
733
734 /* Clears all permissions from the permset */
735 static PyObject* Permset_clear(PyObject* obj, PyObject* args) {
736     Permset_Object *self = (Permset_Object*) obj;
737
738     if(acl_clear_perms(self->permset) == -1)
739         return PyErr_SetFromErrno(PyExc_IOError);
740
741     /* Return the result */
742     Py_INCREF(Py_None);
743     return Py_None;
744 }
745
746 static PyObject* Permset_get_right(PyObject *obj, void* arg) {
747     Permset_Object *self = (Permset_Object*) obj;
748
749     if(get_perm(self->permset, (int)arg)) {
750         Py_INCREF(Py_True);
751         return Py_True;
752     } else {
753         Py_INCREF(Py_False);
754         return Py_False;
755     }
756 }
757
758 static int Permset_set_right(PyObject* obj, PyObject* value, void* arg) {
759     Permset_Object *self = (Permset_Object*) obj;
760     int on;
761     int nerr;
762
763     if(!PyInt_Check(value)) {
764         PyErr_SetString(PyExc_ValueError, "a maximum of one argument must be passed");
765         return -1;
766     }        
767     on = PyInt_AsLong(value);
768     if(on)
769         nerr = acl_add_perm(self->permset, (int)arg);
770     else
771         nerr = acl_delete_perm(self->permset, (int)arg);
772     if(nerr == -1) {
773         PyErr_SetFromErrno(PyExc_IOError);
774         return -1;
775     }
776     return 0;
777 }
778
779 static char __Permset_add_doc__[] = \
780 "Add a permission to the permission set.\n" \
781 "\n" \
782 "The add() function adds the permission contained in \n" \
783 "the argument perm to the permission set.  An attempt \n" \
784 "to add a permission that is already contained in the \n" \
785 "permission set is not considered an error.\n" \
786 "Parameters:\n" \
787 "  - perm a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...\n" \
788 "Return value:\n" \
789 "  None\n" \
790 "Can raise: IOError\n" \
791 ;
792
793 static PyObject* Permset_add(PyObject* obj, PyObject* args) {
794     Permset_Object *self = (Permset_Object*) obj;
795     int right;
796
797     if (!PyArg_ParseTuple(args, "i", &right))
798         return NULL;
799
800     if(acl_add_perm(self->permset, (acl_perm_t) right) == -1)
801         return PyErr_SetFromErrno(PyExc_IOError);
802
803     /* Return the result */
804     Py_INCREF(Py_None);
805     return Py_None;
806 }
807
808 static char __Permset_delete_doc__[] = \
809 "Delete a permission from the permission set.\n" \
810 "\n" \
811 "The delete() function deletes the permission contained in \n" \
812 "the argument perm from the permission set.  An attempt \n" \
813 "to delete a permission that is not contained in the \n" \
814 "permission set is not considered an error.\n" \
815 "Parameters:\n" \
816 "  - perm a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...\n" \
817 "Return value:\n" \
818 "  None\n" \
819 "Can raise: IOError\n" \
820 ;
821
822 static PyObject* Permset_delete(PyObject* obj, PyObject* args) {
823     Permset_Object *self = (Permset_Object*) obj;
824     int right;
825
826     if (!PyArg_ParseTuple(args, "i", &right))
827         return NULL;
828
829     if(acl_delete_perm(self->permset, (acl_perm_t) right) == -1)
830         return PyErr_SetFromErrno(PyExc_IOError);
831
832     /* Return the result */
833     Py_INCREF(Py_None);
834     return Py_None;
835 }
836
837 static char __Permset_test_doc__[] = \
838 "Test if a permission exists in the permission set.\n" \
839 "\n" \
840 "The test() function tests if the permission contained in \n" \
841 "the argument perm exits the permission set.\n" \
842 "Parameters:\n" \
843 "  - perm a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...\n" \
844 "Return value:\n" \
845 "  Bool\n" \
846 "Can raise: IOError\n" \
847 ;
848
849 static PyObject* Permset_test(PyObject* obj, PyObject* args) {
850     Permset_Object *self = (Permset_Object*) obj;
851     int right;
852     int ret;
853
854     if (!PyArg_ParseTuple(args, "i", &right))
855         return NULL;
856
857     ret = get_perm(self->permset, (acl_perm_t) right);
858     if(ret == -1)
859         return PyErr_SetFromErrno(PyExc_IOError);
860
861     if(ret) {
862         Py_INCREF(Py_True);
863         return Py_True;
864     } else {
865         Py_INCREF(Py_False);
866         return Py_False;
867     }
868 }
869
870 #endif
871
872 static char __ACL_Type_doc__[] = \
873 "Type which represents a POSIX ACL\n" \
874 "\n" \
875 "Parameters:\n" \
876 "  Only one keword parameter should be provided:\n"
877 "  - file=\"...\", meaning create ACL representing\n"
878 "    the access ACL of that file\n" \
879 "  - filedef=\"...\", meaning create ACL representing\n"
880 "    the default ACL of that directory\n" \
881 "  - fd=<int>, meaning create ACL representing\n" \
882 "    the access ACL of that file descriptor\n" \
883 "  - text=\"...\", meaning create ACL from a \n" \
884 "    textual description\n" \
885 "  - acl=<ACL instance>, meaning create a copy\n" \
886 "    of an existing ACL instance\n" \
887 "If no parameters are passed, create an empty ACL; this\n" \
888 "makes sense only when your OS supports ACL modification\n" \
889 " (i.e. it implements full POSIX.1e support)\n" \
890 ;
891
892 /* ACL type methods */
893 static PyMethodDef ACL_methods[] = {
894     {"applyto", ACL_applyto, METH_VARARGS, __applyto_doc__},
895     {"valid", ACL_valid, METH_NOARGS, __valid_doc__},
896 #ifdef HAVE_LEVEL2
897     {"__getstate__", ACL_get_state, METH_NOARGS, "Dumps the ACL to an external format."},
898     {"__setstate__", ACL_set_state, METH_VARARGS, "Loads the ACL from an external format."},
899     {"delete_entry", ACL_delete_entry, METH_VARARGS, __ACL_delete_entry_doc__},
900     {"calc_mask", ACL_calc_mask, METH_NOARGS, __ACL_calc_mask_doc__},
901     {"append", ACL_append, METH_VARARGS, __ACL_append_doc__},
902 #endif
903     {NULL, NULL, 0, NULL}
904 };
905
906
907 /* The definition of the ACL Type */
908 static PyTypeObject ACL_Type = {
909     PyObject_HEAD_INIT(NULL)
910     0,
911     "posix1e.ACL",
912     sizeof(ACL_Object),
913     0,
914     ACL_dealloc,        /* tp_dealloc */
915     0,                  /* tp_print */
916     0,                  /* tp_getattr */
917     0,                  /* tp_setattr */
918     0,                  /* tp_compare */
919     0,                  /* tp_repr */
920     0,                  /* tp_as_number */
921     0,                  /* tp_as_sequence */
922     0,                  /* tp_as_mapping */
923     0,                  /* tp_hash */
924     0,                  /* tp_call */
925     ACL_str,            /* tp_str */
926     0,                  /* tp_getattro */
927     0,                  /* tp_setattro */
928     0,                  /* tp_as_buffer */
929     Py_TPFLAGS_DEFAULT, /* tp_flags */
930     __ACL_Type_doc__,   /* tp_doc */
931     0,                  /* tp_traverse */
932     0,                  /* tp_clear */
933     0,                  /* tp_richcompare */
934     0,                  /* tp_weaklistoffset */
935 #ifdef HAVE_LEVEL2
936     ACL_iter,
937     ACL_iternext,
938 #else
939     0,                  /* tp_iter */
940     0,                  /* tp_iternext */
941 #endif
942     ACL_methods,        /* tp_methods */
943     0,                  /* tp_members */
944     0,                  /* tp_getset */
945     0,                  /* tp_base */
946     0,                  /* tp_dict */
947     0,                  /* tp_descr_get */
948     0,                  /* tp_descr_set */
949     0,                  /* tp_dictoffset */
950     ACL_init,           /* tp_init */
951     0,                  /* tp_alloc */
952     ACL_new,            /* tp_new */
953 };
954
955 #ifdef HAVE_LEVEL2
956
957 /* Entry type methods */
958 static PyMethodDef Entry_methods[] = {
959     {"copy", Entry_copy, METH_VARARGS, __Entry_copy_doc__},
960     {NULL, NULL, 0, NULL}
961 };
962
963 static char __Entry_tagtype_doc__[] = \
964 "The tag type of the current entry\n" \
965 "\n" \
966 "This is one of:\n" \
967 " - ACL_UNDEFINED_TAG\n" \
968 " - ACL_USER_OBJ\n" \
969 " - ACL_USER\n" \
970 " - ACL_GROUP_OBJ\n" \
971 " - ACL_GROUP\n" \
972 " - ACL_MASK\n" \
973 " - ACL_OTHER\n" \
974 ;
975
976 static char __Entry_qualifier_doc__[] = \
977 "The qualifier of the current entry\n" \
978 "\n" \
979 "If the tag type is ACL_USER, this should be a user id.\n" \
980 "If the tag type if ACL_GROUP, this should be a group id.\n" \
981 "Else, it doesn't matter.\n" \
982 ;
983
984 static char __Entry_parent_doc__[] = \
985 "The parent ACL of this entry\n" \
986 ;
987
988 static char __Entry_permset_doc__[] = \
989 "The permission set of this ACL entry\n" \
990 ;
991
992 /* Entry getset */
993 static PyGetSetDef Entry_getsets[] = {
994     {"tag_type", Entry_get_tag_type, Entry_set_tag_type, __Entry_tagtype_doc__},
995     {"qualifier", Entry_get_qualifier, Entry_set_qualifier, __Entry_qualifier_doc__},
996     {"parent", Entry_get_parent, NULL, __Entry_parent_doc__},
997     {"permset", Entry_get_permset, Entry_set_permset, __Entry_permset_doc__},
998     {NULL}
999 };
1000
1001 static char __Entry_Type_doc__[] = \
1002 "Type which represents an entry in an ACL.\n" \
1003 "\n" \
1004 "The type exists only if the OS has full support for POSIX.1e\n" \
1005 "Can be created either by:\n" \
1006 "  e = posix1e.Entry(myACL) # this creates a new entry in the ACL\n" \
1007 "or by:\n" \
1008 "  for entry in myACL:\n" \
1009 "      print entry\n" \
1010 "\n" \
1011 "Note that the Entry keeps a reference to its ACL, so even if \n" \
1012 "you delete the ACL, it won't be cleaned up and will continue to \n" \
1013 "exist until its Entry(ies) will be deleted.\n" \
1014 ;
1015 /* The definition of the Entry Type */
1016 static PyTypeObject Entry_Type = {
1017     PyObject_HEAD_INIT(NULL)
1018     0,
1019     "posix1e.Entry",
1020     sizeof(Entry_Object),
1021     0,
1022     Entry_dealloc,      /* tp_dealloc */
1023     0,                  /* tp_print */
1024     0,                  /* tp_getattr */
1025     0,                  /* tp_setattr */
1026     0,                  /* tp_compare */
1027     0,                  /* tp_repr */
1028     0,                  /* tp_as_number */
1029     0,                  /* tp_as_sequence */
1030     0,                  /* tp_as_mapping */
1031     0,                  /* tp_hash */
1032     0,                  /* tp_call */
1033     Entry_str,          /* tp_str */
1034     0,                  /* tp_getattro */
1035     0,                  /* tp_setattro */
1036     0,                  /* tp_as_buffer */
1037     Py_TPFLAGS_DEFAULT, /* tp_flags */
1038     __Entry_Type_doc__, /* tp_doc */
1039     0,                  /* tp_traverse */
1040     0,                  /* tp_clear */
1041     0,                  /* tp_richcompare */
1042     0,                  /* tp_weaklistoffset */
1043     0,                  /* tp_iter */
1044     0,                  /* tp_iternext */
1045     Entry_methods,      /* tp_methods */
1046     0,                  /* tp_members */
1047     Entry_getsets,      /* tp_getset */
1048     0,                  /* tp_base */
1049     0,                  /* tp_dict */
1050     0,                  /* tp_descr_get */
1051     0,                  /* tp_descr_set */
1052     0,                  /* tp_dictoffset */
1053     Entry_init,         /* tp_init */
1054     0,                  /* tp_alloc */
1055     Entry_new,          /* tp_new */
1056 };
1057
1058 /* Permset type methods */
1059 static PyMethodDef Permset_methods[] = {
1060     {"clear", Permset_clear, METH_NOARGS, __Permset_clear_doc__, },
1061     {"add", Permset_add, METH_VARARGS, __Permset_add_doc__, },
1062     {"delete", Permset_delete, METH_VARARGS, __Permset_delete_doc__, },
1063     {"test", Permset_test, METH_VARARGS, __Permset_test_doc__, },
1064     {NULL, NULL, 0, NULL}
1065 };
1066
1067 static char __Permset_execute_doc__[] = \
1068 "Execute permsission\n" \
1069 "\n" \
1070 "This is a convenience method of access; the \n" \
1071 "same effect can be achieved using the functions\n" \
1072 "add(), test(), delete(), and those can take any \n" \
1073 "permission defined by your platform.\n" \
1074 ;
1075
1076 static char __Permset_read_doc__[] = \
1077 "Read permsission\n" \
1078 "\n" \
1079 "This is a convenience method of access; the \n" \
1080 "same effect can be achieved using the functions\n" \
1081 "add(), test(), delete(), and those can take any \n" \
1082 "permission defined by your platform.\n" \
1083 ;
1084
1085 static char __Permset_write_doc__[] = \
1086 "Write permsission\n" \
1087 "\n" \
1088 "This is a convenience method of access; the \n" \
1089 "same effect can be achieved using the functions\n" \
1090 "add(), test(), delete(), and those can take any \n" \
1091 "permission defined by your platform.\n" \
1092 ;
1093
1094 /* Permset getset */
1095 static PyGetSetDef Permset_getsets[] = {
1096     {"execute", Permset_get_right, Permset_set_right, __Permset_execute_doc__, (void*)ACL_EXECUTE},
1097     {"read", Permset_get_right, Permset_set_right, __Permset_read_doc__, (void*)ACL_READ},
1098     {"write", Permset_get_right, Permset_set_right, __Permset_write_doc__, (void*)ACL_WRITE},
1099     {NULL}
1100 };
1101
1102 static char __Permset_Type_doc__[] = \
1103 "Type which represents the permission set in an ACL entry\n" \
1104 "\n" \
1105 "The type exists only if the OS has full support for POSIX.1e\n" \
1106 "Can be created either by:\n" \
1107 "  perms = myEntry.permset\n" \
1108 "or by:\n" \
1109 "  perms = posix1e.Permset(myEntry)\n" \
1110 "\n" \
1111 "Note that the Permset keeps a reference to its Entry, so even if \n" \
1112 "you delete the entry, it won't be cleaned up and will continue to \n" \
1113 "exist until its Permset will be deleted.\n" \
1114 ;
1115
1116 /* The definition of the Permset Type */
1117 static PyTypeObject Permset_Type = {
1118     PyObject_HEAD_INIT(NULL)
1119     0,
1120     "posix1e.Permset",
1121     sizeof(Permset_Object),
1122     0,
1123     Permset_dealloc,    /* tp_dealloc */
1124     0,                  /* tp_print */
1125     0,                  /* tp_getattr */
1126     0,                  /* tp_setattr */
1127     0,                  /* tp_compare */
1128     0,                  /* tp_repr */
1129     0,                  /* tp_as_number */
1130     0,                  /* tp_as_sequence */
1131     0,                  /* tp_as_mapping */
1132     0,                  /* tp_hash */
1133     0,                  /* tp_call */
1134     Permset_str,        /* tp_str */
1135     0,                  /* tp_getattro */
1136     0,                  /* tp_setattro */
1137     0,                  /* tp_as_buffer */
1138     Py_TPFLAGS_DEFAULT, /* tp_flags */
1139     __Permset_Type_doc__,/* tp_doc */
1140     0,                  /* tp_traverse */
1141     0,                  /* tp_clear */
1142     0,                  /* tp_richcompare */
1143     0,                  /* tp_weaklistoffset */
1144     0,                  /* tp_iter */
1145     0,                  /* tp_iternext */
1146     Permset_methods,    /* tp_methods */
1147     0,                  /* tp_members */
1148     Permset_getsets,    /* tp_getset */
1149     0,                  /* tp_base */
1150     0,                  /* tp_dict */
1151     0,                  /* tp_descr_get */
1152     0,                  /* tp_descr_set */
1153     0,                  /* tp_dictoffset */
1154     Permset_init,       /* tp_init */
1155     0,                  /* tp_alloc */
1156     Permset_new,        /* tp_new */
1157 };
1158
1159 #endif
1160
1161 /* Module methods */
1162
1163 static char __deletedef_doc__[] = \
1164 "Delete the default ACL from a directory.\n" \
1165 "\n" \
1166 "This function deletes the default ACL associated with \n" \
1167 "a directory (the ACL which will be ANDed with the mode\n" \
1168 "parameter to the open, creat functions).\n" \
1169 "Parameters:\n" \
1170 "  - a string representing the directory whose default ACL\n" \
1171 "    should be deleted\n" \
1172 ;
1173
1174 /* Deletes the default ACL from a directory */
1175 static PyObject* aclmodule_delete_default(PyObject* obj, PyObject* args) {
1176     char *filename;
1177
1178     /* Parse the arguments */
1179     if (!PyArg_ParseTuple(args, "s", &filename))
1180         return NULL;
1181
1182     if(acl_delete_def_file(filename) == -1) {
1183         return PyErr_SetFromErrno(PyExc_IOError);
1184     }
1185
1186     /* Return the result */
1187     Py_INCREF(Py_None);
1188     return Py_None;
1189 }
1190
1191 /* The module methods */
1192 static PyMethodDef aclmodule_methods[] = {
1193     {"delete_default", aclmodule_delete_default, METH_VARARGS, __deletedef_doc__},
1194     {NULL, NULL, 0, NULL}
1195 };
1196
1197 static char __posix1e_doc__[] = \
1198 "POSIX.1e ACLs manipulation\n" \
1199 "\n" \
1200 "This module provides support for manipulating POSIX.1e ACLS\n" \
1201 "\n" \
1202 "Depending on the operating system support for POSIX.1e, \n" \
1203 "the ACL type will have more or less capabilities:\n" \
1204 "  - level 1, only basic support, you can create\n" \
1205 "    ACLs from files and text descriptions;\n" \
1206 "    once created, the type is immutable\n" \
1207 "  - level 2, complete support, you can alter\n"\
1208 "    the ACL once it is created\n" \
1209 "\n" \
1210 "Also, in level 2, more types are available, corresponding\n" \
1211 "to acl_entry_t (Entry type), acl_permset_t (Permset type).\n" \
1212 "\n" \
1213 "Example:\n" \
1214 ">>> import posix1e\n" \
1215 ">>> acl1 = posix1e.ACL(file=\"file.txt\") \n" \
1216 ">>> print acl1\n" \
1217 "user::rw-\n" \
1218 "group::rw-\n" \
1219 "other::r--\n" \
1220 "\n" \
1221 ">>> b = posix1e.ACL(text=\"u::rx,g::-,o::-\")\n" \
1222 ">>> print b\n" \
1223 "user::r-x\n" \
1224 "group::---\n" \
1225 "other::---\n" \
1226 "\n" \
1227 ">>> b.applyto(\"file.txt\")\n" \
1228 ">>> print posix1e.ACL(file=\"file.txt\")\n" \
1229 "user::r-x\n" \
1230 "group::---\n" \
1231 "other::---\n" \
1232 "\n" \
1233 ">>>\n" \
1234 ;
1235
1236 void initposix1e(void) {
1237     PyObject *m, *d;
1238
1239     ACL_Type.ob_type = &PyType_Type;
1240     if(PyType_Ready(&ACL_Type) < 0)
1241         return;
1242
1243 #ifdef HAVE_LEVEL2
1244     Entry_Type.ob_type = &PyType_Type;
1245     if(PyType_Ready(&Entry_Type) < 0)
1246         return;
1247
1248     Permset_Type.ob_type = &PyType_Type;
1249     if(PyType_Ready(&Permset_Type) < 0)
1250         return;
1251 #endif
1252
1253     m = Py_InitModule3("posix1e", aclmodule_methods, __posix1e_doc__);
1254
1255     d = PyModule_GetDict(m);
1256     if (d == NULL)
1257         return;
1258
1259     Py_INCREF(&ACL_Type);
1260     if (PyDict_SetItemString(d, "ACL",
1261                              (PyObject *) &ACL_Type) < 0)
1262         return;
1263
1264     /* 23.3.6 acl_type_t values */
1265     PyModule_AddIntConstant(m, "ACL_TYPE_ACCESS", ACL_TYPE_ACCESS);
1266     PyModule_AddIntConstant(m, "ACL_TYPE_DEFAULT", ACL_TYPE_DEFAULT);
1267
1268
1269 #ifdef HAVE_LEVEL2
1270     Py_INCREF(&Entry_Type);
1271     if (PyDict_SetItemString(d, "Entry",
1272                              (PyObject *) &Entry_Type) < 0)
1273         return;
1274
1275     Py_INCREF(&Permset_Type);
1276     if (PyDict_SetItemString(d, "Permset",
1277                              (PyObject *) &Permset_Type) < 0)
1278         return;
1279
1280     /* 23.2.2 acl_perm_t values */
1281     PyModule_AddIntConstant(m, "ACL_READ", ACL_READ);
1282     PyModule_AddIntConstant(m, "ACL_WRITE", ACL_WRITE);
1283     PyModule_AddIntConstant(m, "ACL_EXECUTE", ACL_EXECUTE);
1284
1285     /* 23.2.5 acl_tag_t values */
1286     PyModule_AddIntConstant(m, "ACL_UNDEFINED_TAG", ACL_UNDEFINED_TAG);
1287     PyModule_AddIntConstant(m, "ACL_USER_OBJ", ACL_USER_OBJ);
1288     PyModule_AddIntConstant(m, "ACL_USER", ACL_USER);
1289     PyModule_AddIntConstant(m, "ACL_GROUP_OBJ", ACL_GROUP_OBJ);
1290     PyModule_AddIntConstant(m, "ACL_GROUP", ACL_GROUP);
1291     PyModule_AddIntConstant(m, "ACL_MASK", ACL_MASK);
1292     PyModule_AddIntConstant(m, "ACL_OTHER", ACL_OTHER);
1293
1294 #endif
1295 }