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