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