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