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