]> git.k1024.org Git - pyxattr.git/blob - xattr.c
Add license information to source file
[pyxattr.git] / xattr.c
1 /*
2     xattr - a python module for manipulating filesystem extended attributes
3
4     Copyright (C) 2002, 2003, 2006, 2008 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 /* the estimated (startup) attribute buffer size in
36    multi-operations */
37 #define ESTIMATE_ATTR_SIZE 256
38
39 typedef enum {T_FD, T_PATH, T_LINK} target_e;
40
41 typedef struct {
42     target_e type;
43     union {
44         const char *name;
45         int fd;
46     };
47 } target_t;
48
49 /** Converts from a string, file or int argument to what we need. */
50 static int convertObj(PyObject *myobj, target_t *tgt, int nofollow) {
51     int fd;
52     if(PyString_Check(myobj)) {
53         tgt->type = nofollow ? T_LINK : T_PATH;
54         tgt->name = PyString_AS_STRING(myobj);
55     } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
56         tgt->type = T_FD;
57         tgt->fd = fd;
58     } else {
59         PyErr_SetString(PyExc_TypeError, "argument must be string or int");
60         return 0;
61     }
62     return 1;
63 }
64
65 /* Combine a namespace string and an attribute name into a
66    fully-qualified name */
67 static const char* merge_ns(const char *ns, const char *name, char **buf) {
68     if(ns != NULL) {
69         int cnt;
70         size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
71         if((*buf = PyMem_Malloc(new_size)) == NULL) {
72             PyErr_NoMemory();
73             return NULL;
74         }
75         cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
76         if(cnt > new_size || cnt < 0) {
77             PyErr_SetString(PyExc_ValueError,
78                             "can't format the attribute name");
79             PyMem_Free(*buf);
80             return NULL;
81         }
82         return *buf;
83     } else {
84         *buf = NULL;
85         return name;
86     }
87 }
88
89 static ssize_t _list_obj(target_t *tgt, char *list, size_t size) {
90     if(tgt->type == T_FD)
91         return flistxattr(tgt->fd, list, size);
92     else if (tgt->type == T_LINK)
93         return llistxattr(tgt->name, list, size);
94     else
95         return listxattr(tgt->name, list, size);
96 }
97
98 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
99                         size_t size) {
100     if(tgt->type == T_FD)
101         return fgetxattr(tgt->fd, name, value, size);
102     else if (tgt->type == T_LINK)
103         return lgetxattr(tgt->name, name, value, size);
104     else
105         return getxattr(tgt->name, name, value, size);
106 }
107
108 static int _set_obj(target_t *tgt, const char *name,
109                     const void *value, size_t size, int flags) {
110     if(tgt->type == T_FD)
111         return fsetxattr(tgt->fd, name, value, size, flags);
112     else if (tgt->type == T_LINK)
113         return lsetxattr(tgt->name, name, value, size, flags);
114     else
115         return setxattr(tgt->name, name, value, size, flags);
116 }
117
118 static int _remove_obj(target_t *tgt, const char *name) {
119     if(tgt->type == T_FD)
120         return fremovexattr(tgt->fd, name);
121     else if (tgt->type == T_LINK)
122         return lremovexattr(tgt->name, name);
123     else
124         return removexattr(tgt->name, name);
125 }
126
127 /*
128    Checks if an attribute name matches an optional namespace.
129
130    If the namespace is NULL, it will return the name itself.  If the
131    namespace is non-NULL and the name matches, it will return a
132    pointer to the offset in the name after the namespace and the
133    separator. If however the name doesn't match the namespace, it will
134    return NULL.
135 */
136 const char *matches_ns(const char *ns, const char *name) {
137     size_t ns_size;
138     if (ns == NULL)
139         return name;
140     ns_size = strlen(ns);
141
142     if (strlen(name) > (ns_size+1) && !strncmp(name, ns, ns_size) &&
143         name[ns_size] == '.')
144         return name + ns_size + 1;
145     return NULL;
146 }
147
148 /* Wrapper for getxattr */
149 static char __pygetxattr_doc__[] =
150     "Get the value of a given extended attribute (deprecated).\n"
151     "\n"
152     "Parameters:\n"
153     "  - a string representing filename, or a file-like object,\n"
154     "    or a file descriptor; this represents the file on \n"
155     "    which to act\n"
156     "  - a string, representing the attribute whose value to retrieve;\n"
157     "    usually in form of system.posix_acl or user.mime_type\n"
158     "  - (optional) a boolean value (defaults to false), which, if\n"
159     "    the file name given is a symbolic link, makes the\n"
160     "    function operate on the symbolic link itself instead\n"
161     "    of its target;\n"
162     "@deprecated: since version 0.4, this function has been deprecated\n"
163     "    by the L{get} function\n"
164     ;
165
166 static PyObject *
167 pygetxattr(PyObject *self, PyObject *args)
168 {
169     PyObject *myarg;
170     target_t tgt;
171     int nofollow=0;
172     char *attrname;
173     char *buf;
174     ssize_t nalloc, nret;
175     PyObject *res;
176
177     /* Parse the arguments */
178     if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
179         return NULL;
180     if(!convertObj(myarg, &tgt, nofollow))
181         return NULL;
182
183     /* Find out the needed size of the buffer */
184     if((nalloc = _get_obj(&tgt, attrname, NULL, 0)) == -1) {
185         return PyErr_SetFromErrno(PyExc_IOError);
186     }
187
188     /* Try to allocate the memory, using Python's allocator */
189     if((buf = PyMem_Malloc(nalloc)) == NULL) {
190         PyErr_NoMemory();
191         return NULL;
192     }
193
194     /* Now retrieve the attribute value */
195     if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) {
196         PyMem_Free(buf);
197         return PyErr_SetFromErrno(PyExc_IOError);
198     }
199
200     /* Create the string which will hold the result */
201     res = PyString_FromStringAndSize(buf, nret);
202
203     /* Free the buffer, now it is no longer needed */
204     PyMem_Free(buf);
205
206     /* Return the result */
207     return res;
208 }
209
210 /* Wrapper for getxattr */
211 static char __get_doc__[] =
212     "Get the value of a given extended attribute.\n"
213     "\n"
214     "Example:\n"
215     "    >>> xattr.get('/path/to/file', 'user.comment')\n"
216     "    'test'\n"
217     "    >>> xattr.get('/path/to/file', 'comment', namespace=xattr.NS_USER)\n"
218     "    'test'\n"
219     "\n"
220     "@param item: the item to query; either a string representing the\n"
221     "    filename, or a file-like object, or a file descriptor\n"
222     "@param name: the attribute whose value to set; usually in form of\n"
223     "    system.posix_acl or user.mime_type\n"
224     "@type name: string\n"
225     "@param nofollow: if given and True, and the function is passed a\n"
226     "    filename that points to a symlink, the function will act on the\n"
227     "    symlink itself instead of its target\n"
228     "@type nofollow: boolean\n"
229     "@param namespace: if given, the attribute must not contain the\n"
230     "    namespace itself, but instead the namespace will be taken from\n"
231     "    this parameter\n"
232     "@type namespace: string\n"
233     "@return: the value of the extended attribute (can contain NULLs)\n"
234     "@rtype: string\n"
235     "@raise EnvironmentError: caused by any system errors\n"
236     "@since: 0.4\n"
237     ;
238
239 static PyObject *
240 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
241 {
242     PyObject *myarg;
243     target_t tgt;
244     int nofollow=0;
245     char *attrname, *namebuf;
246     const char *fullname;
247     char *buf;
248     char *ns = NULL;
249     ssize_t nalloc, nret;
250     PyObject *res;
251     static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
252
253     /* Parse the arguments */
254     if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist,
255                                      &myarg, &attrname, &nofollow, &ns))
256         return NULL;
257     if(!convertObj(myarg, &tgt, nofollow))
258         return NULL;
259
260     fullname = merge_ns(ns, attrname, &namebuf);
261
262     /* Find out the needed size of the buffer */
263     if((nalloc = _get_obj(&tgt, fullname, NULL, 0)) == -1) {
264         return PyErr_SetFromErrno(PyExc_IOError);
265     }
266
267     /* Try to allocate the memory, using Python's allocator */
268     if((buf = PyMem_Malloc(nalloc)) == NULL) {
269         PyMem_Free(namebuf);
270         PyErr_NoMemory();
271         return NULL;
272     }
273
274     /* Now retrieve the attribute value */
275     if((nret = _get_obj(&tgt, fullname, buf, nalloc)) == -1) {
276         PyMem_Free(buf);
277         PyMem_Free(namebuf);
278         return PyErr_SetFromErrno(PyExc_IOError);
279     }
280
281     /* Create the string which will hold the result */
282     res = PyString_FromStringAndSize(buf, nret);
283
284     /* Free the buffers, they are no longer needed */
285     PyMem_Free(namebuf);
286     PyMem_Free(buf);
287
288     /* Return the result */
289     return res;
290 }
291
292 /* Wrapper for getxattr */
293 static char __get_all_doc__[] =
294     "Get all the extended attributes of an item.\n"
295     "\n"
296     "This function performs a bulk-get of all extended attribute names\n"
297     "and the corresponding value.\n"
298     "Example:\n"
299     "    >>> xattr.get_all('/path/to/file')\n"
300     "    [('user.mime-type', 'plain/text'), ('user.comment', 'test'),\n"
301     "     ('system.posix_acl_access', '\\x02\\x00...')]\n"
302     "    >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
303     "    [('mime-type', 'plain/text'), ('comment', 'test')]\n"
304     "\n"
305     "@param item: the item to query; either a string representing the\n"
306     "    filename, or a file-like object, or a file descriptor\n"
307     "@keyword namespace: an optional namespace for filtering the\n"
308     "    attributes; for example, querying all user attributes can be\n"
309     "    accomplished by passing namespace=L{NS_USER}\n"
310     "@type namespace: string\n"
311     "@keyword nofollow: if passed and true, if the target file is a\n"
312     "    symbolic link, the attributes for the link itself will be\n"
313     "    returned, instead of the attributes of the target\n"
314     "@type nofollow: boolean\n"
315     "@return: list of tuples (name, value); note that if a namespace\n"
316     "    argument was passed, it (and the separator) will be stripped from\n"
317     "    the names returned\n"
318     "@rtype: list\n"
319     "@raise EnvironmentError: caused by any system errors\n"
320     "@note: Since reading the whole attribute list is not an atomic\n"
321     "    operation, it might be possible that attributes are added\n"
322     "    or removed between the initial query and the actual reading\n"
323     "    of the attributes; the returned list will contain only the\n"
324     "    attributes that were present at the initial listing of the\n"
325     "    attribute names and that were still present when the read\n"
326     "    attempt for the value is made.\n"
327     "@since: 0.4\n"
328     ;
329
330 static PyObject *
331 get_all(PyObject *self, PyObject *args, PyObject *keywds)
332 {
333     PyObject *myarg;
334     int dolink=0;
335     char *ns = NULL;
336     char *buf_list, *buf_val;
337     char *s;
338     size_t nalloc, nlist, nval;
339     PyObject *mylist;
340     target_t tgt;
341     static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
342
343     /* Parse the arguments */
344     if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
345                                      &myarg, &dolink, &ns))
346         return NULL;
347     if(!convertObj(myarg, &tgt, dolink))
348         return NULL;
349
350     /* Compute first the list of attributes */
351
352     /* Find out the needed size of the buffer for the attribute list */
353     nalloc = _list_obj(&tgt, NULL, 0);
354
355     if(nalloc == -1) {
356         return PyErr_SetFromErrno(PyExc_IOError);
357     }
358
359     /* Try to allocate the memory, using Python's allocator */
360     if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
361         PyErr_NoMemory();
362         return NULL;
363     }
364
365     /* Now retrieve the list of attributes */
366     nlist = _list_obj(&tgt, buf_list, nalloc);
367
368     if(nlist == -1) {
369         PyErr_SetFromErrno(PyExc_IOError);
370         goto free_buf_list;
371     }
372
373     /* Create the list which will hold the result */
374     mylist = PyList_New(0);
375     nalloc = ESTIMATE_ATTR_SIZE;
376     if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
377         PyErr_NoMemory();
378         goto free_list;
379     }
380
381     /* Create and insert the attributes as strings in the list */
382     for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
383         PyObject *my_tuple;
384         int missing;
385         const char *name;
386
387         if((name=matches_ns(ns, s))==NULL)
388             continue;
389         /* Now retrieve the attribute value */
390         missing = 0;
391         while(1) {
392             nval = _get_obj(&tgt, s, buf_val, nalloc);
393
394             if(nval == -1) {
395                 if(errno == ERANGE) {
396                     nval = _get_obj(&tgt, s, NULL, 0);
397                     if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL)
398                         goto free_list;
399                     nalloc = nval;
400                     continue;
401                 } else if(errno == ENODATA || errno == ENOATTR) {
402                     /* this attribute has gone away since we queried
403                        the attribute list */
404                     missing = 1;
405                     break;
406                 }
407                 goto exit_errno;
408             }
409             break;
410         }
411         if(missing)
412             continue;
413         my_tuple = Py_BuildValue("ss#", name, buf_val, nval);
414
415         PyList_Append(mylist, my_tuple);
416         Py_DECREF(my_tuple);
417     }
418
419     /* Free the buffers, now they are no longer needed */
420     PyMem_Free(buf_val);
421     PyMem_Free(buf_list);
422
423     /* Return the result */
424     return mylist;
425  exit_errno:
426     PyErr_SetFromErrno(PyExc_IOError);
427     PyMem_Free(buf_val);
428  free_list:
429     Py_DECREF(mylist);
430  free_buf_list:
431     PyMem_Free(buf_list);
432     return NULL;
433 }
434
435
436 static char __pysetxattr_doc__[] =
437     "Set the value of a given extended attribute (deprecated).\n"
438     "\n"
439     "Be carefull in case you want to set attributes on symbolic\n"
440     "links, you have to use all the 5 parameters; use 0 for the \n"
441     "flags value if you want the default behavior (create or "
442     "replace)\n"
443     "\n"
444     "Parameters:\n"
445     "  - a string representing filename, or a file-like object,\n"
446     "    or a file descriptor; this represents the file on \n"
447     "    which to act\n"
448     "  - a string, representing the attribute whose value to set;\n"
449     "    usually in form of system.posix_acl or user.mime_type\n"
450     "  - a string, possibly with embedded NULLs; note that there\n"
451     "    are restrictions regarding the size of the value, for\n"
452     "    example, for ext2/ext3, maximum size is the block size\n"
453     "  - (optional) flags; if 0 or ommited the attribute will be \n"
454     "    created or replaced; if XATTR_CREATE, the attribute \n"
455     "    will be created, giving an error if it already exists;\n"
456     "    of XATTR_REPLACE, the attribute will be replaced,\n"
457     "    giving an error if it doesn't exists;\n"
458     "  - (optional) a boolean value (defaults to false), which, if\n"
459     "    the file name given is a symbolic link, makes the\n"
460     "    function operate on the symbolic link itself instead\n"
461     "    of its target;\n"
462     "@deprecated: since version 0.4, this function has been deprecated\n"
463     "    by the L{set} function\n"
464     ;
465
466 /* Wrapper for setxattr */
467 static PyObject *
468 pysetxattr(PyObject *self, PyObject *args)
469 {
470     PyObject *myarg;
471     int nofollow=0;
472     char *attrname;
473     char *buf;
474     Py_ssize_t bufsize;
475     int nret;
476     int flags = 0;
477     target_t tgt;
478
479     /* Parse the arguments */
480     if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname,
481                           &buf, &bufsize, &flags, &nofollow))
482         return NULL;
483     if(!convertObj(myarg, &tgt, nofollow))
484         return NULL;
485
486     /* Set the attribute's value */
487     if((nret = _set_obj(&tgt, attrname, buf, bufsize, flags)) == -1) {
488         return PyErr_SetFromErrno(PyExc_IOError);
489     }
490
491     /* Return the result */
492     Py_RETURN_NONE;
493 }
494
495 static char __set_doc__[] =
496     "Set the value of a given extended attribute.\n"
497     "\n"
498     "Example:\n"
499     "    >>> xattr.set('/path/to/file', 'user.comment', 'test')\n"
500     "    >>> xattr.set('/path/to/file', 'comment', 'test',"
501     " namespace=xattr.NS_USER)\n"
502     "\n"
503     "@param item: the item to query; either a string representing the\n"
504     "    filename, or a file-like object, or a file descriptor\n"
505     "@param name: the attribute whose value to set; usually in form of\n"
506     "    system.posix_acl or user.mime_type\n"
507     "@type name: string\n"
508     "@param value: a string, possibly with embedded NULLs; note that there\n"
509     "    are restrictions regarding the size of the value, for\n"
510     "    example, for ext2/ext3, maximum size is the block size\n"
511     "@type value: string\n"
512     "@param flags: if 0 or ommited the attribute will be\n"
513     "    created or replaced; if L{XATTR_CREATE}, the attribute\n"
514     "    will be created, giving an error if it already exists;\n"
515     "    if L{XATTR_REPLACE}, the attribute will be replaced,\n"
516     "    giving an error if it doesn't exists;\n"
517     "@type flags: integer\n"
518     "@param nofollow: if given and True, and the function is passed a\n"
519     "    filename that points to a symlink, the function will act on the\n"
520     "    symlink itself instead of its target\n"
521     "@type nofollow: boolean\n"
522     "@param namespace: if given, the attribute must not contain the\n"
523     "    namespace itself, but instead the namespace will be taken from\n"
524     "    this parameter\n"
525     "@type namespace: string\n"
526     "@rtype: None\n"
527     "@raise EnvironmentError: caused by any system errors\n"
528     "@since: 0.4\n"
529     ;
530
531 /* Wrapper for setxattr */
532 static PyObject *
533 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
534 {
535     PyObject *myarg;
536     int nofollow=0;
537     char *attrname;
538     char *buf;
539     Py_ssize_t bufsize;
540     int nret;
541     int flags = 0;
542     target_t tgt;
543     char *ns = NULL;
544     char *newname;
545     const char *full_name;
546     static char *kwlist[] = {"item", "name", "value", "flags",
547                              "nofollow", "namespace", NULL};
548
549     /* Parse the arguments */
550     if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oss#|iiz", kwlist,
551                                      &myarg, &attrname,
552                                      &buf, &bufsize, &flags, &nofollow, &ns))
553         return NULL;
554     if(!convertObj(myarg, &tgt, nofollow))
555         return NULL;
556
557     full_name = merge_ns(ns, attrname, &newname);
558     /* Set the attribute's value */
559     nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
560     if(newname != NULL)
561         PyMem_Free(newname);
562     if(nret == -1) {
563         return PyErr_SetFromErrno(PyExc_IOError);
564     }
565
566     /* Return the result */
567     Py_RETURN_NONE;
568 }
569
570
571 static char __pyremovexattr_doc__[] =
572     "Remove an attribute from a file (deprecated).\n"
573     "\n"
574     "Parameters:\n"
575     "  - a string representing filename, or a file-like object,\n"
576     "    or a file descriptor; this represents the file on \n"
577     "    which to act\n"
578     "  - a string, representing the attribute to be removed;\n"
579     "    usually in form of system.posix_acl or user.mime_type\n"
580     "  - (optional) a boolean value (defaults to false), which, if\n"
581     "    the file name given is a symbolic link, makes the\n"
582     "    function operate on the symbolic link itself instead\n"
583     "    of its target;\n"
584     "@deprecated: since version 0.4, this function has been deprecated\n"
585     "    by the L{remove}"
586     " function\n"
587     ;
588
589 /* Wrapper for removexattr */
590 static PyObject *
591 pyremovexattr(PyObject *self, PyObject *args)
592 {
593     PyObject *myarg;
594     int nofollow=0;
595     char *attrname;
596     int nret;
597     target_t tgt;
598
599     /* Parse the arguments */
600     if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
601         return NULL;
602
603     if(!convertObj(myarg, &tgt, nofollow))
604         return NULL;
605
606     /* Remove the attribute */
607     if((nret = _remove_obj(&tgt, attrname)) == -1) {
608         return PyErr_SetFromErrno(PyExc_IOError);
609     }
610
611     /* Return the result */
612     Py_RETURN_NONE;
613 }
614
615 static char __remove_doc__[] =
616     "Remove an attribute from a file.\n"
617     "\n"
618     "Example:\n"
619     "    >>> xattr.remove('/path/to/file', 'user.comment')\n"
620     "\n"
621     "@param item: the item to query; either a string representing the\n"
622     "    filename, or a file-like object, or a file descriptor\n"
623     "@param name: the attribute whose value to set; usually in form of\n"
624     "    system.posix_acl or user.mime_type\n"
625     "@type name: string\n"
626     "@param nofollow: if given and True, and the function is passed a\n"
627     "    filename that points to a symlink, the function will act on the\n"
628     "    symlink itself instead of its target\n"
629     "@type nofollow: boolean\n"
630     "@param namespace: if given, the attribute must not contain the\n"
631     "    namespace itself, but instead the namespace will be taken from\n"
632     "    this parameter\n"
633     "@type namespace: string\n"
634     "@since: 0.4\n"
635     "@rtype: None\n"
636     "@raise EnvironmentError: caused by any system errors\n"
637     ;
638
639 /* Wrapper for removexattr */
640 static PyObject *
641 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
642 {
643     PyObject *myarg;
644     int nofollow=0;
645     char *attrname, *name_buf;
646     char *ns = NULL;
647     const char *full_name;
648     int nret;
649     target_t tgt;
650     static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
651
652     /* Parse the arguments */
653     if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist,
654                                      &myarg, &attrname, &nofollow, &ns))
655         return NULL;
656
657     if(!convertObj(myarg, &tgt, nofollow))
658         return NULL;
659     full_name = merge_ns(ns, attrname, &name_buf);
660     if(full_name == NULL)
661         return NULL;
662
663     /* Remove the attribute */
664     nret = _remove_obj(&tgt, full_name);
665     PyMem_Free(name_buf);
666     if(nret == -1) {
667         return PyErr_SetFromErrno(PyExc_IOError);
668     }
669
670     /* Return the result */
671     Py_RETURN_NONE;
672 }
673
674 static char __pylistxattr_doc__[] =
675     "Return the list of attribute names for a file (deprecated).\n"
676     "\n"
677     "Parameters:\n"
678     "  - a string representing filename, or a file-like object,\n"
679     "    or a file descriptor; this represents the file to \n"
680     "    be queried\n"
681     "  - (optional) a boolean value (defaults to false), which, if\n"
682     "    the file name given is a symbolic link, makes the\n"
683     "    function operate on the symbolic link itself instead\n"
684     "    of its target;\n"
685     "@deprecated: since version 0.4, this function has been deprecated\n"
686     "    by the L{list}"
687     " function\n"
688     ;
689
690 /* Wrapper for listxattr */
691 static PyObject *
692 pylistxattr(PyObject *self, PyObject *args)
693 {
694     char *buf;
695     int nofollow=0;
696     ssize_t nalloc, nret;
697     PyObject *myarg;
698     PyObject *mylist;
699     Py_ssize_t nattrs;
700     char *s;
701     target_t tgt;
702
703     /* Parse the arguments */
704     if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
705         return NULL;
706     if(!convertObj(myarg, &tgt, nofollow))
707         return NULL;
708
709     /* Find out the needed size of the buffer */
710     if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
711         return PyErr_SetFromErrno(PyExc_IOError);
712     }
713
714     /* Try to allocate the memory, using Python's allocator */
715     if((buf = PyMem_Malloc(nalloc)) == NULL) {
716         PyErr_NoMemory();
717         return NULL;
718     }
719
720     /* Now retrieve the list of attributes */
721     if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
722         PyMem_Free(buf);
723         return PyErr_SetFromErrno(PyExc_IOError);
724     }
725
726     /* Compute the number of attributes in the list */
727     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
728         nattrs++;
729     }
730
731     /* Create the list which will hold the result */
732     mylist = PyList_New(nattrs);
733
734     /* Create and insert the attributes as strings in the list */
735     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
736         PyList_SET_ITEM(mylist, nattrs, PyString_FromString(s));
737         nattrs++;
738     }
739
740     /* Free the buffer, now it is no longer needed */
741     PyMem_Free(buf);
742
743     /* Return the result */
744     return mylist;
745 }
746
747 static char __list_doc__[] =
748     "Return the list of attribute names for a file.\n"
749     "\n"
750     "Example:\n"
751     "    >>> xattr.list('/path/to/file')\n"
752     "    ['user.test', 'user.comment', 'system.posix_acl_access']\n"
753     "    >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
754     "    ['test', 'comment']\n"
755     "\n"
756     "@param item: the item to query; either a string representing the\n"
757     "    filename, or a file-like object, or a file descriptor\n"
758     "@param nofollow: if given and True, and the function is passed a\n"
759     "    filename that points to a symlink, the function will act on the\n"
760     "    symlink itself instead of its target\n"
761     "@type nofollow: boolean\n"
762     "@param namespace: if given, the attribute must not contain the\n"
763     "    namespace itself, but instead the namespace will be taken from\n"
764     "    this parameter\n"
765     "@type namespace: string\n"
766     "@return: list of strings; note that if a namespace argument was\n"
767     "    passed, it (and the separator) will be stripped from the names\n"
768     "    returned\n"
769     "@rtype: list\n"
770     "@raise EnvironmentError: caused by any system errors\n"
771     "@since: 0.4\n"
772     ;
773
774 /* Wrapper for listxattr */
775 static PyObject *
776 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
777 {
778     char *buf;
779     int nofollow=0;
780     ssize_t nalloc, nret;
781     PyObject *myarg;
782     PyObject *mylist;
783     char *ns = NULL;
784     Py_ssize_t nattrs;
785     char *s;
786     target_t tgt;
787     static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
788
789     /* Parse the arguments */
790     if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
791                           &myarg, &nofollow, &ns))
792         return NULL;
793     if(!convertObj(myarg, &tgt, nofollow))
794         return NULL;
795
796     /* Find out the needed size of the buffer */
797     if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
798         return PyErr_SetFromErrno(PyExc_IOError);
799     }
800
801     /* Try to allocate the memory, using Python's allocator */
802     if((buf = PyMem_Malloc(nalloc)) == NULL) {
803         PyErr_NoMemory();
804         return NULL;
805     }
806
807     /* Now retrieve the list of attributes */
808     if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
809         PyMem_Free(buf);
810         return PyErr_SetFromErrno(PyExc_IOError);
811     }
812
813     /* Compute the number of attributes in the list */
814     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
815         if(matches_ns(ns, s)!=NULL)
816             nattrs++;
817     }
818     /* Create the list which will hold the result */
819     mylist = PyList_New(nattrs);
820
821     /* Create and insert the attributes as strings in the list */
822     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
823         const char *name = matches_ns(ns, s);
824         if(name!=NULL) {
825             PyList_SET_ITEM(mylist, nattrs, PyString_FromString(name));
826             nattrs++;
827         }
828     }
829
830     /* Free the buffer, now it is no longer needed */
831     PyMem_Free(buf);
832
833     /* Return the result */
834     return mylist;
835 }
836
837 static PyMethodDef xattr_methods[] = {
838     {"getxattr",  pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
839     {"get",  (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
840      __get_doc__ },
841     {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
842      __get_all_doc__ },
843     {"setxattr",  pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
844     {"set",  (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
845      __set_doc__ },
846     {"removexattr",  pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
847     {"remove",  (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
848      __remove_doc__ },
849     {"listxattr",  pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
850     {"list",  (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
851      __list_doc__ },
852     {NULL, NULL, 0, NULL}        /* Sentinel */
853 };
854
855 static char __xattr_doc__[] = \
856     "Interface to extended filesystem attributes.\n"
857     "\n"
858     "This module gives access to the extended attributes present\n"
859     "in some operating systems/filesystems. You can list attributes,\n"
860     "get, set and remove them.\n"
861     "\n"
862     "The module exposes two sets of functions:\n"
863     "  - the 'old' L{listxattr}, L{getxattr}, L{setxattr}, L{removexattr}\n"
864     "    functions which are deprecated since version 0.4\n"
865     "  - the new L{list}, L{get}, L{get_all}, L{set}, L{remove} functions\n"
866     "    which expose a namespace-aware API and simplify a bit the calling\n"
867     "    model by using keyword arguments\n"
868     "\n"
869     "Example: \n\n"
870     "  >>> import xattr\n"
871     "  >>> xattr.listxattr(\"file.txt\")\n"
872     "  ['user.mime_type']\n"
873     "  >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
874     "  'text/plain'\n"
875     "  >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
876     "\"Simple text file\")\n"
877     "  >>> xattr.listxattr(\"file.txt\")\n"
878     "  ['user.mime_type', 'user.comment']\n"
879     "  >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
880     "\n"
881     "@note: Most or all errors reported by the system while using the xattr\n"
882     "library will be reported by raising a L{EnvironmentError}; under Linux,\n"
883     "the following C{errno} values are used:\n"
884     "  - C{ENOATTR} and C{ENODATA} mean that the attribute name is invalid\n"
885     "  - C{ENOTSUP} and C{EOPNOTSUPP} mean that the filesystem does not\n"
886     "    support extended attributes, or that the namespace is invalid\n"
887     "  - C{E2BIG} mean that the attribute value is too big\n"
888     "  - C{ERANGE} mean that the attribute name is too big (it might also\n"
889     "    mean an error in the xattr module itself)\n"
890     "  - C{ENOSPC} and C{EDQUOT} are documented as meaning out of disk space\n"
891     "    or out of disk space because of quota limits\n"
892     "\n"
893     "@group Deprecated API: *xattr\n"
894     "@group Namespace constants: NS_*\n"
895     "@group set function flags: XATTR_CREATE, XATTR_REPLACE\n"
896     "@sort: list, get, get_all, set, remove, listxattr, getxattr, setxattr\n"
897     "    removexattr\n"
898     ;
899
900 void
901 initxattr(void)
902 {
903     PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
904
905     PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
906     PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
907     PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
908     PyModule_AddStringConstant(m, "__license__",
909                                "GNU Lesser General Public License (LGPL)");
910     PyModule_AddStringConstant(m, "__docformat__", "epytext en");
911
912     PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
913     PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
914
915     /* namespace constants */
916     PyModule_AddStringConstant(m, "NS_SECURITY", "security");
917     PyModule_AddStringConstant(m, "NS_SYSTEM", "system");
918     PyModule_AddStringConstant(m, "NS_TRUSTED", "trusted");
919     PyModule_AddStringConstant(m, "NS_USER", "user");
920
921 }