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