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