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