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