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