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