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