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