1 /*
2 * Python bindings for the libmount library.
3 *
4 * Copyright (C) 2013, Red Hat, Inc. All rights reserved.
5 * Written by Ondrej Oprala and Karel Zak
6 *
7 * This file is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 *
12 * This file is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this file; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21 #include "pylibmount.h"
22
23 /* Libmount-specific Exception class */
24 PyObject *LibmountError;
25 int pylibmount_debug_mask;
26
27 PyObject *UL_IncRef(void *killme)
28 {
29 Py_INCREF(killme);
30 return killme;
31 }
32
33 void PyFree(void *o)
34 {
35 #if PY_MAJOR_VERSION >= 3
36 Py_TYPE(o)->tp_free((PyObject *)o);
37 #else
38 ((PyObject *)o)->ob_type->tp_free((PyObject *)o);
39 #endif
40 }
41
42 /* Demultiplexer for various possible error conditions across the libmount library */
43 void *UL_RaiseExc(int e)
44 {
45 /* TODO: Do we need to deal with -1/1? */
46 switch (e) {
47 case ENOMEM:
48 PyErr_SetString(PyExc_MemoryError, strerror(e));
49 break;
50 case EINVAL:
51 PyErr_SetString(PyExc_TypeError, strerror(e));
52 break;
53 /* libmount-specific errors */
54 case MNT_ERR_NOFSTAB:
55 PyErr_SetString(LibmountError, "Not found required entry in fstab");
56 break;
57 case MNT_ERR_NOFSTYPE:
58 PyErr_SetString(LibmountError, "Lailed to detect filesystem type");
59 break;
60 case MNT_ERR_NOSOURCE:
61 PyErr_SetString(LibmountError, "Required mount source undefined");
62 break;
63 case MNT_ERR_LOOPDEV:
64 PyErr_SetString(LibmountError, "Loopdev setup failed");
65 break;
66 case MNT_ERR_APPLYFLAGS:
67 PyErr_SetString(LibmountError, "Failed to parse/use userspace mount options");
68 break;
69 case MNT_ERR_MOUNTOPT:
70 PyErr_SetString(LibmountError, "Failed to apply propagation flags");
71 break;
72 case MNT_ERR_AMBIFS:
73 PyErr_SetString(LibmountError, "Libblkid detected more filesystems on the device");
74 break;
75 case MNT_ERR_LOOPOVERLAP:
76 PyErr_SetString(LibmountError, "Detected overlapping loop device that cannot be re-use");
77 break;
78 case MNT_ERR_LOCK:
79 PyErr_SetString(LibmountError, "Failed to lock mtab/utab or so");
80 break;
81 case MNT_ERR_NAMESPACE:
82 PyErr_SetString(LibmountError, "Failed to switch namespace");
83 break;
84 /* some other errno */
85 default:
86 PyErr_SetString(PyExc_Exception, strerror(e));
87 break;
88 }
89 return NULL;
90 }
91
92 /*
93 * General functions
94 */
95 PyObject *PyObjectResultInt(int i)
96 {
97 PyObject *result = Py_BuildValue("i", i);
98 if (!result)
99 PyErr_SetString(PyExc_RuntimeError, CONSTRUCT_ERR);
100 return result;
101 }
102
103 PyObject *PyObjectResultStr(const char *s)
104 {
105 PyObject *result;
106 if (!s)
107 /* TODO: maybe lie about it and return "":
108 * which would allow for
109 * fs = libmount.Fs()
110 * fs.comment += "comment"
111 return Py_BuildValue("s", ""); */
112 Py_RETURN_NONE;
113 result = Py_BuildValue("s", s);
114 if (!result)
115 PyErr_SetString(PyExc_RuntimeError, CONSTRUCT_ERR);
116 return result;
117 }
118
119 /* wrapper around a common use case for PyUnicode_AsASCIIString() */
120 char *pystos(PyObject *pys)
121 {
122 #if PY_MAJOR_VERSION >= 3
123 if (!PyUnicode_Check(pys)) {
124 PyErr_SetString(PyExc_TypeError, ARG_ERR);
125 return NULL;
126 }
127 return (char *)PyUnicode_1BYTE_DATA(pys);
128 #else
129 if (!PyString_Check(pys)) {
130 PyErr_SetString(PyExc_TypeError, ARG_ERR);
131 return NULL;
132 }
133 return PyString_AsString(pys);
134 #endif
135 }
136
137 /*
138 * the libmount module
139 */
140 #define PYLIBMOUNT_DESC \
141 "Python API for the util-linux libmount library.\n\n" \
142 "Please note that none of the classes' attributes may be deleted.\n" \
143 "This is not a complete mapping to the libmount C API, nor is it\n" \
144 "attempting to be one.\n" "Iterator functions only allow forward\n" \
145 "iteration for now. Context.get_{user_,}mflags() differs from the C API\n" \
146 "and returns the flags directly. Fs.get_tag() differs from the C API\n" \
147 "and returns a (tag, value) tuple. Every attribute is \"filtered\"" \
148 "through appropriate getters/setters, no values are set directly."
149
150
151 struct module_state {
152 PyObject *error;
153 };
154
155 #if PY_MAJOR_VERSION >= 3
156 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
157 #else
158 #define GETSTATE(m) (&_state)
159 static struct module_state _state;
160 #endif
161
162 static PyObject *
163 error_out(PyObject *m __attribute__((unused))) {
164 struct module_state *st = GETSTATE(m);
165 PyErr_SetString(st->error, "something bad happened");
166 return NULL;
167 }
168
169 static PyMethodDef pylibmount_methods[] = {
170 {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},
171 {NULL, NULL}
172 };
173
174 #if PY_MAJOR_VERSION >= 3
175
176 static int pylibmount_traverse(PyObject *m, visitproc visit, void *arg) {
177 Py_VISIT(GETSTATE(m)->error);
178 return 0;
179 }
180
181 static int pylibmount_clear(PyObject *m) {
182 Py_CLEAR(GETSTATE(m)->error);
183 return 0;
184 }
185
186 static struct PyModuleDef moduledef = {
187 PyModuleDef_HEAD_INIT,
188 "pylibmount",
189 NULL,
190 sizeof(struct module_state),
191 pylibmount_methods,
192 NULL,
193 pylibmount_traverse,
194 pylibmount_clear,
195 NULL
196 };
197 #define INITERROR return NULL
198 PyMODINIT_FUNC PyInit_pylibmount(void);
199 PyMODINIT_FUNC PyInit_pylibmount(void)
200 #else
201 #define INITERROR return
202 # ifndef PyMODINIT_FUNC
203 # define PyMODINIT_FUNC void
204 # endif
205 PyMODINIT_FUNC initpylibmount(void);
206 PyMODINIT_FUNC initpylibmount(void)
207 #endif
208 {
209 #if PY_MAJOR_VERSION >= 3
210 PyObject *m = PyModule_Create(&moduledef);
211 #else
212 PyObject *m = Py_InitModule3("pylibmount", pylibmount_methods, PYLIBMOUNT_DESC);
213 #endif
214
215 if (!m)
216 INITERROR;
217 /*
218 * init debug stuff
219 */
220 if (!(pylibmount_debug_mask & PYMNT_DEBUG_INIT)) {
221 char *str = getenv("PYLIBMOUNT_DEBUG");
222
223 errno = 0;
224 pylibmount_debug_mask = 0;
225 if (str)
226 pylibmount_debug_mask = strtoul(str, NULL, 0);
227 if (errno)
228 pylibmount_debug_mask = 0;
229
230 pylibmount_debug_mask |= PYMNT_DEBUG_INIT;
231 }
232
233 if (pylibmount_debug_mask && pylibmount_debug_mask != PYMNT_DEBUG_INIT)
234 DBG(INIT, pymnt_debug("library debug mask: 0x%04x",
235 pylibmount_debug_mask));
236 mnt_init_debug(0);
237
238 /*
239 * Add module objects
240 */
241 LibmountError = PyErr_NewException("libmount.Error", NULL, NULL);
242 Py_INCREF(LibmountError);
243 PyModule_AddObject(m, "Error", (PyObject *)LibmountError);
244
245 FS_AddModuleObject(m);
246 Table_AddModuleObject(m);
247 #ifdef __linux__
248 Context_AddModuleObject(m);
249 #endif
250
251 /*
252 * mount(8) userspace options masks (MNT_MAP_USERSPACE map)
253 */
254 PyModule_AddIntConstant(m, "MNT_MS_COMMENT", MNT_MS_COMMENT);
255 PyModule_AddIntConstant(m, "MNT_MS_GROUP", MNT_MS_GROUP);
256 PyModule_AddIntConstant(m, "MNT_MS_HELPER", MNT_MS_HELPER);
257 PyModule_AddIntConstant(m, "MNT_MS_LOOP", MNT_MS_LOOP);
258 PyModule_AddIntConstant(m, "MNT_MS_NETDEV", MNT_MS_NETDEV);
259 PyModule_AddIntConstant(m, "MNT_MS_NOAUTO", MNT_MS_NOAUTO);
260 PyModule_AddIntConstant(m, "MNT_MS_NOFAIL", MNT_MS_NOFAIL);
261 PyModule_AddIntConstant(m, "MNT_MS_OFFSET", MNT_MS_OFFSET);
262 PyModule_AddIntConstant(m, "MNT_MS_OWNER", MNT_MS_OWNER);
263 PyModule_AddIntConstant(m, "MNT_MS_SIZELIMIT", MNT_MS_SIZELIMIT);
264 PyModule_AddIntConstant(m, "MNT_MS_ENCRYPTION", MNT_MS_ENCRYPTION);
265 PyModule_AddIntConstant(m, "MNT_MS_UHELPER", MNT_MS_UHELPER);
266 PyModule_AddIntConstant(m, "MNT_MS_USER", MNT_MS_USER);
267 PyModule_AddIntConstant(m, "MNT_MS_USERS", MNT_MS_USERS);
268 PyModule_AddIntConstant(m, "MNT_MS_XCOMMENT", MNT_MS_XCOMMENT);
269 PyModule_AddIntConstant(m, "MNT_MS_HASH_DEVICE", MNT_MS_HASH_DEVICE);
270 PyModule_AddIntConstant(m, "MNT_MS_ROOT_HASH", MNT_MS_ROOT_HASH);
271 PyModule_AddIntConstant(m, "MNT_MS_HASH_OFFSET", MNT_MS_HASH_OFFSET);
272 PyModule_AddIntConstant(m, "MNT_MS_ROOT_HASH_FILE", MNT_MS_ROOT_HASH_FILE);
273 PyModule_AddIntConstant(m, "MNT_MS_FEC_DEVICE", MNT_MS_FEC_DEVICE);
274 PyModule_AddIntConstant(m, "MNT_MS_FEC_OFFSET", MNT_MS_FEC_OFFSET);
275 PyModule_AddIntConstant(m, "MNT_MS_FEC_ROOTS", MNT_MS_FEC_ROOTS);
276 PyModule_AddIntConstant(m, "MNT_MS_ROOT_HASH_SIG", MNT_MS_ROOT_HASH_SIG);
277
278 /*
279 * mount(2) MS_* masks (MNT_MAP_LINUX map)
280 */
281 PyModule_AddIntConstant(m, "MS_BIND", MS_BIND);
282 PyModule_AddIntConstant(m, "MS_DIRSYNC", MS_DIRSYNC);
283 PyModule_AddIntConstant(m, "MS_I_VERSION", MS_I_VERSION);
284 PyModule_AddIntConstant(m, "MS_MANDLOCK", MS_MANDLOCK);
285 PyModule_AddIntConstant(m, "MS_MGC_MSK", MS_MGC_MSK);
286 PyModule_AddIntConstant(m, "MS_MGC_VAL", MS_MGC_VAL);
287 PyModule_AddIntConstant(m, "MS_MOVE", MS_MOVE);
288 PyModule_AddIntConstant(m, "MS_NOATIME", MS_NOATIME);
289 PyModule_AddIntConstant(m, "MS_NODEV", MS_NODEV);
290 PyModule_AddIntConstant(m, "MS_NODIRATIME", MS_NODIRATIME);
291 PyModule_AddIntConstant(m, "MS_NOEXEC", MS_NOEXEC);
292 PyModule_AddIntConstant(m, "MS_NOSUID", MS_NOSUID);
293 PyModule_AddIntConstant(m, "MS_OWNERSECURE", MS_OWNERSECURE);
294 PyModule_AddIntConstant(m, "MS_PRIVATE", MS_PRIVATE);
295 PyModule_AddIntConstant(m, "MS_PROPAGATION", MS_PROPAGATION);
296 PyModule_AddIntConstant(m, "MS_RDONLY", MS_RDONLY);
297 PyModule_AddIntConstant(m, "MS_REC", MS_REC);
298 PyModule_AddIntConstant(m, "MS_RELATIME", MS_RELATIME);
299 PyModule_AddIntConstant(m, "MS_REMOUNT", MS_REMOUNT);
300 PyModule_AddIntConstant(m, "MS_SECURE", MS_SECURE);
301 PyModule_AddIntConstant(m, "MS_SHARED", MS_SHARED);
302 PyModule_AddIntConstant(m, "MS_SILENT", MS_SILENT);
303 PyModule_AddIntConstant(m, "MS_SLAVE", MS_SLAVE);
304 PyModule_AddIntConstant(m, "MS_STRICTATIME", MS_STRICTATIME);
305 PyModule_AddIntConstant(m, "MS_SYNCHRONOUS", MS_SYNCHRONOUS);
306 PyModule_AddIntConstant(m, "MS_UNBINDABLE", MS_UNBINDABLE);
307
308 /* Will we need these directly?
309 PyModule_AddIntConstant(m, "MNT_ERR_AMBIFS", MNT_ERR_AMBIFS);
310 PyModule_AddIntConstant(m, "MNT_ERR_APPLYFLAGS", MNT_ERR_APPLYFLAGS);
311 PyModule_AddIntConstant(m, "MNT_ERR_LOOPDEV", MNT_ERR_LOOPDEV);
312 PyModule_AddIntConstant(m, "MNT_ERR_MOUNTOPT", MNT_ERR_MOUNTOPT);
313 PyModule_AddIntConstant(m, "MNT_ERR_NOFSTAB", MNT_ERR_NOFSTAB);
314 PyModule_AddIntConstant(m, "MNT_ERR_NOFSTYPE", MNT_ERR_NOFSTYPE);
315 PyModule_AddIntConstant(m, "MNT_ERR_NOSOURCE", MNT_ERR_NOSOURCE);
316 */
317
318 /* Still useful for functions using iterators internally */
319 PyModule_AddIntConstant(m, "MNT_ITER_FORWARD", MNT_ITER_FORWARD);
320 PyModule_AddIntConstant(m, "MNT_ITER_BACKWARD", MNT_ITER_BACKWARD);
321
322 #if PY_MAJOR_VERSION >= 3
323 return m;
324 #endif
325 }
326