1 /*
2 * Python object definition of the libolecf stream
3 *
4 * Copyright (C) 2008-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 <types.h>
24
25 #if defined( HAVE_STDLIB_H ) || defined( HAVE_WINAPI )
26 #include <stdlib.h>
27 #endif
28
29 #include "pyolecf_error.h"
30 #include "pyolecf_integer.h"
31 #include "pyolecf_item.h"
32 #include "pyolecf_libcerror.h"
33 #include "pyolecf_libolecf.h"
34 #include "pyolecf_python.h"
35 #include "pyolecf_stream.h"
36 #include "pyolecf_unused.h"
37
38 PyMethodDef pyolecf_stream_object_methods[] = {
39
40 /* Functions to access the stream data */
41
42 { "read_buffer",
43 (PyCFunction) pyolecf_stream_read_buffer,
44 METH_VARARGS | METH_KEYWORDS,
45 "read_buffer(size) -> String\n"
46 "\n"
47 "Reads a buffer of stream data from the file(s)." },
48
49 { "read_buffer_at_offset",
50 (PyCFunction) pyolecf_stream_read_buffer_at_offset,
51 METH_VARARGS | METH_KEYWORDS,
52 "read_buffer_at_offset(size, offset) -> String\n"
53 "\n"
54 "Reads a buffer of stream data at a specific offset from the file(s)." },
55
56 { "seek_offset",
57 (PyCFunction) pyolecf_stream_seek_offset,
58 METH_VARARGS | METH_KEYWORDS,
59 "seek_offset(offset, whence) -> None\n"
60 "\n"
61 "Seeks an offset within the stream data." },
62
63 { "get_offset",
64 (PyCFunction) pyolecf_stream_get_offset,
65 METH_NOARGS,
66 "get_offset() -> Integer\n"
67 "\n"
68 "Retrieves the current offset within the stream data." },
69
70 /* Some Pythonesque aliases */
71
72 { "read",
73 (PyCFunction) pyolecf_stream_read_buffer,
74 METH_VARARGS | METH_KEYWORDS,
75 "read(size) -> String\n"
76 "\n"
77 "Reads a buffer of stream data from the file(s)." },
78
79 { "seek",
80 (PyCFunction) pyolecf_stream_seek_offset,
81 METH_VARARGS | METH_KEYWORDS,
82 "seek(offset, whence) -> None\n"
83 "\n"
84 "Seeks an offset within the stream data." },
85
86 { "tell",
87 (PyCFunction) pyolecf_stream_get_offset,
88 METH_NOARGS,
89 "tell() -> Integer\n"
90 "\n"
91 "Retrieves the current offset within the stream data." },
92
93 /* Functions to access the stream values */
94
95 /* Sentinel */
96 { NULL, NULL, 0, NULL }
97 };
98
99 PyTypeObject pyolecf_stream_type_object = {
100 PyVarObject_HEAD_INIT( NULL, 0 )
101
102 /* tp_name */
103 "pyolecf.stream",
104 /* tp_basicsize */
105 sizeof( pyolecf_item_t ),
106 /* tp_itemsize */
107 0,
108 /* tp_dealloc */
109 0,
110 /* tp_print */
111 0,
112 /* tp_getattr */
113 0,
114 /* tp_setattr */
115 0,
116 /* tp_compare */
117 0,
118 /* tp_repr */
119 0,
120 /* tp_as_number */
121 0,
122 /* tp_as_sequence */
123 0,
124 /* tp_as_mapping */
125 0,
126 /* tp_hash */
127 0,
128 /* tp_call */
129 0,
130 /* tp_str */
131 0,
132 /* tp_getattro */
133 0,
134 /* tp_setattro */
135 0,
136 /* tp_as_buffer */
137 0,
138 /* tp_flags */
139 Py_TPFLAGS_DEFAULT,
140 /* tp_doc */
141 "pyolecf stream object (wraps libolecf_item_t type 0x02)",
142 /* tp_traverse */
143 0,
144 /* tp_clear */
145 0,
146 /* tp_richcompare */
147 0,
148 /* tp_weaklistoffset */
149 0,
150 /* tp_iter */
151 0,
152 /* tp_iternext */
153 0,
154 /* tp_methods */
155 pyolecf_stream_object_methods,
156 /* tp_members */
157 0,
158 /* tp_getset */
159 0,
160 /* tp_base */
161 &pyolecf_item_type_object,
162 /* tp_dict */
163 0,
164 /* tp_descr_get */
165 0,
166 /* tp_descr_set */
167 0,
168 /* tp_dictoffset */
169 0,
170 /* tp_init */
171 0,
172 /* tp_alloc */
173 0,
174 /* tp_new */
175 0,
176 /* tp_free */
177 0,
178 /* tp_is_gc */
179 0,
180 /* tp_bases */
181 NULL,
182 /* tp_mro */
183 NULL,
184 /* tp_cache */
185 NULL,
186 /* tp_subclasses */
187 NULL,
188 /* tp_weaklist */
189 NULL,
190 /* tp_del */
191 0
192 };
193
194 /* Reads (stream) data at the current offset into a buffer
195 * Returns a Python object if successful or NULL on error
196 */
pyolecf_stream_read_buffer(pyolecf_item_t * pyolecf_item,PyObject * arguments,PyObject * keywords)197 PyObject *pyolecf_stream_read_buffer(
198 pyolecf_item_t *pyolecf_item,
199 PyObject *arguments,
200 PyObject *keywords )
201 {
202 libcerror_error_t *error = NULL;
203 PyObject *integer_object = NULL;
204 PyObject *string_object = NULL;
205 static char *function = "pyolecf_stream_read_buffer";
206 static char *keyword_list[] = { "size", NULL };
207 char *buffer = NULL;
208 size64_t read_size = 0;
209 ssize_t read_count = 0;
210 int result = 0;
211
212 if( pyolecf_item == NULL )
213 {
214 PyErr_Format(
215 PyExc_TypeError,
216 "%s: invalid pyolecf item.",
217 function );
218
219 return( NULL );
220 }
221 if( pyolecf_item->item == NULL )
222 {
223 PyErr_Format(
224 PyExc_TypeError,
225 "%s: invalid pyolecf item - missing libolecf item.",
226 function );
227
228 return( NULL );
229 }
230 if( PyArg_ParseTupleAndKeywords(
231 arguments,
232 keywords,
233 "|O",
234 keyword_list,
235 &integer_object ) == 0 )
236 {
237 return( NULL );
238 }
239 if( integer_object == NULL )
240 {
241 result = 0;
242 }
243 else
244 {
245 result = PyObject_IsInstance(
246 integer_object,
247 (PyObject *) &PyLong_Type );
248
249 if( result == -1 )
250 {
251 pyolecf_error_fetch_and_raise(
252 PyExc_RuntimeError,
253 "%s: unable to determine if integer object is of type long.",
254 function );
255
256 return( NULL );
257 }
258 #if PY_MAJOR_VERSION < 3
259 else if( result == 0 )
260 {
261 PyErr_Clear();
262
263 result = PyObject_IsInstance(
264 integer_object,
265 (PyObject *) &PyInt_Type );
266
267 if( result == -1 )
268 {
269 pyolecf_error_fetch_and_raise(
270 PyExc_RuntimeError,
271 "%s: unable to determine if integer object is of type int.",
272 function );
273
274 return( NULL );
275 }
276 }
277 #endif
278 }
279 if( result != 0 )
280 {
281 if( pyolecf_integer_unsigned_copy_to_64bit(
282 integer_object,
283 (uint64_t *) &read_size,
284 &error ) != 1 )
285 {
286 pyolecf_error_raise(
287 error,
288 PyExc_IOError,
289 "%s: unable to convert integer object into read size.",
290 function );
291
292 libcerror_error_free(
293 &error );
294
295 return( NULL );
296 }
297 }
298 else if( ( integer_object == NULL )
299 || ( integer_object == Py_None ) )
300 {
301 Py_BEGIN_ALLOW_THREADS
302
303 result = libolecf_item_get_size(
304 pyolecf_item->item,
305 (uint32_t *) &read_size,
306 &error );
307
308 Py_END_ALLOW_THREADS
309
310 if( result != 1 )
311 {
312 pyolecf_error_raise(
313 error,
314 PyExc_IOError,
315 "%s: unable to retrieve size.",
316 function );
317
318 libcerror_error_free(
319 &error );
320
321 return( NULL );
322 }
323 }
324 else
325 {
326 PyErr_Format(
327 PyExc_TypeError,
328 "%s: unsupported integer object type.",
329 function );
330
331 return( NULL );
332 }
333 if( read_size == 0 )
334 {
335 #if PY_MAJOR_VERSION >= 3
336 string_object = PyBytes_FromString(
337 "" );
338 #else
339 string_object = PyString_FromString(
340 "" );
341 #endif
342 return( string_object );
343 }
344 /* Make sure the data fits into a memory buffer
345 */
346 if( ( read_size > (size64_t) INT_MAX )
347 || ( read_size > (size64_t) SSIZE_MAX ) )
348 {
349 PyErr_Format(
350 PyExc_ValueError,
351 "%s: invalid argument read size value exceeds maximum.",
352 function );
353
354 return( NULL );
355 }
356 #if PY_MAJOR_VERSION >= 3
357 string_object = PyBytes_FromStringAndSize(
358 NULL,
359 read_size );
360
361 buffer = PyBytes_AsString(
362 string_object );
363 #else
364 /* Note that a size of 0 is not supported
365 */
366 string_object = PyString_FromStringAndSize(
367 NULL,
368 read_size );
369
370 buffer = PyString_AsString(
371 string_object );
372 #endif
373 Py_BEGIN_ALLOW_THREADS
374
375 read_count = libolecf_stream_read_buffer(
376 pyolecf_item->item,
377 (uint8_t *) buffer,
378 (size_t) read_size,
379 &error );
380
381 Py_END_ALLOW_THREADS
382
383 if( read_count <= -1 )
384 {
385 pyolecf_error_raise(
386 error,
387 PyExc_IOError,
388 "%s: unable to read data.",
389 function );
390
391 libcerror_error_free(
392 &error );
393
394 Py_DecRef(
395 (PyObject *) string_object );
396
397 return( NULL );
398 }
399 /* Need to resize the string here in case read_size was not fully read.
400 */
401 #if PY_MAJOR_VERSION >= 3
402 if( _PyBytes_Resize(
403 &string_object,
404 (Py_ssize_t) read_count ) != 0 )
405 #else
406 if( _PyString_Resize(
407 &string_object,
408 (Py_ssize_t) read_count ) != 0 )
409 #endif
410 {
411 Py_DecRef(
412 (PyObject *) string_object );
413
414 return( NULL );
415 }
416 return( string_object );
417 }
418
419 /* Reads (stream) data at a specific offset
420 * Returns a Python object if successful or NULL on error
421 */
pyolecf_stream_read_buffer_at_offset(pyolecf_item_t * pyolecf_item,PyObject * arguments,PyObject * keywords)422 PyObject *pyolecf_stream_read_buffer_at_offset(
423 pyolecf_item_t *pyolecf_item,
424 PyObject *arguments,
425 PyObject *keywords )
426 {
427 libcerror_error_t *error = NULL;
428 PyObject *integer_object = NULL;
429 PyObject *string_object = NULL;
430 static char *function = "pyolecf_stream_read_buffer_at_offset";
431 static char *keyword_list[] = { "size", "offset", NULL };
432 char *buffer = NULL;
433 off64_t read_offset = 0;
434 size64_t read_size = 0;
435 ssize_t read_count = 0;
436 int result = 0;
437
438 if( pyolecf_item == NULL )
439 {
440 PyErr_Format(
441 PyExc_TypeError,
442 "%s: invalid pyolecf item.",
443 function );
444
445 return( NULL );
446 }
447 if( pyolecf_item->item == NULL )
448 {
449 PyErr_Format(
450 PyExc_TypeError,
451 "%s: invalid pyolecf item - missing libolecf stream.",
452 function );
453
454 return( NULL );
455 }
456 if( PyArg_ParseTupleAndKeywords(
457 arguments,
458 keywords,
459 "OL",
460 keyword_list,
461 &integer_object,
462 &read_offset ) == 0 )
463 {
464 return( NULL );
465 }
466 result = PyObject_IsInstance(
467 integer_object,
468 (PyObject *) &PyLong_Type );
469
470 if( result == -1 )
471 {
472 pyolecf_error_fetch_and_raise(
473 PyExc_RuntimeError,
474 "%s: unable to determine if integer object is of type long.",
475 function );
476
477 return( NULL );
478 }
479 #if PY_MAJOR_VERSION < 3
480 else if( result == 0 )
481 {
482 PyErr_Clear();
483
484 result = PyObject_IsInstance(
485 integer_object,
486 (PyObject *) &PyInt_Type );
487
488 if( result == -1 )
489 {
490 pyolecf_error_fetch_and_raise(
491 PyExc_RuntimeError,
492 "%s: unable to determine if integer object is of type int.",
493 function );
494
495 return( NULL );
496 }
497 }
498 #endif
499 if( result != 0 )
500 {
501 if( pyolecf_integer_unsigned_copy_to_64bit(
502 integer_object,
503 (uint64_t *) &read_size,
504 &error ) != 1 )
505 {
506 pyolecf_error_raise(
507 error,
508 PyExc_IOError,
509 "%s: unable to convert integer object into read size.",
510 function );
511
512 libcerror_error_free(
513 &error );
514
515 return( NULL );
516 }
517 }
518 else
519 {
520 PyErr_Format(
521 PyExc_TypeError,
522 "%s: unsupported integer object type.",
523 function );
524
525 return( NULL );
526 }
527 if( read_size == 0 )
528 {
529 #if PY_MAJOR_VERSION >= 3
530 string_object = PyBytes_FromString(
531 "" );
532 #else
533 string_object = PyString_FromString(
534 "" );
535 #endif
536 return( string_object );
537 }
538 /* Make sure the data fits into a memory buffer
539 */
540 if( ( read_size > (size64_t) INT_MAX )
541 || ( read_size > (size64_t) SSIZE_MAX ) )
542 {
543 PyErr_Format(
544 PyExc_ValueError,
545 "%s: invalid argument read size value exceeds maximum.",
546 function );
547
548 return( NULL );
549 }
550 #if PY_MAJOR_VERSION >= 3
551 string_object = PyBytes_FromStringAndSize(
552 NULL,
553 read_size );
554
555 buffer = PyBytes_AsString(
556 string_object );
557 #else
558 /* Note that a size of 0 is not supported
559 */
560 string_object = PyString_FromStringAndSize(
561 NULL,
562 read_size );
563
564 buffer = PyString_AsString(
565 string_object );
566 #endif
567 Py_BEGIN_ALLOW_THREADS
568
569 read_count = libolecf_stream_read_buffer_at_offset(
570 pyolecf_item->item,
571 (uint8_t *) buffer,
572 (size_t) read_size,
573 (off64_t) read_offset,
574 &error );
575
576 Py_END_ALLOW_THREADS
577
578 if( read_count <= -1 )
579 {
580 pyolecf_error_raise(
581 error,
582 PyExc_IOError,
583 "%s: unable to read data.",
584 function );
585
586 libcerror_error_free(
587 &error );
588
589 Py_DecRef(
590 (PyObject *) string_object );
591
592 return( NULL );
593 }
594 /* Need to resize the string here in case read_size was not fully read.
595 */
596 #if PY_MAJOR_VERSION >= 3
597 if( _PyBytes_Resize(
598 &string_object,
599 (Py_ssize_t) read_count ) != 0 )
600 #else
601 if( _PyString_Resize(
602 &string_object,
603 (Py_ssize_t) read_count ) != 0 )
604 #endif
605 {
606 Py_DecRef(
607 (PyObject *) string_object );
608
609 return( NULL );
610 }
611 return( string_object );
612 }
613
614 /* Seeks a certain offset in the (stream) data
615 * Returns a Python object if successful or NULL on error
616 */
pyolecf_stream_seek_offset(pyolecf_item_t * pyolecf_item,PyObject * arguments,PyObject * keywords)617 PyObject *pyolecf_stream_seek_offset(
618 pyolecf_item_t *pyolecf_item,
619 PyObject *arguments,
620 PyObject *keywords )
621 {
622 libcerror_error_t *error = NULL;
623 static char *function = "pyolecf_stream_seek_offset";
624 static char *keyword_list[] = { "offset", "whence", NULL };
625 off64_t offset = 0;
626 int whence = 0;
627
628 if( pyolecf_item == NULL )
629 {
630 PyErr_Format(
631 PyExc_TypeError,
632 "%s: invalid item.",
633 function );
634
635 return( NULL );
636 }
637 if( PyArg_ParseTupleAndKeywords(
638 arguments,
639 keywords,
640 "L|i",
641 keyword_list,
642 &offset,
643 &whence ) == 0 )
644 {
645 return( NULL );
646 }
647 Py_BEGIN_ALLOW_THREADS
648
649 offset = libolecf_stream_seek_offset(
650 pyolecf_item->item,
651 offset,
652 whence,
653 &error );
654
655 Py_END_ALLOW_THREADS
656
657 if( offset == -1 )
658 {
659 pyolecf_error_raise(
660 error,
661 PyExc_IOError,
662 "%s: unable to seek offset.",
663 function );
664
665 libcerror_error_free(
666 &error );
667
668 return( NULL );
669 }
670 Py_IncRef(
671 Py_None );
672
673 return( Py_None );
674 }
675
676 /* Retrieves the current offset in the (stream) data
677 * Returns a Python object if successful or NULL on error
678 */
pyolecf_stream_get_offset(pyolecf_item_t * pyolecf_item,PyObject * arguments PYOLECF_ATTRIBUTE_UNUSED)679 PyObject *pyolecf_stream_get_offset(
680 pyolecf_item_t *pyolecf_item,
681 PyObject *arguments PYOLECF_ATTRIBUTE_UNUSED )
682 {
683 libcerror_error_t *error = NULL;
684 PyObject *integer_object = NULL;
685 static char *function = "pyolecf_stream_get_offset";
686 off64_t current_offset = 0;
687 int result = 0;
688
689 PYOLECF_UNREFERENCED_PARAMETER( arguments )
690
691 if( pyolecf_item == NULL )
692 {
693 PyErr_Format(
694 PyExc_TypeError,
695 "%s: invalid item.",
696 function );
697
698 return( NULL );
699 }
700 Py_BEGIN_ALLOW_THREADS
701
702 result = libolecf_stream_get_offset(
703 pyolecf_item->item,
704 ¤t_offset,
705 &error );
706
707 Py_END_ALLOW_THREADS
708
709 if( result != 1 )
710 {
711 pyolecf_error_raise(
712 error,
713 PyExc_IOError,
714 "%s: unable to retrieve offset.",
715 function );
716
717 libcerror_error_free(
718 &error );
719
720 return( NULL );
721 }
722 integer_object = pyolecf_integer_signed_new_from_64bit(
723 (int64_t) current_offset );
724
725 return( integer_object );
726 }
727
728