1 /*
2  * Permission is hereby granted, free of charge, to any person obtaining a copy
3  * of this software and associated documentation files (the "Software"), to
4  * deal in the Software without restriction, including without limitation the
5  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
6  * sell copies of the Software, and to permit persons to whom the Software is
7  * furnished to do so, subject to the following conditions:
8  *
9  * The above copyright notice and this permission notice shall be included in
10  * all copies or substantial portions of the Software.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18  * DEALINGS IN THE SOFTWARE.
19  *
20  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
21  * Use is subject to license terms.
22  */
23 
24 #include <Python.h>
25 
26 #include <xenfsimage.h>
27 #include <stdlib.h>
28 
29 typedef struct fsimage_fs {
30 	PyObject_HEAD
31 	fsi_t *fs;
32 } fsimage_fs_t;
33 
34 typedef struct fsimage_file {
35 	PyObject_HEAD
36 	fsimage_fs_t *fs;
37 	fsi_file_t *file;
38 } fsimage_file_t;
39 
40 static PyObject *
fsimage_file_read(fsimage_file_t * file,PyObject * args,PyObject * kwargs)41 fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs)
42 {
43 	static char *kwlist[] = { "size", "offset", NULL };
44 	int bufsize;
45 	int size = 0;
46 	uint64_t offset = 0;
47 	ssize_t bytesread = 0;
48 	PyObject * buffer;
49 
50 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iL", kwlist,
51 	    &size, &offset))
52 		return (NULL);
53 
54 	bufsize = size ? size : 4096;
55 
56 	buffer =
57 #if PY_MAJOR_VERSION < 3
58 		PyString_FromStringAndSize(NULL, bufsize);
59 #else
60 		PyBytes_FromStringAndSize(NULL, bufsize);
61 #endif
62 
63 	if (buffer == NULL)
64 		return (NULL);
65 
66 	while (1) {
67 		int err;
68 		void *buf =
69 #if PY_MAJOR_VERSION < 3
70 			PyString_AS_STRING(buffer) + bytesread;
71 #else
72 			PyBytes_AS_STRING(buffer) + bytesread;
73 #endif
74 
75 		err = fsi_pread_file(file->file, buf, bufsize,
76 		    bytesread + offset);
77 
78 		if (err == -1) {
79 			Py_DECREF(buffer);
80 			PyErr_SetFromErrno(PyExc_IOError);
81 			return (NULL);
82 		} else if (err == 0) {
83 			break;
84 		}
85 
86 		bytesread += err;
87 
88 		if (size != 0) {
89 			bufsize -= bytesread;
90 			if (bufsize == 0)
91 				break;
92 		} else {
93 #if PY_MAJOR_VERSION < 3
94 			if (_PyString_Resize(&buffer, bytesread + bufsize) < 0)
95 #else
96 			if (_PyBytes_Resize(&buffer, bytesread + bufsize) < 0)
97 #endif
98 				return (NULL);
99 		}
100 	}
101 
102 #if PY_MAJOR_VERSION < 3
103 	_PyString_Resize(&buffer, bytesread);
104 #else
105 	_PyBytes_Resize(&buffer, bytesread);
106 #endif
107 	return (buffer);
108 }
109 
110 PyDoc_STRVAR(fsimage_file_read__doc__,
111    "read(file, [size=size, offset=off])\n"
112    "\n"
113    "Read size bytes (or all bytes if not set) from the given "
114    "file. If offset is specified as well, read from the given "
115    "offset.\n");
116 
117 static struct PyMethodDef fsimage_file_methods[] = {
118 	{ "read", (PyCFunction) fsimage_file_read,
119 	    METH_VARARGS|METH_KEYWORDS, fsimage_file_read__doc__ },
120 	{ NULL, NULL, 0, NULL }
121 };
122 
123 #if PY_MAJOR_VERSION < 3
124 static PyObject *
fsimage_file_getattr(fsimage_file_t * file,char * name)125 fsimage_file_getattr(fsimage_file_t *file, char *name)
126 {
127 	return (Py_FindMethod(fsimage_file_methods, (PyObject *)file, name));
128 }
129 #endif
130 
131 static void
fsimage_file_dealloc(fsimage_file_t * file)132 fsimage_file_dealloc(fsimage_file_t *file)
133 {
134 	if (file->file != NULL)
135 		fsi_close_file(file->file);
136 	Py_XDECREF(file->fs);
137 	PyObject_DEL(file);
138 }
139 
140 static char fsimage_file_type__doc__[] = "Filesystem image file";
141 PyTypeObject fsimage_file_type = {
142 	PyVarObject_HEAD_INIT(&PyType_Type, 0)
143 	.tp_name = "xenfsimage.file",
144 	.tp_basicsize = sizeof(fsimage_file_t),
145 	.tp_dealloc = (destructor) fsimage_file_dealloc,
146 #if PY_MAJOR_VERSION < 3
147 	.tp_getattr = (getattrfunc) fsimage_file_getattr,
148 #endif
149 	.tp_flags = Py_TPFLAGS_DEFAULT,
150 	.tp_doc = fsimage_file_type__doc__,
151 #if PY_MAJOR_VERSION >= 3
152 	.tp_methods = fsimage_file_methods,
153 #endif
154 };
155 
156 static PyObject *
fsimage_fs_open_file(fsimage_fs_t * fs,PyObject * args,PyObject * kwargs)157 fsimage_fs_open_file(fsimage_fs_t *fs, PyObject *args, PyObject *kwargs)
158 {
159 	static char *kwlist[] = { "name", NULL };
160 	fsimage_file_t *file;
161 	char *name;
162 
163 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name))
164 		return (NULL);
165 
166 	file = (fsimage_file_t *)PyObject_NEW(fsimage_file_t, &fsimage_file_type);
167 
168 	if (file == NULL)
169 		return (NULL);
170 
171 	file->fs = fs;
172 
173 	Py_INCREF(file->fs);
174 	if ((file->file = fsi_open_file(fs->fs, name)) == NULL) {
175 		Py_DECREF(file->fs);
176 		file->fs = NULL;
177 		PyErr_SetFromErrno(PyExc_IOError);
178 		return (NULL);
179 	}
180 
181 	return ((PyObject *)file);
182 }
183 
184 static PyObject *
fsimage_fs_file_exists(fsimage_fs_t * fs,PyObject * args,PyObject * kwargs)185 fsimage_fs_file_exists(fsimage_fs_t *fs, PyObject *args, PyObject *kwargs)
186 {
187 	static char *kwlist[] = { "name", NULL };
188 	char *name;
189 
190 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name))
191 		return (NULL);
192 
193 	if (fsi_file_exists(fs->fs, name)) {
194 		Py_INCREF(Py_True);
195 		return (Py_True);
196 	}
197 
198 	Py_INCREF(Py_False);
199 	return (Py_False);
200 }
201 
202 PyDoc_STRVAR(fsimage_fs_open_file__doc__,
203    "open_file(fs, filename) - lookup name in the given fs and return the file");
204 PyDoc_STRVAR(fsimage_fs_file_exists__doc__,
205    "file_exists(fs, name) - lookup name in the given fs and return "
206    "True if it exists");
207 
208 static struct PyMethodDef fsimage_fs_methods[] = {
209 	{ "open_file", (PyCFunction) fsimage_fs_open_file,
210 	  METH_VARARGS|METH_KEYWORDS, fsimage_fs_open_file__doc__ },
211 	{ "file_exists", (PyCFunction) fsimage_fs_file_exists,
212 	  METH_VARARGS|METH_KEYWORDS, fsimage_fs_file_exists__doc__ },
213 	{ NULL, NULL, 0, NULL }
214 };
215 
216 #if PY_MAJOR_VERSION < 3
217 static PyObject *
fsimage_fs_getattr(fsimage_fs_t * fs,char * name)218 fsimage_fs_getattr(fsimage_fs_t *fs, char *name)
219 {
220 	return (Py_FindMethod(fsimage_fs_methods, (PyObject *)fs, name));
221 }
222 #endif
223 
224 static void
fsimage_fs_dealloc(fsimage_fs_t * fs)225 fsimage_fs_dealloc (fsimage_fs_t *fs)
226 {
227 	if (fs->fs != NULL)
228 		fsi_close_fsimage(fs->fs);
229 	PyObject_DEL(fs);
230 }
231 
232 PyDoc_STRVAR(fsimage_fs_type__doc__, "Filesystem image");
233 
234 PyTypeObject fsimage_fs_type = {
235 	PyVarObject_HEAD_INIT(&PyType_Type, 0)
236 	.tp_name = "xenfsimage.fs",
237 	.tp_basicsize = sizeof(fsimage_fs_t),
238 	.tp_dealloc = (destructor) fsimage_fs_dealloc,
239 #if PY_MAJOR_VERSION < 3
240 	.tp_getattr = (getattrfunc) fsimage_fs_getattr,
241 #endif
242 	.tp_flags = Py_TPFLAGS_DEFAULT,
243 	.tp_doc = fsimage_fs_type__doc__,
244 #if PY_MAJOR_VERSION >= 3
245 	.tp_methods = fsimage_fs_methods,
246 #endif
247 };
248 
249 static PyObject *
fsimage_open(PyObject * o,PyObject * args,PyObject * kwargs)250 fsimage_open(PyObject *o, PyObject *args, PyObject *kwargs)
251 {
252 	static char *kwlist[] = { "name", "offset", "options", NULL };
253 	char *name;
254 	char *options = NULL;
255 	uint64_t offset = 0;
256 	fsimage_fs_t *fs;
257 
258 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|Ls", kwlist,
259 	    &name, &offset, &options))
260 		return (NULL);
261 
262 	if ((fs = PyObject_NEW(fsimage_fs_t, &fsimage_fs_type)) == NULL)
263 		return (NULL);
264 
265 	if ((fs->fs = fsi_open_fsimage(name, offset, options)) == NULL) {
266 		PyErr_SetFromErrno(PyExc_IOError);
267 		return (NULL);
268 	}
269 
270 	return (PyObject *)fs;
271 }
272 
273 static PyObject *
fsimage_getbootstring(PyObject * o,PyObject * args)274 fsimage_getbootstring(PyObject *o, PyObject *args)
275 {
276 	PyObject *fs;
277 	char	*bootstring;
278 	fsi_t	*fsi;
279 
280 	if (!PyArg_ParseTuple(args, "O", &fs))
281 		return (NULL);
282 
283 	fsi = ((fsimage_fs_t *)fs)->fs;
284 	bootstring = fsi_fs_bootstring(fsi);
285 
286 	return Py_BuildValue("s", bootstring);
287 }
288 
289 PyDoc_STRVAR(fsimage_open__doc__,
290     "open(name, [offset=off]) - Open the given file as a filesystem image.\n"
291     "\n"
292     "name - name of file to open.\n"
293     "offset - offset of file system within file image.\n"
294     "options - mount options string.\n");
295 
296 PyDoc_STRVAR(fsimage_getbootstring__doc__,
297     "getbootstring(fs) - Return the boot string needed for this file system "
298     "or NULL if none is needed.\n");
299 
300 static struct PyMethodDef fsimage_module_methods[] = {
301 	{ "open", (PyCFunction)fsimage_open,
302 	    METH_VARARGS|METH_KEYWORDS, fsimage_open__doc__ },
303 	{ "getbootstring", (PyCFunction)fsimage_getbootstring,
304 	    METH_VARARGS, fsimage_getbootstring__doc__ },
305 	{ NULL, NULL, 0, NULL }
306 };
307 
308 #if PY_MAJOR_VERSION >= 3
309 static struct PyModuleDef fsimage_module_def = {
310 	PyModuleDef_HEAD_INIT,
311 	.m_name = "xenfsimage",
312 	.m_size = -1,
313 	.m_methods = fsimage_module_methods,
314 };
315 #endif
316 
317 PyMODINIT_FUNC
318 #if PY_MAJOR_VERSION >= 3
PyInit_xenfsimage(void)319 PyInit_xenfsimage(void)
320 #else
321 initxenfsimage(void)
322 #endif
323 {
324 #if PY_MAJOR_VERSION < 3
325 	Py_InitModule("xenfsimage", fsimage_module_methods);
326 #else
327 	if (PyType_Ready(&fsimage_fs_type) < 0 || PyType_Ready(&fsimage_file_type) < 0)
328 		return NULL;
329 	return PyModule_Create(&fsimage_module_def);
330 #endif
331 }
332