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