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