1 /*
2 * Python bindings module for libvhdi (pyvhdi)
3 *
4 * Copyright (C) 2012-2021, Joachim Metz <joachim.metz@gmail.com>
5 *
6 * Refer to AUTHORS for acknowledgements.
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22 #include <common.h>
23 #include <narrow_string.h>
24 #include <types.h>
25
26 #if defined( HAVE_STDLIB_H ) || defined( HAVE_WINAPI )
27 #include <stdlib.h>
28 #endif
29
30 #include "pyvhdi.h"
31 #include "pyvhdi_disk_types.h"
32 #include "pyvhdi_error.h"
33 #include "pyvhdi_file.h"
34 #include "pyvhdi_file_object_io_handle.h"
35 #include "pyvhdi_libbfio.h"
36 #include "pyvhdi_libcerror.h"
37 #include "pyvhdi_libvhdi.h"
38 #include "pyvhdi_python.h"
39 #include "pyvhdi_unused.h"
40
41 #if !defined( LIBVHDI_HAVE_BFIO )
42
43 LIBVHDI_EXTERN \
44 int libvhdi_check_file_signature_file_io_handle(
45 libbfio_handle_t *file_io_handle,
46 libvhdi_error_t **error );
47
48 #endif /* !defined( LIBVHDI_HAVE_BFIO ) */
49
50 /* The pyvhdi module methods
51 */
52 PyMethodDef pyvhdi_module_methods[] = {
53 { "get_version",
54 (PyCFunction) pyvhdi_get_version,
55 METH_NOARGS,
56 "get_version() -> String\n"
57 "\n"
58 "Retrieves the version." },
59
60 { "check_file_signature",
61 (PyCFunction) pyvhdi_check_file_signature,
62 METH_VARARGS | METH_KEYWORDS,
63 "check_file_signature(filename) -> Boolean\n"
64 "\n"
65 "Checks if a file has a Virtual Hard Disk (VHD) image file signature." },
66
67 { "check_file_signature_file_object",
68 (PyCFunction) pyvhdi_check_file_signature_file_object,
69 METH_VARARGS | METH_KEYWORDS,
70 "check_file_signature_file_object(file_object) -> Boolean\n"
71 "\n"
72 "Checks if a file has a Virtual Hard Disk (VHD) image file signature using a file-like object." },
73
74 { "open",
75 (PyCFunction) pyvhdi_open_new_file,
76 METH_VARARGS | METH_KEYWORDS,
77 "open(filename, mode='r') -> Object\n"
78 "\n"
79 "Opens a file." },
80
81 { "open_file_object",
82 (PyCFunction) pyvhdi_open_new_file_with_file_object,
83 METH_VARARGS | METH_KEYWORDS,
84 "open_file_object(file_object, mode='r') -> Object\n"
85 "\n"
86 "Opens a file using a file-like object." },
87
88 /* Sentinel */
89 { NULL, NULL, 0, NULL }
90 };
91
92 /* Retrieves the pyvhdi/libvhdi version
93 * Returns a Python object if successful or NULL on error
94 */
pyvhdi_get_version(PyObject * self PYVHDI_ATTRIBUTE_UNUSED,PyObject * arguments PYVHDI_ATTRIBUTE_UNUSED)95 PyObject *pyvhdi_get_version(
96 PyObject *self PYVHDI_ATTRIBUTE_UNUSED,
97 PyObject *arguments PYVHDI_ATTRIBUTE_UNUSED )
98 {
99 const char *errors = NULL;
100 const char *version_string = NULL;
101 size_t version_string_length = 0;
102
103 PYVHDI_UNREFERENCED_PARAMETER( self )
104 PYVHDI_UNREFERENCED_PARAMETER( arguments )
105
106 Py_BEGIN_ALLOW_THREADS
107
108 version_string = libvhdi_get_version();
109
110 Py_END_ALLOW_THREADS
111
112 version_string_length = narrow_string_length(
113 version_string );
114
115 /* Pass the string length to PyUnicode_DecodeUTF8
116 * otherwise it makes the end of string character is part
117 * of the string
118 */
119 return( PyUnicode_DecodeUTF8(
120 version_string,
121 (Py_ssize_t) version_string_length,
122 errors ) );
123 }
124
125 /* Checks if a file has a Virtual Hard Disk (VHD) image file signature
126 * Returns a Python object if successful or NULL on error
127 */
pyvhdi_check_file_signature(PyObject * self PYVHDI_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)128 PyObject *pyvhdi_check_file_signature(
129 PyObject *self PYVHDI_ATTRIBUTE_UNUSED,
130 PyObject *arguments,
131 PyObject *keywords )
132 {
133 PyObject *string_object = NULL;
134 libcerror_error_t *error = NULL;
135 const char *filename_narrow = NULL;
136 static char *function = "pyvhdi_check_file_signature";
137 static char *keyword_list[] = { "filename", NULL };
138 int result = 0;
139
140 #if defined( HAVE_WIDE_SYSTEM_CHARACTER )
141 const wchar_t *filename_wide = NULL;
142 #else
143 PyObject *utf8_string_object = NULL;
144 #endif
145
146 PYVHDI_UNREFERENCED_PARAMETER( self )
147
148 /* Note that PyArg_ParseTupleAndKeywords with "s" will force Unicode strings to be converted to narrow character string.
149 * On Windows the narrow character strings contains an extended ASCII string with a codepage. Hence we get a conversion
150 * exception. This will also fail if the default encoding is not set correctly. We cannot use "u" here either since that
151 * does not allow us to pass non Unicode string objects and Python (at least 2.7) does not seems to automatically upcast them.
152 */
153 if( PyArg_ParseTupleAndKeywords(
154 arguments,
155 keywords,
156 "O|",
157 keyword_list,
158 &string_object ) == 0 )
159 {
160 return( NULL );
161 }
162 PyErr_Clear();
163
164 result = PyObject_IsInstance(
165 string_object,
166 (PyObject *) &PyUnicode_Type );
167
168 if( result == -1 )
169 {
170 pyvhdi_error_fetch_and_raise(
171 PyExc_RuntimeError,
172 "%s: unable to determine if string object is of type Unicode.",
173 function );
174
175 return( NULL );
176 }
177 else if( result != 0 )
178 {
179 PyErr_Clear();
180
181 #if defined( HAVE_WIDE_SYSTEM_CHARACTER )
182 filename_wide = (wchar_t *) PyUnicode_AsUnicode(
183 string_object );
184 Py_BEGIN_ALLOW_THREADS
185
186 result = libvhdi_check_file_signature_wide(
187 filename_wide,
188 &error );
189
190 Py_END_ALLOW_THREADS
191 #else
192 utf8_string_object = PyUnicode_AsUTF8String(
193 string_object );
194
195 if( utf8_string_object == NULL )
196 {
197 pyvhdi_error_fetch_and_raise(
198 PyExc_RuntimeError,
199 "%s: unable to convert Unicode string to UTF-8.",
200 function );
201
202 return( NULL );
203 }
204 #if PY_MAJOR_VERSION >= 3
205 filename_narrow = PyBytes_AsString(
206 utf8_string_object );
207 #else
208 filename_narrow = PyString_AsString(
209 utf8_string_object );
210 #endif
211 Py_BEGIN_ALLOW_THREADS
212
213 result = libvhdi_check_file_signature(
214 filename_narrow,
215 &error );
216
217 Py_END_ALLOW_THREADS
218
219 Py_DecRef(
220 utf8_string_object );
221
222 #endif /* defined( HAVE_WIDE_SYSTEM_CHARACTER ) */
223
224 if( result == -1 )
225 {
226 pyvhdi_error_raise(
227 error,
228 PyExc_IOError,
229 "%s: unable to check file signature.",
230 function );
231
232 libcerror_error_free(
233 &error );
234
235 return( NULL );
236 }
237 if( result != 0 )
238 {
239 Py_IncRef(
240 (PyObject *) Py_True );
241
242 return( Py_True );
243 }
244 Py_IncRef(
245 (PyObject *) Py_False );
246
247 return( Py_False );
248 }
249 PyErr_Clear();
250
251 #if PY_MAJOR_VERSION >= 3
252 result = PyObject_IsInstance(
253 string_object,
254 (PyObject *) &PyBytes_Type );
255 #else
256 result = PyObject_IsInstance(
257 string_object,
258 (PyObject *) &PyString_Type );
259 #endif
260 if( result == -1 )
261 {
262 pyvhdi_error_fetch_and_raise(
263 PyExc_RuntimeError,
264 "%s: unable to determine if string object is of type string.",
265 function );
266
267 return( NULL );
268 }
269 else if( result != 0 )
270 {
271 PyErr_Clear();
272
273 #if PY_MAJOR_VERSION >= 3
274 filename_narrow = PyBytes_AsString(
275 string_object );
276 #else
277 filename_narrow = PyString_AsString(
278 string_object );
279 #endif
280 Py_BEGIN_ALLOW_THREADS
281
282 result = libvhdi_check_file_signature(
283 filename_narrow,
284 &error );
285
286 Py_END_ALLOW_THREADS
287
288 if( result == -1 )
289 {
290 pyvhdi_error_raise(
291 error,
292 PyExc_IOError,
293 "%s: unable to check file signature.",
294 function );
295
296 libcerror_error_free(
297 &error );
298
299 return( NULL );
300 }
301 if( result != 0 )
302 {
303 Py_IncRef(
304 (PyObject *) Py_True );
305
306 return( Py_True );
307 }
308 Py_IncRef(
309 (PyObject *) Py_False );
310
311 return( Py_False );
312 }
313 PyErr_Format(
314 PyExc_TypeError,
315 "%s: unsupported string object type.",
316 function );
317
318 return( NULL );
319 }
320
321 /* Checks if a file has a Virtual Hard Disk (VHD) image file signature using a file-like object
322 * Returns a Python object if successful or NULL on error
323 */
pyvhdi_check_file_signature_file_object(PyObject * self PYVHDI_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)324 PyObject *pyvhdi_check_file_signature_file_object(
325 PyObject *self PYVHDI_ATTRIBUTE_UNUSED,
326 PyObject *arguments,
327 PyObject *keywords )
328 {
329 PyObject *file_object = NULL;
330 libbfio_handle_t *file_io_handle = NULL;
331 libcerror_error_t *error = NULL;
332 static char *function = "pyvhdi_check_file_signature_file_object";
333 static char *keyword_list[] = { "file_object", NULL };
334 int result = 0;
335
336 PYVHDI_UNREFERENCED_PARAMETER( self )
337
338 if( PyArg_ParseTupleAndKeywords(
339 arguments,
340 keywords,
341 "O|",
342 keyword_list,
343 &file_object ) == 0 )
344 {
345 return( NULL );
346 }
347 if( pyvhdi_file_object_initialize(
348 &file_io_handle,
349 file_object,
350 &error ) != 1 )
351 {
352 pyvhdi_error_raise(
353 error,
354 PyExc_MemoryError,
355 "%s: unable to initialize file IO handle.",
356 function );
357
358 libcerror_error_free(
359 &error );
360
361 goto on_error;
362 }
363 Py_BEGIN_ALLOW_THREADS
364
365 result = libvhdi_check_file_signature_file_io_handle(
366 file_io_handle,
367 &error );
368
369 Py_END_ALLOW_THREADS
370
371 if( result == -1 )
372 {
373 pyvhdi_error_raise(
374 error,
375 PyExc_IOError,
376 "%s: unable to check file signature.",
377 function );
378
379 libcerror_error_free(
380 &error );
381
382 goto on_error;
383 }
384 if( libbfio_handle_free(
385 &file_io_handle,
386 &error ) != 1 )
387 {
388 pyvhdi_error_raise(
389 error,
390 PyExc_MemoryError,
391 "%s: unable to free file IO handle.",
392 function );
393
394 libcerror_error_free(
395 &error );
396
397 goto on_error;
398 }
399 if( result != 0 )
400 {
401 Py_IncRef(
402 (PyObject *) Py_True );
403
404 return( Py_True );
405 }
406 Py_IncRef(
407 (PyObject *) Py_False );
408
409 return( Py_False );
410
411 on_error:
412 if( file_io_handle != NULL )
413 {
414 libbfio_handle_free(
415 &file_io_handle,
416 NULL );
417 }
418 return( NULL );
419 }
420
421 /* Creates a new file object and opens it
422 * Returns a Python object if successful or NULL on error
423 */
pyvhdi_open_new_file(PyObject * self PYVHDI_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)424 PyObject *pyvhdi_open_new_file(
425 PyObject *self PYVHDI_ATTRIBUTE_UNUSED,
426 PyObject *arguments,
427 PyObject *keywords )
428 {
429 pyvhdi_file_t *pyvhdi_file = NULL;
430 static char *function = "pyvhdi_open_new_file";
431
432 PYVHDI_UNREFERENCED_PARAMETER( self )
433
434 /* PyObject_New does not invoke tp_init
435 */
436 pyvhdi_file = PyObject_New(
437 struct pyvhdi_file,
438 &pyvhdi_file_type_object );
439
440 if( pyvhdi_file == NULL )
441 {
442 PyErr_Format(
443 PyExc_MemoryError,
444 "%s: unable to create file.",
445 function );
446
447 goto on_error;
448 }
449 if( pyvhdi_file_init(
450 pyvhdi_file ) != 0 )
451 {
452 goto on_error;
453 }
454 if( pyvhdi_file_open(
455 pyvhdi_file,
456 arguments,
457 keywords ) == NULL )
458 {
459 goto on_error;
460 }
461 return( (PyObject *) pyvhdi_file );
462
463 on_error:
464 if( pyvhdi_file != NULL )
465 {
466 Py_DecRef(
467 (PyObject *) pyvhdi_file );
468 }
469 return( NULL );
470 }
471
472 /* Creates a new file object and opens it using a file-like object
473 * Returns a Python object if successful or NULL on error
474 */
pyvhdi_open_new_file_with_file_object(PyObject * self PYVHDI_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)475 PyObject *pyvhdi_open_new_file_with_file_object(
476 PyObject *self PYVHDI_ATTRIBUTE_UNUSED,
477 PyObject *arguments,
478 PyObject *keywords )
479 {
480 pyvhdi_file_t *pyvhdi_file = NULL;
481 static char *function = "pyvhdi_open_new_file_with_file_object";
482
483 PYVHDI_UNREFERENCED_PARAMETER( self )
484
485 /* PyObject_New does not invoke tp_init
486 */
487 pyvhdi_file = PyObject_New(
488 struct pyvhdi_file,
489 &pyvhdi_file_type_object );
490
491 if( pyvhdi_file == NULL )
492 {
493 PyErr_Format(
494 PyExc_MemoryError,
495 "%s: unable to create file.",
496 function );
497
498 goto on_error;
499 }
500 if( pyvhdi_file_init(
501 pyvhdi_file ) != 0 )
502 {
503 goto on_error;
504 }
505 if( pyvhdi_file_open_file_object(
506 pyvhdi_file,
507 arguments,
508 keywords ) == NULL )
509 {
510 goto on_error;
511 }
512 return( (PyObject *) pyvhdi_file );
513
514 on_error:
515 if( pyvhdi_file != NULL )
516 {
517 Py_DecRef(
518 (PyObject *) pyvhdi_file );
519 }
520 return( NULL );
521 }
522
523 #if PY_MAJOR_VERSION >= 3
524
525 /* The pyvhdi module definition
526 */
527 PyModuleDef pyvhdi_module_definition = {
528 PyModuleDef_HEAD_INIT,
529
530 /* m_name */
531 "pyvhdi",
532 /* m_doc */
533 "Python libvhdi module (pyvhdi).",
534 /* m_size */
535 -1,
536 /* m_methods */
537 pyvhdi_module_methods,
538 /* m_reload */
539 NULL,
540 /* m_traverse */
541 NULL,
542 /* m_clear */
543 NULL,
544 /* m_free */
545 NULL,
546 };
547
548 #endif /* PY_MAJOR_VERSION >= 3 */
549
550 /* Initializes the pyvhdi module
551 */
552 #if PY_MAJOR_VERSION >= 3
PyInit_pyvhdi(void)553 PyMODINIT_FUNC PyInit_pyvhdi(
554 void )
555 #else
556 PyMODINIT_FUNC initpyvhdi(
557 void )
558 #endif
559 {
560 PyObject *module = NULL;
561 PyGILState_STATE gil_state = 0;
562
563 #if defined( HAVE_DEBUG_OUTPUT )
564 libvhdi_notify_set_stream(
565 stderr,
566 NULL );
567 libvhdi_notify_set_verbose(
568 1 );
569 #endif
570
571 /* Create the module
572 * This function must be called before grabbing the GIL
573 * otherwise the module will segfault on a version mismatch
574 */
575 #if PY_MAJOR_VERSION >= 3
576 module = PyModule_Create(
577 &pyvhdi_module_definition );
578 #else
579 module = Py_InitModule3(
580 "pyvhdi",
581 pyvhdi_module_methods,
582 "Python libvhdi module (pyvhdi)." );
583 #endif
584 if( module == NULL )
585 {
586 #if PY_MAJOR_VERSION >= 3
587 return( NULL );
588 #else
589 return;
590 #endif
591 }
592 #if PY_VERSION_HEX < 0x03070000
593 PyEval_InitThreads();
594 #endif
595 gil_state = PyGILState_Ensure();
596
597 /* Setup the disk_types type object
598 */
599 pyvhdi_disk_types_type_object.tp_new = PyType_GenericNew;
600
601 if( pyvhdi_disk_types_init_type(
602 &pyvhdi_disk_types_type_object ) != 1 )
603 {
604 goto on_error;
605 }
606 if( PyType_Ready(
607 &pyvhdi_disk_types_type_object ) < 0 )
608 {
609 goto on_error;
610 }
611 Py_IncRef(
612 (PyObject *) &pyvhdi_disk_types_type_object );
613
614 PyModule_AddObject(
615 module,
616 "disk_types",
617 (PyObject *) &pyvhdi_disk_types_type_object );
618
619 /* Setup the file type object
620 */
621 pyvhdi_file_type_object.tp_new = PyType_GenericNew;
622
623 if( PyType_Ready(
624 &pyvhdi_file_type_object ) < 0 )
625 {
626 goto on_error;
627 }
628 Py_IncRef(
629 (PyObject *) &pyvhdi_file_type_object );
630
631 PyModule_AddObject(
632 module,
633 "file",
634 (PyObject *) &pyvhdi_file_type_object );
635
636 PyGILState_Release(
637 gil_state );
638
639 #if PY_MAJOR_VERSION >= 3
640 return( module );
641 #else
642 return;
643 #endif
644
645 on_error:
646 PyGILState_Release(
647 gil_state );
648
649 #if PY_MAJOR_VERSION >= 3
650 return( NULL );
651 #else
652 return;
653 #endif
654 }
655
656