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>
31 /* Compatibility with python 2.4 regarding python size type (PEP 353) */
32 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
33 typedef int Py_ssize_t;
34 #define PY_SSIZE_T_MAX INT_MAX
35 #define PY_SSIZE_T_MIN INT_MIN
38 #if PY_MAJOR_VERSION >= 3
40 #define BYTES_CHAR "y"
41 #define BYTES_TUPLE "yy#"
43 #define BYTES_CHAR "s"
44 #define BYTES_TUPLE "ss#"
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 initial I/O buffer size for list and get operations; if the
102 * actual values will be smaller than this, we save a syscall out of
103 * two and allocate more memory upfront than needed, otherwise we
104 * incur three syscalls (get with ENORANGE, get with 0 to compute
105 * actual size, final get). The test suite is marginally faster (5%)
106 * with this, so it seems worth doing.
108 #define ESTIMATE_ATTR_SIZE 1024
110 typedef enum {T_FD, T_PATH, T_LINK} target_e;
121 /* Cleans up a tgt structure */
122 static void free_tgt(target_t *tgt) {
123 if (tgt->tmp != NULL) {
128 /* Used for cpychecker: */
129 /* The checker automatically defines this preprocessor name when creating
130 the custom attribute: */
131 #if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
132 #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION \
133 __attribute__((cpychecker_negative_result_sets_exception))
135 #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
138 static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow)
139 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
141 static int merge_ns(const char *ns, const char *name,
142 const char **result, char **buf)
143 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
146 /** Converts from a string, file or int argument to what we need.
148 * Returns -1 on failure, 0 on success.
150 static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow) {
153 if(PyBytes_Check(myobj)) {
154 tgt->type = nofollow ? T_LINK : T_PATH;
155 tgt->name = PyBytes_AS_STRING(myobj);
156 } else if(PyUnicode_Check(myobj)) {
157 tgt->type = nofollow ? T_LINK : T_PATH;
159 PyUnicode_AsEncodedString(myobj,
160 Py_FileSystemDefaultEncoding,
169 tgt->name = PyBytes_AS_STRING(tgt->tmp);
170 } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
174 PyErr_SetString(PyExc_TypeError, "argument must be string or int");
182 /* Combine a namespace string and an attribute name into a
183 fully-qualified name */
184 static int merge_ns(const char *ns, const char *name,
185 const char **result, char **buf) {
186 if(ns != NULL && *ns != '\0') {
188 /* The value of new_size is related to/must be kept in-sync
189 with the format string below */
190 size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
191 if((*buf = PyMem_Malloc(new_size)) == NULL) {
195 cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
196 if((size_t) cnt >= new_size || cnt < 0) {
197 PyErr_SetString(PyExc_ValueError,
198 "unexpected: can't format the attribute name");
210 #if defined(__APPLE__)
211 static inline ssize_t _listxattr(const char *path, char *namebuf, size_t size) {
212 return listxattr(path, namebuf, size, 0);
214 static inline ssize_t _llistxattr(const char *path, char *namebuf, size_t size) {
215 return listxattr(path, namebuf, size, XATTR_NOFOLLOW);
217 static inline ssize_t _flistxattr(int fd, char *namebuf, size_t size) {
218 return flistxattr(fd, namebuf, size, 0);
221 static inline ssize_t _getxattr (const char *path, const char *name, void *value, size_t size) {
222 return getxattr(path, name, value, size, 0, 0);
224 static inline ssize_t _lgetxattr (const char *path, const char *name, void *value, size_t size) {
225 return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
227 static inline ssize_t _fgetxattr (int filedes, const char *name, void *value, size_t size) {
228 return fgetxattr(filedes, name, value, size, 0, 0);
231 // [fl]setxattr: Both OS X and Linux define XATTR_CREATE and XATTR_REPLACE for the last option.
232 static inline int _setxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
233 return setxattr(path, name, value, size, 0, flags);
235 static inline int _lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
236 return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
238 static inline int _fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) {
239 return fsetxattr(filedes, name, value, size, 0, flags);
242 static inline int _removexattr(const char *path, const char *name) {
243 return removexattr(path, name, 0);
245 static inline int _lremovexattr(const char *path, const char *name) {
246 return removexattr(path, name, XATTR_NOFOLLOW);
248 static inline int _fremovexattr(int filedes, const char *name) {
249 return fremovexattr(filedes, name, 0);
252 #elif defined(__linux__)
253 #define _listxattr(path, list, size) listxattr(path, list, size)
254 #define _llistxattr(path, list, size) llistxattr(path, list, size)
255 #define _flistxattr(fd, list, size) flistxattr(fd, list, size)
257 #define _getxattr(path, name, value, size) getxattr(path, name, value, size)
258 #define _lgetxattr(path, name, value, size) lgetxattr(path, name, value, size)
259 #define _fgetxattr(fd, name, value, size) fgetxattr(fd, name, value, size)
261 #define _setxattr(path, name, value, size, flags) setxattr(path, name, value, size, flags)
262 #define _lsetxattr(path, name, value, size, flags) lsetxattr(path, name, value, size, flags)
263 #define _fsetxattr(fd, name, value, size, flags) fsetxattr(fd, name, value, size, flags)
265 #define _removexattr(path, name) removexattr(path, name)
266 #define _lremovexattr(path, name) lremovexattr(path, name)
267 #define _fremovexattr(fd, name) fremovexattr(fd, name)
271 typedef ssize_t (*buf_getter)(target_t *tgt, const char *name,
272 void *output, size_t size);
274 static ssize_t _list_obj(target_t *tgt, const char *unused, void *list,
278 Py_BEGIN_ALLOW_THREADS;
279 if(tgt->type == T_FD)
280 ret = _flistxattr(tgt->fd, list, size);
281 else if (tgt->type == T_LINK)
282 ret = _llistxattr(tgt->name, list, size);
284 ret = _listxattr(tgt->name, list, size);
285 Py_END_ALLOW_THREADS;
289 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
292 Py_BEGIN_ALLOW_THREADS;
293 if(tgt->type == T_FD)
294 ret = _fgetxattr(tgt->fd, name, value, size);
295 else if (tgt->type == T_LINK)
296 ret = _lgetxattr(tgt->name, name, value, size);
298 ret = _getxattr(tgt->name, name, value, size);
299 Py_END_ALLOW_THREADS;
303 static int _set_obj(target_t *tgt, const char *name,
304 const void *value, size_t size, int flags) {
306 Py_BEGIN_ALLOW_THREADS;
307 if(tgt->type == T_FD)
308 ret = _fsetxattr(tgt->fd, name, value, size, flags);
309 else if (tgt->type == T_LINK)
310 ret = _lsetxattr(tgt->name, name, value, size, flags);
312 ret = _setxattr(tgt->name, name, value, size, flags);
313 Py_END_ALLOW_THREADS;
317 static int _remove_obj(target_t *tgt, const char *name) {
319 Py_BEGIN_ALLOW_THREADS;
320 if(tgt->type == T_FD)
321 ret = _fremovexattr(tgt->fd, name);
322 else if (tgt->type == T_LINK)
323 ret = _lremovexattr(tgt->name, name);
325 ret = _removexattr(tgt->name, name);
326 Py_END_ALLOW_THREADS;
330 /* Perform a get/list operation with appropriate buffer size,
331 * determined dynamically.
334 * - getter: the function that actually does the I/O.
335 * - tgt, name: passed to the getter.
336 * - buffer: pointer to either an already allocated memory area (in
337 * which case size contains its current size), or NULL to
338 * allocate. In all cases (success or failure), the caller should
339 * deallocate the buffer, using PyMem_Free(). Note that if size is
340 * zero but buffer already points to allocate memory, it will be
342 * - size: either size of current buffer (if non-NULL), or size for
343 * initial allocation (if non-zero), or a zero value which means
344 * auto-allocate buffer with automatically queried size. Value will
345 * be updated upon return with the current buffer size.
346 * - io_errno: if non-NULL, the actual errno will be recorded here; if
347 * zero, the call was successful and the output/size/nval are valid.
349 * Return value: if positive or zero, buffer will contain the read
350 * value. Otherwise, io_errno will contain the I/O errno, or zero
351 * to signify a Python-level error. In all cases, the Python-level
352 * error is set to the appropriate value.
354 static ssize_t _generic_get(buf_getter getter, target_t *tgt,
359 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
361 static ssize_t _generic_get(buf_getter getter, target_t *tgt,
367 /* Clear errno for now, will only set it when it fails in I/O. */
368 if (io_errno != NULL) {
372 #define EXIT_IOERROR() \
374 if (io_errno != NULL) { \
377 PyErr_SetFromErrno(PyExc_IOError); \
381 /* Initialize the buffer, if needed. */
382 if (*size == 0 || *buffer == NULL) {
385 if ((nalloc = getter(tgt, name, NULL, 0)) == -1) {
389 /* Empty, so no need to retrieve it. */
394 if((*buffer = PyMem_Malloc(*size)) == NULL) {
399 // Try to get the value, while increasing the buffer if too small.
400 while((res = getter(tgt, name, *buffer, *size)) == -1) {
401 if(errno == ERANGE) {
402 ssize_t realloc_size_s = getter(tgt, name, NULL, 0);
403 /* ERANGE + proper size _should_ not fail, but... */
404 if(realloc_size_s == -1) {
407 size_t realloc_size = (size_t) realloc_size_s;
409 if((tmp_buf = PyMem_Realloc(*buffer, realloc_size)) == NULL) {
414 *size = realloc_size;
417 /* else we're dealing with a different error, which we
418 don't know how to handle nicely, so we return */
427 Checks if an attribute name matches an optional namespace.
429 If the namespace is NULL or an empty string, it will return the
430 name itself. If the namespace is non-NULL and the name matches, it
431 will return a pointer to the offset in the name after the namespace
432 and the separator. If however the name doesn't match the namespace,
436 const char *matches_ns(const char *ns, const char *name) {
438 if (ns == NULL || *ns == '\0')
440 ns_size = strlen(ns);
442 if (strlen(name) > (ns_size+1) && !strncmp(name, ns, ns_size) &&
443 name[ns_size] == '.')
444 return name + ns_size + 1;
448 /* Wrapper for getxattr */
449 static char __pygetxattr_doc__[] =
450 "getxattr(item, attribute[, nofollow=False])\n"
451 "Get the value of a given extended attribute (deprecated).\n"
457 ".. deprecated:: 0.4\n"
458 " this function has been deprecated\n"
459 " by the :func:`get` function.\n"
463 pygetxattr(PyObject *self, PyObject *args)
468 char *attrname = NULL;
471 size_t nalloc = ESTIMATE_ATTR_SIZE;
474 /* Parse the arguments */
475 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
477 if(convert_obj(myarg, &tgt, nofollow) < 0) {
482 nret = _generic_get(_get_obj, &tgt, attrname, &buf, &nalloc, NULL);
487 /* Create the string which will hold the result */
488 res = PyBytes_FromStringAndSize(buf, nret);
491 /* Free the buffer, now it is no longer needed */
495 PyMem_Free(attrname);
497 /* Return the result */
501 /* Wrapper for getxattr */
502 static char __get_doc__[] =
503 "get(item, name[, nofollow=False, namespace=None])\n"
504 "Get the value of a given extended attribute.\n"
507 " >>> xattr.get('/path/to/file', 'user.comment')\n"
509 " >>> xattr.get('/path/to/file', 'comment', namespace=xattr.NS_USER)\n"
516 ":return: the value of the extended attribute (can contain NULLs)\n"
518 ":raises EnvironmentError: caused by any system errors\n"
520 ".. versionadded:: 0.4\n"
525 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
530 char *attrname = NULL, *namebuf;
531 const char *fullname;
533 const char *ns = NULL;
535 size_t nalloc = ESTIMATE_ATTR_SIZE;
536 PyObject *res = NULL;
537 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
539 /* Parse the arguments */
540 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist,
541 &myarg, NULL, &attrname, &nofollow, &ns))
544 if(convert_obj(myarg, &tgt, nofollow) < 0) {
548 if(merge_ns(ns, attrname, &fullname, &namebuf) < 0) {
552 nret = _generic_get(_get_obj, &tgt, fullname, &buf, &nalloc, NULL);
557 /* Create the string which will hold the result */
558 res = PyBytes_FromStringAndSize(buf, nret);
560 /* Free the buffers, they are no longer needed */
567 PyMem_Free(attrname);
569 /* Return the result */
573 /* Wrapper for getxattr */
574 static char __get_all_doc__[] =
575 "get_all(item[, nofollow=False, namespace=None])\n"
576 "Get all the extended attributes of an item.\n"
578 "This function performs a bulk-get of all extended attribute names\n"
579 "and the corresponding value.\n"
582 " >>> xattr.get_all('/path/to/file')\n"
583 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),\n"
584 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
585 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
586 " [('mime-type', 'plain/text'), ('comment', 'test')]\n"
589 ":keyword namespace: an optional namespace for filtering the\n"
590 " attributes; for example, querying all user attributes can be\n"
591 " accomplished by passing namespace=:const:`NS_USER`\n"
592 ":type namespace: string\n"
594 ":return: list of tuples (name, value); note that if a namespace\n"
595 " argument was passed, it (and the separator) will be stripped from\n"
596 " the names returned\n"
598 ":raises EnvironmentError: caused by any system errors\n"
600 ".. note:: Since reading the whole attribute list is not an atomic\n"
601 " operation, it might be possible that attributes are added\n"
602 " or removed between the initial query and the actual reading\n"
603 " of the attributes; the returned list will contain only the\n"
604 " attributes that were present at the initial listing of the\n"
605 " attribute names and that were still present when the read\n"
606 " attempt for the value is made.\n"
607 ".. versionadded:: 0.4\n"
612 get_all(PyObject *self, PyObject *args, PyObject *keywds)
614 PyObject *myarg, *res;
616 const char *ns = NULL;
617 char *buf_list = NULL, *buf_val = NULL;
619 size_t nalloc = ESTIMATE_ATTR_SIZE;
623 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
626 /* Parse the arguments */
627 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist,
628 &myarg, &nofollow, &ns))
630 if(convert_obj(myarg, &tgt, nofollow) < 0)
634 /* Compute first the list of attributes */
635 nlist = _generic_get(_list_obj, &tgt, NULL, &buf_list,
638 /* We can't handle any errors, and the Python error is already
639 set, just bail out. */
643 /* Create the list which will hold the result. */
644 mylist = PyList_New(0);
649 nalloc = ESTIMATE_ATTR_SIZE;
650 /* Create and insert the attributes as strings in the list */
651 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
655 if((name = matches_ns(ns, s)) == NULL)
657 /* Now retrieve the attribute value */
658 nval = _generic_get(_get_obj, &tgt, s, &buf_val, &nalloc, &io_errno);
660 if (io_errno == ENODATA) {
668 my_tuple = Py_BuildValue(BYTES_TUPLE, name, buf_val, nval);
669 if (my_tuple == NULL) {
673 PyList_Append(mylist, my_tuple);
677 /* Successful exit */
684 PyMem_Free(buf_list);
689 /* Return the result */
694 static char __pysetxattr_doc__[] =
695 "setxattr(item, name, value[, flags=0, nofollow=False])\n"
696 "Set the value of a given extended attribute (deprecated).\n"
698 "Be careful in case you want to set attributes on symbolic\n"
699 "links, you have to use all the 5 parameters; use 0 for the \n"
700 "flags value if you want the default behaviour (create or "
709 ".. deprecated:: 0.4\n"
710 " this function has been deprecated\n"
711 " by the :func:`set` function.\n"
714 /* Wrapper for setxattr */
716 pysetxattr(PyObject *self, PyObject *args)
718 PyObject *myarg, *res;
720 char *attrname = NULL;
722 Py_ssize_t bufsize_s;
728 /* Parse the arguments */
729 if (!PyArg_ParseTuple(args, "Oetet#|ii", &myarg, NULL, &attrname,
730 NULL, &buf, &bufsize_s, &flags, &nofollow))
734 PyErr_SetString(PyExc_ValueError,
735 "negative value size?!");
739 bufsize = (size_t) bufsize_s;
741 if(convert_obj(myarg, &tgt, nofollow) < 0) {
746 /* Set the attribute's value */
747 nret = _set_obj(&tgt, attrname, buf, bufsize, flags);
752 res = PyErr_SetFromErrno(PyExc_IOError);
760 PyMem_Free(attrname);
763 /* Return the result */
767 static char __set_doc__[] =
768 "set(item, name, value[, flags=0, namespace=None])\n"
769 "Set the value of a given extended attribute.\n"
773 " >>> xattr.set('/path/to/file', 'user.comment', 'test')\n"
774 " >>> xattr.set('/path/to/file', 'comment', 'test',"
775 " namespace=xattr.NS_USER)\n"
784 ":raises EnvironmentError: caused by any system errors\n"
786 ".. versionadded:: 0.4\n"
790 /* Wrapper for setxattr */
792 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
794 PyObject *myarg, *res;
796 char *attrname = NULL;
798 Py_ssize_t bufsize_s;
803 const char *ns = NULL;
805 const char *full_name;
806 static char *kwlist[] = {"item", "name", "value", "flags",
807 "nofollow", "namespace", NULL};
809 /* Parse the arguments */
810 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oetet#|ii" BYTES_CHAR,
811 kwlist, &myarg, NULL, &attrname, NULL,
812 &buf, &bufsize_s, &flags, &nofollow, &ns))
816 PyErr_SetString(PyExc_ValueError,
817 "negative value size?!");
821 bufsize = (size_t) bufsize_s;
823 if(convert_obj(myarg, &tgt, nofollow) < 0) {
828 if(merge_ns(ns, attrname, &full_name, &newname) < 0) {
833 /* Set the attribute's value */
834 nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
841 res = PyErr_SetFromErrno(PyExc_IOError);
849 PyMem_Free(attrname);
852 /* Return the result */
857 static char __pyremovexattr_doc__[] =
858 "removexattr(item, name[, nofollow])\n"
859 "Remove an attribute from a file (deprecated).\n"
865 ".. deprecated:: 0.4\n"
866 " this function has been deprecated by the :func:`remove` function.\n"
869 /* Wrapper for removexattr */
871 pyremovexattr(PyObject *self, PyObject *args)
873 PyObject *myarg, *res;
875 char *attrname = NULL;
879 /* Parse the arguments */
880 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
883 if(convert_obj(myarg, &tgt, nofollow) < 0) {
888 /* Remove the attribute */
889 nret = _remove_obj(&tgt, attrname);
894 res = PyErr_SetFromErrno(PyExc_IOError);
902 PyMem_Free(attrname);
904 /* Return the result */
908 static char __remove_doc__[] =
909 "remove(item, name[, nofollow=False, namespace=None])\n"
910 "Remove an attribute from a file.\n"
914 " >>> xattr.remove('/path/to/file', 'user.comment')\n"
921 ":raises EnvironmentError: caused by any system errors\n"
923 ".. versionadded:: 0.4\n"
927 /* Wrapper for removexattr */
929 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
931 PyObject *myarg, *res;
933 char *attrname = NULL, *name_buf;
934 const char *ns = NULL;
935 const char *full_name;
938 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
940 /* Parse the arguments */
941 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist,
942 &myarg, NULL, &attrname, &nofollow, &ns))
945 if(convert_obj(myarg, &tgt, nofollow) < 0) {
950 if(merge_ns(ns, attrname, &full_name, &name_buf) < 0) {
955 /* Remove the attribute */
956 nret = _remove_obj(&tgt, full_name);
958 PyMem_Free(name_buf);
963 res = PyErr_SetFromErrno(PyExc_IOError);
971 PyMem_Free(attrname);
973 /* Return the result */
977 static char __pylistxattr_doc__[] =
978 "listxattr(item[, nofollow=False])\n"
979 "Return the list of attribute names for a file (deprecated).\n"
984 ".. deprecated:: 0.4\n"
985 " this function has been deprecated by the :func:`list` function.\n"
988 /* Wrapper for listxattr */
990 pylistxattr(PyObject *self, PyObject *args)
995 size_t nalloc = ESTIMATE_ATTR_SIZE;
1002 /* Parse the arguments */
1003 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
1005 if(convert_obj(myarg, &tgt, nofollow) < 0)
1008 nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL);
1014 /* Compute the number of attributes in the list */
1015 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
1019 /* Create the list which will hold the result */
1020 mylist = PyList_New(nattrs);
1021 if(mylist == NULL) {
1025 /* Create and insert the attributes as strings in the list */
1026 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1027 PyObject *item = PyBytes_FromString(s);
1033 PyList_SET_ITEM(mylist, nattrs, item);
1038 /* 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)
1078 size_t nalloc = ESTIMATE_ATTR_SIZE;
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))
1092 if(convert_obj(myarg, &tgt, nofollow) < 0) {
1095 nret = _generic_get(_list_obj, &tgt, NULL, &buf, &nalloc, NULL);
1100 /* Compute the number of attributes in the list */
1101 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
1102 if(matches_ns(ns, s) != NULL)
1106 /* Create the list which will hold the result */
1107 if((res = PyList_New(nattrs)) == NULL) {
1111 /* Create and insert the attributes as strings in the list */
1112 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
1113 const char *name = matches_ns(ns, s);
1115 PyObject *item = PyBytes_FromString(name);
1121 PyList_SET_ITEM(res, nattrs, item);
1127 /* Free the buffer, now it is no longer needed */
1134 /* Return the result */
1138 static PyMethodDef xattr_methods[] = {
1139 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
1140 {"get", (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
1142 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
1144 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
1145 {"set", (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
1147 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
1148 {"remove", (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
1150 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
1151 {"list", (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
1153 {NULL, NULL, 0, NULL} /* Sentinel */
1156 static char __xattr_doc__[] = \
1157 "This module gives access to the extended attributes present\n"
1158 "in some operating systems/filesystems. You can list attributes,\n"
1159 "get, set and remove them.\n"
1161 "The module exposes two sets of functions:\n"
1162 " - the 'old' :func:`listxattr`, :func:`getxattr`, :func:`setxattr`,\n"
1163 " :func:`removexattr`\n"
1164 " functions which are deprecated since version 0.4\n"
1165 " - the new :func:`list`, :func:`get`, :func:`get_all`, :func:`set`,\n"
1166 " :func:`remove` functions\n"
1167 " which expose a namespace-aware API and simplify a bit the calling\n"
1168 " model by using keyword arguments\n"
1171 " >>> import xattr\n"
1172 " >>> xattr.listxattr(\"file.txt\")\n"
1173 " ['user.mime_type']\n"
1174 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
1176 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
1177 "\"Simple text file\")\n"
1178 " >>> xattr.listxattr(\"file.txt\")\n"
1179 " ['user.mime_type', 'user.comment']\n"
1180 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
1182 ".. note:: Most or all errors reported by the system while using\n"
1183 " the ``xattr`` library will be reported by raising\n"
1184 " a :exc:`EnvironmentError`; under\n"
1185 " Linux, the following ``errno`` values are used:\n"
1187 " - ``ENODATA`` means that the attribute name is invalid\n"
1188 " - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n"
1189 " support extended attributes, or that the namespace is invalid\n"
1190 " - ``E2BIG`` mean that the attribute value is too big\n"
1191 " - ``ERANGE`` mean that the attribute name is too big (it might also\n"
1192 " mean an error in the xattr module itself)\n"
1193 " - ``ENOSPC`` and ``EDQUOT`` are documented as meaning out of disk\n"
1194 " space or out of disk space because of quota limits\n"
1195 ".. note:: Under Python 3, the namespace argument is a byte string,\n"
1196 " not a unicode string.\n"
1202 static struct PyModuleDef xattrmodule = {
1203 PyModuleDef_HEAD_INIT,
1210 #define INITERROR return NULL
1216 #define INITERROR return
1221 PyObject *ns_security = NULL;
1222 PyObject *ns_system = NULL;
1223 PyObject *ns_trusted = NULL;
1224 PyObject *ns_user = NULL;
1226 PyObject *m = PyModule_Create(&xattrmodule);
1228 PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
1233 PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
1234 PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
1235 PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
1236 PyModule_AddStringConstant(m, "__license__",
1237 "GNU Lesser General Public License (LGPL)");
1238 PyModule_AddStringConstant(m, "__docformat__", "restructuredtext en");
1240 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
1241 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
1243 /* namespace constants */
1244 if((ns_security = PyBytes_FromString("security")) == NULL)
1246 if((ns_system = PyBytes_FromString("system")) == NULL)
1248 if((ns_trusted = PyBytes_FromString("trusted")) == NULL)
1250 if((ns_user = PyBytes_FromString("user")) == NULL)
1252 if(PyModule_AddObject(m, "NS_SECURITY", ns_security) < 0)
1255 if(PyModule_AddObject(m, "NS_SYSTEM", ns_system) < 0)
1258 if(PyModule_AddObject(m, "NS_TRUSTED", ns_trusted) < 0)
1261 if(PyModule_AddObject(m, "NS_USER", ns_user) < 0)
1272 Py_XDECREF(ns_user);
1273 Py_XDECREF(ns_trusted);
1274 Py_XDECREF(ns_system);
1275 Py_XDECREF(ns_security);