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