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