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__) || defined(__linux__)
27 #include <sys/xattr.h>
32 ":param item: a string representing a file-name, a file-like\n" \
33 " object, a file descriptor, or (in Python 3.6+) a path-like\n" \
34 " object; this represents the file on which to act\n"
36 #define NOFOLLOW_DOC \
37 ":param nofollow: if true and if\n" \
38 " the file name given is a symbolic link, the\n" \
39 " function will operate on the symbolic link itself instead\n" \
40 " of its target; defaults to false\n" \
41 ":type nofollow: boolean, optional\n" \
44 ":param namespace: if given, the attribute must not contain the\n" \
45 " namespace, but instead it will be taken from this parameter\n" \
46 ":type namespace: bytes\n"
48 #define NAME_GET_DOC \
49 ":param string name: the attribute whose value to retrieve;\n" \
50 " usually in the form of ``system.posix_acl`` or ``user.mime_type``\n"
52 #define NAME_SET_DOC \
53 ":param string name: the attribute whose value to set;\n" \
54 " usually in the form of ``system.posix_acl`` or ``user.mime_type``\n"
56 #define NAME_REMOVE_DOC \
57 ":param string name: the attribute to remove;\n" \
58 " usually in the form of ``system.posix_acl`` or \n" \
59 " ``user.mime_type``\n"
62 ":param string value: possibly with embedded NULLs; note that there\n" \
63 " are restrictions regarding the size of the value, for\n" \
64 " example, for ext2/ext3, maximum size is the block size\n" \
67 ":param flags: if 0 or omitted the attribute will be\n" \
68 " created or replaced; if :const:`XATTR_CREATE`, the attribute\n" \
69 " will be created, giving an error if it already exists;\n" \
70 " if :const:`XATTR_REPLACE`, the attribute will be replaced,\n" \
71 " giving an error if it doesn't exist;\n" \
72 ":type flags: integer\n"
74 #define NS_CHANGED_DOC \
75 ".. versionchanged:: 0.5.1\n" \
76 " The namespace argument, if passed, cannot be None anymore; to\n" \
77 " explicitly specify an empty namespace, pass an empty\n" \
78 " string (byte string under Python 3)."
81 /* The initial I/O buffer size for list and get operations; if the
82 * actual values will be smaller than this, we save a syscall out of
83 * two and allocate more memory upfront than needed, otherwise we
84 * incur three syscalls (get with ENORANGE, get with 0 to compute
85 * actual size, final get). The test suite is marginally faster (5%)
86 * with this, so it seems worth doing.
88 #define ESTIMATE_ATTR_SIZE 1024
90 typedef enum {T_FD, T_PATH, T_LINK} target_e;
101 /* Cleans up a tgt structure */
102 static void free_tgt(target_t *tgt) {
103 if (tgt->tmp != NULL) {
108 /* Used for cpychecker: */
109 /* The checker automatically defines this preprocessor name when creating
110 the custom attribute: */
111 #if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
112 #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION \
113 __attribute__((cpychecker_negative_result_sets_exception))
115 #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
118 static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow)
119 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
121 static int merge_ns(const char *ns, const char *name,
122 const char **result, char **buf)
123 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
126 /** Converts from a string, file or int argument to what we need.
128 * Returns -1 on failure, 0 on success.
130 static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow) {
133 if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
138 // PyObject_AsFileDescriptor sets an error when failing, so clear
139 // it such that further code works; some method lookups fail if an
140 // error already occured when called, which breaks at least
141 // PyOS_FSPath (called by FSConverter).
144 if(PyUnicode_FSConverter(myobj, &(tgt->tmp))) {
145 tgt->type = nofollow ? T_LINK : T_PATH;
146 tgt->name = PyBytes_AS_STRING(tgt->tmp);
149 // Don't set our own exception type, since we'd ignore the
150 // FSConverter-generated one.
157 /* Combine a namespace string and an attribute name into a
158 fully-qualified name */
159 static int merge_ns(const char *ns, const char *name,
160 const char **result, char **buf) {
161 if(ns != NULL && *ns != '\0') {
163 /* The value of new_size is related to/must be kept in-sync
164 with the format string below */
165 size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
166 if((*buf = PyMem_Malloc(new_size)) == NULL) {
170 cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
171 if((size_t) cnt >= new_size || cnt < 0) {
172 PyErr_SetString(PyExc_ValueError,
173 "unexpected: can't format the attribute name");
185 #if defined(__APPLE__)
186 static inline ssize_t _listxattr(const char *path, char *namebuf, size_t size) {
187 return listxattr(path, namebuf, size, 0);
189 static inline ssize_t _llistxattr(const char *path, char *namebuf, size_t size) {
190 return listxattr(path, namebuf, size, XATTR_NOFOLLOW);
192 static inline ssize_t _flistxattr(int fd, char *namebuf, size_t size) {
193 return flistxattr(fd, namebuf, size, 0);
196 static inline ssize_t _getxattr (const char *path, const char *name, void *value, size_t size) {
197 return getxattr(path, name, value, size, 0, 0);
199 static inline ssize_t _lgetxattr (const char *path, const char *name, void *value, size_t size) {
200 return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
202 static inline ssize_t _fgetxattr (int filedes, const char *name, void *value, size_t size) {
203 return fgetxattr(filedes, name, value, size, 0, 0);
206 // [fl]setxattr: Both OS X and Linux define XATTR_CREATE and XATTR_REPLACE for the last option.
207 static inline int _setxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
208 return setxattr(path, name, value, size, 0, flags);
210 static inline int _lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
211 return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
213 static inline int _fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) {
214 return fsetxattr(filedes, name, value, size, 0, flags);
217 static inline int _removexattr(const char *path, const char *name) {
218 return removexattr(path, name, 0);
220 static inline int _lremovexattr(const char *path, const char *name) {
221 return removexattr(path, name, XATTR_NOFOLLOW);
223 static inline int _fremovexattr(int filedes, const char *name) {
224 return fremovexattr(filedes, name, 0);
227 #elif defined(__linux__)
228 #define _listxattr(path, list, size) listxattr(path, list, size)
229 #define _llistxattr(path, list, size) llistxattr(path, list, size)
230 #define _flistxattr(fd, list, size) flistxattr(fd, list, size)
232 #define _getxattr(path, name, value, size) getxattr(path, name, value, size)
233 #define _lgetxattr(path, name, value, size) lgetxattr(path, name, value, size)
234 #define _fgetxattr(fd, name, value, size) fgetxattr(fd, name, value, size)
236 #define _setxattr(path, name, value, size, flags) setxattr(path, name, value, size, flags)
237 #define _lsetxattr(path, name, value, size, flags) lsetxattr(path, name, value, size, flags)
238 #define _fsetxattr(fd, name, value, size, flags) fsetxattr(fd, name, value, size, flags)
240 #define _removexattr(path, name) removexattr(path, name)
241 #define _lremovexattr(path, name) lremovexattr(path, name)
242 #define _fremovexattr(fd, name) fremovexattr(fd, name)
246 typedef ssize_t (*buf_getter)(target_t *tgt, const char *name,
247 void *output, size_t size);
249 static ssize_t _list_obj(target_t *tgt, const char *unused, void *list,
253 Py_BEGIN_ALLOW_THREADS;
254 if(tgt->type == T_FD)
255 ret = _flistxattr(tgt->fd, list, size);
256 else if (tgt->type == T_LINK)
257 ret = _llistxattr(tgt->name, list, size);
259 ret = _listxattr(tgt->name, list, size);
260 Py_END_ALLOW_THREADS;
264 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
267 Py_BEGIN_ALLOW_THREADS;
268 if(tgt->type == T_FD)
269 ret = _fgetxattr(tgt->fd, name, value, size);
270 else if (tgt->type == T_LINK)
271 ret = _lgetxattr(tgt->name, name, value, size);
273 ret = _getxattr(tgt->name, name, value, size);
274 Py_END_ALLOW_THREADS;
278 static int _set_obj(target_t *tgt, const char *name,
279 const void *value, size_t size, int flags) {
281 Py_BEGIN_ALLOW_THREADS;
282 if(tgt->type == T_FD)
283 ret = _fsetxattr(tgt->fd, name, value, size, flags);
284 else if (tgt->type == T_LINK)
285 ret = _lsetxattr(tgt->name, name, value, size, flags);
287 ret = _setxattr(tgt->name, name, value, size, flags);
288 Py_END_ALLOW_THREADS;
292 static int _remove_obj(target_t *tgt, const char *name) {
294 Py_BEGIN_ALLOW_THREADS;
295 if(tgt->type == T_FD)
296 ret = _fremovexattr(tgt->fd, name);
297 else if (tgt->type == T_LINK)
298 ret = _lremovexattr(tgt->name, name);
300 ret = _removexattr(tgt->name, name);
301 Py_END_ALLOW_THREADS;
305 /* Perform a get/list operation with appropriate buffer size,
306 * determined dynamically.
309 * - getter: the function that actually does the I/O.
310 * - tgt, name: passed to the getter.
311 * - buffer: pointer to either an already allocated memory area (in
312 * which case size contains its current size), or NULL to
313 * allocate. In all cases (success or failure), the caller should
314 * deallocate the buffer, using PyMem_Free(). Note that if size is
315 * zero but buffer already points to allocate memory, it will be
317 * - size: either size of current buffer (if non-NULL), or size for
318 * initial allocation (if non-zero), or a zero value which means
319 * auto-allocate buffer with automatically queried size. Value will
320 * be updated upon return with the current buffer size.
321 * - io_errno: if non-NULL, the actual errno will be recorded here; if
322 * zero, the call was successful and the output/size/nval are valid.
324 * Return value: if positive or zero, buffer will contain the read
325 * value. Otherwise, io_errno will contain the I/O errno, or zero
326 * to signify a Python-level error. In all cases, the Python-level
327 * error is set to the appropriate value.
329 static ssize_t _generic_get(buf_getter getter, target_t *tgt,
334 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
336 static ssize_t _generic_get(buf_getter getter, target_t *tgt,
342 /* Clear errno for now, will only set it when it fails in I/O. */
343 if (io_errno != NULL) {
347 #define EXIT_IOERROR() \
349 if (io_errno != NULL) { \
352 PyErr_SetFromErrno(PyExc_IOError); \
356 /* Initialize the buffer, if needed. */
357 if (*size == 0 || *buffer == NULL) {
360 if ((nalloc = getter(tgt, name, NULL, 0)) == -1) {
364 /* Empty, so no need to retrieve it. */
369 if((*buffer = PyMem_Malloc(*size)) == NULL) {
374 // Try to get the value, while increasing the buffer if too small.
375 while((res = getter(tgt, name, *buffer, *size)) == -1) {
376 if(errno == ERANGE) {
377 ssize_t realloc_size_s = getter(tgt, name, NULL, 0);
378 /* ERANGE + proper size _should_ not fail, but... */
379 if(realloc_size_s == -1) {
382 size_t realloc_size = (size_t) realloc_size_s;
384 if((tmp_buf = PyMem_Realloc(*buffer, realloc_size)) == NULL) {
389 *size = realloc_size;
392 /* else we're dealing with a different error, which we
393 don't know how to handle nicely, so we return */
402 Checks if an attribute name matches an optional namespace.
404 If the namespace is NULL or an empty string, it will return the
405 name itself. If the namespace is non-NULL and the name matches, it
406 will return a pointer to the offset in the name after the namespace
407 and the separator. If however the name doesn't match the namespace,
411 const char *matches_ns(const char *ns, const char *name) {
413 if (ns == NULL || *ns == '\0')
415 ns_size = strlen(ns);
417 if (strlen(name) > (ns_size+1) && !strncmp(name, ns, ns_size) &&
418 name[ns_size] == '.')
419 return name + ns_size + 1;
423 /* Wrapper for getxattr */
424 static char __pygetxattr_doc__[] =
425 "getxattr(item, attribute[, nofollow=False])\n"
426 "Get the value of a given extended attribute (deprecated).\n"
432 ".. deprecated:: 0.4\n"
433 " this function has been deprecated\n"
434 " by the :func:`get` function.\n"
438 pygetxattr(PyObject *self, PyObject *args)
443 char *attrname = NULL;
446 size_t nalloc = ESTIMATE_ATTR_SIZE;
449 /* Parse the arguments */
450 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
452 if(convert_obj(myarg, &tgt, nofollow) < 0) {
457 nret = _generic_get(_get_obj, &tgt, attrname, &buf, &nalloc, NULL);
462 /* Create the string which will hold the result */
463 res = PyBytes_FromStringAndSize(buf, nret);
466 /* Free the buffer, now it is no longer needed */
470 PyMem_Free(attrname);
472 /* Return the result */
476 /* Wrapper for getxattr */
477 static char __get_doc__[] =
478 "get(item, name[, nofollow=False, namespace=None])\n"
479 "Get the value of a given extended attribute.\n"
482 " >>> xattr.get('/path/to/file', 'user.comment')\n"
484 " >>> xattr.get('/path/to/file', 'comment', namespace=xattr.NS_USER)\n"
491 ":return: the value of the extended attribute (can contain NULLs)\n"
493 ":raises EnvironmentError: caused by any system errors\n"
495 ".. versionadded:: 0.4\n"
500 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
505 char *attrname = NULL, *namebuf;
506 const char *fullname;
508 const char *ns = NULL;
510 size_t nalloc = ESTIMATE_ATTR_SIZE;
511 PyObject *res = NULL;
512 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
514 /* Parse the arguments */
515 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|iy", kwlist,
516 &myarg, NULL, &attrname, &nofollow, &ns))
519 if(convert_obj(myarg, &tgt, nofollow) < 0) {
523 if(merge_ns(ns, attrname, &fullname, &namebuf) < 0) {
527 nret = _generic_get(_get_obj, &tgt, fullname, &buf, &nalloc, NULL);
532 /* Create the string which will hold the result */
533 res = PyBytes_FromStringAndSize(buf, nret);
535 /* Free the buffers, they are no longer needed */
542 PyMem_Free(attrname);
544 /* Return the result */
548 /* Wrapper for getxattr */
549 static char __get_all_doc__[] =
550 "get_all(item[, nofollow=False, namespace=None])\n"
551 "Get all the extended attributes of an item.\n"
553 "This function performs a bulk-get of all extended attribute names\n"
554 "and the corresponding value.\n"
557 " >>> xattr.get_all('/path/to/file')\n"
558 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),\n"
559 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
560 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
561 " [('mime-type', 'plain/text'), ('comment', 'test')]\n"
564 ":keyword namespace: an optional namespace for filtering the\n"
565 " attributes; for example, querying all user attributes can be\n"
566 " accomplished by passing namespace=:const:`NS_USER`\n"
567 ":type namespace: string\n"
569 ":return: list of tuples (name, value); note that if a namespace\n"
570 " argument was passed, it (and the separator) will be stripped from\n"
571 " the names returned\n"
573 ":raises EnvironmentError: caused by any system errors\n"
575 ".. note:: Since reading the whole attribute list is not an atomic\n"
576 " operation, it might be possible that attributes are added\n"
577 " or removed between the initial query and the actual reading\n"
578 " of the attributes; the returned list will contain only the\n"
579 " attributes that were present at the initial listing of the\n"
580 " attribute names and that were still present when the read\n"
581 " attempt for the value is made.\n"
582 ".. versionadded:: 0.4\n"
587 get_all(PyObject *self, PyObject *args, PyObject *keywds)
589 PyObject *myarg, *res;
591 const char *ns = NULL;
592 char *buf_list = NULL, *buf_val = NULL;
594 size_t nalloc = ESTIMATE_ATTR_SIZE;
598 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
601 /* Parse the arguments */
602 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iy", kwlist,
603 &myarg, &nofollow, &ns))
605 if(convert_obj(myarg, &tgt, nofollow) < 0)
609 /* Compute first the list of attributes */
610 nlist = _generic_get(_list_obj, &tgt, NULL, &buf_list,
613 /* We can't handle any errors, and the Python error is already
614 set, just bail out. */
618 /* Create the list which will hold the result. */
619 mylist = PyList_New(0);
624 nalloc = ESTIMATE_ATTR_SIZE;
625 /* Create and insert the attributes as strings in the list */
626 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
630 if((name = matches_ns(ns, s)) == NULL)
632 /* Now retrieve the attribute value */
633 nval = _generic_get(_get_obj, &tgt, s, &buf_val, &nalloc, &io_errno);
635 if (io_errno == ENODATA) {
643 my_tuple = Py_BuildValue("yy#", name, buf_val, nval);
644 if (my_tuple == NULL) {
648 if(PyList_Append(mylist, my_tuple) < 0) {
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#|iiy",
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|iy", 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)
973 size_t nalloc = ESTIMATE_ATTR_SIZE;
980 /* Parse the arguments */
981 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
983 if(convert_obj(myarg, &tgt, nofollow) < 0)
986 nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL);
992 /* Compute the number of attributes in the list */
993 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
997 /* Create the list which will hold the result */
998 mylist = PyList_New(nattrs);
1003 /* Create and insert the attributes as strings in the list */
1004 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1005 PyObject *item = PyBytes_FromString(s);
1011 PyList_SET_ITEM(mylist, nattrs, item);
1016 /* Free the buffer, now it is no longer needed */
1020 /* Return the result */
1024 static char __list_doc__[] =
1025 "list(item[, nofollow=False, namespace=None])\n"
1026 "Return the list of attribute names for a file.\n"
1030 " >>> xattr.list('/path/to/file')\n"
1031 " ['user.test', 'user.comment', 'system.posix_acl_access']\n"
1032 " >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
1033 " ['test', 'comment']\n"
1038 ":returns: the list of attributes; note that if a namespace \n"
1039 " argument was passed, it (and the separator) will be stripped\n"
1043 ":raises EnvironmentError: caused by any system errors\n"
1045 ".. versionadded:: 0.4\n"
1049 /* Wrapper for listxattr */
1051 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
1056 size_t nalloc = ESTIMATE_ATTR_SIZE;
1059 const char *ns = NULL;
1063 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
1065 /* Parse the arguments */
1066 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iy", kwlist,
1067 &myarg, &nofollow, &ns))
1070 if(convert_obj(myarg, &tgt, nofollow) < 0) {
1073 nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL);
1078 /* Compute the number of attributes in the list */
1079 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
1080 if(matches_ns(ns, s) != NULL)
1084 /* Create the list which will hold the result */
1085 if((res = PyList_New(nattrs)) == NULL) {
1089 /* Create and insert the attributes as strings in the list */
1090 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1091 const char *name = matches_ns(ns, s);
1093 PyObject *item = PyBytes_FromString(name);
1099 PyList_SET_ITEM(res, nattrs, item);
1105 /* Free the buffer, now it is no longer needed */
1112 /* Return the result */
1116 static PyMethodDef xattr_methods[] = {
1117 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
1118 {"get", (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
1120 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
1122 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
1123 {"set", (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
1125 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
1126 {"remove", (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
1128 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
1129 {"list", (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
1131 {NULL, NULL, 0, NULL} /* Sentinel */
1134 static char __xattr_doc__[] = \
1135 "This module gives access to the extended attributes present\n"
1136 "in some operating systems/filesystems. You can list attributes,\n"
1137 "get, set and remove them.\n"
1139 "The module exposes two sets of functions:\n"
1140 " - the 'old' :func:`listxattr`, :func:`getxattr`, :func:`setxattr`,\n"
1141 " :func:`removexattr`\n"
1142 " functions which are deprecated since version 0.4\n"
1143 " - the new :func:`list`, :func:`get`, :func:`get_all`, :func:`set`,\n"
1144 " :func:`remove` functions\n"
1145 " which expose a namespace-aware API and simplify a bit the calling\n"
1146 " model by using keyword arguments\n"
1149 " >>> import xattr\n"
1150 " >>> xattr.listxattr(\"file.txt\")\n"
1151 " ['user.mime_type']\n"
1152 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
1154 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
1155 "\"Simple text file\")\n"
1156 " >>> xattr.listxattr(\"file.txt\")\n"
1157 " ['user.mime_type', 'user.comment']\n"
1158 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
1160 ".. note:: Most or all errors reported by the system while using\n"
1161 " the ``xattr`` library will be reported by raising\n"
1162 " a :exc:`EnvironmentError`; under\n"
1163 " Linux, the following ``errno`` values are used:\n"
1165 " - ``ENODATA`` means that the attribute name is invalid\n"
1166 " - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n"
1167 " support extended attributes, or that the namespace is invalid\n"
1168 " - ``E2BIG`` mean that the attribute value is too big\n"
1169 " - ``ERANGE`` mean that the attribute name is too big (it might also\n"
1170 " mean an error in the xattr module itself)\n"
1171 " - ``ENOSPC`` and ``EDQUOT`` are documented as meaning out of disk\n"
1172 " space or out of disk space because of quota limits\n"
1173 ".. note:: Under Python 3, the namespace argument is a byte string,\n"
1174 " not a unicode string.\n"
1178 static struct PyModuleDef xattrmodule = {
1179 PyModuleDef_HEAD_INIT,
1186 #define INITERROR return NULL
1192 PyObject *ns_security = NULL;
1193 PyObject *ns_system = NULL;
1194 PyObject *ns_trusted = NULL;
1195 PyObject *ns_user = NULL;
1196 PyObject *m = PyModule_Create(&xattrmodule);
1200 PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
1201 PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
1202 PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
1203 PyModule_AddStringConstant(m, "__license__",
1204 "GNU Lesser General Public License (LGPL)");
1205 PyModule_AddStringConstant(m, "__docformat__", "restructuredtext en");
1207 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
1208 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
1210 /* namespace constants */
1211 if((ns_security = PyBytes_FromString("security")) == NULL)
1213 if((ns_system = PyBytes_FromString("system")) == NULL)
1215 if((ns_trusted = PyBytes_FromString("trusted")) == NULL)
1217 if((ns_user = PyBytes_FromString("user")) == NULL)
1219 if(PyModule_AddObject(m, "NS_SECURITY", ns_security) < 0)
1222 if(PyModule_AddObject(m, "NS_SYSTEM", ns_system) < 0)
1225 if(PyModule_AddObject(m, "NS_TRUSTED", ns_trusted) < 0)
1228 if(PyModule_AddObject(m, "NS_USER", ns_user) < 0)
1235 Py_XDECREF(ns_user);
1236 Py_XDECREF(ns_trusted);
1237 Py_XDECREF(ns_system);
1238 Py_XDECREF(ns_security);