]> git.k1024.org Git - debian-pyxattr.git/blob - xattr.c
Remove dbg package and prepare new upload
[debian-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         if(PyList_Append(mylist, my_tuple) < 0) {
649             Py_DECREF(mylist);
650             goto free_buf_val;
651         }
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#|iiy",
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|iy", 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 = NULL;
971     int nofollow = 0;
972     ssize_t nret;
973     size_t nalloc = ESTIMATE_ATTR_SIZE;
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     nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL);
987     if (nret == -1) {
988       mylist = NULL;
989       goto free_buf;
990     }
991
992     /* Compute the number of attributes in the list */
993     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
994         nattrs++;
995     }
996
997     /* Create the list which will hold the result */
998     mylist = PyList_New(nattrs);
999     if(mylist == NULL) {
1000       goto free_buf;
1001     }
1002
1003     /* Create and insert the attributes as strings in the list */
1004     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1005         PyObject *item = PyBytes_FromString(s);
1006         if(item == NULL) {
1007             Py_DECREF(mylist);
1008             mylist = NULL;
1009             goto free_buf;
1010         }
1011         PyList_SET_ITEM(mylist, nattrs, item);
1012         nattrs++;
1013     }
1014
1015  free_buf:
1016     /* Free the buffer, now it is no longer needed */
1017     PyMem_Free(buf);
1018     free_tgt(&tgt);
1019
1020     /* Return the result */
1021     return mylist;
1022 }
1023
1024 static char __list_doc__[] =
1025     "list(item[, nofollow=False, namespace=None])\n"
1026     "Return the list of attribute names for a file.\n"
1027     "\n"
1028     "Example:\n"
1029     "\n"
1030     "    >>> xattr.list('/path/to/file')\n"
1031     "    ['user.test', 'user.comment', 'system.posix_acl_access']\n"
1032     "    >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
1033     "    ['test', 'comment']\n"
1034     "\n"
1035     ITEM_DOC
1036     NOFOLLOW_DOC
1037     NS_DOC
1038     ":returns: the list of attributes; note that if a namespace \n"
1039     "    argument was passed, it (and the separator) will be stripped\n"
1040     "    from the names\n"
1041     "    returned\n"
1042     ":rtype: list\n"
1043     ":raises EnvironmentError: caused by any system errors\n"
1044     "\n"
1045     ".. versionadded:: 0.4\n"
1046     NS_CHANGED_DOC
1047     ;
1048
1049 /* Wrapper for listxattr */
1050 static PyObject *
1051 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
1052 {
1053     char *buf = NULL;
1054     int nofollow = 0;
1055     ssize_t nret;
1056     size_t nalloc = ESTIMATE_ATTR_SIZE;
1057     PyObject *myarg;
1058     PyObject *res;
1059     const char *ns = NULL;
1060     Py_ssize_t nattrs;
1061     char *s;
1062     target_t tgt;
1063     static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
1064
1065     /* Parse the arguments */
1066     if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iy", kwlist,
1067                                      &myarg, &nofollow, &ns))
1068         return NULL;
1069     res = NULL;
1070     if(convert_obj(myarg, &tgt, nofollow) < 0) {
1071         goto free_arg;
1072     }
1073     nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL);
1074     if (nret == -1) {
1075       goto free_tgt;
1076     }
1077
1078     /* Compute the number of attributes in the list */
1079     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
1080         if(matches_ns(ns, s) != NULL)
1081             nattrs++;
1082     }
1083
1084     /* Create the list which will hold the result */
1085     if((res = PyList_New(nattrs)) == NULL) {
1086         goto free_buf;
1087     }
1088
1089     /* Create and insert the attributes as strings in the list */
1090     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1091         const char *name = matches_ns(ns, s);
1092         if(name != NULL) {
1093             PyObject *item = PyBytes_FromString(name);
1094             if(item == NULL) {
1095                 Py_DECREF(res);
1096                 res = NULL;
1097                 goto free_buf;
1098             }
1099             PyList_SET_ITEM(res, nattrs, item);
1100             nattrs++;
1101         }
1102     }
1103
1104  free_buf:
1105     /* Free the buffer, now it is no longer needed */
1106     PyMem_Free(buf);
1107
1108  free_tgt:
1109     free_tgt(&tgt);
1110  free_arg:
1111
1112     /* Return the result */
1113     return res;
1114 }
1115
1116 static PyMethodDef xattr_methods[] = {
1117     {"getxattr",  pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
1118     {"get",  (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
1119      __get_doc__ },
1120     {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
1121      __get_all_doc__ },
1122     {"setxattr",  pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
1123     {"set",  (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
1124      __set_doc__ },
1125     {"removexattr",  pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
1126     {"remove",  (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
1127      __remove_doc__ },
1128     {"listxattr",  pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
1129     {"list",  (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
1130      __list_doc__ },
1131     {NULL, NULL, 0, NULL}        /* Sentinel */
1132 };
1133
1134 static char __xattr_doc__[] = \
1135     "This module gives access to the extended attributes present\n"
1136     "in some operating systems/filesystems. You can list attributes,\n"
1137     "get, set and remove them.\n"
1138     "\n"
1139     "The module exposes two sets of functions:\n"
1140     "  - the 'old' :func:`listxattr`, :func:`getxattr`, :func:`setxattr`,\n"
1141     "    :func:`removexattr`\n"
1142     "    functions which are deprecated since version 0.4\n"
1143     "  - the new :func:`list`, :func:`get`, :func:`get_all`, :func:`set`,\n"
1144     "    :func:`remove` functions\n"
1145     "    which expose a namespace-aware API and simplify a bit the calling\n"
1146     "    model by using keyword arguments\n"
1147     "\n"
1148     "Example: \n\n"
1149     "  >>> import xattr\n"
1150     "  >>> xattr.listxattr(\"file.txt\")\n"
1151     "  ['user.mime_type']\n"
1152     "  >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
1153     "  'text/plain'\n"
1154     "  >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
1155     "\"Simple text file\")\n"
1156     "  >>> xattr.listxattr(\"file.txt\")\n"
1157     "  ['user.mime_type', 'user.comment']\n"
1158     "  >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
1159     "\n"
1160     ".. note:: Most or all errors reported by the system while using\n"
1161     "   the ``xattr`` library will be reported by raising\n"
1162     "   a :exc:`EnvironmentError`; under\n"
1163     "   Linux, the following ``errno`` values are used:\n"
1164     "\n"
1165     "   - ``ENODATA`` means that the attribute name is invalid\n"
1166     "   - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n"
1167     "     support extended attributes, or that the namespace is invalid\n"
1168     "   - ``E2BIG`` mean that the attribute value is too big\n"
1169     "   - ``ERANGE`` mean that the attribute name is too big (it might also\n"
1170     "     mean an error in the xattr module itself)\n"
1171     "   - ``ENOSPC`` and ``EDQUOT`` are documented as meaning out of disk\n"
1172     "     space or out of disk space because of quota limits\n"
1173     ".. note:: Under Python 3, the namespace argument is a byte string,\n"
1174     "   not a unicode string.\n"
1175     "\n"
1176     ;
1177
1178 static struct PyModuleDef xattrmodule = {
1179     PyModuleDef_HEAD_INIT,
1180     "xattr",
1181     __xattr_doc__,
1182     0,
1183     xattr_methods,
1184 };
1185
1186 #define INITERROR return NULL
1187
1188 PyMODINIT_FUNC
1189 PyInit_xattr(void)
1190
1191 {
1192     PyObject *ns_security = NULL;
1193     PyObject *ns_system   = NULL;
1194     PyObject *ns_trusted  = NULL;
1195     PyObject *ns_user     = NULL;
1196     PyObject *m = PyModule_Create(&xattrmodule);
1197     if (m==NULL)
1198         return NULL;
1199
1200     PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
1201     PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
1202     PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
1203     PyModule_AddStringConstant(m, "__license__",
1204                                "GNU Lesser General Public License (LGPL)");
1205     PyModule_AddStringConstant(m, "__docformat__", "restructuredtext en");
1206
1207     PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
1208     PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
1209
1210     /* namespace constants */
1211     if((ns_security = PyBytes_FromString("security")) == NULL)
1212         goto err_out;
1213     if((ns_system = PyBytes_FromString("system")) == NULL)
1214         goto err_out;
1215     if((ns_trusted = PyBytes_FromString("trusted")) == NULL)
1216         goto err_out;
1217     if((ns_user = PyBytes_FromString("user")) == NULL)
1218         goto err_out;
1219     if(PyModule_AddObject(m, "NS_SECURITY", ns_security) < 0)
1220         goto err_out;
1221     ns_security = NULL;
1222     if(PyModule_AddObject(m, "NS_SYSTEM", ns_system) < 0)
1223         goto err_out;
1224     ns_system = NULL;
1225     if(PyModule_AddObject(m, "NS_TRUSTED", ns_trusted) < 0)
1226         goto err_out;
1227     ns_trusted = NULL;
1228     if(PyModule_AddObject(m, "NS_USER", ns_user) < 0)
1229         goto err_out;
1230     ns_user = NULL;
1231
1232     return m;
1233
1234  err_out:
1235     Py_XDECREF(ns_user);
1236     Py_XDECREF(ns_trusted);
1237     Py_XDECREF(ns_system);
1238     Py_XDECREF(ns_security);
1239     INITERROR;
1240 }