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