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