2 xattr - a python module for manipulating filesystem extended attributes
4 Copyright (C) 2002, 2003, 2006, 2008, 2012 Iustin Pop <iusty@k1024.org>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 #define PY_SSIZE_T_CLEAN
25 #include <attr/xattr.h>
28 /* Compatibility with python 2.4 regarding python size type (PEP 353) */
29 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
30 typedef int Py_ssize_t;
31 #define PY_SSIZE_T_MAX INT_MAX
32 #define PY_SSIZE_T_MIN INT_MIN
35 #if PY_MAJOR_VERSION >= 3
37 #define BYTES_CHAR "y"
39 #define BYTES_CHAR "z"
40 #define PyBytes_Check PyString_Check
41 #define PyBytes_AS_STRING PyString_AS_STRING
42 #define PyBytes_FromStringAndSize PyString_FromStringAndSize
43 #define PyBytes_FromString PyString_FromString
47 ":param item: a string representing a file-name, or a file-like\n" \
48 " object, or a file descriptor; this represents the file on \n" \
51 #define NOFOLLOW_DOC \
52 ":param nofollow: if true and if\n" \
53 " the file name given is a symbolic link, the\n" \
54 " function will operate on the symbolic link itself instead\n" \
55 " of its target; defaults to false\n" \
56 ":type nofollow: boolean, optional\n" \
59 ":param namespace: if given, the attribute must not contain the\n" \
60 " namespace, but instead it will be taken from this parameter\n" \
61 ":type namespace: bytes\n"
63 #define NAME_GET_DOC \
64 ":param string name: the attribute whose value to retrieve;\n" \
65 " usually in the form of ``system.posix_acl`` or ``user.mime_type``\n"
67 #define NAME_SET_DOC \
68 ":param string name: the attribute whose value to set;\n" \
69 " usually in the form of ``system.posix_acl`` or ``user.mime_type``\n"
71 #define NAME_REMOVE_DOC \
72 ":param string name: the attribute to remove;\n" \
73 " usually in the form of ``system.posix_acl`` or \n" \
74 " ``user.mime_type``\n"
77 ":param string value: possibly with embedded NULLs; note that there\n" \
78 " are restrictions regarding the size of the value, for\n" \
79 " example, for ext2/ext3, maximum size is the block size\n" \
82 ":param flags: if 0 or omitted the attribute will be\n" \
83 " created or replaced; if :const:`XATTR_CREATE`, the attribute\n" \
84 " will be created, giving an error if it already exists;\n" \
85 " if :const:`XATTR_REPLACE`, the attribute will be replaced,\n" \
86 " giving an error if it doesn't exist;\n" \
87 ":type flags: integer\n"
90 /* the estimated (startup) attribute buffer size in
92 #define ESTIMATE_ATTR_SIZE 256
94 typedef enum {T_FD, T_PATH, T_LINK} target_e;
105 /* Cleans up a tgt structure */
106 static void free_tgt(target_t *tgt) {
107 if (tgt->tmp != NULL) {
112 /* Used for cpychecker: */
113 /* The checker automatically defines this preprocessor name when creating
114 the custom attribute: */
115 #if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
116 #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION \
117 __attribute__((cpychecker_negative_result_sets_exception))
119 #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
122 static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow)
123 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
125 static int merge_ns(const char *ns, const char *name,
126 const char **result, char **buf)
127 CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
130 /** Converts from a string, file or int argument to what we need.
132 * Returns -1 on failure, 0 on success.
134 static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow) {
137 if(PyBytes_Check(myobj)) {
138 tgt->type = nofollow ? T_LINK : T_PATH;
139 tgt->name = PyBytes_AS_STRING(myobj);
140 } else if(PyUnicode_Check(myobj)) {
141 tgt->type = nofollow ? T_LINK : T_PATH;
143 PyUnicode_AsEncodedString(myobj,
144 Py_FileSystemDefaultEncoding, "strict");
147 tgt->name = PyBytes_AS_STRING(tgt->tmp);
148 } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
152 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 size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
165 if((*buf = PyMem_Malloc(new_size)) == NULL) {
169 cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
170 if(cnt > new_size || cnt < 0) {
171 PyErr_SetString(PyExc_ValueError,
172 "can't format the attribute name");
184 static ssize_t _list_obj(target_t *tgt, char *list, size_t size) {
185 if(tgt->type == T_FD)
186 return flistxattr(tgt->fd, list, size);
187 else if (tgt->type == T_LINK)
188 return llistxattr(tgt->name, list, size);
190 return listxattr(tgt->name, list, size);
193 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
195 if(tgt->type == T_FD)
196 return fgetxattr(tgt->fd, name, value, size);
197 else if (tgt->type == T_LINK)
198 return lgetxattr(tgt->name, name, value, size);
200 return getxattr(tgt->name, name, value, size);
203 static int _set_obj(target_t *tgt, const char *name,
204 const void *value, size_t size, int flags) {
205 if(tgt->type == T_FD)
206 return fsetxattr(tgt->fd, name, value, size, flags);
207 else if (tgt->type == T_LINK)
208 return lsetxattr(tgt->name, name, value, size, flags);
210 return setxattr(tgt->name, name, value, size, flags);
213 static int _remove_obj(target_t *tgt, const char *name) {
214 if(tgt->type == T_FD)
215 return fremovexattr(tgt->fd, name);
216 else if (tgt->type == T_LINK)
217 return lremovexattr(tgt->name, name);
219 return removexattr(tgt->name, name);
223 Checks if an attribute name matches an optional namespace.
225 If the namespace is NULL, it will return the name itself. If the
226 namespace is non-NULL and the name matches, it will return a
227 pointer to the offset in the name after the namespace and the
228 separator. If however the name doesn't match the namespace, it will
231 const char *matches_ns(const char *ns, const char *name) {
235 ns_size = strlen(ns);
237 if (strlen(name) > (ns_size+1) && !strncmp(name, ns, ns_size) &&
238 name[ns_size] == '.')
239 return name + ns_size + 1;
243 /* Wrapper for getxattr */
244 static char __pygetxattr_doc__[] =
245 "getxattr(item, attribute[, nofollow=False])\n"
246 "Get the value of a given extended attribute (deprecated).\n"
252 ".. deprecated:: 0.4\n"
253 " this function has been deprecated\n"
254 " by the :func:`get` function.\n"
258 pygetxattr(PyObject *self, PyObject *args)
263 char *attrname = NULL;
265 ssize_t nalloc, nret;
268 /* Parse the arguments */
269 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
271 if(convert_obj(myarg, &tgt, nofollow) < 0) {
276 /* Find out the needed size of the buffer */
277 if((nalloc = _get_obj(&tgt, attrname, NULL, 0)) == -1) {
278 res = PyErr_SetFromErrno(PyExc_IOError);
282 /* Try to allocate the memory, using Python's allocator */
283 if((buf = PyMem_Malloc(nalloc)) == NULL) {
284 res = PyErr_NoMemory();
288 /* Now retrieve the attribute value */
289 if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) {
290 res = PyErr_SetFromErrno(PyExc_IOError);
294 /* Create the string which will hold the result */
295 res = PyBytes_FromStringAndSize(buf, nret);
298 /* Free the buffer, now it is no longer needed */
303 PyMem_Free(attrname);
305 /* Return the result */
309 /* Wrapper for getxattr */
310 static char __get_doc__[] =
311 "get(item, name[, nofollow=False, namespace=None])\n"
312 "Get the value of a given extended attribute.\n"
315 " >>> xattr.get('/path/to/file', 'user.comment')\n"
317 " >>> xattr.get('/path/to/file', 'comment', namespace=xattr.NS_USER)\n"
324 ":return: the value of the extended attribute (can contain NULLs)\n"
326 ":raises EnvironmentError: caused by any system errors\n"
328 ".. versionadded:: 0.4\n"
332 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
337 char *attrname = NULL, *namebuf;
338 const char *fullname;
340 const char *ns = NULL;
341 ssize_t nalloc, nret;
343 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
345 /* Parse the arguments */
346 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist,
347 &myarg, NULL, &attrname, &nofollow, &ns))
349 if(convert_obj(myarg, &tgt, nofollow) < 0) {
354 if(merge_ns(ns, attrname, &fullname, &namebuf) < 0) {
359 /* Find out the needed size of the buffer */
360 if((nalloc = _get_obj(&tgt, fullname, NULL, 0)) == -1) {
361 res = PyErr_SetFromErrno(PyExc_IOError);
365 /* Try to allocate the memory, using Python's allocator */
366 if((buf = PyMem_Malloc(nalloc)) == NULL) {
367 res = PyErr_NoMemory();
371 /* Now retrieve the attribute value */
372 if((nret = _get_obj(&tgt, fullname, buf, nalloc)) == -1) {
373 res = PyErr_SetFromErrno(PyExc_IOError);
377 /* Create the string which will hold the result */
378 res = PyBytes_FromStringAndSize(buf, nret);
380 /* Free the buffers, they are no longer needed */
388 PyMem_Free(attrname);
390 /* Return the result */
394 /* Wrapper for getxattr */
395 static char __get_all_doc__[] =
396 "get_all(item[, nofollow=False, namespace=None])\n"
397 "Get all the extended attributes of an item.\n"
399 "This function performs a bulk-get of all extended attribute names\n"
400 "and the corresponding value.\n"
403 " >>> xattr.get_all('/path/to/file')\n"
404 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),\n"
405 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
406 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
407 " [('mime-type', 'plain/text'), ('comment', 'test')]\n"
410 ":keyword namespace: an optional namespace for filtering the\n"
411 " attributes; for example, querying all user attributes can be\n"
412 " accomplished by passing namespace=:const:`NS_USER`\n"
413 ":type namespace: string\n"
415 ":return: list of tuples (name, value); note that if a namespace\n"
416 " argument was passed, it (and the separator) will be stripped from\n"
417 " the names returned\n"
419 ":raises EnvironmentError: caused by any system errors\n"
421 ".. note:: Since reading the whole attribute list is not an atomic\n"
422 " operation, it might be possible that attributes are added\n"
423 " or removed between the initial query and the actual reading\n"
424 " of the attributes; the returned list will contain only the\n"
425 " attributes that were present at the initial listing of the\n"
426 " attribute names and that were still present when the read\n"
427 " attempt for the value is made.\n"
428 ".. versionadded:: 0.4\n"
432 get_all(PyObject *self, PyObject *args, PyObject *keywds)
434 PyObject *myarg, *res;
436 const char *ns = NULL;
437 char *buf_list, *buf_val;
439 ssize_t nalloc, nlist, nval;
442 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
444 /* Parse the arguments */
445 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist,
446 &myarg, &nofollow, &ns))
448 if(convert_obj(myarg, &tgt, nofollow) < 0)
451 /* Compute first the list of attributes */
453 /* Find out the needed size of the buffer for the attribute list */
454 nalloc = _list_obj(&tgt, NULL, 0);
457 res = PyErr_SetFromErrno(PyExc_IOError);
461 /* Try to allocate the memory, using Python's allocator */
462 if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
463 res = PyErr_NoMemory();
467 /* Now retrieve the list of attributes */
468 nlist = _list_obj(&tgt, buf_list, nalloc);
471 res = PyErr_SetFromErrno(PyExc_IOError);
475 /* Create the list which will hold the result */
476 mylist = PyList_New(0);
482 nalloc = ESTIMATE_ATTR_SIZE;
483 if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
485 res = PyErr_NoMemory();
489 /* Create and insert the attributes as strings in the list */
490 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
495 if((name=matches_ns(ns, s))==NULL)
497 /* Now retrieve the attribute value */
500 nval = _get_obj(&tgt, s, buf_val, nalloc);
503 if(errno == ERANGE) {
504 nval = _get_obj(&tgt, s, NULL, 0);
505 if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL) {
506 res = PyErr_NoMemory();
512 } else if(errno == ENODATA || errno == ENOATTR) {
513 /* this attribute has gone away since we queried
514 the attribute list */
518 /* else we're dealing with a different error, which we
519 don't know how to handle nicely, so we abort */
521 res = PyErr_SetFromErrno(PyExc_IOError);
529 my_tuple = Py_BuildValue("yy#", name, buf_val, nval);
531 my_tuple = Py_BuildValue("ss#", name, buf_val, nval);
533 if (my_tuple == NULL) {
538 PyList_Append(mylist, my_tuple);
542 /* Successful exit */
549 PyMem_Free(buf_list);
554 /* Return the result */
559 static char __pysetxattr_doc__[] =
560 "setxattr(item, name, value[, flags=0, nofollow=False])\n"
561 "Set the value of a given extended attribute (deprecated).\n"
563 "Be careful in case you want to set attributes on symbolic\n"
564 "links, you have to use all the 5 parameters; use 0 for the \n"
565 "flags value if you want the default behaviour (create or "
574 ".. deprecated:: 0.4\n"
575 " this function has been deprecated\n"
576 " by the :func:`set` function.\n"
579 /* Wrapper for setxattr */
581 pysetxattr(PyObject *self, PyObject *args)
583 PyObject *myarg, *res;
585 char *attrname = NULL;
592 /* Parse the arguments */
593 if (!PyArg_ParseTuple(args, "Oetet#|ii", &myarg, NULL, &attrname,
594 NULL, &buf, &bufsize, &flags, &nofollow))
596 if(convert_obj(myarg, &tgt, nofollow) < 0) {
601 /* Set the attribute's value */
602 nret = _set_obj(&tgt, attrname, buf, bufsize, flags);
607 res = PyErr_SetFromErrno(PyExc_IOError);
615 PyMem_Free(attrname);
618 /* Return the result */
622 static char __set_doc__[] =
623 "set(item, name, value[, flags=0, namespace=None])\n"
624 "Set the value of a given extended attribute.\n"
628 " >>> xattr.set('/path/to/file', 'user.comment', 'test')\n"
629 " >>> xattr.set('/path/to/file', 'comment', 'test',"
630 " namespace=xattr.NS_USER)\n"
639 ":raises EnvironmentError: caused by any system errors\n"
641 ".. versionadded:: 0.4\n"
644 /* Wrapper for setxattr */
646 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
648 PyObject *myarg, *res;
650 char *attrname = NULL;
656 const char *ns = NULL;
658 const char *full_name;
659 static char *kwlist[] = {"item", "name", "value", "flags",
660 "nofollow", "namespace", NULL};
662 /* Parse the arguments */
663 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oetet#|ii" BYTES_CHAR,
664 kwlist, &myarg, NULL, &attrname, NULL,
665 &buf, &bufsize, &flags, &nofollow, &ns))
667 if(convert_obj(myarg, &tgt, nofollow) < 0) {
672 if(merge_ns(ns, attrname, &full_name, &newname) < 0) {
677 /* Set the attribute's value */
678 nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
685 res = PyErr_SetFromErrno(PyExc_IOError);
693 PyMem_Free(attrname);
696 /* Return the result */
701 static char __pyremovexattr_doc__[] =
702 "removexattr(item, name[, nofollow])\n"
703 "Remove an attribute from a file (deprecated).\n"
709 ".. deprecated:: 0.4\n"
710 " this function has been deprecated by the :func:`remove` function.\n"
713 /* Wrapper for removexattr */
715 pyremovexattr(PyObject *self, PyObject *args)
717 PyObject *myarg, *res;
719 char *attrname = NULL;
723 /* Parse the arguments */
724 if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow))
727 if(convert_obj(myarg, &tgt, nofollow) < 0) {
732 /* Remove the attribute */
733 nret = _remove_obj(&tgt, attrname);
738 res = PyErr_SetFromErrno(PyExc_IOError);
746 PyMem_Free(attrname);
748 /* Return the result */
752 static char __remove_doc__[] =
753 "remove(item, name[, nofollow=False, namespace=None])\n"
754 "Remove an attribute from a file.\n"
758 " >>> xattr.remove('/path/to/file', 'user.comment')\n"
765 ":raises EnvironmentError: caused by any system errors\n"
767 ".. versionadded:: 0.4\n"
770 /* Wrapper for removexattr */
772 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
774 PyObject *myarg, *res;
776 char *attrname = NULL, *name_buf;
777 const char *ns = NULL;
778 const char *full_name;
781 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
783 /* Parse the arguments */
784 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist,
785 &myarg, NULL, &attrname, &nofollow, &ns))
788 if(convert_obj(myarg, &tgt, nofollow) < 0) {
793 if(merge_ns(ns, attrname, &full_name, &name_buf) < 0) {
798 /* Remove the attribute */
799 nret = _remove_obj(&tgt, full_name);
801 PyMem_Free(name_buf);
806 res = PyErr_SetFromErrno(PyExc_IOError);
814 PyMem_Free(attrname);
816 /* Return the result */
820 static char __pylistxattr_doc__[] =
821 "listxattr(item[, nofollow=False])\n"
822 "Return the list of attribute names for a file (deprecated).\n"
827 ".. deprecated:: 0.4\n"
828 " this function has been deprecated by the :func:`list` function.\n"
831 /* Wrapper for listxattr */
833 pylistxattr(PyObject *self, PyObject *args)
837 ssize_t nalloc, nret;
844 /* Parse the arguments */
845 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
847 if(convert_obj(myarg, &tgt, nofollow) < 0)
850 /* Find out the needed size of the buffer */
851 if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
852 mylist = PyErr_SetFromErrno(PyExc_IOError);
856 /* Try to allocate the memory, using Python's allocator */
857 if((buf = PyMem_Malloc(nalloc)) == NULL) {
858 mylist = PyErr_NoMemory();
862 /* Now retrieve the list of attributes */
863 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
864 mylist = PyErr_SetFromErrno(PyExc_IOError);
868 /* Compute the number of attributes in the list */
869 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
873 /* Create the list which will hold the result */
874 mylist = PyList_New(nattrs);
878 /* Create and insert the attributes as strings in the list */
879 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
880 PyObject *item = PyBytes_FromString(s);
886 PyList_SET_ITEM(mylist, nattrs, item);
891 /* Free the buffer, now it is no longer needed */
897 /* Return the result */
901 static char __list_doc__[] =
902 "list(item[, nofollow=False, namespace=None])\n"
903 "Return the list of attribute names for a file.\n"
907 " >>> xattr.list('/path/to/file')\n"
908 " ['user.test', 'user.comment', 'system.posix_acl_access']\n"
909 " >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
910 " ['test', 'comment']\n"
915 ":returns: the list of attributes; note that if a namespace \n"
916 " argument was passed, it (and the separator) will be stripped\n"
920 ":raises EnvironmentError: caused by any system errors\n"
922 ".. versionadded:: 0.4\n"
925 /* Wrapper for listxattr */
927 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
931 ssize_t nalloc, nret;
934 const char *ns = NULL;
938 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
940 /* Parse the arguments */
941 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist,
942 &myarg, &nofollow, &ns))
944 if(convert_obj(myarg, &tgt, nofollow) < 0) {
949 /* Find out the needed size of the buffer */
950 if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
951 res = PyErr_SetFromErrno(PyExc_IOError);
955 /* Try to allocate the memory, using Python's allocator */
956 if((buf = PyMem_Malloc(nalloc)) == NULL) {
957 res = PyErr_NoMemory();
961 /* Now retrieve the list of attributes */
962 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
963 res = PyErr_SetFromErrno(PyExc_IOError);
967 /* Compute the number of attributes in the list */
968 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
969 if(matches_ns(ns, s) != NULL)
972 /* Create the list which will hold the result */
973 res = PyList_New(nattrs);
977 /* Create and insert the attributes as strings in the list */
978 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
979 const char *name = matches_ns(ns, s);
981 PyObject *item = PyBytes_FromString(name);
987 PyList_SET_ITEM(res, nattrs, item);
993 /* Free the buffer, now it is no longer needed */
1000 /* Return the result */
1004 static PyMethodDef xattr_methods[] = {
1005 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
1006 {"get", (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
1008 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
1010 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
1011 {"set", (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
1013 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
1014 {"remove", (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
1016 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
1017 {"list", (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
1019 {NULL, NULL, 0, NULL} /* Sentinel */
1022 static char __xattr_doc__[] = \
1023 "This module gives access to the extended attributes present\n"
1024 "in some operating systems/filesystems. You can list attributes,\n"
1025 "get, set and remove them.\n"
1027 "The module exposes two sets of functions:\n"
1028 " - the 'old' :func:`listxattr`, :func:`getxattr`, :func:`setxattr`,\n"
1029 " :func:`removexattr`\n"
1030 " functions which are deprecated since version 0.4\n"
1031 " - the new :func:`list`, :func:`get`, :func:`get_all`, :func:`set`,\n"
1032 " :func:`remove` functions\n"
1033 " which expose a namespace-aware API and simplify a bit the calling\n"
1034 " model by using keyword arguments\n"
1037 " >>> import xattr\n"
1038 " >>> xattr.listxattr(\"file.txt\")\n"
1039 " ['user.mime_type']\n"
1040 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
1042 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
1043 "\"Simple text file\")\n"
1044 " >>> xattr.listxattr(\"file.txt\")\n"
1045 " ['user.mime_type', 'user.comment']\n"
1046 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
1048 ".. note:: Most or all errors reported by the system while using\n"
1049 " the ``xattr`` library will be reported by raising\n"
1050 " a :exc:`EnvironmentError`; under\n"
1051 " Linux, the following ``errno`` values are used:\n"
1053 " - ``ENOATTR`` and ``ENODATA`` mean that the attribute name is\n"
1055 " - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n"
1056 " support extended attributes, or that the namespace is invalid\n"
1057 " - ``E2BIG`` mean that the attribute value is too big\n"
1058 " - ``ERANGE`` mean that the attribute name is too big (it might also\n"
1059 " mean an error in the xattr module itself)\n"
1060 " - ``ENOSPC`` and ``EDQUOT`` are documented as meaning out of disk\n"
1061 " space or out of disk space because of quota limits\n"
1067 static struct PyModuleDef xattrmodule = {
1068 PyModuleDef_HEAD_INIT,
1075 #define INITERROR return NULL
1081 #define INITERROR return
1086 PyObject *ns_security = NULL;
1087 PyObject *ns_system = NULL;
1088 PyObject *ns_trusted = NULL;
1089 PyObject *ns_user = NULL;
1091 PyObject *m = PyModule_Create(&xattrmodule);
1093 PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
1098 PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
1099 PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
1100 PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
1101 PyModule_AddStringConstant(m, "__license__",
1102 "GNU Lesser General Public License (LGPL)");
1103 PyModule_AddStringConstant(m, "__docformat__", "restructuredtext en");
1105 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
1106 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
1108 /* namespace constants */
1109 if((ns_security = PyBytes_FromString("security")) == NULL)
1111 if((ns_system = PyBytes_FromString("system")) == NULL)
1113 if((ns_trusted = PyBytes_FromString("trusted")) == NULL)
1115 if((ns_user = PyBytes_FromString("user")) == NULL)
1117 if(PyModule_AddObject(m, "NS_SECURITY", ns_security) < 0)
1120 if(PyModule_AddObject(m, "NS_SYSTEM", ns_system) < 0)
1123 if(PyModule_AddObject(m, "NS_TRUSTED", ns_trusted) < 0)
1126 if(PyModule_AddObject(m, "NS_USER", ns_user) < 0)
1137 Py_XDECREF(ns_user);
1138 Py_XDECREF(ns_trusted);
1139 Py_XDECREF(ns_system);
1140 Py_XDECREF(ns_security);