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