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 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, char *name, void *value, size_t size) {
70 return fgetxattr(tgt->fd, name, value, size);
71 else if (tgt->type == T_LINK)
72 return lgetxattr(tgt->name, name, value, size);
74 return getxattr(tgt->name, name, value, size);
77 static ssize_t _set_obj(target_t *tgt, char *name, void *value, size_t size,
80 return fsetxattr(tgt->fd, name, value, size, flags);
81 else if (tgt->type == T_LINK)
82 return lsetxattr(tgt->name, name, value, size, flags);
84 return setxattr(tgt->name, name, value, size, flags);
87 static ssize_t _remove_obj(target_t *tgt, char *name) {
89 return fremovexattr(tgt->fd, name);
90 else if (tgt->type == T_LINK)
91 return lremovexattr(tgt->name, name);
93 return removexattr(tgt->name, name);
96 /* Checks if an attribute name matches an optional namespace */
97 static int matches_ns(const char *name, const char *ns) {
101 ns_size = strlen(ns);
103 if (strlen(name) > ns_size && !strncmp(name, ns, ns_size) &&
104 name[ns_size] == '.')
109 /* Wrapper for getxattr */
110 static char __pygetxattr_doc__[] =
111 "Get the value of a given extended attribute.\n"
114 " - a string representing filename, or a file-like object,\n"
115 " or a file descriptor; this represents the file on \n"
117 " - a string, representing the attribute whose value to retrieve;\n"
118 " usually in form of system.posix_acl or user.mime_type\n"
119 " - (optional) a boolean value (defaults to false), which, if\n"
120 " the file name given is a symbolic link, makes the\n"
121 " function operate on the symbolic link itself instead\n"
123 "@deprecated: this function has been replace with the L{get_all} function"
124 " which replaces the positional parameters with keyword ones\n"
128 pygetxattr(PyObject *self, PyObject *args)
138 /* Parse the arguments */
139 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
141 if(!convertObj(myarg, &tgt, nofollow))
144 /* Find out the needed size of the buffer */
145 if((nalloc = _get_obj(&tgt, attrname, NULL, 0)) == -1) {
146 return PyErr_SetFromErrno(PyExc_IOError);
149 /* Try to allocate the memory, using Python's allocator */
150 if((buf = PyMem_Malloc(nalloc)) == NULL) {
155 /* Now retrieve the attribute value */
156 if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) {
158 return PyErr_SetFromErrno(PyExc_IOError);
161 /* Create the string which will hold the result */
162 res = PyString_FromStringAndSize(buf, nret);
164 /* Free the buffer, now it is no longer needed */
167 /* Return the result */
171 /* Wrapper for getxattr */
172 static char __get_all_doc__[] =
173 "Get all the extended attributes of an item.\n"
175 "This function performs a bulk-get of all extended attribute names\n"
176 "and the corresponding value.\n"
178 " >>> xattr.get_all('/path/to/file')\n"
179 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),"
180 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
181 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
182 " [('user.mime-type', 'plain/text'), ('user.comment', 'test')]\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 "@keyword namespace: an optional namespace for filtering the"
187 " attributes; for example, querying all user attributes can be"
188 " accomplished by passing namespace=L{NS_USER}\n"
189 "@type namespace: string\n"
190 "@keyword nofollow: if passed and true, if the target file is a symbolic"
192 " the attributes for the link itself will be returned, instead of the"
193 " attributes of the target\n"
194 "@type nofollow: boolean\n"
195 "@return: list of tuples (name, value)\n"
199 get_all(PyObject *self, PyObject *args, PyObject *keywds)
204 char *buf_list, *buf_val;
206 size_t nalloc, nlist, nval;
209 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
211 /* Parse the arguments */
212 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
213 &myarg, &dolink, &ns))
215 if(!convertObj(myarg, &tgt, dolink))
218 /* Compute first the list of attributes */
220 /* Find out the needed size of the buffer for the attribute list */
221 nalloc = _list_obj(&tgt, NULL, 0);
224 return PyErr_SetFromErrno(PyExc_IOError);
227 /* Try to allocate the memory, using Python's allocator */
228 if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
233 /* Now retrieve the list of attributes */
234 nlist = _list_obj(&tgt, buf_list, nalloc);
237 PyErr_SetFromErrno(PyExc_IOError);
241 /* Create the list which will hold the result */
242 mylist = PyList_New(0);
243 nalloc = ESTIMATE_ATTR_SIZE;
244 if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
249 /* Create and insert the attributes as strings in the list */
250 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
254 if(!matches_ns(s, ns))
256 /* Now retrieve the attribute value */
259 nval = _get_obj(&tgt, s, buf_val, nalloc);
262 if(errno == ERANGE) {
263 nval = _get_obj(&tgt, s, NULL, 0);
264 if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL)
268 } else if(errno == ENODATA || errno == ENOATTR) {
269 /* this attribute has gone away since we queried
270 the attribute list */
280 my_tuple = Py_BuildValue("ss#", s, buf_val, nval);
282 PyList_Append(mylist, my_tuple);
286 /* Free the buffers, now they are no longer needed */
288 PyMem_Free(buf_list);
290 /* Return the result */
293 PyErr_SetFromErrno(PyExc_IOError);
298 PyMem_Free(buf_list);
303 static char __pysetxattr_doc__[] =
304 "Set the value of a given extended attribute.\n"
305 "Be carefull in case you want to set attributes on symbolic\n"
306 "links, you have to use all the 5 parameters; use 0 for the \n"
307 "flags value if you want the default behavior (create or "
311 " - a string representing filename, or a file-like object,\n"
312 " or a file descriptor; this represents the file on \n"
314 " - a string, representing the attribute whose value to set;\n"
315 " usually in form of system.posix_acl or user.mime_type\n"
316 " - a string, possibly with embedded NULLs; note that there\n"
317 " are restrictions regarding the size of the value, for\n"
318 " example, for ext2/ext3, maximum size is the block size\n"
319 " - (optional) flags; if 0 or ommited the attribute will be \n"
320 " created or replaced; if XATTR_CREATE, the attribute \n"
321 " will be created, giving an error if it already exists;\n"
322 " of XATTR_REPLACE, the attribute will be replaced,\n"
323 " giving an error if it doesn't exists;\n"
324 " - (optional) a boolean value (defaults to false), which, if\n"
325 " the file name given is a symbolic link, makes the\n"
326 " function operate on the symbolic link itself instead\n"
328 "@deprecated: this function has been deprecated by the new L{set}"
332 /* Wrapper for setxattr */
334 pysetxattr(PyObject *self, PyObject *args)
344 /* Parse the arguments */
345 if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname,
346 &buf, &bufsize, &flags, &nofollow))
348 if(!convertObj(myarg, &tgt, nofollow))
351 /* Set the attribute's value */
352 if((nret = _set_obj(&tgt, attrname, buf, bufsize, flags)) == -1) {
353 return PyErr_SetFromErrno(PyExc_IOError);
356 /* Return the result */
360 static char __set_doc__[] =
361 "Set the value of a given extended attribute.\n"
363 "@param item: the item to query; either a string representing the"
364 " filename, or a file-like object, or a file descriptor\n"
365 "@param name: the attribute whose value to set; usually in form of"
366 " system.posix_acl or user.mime_type\n"
367 "@type name: string\n"
368 "@param value: a string, possibly with embedded NULLs; note that there"
369 " are restrictions regarding the size of the value, for"
370 " example, for ext2/ext3, maximum size is the block size\n"
371 "@type value: string\n"
372 "@param flags: if 0 or ommited the attribute will be"
373 " created or replaced; if L{XATTR_CREATE}, the attribute"
374 " will be created, giving an error if it already exists;"
375 " if L{XATTR_REPLACE}, the attribute will be replaced,"
376 " giving an error if it doesn't exists;\n"
377 "@type flags: integer\n"
378 "@param nofollow: if given and True, and the function is passed a"
379 " filename that points to a symlink, the function will act on the symlink"
380 " itself instead of its target\n"
381 "@type nofollow: boolean\n"
382 "@param namespace: if given, the attribute must not contain the namespace"
383 " itself, but instead the namespace will be taken from this parameter\n"
384 "@type namespace: string\n"
387 /* Wrapper for setxattr */
389 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
401 static char *kwlist[] = {"item", "name", "value", "flags",
402 "nofollow", "namespace", NULL};
404 /* Parse the arguments */
405 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oss#|iiz", kwlist,
407 &buf, &bufsize, &flags, &nofollow, &ns))
409 if(!convertObj(myarg, &tgt, nofollow))
412 full_name = merge_ns(ns, attrname, &newname);
413 /* Set the attribute's value */
414 nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
418 return PyErr_SetFromErrno(PyExc_IOError);
421 /* Return the result */
426 static char __pyremovexattr_doc__[] =
427 "Remove an attribute from a file\n"
430 " - a string representing filename, or a file-like object,\n"
431 " or a file descriptor; this represents the file on \n"
433 " - a string, representing the attribute to be removed;\n"
434 " usually in form of system.posix_acl or user.mime_type\n"
435 " - (optional) a boolean value (defaults to false), which, if\n"
436 " the file name given is a symbolic link, makes the\n"
437 " function operate on the symbolic link itself instead\n"
441 /* Wrapper for removexattr */
443 pyremovexattr(PyObject *self, PyObject *args)
451 /* Parse the arguments */
452 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
455 if(!convertObj(myarg, &tgt, nofollow))
458 /* Remove the attribute */
459 if((nret = _remove_obj(&tgt, attrname)) == -1) {
460 return PyErr_SetFromErrno(PyExc_IOError);
463 /* Return the result */
467 static char __pylistxattr_doc__[] =
468 "Return the list of attribute names for a file\n"
471 " - a string representing filename, or a file-like object,\n"
472 " or a file descriptor; this represents the file to \n"
474 " - (optional) a boolean value (defaults to false), which, if\n"
475 " the file name given is a symbolic link, makes the\n"
476 " function operate on the symbolic link itself instead\n"
480 /* Wrapper for listxattr */
482 pylistxattr(PyObject *self, PyObject *args)
493 /* Parse the arguments */
494 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
496 if(!convertObj(myarg, &tgt, nofollow))
499 /* Find out the needed size of the buffer */
500 if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
501 return PyErr_SetFromErrno(PyExc_IOError);
504 /* Try to allocate the memory, using Python's allocator */
505 if((buf = PyMem_Malloc(nalloc)) == NULL) {
510 /* Now retrieve the list of attributes */
511 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
513 return PyErr_SetFromErrno(PyExc_IOError);
516 /* Compute the number of attributes in the list */
517 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
521 /* Create the list which will hold the result */
522 mylist = PyList_New(nattrs);
524 /* Create and insert the attributes as strings in the list */
525 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
526 PyList_SET_ITEM(mylist, nattrs, PyString_FromString(s));
530 /* Free the buffer, now it is no longer needed */
533 /* Return the result */
537 static PyMethodDef xattr_methods[] = {
538 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
539 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
541 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
542 {"set", (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
544 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
545 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
546 {NULL, NULL, 0, NULL} /* Sentinel */
549 static char __xattr_doc__[] = \
550 "Access extended filesystem attributes\n"
552 "This module gives access to the extended attributes present\n"
553 "in some operating systems/filesystems. You can list attributes,\n"
554 "get, set and remove them.\n"
555 "The last and optional parameter for all functions is a boolean \n"
556 "value which enables the 'l-' version of the functions - acting\n"
557 "on symbolic links and not their destination.\n"
560 " >>> import xattr\n"
561 " >>> xattr.listxattr(\"file.txt\")\n"
562 " ['user.mime_type']\n"
563 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
565 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
566 "\"Simple text file\")\n"
567 " >>> xattr.listxattr(\"file.txt\")\n"
568 " ['user.mime_type', 'user.comment']\n"
569 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
576 PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
578 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
579 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
581 /* namespace constants */
582 PyModule_AddStringConstant(m, "NS_SECURITY", "security");
583 PyModule_AddStringConstant(m, "NS_SYSTEM", "system");
584 PyModule_AddStringConstant(m, "NS_TRUSTED", "trusted");
585 PyModule_AddStringConstant(m, "NS_USER", "user");