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