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