]> git.k1024.org Git - pyxattr.git/blob - xattr.c
First iversion get_all
[pyxattr.git] / xattr.c
1 #include <Python.h>
2 #include <attr/xattr.h>
3
4 /** Converts from a string, file or int argument to what we need. */
5 static int convertObj(PyObject *myobj, int *ishandle, int *filehandle,
6                       char **filename) {
7     if(PyString_Check(myobj)) {
8         *ishandle = 0;
9         *filename = PyString_AS_STRING(myobj);
10     } else if((*filehandle = PyObject_AsFileDescriptor(myobj)) != -1) {
11         *ishandle = 1;
12     } else {
13         PyErr_SetString(PyExc_TypeError, "argument 1 must be string or int");
14         return 0;
15     }
16     return 1;
17 }
18
19 /* Checks if an attribute name matches an optional namespace */
20 static int matches_ns(const char *name, const char *ns) {
21     size_t ns_size;
22     if (ns == NULL)
23         return 1;
24     ns_size = strlen(ns);
25
26     if (strlen(name) > ns_size && !strncmp(name, ns, ns_size) &&
27         name[ns_size] == '.')
28         return 1;
29     return 0;
30 }
31
32 /* Wrapper for getxattr */
33 static char __pygetxattr_doc__[] =
34     "Get the value of a given extended attribute.\n"
35     "\n"
36     "Parameters:\n"
37     "  - a string representing filename, or a file-like object,\n"
38     "    or a file descriptor; this represents the file on \n"
39     "    which to act\n"
40     "  - a string, representing the attribute whose value to retrieve;\n"
41     "    usually in form of system.posix_acl or user.mime_type\n"
42     "  - (optional) a boolean value (defaults to false), which, if\n"
43     "    the file name given is a symbolic link, makes the\n"
44     "    function operate on the symbolic link itself instead\n"
45     "    of its target;"
46     ;
47
48 static PyObject *
49 pygetxattr(PyObject *self, PyObject *args)
50 {
51     PyObject *myarg;
52     char *file = NULL;
53     int filedes = -1, ishandle, dolink=0;
54     char *attrname;
55     char *buf;
56     int nalloc, nret;
57     PyObject *res;
58
59     /* Parse the arguments */
60     if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &dolink))
61         return NULL;
62     if(!convertObj(myarg, &ishandle, &filedes, &file))
63         return NULL;
64
65     /* Find out the needed size of the buffer */
66     nalloc = ishandle ?
67         fgetxattr(filedes, attrname, NULL, 0) :
68         dolink ?
69         lgetxattr(file, attrname, NULL, 0) :
70         getxattr(file, attrname, NULL, 0);
71     if(nalloc == -1) {
72         return PyErr_SetFromErrno(PyExc_IOError);
73     }
74
75     /* Try to allocate the memory, using Python's allocator */
76     if((buf = PyMem_Malloc(nalloc)) == NULL) {
77         PyErr_NoMemory();
78         return NULL;
79     }
80
81     /* Now retrieve the attribute value */
82     nret = ishandle ?
83         fgetxattr(filedes, attrname, buf, nalloc) :
84         dolink ?
85         lgetxattr(file, attrname, buf, nalloc) :
86         getxattr(file, attrname, buf, nalloc);
87     if(nret == -1) {
88         PyMem_Free(buf);
89         return PyErr_SetFromErrno(PyExc_IOError);
90     }
91
92     /* Create the string which will hold the result */
93     res = PyString_FromStringAndSize(buf, nret);
94
95     /* Free the buffer, now it is no longer needed */
96     PyMem_Free(buf);
97
98     /* Return the result */
99     return res;
100 }
101
102 /* Wrapper for getxattr */
103 static char __get_all_doc__[] =
104     "Get all the extended attributes of an item.\n"
105     "\n"
106     "Parameters:\n"
107     "  - a string representing filename, or a file-like object,\n"
108     "    or a file descriptor; this represents the file on \n"
109     "    which to act\n"
110     "  - (optional) a boolean value (defaults to false), which, if\n"
111     "    the file name given is a symbolic link, makes the\n"
112     "    function operate on the symbolic link itself instead\n"
113     "    of its target;"
114     ;
115
116 static PyObject *
117 get_all(PyObject *self, PyObject *args, PyObject *keywds)
118 {
119     PyObject *myarg;
120     char *file = NULL;
121     int filedes = -1, ishandle, dolink=0;
122     char *ns = NULL;
123     char *buf_list, *buf_val;
124     char *s;
125     int nalloc, nlist, nval, nattrs;
126     PyObject *mylist;
127     static char *kwlist[] = {"item", "noderef", "namespace", NULL};
128
129     /* Parse the arguments */
130     if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
131                                      &myarg, &dolink, &ns))
132         return NULL;
133     if(!convertObj(myarg, &ishandle, &filedes, &file))
134         return NULL;
135
136     /* Compute first the list of attributes */
137
138     /* Find out the needed size of the buffer for the attribute list */
139     nalloc = ishandle ?
140         flistxattr(filedes, NULL, 0) :
141         dolink ?
142         llistxattr(file, NULL, 0) :
143         listxattr(file, NULL, 0);
144
145     if(nalloc == -1) {
146         return PyErr_SetFromErrno(PyExc_IOError);
147     }
148
149     /* Try to allocate the memory, using Python's allocator */
150     if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
151         PyErr_NoMemory();
152         return NULL;
153     }
154
155     /* Now retrieve the list of attributes */
156     nlist = ishandle ?
157         flistxattr(filedes, buf_list, nalloc) :
158         dolink ?
159         llistxattr(file, buf_list, nalloc) :
160         listxattr(file, buf_list, nalloc);
161
162     if(nlist == -1) {
163         return PyErr_SetFromErrno(PyExc_IOError);
164     }
165
166     /* Compute the number of attributes in the list */
167     for(s = buf_list, nattrs = 0; (s - buf_list) < nlist; s += strlen(s) + 1) {
168         if(matches_ns(s, ns))
169             nattrs++;
170     }
171
172     /* Create the list which will hold the result */
173     mylist = PyList_New(nattrs);
174
175     /* Create and insert the attributes as strings in the list */
176     for(s = buf_list, nattrs = 0; s - buf_list < nlist; s += strlen(s) + 1) {
177         PyObject *my_tuple;
178
179         if(!matches_ns(s, ns))
180             continue;
181
182         /* Find out the needed size of the value buffer */
183         nalloc = ishandle ?
184             fgetxattr(filedes, s, NULL, 0) :
185             dolink ?
186             lgetxattr(file, s, NULL, 0) :
187             getxattr(file, s, NULL, 0);
188         if(nalloc == -1) {
189             PyMem_Free(buf_list);
190             return PyErr_SetFromErrno(PyExc_IOError);
191         }
192
193         /* Try to allocate the memory, using Python's allocator */
194         if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
195             PyMem_Free(buf_list);
196             PyErr_NoMemory();
197             return NULL;
198         }
199
200         /* Now retrieve the attribute value */
201         nval = ishandle ?
202             fgetxattr(filedes, s, buf_val, nalloc) :
203             dolink ?
204             lgetxattr(file, s, buf_val, nalloc) :
205             getxattr(file, s, buf_val, nalloc);
206
207         if(nval == -1) {
208             PyMem_Free(buf_list);
209             PyMem_Free(buf_val);
210             return PyErr_SetFromErrno(PyExc_IOError);
211         }
212         my_tuple = Py_BuildValue("ss#", s, buf_val, nval);
213
214         /* Free the buffer, now it is no longer needed */
215         PyMem_Free(buf_val);
216
217         PyList_SET_ITEM(mylist, nattrs, my_tuple);
218         nattrs++;
219     }
220
221     /* Free the buffer, now it is no longer needed */
222     PyMem_Free(buf_list);
223
224     /* Return the result */
225     return mylist;
226
227 }
228
229
230 static char __pysetxattr_doc__[] =
231     "Set the value of a given extended attribute.\n"
232     "Be carefull in case you want to set attributes on symbolic\n"
233     "links, you have to use all the 5 parameters; use 0 for the \n"
234     "flags value if you want the default behavior (create or "
235     "replace)\n"
236     "\n"
237     "Parameters:\n"
238     "  - a string representing filename, or a file-like object,\n"
239     "    or a file descriptor; this represents the file on \n"
240     "    which to act\n"
241     "  - a string, representing the attribute whose value to set;\n"
242     "    usually in form of system.posix_acl or user.mime_type\n"
243     "  - a string, possibly with embedded NULLs; note that there\n"
244     "    are restrictions regarding the size of the value, for\n"
245     "    example, for ext2/ext3, maximum size is the block size\n"
246     "  - (optional) flags; if 0 or ommited the attribute will be \n"
247     "    created or replaced; if XATTR_CREATE, the attribute \n"
248     "    will be created, giving an error if it already exists;\n"
249     "    of XATTR_REPLACE, the attribute will be replaced,\n"
250     "    giving an error if it doesn't exists;\n"
251     "  - (optional) a boolean value (defaults to false), which, if\n"
252     "    the file name given is a symbolic link, makes the\n"
253     "    function operate on the symbolic link itself instead\n"
254     "    of its target;"
255     ;
256
257 /* Wrapper for setxattr */
258 static PyObject *
259 pysetxattr(PyObject *self, PyObject *args)
260 {
261     PyObject *myarg;
262     char *file;
263     int ishandle, filedes, dolink=0;
264     char *attrname;
265     char *buf;
266     int bufsize, nret;
267     int flags = 0;
268
269     /* Parse the arguments */
270     if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname,
271                           &buf, &bufsize, &flags, &dolink))
272         return NULL;
273     if(!convertObj(myarg, &ishandle, &filedes, &file))
274         return NULL;
275
276     /* Set the attribute's value */
277     nret = ishandle ?
278         fsetxattr(filedes, attrname, buf, bufsize, flags) :
279         dolink ?
280         lsetxattr(file, attrname, buf, bufsize, flags) :
281         setxattr(file, attrname, buf, bufsize, flags);
282
283     if(nret == -1) {
284         return PyErr_SetFromErrno(PyExc_IOError);
285     }
286
287     /* Return the result */
288     Py_INCREF(Py_None);
289     return Py_None;
290 }
291
292 static char __pyremovexattr_doc__[] =
293     "Remove an attribute from a file\n"
294     "\n"
295     "Parameters:\n"
296     "  - a string representing filename, or a file-like object,\n"
297     "    or a file descriptor; this represents the file on \n"
298     "    which to act\n"
299     "  - a string, representing the attribute to be removed;\n"
300     "    usually in form of system.posix_acl or user.mime_type\n"
301     "  - (optional) a boolean value (defaults to false), which, if\n"
302     "    the file name given is a symbolic link, makes the\n"
303     "    function operate on the symbolic link itself instead\n"
304     "    of its target;\n"
305     ;
306
307 /* Wrapper for removexattr */
308 static PyObject *
309 pyremovexattr(PyObject *self, PyObject *args)
310 {
311     PyObject *myarg;
312     char *file;
313     int ishandle, filedes, dolink=0;
314     char *attrname;
315     int nret;
316
317     /* Parse the arguments */
318     if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &dolink))
319         return NULL;
320
321     if(!convertObj(myarg, &ishandle, &filedes, &file))
322         return NULL;
323
324     /* Remove the attribute */
325     nret = ishandle ?
326         fremovexattr(filedes, attrname) :
327         dolink ?
328         lremovexattr(file, attrname) :
329         removexattr(file, attrname);
330
331     if(nret == -1)
332         return PyErr_SetFromErrno(PyExc_IOError);
333
334     /* Return the result */
335     Py_INCREF(Py_None);
336     return Py_None;
337 }
338
339 static char __pylistxattr_doc__[] =
340     "Return the list of attribute names for a file\n"
341     "\n"
342     "Parameters:\n"
343     "  - a string representing filename, or a file-like object,\n"
344     "    or a file descriptor; this represents the file to \n"
345     "    be queried\n"
346     "  - (optional) a boolean value (defaults to false), which, if\n"
347     "    the file name given is a symbolic link, makes the\n"
348     "    function operate on the symbolic link itself instead\n"
349     "    of its target;\n"
350     ;
351
352 /* Wrapper for listxattr */
353 static PyObject *
354 pylistxattr(PyObject *self, PyObject *args)
355 {
356     char *file = NULL;
357     int filedes = -1;
358     char *buf;
359     int ishandle, dolink=0;
360     int nalloc, nret;
361     PyObject *myarg;
362     PyObject *mylist;
363     int nattrs;
364     char *s;
365
366     /* Parse the arguments */
367     if (!PyArg_ParseTuple(args, "O|i", &myarg, &dolink))
368         return NULL;
369     if(!convertObj(myarg, &ishandle, &filedes, &file))
370         return NULL;
371
372     /* Find out the needed size of the buffer */
373     nalloc = ishandle ?
374         flistxattr(filedes, NULL, 0) :
375         dolink ?
376         llistxattr(file, NULL, 0) :
377         listxattr(file, NULL, 0);
378
379     if(nalloc == -1) {
380         return PyErr_SetFromErrno(PyExc_IOError);
381     }
382
383     /* Try to allocate the memory, using Python's allocator */
384     if((buf = PyMem_Malloc(nalloc)) == NULL) {
385         PyErr_NoMemory();
386         return NULL;
387     }
388
389     /* Now retrieve the list of attributes */
390     nret = ishandle ?
391         flistxattr(filedes, buf, nalloc) :
392         dolink ?
393         llistxattr(file, buf, nalloc) :
394         listxattr(file, buf, nalloc);
395
396     if(nret == -1) {
397         return PyErr_SetFromErrno(PyExc_IOError);
398     }
399
400     /* Compute the number of attributes in the list */
401     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
402         nattrs++;
403     }
404
405     /* Create the list which will hold the result */
406     mylist = PyList_New(nattrs);
407
408     /* Create and insert the attributes as strings in the list */
409     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
410         PyList_SET_ITEM(mylist, nattrs, PyString_FromString(s));
411         nattrs++;
412     }
413
414     /* Free the buffer, now it is no longer needed */
415     PyMem_Free(buf);
416
417     /* Return the result */
418     return mylist;
419 }
420
421 static PyMethodDef xattr_methods[] = {
422     {"getxattr",  pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
423     {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
424      __get_all_doc__ },
425     {"setxattr",  pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
426     {"removexattr",  pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
427     {"listxattr",  pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
428     {NULL, NULL, 0, NULL}        /* Sentinel */
429 };
430
431 static char __xattr_doc__[] = \
432     "Access extended filesystem attributes\n"
433     "\n"
434     "This module gives access to the extended attributes present\n"
435     "in some operating systems/filesystems. You can list attributes,\n"
436     "get, set and remove them.\n"
437     "The last and optional parameter for all functions is a boolean \n"
438     "value which enables the 'l-' version of the functions - acting\n"
439     "on symbolic links and not their destination.\n"
440     "\n"
441     "Example: \n\n"
442     "  >>> import xattr\n"
443     "  >>> xattr.listxattr(\"file.txt\")\n"
444     "  ['user.mime_type']\n"
445     "  >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
446     "  'text/plain'\n"
447     "  >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
448     "\"Simple text file\")\n"
449     "  >>> xattr.listxattr(\"file.txt\")\n"
450     "  ['user.mime_type', 'user.comment']\n"
451     "  >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
452     ""
453     ;
454
455 void
456 initxattr(void)
457 {
458     PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
459
460     PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
461     PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
462
463     /* namespace constants */
464     PyModule_AddStringConstant(m, "NS_SECURITY", "security");
465     PyModule_AddStringConstant(m, "NS_SYSTEM", "system");
466     PyModule_AddStringConstant(m, "NS_TRUSTED", "trusted");
467     PyModule_AddStringConstant(m, "NS_USER", "user");
468
469 }