]> git.k1024.org Git - pyxattr.git/blob - xattr.c
Docstring updates
[pyxattr.git] / xattr.c
1 #include <Python.h>
2 #include <attr/xattr.h>
3 #include <stdio.h>
4
5 /* the estimated (startup) attribute buffer size in
6    multi-operations */
7 #define ESTIMATE_ATTR_SIZE 256
8
9 typedef enum {T_FD, T_PATH, T_LINK} target_e;
10
11 typedef struct {
12     target_e type;
13     union {
14         const char *name;
15         int fd;
16     };
17 } target_t;
18
19 /** Converts from a string, file or int argument to what we need. */
20 static int convertObj(PyObject *myobj, target_t *tgt, int nofollow) {
21     int fd;
22     if(PyString_Check(myobj)) {
23         tgt->type = nofollow ? T_LINK : T_PATH;
24         tgt->name = PyString_AS_STRING(myobj);
25     } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
26         tgt->type = T_FD;
27         tgt->fd = fd;
28     } else {
29         PyErr_SetString(PyExc_TypeError, "argument must be string or int");
30         return 0;
31     }
32     return 1;
33 }
34
35 /* Combine a namespace string and an attribute name into a
36    fully-qualified name */
37 static const char* merge_ns(const char *ns, const char *name, char **buf) {
38     if(ns != NULL) {
39         int cnt;
40         size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
41         if((*buf = PyMem_Malloc(new_size)) == NULL) {
42             PyErr_NoMemory();
43             return NULL;
44         }
45         cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
46         if(cnt > new_size || cnt < 0) {
47             PyErr_SetString(PyExc_ValueError,
48                             "can't format the attribute name");
49             PyMem_Free(*buf);
50             return NULL;
51         }
52         return *buf;
53     } else {
54         *buf = NULL;
55         return name;
56     }
57 }
58
59 static ssize_t _list_obj(target_t *tgt, char *list, size_t size) {
60     if(tgt->type == T_FD)
61         return flistxattr(tgt->fd, list, size);
62     else if (tgt->type == T_LINK)
63         return llistxattr(tgt->name, list, size);
64     else
65         return listxattr(tgt->name, list, size);
66 }
67
68 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
69                         size_t size) {
70     if(tgt->type == T_FD)
71         return fgetxattr(tgt->fd, name, value, size);
72     else if (tgt->type == T_LINK)
73         return lgetxattr(tgt->name, name, value, size);
74     else
75         return getxattr(tgt->name, name, value, size);
76 }
77
78 static ssize_t _set_obj(target_t *tgt, const char *name,
79                         const void *value, size_t size,
80                         int flags) {
81     if(tgt->type == T_FD)
82         return fsetxattr(tgt->fd, name, value, size, flags);
83     else if (tgt->type == T_LINK)
84         return lsetxattr(tgt->name, name, value, size, flags);
85     else
86         return setxattr(tgt->name, name, value, size, flags);
87 }
88
89 static ssize_t _remove_obj(target_t *tgt, const char *name) {
90     if(tgt->type == T_FD)
91         return fremovexattr(tgt->fd, name);
92     else if (tgt->type == T_LINK)
93         return lremovexattr(tgt->name, name);
94     else
95         return removexattr(tgt->name, name);
96 }
97
98 /* Checks if an attribute name matches an optional namespace */
99 static int matches_ns(const char *name, const char *ns) {
100     size_t ns_size;
101     if (ns == NULL)
102         return 1;
103     ns_size = strlen(ns);
104
105     if (strlen(name) > ns_size && !strncmp(name, ns, ns_size) &&
106         name[ns_size] == '.')
107         return 1;
108     return 0;
109 }
110
111 /* Wrapper for getxattr */
112 static char __pygetxattr_doc__[] =
113     "Get the value of a given extended attribute (deprecated).\n"
114     "\n"
115     "Parameters:\n"
116     "  - a string representing filename, or a file-like object,\n"
117     "    or a file descriptor; this represents the file on \n"
118     "    which to act\n"
119     "  - a string, representing the attribute whose value to retrieve;\n"
120     "    usually in form of system.posix_acl or user.mime_type\n"
121     "  - (optional) a boolean value (defaults to false), which, if\n"
122     "    the file name given is a symbolic link, makes the\n"
123     "    function operate on the symbolic link itself instead\n"
124     "    of its target;\n"
125     "@deprecated: this function has been deprecated by the L{get} function\n"
126     ;
127
128 static PyObject *
129 pygetxattr(PyObject *self, PyObject *args)
130 {
131     PyObject *myarg;
132     target_t tgt;
133     int nofollow=0;
134     char *attrname;
135     char *buf;
136     int nalloc, nret;
137     PyObject *res;
138
139     /* Parse the arguments */
140     if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
141         return NULL;
142     if(!convertObj(myarg, &tgt, nofollow))
143         return NULL;
144
145     /* Find out the needed size of the buffer */
146     if((nalloc = _get_obj(&tgt, attrname, NULL, 0)) == -1) {
147         return PyErr_SetFromErrno(PyExc_IOError);
148     }
149
150     /* Try to allocate the memory, using Python's allocator */
151     if((buf = PyMem_Malloc(nalloc)) == NULL) {
152         PyErr_NoMemory();
153         return NULL;
154     }
155
156     /* Now retrieve the attribute value */
157     if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) {
158         PyMem_Free(buf);
159         return PyErr_SetFromErrno(PyExc_IOError);
160     }
161
162     /* Create the string which will hold the result */
163     res = PyString_FromStringAndSize(buf, nret);
164
165     /* Free the buffer, now it is no longer needed */
166     PyMem_Free(buf);
167
168     /* Return the result */
169     return res;
170 }
171
172 /* Wrapper for getxattr */
173 static char __get_doc__[] =
174     "Get the value of a given extended attribute.\n"
175     "\n"
176     "@param item: the item to query; either a string representing the"
177     " filename, or a file-like object, or a file descriptor\n"
178     "@param name: the attribute whose value to set; usually in form of"
179     " system.posix_acl or user.mime_type\n"
180     "@type name: string\n"
181     "@param nofollow: if given and True, and the function is passed a"
182     " filename that points to a symlink, the function will act on the symlink"
183     " itself instead of its target\n"
184     "@type nofollow: boolean\n"
185     "@param namespace: if given, the attribute must not contain the namespace"
186     " itself, but instead the namespace will be taken from this parameter\n"
187     "@type namespace: string\n"
188     "@return: the value of the extended attribute (can contain NULLs)\n"
189     "@rtype: string\n"
190     "@raise EnvironmentError: system errors will raise an exception\n"
191     "@since: 0.4\n"
192     ;
193
194 static PyObject *
195 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
196 {
197     PyObject *myarg;
198     target_t tgt;
199     int nofollow=0;
200     char *attrname, *namebuf;
201     const char *fullname;
202     char *buf;
203     char *ns = NULL;
204     int nalloc, nret;
205     PyObject *res;
206     static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
207
208     /* Parse the arguments */
209     if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist,
210                                      &myarg, &attrname, &nofollow, &ns))
211         return NULL;
212     if(!convertObj(myarg, &tgt, nofollow))
213         return NULL;
214
215     fullname = merge_ns(ns, attrname, &namebuf);
216
217     /* Find out the needed size of the buffer */
218     if((nalloc = _get_obj(&tgt, fullname, NULL, 0)) == -1) {
219         return PyErr_SetFromErrno(PyExc_IOError);
220     }
221
222     /* Try to allocate the memory, using Python's allocator */
223     if((buf = PyMem_Malloc(nalloc)) == NULL) {
224         PyMem_Free(namebuf);
225         PyErr_NoMemory();
226         return NULL;
227     }
228
229     /* Now retrieve the attribute value */
230     if((nret = _get_obj(&tgt, fullname, buf, nalloc)) == -1) {
231         PyMem_Free(buf);
232         PyMem_Free(namebuf);
233         return PyErr_SetFromErrno(PyExc_IOError);
234     }
235
236     /* Create the string which will hold the result */
237     res = PyString_FromStringAndSize(buf, nret);
238
239     /* Free the buffers, they are no longer needed */
240     PyMem_Free(namebuf);
241     PyMem_Free(buf);
242
243     /* Return the result */
244     return res;
245 }
246
247 /* Wrapper for getxattr */
248 static char __get_all_doc__[] =
249     "Get all the extended attributes of an item.\n"
250     "\n"
251     "This function performs a bulk-get of all extended attribute names\n"
252     "and the corresponding value.\n"
253     "Example:\n"
254     "  >>> xattr.get_all('/path/to/file')\n"
255     "  [('user.mime-type', 'plain/text'), ('user.comment', 'test'),"
256     " ('system.posix_acl_access', '\\x02\\x00...')]\n"
257     "  >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
258     "  [('user.mime-type', 'plain/text'), ('user.comment', 'test')]\n"
259     "\n"
260     "@param item: the item to query; either a string representing the"
261     " filename, or a file-like object, or a file descriptor\n"
262     "@keyword namespace: an optional namespace for filtering the"
263     " attributes; for example, querying all user attributes can be"
264     " accomplished by passing namespace=L{NS_USER}\n"
265     "@type namespace: string\n"
266     "@keyword nofollow: if passed and true, if the target file is a symbolic"
267     " link,"
268     " the attributes for the link itself will be returned, instead of the"
269     " attributes of the target\n"
270     "@type nofollow: boolean\n"
271     "@return: list of tuples (name, value)\n"
272     "@rtype: list\n"
273     "@raise EnvironmentError: system errors will raise an exception\n"
274     "@since: 0.4\n"
275     ;
276
277 static PyObject *
278 get_all(PyObject *self, PyObject *args, PyObject *keywds)
279 {
280     PyObject *myarg;
281     int dolink=0;
282     char *ns = NULL;
283     char *buf_list, *buf_val;
284     char *s;
285     size_t nalloc, nlist, nval;
286     PyObject *mylist;
287     target_t tgt;
288     static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
289
290     /* Parse the arguments */
291     if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
292                                      &myarg, &dolink, &ns))
293         return NULL;
294     if(!convertObj(myarg, &tgt, dolink))
295         return NULL;
296
297     /* Compute first the list of attributes */
298
299     /* Find out the needed size of the buffer for the attribute list */
300     nalloc = _list_obj(&tgt, NULL, 0);
301
302     if(nalloc == -1) {
303         return PyErr_SetFromErrno(PyExc_IOError);
304     }
305
306     /* Try to allocate the memory, using Python's allocator */
307     if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
308         PyErr_NoMemory();
309         return NULL;
310     }
311
312     /* Now retrieve the list of attributes */
313     nlist = _list_obj(&tgt, buf_list, nalloc);
314
315     if(nlist == -1) {
316         PyErr_SetFromErrno(PyExc_IOError);
317         goto free_buf_list;
318     }
319
320     /* Create the list which will hold the result */
321     mylist = PyList_New(0);
322     nalloc = ESTIMATE_ATTR_SIZE;
323     if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
324         PyErr_NoMemory();
325         goto free_list;
326     }
327
328     /* Create and insert the attributes as strings in the list */
329     for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
330         PyObject *my_tuple;
331         int missing;
332
333         if(!matches_ns(s, ns))
334             continue;
335         /* Now retrieve the attribute value */
336         missing = 0;
337         while(1) {
338             nval = _get_obj(&tgt, s, buf_val, nalloc);
339
340             if(nval == -1) {
341                 if(errno == ERANGE) {
342                     nval = _get_obj(&tgt, s, NULL, 0);
343                     if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL)
344                         goto free_list;
345                     nalloc = nval;
346                     continue;
347                 } else if(errno == ENODATA || errno == ENOATTR) {
348                     /* this attribute has gone away since we queried
349                        the attribute list */
350                     missing = 1;
351                     break;
352                 }
353                 goto exit_errno;
354             }
355             break;
356         }
357         if(missing)
358             continue;
359         my_tuple = Py_BuildValue("ss#", s, buf_val, nval);
360
361         PyList_Append(mylist, my_tuple);
362         Py_DECREF(my_tuple);
363     }
364
365     /* Free the buffers, now they are no longer needed */
366     PyMem_Free(buf_val);
367     PyMem_Free(buf_list);
368
369     /* Return the result */
370     return mylist;
371  exit_errno:
372     PyErr_SetFromErrno(PyExc_IOError);
373     PyMem_Free(buf_val);
374  free_list:
375     Py_DECREF(mylist);
376  free_buf_list:
377     PyMem_Free(buf_list);
378     return NULL;
379 }
380
381
382 static char __pysetxattr_doc__[] =
383     "Set the value of a given extended attribute (deprecated).\n"
384     "Be carefull in case you want to set attributes on symbolic\n"
385     "links, you have to use all the 5 parameters; use 0 for the \n"
386     "flags value if you want the default behavior (create or "
387     "replace)\n"
388     "\n"
389     "Parameters:\n"
390     "  - a string representing filename, or a file-like object,\n"
391     "    or a file descriptor; this represents the file on \n"
392     "    which to act\n"
393     "  - a string, representing the attribute whose value to set;\n"
394     "    usually in form of system.posix_acl or user.mime_type\n"
395     "  - a string, possibly with embedded NULLs; note that there\n"
396     "    are restrictions regarding the size of the value, for\n"
397     "    example, for ext2/ext3, maximum size is the block size\n"
398     "  - (optional) flags; if 0 or ommited the attribute will be \n"
399     "    created or replaced; if XATTR_CREATE, the attribute \n"
400     "    will be created, giving an error if it already exists;\n"
401     "    of XATTR_REPLACE, the attribute will be replaced,\n"
402     "    giving an error if it doesn't exists;\n"
403     "  - (optional) a boolean value (defaults to false), which, if\n"
404     "    the file name given is a symbolic link, makes the\n"
405     "    function operate on the symbolic link itself instead\n"
406     "    of its target;\n"
407     "@deprecated: this function has been deprecated by the L{set} function\n"
408     ;
409
410 /* Wrapper for setxattr */
411 static PyObject *
412 pysetxattr(PyObject *self, PyObject *args)
413 {
414     PyObject *myarg;
415     int nofollow=0;
416     char *attrname;
417     char *buf;
418     int bufsize, nret;
419     int flags = 0;
420     target_t tgt;
421
422     /* Parse the arguments */
423     if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname,
424                           &buf, &bufsize, &flags, &nofollow))
425         return NULL;
426     if(!convertObj(myarg, &tgt, nofollow))
427         return NULL;
428
429     /* Set the attribute's value */
430     if((nret = _set_obj(&tgt, attrname, buf, bufsize, flags)) == -1) {
431         return PyErr_SetFromErrno(PyExc_IOError);
432     }
433
434     /* Return the result */
435     Py_RETURN_NONE;
436 }
437
438 static char __set_doc__[] =
439     "Set the value of a given extended attribute.\n"
440     "\n"
441     "@param item: the item to query; either a string representing the"
442     " filename, or a file-like object, or a file descriptor\n"
443     "@param name: the attribute whose value to set; usually in form of"
444     " system.posix_acl or user.mime_type\n"
445     "@type name: string\n"
446     "@param value: a string, possibly with embedded NULLs; note that there"
447     " are restrictions regarding the size of the value, for"
448     " example, for ext2/ext3, maximum size is the block size\n"
449     "@type value: string\n"
450     "@param flags: if 0 or ommited the attribute will be"
451     " created or replaced; if L{XATTR_CREATE}, the attribute"
452     " will be created, giving an error if it already exists;"
453     " if L{XATTR_REPLACE}, the attribute will be replaced,"
454     " giving an error if it doesn't exists;\n"
455     "@type flags: integer\n"
456     "@param nofollow: if given and True, and the function is passed a"
457     " filename that points to a symlink, the function will act on the symlink"
458     " itself instead of its target\n"
459     "@type nofollow: boolean\n"
460     "@param namespace: if given, the attribute must not contain the namespace"
461     " itself, but instead the namespace will be taken from this parameter\n"
462     "@type namespace: string\n"
463     "@rtype: None\n"
464     "@raise EnvironmentError: system errors will raise an exception\n"
465     "@since: 0.4\n"
466     ;
467
468 /* Wrapper for setxattr */
469 static PyObject *
470 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
471 {
472     PyObject *myarg;
473     int nofollow=0;
474     char *attrname;
475     char *buf;
476     int bufsize, nret;
477     int flags = 0;
478     target_t tgt;
479     char *ns = NULL;
480     char *newname;
481     const char *full_name;
482     static char *kwlist[] = {"item", "name", "value", "flags",
483                              "nofollow", "namespace", NULL};
484
485     /* Parse the arguments */
486     if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oss#|iiz", kwlist,
487                                      &myarg, &attrname,
488                                      &buf, &bufsize, &flags, &nofollow, &ns))
489         return NULL;
490     if(!convertObj(myarg, &tgt, nofollow))
491         return NULL;
492
493     full_name = merge_ns(ns, attrname, &newname);
494     /* Set the attribute's value */
495     nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
496     if(newname != NULL)
497         PyMem_Free(newname);
498     if(nret == -1) {
499         return PyErr_SetFromErrno(PyExc_IOError);
500     }
501
502     /* Return the result */
503     Py_RETURN_NONE;
504 }
505
506
507 static char __pyremovexattr_doc__[] =
508     "Remove an attribute from a file (deprecated)\n"
509     "\n"
510     "Parameters:\n"
511     "  - a string representing filename, or a file-like object,\n"
512     "    or a file descriptor; this represents the file on \n"
513     "    which to act\n"
514     "  - a string, representing the attribute to be removed;\n"
515     "    usually in form of system.posix_acl or user.mime_type\n"
516     "  - (optional) a boolean value (defaults to false), which, if\n"
517     "    the file name given is a symbolic link, makes the\n"
518     "    function operate on the symbolic link itself instead\n"
519     "    of its target;\n"
520     "@deprecated: this function has been deprecated by the L{remove}"
521     " function\n"
522     ;
523
524 /* Wrapper for removexattr */
525 static PyObject *
526 pyremovexattr(PyObject *self, PyObject *args)
527 {
528     PyObject *myarg;
529     int nofollow=0;
530     char *attrname;
531     int nret;
532     target_t tgt;
533
534     /* Parse the arguments */
535     if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
536         return NULL;
537
538     if(!convertObj(myarg, &tgt, nofollow))
539         return NULL;
540
541     /* Remove the attribute */
542     if((nret = _remove_obj(&tgt, attrname)) == -1) {
543         return PyErr_SetFromErrno(PyExc_IOError);
544     }
545
546     /* Return the result */
547     Py_RETURN_NONE;
548 }
549
550 static char __remove_doc__[] =
551     "Remove an attribute from a file\n"
552     "\n"
553     "@param item: the item to query; either a string representing the"
554     " filename, or a file-like object, or a file descriptor\n"
555     "@param name: the attribute whose value to set; usually in form of"
556     " system.posix_acl or user.mime_type\n"
557     "@type name: string\n"
558     "@param nofollow: if given and True, and the function is passed a"
559     " filename that points to a symlink, the function will act on the symlink"
560     " itself instead of its target\n"
561     "@type nofollow: boolean\n"
562     "@param namespace: if given, the attribute must not contain the namespace"
563     " itself, but instead the namespace will be taken from this parameter\n"
564     "@type namespace: string\n"
565     "@since: 0.4\n"
566     "@rtype: None\n"
567     "@raise EnvironmentError: system errors will raise an exception\n"
568     ;
569
570 /* Wrapper for removexattr */
571 static PyObject *
572 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
573 {
574     PyObject *myarg;
575     int nofollow=0;
576     char *attrname, *name_buf;
577     char *ns = NULL;
578     const char *full_name;
579     int nret;
580     target_t tgt;
581     static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
582
583     /* Parse the arguments */
584     if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist,
585                                      &myarg, &attrname, &nofollow, &ns))
586         return NULL;
587
588     if(!convertObj(myarg, &tgt, nofollow))
589         return NULL;
590     full_name = merge_ns(ns, attrname, &name_buf);
591     if(full_name == NULL)
592         return NULL;
593
594     /* Remove the attribute */
595     nret = _remove_obj(&tgt, full_name);
596     PyMem_Free(name_buf);
597     if(nret == -1) {
598         return PyErr_SetFromErrno(PyExc_IOError);
599     }
600
601     /* Return the result */
602     Py_RETURN_NONE;
603 }
604
605 static char __pylistxattr_doc__[] =
606     "Return the list of attribute names for a file (deprecated)\n"
607     "\n"
608     "Parameters:\n"
609     "  - a string representing filename, or a file-like object,\n"
610     "    or a file descriptor; this represents the file to \n"
611     "    be queried\n"
612     "  - (optional) a boolean value (defaults to false), which, if\n"
613     "    the file name given is a symbolic link, makes the\n"
614     "    function operate on the symbolic link itself instead\n"
615     "    of its target;\n"
616     "@deprecated: this function has been deprecated by the L{list}"
617     " function\n"
618     ;
619
620 /* Wrapper for listxattr */
621 static PyObject *
622 pylistxattr(PyObject *self, PyObject *args)
623 {
624     char *buf;
625     int nofollow=0;
626     int nalloc, nret;
627     PyObject *myarg;
628     PyObject *mylist;
629     int nattrs;
630     char *s;
631     target_t tgt;
632
633     /* Parse the arguments */
634     if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
635         return NULL;
636     if(!convertObj(myarg, &tgt, nofollow))
637         return NULL;
638
639     /* Find out the needed size of the buffer */
640     if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
641         return PyErr_SetFromErrno(PyExc_IOError);
642     }
643
644     /* Try to allocate the memory, using Python's allocator */
645     if((buf = PyMem_Malloc(nalloc)) == NULL) {
646         PyErr_NoMemory();
647         return NULL;
648     }
649
650     /* Now retrieve the list of attributes */
651     if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
652         PyMem_Free(buf);
653         return PyErr_SetFromErrno(PyExc_IOError);
654     }
655
656     /* Compute the number of attributes in the list */
657     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
658         nattrs++;
659     }
660
661     /* Create the list which will hold the result */
662     mylist = PyList_New(nattrs);
663
664     /* Create and insert the attributes as strings in the list */
665     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
666         PyList_SET_ITEM(mylist, nattrs, PyString_FromString(s));
667         nattrs++;
668     }
669
670     /* Free the buffer, now it is no longer needed */
671     PyMem_Free(buf);
672
673     /* Return the result */
674     return mylist;
675 }
676
677 static char __list_doc__[] =
678     "Return the list of attribute names for a file\n"
679     "\n"
680     "Example:\n"
681     "  >>> xattr.list('/path/to/file')\n"
682     "  ['user.test', 'user.comment', 'system.posix_acl_access']\n"
683     "  >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
684     "  ['test', 'comment']\n"
685     "\n"
686     "@param item: the item to query; either a string representing the"
687     " filename, or a file-like object, or a file descriptor\n"
688     "@param nofollow: if given and True, and the function is passed a"
689     " filename that points to a symlink, the function will act on the symlink"
690     " itself instead of its target\n"
691     "@type nofollow: boolean\n"
692     "@param namespace: if given, the attribute must not contain the namespace"
693     " itself, but instead the namespace will be taken from this parameter\n"
694     "@type namespace: string\n"
695     "@return: list of strings; note that if the namespace attribute has been\n"
696     "passed, the names will have the namespace prefix stripped\n"
697     "@rtype: list\n"
698     "@raise EnvironmentError: system errors will raise an exception\n"
699     "@since: 0.4\n"
700     ;
701
702 /* Wrapper for listxattr */
703 static PyObject *
704 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
705 {
706     char *buf;
707     int nofollow=0;
708     int nalloc, nret;
709     PyObject *myarg;
710     PyObject *mylist;
711     char *ns = NULL;
712     int nattrs;
713     char *s;
714     target_t tgt;
715     static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
716
717     /* Parse the arguments */
718     if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
719                           &myarg, &nofollow, &ns))
720         return NULL;
721     if(!convertObj(myarg, &tgt, nofollow))
722         return NULL;
723
724     /* Find out the needed size of the buffer */
725     if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
726         return PyErr_SetFromErrno(PyExc_IOError);
727     }
728
729     /* Try to allocate the memory, using Python's allocator */
730     if((buf = PyMem_Malloc(nalloc)) == NULL) {
731         PyErr_NoMemory();
732         return NULL;
733     }
734
735     /* Now retrieve the list of attributes */
736     if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
737         PyMem_Free(buf);
738         return PyErr_SetFromErrno(PyExc_IOError);
739     }
740
741     /* Compute the number of attributes in the list */
742     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
743         if(matches_ns(s, ns))
744             nattrs++;
745     }
746     /* Create the list which will hold the result */
747     mylist = PyList_New(nattrs);
748
749     /* Create and insert the attributes as strings in the list */
750     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
751         if(matches_ns(s, ns)) {
752             char *short_form = ns == NULL ? s : s + strlen(ns) + 1;
753             PyList_SET_ITEM(mylist, nattrs, PyString_FromString(short_form));
754             nattrs++;
755         }
756     }
757
758     /* Free the buffer, now it is no longer needed */
759     PyMem_Free(buf);
760
761     /* Return the result */
762     return mylist;
763 }
764
765 static PyMethodDef xattr_methods[] = {
766     {"getxattr",  pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
767     {"get",  (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
768      __get_doc__ },
769     {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
770      __get_all_doc__ },
771     {"setxattr",  pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
772     {"set",  (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
773      __set_doc__ },
774     {"removexattr",  pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
775     {"remove",  (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
776      __remove_doc__ },
777     {"listxattr",  pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
778     {"list",  (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
779      __list_doc__ },
780     {NULL, NULL, 0, NULL}        /* Sentinel */
781 };
782
783 static char __xattr_doc__[] = \
784     "Access extended filesystem attributes\n"
785     "\n"
786     "This module gives access to the extended attributes present\n"
787     "in some operating systems/filesystems. You can list attributes,\n"
788     "get, set and remove them.\n"
789     "The last and optional parameter for all functions is a boolean \n"
790     "value which enables the 'l-' version of the functions - acting\n"
791     "on symbolic links and not their destination.\n"
792     "\n"
793     "Example: \n\n"
794     "  >>> import xattr\n"
795     "  >>> xattr.listxattr(\"file.txt\")\n"
796     "  ['user.mime_type']\n"
797     "  >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
798     "  'text/plain'\n"
799     "  >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
800     "\"Simple text file\")\n"
801     "  >>> xattr.listxattr(\"file.txt\")\n"
802     "  ['user.mime_type', 'user.comment']\n"
803     "  >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
804     ""
805     ;
806
807 void
808 initxattr(void)
809 {
810     PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
811
812     PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
813     PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
814
815     /* namespace constants */
816     PyModule_AddStringConstant(m, "NS_SECURITY", "security");
817     PyModule_AddStringConstant(m, "NS_SYSTEM", "system");
818     PyModule_AddStringConstant(m, "NS_TRUSTED", "trusted");
819     PyModule_AddStringConstant(m, "NS_USER", "user");
820
821 }