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