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