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