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