]> git.k1024.org Git - pyxattr.git/blob - xattr.c
Merge pull request #4 from edelkind/master
[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 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     if(nalloc == 0) {
471         res = PyList_New(0);
472         goto freetgt;
473     }
474
475     /* Try to allocate the memory, using Python's allocator */
476     if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
477         res = PyErr_NoMemory();
478         goto freetgt;
479     }
480
481     /* Now retrieve the list of attributes */
482     nlist = _list_obj(&tgt, buf_list, nalloc);
483
484     if(nlist == -1) {
485         res = PyErr_SetFromErrno(PyExc_IOError);
486         goto free_buf_list;
487     }
488
489     /* Create the list which will hold the result */
490     mylist = PyList_New(0);
491     if(mylist == NULL) {
492       res = NULL;
493       goto free_buf_list;
494     }
495
496     nalloc = ESTIMATE_ATTR_SIZE;
497     if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
498         Py_DECREF(mylist);
499         res = PyErr_NoMemory();
500         goto free_buf_list;
501     }
502
503     /* Create and insert the attributes as strings in the list */
504     for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
505         PyObject *my_tuple;
506         int missing;
507         const char *name;
508
509         if((name=matches_ns(ns, s))==NULL)
510             continue;
511         /* Now retrieve the attribute value */
512         missing = 0;
513         while(1) {
514             nval = _get_obj(&tgt, s, buf_val, nalloc);
515
516             if(nval == -1) {
517                 if(errno == ERANGE) {
518                     nval = _get_obj(&tgt, s, NULL, 0);
519                     if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL) {
520                         res = PyErr_NoMemory();
521                         Py_DECREF(mylist);
522                         goto free_buf_list;
523                     }
524                     nalloc = nval;
525                     continue;
526                 } else if(errno == ENODATA || errno == ENOATTR) {
527                     /* this attribute has gone away since we queried
528                        the attribute list */
529                     missing = 1;
530                     break;
531                 }
532                 /* else we're dealing with a different error, which we
533                    don't know how to handle nicely, so we abort */
534                 Py_DECREF(mylist);
535                 res = PyErr_SetFromErrno(PyExc_IOError);
536                 goto freebufval;
537             }
538             break;
539         }
540         if(missing)
541             continue;
542 #ifdef IS_PY3K
543         my_tuple = Py_BuildValue("yy#", name, buf_val, nval);
544 #else
545         my_tuple = Py_BuildValue("ss#", name, buf_val, nval);
546 #endif
547         if (my_tuple == NULL) {
548           Py_DECREF(mylist);
549           res = NULL;
550           goto freebufval;
551         }
552         PyList_Append(mylist, my_tuple);
553         Py_DECREF(my_tuple);
554     }
555
556     /* Successful exit */
557     res = mylist;
558
559  freebufval:
560     PyMem_Free(buf_val);
561
562  free_buf_list:
563     PyMem_Free(buf_list);
564
565  freetgt:
566     free_tgt(&tgt);
567
568     /* Return the result */
569     return res;
570 }
571
572
573 static char __pysetxattr_doc__[] =
574     "setxattr(item, name, value[, flags=0, nofollow=False])\n"
575     "Set the value of a given extended attribute (deprecated).\n"
576     "\n"
577     "Be careful in case you want to set attributes on symbolic\n"
578     "links, you have to use all the 5 parameters; use 0 for the \n"
579     "flags value if you want the default behaviour (create or "
580     "replace)\n"
581     "\n"
582     ITEM_DOC
583     NAME_SET_DOC
584     VALUE_DOC
585     FLAGS_DOC
586     NOFOLLOW_DOC
587     "\n"
588     ".. deprecated:: 0.4\n"
589     "   this function has been deprecated\n"
590     "   by the :func:`set` function.\n"
591     ;
592
593 /* Wrapper for setxattr */
594 static PyObject *
595 pysetxattr(PyObject *self, PyObject *args)
596 {
597     PyObject *myarg, *res;
598     int nofollow = 0;
599     char *attrname = NULL;
600     char *buf = NULL;
601     int bufsize;
602     int nret;
603     int flags = 0;
604     target_t tgt;
605
606     /* Parse the arguments */
607     if (!PyArg_ParseTuple(args, "Oetet#|ii", &myarg, NULL, &attrname,
608                           NULL, &buf, &bufsize, &flags, &nofollow))
609         return NULL;
610     if(convert_obj(myarg, &tgt, nofollow) < 0) {
611         res = NULL;
612         goto freearg;
613     }
614
615     /* Set the attribute's value */
616     nret = _set_obj(&tgt, attrname, buf, bufsize, flags);
617
618     free_tgt(&tgt);
619
620     if(nret == -1) {
621         res = PyErr_SetFromErrno(PyExc_IOError);
622         goto freearg;
623     }
624
625     Py_INCREF(Py_None);
626     res = Py_None;
627
628  freearg:
629     PyMem_Free(attrname);
630     PyMem_Free(buf);
631
632     /* Return the result */
633     return res;
634 }
635
636 static char __set_doc__[] =
637     "set(item, name, value[, flags=0, namespace=None])\n"
638     "Set the value of a given extended attribute.\n"
639     "\n"
640     "Example:\n"
641     "\n"
642     "    >>> xattr.set('/path/to/file', 'user.comment', 'test')\n"
643     "    >>> xattr.set('/path/to/file', 'comment', 'test',"
644     " namespace=xattr.NS_USER)\n"
645     "\n"
646     ITEM_DOC
647     NAME_SET_DOC
648     VALUE_DOC
649     FLAGS_DOC
650     NOFOLLOW_DOC
651     NS_DOC
652     ":returns: None\n"
653     ":raises EnvironmentError: caused by any system errors\n"
654     "\n"
655     ".. versionadded:: 0.4\n"
656     NS_CHANGED_DOC
657     ;
658
659 /* Wrapper for setxattr */
660 static PyObject *
661 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
662 {
663     PyObject *myarg, *res;
664     int nofollow = 0;
665     char *attrname = NULL;
666     char *buf = NULL;
667     int bufsize;
668     int nret;
669     int flags = 0;
670     target_t tgt;
671     const char *ns = NULL;
672     char *newname;
673     const char *full_name;
674     static char *kwlist[] = {"item", "name", "value", "flags",
675                              "nofollow", "namespace", NULL};
676
677     /* Parse the arguments */
678     if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oetet#|ii" BYTES_CHAR,
679                                      kwlist, &myarg, NULL, &attrname, NULL,
680                                      &buf, &bufsize, &flags, &nofollow, &ns))
681         return NULL;
682     if(convert_obj(myarg, &tgt, nofollow) < 0) {
683         res = NULL;
684         goto freearg;
685     }
686
687     if(merge_ns(ns, attrname, &full_name, &newname) < 0) {
688         res = NULL;
689         goto freearg;
690     }
691
692     /* Set the attribute's value */
693     nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
694
695     PyMem_Free(newname);
696
697     free_tgt(&tgt);
698
699     if(nret == -1) {
700         res = PyErr_SetFromErrno(PyExc_IOError);
701         goto freearg;
702     }
703
704     Py_INCREF(Py_None);
705     res = Py_None;
706
707  freearg:
708     PyMem_Free(attrname);
709     PyMem_Free(buf);
710
711     /* Return the result */
712     return res;
713 }
714
715
716 static char __pyremovexattr_doc__[] =
717     "removexattr(item, name[, nofollow])\n"
718     "Remove an attribute from a file (deprecated).\n"
719     "\n"
720     ITEM_DOC
721     NAME_REMOVE_DOC
722     NOFOLLOW_DOC
723     "\n"
724     ".. deprecated:: 0.4\n"
725     "   this function has been deprecated by the :func:`remove` function.\n"
726     ;
727
728 /* Wrapper for removexattr */
729 static PyObject *
730 pyremovexattr(PyObject *self, PyObject *args)
731 {
732     PyObject *myarg, *res;
733     int nofollow = 0;
734     char *attrname = NULL;
735     int nret;
736     target_t tgt;
737
738     /* Parse the arguments */
739     if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
740         return NULL;
741
742     if(convert_obj(myarg, &tgt, nofollow) < 0) {
743         res = NULL;
744         goto freearg;
745     }
746
747     /* Remove the attribute */
748     nret = _remove_obj(&tgt, attrname);
749
750     free_tgt(&tgt);
751
752     if(nret == -1) {
753         res = PyErr_SetFromErrno(PyExc_IOError);
754         goto freearg;
755     }
756
757     Py_INCREF(Py_None);
758     res = Py_None;
759
760  freearg:
761     PyMem_Free(attrname);
762
763     /* Return the result */
764     return res;
765 }
766
767 static char __remove_doc__[] =
768     "remove(item, name[, nofollow=False, namespace=None])\n"
769     "Remove an attribute from a file.\n"
770     "\n"
771     "Example:\n"
772     "\n"
773     "    >>> xattr.remove('/path/to/file', 'user.comment')\n"
774     "\n"
775     ITEM_DOC
776     NAME_REMOVE_DOC
777     NOFOLLOW_DOC
778     NS_DOC
779     ":returns: None\n"
780     ":raises EnvironmentError: caused by any system errors\n"
781     "\n"
782     ".. versionadded:: 0.4\n"
783     NS_CHANGED_DOC
784     ;
785
786 /* Wrapper for removexattr */
787 static PyObject *
788 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
789 {
790     PyObject *myarg, *res;
791     int nofollow = 0;
792     char *attrname = NULL, *name_buf;
793     const char *ns = NULL;
794     const char *full_name;
795     int nret;
796     target_t tgt;
797     static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
798
799     /* Parse the arguments */
800     if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist,
801                                      &myarg, NULL, &attrname, &nofollow, &ns))
802         return NULL;
803
804     if(convert_obj(myarg, &tgt, nofollow) < 0) {
805         res = NULL;
806         goto freearg;
807     }
808
809     if(merge_ns(ns, attrname, &full_name, &name_buf) < 0) {
810         res = NULL;
811         goto freearg;
812     }
813
814     /* Remove the attribute */
815     nret = _remove_obj(&tgt, full_name);
816
817     PyMem_Free(name_buf);
818
819     free_tgt(&tgt);
820
821     if(nret == -1) {
822         res = PyErr_SetFromErrno(PyExc_IOError);
823         goto freearg;
824     }
825
826     Py_INCREF(Py_None);
827     res = Py_None;
828
829  freearg:
830     PyMem_Free(attrname);
831
832     /* Return the result */
833     return res;
834 }
835
836 static char __pylistxattr_doc__[] =
837     "listxattr(item[, nofollow=False])\n"
838     "Return the list of attribute names for a file (deprecated).\n"
839     "\n"
840     ITEM_DOC
841     NOFOLLOW_DOC
842     "\n"
843     ".. deprecated:: 0.4\n"
844     "   this function has been deprecated by the :func:`list` function.\n"
845     ;
846
847 /* Wrapper for listxattr */
848 static PyObject *
849 pylistxattr(PyObject *self, PyObject *args)
850 {
851     char *buf;
852     int nofollow=0;
853     ssize_t nalloc, nret;
854     PyObject *myarg;
855     PyObject *mylist;
856     Py_ssize_t nattrs;
857     char *s;
858     target_t tgt;
859
860     /* Parse the arguments */
861     if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
862         return NULL;
863     if(convert_obj(myarg, &tgt, nofollow) < 0)
864         return NULL;
865
866     /* Find out the needed size of the buffer */
867     if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
868         mylist = PyErr_SetFromErrno(PyExc_IOError);
869         goto freetgt;
870     }
871
872     if(nalloc == 0) {
873         mylist = PyList_New(0);
874         goto freetgt;
875     }
876
877     /* Try to allocate the memory, using Python's allocator */
878     if((buf = PyMem_Malloc(nalloc)) == NULL) {
879         mylist = PyErr_NoMemory();
880         goto freetgt;
881     }
882
883     /* Now retrieve the list of attributes */
884     if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
885         mylist = PyErr_SetFromErrno(PyExc_IOError);
886         goto freebuf;
887     }
888
889     /* Compute the number of attributes in the list */
890     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
891         nattrs++;
892     }
893
894     /* Create the list which will hold the result */
895     mylist = PyList_New(nattrs);
896     if(mylist == NULL)
897       goto freebuf;
898
899     /* Create and insert the attributes as strings in the list */
900     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
901         PyObject *item = PyBytes_FromString(s);
902         if(item == NULL) {
903             Py_DECREF(mylist);
904             mylist = NULL;
905             goto freebuf;
906         }
907         PyList_SET_ITEM(mylist, nattrs, item);
908         nattrs++;
909     }
910
911  freebuf:
912     /* Free the buffer, now it is no longer needed */
913     PyMem_Free(buf);
914
915  freetgt:
916     free_tgt(&tgt);
917
918     /* Return the result */
919     return mylist;
920 }
921
922 static char __list_doc__[] =
923     "list(item[, nofollow=False, namespace=None])\n"
924     "Return the list of attribute names for a file.\n"
925     "\n"
926     "Example:\n"
927     "\n"
928     "    >>> xattr.list('/path/to/file')\n"
929     "    ['user.test', 'user.comment', 'system.posix_acl_access']\n"
930     "    >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
931     "    ['test', 'comment']\n"
932     "\n"
933     ITEM_DOC
934     NOFOLLOW_DOC
935     NS_DOC
936     ":returns: the list of attributes; note that if a namespace \n"
937     "    argument was passed, it (and the separator) will be stripped\n"
938     "    from the names\n"
939     "    returned\n"
940     ":rtype: list\n"
941     ":raises EnvironmentError: caused by any system errors\n"
942     "\n"
943     ".. versionadded:: 0.4\n"
944     NS_CHANGED_DOC
945     ;
946
947 /* Wrapper for listxattr */
948 static PyObject *
949 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
950 {
951     char *buf;
952     int nofollow = 0;
953     ssize_t nalloc, nret;
954     PyObject *myarg;
955     PyObject *res;
956     const char *ns = NULL;
957     Py_ssize_t nattrs;
958     char *s;
959     target_t tgt;
960     static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
961
962     /* Parse the arguments */
963     if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist,
964                                      &myarg, &nofollow, &ns))
965         return NULL;
966     if(convert_obj(myarg, &tgt, nofollow) < 0) {
967         res = NULL;
968         goto freearg;
969     }
970
971     /* Find out the needed size of the buffer */
972     if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
973         res = PyErr_SetFromErrno(PyExc_IOError);
974         goto freetgt;
975     }
976
977     if(nalloc == 0) {
978         res = PyList_New(0);
979         goto freetgt;
980     }
981
982     /* Try to allocate the memory, using Python's allocator */
983     if((buf = PyMem_Malloc(nalloc)) == NULL) {
984         res = PyErr_NoMemory();
985         goto freetgt;
986     }
987
988     /* Now retrieve the list of attributes */
989     if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
990         res = PyErr_SetFromErrno(PyExc_IOError);
991         goto freebuf;
992     }
993
994     /* Compute the number of attributes in the list */
995     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
996         if(matches_ns(ns, s) != NULL)
997             nattrs++;
998     }
999     /* Create the list which will hold the result */
1000     res = PyList_New(nattrs);
1001     if(res == NULL)
1002         goto freebuf;
1003
1004     /* Create and insert the attributes as strings in the list */
1005     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1006         const char *name = matches_ns(ns, s);
1007         if(name!=NULL) {
1008             PyObject *item = PyBytes_FromString(name);
1009             if(item == NULL) {
1010                 Py_DECREF(res);
1011                 res = NULL;
1012                 goto freebuf;
1013             }
1014             PyList_SET_ITEM(res, nattrs, item);
1015             nattrs++;
1016         }
1017     }
1018
1019  freebuf:
1020     /* Free the buffer, now it is no longer needed */
1021     PyMem_Free(buf);
1022
1023  freetgt:
1024     free_tgt(&tgt);
1025  freearg:
1026
1027     /* Return the result */
1028     return res;
1029 }
1030
1031 static PyMethodDef xattr_methods[] = {
1032     {"getxattr",  pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
1033     {"get",  (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
1034      __get_doc__ },
1035     {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
1036      __get_all_doc__ },
1037     {"setxattr",  pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
1038     {"set",  (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
1039      __set_doc__ },
1040     {"removexattr",  pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
1041     {"remove",  (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
1042      __remove_doc__ },
1043     {"listxattr",  pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
1044     {"list",  (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
1045      __list_doc__ },
1046     {NULL, NULL, 0, NULL}        /* Sentinel */
1047 };
1048
1049 static char __xattr_doc__[] = \
1050     "This module gives access to the extended attributes present\n"
1051     "in some operating systems/filesystems. You can list attributes,\n"
1052     "get, set and remove them.\n"
1053     "\n"
1054     "The module exposes two sets of functions:\n"
1055     "  - the 'old' :func:`listxattr`, :func:`getxattr`, :func:`setxattr`,\n"
1056     "    :func:`removexattr`\n"
1057     "    functions which are deprecated since version 0.4\n"
1058     "  - the new :func:`list`, :func:`get`, :func:`get_all`, :func:`set`,\n"
1059     "    :func:`remove` functions\n"
1060     "    which expose a namespace-aware API and simplify a bit the calling\n"
1061     "    model by using keyword arguments\n"
1062     "\n"
1063     "Example: \n\n"
1064     "  >>> import xattr\n"
1065     "  >>> xattr.listxattr(\"file.txt\")\n"
1066     "  ['user.mime_type']\n"
1067     "  >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
1068     "  'text/plain'\n"
1069     "  >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
1070     "\"Simple text file\")\n"
1071     "  >>> xattr.listxattr(\"file.txt\")\n"
1072     "  ['user.mime_type', 'user.comment']\n"
1073     "  >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
1074     "\n"
1075     ".. note:: Most or all errors reported by the system while using\n"
1076     "   the ``xattr`` library will be reported by raising\n"
1077     "   a :exc:`EnvironmentError`; under\n"
1078     "   Linux, the following ``errno`` values are used:\n"
1079     "\n"
1080     "   - ``ENOATTR`` and ``ENODATA`` mean that the attribute name is\n"
1081     "     invalid\n"
1082     "   - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n"
1083     "     support extended attributes, or that the namespace is invalid\n"
1084     "   - ``E2BIG`` mean that the attribute value is too big\n"
1085     "   - ``ERANGE`` mean that the attribute name is too big (it might also\n"
1086     "     mean an error in the xattr module itself)\n"
1087     "   - ``ENOSPC`` and ``EDQUOT`` are documented as meaning out of disk\n"
1088     "     space or out of disk space because of quota limits\n"
1089     ".. note:: Under Python 3, the namespace argument is a byte string,\n"
1090     "   not a unicode string.\n"
1091     "\n"
1092     ;
1093
1094 #ifdef IS_PY3K
1095
1096 static struct PyModuleDef xattrmodule = {
1097     PyModuleDef_HEAD_INIT,
1098     "xattr",
1099     __xattr_doc__,
1100     0,
1101     xattr_methods,
1102 };
1103
1104 #define INITERROR return NULL
1105
1106 PyMODINIT_FUNC
1107 PyInit_xattr(void)
1108
1109 #else
1110 #define INITERROR return
1111 void
1112 initxattr(void)
1113 #endif
1114 {
1115     PyObject *ns_security = NULL;
1116     PyObject *ns_system   = NULL;
1117     PyObject *ns_trusted  = NULL;
1118     PyObject *ns_user     = NULL;
1119 #ifdef IS_PY3K
1120     PyObject *m = PyModule_Create(&xattrmodule);
1121 #else
1122     PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
1123 #endif
1124     if (m==NULL)
1125         INITERROR;
1126
1127     PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
1128     PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
1129     PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
1130     PyModule_AddStringConstant(m, "__license__",
1131                                "GNU Lesser General Public License (LGPL)");
1132     PyModule_AddStringConstant(m, "__docformat__", "restructuredtext en");
1133
1134     PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
1135     PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
1136
1137     /* namespace constants */
1138     if((ns_security = PyBytes_FromString("security")) == NULL)
1139         goto err_out;
1140     if((ns_system = PyBytes_FromString("system")) == NULL)
1141         goto err_out;
1142     if((ns_trusted = PyBytes_FromString("trusted")) == NULL)
1143         goto err_out;
1144     if((ns_user = PyBytes_FromString("user")) == NULL)
1145         goto err_out;
1146     if(PyModule_AddObject(m, "NS_SECURITY", ns_security) < 0)
1147         goto err_out;
1148     ns_security = NULL;
1149     if(PyModule_AddObject(m, "NS_SYSTEM", ns_system) < 0)
1150         goto err_out;
1151     ns_system = NULL;
1152     if(PyModule_AddObject(m, "NS_TRUSTED", ns_trusted) < 0)
1153         goto err_out;
1154     ns_trusted = NULL;
1155     if(PyModule_AddObject(m, "NS_USER", ns_user) < 0)
1156         goto err_out;
1157     ns_user = NULL;
1158
1159 #ifdef IS_PY3K
1160     return m;
1161 #else
1162     return;
1163 #endif
1164
1165  err_out:
1166     Py_XDECREF(ns_user);
1167     Py_XDECREF(ns_trusted);
1168     Py_XDECREF(ns_system);
1169     Py_XDECREF(ns_security);
1170     INITERROR;
1171 }