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