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