2 #include <attr/xattr.h>
5 /* the estimated (startup) attribute buffer size in
7 #define ESTIMATE_ATTR_SIZE 256
9 typedef enum {T_FD, T_PATH, T_LINK} target_e;
19 /** Converts from a string, file or int argument to what we need. */
20 static int convertObj(PyObject *myobj, target_t *tgt, int nofollow) {
22 if(PyString_Check(myobj)) {
23 tgt->type = nofollow ? T_LINK : T_PATH;
24 tgt->name = PyString_AS_STRING(myobj);
25 } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
29 PyErr_SetString(PyExc_TypeError, "argument must be string or int");
35 /* Combine a namespace string and an attribute name into a
36 fully-qualified name */
37 static const char* merge_ns(const char *ns, const char *name, char **buf) {
40 size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
41 if((*buf = PyMem_Malloc(new_size)) == NULL) {
45 cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
46 if(cnt > new_size || cnt < 0) {
47 PyErr_SetString(PyExc_ValueError,
48 "can't format the attribute name");
59 static ssize_t _list_obj(target_t *tgt, char *list, size_t size) {
61 return flistxattr(tgt->fd, list, size);
62 else if (tgt->type == T_LINK)
63 return llistxattr(tgt->name, list, size);
65 return listxattr(tgt->name, list, size);
68 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
71 return fgetxattr(tgt->fd, name, value, size);
72 else if (tgt->type == T_LINK)
73 return lgetxattr(tgt->name, name, value, size);
75 return getxattr(tgt->name, name, value, size);
78 static ssize_t _set_obj(target_t *tgt, const char *name,
79 const void *value, size_t size,
82 return fsetxattr(tgt->fd, name, value, size, flags);
83 else if (tgt->type == T_LINK)
84 return lsetxattr(tgt->name, name, value, size, flags);
86 return setxattr(tgt->name, name, value, size, flags);
89 static ssize_t _remove_obj(target_t *tgt, const char *name) {
91 return fremovexattr(tgt->fd, name);
92 else if (tgt->type == T_LINK)
93 return lremovexattr(tgt->name, name);
95 return removexattr(tgt->name, name);
99 Checks if an attribute name matches an optional namespace.
101 If the namespace is NULL, it will return the name itself. If the
102 namespace is non-NULL and the name matches, it will return a
103 pointer to the offset in the name after the namespace and the
104 separator. If however the name doesn't match the namespace, it will
107 const char *matches_ns(const char *ns, const char *name) {
111 ns_size = strlen(ns);
113 if (strlen(name) > (ns_size+1) && !strncmp(name, ns, ns_size) &&
114 name[ns_size] == '.')
115 return name + ns_size + 1;
119 /* Wrapper for getxattr */
120 static char __pygetxattr_doc__[] =
121 "Get the value of a given extended attribute (deprecated).\n"
124 " - a string representing filename, or a file-like object,\n"
125 " or a file descriptor; this represents the file on \n"
127 " - a string, representing the attribute whose value to retrieve;\n"
128 " usually in form of system.posix_acl or user.mime_type\n"
129 " - (optional) a boolean value (defaults to false), which, if\n"
130 " the file name given is a symbolic link, makes the\n"
131 " function operate on the symbolic link itself instead\n"
133 "@deprecated: this function has been deprecated by the L{get} function\n"
137 pygetxattr(PyObject *self, PyObject *args)
147 /* Parse the arguments */
148 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
150 if(!convertObj(myarg, &tgt, nofollow))
153 /* Find out the needed size of the buffer */
154 if((nalloc = _get_obj(&tgt, attrname, NULL, 0)) == -1) {
155 return PyErr_SetFromErrno(PyExc_IOError);
158 /* Try to allocate the memory, using Python's allocator */
159 if((buf = PyMem_Malloc(nalloc)) == NULL) {
164 /* Now retrieve the attribute value */
165 if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) {
167 return PyErr_SetFromErrno(PyExc_IOError);
170 /* Create the string which will hold the result */
171 res = PyString_FromStringAndSize(buf, nret);
173 /* Free the buffer, now it is no longer needed */
176 /* Return the result */
180 /* Wrapper for getxattr */
181 static char __get_doc__[] =
182 "Get the value of a given extended attribute.\n"
184 "@param item: the item to query; either a string representing the"
185 " filename, or a file-like object, or a file descriptor\n"
186 "@param name: the attribute whose value to set; usually in form of"
187 " system.posix_acl or user.mime_type\n"
188 "@type name: string\n"
189 "@param nofollow: if given and True, and the function is passed a"
190 " filename that points to a symlink, the function will act on the symlink"
191 " itself instead of its target\n"
192 "@type nofollow: boolean\n"
193 "@param namespace: if given, the attribute must not contain the namespace"
194 " itself, but instead the namespace will be taken from this parameter\n"
195 "@type namespace: string\n"
196 "@return: the value of the extended attribute (can contain NULLs)\n"
198 "@raise EnvironmentError: system errors will raise an exception\n"
203 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
208 char *attrname, *namebuf;
209 const char *fullname;
214 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
216 /* Parse the arguments */
217 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist,
218 &myarg, &attrname, &nofollow, &ns))
220 if(!convertObj(myarg, &tgt, nofollow))
223 fullname = merge_ns(ns, attrname, &namebuf);
225 /* Find out the needed size of the buffer */
226 if((nalloc = _get_obj(&tgt, fullname, NULL, 0)) == -1) {
227 return PyErr_SetFromErrno(PyExc_IOError);
230 /* Try to allocate the memory, using Python's allocator */
231 if((buf = PyMem_Malloc(nalloc)) == NULL) {
237 /* Now retrieve the attribute value */
238 if((nret = _get_obj(&tgt, fullname, buf, nalloc)) == -1) {
241 return PyErr_SetFromErrno(PyExc_IOError);
244 /* Create the string which will hold the result */
245 res = PyString_FromStringAndSize(buf, nret);
247 /* Free the buffers, they are no longer needed */
251 /* Return the result */
255 /* Wrapper for getxattr */
256 static char __get_all_doc__[] =
257 "Get all the extended attributes of an item.\n"
259 "This function performs a bulk-get of all extended attribute names\n"
260 "and the corresponding value.\n"
262 " >>> xattr.get_all('/path/to/file')\n"
263 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),"
264 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
265 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
266 " [('mime-type', 'plain/text'), ('comment', 'test')]\n"
268 "@param item: the item to query; either a string representing the"
269 " filename, or a file-like object, or a file descriptor\n"
270 "@keyword namespace: an optional namespace for filtering the"
271 " attributes; for example, querying all user attributes can be"
272 " accomplished by passing namespace=L{NS_USER}\n"
273 "@type namespace: string\n"
274 "@keyword nofollow: if passed and true, if the target file is a symbolic"
276 " the attributes for the link itself will be returned, instead of the"
277 " attributes of the target\n"
278 "@type nofollow: boolean\n"
279 "@return: list of tuples (name, value); note that if a namespace\n"
280 "argument was passed, it (and the separator) will be stripped from\n"
281 "the names returned\n"
283 "@raise EnvironmentError: system errors will raise an exception\n"
288 get_all(PyObject *self, PyObject *args, PyObject *keywds)
293 char *buf_list, *buf_val;
295 size_t nalloc, nlist, nval;
298 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
300 /* Parse the arguments */
301 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
302 &myarg, &dolink, &ns))
304 if(!convertObj(myarg, &tgt, dolink))
307 /* Compute first the list of attributes */
309 /* Find out the needed size of the buffer for the attribute list */
310 nalloc = _list_obj(&tgt, NULL, 0);
313 return PyErr_SetFromErrno(PyExc_IOError);
316 /* Try to allocate the memory, using Python's allocator */
317 if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
322 /* Now retrieve the list of attributes */
323 nlist = _list_obj(&tgt, buf_list, nalloc);
326 PyErr_SetFromErrno(PyExc_IOError);
330 /* Create the list which will hold the result */
331 mylist = PyList_New(0);
332 nalloc = ESTIMATE_ATTR_SIZE;
333 if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
338 /* Create and insert the attributes as strings in the list */
339 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
344 if((name=matches_ns(ns, s))==NULL)
346 /* Now retrieve the attribute value */
349 nval = _get_obj(&tgt, s, buf_val, nalloc);
352 if(errno == ERANGE) {
353 nval = _get_obj(&tgt, s, NULL, 0);
354 if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL)
358 } else if(errno == ENODATA || errno == ENOATTR) {
359 /* this attribute has gone away since we queried
360 the attribute list */
370 my_tuple = Py_BuildValue("ss#", name, buf_val, nval);
372 PyList_Append(mylist, my_tuple);
376 /* Free the buffers, now they are no longer needed */
378 PyMem_Free(buf_list);
380 /* Return the result */
383 PyErr_SetFromErrno(PyExc_IOError);
388 PyMem_Free(buf_list);
393 static char __pysetxattr_doc__[] =
394 "Set the value of a given extended attribute (deprecated).\n"
395 "Be carefull in case you want to set attributes on symbolic\n"
396 "links, you have to use all the 5 parameters; use 0 for the \n"
397 "flags value if you want the default behavior (create or "
401 " - a string representing filename, or a file-like object,\n"
402 " or a file descriptor; this represents the file on \n"
404 " - a string, representing the attribute whose value to set;\n"
405 " usually in form of system.posix_acl or user.mime_type\n"
406 " - a string, possibly with embedded NULLs; note that there\n"
407 " are restrictions regarding the size of the value, for\n"
408 " example, for ext2/ext3, maximum size is the block size\n"
409 " - (optional) flags; if 0 or ommited the attribute will be \n"
410 " created or replaced; if XATTR_CREATE, the attribute \n"
411 " will be created, giving an error if it already exists;\n"
412 " of XATTR_REPLACE, the attribute will be replaced,\n"
413 " giving an error if it doesn't exists;\n"
414 " - (optional) a boolean value (defaults to false), which, if\n"
415 " the file name given is a symbolic link, makes the\n"
416 " function operate on the symbolic link itself instead\n"
418 "@deprecated: this function has been deprecated by the L{set} function\n"
421 /* Wrapper for setxattr */
423 pysetxattr(PyObject *self, PyObject *args)
433 /* Parse the arguments */
434 if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname,
435 &buf, &bufsize, &flags, &nofollow))
437 if(!convertObj(myarg, &tgt, nofollow))
440 /* Set the attribute's value */
441 if((nret = _set_obj(&tgt, attrname, buf, bufsize, flags)) == -1) {
442 return PyErr_SetFromErrno(PyExc_IOError);
445 /* Return the result */
449 static char __set_doc__[] =
450 "Set the value of a given extended attribute.\n"
452 "@param item: the item to query; either a string representing the"
453 " filename, or a file-like object, or a file descriptor\n"
454 "@param name: the attribute whose value to set; usually in form of"
455 " system.posix_acl or user.mime_type\n"
456 "@type name: string\n"
457 "@param value: a string, possibly with embedded NULLs; note that there"
458 " are restrictions regarding the size of the value, for"
459 " example, for ext2/ext3, maximum size is the block size\n"
460 "@type value: string\n"
461 "@param flags: if 0 or ommited the attribute will be"
462 " created or replaced; if L{XATTR_CREATE}, the attribute"
463 " will be created, giving an error if it already exists;"
464 " if L{XATTR_REPLACE}, the attribute will be replaced,"
465 " giving an error if it doesn't exists;\n"
466 "@type flags: integer\n"
467 "@param nofollow: if given and True, and the function is passed a"
468 " filename that points to a symlink, the function will act on the symlink"
469 " itself instead of its target\n"
470 "@type nofollow: boolean\n"
471 "@param namespace: if given, the attribute must not contain the namespace"
472 " itself, but instead the namespace will be taken from this parameter\n"
473 "@type namespace: string\n"
475 "@raise EnvironmentError: system errors will raise an exception\n"
479 /* Wrapper for setxattr */
481 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
492 const char *full_name;
493 static char *kwlist[] = {"item", "name", "value", "flags",
494 "nofollow", "namespace", NULL};
496 /* Parse the arguments */
497 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oss#|iiz", kwlist,
499 &buf, &bufsize, &flags, &nofollow, &ns))
501 if(!convertObj(myarg, &tgt, nofollow))
504 full_name = merge_ns(ns, attrname, &newname);
505 /* Set the attribute's value */
506 nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
510 return PyErr_SetFromErrno(PyExc_IOError);
513 /* Return the result */
518 static char __pyremovexattr_doc__[] =
519 "Remove an attribute from a file (deprecated)\n"
522 " - a string representing filename, or a file-like object,\n"
523 " or a file descriptor; this represents the file on \n"
525 " - a string, representing the attribute to be removed;\n"
526 " usually in form of system.posix_acl or user.mime_type\n"
527 " - (optional) a boolean value (defaults to false), which, if\n"
528 " the file name given is a symbolic link, makes the\n"
529 " function operate on the symbolic link itself instead\n"
531 "@deprecated: this function has been deprecated by the L{remove}"
535 /* Wrapper for removexattr */
537 pyremovexattr(PyObject *self, PyObject *args)
545 /* Parse the arguments */
546 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
549 if(!convertObj(myarg, &tgt, nofollow))
552 /* Remove the attribute */
553 if((nret = _remove_obj(&tgt, attrname)) == -1) {
554 return PyErr_SetFromErrno(PyExc_IOError);
557 /* Return the result */
561 static char __remove_doc__[] =
562 "Remove an attribute from a file\n"
564 "@param item: the item to query; either a string representing the"
565 " filename, or a file-like object, or a file descriptor\n"
566 "@param name: the attribute whose value to set; usually in form of"
567 " system.posix_acl or user.mime_type\n"
568 "@type name: string\n"
569 "@param nofollow: if given and True, and the function is passed a"
570 " filename that points to a symlink, the function will act on the symlink"
571 " itself instead of its target\n"
572 "@type nofollow: boolean\n"
573 "@param namespace: if given, the attribute must not contain the namespace"
574 " itself, but instead the namespace will be taken from this parameter\n"
575 "@type namespace: string\n"
578 "@raise EnvironmentError: system errors will raise an exception\n"
581 /* Wrapper for removexattr */
583 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
587 char *attrname, *name_buf;
589 const char *full_name;
592 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
594 /* Parse the arguments */
595 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist,
596 &myarg, &attrname, &nofollow, &ns))
599 if(!convertObj(myarg, &tgt, nofollow))
601 full_name = merge_ns(ns, attrname, &name_buf);
602 if(full_name == NULL)
605 /* Remove the attribute */
606 nret = _remove_obj(&tgt, full_name);
607 PyMem_Free(name_buf);
609 return PyErr_SetFromErrno(PyExc_IOError);
612 /* Return the result */
616 static char __pylistxattr_doc__[] =
617 "Return the list of attribute names for a file (deprecated)\n"
620 " - a string representing filename, or a file-like object,\n"
621 " or a file descriptor; this represents the file to \n"
623 " - (optional) a boolean value (defaults to false), which, if\n"
624 " the file name given is a symbolic link, makes the\n"
625 " function operate on the symbolic link itself instead\n"
627 "@deprecated: this function has been deprecated by the L{list}"
631 /* Wrapper for listxattr */
633 pylistxattr(PyObject *self, PyObject *args)
644 /* Parse the arguments */
645 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
647 if(!convertObj(myarg, &tgt, nofollow))
650 /* Find out the needed size of the buffer */
651 if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
652 return PyErr_SetFromErrno(PyExc_IOError);
655 /* Try to allocate the memory, using Python's allocator */
656 if((buf = PyMem_Malloc(nalloc)) == NULL) {
661 /* Now retrieve the list of attributes */
662 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
664 return PyErr_SetFromErrno(PyExc_IOError);
667 /* Compute the number of attributes in the list */
668 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
672 /* Create the list which will hold the result */
673 mylist = PyList_New(nattrs);
675 /* Create and insert the attributes as strings in the list */
676 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
677 PyList_SET_ITEM(mylist, nattrs, PyString_FromString(s));
681 /* Free the buffer, now it is no longer needed */
684 /* Return the result */
688 static char __list_doc__[] =
689 "Return the list of attribute names for a file\n"
692 " >>> xattr.list('/path/to/file')\n"
693 " ['user.test', 'user.comment', 'system.posix_acl_access']\n"
694 " >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
695 " ['test', 'comment']\n"
697 "@param item: the item to query; either a string representing the"
698 " filename, or a file-like object, or a file descriptor\n"
699 "@param nofollow: if given and True, and the function is passed a"
700 " filename that points to a symlink, the function will act on the symlink"
701 " itself instead of its target\n"
702 "@type nofollow: boolean\n"
703 "@param namespace: if given, the attribute must not contain the namespace"
704 " itself, but instead the namespace will be taken from this parameter\n"
705 "@type namespace: string\n"
706 "@return: list of strings; note that if a namespace argument was\n"
707 "passed, it (and the separator) will be stripped from the names returned\n"
709 "@raise EnvironmentError: system errors will raise an exception\n"
713 /* Wrapper for listxattr */
715 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
726 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
728 /* Parse the arguments */
729 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
730 &myarg, &nofollow, &ns))
732 if(!convertObj(myarg, &tgt, nofollow))
735 /* Find out the needed size of the buffer */
736 if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
737 return PyErr_SetFromErrno(PyExc_IOError);
740 /* Try to allocate the memory, using Python's allocator */
741 if((buf = PyMem_Malloc(nalloc)) == NULL) {
746 /* Now retrieve the list of attributes */
747 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
749 return PyErr_SetFromErrno(PyExc_IOError);
752 /* Compute the number of attributes in the list */
753 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
754 if(matches_ns(ns, s)!=NULL)
757 /* Create the list which will hold the result */
758 mylist = PyList_New(nattrs);
760 /* Create and insert the attributes as strings in the list */
761 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
762 const char *name = matches_ns(ns, s);
764 PyList_SET_ITEM(mylist, nattrs, PyString_FromString(name));
769 /* Free the buffer, now it is no longer needed */
772 /* Return the result */
776 static PyMethodDef xattr_methods[] = {
777 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
778 {"get", (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
780 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
782 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
783 {"set", (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
785 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
786 {"remove", (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
788 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
789 {"list", (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
791 {NULL, NULL, 0, NULL} /* Sentinel */
794 static char __xattr_doc__[] = \
795 "Access extended filesystem attributes\n"
797 "This module gives access to the extended attributes present\n"
798 "in some operating systems/filesystems. You can list attributes,\n"
799 "get, set and remove them.\n"
800 "The last and optional parameter for all functions is a boolean \n"
801 "value which enables the 'l-' version of the functions - acting\n"
802 "on symbolic links and not their destination.\n"
805 " >>> import xattr\n"
806 " >>> xattr.listxattr(\"file.txt\")\n"
807 " ['user.mime_type']\n"
808 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
810 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
811 "\"Simple text file\")\n"
812 " >>> xattr.listxattr(\"file.txt\")\n"
813 " ['user.mime_type', 'user.comment']\n"
814 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
821 PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
823 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
824 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
826 /* namespace constants */
827 PyModule_AddStringConstant(m, "NS_SECURITY", "security");
828 PyModule_AddStringConstant(m, "NS_SYSTEM", "system");
829 PyModule_AddStringConstant(m, "NS_TRUSTED", "trusted");
830 PyModule_AddStringConstant(m, "NS_USER", "user");