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