]> git.k1024.org Git - pylibacl.git/blob - acl.c
Prepare for first public release
[pylibacl.git] / acl.c
1 #include <sys/types.h>
2 #include <sys/acl.h>
3
4 #include <Python.h>
5
6 staticforward PyTypeObject ACLType;
7 static PyObject* ACL_applyto(PyObject* obj, PyObject* args);
8 static PyObject* ACL_valid(PyObject* obj, PyObject* args);
9 #ifdef HAVE_LEVEL2
10 static PyObject* ACL_get_state(PyObject *obj, PyObject* args);
11 static PyObject* ACL_set_state(PyObject *obj, PyObject* args);
12 #endif
13
14 typedef struct {
15     PyObject_HEAD
16     acl_t ob_acl;
17 } ACLObject;
18
19 /* Creation of a new ACL instance */
20 static PyObject* ACL_new(PyTypeObject* type, PyObject* args, PyObject *keywds) {
21     PyObject* newacl;
22
23     newacl = type->tp_alloc(type, 0);
24
25     if(newacl != NULL)
26         ((ACLObject*)newacl)->ob_acl = NULL;
27
28     return newacl;
29 }
30
31 /* Initialization of a new ACL instance */
32 static int ACL_init(PyObject* obj, PyObject* args, PyObject *keywds) {
33     ACLObject* self = (ACLObject*) obj;
34     static char *kwlist[] = { "file", "fd", "text", "acl", NULL };
35     char *file = NULL;
36     char *text = NULL;
37     int fd = -1;
38     ACLObject* thesrc = NULL;
39     int tmp;
40
41     if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sisO!", kwlist,
42                                      &file, &fd, &text, &ACLType, &thesrc))
43         return -1;
44     tmp = 0;
45     if(file != NULL)
46         tmp++;
47     if(text != NULL)
48         tmp++;
49     if(fd != -1)
50         tmp++;
51     if(thesrc != NULL)
52         tmp++;
53     if(tmp > 1) {
54         PyErr_SetString(PyExc_ValueError, "a maximum of one argument must be passed");
55         return -1;
56     }
57
58     /* Free the old acl_t without checking for error, we don't
59      * care right now */
60     if(self->ob_acl != NULL)
61         acl_free(self->ob_acl);
62
63     if(file != NULL)
64         self->ob_acl = acl_get_file(file, ACL_TYPE_ACCESS);
65     else if(text != NULL)
66         self->ob_acl = acl_from_text(text);
67     else if(fd != -1)
68         self->ob_acl = acl_get_fd(fd);
69     else if(thesrc != NULL)
70         self->ob_acl = acl_dup(thesrc->ob_acl);
71     else
72         self->ob_acl = acl_init(0);
73
74     if(self->ob_acl == NULL) {
75         PyErr_SetFromErrno(PyExc_IOError);
76         return -1;
77     }
78
79     return 0;
80 }
81
82 /* Standard type functions */
83 static void ACL_dealloc(PyObject* obj) {
84     ACLObject *self = (ACLObject*) obj;
85     PyObject *err_type, *err_value, *err_traceback;
86     int have_error = PyErr_Occurred() ? 1 : 0;
87
88     if (have_error)
89         PyErr_Fetch(&err_type, &err_value, &err_traceback);
90     if(acl_free(self->ob_acl) != 0)
91         PyErr_WriteUnraisable(obj);
92     if (have_error)
93         PyErr_Restore(err_type, err_value, err_traceback);
94     PyObject_DEL(self);
95 }
96
97 /* Converts the acl to a text format */
98 static PyObject* ACL_repr(PyObject *obj) {
99     char *text;
100     ACLObject *self = (ACLObject*) obj;
101     PyObject *ret;
102
103     text = acl_to_text(self->ob_acl, NULL);
104     if(text == NULL) {
105         return PyErr_SetFromErrno(PyExc_IOError);
106     }
107     ret = PyString_FromString(text);
108     if(acl_free(text) != 0) {
109         Py_DECREF(ret);
110         return PyErr_SetFromErrno(PyExc_IOError);
111     }
112     return ret;
113 }
114
115 /* Custom methods */
116 static char __applyto_doc__[] = \
117 "Apply the ACL to a file or filehandle.\n" \
118 "\n" \
119 "Parameters:\n" \
120 "  - either a filename or a file-like object or an integer; this\n" \
121 "    represents the filesystem object on which to act\n" \
122 ;
123
124 /* Applyes the ACL to a file */
125 static PyObject* ACL_applyto(PyObject* obj, PyObject* args) {
126     ACLObject *self = (ACLObject*) obj;
127     PyObject *myarg;
128     int type_default = 0;
129     acl_type_t type = ACL_TYPE_ACCESS;
130     int nret;
131     int fd;
132
133     if (!PyArg_ParseTuple(args, "O|i", &myarg, &type_default))
134         return NULL;
135     if(type_default)
136         type = ACL_TYPE_DEFAULT;
137
138     if(PyString_Check(myarg)) {
139         char *filename = PyString_AS_STRING(myarg);
140         nret = acl_set_file(filename, type, self->ob_acl);
141     } else if((fd = PyObject_AsFileDescriptor(myarg)) != -1) {
142         nret = acl_set_fd(fd, self->ob_acl);
143     } else {
144         PyErr_SetString(PyExc_TypeError, "argument 1 must be string, int, or file-like object");
145         return 0;
146     }
147     if(nret == -1) {
148         return PyErr_SetFromErrno(PyExc_IOError);
149     }
150
151     /* Return the result */
152     Py_INCREF(Py_None);
153     return Py_None;
154 }
155
156 static char __valid_doc__[] = \
157 "Test the ACL for validity.\n" \
158 "\n" \
159 "This method tests the ACL to see if it is a valid ACL\n" \
160 "in terms of the filesystem. More precisely, it checks:\n" \
161 "A valid ACL contains exactly one entry with each of the ACL_USER_OBJ,\n" \
162 "ACL_GROUP_OBJ, and ACL_OTHER tag types. Entries with ACL_USER and\n" \
163 "ACL_GROUP tag types may appear zero or more times in an ACL. An ACL that\n" \
164 "contains entries of ACL_USER or ACL_GROUP tag types must contain exactly\n" \
165 "one entry of the ACL_MASK tag type. If an ACL contains no entries of\n" \
166 "ACL_USER or ACL_GROUP tag types, the ACL_MASK entry is optional.\n" \
167 "\n" \
168 "All user ID qualifiers must be unique among all entries of ACL_USER tag\n" \
169 "type, and all group IDs must be unique among all entries of ACL_GROUP tag\n" \
170 "type." \
171 ;
172 /* Checks the ACL for validity */
173 static PyObject* ACL_valid(PyObject* obj, PyObject* args) {
174     ACLObject *self = (ACLObject*) obj;
175
176     if(acl_valid(self->ob_acl) == -1) {
177         return PyErr_SetFromErrno(PyExc_IOError);
178     }
179
180     /* Return the result */
181     Py_INCREF(Py_None);
182     return Py_None;
183 }
184
185 #ifdef HAVE_LEVEL2
186
187 static PyObject* ACL_get_state(PyObject *obj, PyObject* args) {
188     ACLObject *self = (ACLObject*) obj;
189     PyObject *ret;
190     ssize_t size, nsize;
191     char *buf;
192
193     size = acl_size(self->ob_acl);
194     if(size == -1)
195         return PyErr_SetFromErrno(PyExc_IOError);
196
197     if((ret = PyString_FromStringAndSize(NULL, size)) == NULL)
198         return NULL;
199     buf = PyString_AsString(ret);
200     
201     if((nsize = acl_copy_ext(buf, self->ob_acl, size)) == -1) {
202         Py_DECREF(ret);
203         return PyErr_SetFromErrno(PyExc_IOError);
204     }
205     
206     return ret;
207 }
208
209 static PyObject* ACL_set_state(PyObject *obj, PyObject* args) {
210     ACLObject *self = (ACLObject*) obj;
211     const void *buf;
212     int bufsize;
213     acl_t ptr;
214
215     /* Parse the argument */
216     if (!PyArg_ParseTuple(args, "s#", &buf, &bufsize))
217         return NULL;
218
219     /* Try to import the external representation */
220     if((ptr = acl_copy_int(buf)) == NULL)
221         return PyErr_SetFromErrno(PyExc_IOError);
222         
223     /* Free the old acl. Should we ignore errors here? */
224     if(self->ob_acl != NULL) {
225         if(acl_free(self->ob_acl) == -1)
226             return PyErr_SetFromErrno(PyExc_IOError);
227     }
228
229     self->ob_acl = ptr;
230
231     /* Return the result */
232     Py_INCREF(Py_None);
233     return Py_None;
234 }
235
236 #endif
237
238 static char __acltype_doc__[] = \
239 "Type which represents a POSIX ACL\n" \
240 "\n" \
241 "Parameters:\n" \
242 "  Only one keword parameter should be provided:\n"
243 "  - file=\"...\", meaning create ACL representing\n"
244 "    the ACL of that file\n" \
245 "  - fd=<int>, meaning create ACL representing\n" \
246 "    the ACL of that file descriptor\n" \
247 "  - text=\"...\", meaning create ACL from a \n" \
248 "    textual description\n" \
249 "  - acl=<ACL instance>, meaning create a copy\n" \
250 "    of an existing ACL instance\n" \
251 ;
252
253 /* ACL type methods */
254 static PyMethodDef ACL_methods[] = {
255     {"applyto", ACL_applyto, METH_VARARGS, __applyto_doc__},
256     {"valid", ACL_valid, METH_NOARGS, __valid_doc__},
257 #ifdef HAVE_LEVEL2
258     {"__getstate__", ACL_get_state, METH_NOARGS, "Dumps the ACL to an external format."},
259     {"__setstate__", ACL_set_state, METH_VARARGS, "Loads the ACL from an external format."},
260 #endif
261     {NULL, NULL, 0, NULL}
262 };
263
264
265 /* The definition of the ACL Type */
266 static PyTypeObject ACLType = {
267     PyObject_HEAD_INIT(NULL)
268     0,
269     "posix1e.ACL",
270     sizeof(ACLObject),
271     0,
272     ACL_dealloc,        /* tp_dealloc */
273     0,                  /* tp_print */
274     0,                  /* tp_getattr */
275     0,                  /* tp_setattr */
276     0,                  /* tp_compare */
277     ACL_repr,           /* tp_repr */
278     0,                  /* tp_as_number */
279     0,                  /* tp_as_sequence */
280     0,                  /* tp_as_mapping */
281     0,                  /* tp_hash */
282     0,                  /* tp_call */
283     0,                  /* tp_str */
284     0,                  /* tp_getattro */
285     0,                  /* tp_setattro */
286     0,                  /* tp_as_buffer */
287     Py_TPFLAGS_DEFAULT, /* tp_flags */
288     __acltype_doc__,    /* tp_doc */
289     0,                  /* tp_traverse */
290     0,                  /* tp_clear */
291     0,                  /* tp_richcompare */
292     0,                  /* tp_weaklistoffset */
293     0,                  /* tp_iter */
294     0,                  /* tp_iternext */
295     ACL_methods,        /* tp_methods */
296     0,                  /* tp_members */
297     0,                  /* tp_getset */
298     0,                  /* tp_base */
299     0,                  /* tp_dict */
300     0,                  /* tp_descr_get */
301     0,                  /* tp_descr_set */
302     0,                  /* tp_dictoffset */
303     ACL_init,           /* tp_init */
304     0,                  /* tp_alloc */
305     ACL_new,            /* tp_new */
306 };
307
308 /* Module methods */
309
310 static char __deletedef_doc__[] = \
311 "Delete the default ACL from a directory.\n" \
312 "\n" \
313 "This function deletes the default ACL associated with \n" \
314 "a directory (the ACL which will be ANDed with the mode\n" \
315 "parameter to the open, creat functions.\n" \
316 "Parameters:\n" \
317 "  - a string representing the directory whose default ACL\n" \
318 "    should be deleted\n" \
319 ;
320
321 /* Deletes the default ACL from a directory */
322 static PyObject* aclmodule_delete_default(PyObject* obj, PyObject* args) {
323     char *filename;
324
325     /* Parse the arguments */
326     if (!PyArg_ParseTuple(args, "s", &filename))
327         return NULL;
328
329     if(acl_delete_def_file(filename) == -1) {
330         return PyErr_SetFromErrno(PyExc_IOError);
331     }
332
333     /* Return the result */
334     Py_INCREF(Py_None);
335     return Py_None;
336 }
337
338 /* The module methods */
339 static PyMethodDef aclmodule_methods[] = {
340     {"delete_default", aclmodule_delete_default, METH_VARARGS, __deletedef_doc__},
341     {NULL, NULL, 0, NULL}
342 };
343
344 static char __posix1e_doc__[] = \
345 "POSIX.1e ACLs manipulation\n" \
346 "\n" \
347 "This module provides support for manipulating POSIX.1e ACLS\n" \
348 "\n" \
349 "Depending on the operating system support for POSIX.1e, \n" \
350 "the ACL type will have more or less capabilities:\n" \
351 "  - level 1, only basic support, you can create\n" \
352 "    ACLs from files and text descriptions;\n" \
353 "    once created, the type is immutable\n" \
354 "  - level 2, complete support, you can alter\n"\
355 "    the ACL once it is created\n" \
356 "\n" \
357 "Also, in level 2, more types will be available, corresponding\n" \
358 "to acl_entry_t, acl_permset_t, etc.\n" \
359 "\n" \
360 "Example:\n" \
361 ">>> import posix1e\n" \
362 ">>> acl1 = posix1e.ACL(file=\"file.txt\") \n" \
363 ">>> print acl1\n" \
364 "user::rw-\n" \
365 "group::rw-\n" \
366 "other::r--\n" \
367 "\n" \
368 ">>> b = posix1e.ACL(text=\"u::rx,g::-,o::-\")\n" \
369 ">>> print b\n" \
370 "user::r-x\n" \
371 "group::---\n" \
372 "other::---\n" \
373 "\n" \
374 ">>> b.applyto(\"file.txt\")\n" \
375 ">>> print posix1e.ACL(file=\"file.txt\")\n" \
376 "user::r-x\n" \
377 "group::---\n" \
378 "other::---\n" \
379 "\n" \
380 ">>>\n" \
381 ;
382
383 DL_EXPORT(void) initposix1e(void) {
384     PyObject *m, *d;
385
386     ACLType.ob_type = &PyType_Type;
387
388     if(PyType_Ready(&ACLType) < 0)
389         return;
390
391     m = Py_InitModule3("posix1e", aclmodule_methods, __posix1e_doc__);
392
393     d = PyModule_GetDict(m);
394     if (d == NULL)
395         return;
396
397     Py_INCREF(&ACLType);
398     if (PyDict_SetItemString(d, "ACL",
399                              (PyObject *) &ACLType) < 0)
400         return;
401 }