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