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