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