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