2 xattr - a python module for manipulating filesystem extended attributes
4 Copyright (C) 2002, 2003, 2006, 2008, 2012, 2013, 2015
5 Iustin Pop <iustin@k1024.org>
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.
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.
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
24 #define PY_SSIZE_T_CLEAN
26 #if defined(__APPLE__)
27 #include <sys/xattr.h>
28 #elif defined(__linux__)
29 #include <attr/xattr.h>
33 /* Compatibility with python 2.4 regarding python size type (PEP 353) */
34 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
35 typedef int Py_ssize_t;
36 #define PY_SSIZE_T_MAX INT_MAX
37 #define PY_SSIZE_T_MIN INT_MIN
40 #if PY_MAJOR_VERSION >= 3
42 #define BYTES_CHAR "y"
44 #define BYTES_CHAR "s"
45 #define PyBytes_Check PyString_Check
46 #define PyBytes_AS_STRING PyString_AS_STRING
47 #define PyBytes_FromStringAndSize PyString_FromStringAndSize
48 #define PyBytes_FromString PyString_FromString
52 ":param item: a string representing a file-name, or a file-like\n" \
53 " object, or a file descriptor; this represents the file on \n" \
56 #define NOFOLLOW_DOC \
57 ":param nofollow: if true and if\n" \
58 " the file name given is a symbolic link, the\n" \
59 " function will operate on the symbolic link itself instead\n" \
60 " of its target; defaults to false\n" \
61 ":type nofollow: boolean, optional\n" \
64 ":param namespace: if given, the attribute must not contain the\n" \
65 " namespace, but instead it will be taken from this parameter\n" \
66 ":type namespace: bytes\n"
68 #define NAME_GET_DOC \
69 ":param string name: the attribute whose value to retrieve;\n" \
70 " usually in the form of ``system.posix_acl`` or ``user.mime_type``\n"
72 #define NAME_SET_DOC \
73 ":param string name: the attribute whose value to set;\n" \
74 " usually in the form of ``system.posix_acl`` or ``user.mime_type``\n"
76 #define NAME_REMOVE_DOC \
77 ":param string name: the attribute to remove;\n" \
78 " usually in the form of ``system.posix_acl`` or \n" \
79 " ``user.mime_type``\n"
82 ":param string value: possibly with embedded NULLs; note that there\n" \
83 " are restrictions regarding the size of the value, for\n" \
84 " example, for ext2/ext3, maximum size is the block size\n" \
87 ":param flags: if 0 or omitted the attribute will be\n" \
88 " created or replaced; if :const:`XATTR_CREATE`, the attribute\n" \
89 " will be created, giving an error if it already exists;\n" \
90 " if :const:`XATTR_REPLACE`, the attribute will be replaced,\n" \
91 " giving an error if it doesn't exist;\n" \
92 ":type flags: integer\n"
94 #define NS_CHANGED_DOC \
95 ".. versionchanged:: 0.5.1\n" \
96 " The namespace argument, if passed, cannot be None anymore; to\n" \
97 " explicitly specify an empty namespace, pass an empty\n" \
98 " string (byte string under Python 3)."
101 /* the estimated (startup) attribute buffer size in
103 #define ESTIMATE_ATTR_SIZE 256
105 typedef enum {T_FD, T_PATH, T_LINK} target_e;
116 /* Cleans up a tgt structure */
117 static void free_tgt(target_t *tgt) {
118 if (tgt->tmp != NULL) {
123 /* Used for cpychecker: */
124 /* The checker automatically defines this preprocessor name when creating
125 the custom attribute: */
126 #if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
127 #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION \
128 __attribute__((cpychecker_negative_result_sets_exception))
130 #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
133 static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow)
134 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
136 static int merge_ns(const char *ns, const char *name,
137 const char **result, char **buf)
138 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
141 /** Converts from a string, file or int argument to what we need.
143 * Returns -1 on failure, 0 on success.
145 static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow) {
148 if(PyBytes_Check(myobj)) {
149 tgt->type = nofollow ? T_LINK : T_PATH;
150 tgt->name = PyBytes_AS_STRING(myobj);
151 } else if(PyUnicode_Check(myobj)) {
152 tgt->type = nofollow ? T_LINK : T_PATH;
154 PyUnicode_AsEncodedString(myobj,
155 Py_FileSystemDefaultEncoding,
164 tgt->name = PyBytes_AS_STRING(tgt->tmp);
165 } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
169 PyErr_SetString(PyExc_TypeError, "argument must be string or int");
175 /* Combine a namespace string and an attribute name into a
176 fully-qualified name */
177 static int merge_ns(const char *ns, const char *name,
178 const char **result, char **buf) {
179 if(ns != NULL && *ns != '\0') {
181 /* The value of new_size is related to/must be kept in-sync
182 with the format string below */
183 size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
184 if((*buf = PyMem_Malloc(new_size)) == NULL) {
188 cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
189 if((size_t) cnt >= new_size || cnt < 0) {
190 PyErr_SetString(PyExc_ValueError,
191 "unexpected: can't format the attribute name");
203 #if defined(__APPLE__)
204 static inline ssize_t _listxattr(const char *path, char *namebuf, size_t size) {
205 return listxattr(path, namebuf, size, 0);
207 static inline ssize_t _llistxattr(const char *path, char *namebuf, size_t size) {
208 return listxattr(path, namebuf, size, XATTR_NOFOLLOW);
210 static inline ssize_t _flistxattr(int fd, char *namebuf, size_t size) {
211 return flistxattr(fd, namebuf, size, 0);
214 static inline ssize_t _getxattr (const char *path, const char *name, void *value, size_t size) {
215 return getxattr(path, name, value, size, 0, 0);
217 static inline ssize_t _lgetxattr (const char *path, const char *name, void *value, size_t size) {
218 return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
220 static inline ssize_t _fgetxattr (int filedes, const char *name, void *value, size_t size) {
221 return fgetxattr(filedes, name, value, size, 0, 0);
224 // [fl]setxattr: Both OS X and Linux define XATTR_CREATE and XATTR_REPLACE for the last option.
225 static inline int _setxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
226 return setxattr(path, name, value, size, 0, flags);
228 static inline int _lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
229 return setxattr(path, name, value, size, 0, flags & XATTR_NOFOLLOW);
231 static inline int _fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) {
232 return fsetxattr(filedes, name, value, size, 0, flags);
235 static inline int _removexattr(const char *path, const char *name) {
236 return removexattr(path, name, 0);
238 static inline int _lremovexattr(const char *path, const char *name) {
239 return removexattr(path, name, XATTR_NOFOLLOW);
241 static inline int _fremovexattr(int filedes, const char *name) {
242 return fremovexattr(filedes, name, 0);
245 #elif defined(__linux__)
246 #define _listxattr(path, list, size) listxattr(path, list, size)
247 #define _llistxattr(path, list, size) llistxattr(path, list, size)
248 #define _flistxattr(fd, list, size) flistxattr(fd, list, size)
250 #define _getxattr(path, name, value, size) getxattr(path, name, value, size)
251 #define _lgetxattr(path, name, value, size) lgetxattr(path, name, value, size)
252 #define _fgetxattr(fd, name, value, size) fgetxattr(fd, name, value, size)
254 #define _setxattr(path, name, value, size, flags) setxattr(path, name, value, size, flags)
255 #define _lsetxattr(path, name, value, size, flags) lsetxattr(path, name, value, size, flags)
256 #define _fsetxattr(fd, name, value, size, flags) fsetxattr(fd, name, value, size, flags)
258 #define _removexattr(path, name) removexattr(path, name)
259 #define _lremovexattr(path, name) lremovexattr(path, name)
260 #define _fremovexattr(fd, name) fremovexattr(fd, name)
264 static ssize_t _list_obj(target_t *tgt, char *list, size_t size) {
265 if(tgt->type == T_FD)
266 return _flistxattr(tgt->fd, list, size);
267 else if (tgt->type == T_LINK)
268 return _llistxattr(tgt->name, list, size);
270 return _listxattr(tgt->name, list, size);
273 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
275 if(tgt->type == T_FD)
276 return _fgetxattr(tgt->fd, name, value, size);
277 else if (tgt->type == T_LINK)
278 return _lgetxattr(tgt->name, name, value, size);
280 return _getxattr(tgt->name, name, value, size);
283 static int _set_obj(target_t *tgt, const char *name,
284 const void *value, size_t size, int flags) {
285 if(tgt->type == T_FD)
286 return _fsetxattr(tgt->fd, name, value, size, flags);
287 else if (tgt->type == T_LINK)
288 return _lsetxattr(tgt->name, name, value, size, flags);
290 return _setxattr(tgt->name, name, value, size, flags);
293 static int _remove_obj(target_t *tgt, const char *name) {
294 if(tgt->type == T_FD)
295 return _fremovexattr(tgt->fd, name);
296 else if (tgt->type == T_LINK)
297 return _lremovexattr(tgt->name, name);
299 return _removexattr(tgt->name, name);
303 Checks if an attribute name matches an optional namespace.
305 If the namespace is NULL or an empty string, it will return the
306 name itself. If the namespace is non-NULL and the name matches, it
307 will return a pointer to the offset in the name after the namespace
308 and the separator. If however the name doesn't match the namespace,
312 const char *matches_ns(const char *ns, const char *name) {
314 if (ns == NULL || *ns == '\0')
316 ns_size = strlen(ns);
318 if (strlen(name) > (ns_size+1) && !strncmp(name, ns, ns_size) &&
319 name[ns_size] == '.')
320 return name + ns_size + 1;
324 /* Wrapper for getxattr */
325 static char __pygetxattr_doc__[] =
326 "getxattr(item, attribute[, nofollow=False])\n"
327 "Get the value of a given extended attribute (deprecated).\n"
333 ".. deprecated:: 0.4\n"
334 " this function has been deprecated\n"
335 " by the :func:`get` function.\n"
339 pygetxattr(PyObject *self, PyObject *args)
344 char *attrname = NULL;
346 ssize_t nalloc_s, nret;
350 /* Parse the arguments */
351 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
353 if(convert_obj(myarg, &tgt, nofollow) < 0) {
358 /* Find out the needed size of the buffer */
359 if((nalloc_s = _get_obj(&tgt, attrname, NULL, 0)) == -1) {
360 res = PyErr_SetFromErrno(PyExc_IOError);
364 nalloc = (size_t) nalloc_s;
366 /* Try to allocate the memory, using Python's allocator */
367 if((buf = PyMem_Malloc(nalloc)) == NULL) {
368 res = PyErr_NoMemory();
372 /* Now retrieve the attribute value */
373 if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) {
374 res = PyErr_SetFromErrno(PyExc_IOError);
378 /* Create the string which will hold the result */
379 res = PyBytes_FromStringAndSize(buf, nret);
382 /* Free the buffer, now it is no longer needed */
387 PyMem_Free(attrname);
389 /* Return the result */
393 /* Wrapper for getxattr */
394 static char __get_doc__[] =
395 "get(item, name[, nofollow=False, namespace=None])\n"
396 "Get the value of a given extended attribute.\n"
399 " >>> xattr.get('/path/to/file', 'user.comment')\n"
401 " >>> xattr.get('/path/to/file', 'comment', namespace=xattr.NS_USER)\n"
408 ":return: the value of the extended attribute (can contain NULLs)\n"
410 ":raises EnvironmentError: caused by any system errors\n"
412 ".. versionadded:: 0.4\n"
417 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
422 char *attrname = NULL, *namebuf;
423 const char *fullname;
425 const char *ns = NULL;
426 ssize_t nalloc_s, nret;
429 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
431 /* Parse the arguments */
432 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist,
433 &myarg, NULL, &attrname, &nofollow, &ns))
435 if(convert_obj(myarg, &tgt, nofollow) < 0) {
440 if(merge_ns(ns, attrname, &fullname, &namebuf) < 0) {
445 /* Find out the needed size of the buffer */
446 if((nalloc_s = _get_obj(&tgt, fullname, NULL, 0)) == -1) {
447 res = PyErr_SetFromErrno(PyExc_IOError);
451 nalloc = (size_t) nalloc_s;
453 /* Try to allocate the memory, using Python's allocator */
454 if((buf = PyMem_Malloc(nalloc)) == NULL) {
455 res = PyErr_NoMemory();
459 /* Now retrieve the attribute value */
460 if((nret = _get_obj(&tgt, fullname, buf, nalloc)) == -1) {
461 res = PyErr_SetFromErrno(PyExc_IOError);
465 /* Create the string which will hold the result */
466 res = PyBytes_FromStringAndSize(buf, nret);
468 /* Free the buffers, they are no longer needed */
476 PyMem_Free(attrname);
478 /* Return the result */
482 /* Wrapper for getxattr */
483 static char __get_all_doc__[] =
484 "get_all(item[, nofollow=False, namespace=None])\n"
485 "Get all the extended attributes of an item.\n"
487 "This function performs a bulk-get of all extended attribute names\n"
488 "and the corresponding value.\n"
491 " >>> xattr.get_all('/path/to/file')\n"
492 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),\n"
493 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
494 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
495 " [('mime-type', 'plain/text'), ('comment', 'test')]\n"
498 ":keyword namespace: an optional namespace for filtering the\n"
499 " attributes; for example, querying all user attributes can be\n"
500 " accomplished by passing namespace=:const:`NS_USER`\n"
501 ":type namespace: string\n"
503 ":return: list of tuples (name, value); note that if a namespace\n"
504 " argument was passed, it (and the separator) will be stripped from\n"
505 " the names returned\n"
507 ":raises EnvironmentError: caused by any system errors\n"
509 ".. note:: Since reading the whole attribute list is not an atomic\n"
510 " operation, it might be possible that attributes are added\n"
511 " or removed between the initial query and the actual reading\n"
512 " of the attributes; the returned list will contain only the\n"
513 " attributes that were present at the initial listing of the\n"
514 " attribute names and that were still present when the read\n"
515 " attempt for the value is made.\n"
516 ".. versionadded:: 0.4\n"
521 get_all(PyObject *self, PyObject *args, PyObject *keywds)
523 PyObject *myarg, *res;
525 const char *ns = NULL;
526 char *buf_list, *buf_val, *buf_val_tmp;
528 ssize_t nalloc_s, nlist, nval_s;
532 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
534 /* Parse the arguments */
535 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist,
536 &myarg, &nofollow, &ns))
538 if(convert_obj(myarg, &tgt, nofollow) < 0)
541 /* Compute first the list of attributes */
543 /* Find out the needed size of the buffer for the attribute list */
544 nalloc_s = _list_obj(&tgt, NULL, 0);
547 res = PyErr_SetFromErrno(PyExc_IOError);
556 nalloc = (size_t) nalloc_s;
558 /* Try to allocate the memory, using Python's allocator */
559 if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
560 res = PyErr_NoMemory();
564 /* Now retrieve the list of attributes */
565 nlist = _list_obj(&tgt, buf_list, nalloc);
568 res = PyErr_SetFromErrno(PyExc_IOError);
572 /* Create the list which will hold the result */
573 mylist = PyList_New(0);
579 nalloc = ESTIMATE_ATTR_SIZE;
580 if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
582 res = PyErr_NoMemory();
586 /* Create and insert the attributes as strings in the list */
587 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
592 if((name=matches_ns(ns, s))==NULL)
594 /* Now retrieve the attribute value */
597 nval_s = _get_obj(&tgt, s, buf_val, nalloc);
600 if(errno == ERANGE) {
601 ssize_t realloc_size_s = _get_obj(&tgt, s, NULL, 0);
602 /* ERANGE + proper size should not fail, but it
603 still can, so let's check first */
604 if(realloc_size_s == -1) {
605 res = PyErr_SetFromErrno(PyExc_IOError);
609 size_t realloc_size = (size_t) realloc_size_s;
610 if((buf_val_tmp = PyMem_Realloc(buf_val, realloc_size))
612 res = PyErr_NoMemory();
616 buf_val = buf_val_tmp;
617 nalloc = realloc_size;
624 /* this attribute has gone away since we queried
625 the attribute list */
629 /* else we're dealing with a different error, which we
630 don't know how to handle nicely, so we abort */
632 res = PyErr_SetFromErrno(PyExc_IOError);
635 nval = (size_t) nval_s;
642 my_tuple = Py_BuildValue("yy#", name, buf_val, nval);
644 my_tuple = Py_BuildValue("ss#", name, buf_val, nval);
646 if (my_tuple == NULL) {
651 PyList_Append(mylist, my_tuple);
655 /* Successful exit */
662 PyMem_Free(buf_list);
667 /* Return the result */
672 static char __pysetxattr_doc__[] =
673 "setxattr(item, name, value[, flags=0, nofollow=False])\n"
674 "Set the value of a given extended attribute (deprecated).\n"
676 "Be careful in case you want to set attributes on symbolic\n"
677 "links, you have to use all the 5 parameters; use 0 for the \n"
678 "flags value if you want the default behaviour (create or "
687 ".. deprecated:: 0.4\n"
688 " this function has been deprecated\n"
689 " by the :func:`set` function.\n"
692 /* Wrapper for setxattr */
694 pysetxattr(PyObject *self, PyObject *args)
696 PyObject *myarg, *res;
698 char *attrname = NULL;
700 Py_ssize_t bufsize_s;
706 /* Parse the arguments */
707 if (!PyArg_ParseTuple(args, "Oetet#|ii", &myarg, NULL, &attrname,
708 NULL, &buf, &bufsize_s, &flags, &nofollow))
712 PyErr_SetString(PyExc_ValueError,
713 "negative value size?!");
717 bufsize = (size_t) bufsize_s;
719 if(convert_obj(myarg, &tgt, nofollow) < 0) {
724 /* Set the attribute's value */
725 nret = _set_obj(&tgt, attrname, buf, bufsize, flags);
730 res = PyErr_SetFromErrno(PyExc_IOError);
738 PyMem_Free(attrname);
741 /* Return the result */
745 static char __set_doc__[] =
746 "set(item, name, value[, flags=0, namespace=None])\n"
747 "Set the value of a given extended attribute.\n"
751 " >>> xattr.set('/path/to/file', 'user.comment', 'test')\n"
752 " >>> xattr.set('/path/to/file', 'comment', 'test',"
753 " namespace=xattr.NS_USER)\n"
762 ":raises EnvironmentError: caused by any system errors\n"
764 ".. versionadded:: 0.4\n"
768 /* Wrapper for setxattr */
770 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
772 PyObject *myarg, *res;
774 char *attrname = NULL;
776 Py_ssize_t bufsize_s;
781 const char *ns = NULL;
783 const char *full_name;
784 static char *kwlist[] = {"item", "name", "value", "flags",
785 "nofollow", "namespace", NULL};
787 /* Parse the arguments */
788 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oetet#|ii" BYTES_CHAR,
789 kwlist, &myarg, NULL, &attrname, NULL,
790 &buf, &bufsize_s, &flags, &nofollow, &ns))
794 PyErr_SetString(PyExc_ValueError,
795 "negative value size?!");
799 bufsize = (size_t) bufsize_s;
801 if(convert_obj(myarg, &tgt, nofollow) < 0) {
806 if(merge_ns(ns, attrname, &full_name, &newname) < 0) {
811 /* Set the attribute's value */
812 nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
819 res = PyErr_SetFromErrno(PyExc_IOError);
827 PyMem_Free(attrname);
830 /* Return the result */
835 static char __pyremovexattr_doc__[] =
836 "removexattr(item, name[, nofollow])\n"
837 "Remove an attribute from a file (deprecated).\n"
843 ".. deprecated:: 0.4\n"
844 " this function has been deprecated by the :func:`remove` function.\n"
847 /* Wrapper for removexattr */
849 pyremovexattr(PyObject *self, PyObject *args)
851 PyObject *myarg, *res;
853 char *attrname = NULL;
857 /* Parse the arguments */
858 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
861 if(convert_obj(myarg, &tgt, nofollow) < 0) {
866 /* Remove the attribute */
867 nret = _remove_obj(&tgt, attrname);
872 res = PyErr_SetFromErrno(PyExc_IOError);
880 PyMem_Free(attrname);
882 /* Return the result */
886 static char __remove_doc__[] =
887 "remove(item, name[, nofollow=False, namespace=None])\n"
888 "Remove an attribute from a file.\n"
892 " >>> xattr.remove('/path/to/file', 'user.comment')\n"
899 ":raises EnvironmentError: caused by any system errors\n"
901 ".. versionadded:: 0.4\n"
905 /* Wrapper for removexattr */
907 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
909 PyObject *myarg, *res;
911 char *attrname = NULL, *name_buf;
912 const char *ns = NULL;
913 const char *full_name;
916 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
918 /* Parse the arguments */
919 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist,
920 &myarg, NULL, &attrname, &nofollow, &ns))
923 if(convert_obj(myarg, &tgt, nofollow) < 0) {
928 if(merge_ns(ns, attrname, &full_name, &name_buf) < 0) {
933 /* Remove the attribute */
934 nret = _remove_obj(&tgt, full_name);
936 PyMem_Free(name_buf);
941 res = PyErr_SetFromErrno(PyExc_IOError);
949 PyMem_Free(attrname);
951 /* Return the result */
955 static char __pylistxattr_doc__[] =
956 "listxattr(item[, nofollow=False])\n"
957 "Return the list of attribute names for a file (deprecated).\n"
962 ".. deprecated:: 0.4\n"
963 " this function has been deprecated by the :func:`list` function.\n"
966 /* Wrapper for listxattr */
968 pylistxattr(PyObject *self, PyObject *args)
972 ssize_t nalloc_s, nret;
980 /* Parse the arguments */
981 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
983 if(convert_obj(myarg, &tgt, nofollow) < 0)
986 /* Find out the needed size of the buffer */
987 if((nalloc_s = _list_obj(&tgt, NULL, 0)) == -1) {
988 mylist = PyErr_SetFromErrno(PyExc_IOError);
993 mylist = PyList_New(0);
997 nalloc = (size_t) nalloc_s;
999 /* Try to allocate the memory, using Python's allocator */
1000 if((buf = PyMem_Malloc(nalloc)) == NULL) {
1001 mylist = PyErr_NoMemory();
1005 /* Now retrieve the list of attributes */
1006 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
1007 mylist = PyErr_SetFromErrno(PyExc_IOError);
1011 /* Compute the number of attributes in the list */
1012 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
1016 /* Create the list which will hold the result */
1017 mylist = PyList_New(nattrs);
1021 /* Create and insert the attributes as strings in the list */
1022 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1023 PyObject *item = PyBytes_FromString(s);
1029 PyList_SET_ITEM(mylist, nattrs, item);
1034 /* Free the buffer, now it is no longer needed */
1040 /* Return the result */
1044 static char __list_doc__[] =
1045 "list(item[, nofollow=False, namespace=None])\n"
1046 "Return the list of attribute names for a file.\n"
1050 " >>> xattr.list('/path/to/file')\n"
1051 " ['user.test', 'user.comment', 'system.posix_acl_access']\n"
1052 " >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
1053 " ['test', 'comment']\n"
1058 ":returns: the list of attributes; note that if a namespace \n"
1059 " argument was passed, it (and the separator) will be stripped\n"
1063 ":raises EnvironmentError: caused by any system errors\n"
1065 ".. versionadded:: 0.4\n"
1069 /* Wrapper for listxattr */
1071 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
1075 ssize_t nalloc_s, nret;
1079 const char *ns = NULL;
1083 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
1085 /* Parse the arguments */
1086 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist,
1087 &myarg, &nofollow, &ns))
1089 if(convert_obj(myarg, &tgt, nofollow) < 0) {
1094 /* Find out the needed size of the buffer */
1095 if((nalloc_s = _list_obj(&tgt, NULL, 0)) == -1) {
1096 res = PyErr_SetFromErrno(PyExc_IOError);
1101 res = PyList_New(0);
1105 nalloc = (size_t) nalloc_s;
1107 /* Try to allocate the memory, using Python's allocator */
1108 if((buf = PyMem_Malloc(nalloc)) == NULL) {
1109 res = PyErr_NoMemory();
1113 /* Now retrieve the list of attributes */
1114 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
1115 res = PyErr_SetFromErrno(PyExc_IOError);
1119 /* Compute the number of attributes in the list */
1120 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
1121 if(matches_ns(ns, s) != NULL)
1124 /* Create the list which will hold the result */
1125 res = PyList_New(nattrs);
1129 /* Create and insert the attributes as strings in the list */
1130 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1131 const char *name = matches_ns(ns, s);
1133 PyObject *item = PyBytes_FromString(name);
1139 PyList_SET_ITEM(res, nattrs, item);
1145 /* Free the buffer, now it is no longer needed */
1152 /* Return the result */
1156 static PyMethodDef xattr_methods[] = {
1157 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
1158 {"get", (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
1160 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
1162 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
1163 {"set", (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
1165 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
1166 {"remove", (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
1168 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
1169 {"list", (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
1171 {NULL, NULL, 0, NULL} /* Sentinel */
1174 static char __xattr_doc__[] = \
1175 "This module gives access to the extended attributes present\n"
1176 "in some operating systems/filesystems. You can list attributes,\n"
1177 "get, set and remove them.\n"
1179 "The module exposes two sets of functions:\n"
1180 " - the 'old' :func:`listxattr`, :func:`getxattr`, :func:`setxattr`,\n"
1181 " :func:`removexattr`\n"
1182 " functions which are deprecated since version 0.4\n"
1183 " - the new :func:`list`, :func:`get`, :func:`get_all`, :func:`set`,\n"
1184 " :func:`remove` functions\n"
1185 " which expose a namespace-aware API and simplify a bit the calling\n"
1186 " model by using keyword arguments\n"
1189 " >>> import xattr\n"
1190 " >>> xattr.listxattr(\"file.txt\")\n"
1191 " ['user.mime_type']\n"
1192 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
1194 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
1195 "\"Simple text file\")\n"
1196 " >>> xattr.listxattr(\"file.txt\")\n"
1197 " ['user.mime_type', 'user.comment']\n"
1198 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
1200 ".. note:: Most or all errors reported by the system while using\n"
1201 " the ``xattr`` library will be reported by raising\n"
1202 " a :exc:`EnvironmentError`; under\n"
1203 " Linux, the following ``errno`` values are used:\n"
1205 " - ``ENOATTR`` and ``ENODATA`` mean that the attribute name is\n"
1207 " - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n"
1208 " support extended attributes, or that the namespace is invalid\n"
1209 " - ``E2BIG`` mean that the attribute value is too big\n"
1210 " - ``ERANGE`` mean that the attribute name is too big (it might also\n"
1211 " mean an error in the xattr module itself)\n"
1212 " - ``ENOSPC`` and ``EDQUOT`` are documented as meaning out of disk\n"
1213 " space or out of disk space because of quota limits\n"
1214 ".. note:: Under Python 3, the namespace argument is a byte string,\n"
1215 " not a unicode string.\n"
1221 static struct PyModuleDef xattrmodule = {
1222 PyModuleDef_HEAD_INIT,
1229 #define INITERROR return NULL
1235 #define INITERROR return
1240 PyObject *ns_security = NULL;
1241 PyObject *ns_system = NULL;
1242 PyObject *ns_trusted = NULL;
1243 PyObject *ns_user = NULL;
1245 PyObject *m = PyModule_Create(&xattrmodule);
1247 PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
1252 PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
1253 PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
1254 PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
1255 PyModule_AddStringConstant(m, "__license__",
1256 "GNU Lesser General Public License (LGPL)");
1257 PyModule_AddStringConstant(m, "__docformat__", "restructuredtext en");
1259 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
1260 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
1262 /* namespace constants */
1263 if((ns_security = PyBytes_FromString("security")) == NULL)
1265 if((ns_system = PyBytes_FromString("system")) == NULL)
1267 if((ns_trusted = PyBytes_FromString("trusted")) == NULL)
1269 if((ns_user = PyBytes_FromString("user")) == NULL)
1271 if(PyModule_AddObject(m, "NS_SECURITY", ns_security) < 0)
1274 if(PyModule_AddObject(m, "NS_SYSTEM", ns_system) < 0)
1277 if(PyModule_AddObject(m, "NS_TRUSTED", ns_trusted) < 0)
1280 if(PyModule_AddObject(m, "NS_USER", ns_user) < 0)
1291 Py_XDECREF(ns_user);
1292 Py_XDECREF(ns_trusted);
1293 Py_XDECREF(ns_system);
1294 Py_XDECREF(ns_security);