1 /*
2 * Python object definition of the libfsntfs update (or change) journal
3 *
4 * Copyright (C) 2010-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 "pyfsntfs_error.h"
30 #include "pyfsntfs_integer.h"
31 #include "pyfsntfs_libcerror.h"
32 #include "pyfsntfs_libfsntfs.h"
33 #include "pyfsntfs_python.h"
34 #include "pyfsntfs_unused.h"
35 #include "pyfsntfs_usn_change_journal.h"
36
37 PyMethodDef pyfsntfs_usn_change_journal_object_methods[] = {
38
39 /* Functions to access the USN change journal */
40
41 { "get_offset",
42 (PyCFunction) pyfsntfs_usn_change_journal_get_offset,
43 METH_NOARGS,
44 "get_offset() -> Integer\n"
45 "\n"
46 "Returns the current offset within the USN change journal data." },
47
48 { "read_usn_record",
49 (PyCFunction) pyfsntfs_usn_change_journal_read_usn_record,
50 METH_NOARGS,
51 "read_usn_record() -> String\n"
52 "\n"
53 "Reads USN record data." },
54
55 /* Sentinel */
56 { NULL, NULL, 0, NULL }
57 };
58
59 PyGetSetDef pyfsntfs_usn_change_journal_object_get_set_definitions[] = {
60
61 /* Sentinel */
62 { NULL, NULL, NULL, NULL, NULL }
63 };
64
65 PyTypeObject pyfsntfs_usn_change_journal_type_object = {
66 PyVarObject_HEAD_INIT( NULL, 0 )
67
68 /* tp_name */
69 "pyfsntfs.usn_change_journal",
70 /* tp_basicsize */
71 sizeof( pyfsntfs_usn_change_journal_t ),
72 /* tp_itemsize */
73 0,
74 /* tp_dealloc */
75 (destructor) pyfsntfs_usn_change_journal_free,
76 /* tp_print */
77 0,
78 /* tp_getattr */
79 0,
80 /* tp_setattr */
81 0,
82 /* tp_compare */
83 0,
84 /* tp_repr */
85 0,
86 /* tp_as_number */
87 0,
88 /* tp_as_sequence */
89 0,
90 /* tp_as_mapping */
91 0,
92 /* tp_hash */
93 0,
94 /* tp_call */
95 0,
96 /* tp_str */
97 0,
98 /* tp_getattro */
99 0,
100 /* tp_setattro */
101 0,
102 /* tp_as_buffer */
103 0,
104 /* tp_flags */
105 Py_TPFLAGS_DEFAULT,
106 /* tp_doc */
107 "pyfsntfs USN change journal object (wraps libfsntfs_usn_change_journal_t)",
108 /* tp_traverse */
109 0,
110 /* tp_clear */
111 0,
112 /* tp_richcompare */
113 0,
114 /* tp_weaklistoffset */
115 0,
116 /* tp_iter */
117 0,
118 /* tp_iternext */
119 0,
120 /* tp_methods */
121 pyfsntfs_usn_change_journal_object_methods,
122 /* tp_members */
123 0,
124 /* tp_getset */
125 pyfsntfs_usn_change_journal_object_get_set_definitions,
126 /* tp_base */
127 0,
128 /* tp_dict */
129 0,
130 /* tp_descr_get */
131 0,
132 /* tp_descr_set */
133 0,
134 /* tp_dictoffset */
135 0,
136 /* tp_init */
137 (initproc) pyfsntfs_usn_change_journal_init,
138 /* tp_alloc */
139 0,
140 /* tp_new */
141 0,
142 /* tp_free */
143 0,
144 /* tp_is_gc */
145 0,
146 /* tp_bases */
147 NULL,
148 /* tp_mro */
149 NULL,
150 /* tp_cache */
151 NULL,
152 /* tp_subclasses */
153 NULL,
154 /* tp_weaklist */
155 NULL,
156 /* tp_del */
157 0
158 };
159
160 /* Creates a new USN change journal object
161 * Returns a Python object if successful or NULL on error
162 */
pyfsntfs_usn_change_journal_new(libfsntfs_usn_change_journal_t * usn_change_journal,PyObject * parent_object)163 PyObject *pyfsntfs_usn_change_journal_new(
164 libfsntfs_usn_change_journal_t *usn_change_journal,
165 PyObject *parent_object )
166 {
167 pyfsntfs_usn_change_journal_t *pyfsntfs_usn_change_journal = NULL;
168 static char *function = "pyfsntfs_usn_change_journal_new";
169
170 if( usn_change_journal == NULL )
171 {
172 PyErr_Format(
173 PyExc_ValueError,
174 "%s: invalid USN change journal.",
175 function );
176
177 return( NULL );
178 }
179 /* PyObject_New does not invoke tp_init
180 */
181 pyfsntfs_usn_change_journal = PyObject_New(
182 struct pyfsntfs_usn_change_journal,
183 &pyfsntfs_usn_change_journal_type_object );
184
185 if( pyfsntfs_usn_change_journal == NULL )
186 {
187 PyErr_Format(
188 PyExc_MemoryError,
189 "%s: unable to initialize USN change journal.",
190 function );
191
192 goto on_error;
193 }
194 pyfsntfs_usn_change_journal->usn_change_journal = usn_change_journal;
195 pyfsntfs_usn_change_journal->parent_object = parent_object;
196
197 if( pyfsntfs_usn_change_journal->parent_object != NULL )
198 {
199 Py_IncRef(
200 pyfsntfs_usn_change_journal->parent_object );
201 }
202 return( (PyObject *) pyfsntfs_usn_change_journal );
203
204 on_error:
205 if( pyfsntfs_usn_change_journal != NULL )
206 {
207 Py_DecRef(
208 (PyObject *) pyfsntfs_usn_change_journal );
209 }
210 return( NULL );
211 }
212
213 /* Initializes an USN change journal object
214 * Returns 0 if successful or -1 on error
215 */
pyfsntfs_usn_change_journal_init(pyfsntfs_usn_change_journal_t * pyfsntfs_usn_change_journal)216 int pyfsntfs_usn_change_journal_init(
217 pyfsntfs_usn_change_journal_t *pyfsntfs_usn_change_journal )
218 {
219 static char *function = "pyfsntfs_usn_change_journal_init";
220
221 if( pyfsntfs_usn_change_journal == NULL )
222 {
223 PyErr_Format(
224 PyExc_ValueError,
225 "%s: invalid USN change journal.",
226 function );
227
228 return( -1 );
229 }
230 /* Make sure libfsntfs USN change journal is set to NULL
231 */
232 pyfsntfs_usn_change_journal->usn_change_journal = NULL;
233
234 PyErr_Format(
235 PyExc_NotImplementedError,
236 "%s: initialize of USN change journal not supported.",
237 function );
238
239 return( -1 );
240 }
241
242 /* Frees an USN change journal object
243 */
pyfsntfs_usn_change_journal_free(pyfsntfs_usn_change_journal_t * pyfsntfs_usn_change_journal)244 void pyfsntfs_usn_change_journal_free(
245 pyfsntfs_usn_change_journal_t *pyfsntfs_usn_change_journal )
246 {
247 struct _typeobject *ob_type = NULL;
248 libcerror_error_t *error = NULL;
249 static char *function = "pyfsntfs_usn_change_journal_free";
250 int result = 0;
251
252 if( pyfsntfs_usn_change_journal == NULL )
253 {
254 PyErr_Format(
255 PyExc_ValueError,
256 "%s: invalid USN change journal.",
257 function );
258
259 return;
260 }
261 ob_type = Py_TYPE(
262 pyfsntfs_usn_change_journal );
263
264 if( ob_type == NULL )
265 {
266 PyErr_Format(
267 PyExc_ValueError,
268 "%s: missing ob_type.",
269 function );
270
271 return;
272 }
273 if( ob_type->tp_free == NULL )
274 {
275 PyErr_Format(
276 PyExc_ValueError,
277 "%s: invalid ob_type - missing tp_free.",
278 function );
279
280 return;
281 }
282 if( pyfsntfs_usn_change_journal->usn_change_journal != NULL )
283 {
284 Py_BEGIN_ALLOW_THREADS
285
286 result = libfsntfs_usn_change_journal_free(
287 &( pyfsntfs_usn_change_journal->usn_change_journal ),
288 &error );
289
290 Py_END_ALLOW_THREADS
291
292 if( result != 1 )
293 {
294 pyfsntfs_error_raise(
295 error,
296 PyExc_MemoryError,
297 "%s: unable to free libfsntfs USN change journal.",
298 function );
299
300 libcerror_error_free(
301 &error );
302 }
303 }
304 if( pyfsntfs_usn_change_journal->parent_object != NULL )
305 {
306 Py_DecRef(
307 pyfsntfs_usn_change_journal->parent_object );
308 }
309 ob_type->tp_free(
310 (PyObject*) pyfsntfs_usn_change_journal );
311 }
312
313 /* Retrieves the offset
314 * Returns a Python object if successful or NULL on error
315 */
pyfsntfs_usn_change_journal_get_offset(pyfsntfs_usn_change_journal_t * pyfsntfs_usn_change_journal,PyObject * arguments PYFSNTFS_ATTRIBUTE_UNUSED)316 PyObject *pyfsntfs_usn_change_journal_get_offset(
317 pyfsntfs_usn_change_journal_t *pyfsntfs_usn_change_journal,
318 PyObject *arguments PYFSNTFS_ATTRIBUTE_UNUSED )
319 {
320 libcerror_error_t *error = NULL;
321 PyObject *integer_object = NULL;
322 static char *function = "pyfsntfs_usn_change_journal_get_offset";
323 off64_t offset = 0;
324 int result = 0;
325
326 PYFSNTFS_UNREFERENCED_PARAMETER( arguments )
327
328 if( pyfsntfs_usn_change_journal == NULL )
329 {
330 PyErr_Format(
331 PyExc_TypeError,
332 "%s: invalid USN change journal.",
333 function );
334
335 return( NULL );
336 }
337 Py_BEGIN_ALLOW_THREADS
338
339 result = libfsntfs_usn_change_journal_get_offset(
340 pyfsntfs_usn_change_journal->usn_change_journal,
341 &offset,
342 &error );
343
344 Py_END_ALLOW_THREADS
345
346 if( result != 1 )
347 {
348 pyfsntfs_error_raise(
349 error,
350 PyExc_IOError,
351 "%s: unable to retrieve offset.",
352 function );
353
354 libcerror_error_free(
355 &error );
356
357 return( NULL );
358 }
359 integer_object = pyfsntfs_integer_signed_new_from_64bit(
360 (int64_t) offset );
361
362 return( integer_object );
363 }
364
365 /* Reads an USN record
366 * Returns a Python object holding the data if successful or NULL on error
367 */
pyfsntfs_usn_change_journal_read_usn_record(pyfsntfs_usn_change_journal_t * pyfsntfs_usn_change_journal,PyObject * arguments PYFSNTFS_ATTRIBUTE_UNUSED)368 PyObject *pyfsntfs_usn_change_journal_read_usn_record(
369 pyfsntfs_usn_change_journal_t *pyfsntfs_usn_change_journal,
370 PyObject *arguments PYFSNTFS_ATTRIBUTE_UNUSED )
371 {
372 libcerror_error_t *error = NULL;
373 PyObject *string_object = NULL;
374 static char *function = "pyfsntfs_usn_change_journal_read_usn_record";
375 char *usn_record_data = NULL;
376 size_t journal_block_size = 0x1000;
377 ssize_t read_count = 0;
378
379 if( pyfsntfs_usn_change_journal == NULL )
380 {
381 PyErr_Format(
382 PyExc_ValueError,
383 "%s: invalid USN change journal.",
384 function );
385
386 return( NULL );
387 }
388 if( pyfsntfs_usn_change_journal->usn_change_journal == NULL )
389 {
390 PyErr_Format(
391 PyExc_ValueError,
392 "%s: invalid USN change journal - missing libfsntfs USN change journal.",
393 function );
394
395 return( NULL );
396 }
397 /* TODO get journal block size from USN change journal */
398 if( ( journal_block_size == 0 )
399 || ( journal_block_size > (size_t) SSIZE_MAX ) )
400 {
401 PyErr_Format(
402 PyExc_ValueError,
403 "%s: invalid argument read size value exceeds maximum.",
404 function );
405
406 return( NULL );
407 }
408 #if PY_MAJOR_VERSION >= 3
409 string_object = PyBytes_FromStringAndSize(
410 NULL,
411 journal_block_size );
412
413 usn_record_data = PyBytes_AsString(
414 string_object );
415 #else
416 /* Note that a size of 0 is not supported
417 */
418 string_object = PyString_FromStringAndSize(
419 NULL,
420 journal_block_size );
421
422 usn_record_data = PyString_AsString(
423 string_object );
424 #endif
425 Py_BEGIN_ALLOW_THREADS
426
427 read_count = libfsntfs_usn_change_journal_read_usn_record(
428 pyfsntfs_usn_change_journal->usn_change_journal,
429 (uint8_t *) usn_record_data,
430 (size_t) journal_block_size,
431 &error );
432
433 Py_END_ALLOW_THREADS
434
435 if( read_count <= -1 )
436 {
437 pyfsntfs_error_raise(
438 error,
439 PyExc_IOError,
440 "%s: unable to read data.",
441 function );
442
443 libcerror_error_free(
444 &error );
445
446 Py_DecRef(
447 (PyObject *) string_object );
448
449 return( NULL );
450 }
451 /* Need to resize the string here in case journal_block_size was not fully read.
452 */
453 #if PY_MAJOR_VERSION >= 3
454 if( _PyBytes_Resize(
455 &string_object,
456 (Py_ssize_t) read_count ) != 0 )
457 #else
458 if( _PyString_Resize(
459 &string_object,
460 (Py_ssize_t) read_count ) != 0 )
461 #endif
462 {
463 Py_DecRef(
464 (PyObject *) string_object );
465
466 return( NULL );
467 }
468 return( string_object );
469 }
470
471