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