2 #include <attr/xattr.h>
4 typedef enum {T_FD, T_PATH, T_LINK} target_e;
14 /** Converts from a string, file or int argument to what we need. */
15 static int convertObj(PyObject *myobj, target_t *tgt, int nofollow) {
17 if(PyString_Check(myobj)) {
18 tgt->type = nofollow ? T_LINK : T_PATH;
19 tgt->name = PyString_AS_STRING(myobj);
20 } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
24 PyErr_SetString(PyExc_TypeError, "argument must be string or int");
30 static ssize_t _list_obj(target_t *tgt, char *list, size_t size) {
32 return flistxattr(tgt->fd, list, size);
33 else if (tgt->type == T_LINK)
34 return llistxattr(tgt->name, list, size);
36 return listxattr(tgt->name, list, size);
39 static ssize_t _get_obj(target_t *tgt, char *name, void *value, size_t size) {
41 return fgetxattr(tgt->fd, name, value, size);
42 else if (tgt->type == T_LINK)
43 return lgetxattr(tgt->name, name, value, size);
45 return getxattr(tgt->name, name, value, size);
48 static ssize_t _set_obj(target_t *tgt, char *name, void *value, size_t size,
51 return fsetxattr(tgt->fd, name, value, size, flags);
52 else if (tgt->type == T_LINK)
53 return lsetxattr(tgt->name, name, value, size, flags);
55 return setxattr(tgt->name, name, value, size, flags);
58 static ssize_t _remove_obj(target_t *tgt, char *name) {
60 return fremovexattr(tgt->fd, name);
61 else if (tgt->type == T_LINK)
62 return lremovexattr(tgt->name, name);
64 return removexattr(tgt->name, name);
67 /* Checks if an attribute name matches an optional namespace */
68 static int matches_ns(const char *name, const char *ns) {
74 if (strlen(name) > ns_size && !strncmp(name, ns, ns_size) &&
80 /* Wrapper for getxattr */
81 static char __pygetxattr_doc__[] =
82 "Get the value of a given extended attribute.\n"
85 " - a string representing filename, or a file-like object,\n"
86 " or a file descriptor; this represents the file on \n"
88 " - a string, representing the attribute whose value to retrieve;\n"
89 " usually in form of system.posix_acl or user.mime_type\n"
90 " - (optional) a boolean value (defaults to false), which, if\n"
91 " the file name given is a symbolic link, makes the\n"
92 " function operate on the symbolic link itself instead\n"
94 "@deprecated: this function has been replace with the L{get_all} function"
95 " which replaces the positional parameters with keyword ones\n"
99 pygetxattr(PyObject *self, PyObject *args)
109 /* Parse the arguments */
110 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
112 if(!convertObj(myarg, &tgt, nofollow))
115 /* Find out the needed size of the buffer */
116 if((nalloc = _get_obj(&tgt, attrname, NULL, 0)) == -1) {
117 return PyErr_SetFromErrno(PyExc_IOError);
120 /* Try to allocate the memory, using Python's allocator */
121 if((buf = PyMem_Malloc(nalloc)) == NULL) {
126 /* Now retrieve the attribute value */
127 if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) {
129 return PyErr_SetFromErrno(PyExc_IOError);
132 /* Create the string which will hold the result */
133 res = PyString_FromStringAndSize(buf, nret);
135 /* Free the buffer, now it is no longer needed */
138 /* Return the result */
142 /* Wrapper for getxattr */
143 static char __get_all_doc__[] =
144 "Get all the extended attributes of an item.\n"
146 "This function performs a bulk-get of all extended attribute names\n"
147 "and the corresponding value.\n"
149 " >>> xattr.get_all('/path/to/file')\n"
150 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),"
151 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
152 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
153 " [('user.mime-type', 'plain/text'), ('user.comment', 'test')]\n"
155 "@param item: the item to query; either a string representing the"
156 " filename, or a file-like object, or a file descriptor\n"
157 "@keyword namespace: an optional namespace for filtering the"
158 " attributes; for example, querying all user attributes can be"
159 " accomplished by passing namespace=L{NS_USER}\n"
160 "@type namespace: string\n"
161 "@keyword nofollow: if passed and true, if the target file is a symbolic"
163 " the attributes for the link itself will be returned, instead of the"
164 " attributes of the target\n"
165 "@type nofollow: boolean\n"
166 "@return: list of tuples (name, value)\n"
170 get_all(PyObject *self, PyObject *args, PyObject *keywds)
175 char *buf_list, *buf_val;
177 size_t nalloc, nlist, nval;
180 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
182 /* Parse the arguments */
183 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
184 &myarg, &dolink, &ns))
186 if(!convertObj(myarg, &tgt, dolink))
189 /* Compute first the list of attributes */
191 /* Find out the needed size of the buffer for the attribute list */
192 nalloc = _list_obj(&tgt, NULL, 0);
195 return PyErr_SetFromErrno(PyExc_IOError);
198 /* Try to allocate the memory, using Python's allocator */
199 if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
204 /* Now retrieve the list of attributes */
205 nlist = _list_obj(&tgt, buf_list, nalloc);
208 PyErr_SetFromErrno(PyExc_IOError);
212 /* Create the list which will hold the result */
213 mylist = PyList_New(0);
215 if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
220 /* Create and insert the attributes as strings in the list */
221 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
225 if(!matches_ns(s, ns))
227 /* Now retrieve the attribute value */
230 nval = _get_obj(&tgt, s, buf_val, nalloc);
233 if(errno == ERANGE) {
234 nval = _get_obj(&tgt, s, NULL, 0);
235 if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL)
239 } else if(errno == ENODATA || errno == ENOATTR) {
240 /* this attribute has gone away since we queried
241 the attribute list */
251 my_tuple = Py_BuildValue("ss#", s, buf_val, nval);
253 PyList_Append(mylist, my_tuple);
257 /* Free the buffers, now they are no longer needed */
259 PyMem_Free(buf_list);
261 /* Return the result */
264 PyErr_SetFromErrno(PyExc_IOError);
270 PyMem_Free(buf_list);
275 static char __pysetxattr_doc__[] =
276 "Set the value of a given extended attribute.\n"
277 "Be carefull in case you want to set attributes on symbolic\n"
278 "links, you have to use all the 5 parameters; use 0 for the \n"
279 "flags value if you want the default behavior (create or "
283 " - a string representing filename, or a file-like object,\n"
284 " or a file descriptor; this represents the file on \n"
286 " - a string, representing the attribute whose value to set;\n"
287 " usually in form of system.posix_acl or user.mime_type\n"
288 " - a string, possibly with embedded NULLs; note that there\n"
289 " are restrictions regarding the size of the value, for\n"
290 " example, for ext2/ext3, maximum size is the block size\n"
291 " - (optional) flags; if 0 or ommited the attribute will be \n"
292 " created or replaced; if XATTR_CREATE, the attribute \n"
293 " will be created, giving an error if it already exists;\n"
294 " of XATTR_REPLACE, the attribute will be replaced,\n"
295 " giving an error if it doesn't exists;\n"
296 " - (optional) a boolean value (defaults to false), which, if\n"
297 " the file name given is a symbolic link, makes the\n"
298 " function operate on the symbolic link itself instead\n"
302 /* Wrapper for setxattr */
304 pysetxattr(PyObject *self, PyObject *args)
314 /* Parse the arguments */
315 if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname,
316 &buf, &bufsize, &flags, &nofollow))
318 if(!convertObj(myarg, &tgt, nofollow))
321 /* Set the attribute's value */
322 if((nret = _set_obj(&tgt, attrname, buf, bufsize, flags)) == -1) {
323 return PyErr_SetFromErrno(PyExc_IOError);
326 /* Return the result */
331 static char __pyremovexattr_doc__[] =
332 "Remove an attribute from a file\n"
335 " - a string representing filename, or a file-like object,\n"
336 " or a file descriptor; this represents the file on \n"
338 " - a string, representing the attribute to be removed;\n"
339 " usually in form of system.posix_acl or user.mime_type\n"
340 " - (optional) a boolean value (defaults to false), which, if\n"
341 " the file name given is a symbolic link, makes the\n"
342 " function operate on the symbolic link itself instead\n"
346 /* Wrapper for removexattr */
348 pyremovexattr(PyObject *self, PyObject *args)
356 /* Parse the arguments */
357 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
360 if(!convertObj(myarg, &tgt, nofollow))
363 /* Remove the attribute */
364 if((nret = _remove_obj(&tgt, attrname)) == -1) {
365 return PyErr_SetFromErrno(PyExc_IOError);
368 /* Return the result */
373 static char __pylistxattr_doc__[] =
374 "Return the list of attribute names for a file\n"
377 " - a string representing filename, or a file-like object,\n"
378 " or a file descriptor; this represents the file to \n"
380 " - (optional) a boolean value (defaults to false), which, if\n"
381 " the file name given is a symbolic link, makes the\n"
382 " function operate on the symbolic link itself instead\n"
386 /* Wrapper for listxattr */
388 pylistxattr(PyObject *self, PyObject *args)
399 /* Parse the arguments */
400 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
402 if(!convertObj(myarg, &tgt, nofollow))
405 /* Find out the needed size of the buffer */
406 if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
407 return PyErr_SetFromErrno(PyExc_IOError);
410 /* Try to allocate the memory, using Python's allocator */
411 if((buf = PyMem_Malloc(nalloc)) == NULL) {
416 /* Now retrieve the list of attributes */
417 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
419 return PyErr_SetFromErrno(PyExc_IOError);
422 /* Compute the number of attributes in the list */
423 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
427 /* Create the list which will hold the result */
428 mylist = PyList_New(nattrs);
430 /* Create and insert the attributes as strings in the list */
431 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
432 PyList_SET_ITEM(mylist, nattrs, PyString_FromString(s));
436 /* Free the buffer, now it is no longer needed */
439 /* Return the result */
443 static PyMethodDef xattr_methods[] = {
444 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
445 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
447 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
448 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
449 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
450 {NULL, NULL, 0, NULL} /* Sentinel */
453 static char __xattr_doc__[] = \
454 "Access extended filesystem attributes\n"
456 "This module gives access to the extended attributes present\n"
457 "in some operating systems/filesystems. You can list attributes,\n"
458 "get, set and remove them.\n"
459 "The last and optional parameter for all functions is a boolean \n"
460 "value which enables the 'l-' version of the functions - acting\n"
461 "on symbolic links and not their destination.\n"
464 " >>> import xattr\n"
465 " >>> xattr.listxattr(\"file.txt\")\n"
466 " ['user.mime_type']\n"
467 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
469 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
470 "\"Simple text file\")\n"
471 " >>> xattr.listxattr(\"file.txt\")\n"
472 " ['user.mime_type', 'user.comment']\n"
473 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
480 PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
482 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
483 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
485 /* namespace constants */
486 PyModule_AddStringConstant(m, "NS_SECURITY", "security");
487 PyModule_AddStringConstant(m, "NS_SYSTEM", "system");
488 PyModule_AddStringConstant(m, "NS_TRUSTED", "trusted");
489 PyModule_AddStringConstant(m, "NS_USER", "user");