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, or a file-like\n" \
33 " object, or a file descriptor; this represents the file on \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(PyBytes_Check(myobj)) {
134 tgt->type = nofollow ? T_LINK : T_PATH;
135 tgt->name = PyBytes_AS_STRING(myobj);
136 } else if(PyUnicode_Check(myobj)) {
137 tgt->type = nofollow ? T_LINK : T_PATH;
139 PyUnicode_AsEncodedString(myobj,
140 Py_FileSystemDefaultEncoding,
145 tgt->name = PyBytes_AS_STRING(tgt->tmp);
146 } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
150 PyErr_SetString(PyExc_TypeError, "argument must be string or int");
158 /* Combine a namespace string and an attribute name into a
159 fully-qualified name */
160 static int merge_ns(const char *ns, const char *name,
161 const char **result, char **buf) {
162 if(ns != NULL && *ns != '\0') {
164 /* The value of new_size is related to/must be kept in-sync
165 with the format string below */
166 size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
167 if((*buf = PyMem_Malloc(new_size)) == NULL) {
171 cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
172 if((size_t) cnt >= new_size || cnt < 0) {
173 PyErr_SetString(PyExc_ValueError,
174 "unexpected: can't format the attribute name");
186 #if defined(__APPLE__)
187 static inline ssize_t _listxattr(const char *path, char *namebuf, size_t size) {
188 return listxattr(path, namebuf, size, 0);
190 static inline ssize_t _llistxattr(const char *path, char *namebuf, size_t size) {
191 return listxattr(path, namebuf, size, XATTR_NOFOLLOW);
193 static inline ssize_t _flistxattr(int fd, char *namebuf, size_t size) {
194 return flistxattr(fd, namebuf, size, 0);
197 static inline ssize_t _getxattr (const char *path, const char *name, void *value, size_t size) {
198 return getxattr(path, name, value, size, 0, 0);
200 static inline ssize_t _lgetxattr (const char *path, const char *name, void *value, size_t size) {
201 return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
203 static inline ssize_t _fgetxattr (int filedes, const char *name, void *value, size_t size) {
204 return fgetxattr(filedes, name, value, size, 0, 0);
207 // [fl]setxattr: Both OS X and Linux define XATTR_CREATE and XATTR_REPLACE for the last option.
208 static inline int _setxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
209 return setxattr(path, name, value, size, 0, flags);
211 static inline int _lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
212 return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
214 static inline int _fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) {
215 return fsetxattr(filedes, name, value, size, 0, flags);
218 static inline int _removexattr(const char *path, const char *name) {
219 return removexattr(path, name, 0);
221 static inline int _lremovexattr(const char *path, const char *name) {
222 return removexattr(path, name, XATTR_NOFOLLOW);
224 static inline int _fremovexattr(int filedes, const char *name) {
225 return fremovexattr(filedes, name, 0);
228 #elif defined(__linux__)
229 #define _listxattr(path, list, size) listxattr(path, list, size)
230 #define _llistxattr(path, list, size) llistxattr(path, list, size)
231 #define _flistxattr(fd, list, size) flistxattr(fd, list, size)
233 #define _getxattr(path, name, value, size) getxattr(path, name, value, size)
234 #define _lgetxattr(path, name, value, size) lgetxattr(path, name, value, size)
235 #define _fgetxattr(fd, name, value, size) fgetxattr(fd, name, value, size)
237 #define _setxattr(path, name, value, size, flags) setxattr(path, name, value, size, flags)
238 #define _lsetxattr(path, name, value, size, flags) lsetxattr(path, name, value, size, flags)
239 #define _fsetxattr(fd, name, value, size, flags) fsetxattr(fd, name, value, size, flags)
241 #define _removexattr(path, name) removexattr(path, name)
242 #define _lremovexattr(path, name) lremovexattr(path, name)
243 #define _fremovexattr(fd, name) fremovexattr(fd, name)
247 typedef ssize_t (*buf_getter)(target_t *tgt, const char *name,
248 void *output, size_t size);
250 static ssize_t _list_obj(target_t *tgt, const char *unused, void *list,
254 Py_BEGIN_ALLOW_THREADS;
255 if(tgt->type == T_FD)
256 ret = _flistxattr(tgt->fd, list, size);
257 else if (tgt->type == T_LINK)
258 ret = _llistxattr(tgt->name, list, size);
260 ret = _listxattr(tgt->name, list, size);
261 Py_END_ALLOW_THREADS;
265 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
268 Py_BEGIN_ALLOW_THREADS;
269 if(tgt->type == T_FD)
270 ret = _fgetxattr(tgt->fd, name, value, size);
271 else if (tgt->type == T_LINK)
272 ret = _lgetxattr(tgt->name, name, value, size);
274 ret = _getxattr(tgt->name, name, value, size);
275 Py_END_ALLOW_THREADS;
279 static int _set_obj(target_t *tgt, const char *name,
280 const void *value, size_t size, int flags) {
282 Py_BEGIN_ALLOW_THREADS;
283 if(tgt->type == T_FD)
284 ret = _fsetxattr(tgt->fd, name, value, size, flags);
285 else if (tgt->type == T_LINK)
286 ret = _lsetxattr(tgt->name, name, value, size, flags);
288 ret = _setxattr(tgt->name, name, value, size, flags);
289 Py_END_ALLOW_THREADS;
293 static int _remove_obj(target_t *tgt, const char *name) {
295 Py_BEGIN_ALLOW_THREADS;
296 if(tgt->type == T_FD)
297 ret = _fremovexattr(tgt->fd, name);
298 else if (tgt->type == T_LINK)
299 ret = _lremovexattr(tgt->name, name);
301 ret = _removexattr(tgt->name, name);
302 Py_END_ALLOW_THREADS;
306 /* Perform a get/list operation with appropriate buffer size,
307 * determined dynamically.
310 * - getter: the function that actually does the I/O.
311 * - tgt, name: passed to the getter.
312 * - buffer: pointer to either an already allocated memory area (in
313 * which case size contains its current size), or NULL to
314 * allocate. In all cases (success or failure), the caller should
315 * deallocate the buffer, using PyMem_Free(). Note that if size is
316 * zero but buffer already points to allocate memory, it will be
318 * - size: either size of current buffer (if non-NULL), or size for
319 * initial allocation (if non-zero), or a zero value which means
320 * auto-allocate buffer with automatically queried size. Value will
321 * be updated upon return with the current buffer size.
322 * - io_errno: if non-NULL, the actual errno will be recorded here; if
323 * zero, the call was successful and the output/size/nval are valid.
325 * Return value: if positive or zero, buffer will contain the read
326 * value. Otherwise, io_errno will contain the I/O errno, or zero
327 * to signify a Python-level error. In all cases, the Python-level
328 * error is set to the appropriate value.
330 static ssize_t _generic_get(buf_getter getter, target_t *tgt,
335 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
337 static ssize_t _generic_get(buf_getter getter, target_t *tgt,
343 /* Clear errno for now, will only set it when it fails in I/O. */
344 if (io_errno != NULL) {
348 #define EXIT_IOERROR() \
350 if (io_errno != NULL) { \
353 PyErr_SetFromErrno(PyExc_IOError); \
357 /* Initialize the buffer, if needed. */
358 if (*size == 0 || *buffer == NULL) {
361 if ((nalloc = getter(tgt, name, NULL, 0)) == -1) {
365 /* Empty, so no need to retrieve it. */
370 if((*buffer = PyMem_Malloc(*size)) == NULL) {
375 // Try to get the value, while increasing the buffer if too small.
376 while((res = getter(tgt, name, *buffer, *size)) == -1) {
377 if(errno == ERANGE) {
378 ssize_t realloc_size_s = getter(tgt, name, NULL, 0);
379 /* ERANGE + proper size _should_ not fail, but... */
380 if(realloc_size_s == -1) {
383 size_t realloc_size = (size_t) realloc_size_s;
385 if((tmp_buf = PyMem_Realloc(*buffer, realloc_size)) == NULL) {
390 *size = realloc_size;
393 /* else we're dealing with a different error, which we
394 don't know how to handle nicely, so we return */
403 Checks if an attribute name matches an optional namespace.
405 If the namespace is NULL or an empty string, it will return the
406 name itself. If the namespace is non-NULL and the name matches, it
407 will return a pointer to the offset in the name after the namespace
408 and the separator. If however the name doesn't match the namespace,
412 const char *matches_ns(const char *ns, const char *name) {
414 if (ns == NULL || *ns == '\0')
416 ns_size = strlen(ns);
418 if (strlen(name) > (ns_size+1) && !strncmp(name, ns, ns_size) &&
419 name[ns_size] == '.')
420 return name + ns_size + 1;
424 /* Wrapper for getxattr */
425 static char __pygetxattr_doc__[] =
426 "getxattr(item, attribute[, nofollow=False])\n"
427 "Get the value of a given extended attribute (deprecated).\n"
433 ".. deprecated:: 0.4\n"
434 " this function has been deprecated\n"
435 " by the :func:`get` function.\n"
439 pygetxattr(PyObject *self, PyObject *args)
444 char *attrname = NULL;
447 size_t nalloc = ESTIMATE_ATTR_SIZE;
450 /* Parse the arguments */
451 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
453 if(convert_obj(myarg, &tgt, nofollow) < 0) {
458 nret = _generic_get(_get_obj, &tgt, attrname, &buf, &nalloc, NULL);
463 /* Create the string which will hold the result */
464 res = PyBytes_FromStringAndSize(buf, nret);
467 /* Free the buffer, now it is no longer needed */
471 PyMem_Free(attrname);
473 /* Return the result */
477 /* Wrapper for getxattr */
478 static char __get_doc__[] =
479 "get(item, name[, nofollow=False, namespace=None])\n"
480 "Get the value of a given extended attribute.\n"
483 " >>> xattr.get('/path/to/file', 'user.comment')\n"
485 " >>> xattr.get('/path/to/file', 'comment', namespace=xattr.NS_USER)\n"
492 ":return: the value of the extended attribute (can contain NULLs)\n"
494 ":raises EnvironmentError: caused by any system errors\n"
496 ".. versionadded:: 0.4\n"
501 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
506 char *attrname = NULL, *namebuf;
507 const char *fullname;
509 const char *ns = NULL;
511 size_t nalloc = ESTIMATE_ATTR_SIZE;
512 PyObject *res = NULL;
513 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
515 /* Parse the arguments */
516 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|iy", kwlist,
517 &myarg, NULL, &attrname, &nofollow, &ns))
520 if(convert_obj(myarg, &tgt, nofollow) < 0) {
524 if(merge_ns(ns, attrname, &fullname, &namebuf) < 0) {
528 nret = _generic_get(_get_obj, &tgt, fullname, &buf, &nalloc, NULL);
533 /* Create the string which will hold the result */
534 res = PyBytes_FromStringAndSize(buf, nret);
536 /* Free the buffers, they are no longer needed */
543 PyMem_Free(attrname);
545 /* Return the result */
549 /* Wrapper for getxattr */
550 static char __get_all_doc__[] =
551 "get_all(item[, nofollow=False, namespace=None])\n"
552 "Get all the extended attributes of an item.\n"
554 "This function performs a bulk-get of all extended attribute names\n"
555 "and the corresponding value.\n"
558 " >>> xattr.get_all('/path/to/file')\n"
559 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),\n"
560 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
561 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
562 " [('mime-type', 'plain/text'), ('comment', 'test')]\n"
565 ":keyword namespace: an optional namespace for filtering the\n"
566 " attributes; for example, querying all user attributes can be\n"
567 " accomplished by passing namespace=:const:`NS_USER`\n"
568 ":type namespace: string\n"
570 ":return: list of tuples (name, value); note that if a namespace\n"
571 " argument was passed, it (and the separator) will be stripped from\n"
572 " the names returned\n"
574 ":raises EnvironmentError: caused by any system errors\n"
576 ".. note:: Since reading the whole attribute list is not an atomic\n"
577 " operation, it might be possible that attributes are added\n"
578 " or removed between the initial query and the actual reading\n"
579 " of the attributes; the returned list will contain only the\n"
580 " attributes that were present at the initial listing of the\n"
581 " attribute names and that were still present when the read\n"
582 " attempt for the value is made.\n"
583 ".. versionadded:: 0.4\n"
588 get_all(PyObject *self, PyObject *args, PyObject *keywds)
590 PyObject *myarg, *res;
592 const char *ns = NULL;
593 char *buf_list = NULL, *buf_val = NULL;
595 size_t nalloc = ESTIMATE_ATTR_SIZE;
599 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
602 /* Parse the arguments */
603 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iy", kwlist,
604 &myarg, &nofollow, &ns))
606 if(convert_obj(myarg, &tgt, nofollow) < 0)
610 /* Compute first the list of attributes */
611 nlist = _generic_get(_list_obj, &tgt, NULL, &buf_list,
614 /* We can't handle any errors, and the Python error is already
615 set, just bail out. */
619 /* Create the list which will hold the result. */
620 mylist = PyList_New(0);
625 nalloc = ESTIMATE_ATTR_SIZE;
626 /* Create and insert the attributes as strings in the list */
627 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
631 if((name = matches_ns(ns, s)) == NULL)
633 /* Now retrieve the attribute value */
634 nval = _generic_get(_get_obj, &tgt, s, &buf_val, &nalloc, &io_errno);
636 if (io_errno == ENODATA) {
644 my_tuple = Py_BuildValue("yy#", name, buf_val, nval);
645 if (my_tuple == NULL) {
649 PyList_Append(mylist, my_tuple);
653 /* Successful exit */
660 PyMem_Free(buf_list);
665 /* Return the result */
670 static char __pysetxattr_doc__[] =
671 "setxattr(item, name, value[, flags=0, nofollow=False])\n"
672 "Set the value of a given extended attribute (deprecated).\n"
674 "Be careful in case you want to set attributes on symbolic\n"
675 "links, you have to use all the 5 parameters; use 0 for the \n"
676 "flags value if you want the default behaviour (create or "
685 ".. deprecated:: 0.4\n"
686 " this function has been deprecated\n"
687 " by the :func:`set` function.\n"
690 /* Wrapper for setxattr */
692 pysetxattr(PyObject *self, PyObject *args)
694 PyObject *myarg, *res;
696 char *attrname = NULL;
698 Py_ssize_t bufsize_s;
704 /* Parse the arguments */
705 if (!PyArg_ParseTuple(args, "Oetet#|ii", &myarg, NULL, &attrname,
706 NULL, &buf, &bufsize_s, &flags, &nofollow))
710 PyErr_SetString(PyExc_ValueError,
711 "negative value size?!");
715 bufsize = (size_t) bufsize_s;
717 if(convert_obj(myarg, &tgt, nofollow) < 0) {
722 /* Set the attribute's value */
723 nret = _set_obj(&tgt, attrname, buf, bufsize, flags);
728 res = PyErr_SetFromErrno(PyExc_IOError);
736 PyMem_Free(attrname);
739 /* Return the result */
743 static char __set_doc__[] =
744 "set(item, name, value[, flags=0, namespace=None])\n"
745 "Set the value of a given extended attribute.\n"
749 " >>> xattr.set('/path/to/file', 'user.comment', 'test')\n"
750 " >>> xattr.set('/path/to/file', 'comment', 'test',"
751 " namespace=xattr.NS_USER)\n"
760 ":raises EnvironmentError: caused by any system errors\n"
762 ".. versionadded:: 0.4\n"
766 /* Wrapper for setxattr */
768 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
770 PyObject *myarg, *res;
772 char *attrname = NULL;
774 Py_ssize_t bufsize_s;
779 const char *ns = NULL;
781 const char *full_name;
782 static char *kwlist[] = {"item", "name", "value", "flags",
783 "nofollow", "namespace", NULL};
785 /* Parse the arguments */
786 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oetet#|iiy",
787 kwlist, &myarg, NULL, &attrname, NULL,
788 &buf, &bufsize_s, &flags, &nofollow, &ns))
792 PyErr_SetString(PyExc_ValueError,
793 "negative value size?!");
797 bufsize = (size_t) bufsize_s;
799 if(convert_obj(myarg, &tgt, nofollow) < 0) {
804 if(merge_ns(ns, attrname, &full_name, &newname) < 0) {
809 /* Set the attribute's value */
810 nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
817 res = PyErr_SetFromErrno(PyExc_IOError);
825 PyMem_Free(attrname);
828 /* Return the result */
833 static char __pyremovexattr_doc__[] =
834 "removexattr(item, name[, nofollow])\n"
835 "Remove an attribute from a file (deprecated).\n"
841 ".. deprecated:: 0.4\n"
842 " this function has been deprecated by the :func:`remove` function.\n"
845 /* Wrapper for removexattr */
847 pyremovexattr(PyObject *self, PyObject *args)
849 PyObject *myarg, *res;
851 char *attrname = NULL;
855 /* Parse the arguments */
856 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
859 if(convert_obj(myarg, &tgt, nofollow) < 0) {
864 /* Remove the attribute */
865 nret = _remove_obj(&tgt, attrname);
870 res = PyErr_SetFromErrno(PyExc_IOError);
878 PyMem_Free(attrname);
880 /* Return the result */
884 static char __remove_doc__[] =
885 "remove(item, name[, nofollow=False, namespace=None])\n"
886 "Remove an attribute from a file.\n"
890 " >>> xattr.remove('/path/to/file', 'user.comment')\n"
897 ":raises EnvironmentError: caused by any system errors\n"
899 ".. versionadded:: 0.4\n"
903 /* Wrapper for removexattr */
905 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
907 PyObject *myarg, *res;
909 char *attrname = NULL, *name_buf;
910 const char *ns = NULL;
911 const char *full_name;
914 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
916 /* Parse the arguments */
917 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|iy", kwlist,
918 &myarg, NULL, &attrname, &nofollow, &ns))
921 if(convert_obj(myarg, &tgt, nofollow) < 0) {
926 if(merge_ns(ns, attrname, &full_name, &name_buf) < 0) {
931 /* Remove the attribute */
932 nret = _remove_obj(&tgt, full_name);
934 PyMem_Free(name_buf);
939 res = PyErr_SetFromErrno(PyExc_IOError);
947 PyMem_Free(attrname);
949 /* Return the result */
953 static char __pylistxattr_doc__[] =
954 "listxattr(item[, nofollow=False])\n"
955 "Return the list of attribute names for a file (deprecated).\n"
960 ".. deprecated:: 0.4\n"
961 " this function has been deprecated by the :func:`list` function.\n"
964 /* Wrapper for listxattr */
966 pylistxattr(PyObject *self, PyObject *args)
971 size_t nalloc = ESTIMATE_ATTR_SIZE;
978 /* Parse the arguments */
979 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
981 if(convert_obj(myarg, &tgt, nofollow) < 0)
984 nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL);
990 /* Compute the number of attributes in the list */
991 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
995 /* Create the list which will hold the result */
996 mylist = PyList_New(nattrs);
1001 /* Create and insert the attributes as strings in the list */
1002 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1003 PyObject *item = PyBytes_FromString(s);
1009 PyList_SET_ITEM(mylist, nattrs, item);
1014 /* Free the buffer, now it is no longer needed */
1018 /* Return the result */
1022 static char __list_doc__[] =
1023 "list(item[, nofollow=False, namespace=None])\n"
1024 "Return the list of attribute names for a file.\n"
1028 " >>> xattr.list('/path/to/file')\n"
1029 " ['user.test', 'user.comment', 'system.posix_acl_access']\n"
1030 " >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
1031 " ['test', 'comment']\n"
1036 ":returns: the list of attributes; note that if a namespace \n"
1037 " argument was passed, it (and the separator) will be stripped\n"
1041 ":raises EnvironmentError: caused by any system errors\n"
1043 ".. versionadded:: 0.4\n"
1047 /* Wrapper for listxattr */
1049 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
1054 size_t nalloc = ESTIMATE_ATTR_SIZE;
1057 const char *ns = NULL;
1061 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
1063 /* Parse the arguments */
1064 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iy", kwlist,
1065 &myarg, &nofollow, &ns))
1068 if(convert_obj(myarg, &tgt, nofollow) < 0) {
1071 nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL);
1076 /* Compute the number of attributes in the list */
1077 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
1078 if(matches_ns(ns, s) != NULL)
1082 /* Create the list which will hold the result */
1083 if((res = PyList_New(nattrs)) == NULL) {
1087 /* Create and insert the attributes as strings in the list */
1088 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1089 const char *name = matches_ns(ns, s);
1091 PyObject *item = PyBytes_FromString(name);
1097 PyList_SET_ITEM(res, nattrs, item);
1103 /* Free the buffer, now it is no longer needed */
1110 /* Return the result */
1114 static PyMethodDef xattr_methods[] = {
1115 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
1116 {"get", (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
1118 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
1120 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
1121 {"set", (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
1123 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
1124 {"remove", (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
1126 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
1127 {"list", (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
1129 {NULL, NULL, 0, NULL} /* Sentinel */
1132 static char __xattr_doc__[] = \
1133 "This module gives access to the extended attributes present\n"
1134 "in some operating systems/filesystems. You can list attributes,\n"
1135 "get, set and remove them.\n"
1137 "The module exposes two sets of functions:\n"
1138 " - the 'old' :func:`listxattr`, :func:`getxattr`, :func:`setxattr`,\n"
1139 " :func:`removexattr`\n"
1140 " functions which are deprecated since version 0.4\n"
1141 " - the new :func:`list`, :func:`get`, :func:`get_all`, :func:`set`,\n"
1142 " :func:`remove` functions\n"
1143 " which expose a namespace-aware API and simplify a bit the calling\n"
1144 " model by using keyword arguments\n"
1147 " >>> import xattr\n"
1148 " >>> xattr.listxattr(\"file.txt\")\n"
1149 " ['user.mime_type']\n"
1150 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
1152 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
1153 "\"Simple text file\")\n"
1154 " >>> xattr.listxattr(\"file.txt\")\n"
1155 " ['user.mime_type', 'user.comment']\n"
1156 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
1158 ".. note:: Most or all errors reported by the system while using\n"
1159 " the ``xattr`` library will be reported by raising\n"
1160 " a :exc:`EnvironmentError`; under\n"
1161 " Linux, the following ``errno`` values are used:\n"
1163 " - ``ENODATA`` means that the attribute name is invalid\n"
1164 " - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n"
1165 " support extended attributes, or that the namespace is invalid\n"
1166 " - ``E2BIG`` mean that the attribute value is too big\n"
1167 " - ``ERANGE`` mean that the attribute name is too big (it might also\n"
1168 " mean an error in the xattr module itself)\n"
1169 " - ``ENOSPC`` and ``EDQUOT`` are documented as meaning out of disk\n"
1170 " space or out of disk space because of quota limits\n"
1171 ".. note:: Under Python 3, the namespace argument is a byte string,\n"
1172 " not a unicode string.\n"
1176 static struct PyModuleDef xattrmodule = {
1177 PyModuleDef_HEAD_INIT,
1184 #define INITERROR return NULL
1190 PyObject *ns_security = NULL;
1191 PyObject *ns_system = NULL;
1192 PyObject *ns_trusted = NULL;
1193 PyObject *ns_user = NULL;
1194 PyObject *m = PyModule_Create(&xattrmodule);
1198 PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
1199 PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
1200 PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
1201 PyModule_AddStringConstant(m, "__license__",
1202 "GNU Lesser General Public License (LGPL)");
1203 PyModule_AddStringConstant(m, "__docformat__", "restructuredtext en");
1205 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
1206 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
1208 /* namespace constants */
1209 if((ns_security = PyBytes_FromString("security")) == NULL)
1211 if((ns_system = PyBytes_FromString("system")) == NULL)
1213 if((ns_trusted = PyBytes_FromString("trusted")) == NULL)
1215 if((ns_user = PyBytes_FromString("user")) == NULL)
1217 if(PyModule_AddObject(m, "NS_SECURITY", ns_security) < 0)
1220 if(PyModule_AddObject(m, "NS_SYSTEM", ns_system) < 0)
1223 if(PyModule_AddObject(m, "NS_TRUSTED", ns_trusted) < 0)
1226 if(PyModule_AddObject(m, "NS_USER", ns_user) < 0)
1233 Py_XDECREF(ns_user);
1234 Py_XDECREF(ns_trusted);
1235 Py_XDECREF(ns_system);
1236 Py_XDECREF(ns_security);