]> git.k1024.org Git - pyxattr.git/blob - xattr.c
* Fixed bug in dealing with symlinks (didn't work at all)
[pyxattr.git] / xattr.c
1 #include <Python.h>
2 #include <attr/xattr.h>
3
4 /** Converts from a string, file or int argument to what we need. */
5 static int convertObj(PyObject *myobj, int *ishandle, int *filehandle, char **filename) {
6     if(PyString_Check(myobj)) {
7         *ishandle = 0;
8         *filename = PyString_AS_STRING(myobj);
9     } else if((*filehandle = PyObject_AsFileDescriptor(myobj)) != -1) {
10         *ishandle = 1;
11     } else {
12         PyErr_SetString(PyExc_TypeError, "argument 1 must be string or int");
13         return 0;
14     }
15     return 1;
16 }
17
18 /* Wrapper for getxattr */
19 static char __pygetxattr_doc__[] = \
20 "Get the value of a given extended attribute.\n" \
21 "\n" \
22 "Parameters:\n" \
23 "\t- a string representing filename, or a file-like object,\n" \
24 "\t      or a file descriptor; this represents the file on \n" \
25 "\t      which to act\n" \
26 "\t- a string, representing the attribute whose value to retrieve;\n" \
27 "\t      usually in form of system.posix_acl or user.mime_type\n" \
28 "\t- (optional) a boolean value (defaults to false), which, if\n" \
29 "\t      the file name given is a symbolic link, makes the\n" \
30 "\t      function operate on the symbolic link itself instead\n" \
31 "\t      of its target;" \
32 ;
33
34 static PyObject *
35 pygetxattr(PyObject *self, PyObject *args)
36 {
37     PyObject *myarg;
38     char *file = NULL;
39     int filedes = -1, ishandle, dolink=0;
40     char *attrname;
41     char *buf;
42     int nalloc, nret;
43     PyObject *res;
44     
45     /* Parse the arguments */
46     if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &dolink))
47         return NULL;
48     if(!convertObj(myarg, &ishandle, &filedes, &file))
49         return NULL;
50
51     /* Find out the needed size of the buffer */
52     nalloc = ishandle ? 
53         fgetxattr(filedes, attrname, NULL, 0) :
54         dolink ?
55         lgetxattr(file, attrname, NULL, 0) :
56         getxattr(file, attrname, NULL, 0);
57     if(nalloc == -1) {
58         return PyErr_SetFromErrno(PyExc_IOError);
59     }
60
61     /* Try to allocate the memory, using Python's allocator */
62     if((buf = PyMem_Malloc(nalloc)) == NULL) {
63         PyErr_NoMemory();
64         return NULL;
65     }
66
67     /* Now retrieve the attribute value */
68     nret = ishandle ? 
69         fgetxattr(filedes, attrname, buf, nalloc) :
70         dolink ?
71         lgetxattr(file, attrname, buf, nalloc) :
72         getxattr(file, attrname, buf, nalloc);
73     if(nret == -1) {
74         PyMem_Free(buf);
75         return PyErr_SetFromErrno(PyExc_IOError);
76     }
77
78     /* Create the string which will hold the result */
79     res = PyString_FromStringAndSize(buf, nret);
80
81     /* Free the buffer, now it is no longer needed */
82     PyMem_Free(buf);
83
84     /* Return the result */
85     return res;
86 }
87
88 static char __pysetxattr_doc__[] = \
89 "Set the value of a given extended attribute.\n" \
90 "Be carefull in case you want to set attributes on symbolic\n" \
91 "links, you have to use all the 5 parameters; use 0 for the \n" \
92 "flags value if you want the default behavior (create or " \
93 "replace)\n" \
94 "\n" \
95 "Parameters:\n" \
96 "\t- a string representing filename, or a file-like object,\n" \
97 "\t      or a file descriptor; this represents the file on \n" \
98 "\t      which to act\n" \
99 "\t- a string, representing the attribute whose value to set;\n" \
100 "\t      usually in form of system.posix_acl or user.mime_type\n" \
101 "\t- a string, possibly with embedded NULLs; note that there\n" \
102 "\t      are restrictions regarding the size of the value, for\n" \
103 "\t      example, for ext2/ext3, maximum size is the block size\n" \
104 "\t- (optional) flags; if 0 or ommited the attribute will be \n" \
105 "\t      created or replaced; if XATTR_CREATE, the attribute \n" \
106 "\t      will be created, giving an error if it already exists;\n" \
107 "\t      of XATTR_REPLACE, the attribute will be replaced,\n" \
108 "\t      giving an error if it doesn't exists;\n" \
109 "\t- (optional) a boolean value (defaults to false), which, if\n" \
110 "\t      the file name given is a symbolic link, makes the\n" \
111 "\t      function operate on the symbolic link itself instead\n" \
112 "\t      of its target;" \
113 ;
114
115 /* Wrapper for setxattr */
116 static PyObject *
117 pysetxattr(PyObject *self, PyObject *args)
118 {
119     PyObject *myarg;
120     char *file;
121     int ishandle, filedes, dolink=0;
122     char *attrname;
123     char *buf;
124     int bufsize, nret;
125     int flags = 0;
126     
127     /* Parse the arguments */
128     if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname, &buf, &bufsize, &flags, &dolink))
129         return NULL;
130     if(!convertObj(myarg, &ishandle, &filedes, &file))
131         return NULL;
132
133     /* Set the attribute's value */
134     nret = ishandle ?
135         fsetxattr(filedes, attrname, buf, bufsize, flags) :
136         dolink ?
137         lsetxattr(file, attrname, buf, bufsize, flags) :
138         setxattr(file, attrname, buf, bufsize, flags);
139
140     if(nret == -1) {
141         return PyErr_SetFromErrno(PyExc_IOError);
142     }
143
144     /* Return the result */
145     Py_INCREF(Py_None);
146     return Py_None;
147 }
148
149 static char __pyremovexattr_doc__[] = \
150 "Remove an attribute from a file\n" \
151 "\n" \
152 "Parameters:\n" \
153 "\t- a string representing filename, or a file-like object,\n" \
154 "\t      or a file descriptor; this represents the file on \n" \
155 "\t      which to act\n" \
156 "\t- a string, representing the attribute to be removed;\n" \
157 "\t      usually in form of system.posix_acl or user.mime_type\n" \
158 "\t- (optional) a boolean value (defaults to false), which, if\n" \
159 "\t      the file name given is a symbolic link, makes the\n" \
160 "\t      function operate on the symbolic link itself instead\n" \
161 "\t      of its target;" \
162 ;
163
164 /* Wrapper for removexattr */
165 static PyObject *
166 pyremovexattr(PyObject *self, PyObject *args)
167 {
168     PyObject *myarg;
169     char *file;
170     int ishandle, filedes, dolink=0;
171     char *attrname;
172     int nret;
173     
174     /* Parse the arguments */
175     if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &dolink))
176         return NULL;
177
178     if(!convertObj(myarg, &ishandle, &filedes, &file))
179         return NULL;
180
181     /* Remove the attribute */
182     nret = ishandle ?
183         fremovexattr(filedes, attrname) :
184         dolink ?
185         lremovexattr(file, attrname) :
186         removexattr(file, attrname);
187
188     if(nret == -1)
189         return PyErr_SetFromErrno(PyExc_IOError);
190
191     /* Return the result */
192     Py_INCREF(Py_None);
193     return Py_None;
194 }
195
196 static char __pylistxattr_doc__[] = \
197 "Return the tuple of attribute names from a file\n" \
198 "\n" \
199 "Parameters:\n" \
200 "\t- a string representing filename, or a file-like object,\n" \
201 "\t      or a file descriptor; this represents the file to \n" \
202 "\t      be queried\n" \
203 "\t- (optional) a boolean value (defaults to false), which, if\n" \
204 "\t      the file name given is a symbolic link, makes the\n" \
205 "\t      function operate on the symbolic link itself instead\n" \
206 "\t      of its target;" \
207 ;
208
209 /* Wrapper for listxattr */
210 static PyObject *
211 pylistxattr(PyObject *self, PyObject *args)
212 {
213     char *file = NULL;
214     int filedes = -1;
215     char *buf;
216     int ishandle, dolink=0;
217     int nalloc, nret;
218     PyObject *myarg;
219     PyObject *mytuple;
220     int nattrs;
221     char *s;
222     
223     /* Parse the arguments */
224     if (!PyArg_ParseTuple(args, "O|i", &myarg, &dolink))
225         return NULL;
226     if(!convertObj(myarg, &ishandle, &filedes, &file))
227         return NULL;
228
229     /* Find out the needed size of the buffer */
230     nalloc = ishandle ?
231         flistxattr(filedes, NULL, 0) :
232         listxattr(file, NULL, 0);
233
234     if(nalloc == -1) {
235         return PyErr_SetFromErrno(PyExc_IOError);
236     }
237
238     /* Try to allocate the memory, using Python's allocator */
239     if((buf = PyMem_Malloc(nalloc)) == NULL) {
240         PyErr_NoMemory();
241         return NULL;
242     }
243
244     /* Now retrieve the list of attributes */
245     nret = ishandle ? 
246         flistxattr(filedes, buf, nalloc) : 
247         dolink ?
248         llistxattr(file, buf, nalloc) :
249         listxattr(file, buf, nalloc);
250
251     if(nret == -1) {
252         return PyErr_SetFromErrno(PyExc_IOError);
253     }
254
255     /* Compute the number of attributes in the list */
256     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
257         nattrs++;
258     }
259
260     /* Create the tuple which will hold the result */
261     mytuple = PyTuple_New(nattrs);
262
263     /* Create and insert the attributes as strings in the tuple */
264     for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
265         PyTuple_SET_ITEM(mytuple, nattrs, PyString_FromString(s));
266         nattrs++;
267     }
268
269     /* Free the buffer, now it is no longer needed */
270     PyMem_Free(buf);
271
272     /* Return the result */
273     return mytuple;
274 }
275
276 static PyMethodDef xattr_methods[] = {
277     {"getxattr",  pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
278     {"setxattr",  pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
279     {"removexattr",  pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
280     {"listxattr",  pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
281     {NULL, NULL, 0, NULL}        /* Sentinel */
282 };
283
284 static char __xattr_doc__[] = \
285 "Access extended filesystem attributes\n" \
286 "\n" \
287 "This module gives access to the extended attributes present\n" \
288 "in some operating systems/filesystems. You can list attributes,\n"\
289 "get, set and remove them.\n"\
290 "The last and optional parameter for all functions is a boolean \n"\
291 "value which enables the 'l-' version of the functions - acting\n"\
292 "on symbolic links and not their destination.\n"\
293 "\n" \
294 "Example: \n" \
295 ">>> import xattr\n" \
296 ">>> xattr.listxattr(\"file.txt\")\n" \
297 "('user.mime_type',)\n" \
298 ">>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n" \
299 "'text/plain'\n" \
300 ">>> xattr.setxattr(\"file.txt\", \"user.comment\", \"Simple text file\")\n"\
301 ">>> xattr.listxattr(\"file.txt\")\n" \
302 "('user.mime_type', 'user.comment')\n" \
303 ">>> xattr.removexattr (\"file.txt\", \"user.comment\")\n" \
304 "" \
305 ;
306
307 void
308 initxattr(void)
309 {
310     PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
311     
312     PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
313     PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
314
315 }