2 #include <attr/xattr.h>
4 /** Converts from a string, file or int argument to what we need. */
5 static int convertObj(PyObject *myobj, int *ishandle, int *filehandle,
7 if(PyString_Check(myobj)) {
9 *filename = PyString_AS_STRING(myobj);
10 } else if((*filehandle = PyObject_AsFileDescriptor(myobj)) != -1) {
13 PyErr_SetString(PyExc_TypeError, "argument 1 must be string or int");
19 /* Checks if an attribute name matches an optional namespace */
20 static int matches_ns(const char *name, const char *ns) {
26 if (strlen(name) > ns_size && !strncmp(name, ns, ns_size) &&
32 /* Wrapper for getxattr */
33 static char __pygetxattr_doc__[] =
34 "Get the value of a given extended attribute.\n"
37 " - a string representing filename, or a file-like object,\n"
38 " or a file descriptor; this represents the file on \n"
40 " - a string, representing the attribute whose value to retrieve;\n"
41 " usually in form of system.posix_acl or user.mime_type\n"
42 " - (optional) a boolean value (defaults to false), which, if\n"
43 " the file name given is a symbolic link, makes the\n"
44 " function operate on the symbolic link itself instead\n"
46 "@deprecated: this function has been replace with the L{get_all} function"
47 " which replaces the positional parameters with keyword ones\n"
51 pygetxattr(PyObject *self, PyObject *args)
55 int filedes = -1, ishandle, dolink=0;
61 /* Parse the arguments */
62 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &dolink))
64 if(!convertObj(myarg, &ishandle, &filedes, &file))
67 /* Find out the needed size of the buffer */
69 fgetxattr(filedes, attrname, NULL, 0) :
71 lgetxattr(file, attrname, NULL, 0) :
72 getxattr(file, attrname, NULL, 0);
74 return PyErr_SetFromErrno(PyExc_IOError);
77 /* Try to allocate the memory, using Python's allocator */
78 if((buf = PyMem_Malloc(nalloc)) == NULL) {
83 /* Now retrieve the attribute value */
85 fgetxattr(filedes, attrname, buf, nalloc) :
87 lgetxattr(file, attrname, buf, nalloc) :
88 getxattr(file, attrname, buf, nalloc);
91 return PyErr_SetFromErrno(PyExc_IOError);
94 /* Create the string which will hold the result */
95 res = PyString_FromStringAndSize(buf, nret);
97 /* Free the buffer, now it is no longer needed */
100 /* Return the result */
104 /* Wrapper for getxattr */
105 static char __get_all_doc__[] =
106 "Get all the extended attributes of an item.\n"
108 "This function performs a bulk-get of all extended attribute names\n"
109 "and the corresponding value.\n"
111 " >>> xattr.get_all('/path/to/file')\n"
112 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),"
113 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
114 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
115 " [('user.mime-type', 'plain/text'), ('user.comment', 'test')]\n"
117 "@param item: the item to query; either a string representing the"
118 " filename, or a file-like object, or a file descriptor\n"
119 "@keyword namespace: an optional namespace for filtering the"
120 " attributes; for example, querying all user attributes can be"
121 " accomplished by passing namespace=L{NS_USER}\n"
122 "@type namespace: string\n"
123 "@keyword noderef: if passed and true, if the target file is a symbolic"
125 " the attributes for the link itself will be returned, instead of the"
126 " attributes of the target\n"
127 "@type noderef: boolean\n"
128 "@return: list of tuples (name, value)\n"
132 get_all(PyObject *self, PyObject *args, PyObject *keywds)
136 int filedes = -1, ishandle, dolink=0;
138 char *buf_list, *buf_val;
140 int nalloc, nlist, nval, nattrs;
142 static char *kwlist[] = {"item", "noderef", "namespace", NULL};
144 /* Parse the arguments */
145 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
146 &myarg, &dolink, &ns))
148 if(!convertObj(myarg, &ishandle, &filedes, &file))
151 /* Compute first the list of attributes */
153 /* Find out the needed size of the buffer for the attribute list */
155 flistxattr(filedes, NULL, 0) :
157 llistxattr(file, NULL, 0) :
158 listxattr(file, NULL, 0);
161 return PyErr_SetFromErrno(PyExc_IOError);
164 /* Try to allocate the memory, using Python's allocator */
165 if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
170 /* Now retrieve the list of attributes */
172 flistxattr(filedes, buf_list, nalloc) :
174 llistxattr(file, buf_list, nalloc) :
175 listxattr(file, buf_list, nalloc);
178 return PyErr_SetFromErrno(PyExc_IOError);
181 /* Compute the number of attributes in the list */
182 for(s = buf_list, nattrs = 0; (s - buf_list) < nlist; s += strlen(s) + 1) {
183 if(matches_ns(s, ns))
187 /* Create the list which will hold the result */
188 mylist = PyList_New(nattrs);
190 /* Create and insert the attributes as strings in the list */
191 for(s = buf_list, nattrs = 0; s - buf_list < nlist; s += strlen(s) + 1) {
194 if(!matches_ns(s, ns))
197 /* Find out the needed size of the value buffer */
199 fgetxattr(filedes, s, NULL, 0) :
201 lgetxattr(file, s, NULL, 0) :
202 getxattr(file, s, NULL, 0);
204 PyMem_Free(buf_list);
205 return PyErr_SetFromErrno(PyExc_IOError);
208 /* Try to allocate the memory, using Python's allocator */
209 if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
210 PyMem_Free(buf_list);
215 /* Now retrieve the attribute value */
217 fgetxattr(filedes, s, buf_val, nalloc) :
219 lgetxattr(file, s, buf_val, nalloc) :
220 getxattr(file, s, buf_val, nalloc);
223 PyMem_Free(buf_list);
225 return PyErr_SetFromErrno(PyExc_IOError);
227 my_tuple = Py_BuildValue("ss#", s, buf_val, nval);
229 /* Free the buffer, now it is no longer needed */
232 PyList_SET_ITEM(mylist, nattrs, my_tuple);
236 /* Free the buffer, now it is no longer needed */
237 PyMem_Free(buf_list);
239 /* Return the result */
245 static char __pysetxattr_doc__[] =
246 "Set the value of a given extended attribute.\n"
247 "Be carefull in case you want to set attributes on symbolic\n"
248 "links, you have to use all the 5 parameters; use 0 for the \n"
249 "flags value if you want the default behavior (create or "
253 " - a string representing filename, or a file-like object,\n"
254 " or a file descriptor; this represents the file on \n"
256 " - a string, representing the attribute whose value to set;\n"
257 " usually in form of system.posix_acl or user.mime_type\n"
258 " - a string, possibly with embedded NULLs; note that there\n"
259 " are restrictions regarding the size of the value, for\n"
260 " example, for ext2/ext3, maximum size is the block size\n"
261 " - (optional) flags; if 0 or ommited the attribute will be \n"
262 " created or replaced; if XATTR_CREATE, the attribute \n"
263 " will be created, giving an error if it already exists;\n"
264 " of XATTR_REPLACE, the attribute will be replaced,\n"
265 " giving an error if it doesn't exists;\n"
266 " - (optional) a boolean value (defaults to false), which, if\n"
267 " the file name given is a symbolic link, makes the\n"
268 " function operate on the symbolic link itself instead\n"
272 /* Wrapper for setxattr */
274 pysetxattr(PyObject *self, PyObject *args)
278 int ishandle, filedes, dolink=0;
284 /* Parse the arguments */
285 if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname,
286 &buf, &bufsize, &flags, &dolink))
288 if(!convertObj(myarg, &ishandle, &filedes, &file))
291 /* Set the attribute's value */
293 fsetxattr(filedes, attrname, buf, bufsize, flags) :
295 lsetxattr(file, attrname, buf, bufsize, flags) :
296 setxattr(file, attrname, buf, bufsize, flags);
299 return PyErr_SetFromErrno(PyExc_IOError);
302 /* Return the result */
307 static char __pyremovexattr_doc__[] =
308 "Remove an attribute from a file\n"
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 to be removed;\n"
315 " usually in form of system.posix_acl or user.mime_type\n"
316 " - (optional) a boolean value (defaults to false), which, if\n"
317 " the file name given is a symbolic link, makes the\n"
318 " function operate on the symbolic link itself instead\n"
322 /* Wrapper for removexattr */
324 pyremovexattr(PyObject *self, PyObject *args)
328 int ishandle, filedes, dolink=0;
332 /* Parse the arguments */
333 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &dolink))
336 if(!convertObj(myarg, &ishandle, &filedes, &file))
339 /* Remove the attribute */
341 fremovexattr(filedes, attrname) :
343 lremovexattr(file, attrname) :
344 removexattr(file, attrname);
347 return PyErr_SetFromErrno(PyExc_IOError);
349 /* Return the result */
354 static char __pylistxattr_doc__[] =
355 "Return the list of attribute names for a file\n"
358 " - a string representing filename, or a file-like object,\n"
359 " or a file descriptor; this represents the file to \n"
361 " - (optional) a boolean value (defaults to false), which, if\n"
362 " the file name given is a symbolic link, makes the\n"
363 " function operate on the symbolic link itself instead\n"
367 /* Wrapper for listxattr */
369 pylistxattr(PyObject *self, PyObject *args)
374 int ishandle, dolink=0;
381 /* Parse the arguments */
382 if (!PyArg_ParseTuple(args, "O|i", &myarg, &dolink))
384 if(!convertObj(myarg, &ishandle, &filedes, &file))
387 /* Find out the needed size of the buffer */
389 flistxattr(filedes, NULL, 0) :
391 llistxattr(file, NULL, 0) :
392 listxattr(file, NULL, 0);
395 return PyErr_SetFromErrno(PyExc_IOError);
398 /* Try to allocate the memory, using Python's allocator */
399 if((buf = PyMem_Malloc(nalloc)) == NULL) {
404 /* Now retrieve the list of attributes */
406 flistxattr(filedes, buf, nalloc) :
408 llistxattr(file, buf, nalloc) :
409 listxattr(file, buf, nalloc);
412 return PyErr_SetFromErrno(PyExc_IOError);
415 /* Compute the number of attributes in the list */
416 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
420 /* Create the list which will hold the result */
421 mylist = PyList_New(nattrs);
423 /* Create and insert the attributes as strings in the list */
424 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
425 PyList_SET_ITEM(mylist, nattrs, PyString_FromString(s));
429 /* Free the buffer, now it is no longer needed */
432 /* Return the result */
436 static PyMethodDef xattr_methods[] = {
437 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
438 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
440 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
441 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
442 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
443 {NULL, NULL, 0, NULL} /* Sentinel */
446 static char __xattr_doc__[] = \
447 "Access extended filesystem attributes\n"
449 "This module gives access to the extended attributes present\n"
450 "in some operating systems/filesystems. You can list attributes,\n"
451 "get, set and remove them.\n"
452 "The last and optional parameter for all functions is a boolean \n"
453 "value which enables the 'l-' version of the functions - acting\n"
454 "on symbolic links and not their destination.\n"
457 " >>> import xattr\n"
458 " >>> xattr.listxattr(\"file.txt\")\n"
459 " ['user.mime_type']\n"
460 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
462 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
463 "\"Simple text file\")\n"
464 " >>> xattr.listxattr(\"file.txt\")\n"
465 " ['user.mime_type', 'user.comment']\n"
466 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
473 PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
475 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
476 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
478 /* namespace constants */
479 PyModule_AddStringConstant(m, "NS_SECURITY", "security");
480 PyModule_AddStringConstant(m, "NS_SYSTEM", "system");
481 PyModule_AddStringConstant(m, "NS_TRUSTED", "trusted");
482 PyModule_AddStringConstant(m, "NS_USER", "user");