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