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