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