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