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");
177 /* Combine a namespace string and an attribute name into a
178 fully-qualified name */
179 static int merge_ns(const char *ns, const char *name,
180 const char **result, char **buf) {
181 if(ns != NULL && *ns != '\0') {
183 /* The value of new_size is related to/must be kept in-sync
184 with the format string below */
185 size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
186 if((*buf = PyMem_Malloc(new_size)) == NULL) {
190 cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
191 if((size_t) cnt >= new_size || cnt < 0) {
192 PyErr_SetString(PyExc_ValueError,
193 "unexpected: can't format the attribute name");
205 #if defined(__APPLE__)
206 static inline ssize_t _listxattr(const char *path, char *namebuf, size_t size) {
207 return listxattr(path, namebuf, size, 0);
209 static inline ssize_t _llistxattr(const char *path, char *namebuf, size_t size) {
210 return listxattr(path, namebuf, size, XATTR_NOFOLLOW);
212 static inline ssize_t _flistxattr(int fd, char *namebuf, size_t size) {
213 return flistxattr(fd, namebuf, size, 0);
216 static inline ssize_t _getxattr (const char *path, const char *name, void *value, size_t size) {
217 return getxattr(path, name, value, size, 0, 0);
219 static inline ssize_t _lgetxattr (const char *path, const char *name, void *value, size_t size) {
220 return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
222 static inline ssize_t _fgetxattr (int filedes, const char *name, void *value, size_t size) {
223 return fgetxattr(filedes, name, value, size, 0, 0);
226 // [fl]setxattr: Both OS X and Linux define XATTR_CREATE and XATTR_REPLACE for the last option.
227 static inline int _setxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
228 return setxattr(path, name, value, size, 0, flags);
230 static inline int _lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
231 return setxattr(path, name, value, size, 0, flags & XATTR_NOFOLLOW);
233 static inline int _fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) {
234 return fsetxattr(filedes, name, value, size, 0, flags);
237 static inline int _removexattr(const char *path, const char *name) {
238 return removexattr(path, name, 0);
240 static inline int _lremovexattr(const char *path, const char *name) {
241 return removexattr(path, name, XATTR_NOFOLLOW);
243 static inline int _fremovexattr(int filedes, const char *name) {
244 return fremovexattr(filedes, name, 0);
247 #elif defined(__linux__)
248 #define _listxattr(path, list, size) listxattr(path, list, size)
249 #define _llistxattr(path, list, size) llistxattr(path, list, size)
250 #define _flistxattr(fd, list, size) flistxattr(fd, list, size)
252 #define _getxattr(path, name, value, size) getxattr(path, name, value, size)
253 #define _lgetxattr(path, name, value, size) lgetxattr(path, name, value, size)
254 #define _fgetxattr(fd, name, value, size) fgetxattr(fd, name, value, size)
256 #define _setxattr(path, name, value, size, flags) setxattr(path, name, value, size, flags)
257 #define _lsetxattr(path, name, value, size, flags) lsetxattr(path, name, value, size, flags)
258 #define _fsetxattr(fd, name, value, size, flags) fsetxattr(fd, name, value, size, flags)
260 #define _removexattr(path, name) removexattr(path, name)
261 #define _lremovexattr(path, name) lremovexattr(path, name)
262 #define _fremovexattr(fd, name) fremovexattr(fd, name)
266 static ssize_t _list_obj(target_t *tgt, char *list, size_t size) {
267 if(tgt->type == T_FD)
268 return _flistxattr(tgt->fd, list, size);
269 else if (tgt->type == T_LINK)
270 return _llistxattr(tgt->name, list, size);
272 return _listxattr(tgt->name, list, size);
275 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
277 if(tgt->type == T_FD)
278 return _fgetxattr(tgt->fd, name, value, size);
279 else if (tgt->type == T_LINK)
280 return _lgetxattr(tgt->name, name, value, size);
282 return _getxattr(tgt->name, name, value, size);
285 static int _set_obj(target_t *tgt, const char *name,
286 const void *value, size_t size, int flags) {
287 if(tgt->type == T_FD)
288 return _fsetxattr(tgt->fd, name, value, size, flags);
289 else if (tgt->type == T_LINK)
290 return _lsetxattr(tgt->name, name, value, size, flags);
292 return _setxattr(tgt->name, name, value, size, flags);
295 static int _remove_obj(target_t *tgt, const char *name) {
296 if(tgt->type == T_FD)
297 return _fremovexattr(tgt->fd, name);
298 else if (tgt->type == T_LINK)
299 return _lremovexattr(tgt->name, name);
301 return _removexattr(tgt->name, name);
305 Checks if an attribute name matches an optional namespace.
307 If the namespace is NULL or an empty string, it will return the
308 name itself. If the namespace is non-NULL and the name matches, it
309 will return a pointer to the offset in the name after the namespace
310 and the separator. If however the name doesn't match the namespace,
314 const char *matches_ns(const char *ns, const char *name) {
316 if (ns == NULL || *ns == '\0')
318 ns_size = strlen(ns);
320 if (strlen(name) > (ns_size+1) && !strncmp(name, ns, ns_size) &&
321 name[ns_size] == '.')
322 return name + ns_size + 1;
326 /* Wrapper for getxattr */
327 static char __pygetxattr_doc__[] =
328 "getxattr(item, attribute[, nofollow=False])\n"
329 "Get the value of a given extended attribute (deprecated).\n"
335 ".. deprecated:: 0.4\n"
336 " this function has been deprecated\n"
337 " by the :func:`get` function.\n"
341 pygetxattr(PyObject *self, PyObject *args)
346 char *attrname = NULL;
348 ssize_t nalloc_s, nret;
352 /* Parse the arguments */
353 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
355 if(convert_obj(myarg, &tgt, nofollow) < 0) {
360 /* Find out the needed size of the buffer */
361 if((nalloc_s = _get_obj(&tgt, attrname, NULL, 0)) == -1) {
362 res = PyErr_SetFromErrno(PyExc_IOError);
366 nalloc = (size_t) nalloc_s;
368 /* Try to allocate the memory, using Python's allocator */
369 if((buf = PyMem_Malloc(nalloc)) == NULL) {
370 res = PyErr_NoMemory();
374 /* Now retrieve the attribute value */
375 if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) {
376 res = PyErr_SetFromErrno(PyExc_IOError);
380 /* Create the string which will hold the result */
381 res = PyBytes_FromStringAndSize(buf, nret);
384 /* Free the buffer, now it is no longer needed */
389 PyMem_Free(attrname);
391 /* Return the result */
395 /* Wrapper for getxattr */
396 static char __get_doc__[] =
397 "get(item, name[, nofollow=False, namespace=None])\n"
398 "Get the value of a given extended attribute.\n"
401 " >>> xattr.get('/path/to/file', 'user.comment')\n"
403 " >>> xattr.get('/path/to/file', 'comment', namespace=xattr.NS_USER)\n"
410 ":return: the value of the extended attribute (can contain NULLs)\n"
412 ":raises EnvironmentError: caused by any system errors\n"
414 ".. versionadded:: 0.4\n"
419 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
424 char *attrname = NULL, *namebuf;
425 const char *fullname;
427 const char *ns = NULL;
428 ssize_t nalloc_s, nret;
431 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
433 /* Parse the arguments */
434 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist,
435 &myarg, NULL, &attrname, &nofollow, &ns))
437 if(convert_obj(myarg, &tgt, nofollow) < 0) {
442 if(merge_ns(ns, attrname, &fullname, &namebuf) < 0) {
447 /* Find out the needed size of the buffer */
448 if((nalloc_s = _get_obj(&tgt, fullname, NULL, 0)) == -1) {
449 res = PyErr_SetFromErrno(PyExc_IOError);
453 nalloc = (size_t) nalloc_s;
455 /* Try to allocate the memory, using Python's allocator */
456 if((buf = PyMem_Malloc(nalloc)) == NULL) {
457 res = PyErr_NoMemory();
461 /* Now retrieve the attribute value */
462 if((nret = _get_obj(&tgt, fullname, buf, nalloc)) == -1) {
463 res = PyErr_SetFromErrno(PyExc_IOError);
467 /* Create the string which will hold the result */
468 res = PyBytes_FromStringAndSize(buf, nret);
470 /* Free the buffers, they are no longer needed */
478 PyMem_Free(attrname);
480 /* Return the result */
484 /* Wrapper for getxattr */
485 static char __get_all_doc__[] =
486 "get_all(item[, nofollow=False, namespace=None])\n"
487 "Get all the extended attributes of an item.\n"
489 "This function performs a bulk-get of all extended attribute names\n"
490 "and the corresponding value.\n"
493 " >>> xattr.get_all('/path/to/file')\n"
494 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),\n"
495 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
496 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
497 " [('mime-type', 'plain/text'), ('comment', 'test')]\n"
500 ":keyword namespace: an optional namespace for filtering the\n"
501 " attributes; for example, querying all user attributes can be\n"
502 " accomplished by passing namespace=:const:`NS_USER`\n"
503 ":type namespace: string\n"
505 ":return: list of tuples (name, value); note that if a namespace\n"
506 " argument was passed, it (and the separator) will be stripped from\n"
507 " the names returned\n"
509 ":raises EnvironmentError: caused by any system errors\n"
511 ".. note:: Since reading the whole attribute list is not an atomic\n"
512 " operation, it might be possible that attributes are added\n"
513 " or removed between the initial query and the actual reading\n"
514 " of the attributes; the returned list will contain only the\n"
515 " attributes that were present at the initial listing of the\n"
516 " attribute names and that were still present when the read\n"
517 " attempt for the value is made.\n"
518 ".. versionadded:: 0.4\n"
523 get_all(PyObject *self, PyObject *args, PyObject *keywds)
525 PyObject *myarg, *res;
527 const char *ns = NULL;
528 char *buf_list, *buf_val, *buf_val_tmp;
530 ssize_t nalloc_s, nlist, nval_s;
534 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
536 /* Parse the arguments */
537 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist,
538 &myarg, &nofollow, &ns))
540 if(convert_obj(myarg, &tgt, nofollow) < 0)
543 /* Compute first the list of attributes */
545 /* Find out the needed size of the buffer for the attribute list */
546 nalloc_s = _list_obj(&tgt, NULL, 0);
549 res = PyErr_SetFromErrno(PyExc_IOError);
558 nalloc = (size_t) nalloc_s;
560 /* Try to allocate the memory, using Python's allocator */
561 if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
562 res = PyErr_NoMemory();
566 /* Now retrieve the list of attributes */
567 nlist = _list_obj(&tgt, buf_list, nalloc);
570 res = PyErr_SetFromErrno(PyExc_IOError);
574 /* Create the list which will hold the result */
575 mylist = PyList_New(0);
581 nalloc = ESTIMATE_ATTR_SIZE;
582 if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
584 res = PyErr_NoMemory();
588 /* Create and insert the attributes as strings in the list */
589 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
594 if((name=matches_ns(ns, s))==NULL)
596 /* Now retrieve the attribute value */
599 nval_s = _get_obj(&tgt, s, buf_val, nalloc);
602 if(errno == ERANGE) {
603 ssize_t realloc_size_s = _get_obj(&tgt, s, NULL, 0);
604 /* ERANGE + proper size should not fail, but it
605 still can, so let's check first */
606 if(realloc_size_s == -1) {
607 res = PyErr_SetFromErrno(PyExc_IOError);
611 size_t realloc_size = (size_t) realloc_size_s;
612 if((buf_val_tmp = PyMem_Realloc(buf_val, realloc_size))
614 res = PyErr_NoMemory();
618 buf_val = buf_val_tmp;
619 nalloc = realloc_size;
626 /* this attribute has gone away since we queried
627 the attribute list */
631 /* else we're dealing with a different error, which we
632 don't know how to handle nicely, so we abort */
634 res = PyErr_SetFromErrno(PyExc_IOError);
637 nval = (size_t) nval_s;
644 my_tuple = Py_BuildValue("yy#", name, buf_val, nval);
646 my_tuple = Py_BuildValue("ss#", name, buf_val, nval);
648 if (my_tuple == NULL) {
653 PyList_Append(mylist, my_tuple);
657 /* Successful exit */
664 PyMem_Free(buf_list);
669 /* Return the result */
674 static char __pysetxattr_doc__[] =
675 "setxattr(item, name, value[, flags=0, nofollow=False])\n"
676 "Set the value of a given extended attribute (deprecated).\n"
678 "Be careful in case you want to set attributes on symbolic\n"
679 "links, you have to use all the 5 parameters; use 0 for the \n"
680 "flags value if you want the default behaviour (create or "
689 ".. deprecated:: 0.4\n"
690 " this function has been deprecated\n"
691 " by the :func:`set` function.\n"
694 /* Wrapper for setxattr */
696 pysetxattr(PyObject *self, PyObject *args)
698 PyObject *myarg, *res;
700 char *attrname = NULL;
702 Py_ssize_t bufsize_s;
708 /* Parse the arguments */
709 if (!PyArg_ParseTuple(args, "Oetet#|ii", &myarg, NULL, &attrname,
710 NULL, &buf, &bufsize_s, &flags, &nofollow))
714 PyErr_SetString(PyExc_ValueError,
715 "negative value size?!");
719 bufsize = (size_t) bufsize_s;
721 if(convert_obj(myarg, &tgt, nofollow) < 0) {
726 /* Set the attribute's value */
727 nret = _set_obj(&tgt, attrname, buf, bufsize, flags);
732 res = PyErr_SetFromErrno(PyExc_IOError);
740 PyMem_Free(attrname);
743 /* Return the result */
747 static char __set_doc__[] =
748 "set(item, name, value[, flags=0, namespace=None])\n"
749 "Set the value of a given extended attribute.\n"
753 " >>> xattr.set('/path/to/file', 'user.comment', 'test')\n"
754 " >>> xattr.set('/path/to/file', 'comment', 'test',"
755 " namespace=xattr.NS_USER)\n"
764 ":raises EnvironmentError: caused by any system errors\n"
766 ".. versionadded:: 0.4\n"
770 /* Wrapper for setxattr */
772 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
774 PyObject *myarg, *res;
776 char *attrname = NULL;
778 Py_ssize_t bufsize_s;
783 const char *ns = NULL;
785 const char *full_name;
786 static char *kwlist[] = {"item", "name", "value", "flags",
787 "nofollow", "namespace", NULL};
789 /* Parse the arguments */
790 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oetet#|ii" BYTES_CHAR,
791 kwlist, &myarg, NULL, &attrname, NULL,
792 &buf, &bufsize_s, &flags, &nofollow, &ns))
796 PyErr_SetString(PyExc_ValueError,
797 "negative value size?!");
801 bufsize = (size_t) bufsize_s;
803 if(convert_obj(myarg, &tgt, nofollow) < 0) {
808 if(merge_ns(ns, attrname, &full_name, &newname) < 0) {
813 /* Set the attribute's value */
814 nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
821 res = PyErr_SetFromErrno(PyExc_IOError);
829 PyMem_Free(attrname);
832 /* Return the result */
837 static char __pyremovexattr_doc__[] =
838 "removexattr(item, name[, nofollow])\n"
839 "Remove an attribute from a file (deprecated).\n"
845 ".. deprecated:: 0.4\n"
846 " this function has been deprecated by the :func:`remove` function.\n"
849 /* Wrapper for removexattr */
851 pyremovexattr(PyObject *self, PyObject *args)
853 PyObject *myarg, *res;
855 char *attrname = NULL;
859 /* Parse the arguments */
860 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
863 if(convert_obj(myarg, &tgt, nofollow) < 0) {
868 /* Remove the attribute */
869 nret = _remove_obj(&tgt, attrname);
874 res = PyErr_SetFromErrno(PyExc_IOError);
882 PyMem_Free(attrname);
884 /* Return the result */
888 static char __remove_doc__[] =
889 "remove(item, name[, nofollow=False, namespace=None])\n"
890 "Remove an attribute from a file.\n"
894 " >>> xattr.remove('/path/to/file', 'user.comment')\n"
901 ":raises EnvironmentError: caused by any system errors\n"
903 ".. versionadded:: 0.4\n"
907 /* Wrapper for removexattr */
909 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
911 PyObject *myarg, *res;
913 char *attrname = NULL, *name_buf;
914 const char *ns = NULL;
915 const char *full_name;
918 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
920 /* Parse the arguments */
921 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist,
922 &myarg, NULL, &attrname, &nofollow, &ns))
925 if(convert_obj(myarg, &tgt, nofollow) < 0) {
930 if(merge_ns(ns, attrname, &full_name, &name_buf) < 0) {
935 /* Remove the attribute */
936 nret = _remove_obj(&tgt, full_name);
938 PyMem_Free(name_buf);
943 res = PyErr_SetFromErrno(PyExc_IOError);
951 PyMem_Free(attrname);
953 /* Return the result */
957 static char __pylistxattr_doc__[] =
958 "listxattr(item[, nofollow=False])\n"
959 "Return the list of attribute names for a file (deprecated).\n"
964 ".. deprecated:: 0.4\n"
965 " this function has been deprecated by the :func:`list` function.\n"
968 /* Wrapper for listxattr */
970 pylistxattr(PyObject *self, PyObject *args)
974 ssize_t nalloc_s, nret;
982 /* Parse the arguments */
983 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
985 if(convert_obj(myarg, &tgt, nofollow) < 0)
988 /* Find out the needed size of the buffer */
989 if((nalloc_s = _list_obj(&tgt, NULL, 0)) == -1) {
990 mylist = PyErr_SetFromErrno(PyExc_IOError);
995 mylist = PyList_New(0);
999 nalloc = (size_t) nalloc_s;
1001 /* Try to allocate the memory, using Python's allocator */
1002 if((buf = PyMem_Malloc(nalloc)) == NULL) {
1003 mylist = PyErr_NoMemory();
1007 /* Now retrieve the list of attributes */
1008 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
1009 mylist = PyErr_SetFromErrno(PyExc_IOError);
1013 /* Compute the number of attributes in the list */
1014 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
1018 /* Create the list which will hold the result */
1019 mylist = PyList_New(nattrs);
1023 /* Create and insert the attributes as strings in the list */
1024 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1025 PyObject *item = PyBytes_FromString(s);
1031 PyList_SET_ITEM(mylist, nattrs, item);
1036 /* Free the buffer, now it is no longer needed */
1042 /* Return the result */
1046 static char __list_doc__[] =
1047 "list(item[, nofollow=False, namespace=None])\n"
1048 "Return the list of attribute names for a file.\n"
1052 " >>> xattr.list('/path/to/file')\n"
1053 " ['user.test', 'user.comment', 'system.posix_acl_access']\n"
1054 " >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
1055 " ['test', 'comment']\n"
1060 ":returns: the list of attributes; note that if a namespace \n"
1061 " argument was passed, it (and the separator) will be stripped\n"
1065 ":raises EnvironmentError: caused by any system errors\n"
1067 ".. versionadded:: 0.4\n"
1071 /* Wrapper for listxattr */
1073 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
1077 ssize_t nalloc_s, nret;
1081 const char *ns = NULL;
1085 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
1087 /* Parse the arguments */
1088 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist,
1089 &myarg, &nofollow, &ns))
1091 if(convert_obj(myarg, &tgt, nofollow) < 0) {
1096 /* Find out the needed size of the buffer */
1097 if((nalloc_s = _list_obj(&tgt, NULL, 0)) == -1) {
1098 res = PyErr_SetFromErrno(PyExc_IOError);
1103 res = PyList_New(0);
1107 nalloc = (size_t) nalloc_s;
1109 /* Try to allocate the memory, using Python's allocator */
1110 if((buf = PyMem_Malloc(nalloc)) == NULL) {
1111 res = PyErr_NoMemory();
1115 /* Now retrieve the list of attributes */
1116 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
1117 res = PyErr_SetFromErrno(PyExc_IOError);
1121 /* Compute the number of attributes in the list */
1122 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
1123 if(matches_ns(ns, s) != NULL)
1126 /* Create the list which will hold the result */
1127 res = PyList_New(nattrs);
1131 /* Create and insert the attributes as strings in the list */
1132 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1133 const char *name = matches_ns(ns, s);
1135 PyObject *item = PyBytes_FromString(name);
1141 PyList_SET_ITEM(res, nattrs, item);
1147 /* Free the buffer, now it is no longer needed */
1154 /* Return the result */
1158 static PyMethodDef xattr_methods[] = {
1159 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
1160 {"get", (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
1162 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
1164 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
1165 {"set", (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
1167 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
1168 {"remove", (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
1170 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
1171 {"list", (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
1173 {NULL, NULL, 0, NULL} /* Sentinel */
1176 static char __xattr_doc__[] = \
1177 "This module gives access to the extended attributes present\n"
1178 "in some operating systems/filesystems. You can list attributes,\n"
1179 "get, set and remove them.\n"
1181 "The module exposes two sets of functions:\n"
1182 " - the 'old' :func:`listxattr`, :func:`getxattr`, :func:`setxattr`,\n"
1183 " :func:`removexattr`\n"
1184 " functions which are deprecated since version 0.4\n"
1185 " - the new :func:`list`, :func:`get`, :func:`get_all`, :func:`set`,\n"
1186 " :func:`remove` functions\n"
1187 " which expose a namespace-aware API and simplify a bit the calling\n"
1188 " model by using keyword arguments\n"
1191 " >>> import xattr\n"
1192 " >>> xattr.listxattr(\"file.txt\")\n"
1193 " ['user.mime_type']\n"
1194 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
1196 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
1197 "\"Simple text file\")\n"
1198 " >>> xattr.listxattr(\"file.txt\")\n"
1199 " ['user.mime_type', 'user.comment']\n"
1200 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
1202 ".. note:: Most or all errors reported by the system while using\n"
1203 " the ``xattr`` library will be reported by raising\n"
1204 " a :exc:`EnvironmentError`; under\n"
1205 " Linux, the following ``errno`` values are used:\n"
1207 " - ``ENOATTR`` and ``ENODATA`` mean that the attribute name is\n"
1209 " - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n"
1210 " support extended attributes, or that the namespace is invalid\n"
1211 " - ``E2BIG`` mean that the attribute value is too big\n"
1212 " - ``ERANGE`` mean that the attribute name is too big (it might also\n"
1213 " mean an error in the xattr module itself)\n"
1214 " - ``ENOSPC`` and ``EDQUOT`` are documented as meaning out of disk\n"
1215 " space or out of disk space because of quota limits\n"
1216 ".. note:: Under Python 3, the namespace argument is a byte string,\n"
1217 " not a unicode string.\n"
1223 static struct PyModuleDef xattrmodule = {
1224 PyModuleDef_HEAD_INIT,
1231 #define INITERROR return NULL
1237 #define INITERROR return
1242 PyObject *ns_security = NULL;
1243 PyObject *ns_system = NULL;
1244 PyObject *ns_trusted = NULL;
1245 PyObject *ns_user = NULL;
1247 PyObject *m = PyModule_Create(&xattrmodule);
1249 PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
1254 PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
1255 PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
1256 PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
1257 PyModule_AddStringConstant(m, "__license__",
1258 "GNU Lesser General Public License (LGPL)");
1259 PyModule_AddStringConstant(m, "__docformat__", "restructuredtext en");
1261 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
1262 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
1264 /* namespace constants */
1265 if((ns_security = PyBytes_FromString("security")) == NULL)
1267 if((ns_system = PyBytes_FromString("system")) == NULL)
1269 if((ns_trusted = PyBytes_FromString("trusted")) == NULL)
1271 if((ns_user = PyBytes_FromString("user")) == NULL)
1273 if(PyModule_AddObject(m, "NS_SECURITY", ns_security) < 0)
1276 if(PyModule_AddObject(m, "NS_SYSTEM", ns_system) < 0)
1279 if(PyModule_AddObject(m, "NS_TRUSTED", ns_trusted) < 0)
1282 if(PyModule_AddObject(m, "NS_USER", ns_user) < 0)
1293 Py_XDECREF(ns_user);
1294 Py_XDECREF(ns_trusted);
1295 Py_XDECREF(ns_system);
1296 Py_XDECREF(ns_security);