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