/src/sleuthkit/tsk/fs/ext2fs_journal.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | @@@ UNALLOC only if seq is less - alloc can be less than block if it wrapped around ... |
3 | | ** ext2fs_journal |
4 | | ** The Sleuth Kit |
5 | | ** |
6 | | ** Journaling code for TSK_FS_INFO_TYPE_EXT_3 image |
7 | | ** |
8 | | ** Brian Carrier [carrier <at> sleuthkit [dot] org] |
9 | | ** Copyright (c) 2006-2011 Brian Carrier, Basis Technology. All Rights reserved |
10 | | ** Copyright (c) 2004-2005 Brian Carrier. All rights reserved |
11 | | ** |
12 | | ** |
13 | | ** This software is distributed under the Common Public License 1.0 |
14 | | ** |
15 | | */ |
16 | | |
17 | | /** \file ext2fs_journal.c |
18 | | * Contains the internal TSK Ext3 journal walking code. |
19 | | */ |
20 | | |
21 | | #include "tsk_fs_i.h" |
22 | | #include "tsk_ext2fs.h" |
23 | | |
24 | | |
25 | | |
26 | | /* Everything in the journal is in big endian */ |
27 | | #define big_tsk_getu32(x) \ |
28 | 0 | (uint32_t)((((uint8_t *)x)[3] << 0) + \ |
29 | 0 | (((uint8_t *)x)[2] << 8) + \ |
30 | 0 | (((uint8_t *)x)[1] << 16) + \ |
31 | 0 | (((uint8_t *)x)[0] << 24) ) |
32 | | |
33 | | |
34 | | /* |
35 | | * |
36 | | */ |
37 | | |
38 | | static TSK_WALK_RET_ENUM |
39 | | load_sb_action( |
40 | | TSK_FS_FILE * fs_file, |
41 | | [[maybe_unused]] TSK_OFF_T a_off, |
42 | | [[maybe_unused]] TSK_DADDR_T addr, |
43 | | char *buf, |
44 | | size_t size, |
45 | | [[maybe_unused]] TSK_FS_BLOCK_FLAG_ENUM flags, |
46 | | [[maybe_unused]] void *ptr) |
47 | 0 | { |
48 | 0 | TSK_FS_INFO *fs = fs_file->fs_info; |
49 | 0 | ext2fs_journ_sb *sb; |
50 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
51 | 0 | EXT2FS_JINFO *jinfo = ext2fs->jinfo; |
52 | |
|
53 | 0 | if (size < 1024) { |
54 | 0 | tsk_error_reset(); |
55 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
56 | 0 | tsk_error_set_errstr |
57 | 0 | ("FS block size is less than 1024, not supported in journal yet"); |
58 | 0 | return TSK_WALK_ERROR; |
59 | 0 | } |
60 | | |
61 | 0 | sb = (ext2fs_journ_sb *) buf; |
62 | |
|
63 | 0 | if (big_tsk_getu32(sb->magic) != EXT2_JMAGIC) { |
64 | 0 | tsk_error_reset(); |
65 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
66 | 0 | tsk_error_set_errstr("Journal inode %" PRIuINUM |
67 | 0 | " does not have a valid magic value: %" PRIx32, |
68 | 0 | jinfo->j_inum, big_tsk_getu32(sb->magic)); |
69 | 0 | return TSK_WALK_ERROR; |
70 | 0 | } |
71 | | |
72 | 0 | jinfo->bsize = big_tsk_getu32(sb->bsize); |
73 | 0 | jinfo->first_block = big_tsk_getu32(sb->first_blk); |
74 | 0 | jinfo->last_block = big_tsk_getu32(sb->num_blk) - 1; |
75 | 0 | jinfo->start_blk = big_tsk_getu32(sb->start_blk); |
76 | 0 | jinfo->start_seq = big_tsk_getu32(sb->start_seq); |
77 | |
|
78 | 0 | return TSK_WALK_STOP; |
79 | 0 | } |
80 | | |
81 | | /* Place journal data in *fs |
82 | | * |
83 | | * Return 0 on success and 1 on error |
84 | | * */ |
85 | | uint8_t |
86 | | ext2fs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum) |
87 | 0 | { |
88 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
89 | 0 | EXT2FS_JINFO *jinfo; |
90 | | |
91 | | // clean up any error messages that are lying around |
92 | 0 | tsk_error_reset(); |
93 | |
|
94 | 0 | if (!fs) { |
95 | 0 | tsk_error_reset(); |
96 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
97 | 0 | tsk_error_set_errstr("ext2fs_jopen: fs is null"); |
98 | 0 | return 1; |
99 | 0 | } |
100 | | |
101 | 0 | ext2fs->jinfo = jinfo = |
102 | 0 | (EXT2FS_JINFO *) tsk_malloc(sizeof(EXT2FS_JINFO)); |
103 | 0 | if (jinfo == NULL) { |
104 | 0 | return 1; |
105 | 0 | } |
106 | 0 | jinfo->j_inum = inum; |
107 | |
|
108 | 0 | jinfo->fs_file = tsk_fs_file_open_meta(fs, NULL, inum); |
109 | 0 | if (!jinfo->fs_file) { |
110 | 0 | free(jinfo); |
111 | 0 | return 1; |
112 | | // error("error finding journal inode %" PRIu32, inum); |
113 | 0 | } |
114 | | |
115 | 0 | if (tsk_fs_file_walk(jinfo->fs_file, TSK_FS_FILE_WALK_FLAG_NONE, load_sb_action, NULL)) { |
116 | 0 | tsk_error_reset(); |
117 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
118 | 0 | tsk_error_set_errstr("Error loading ext3 journal"); |
119 | 0 | tsk_fs_file_close(jinfo->fs_file); |
120 | 0 | free(jinfo); |
121 | 0 | return 1; |
122 | 0 | } |
123 | | |
124 | 0 | if (tsk_verbose) |
125 | 0 | tsk_fprintf(stderr, |
126 | 0 | "journal opened at inode %" PRIuINUM " bsize: %" PRIu32 |
127 | 0 | " First JBlk: %" PRIuDADDR " Last JBlk: %" PRIuDADDR "\n", |
128 | 0 | inum, jinfo->bsize, jinfo->first_block, jinfo->last_block); |
129 | |
|
130 | 0 | return 0; |
131 | 0 | } |
132 | | |
133 | | |
134 | | /* Limitations: does not use the action or any flags |
135 | | * |
136 | | * return 0 on success and 1 on error |
137 | | * */ |
138 | | uint8_t |
139 | | ext2fs_jentry_walk( |
140 | | TSK_FS_INFO * fs, |
141 | | [[maybe_unused]] int flags, |
142 | | [[maybe_unused]] TSK_FS_JENTRY_WALK_CB action, |
143 | | [[maybe_unused]] void *ptr) |
144 | 0 | { |
145 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
146 | 0 | EXT2FS_JINFO *jinfo = ext2fs->jinfo; |
147 | 0 | char *journ; |
148 | 0 | TSK_FS_LOAD_FILE buf1; |
149 | 0 | TSK_DADDR_T i; |
150 | | // int b_desc_seen = 0; |
151 | 0 | ext2fs_journ_sb *journ_sb = NULL; |
152 | 0 | ext4fs_journ_commit_head *commit_head; |
153 | | |
154 | | // clean up any error messages that are lying around |
155 | 0 | tsk_error_reset(); |
156 | | |
157 | |
|
158 | 0 | if ((jinfo == NULL) || (jinfo->fs_file == NULL) |
159 | 0 | || (jinfo->fs_file->meta == NULL)) { |
160 | 0 | tsk_error_reset(); |
161 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
162 | 0 | tsk_error_set_errstr("ext2fs_jentry_walk: journal is not open"); |
163 | 0 | return 1; |
164 | 0 | } |
165 | | |
166 | 0 | if ((TSK_DADDR_T)jinfo->fs_file->meta->size != |
167 | 0 | (jinfo->last_block + 1) * jinfo->bsize) { |
168 | 0 | tsk_error_reset(); |
169 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
170 | 0 | tsk_error_set_errstr |
171 | 0 | ("ext2fs_jentry_walk: journal file size is different from \nsize reported in journal super block"); |
172 | 0 | return 1; |
173 | 0 | } |
174 | | |
175 | | /* Load the journal into a buffer */ |
176 | 0 | buf1.left = buf1.total = (size_t) jinfo->fs_file->meta->size; |
177 | 0 | journ = buf1.cur = buf1.base = (char*) tsk_malloc(buf1.left); |
178 | 0 | if (journ == NULL) { |
179 | 0 | return 1; |
180 | 0 | } |
181 | | |
182 | 0 | if (tsk_fs_file_walk(jinfo->fs_file, TSK_FS_FILE_WALK_FLAG_NONE, |
183 | 0 | tsk_fs_load_file_action, (void *) &buf1)) { |
184 | 0 | free(journ); |
185 | 0 | return 1; |
186 | 0 | } |
187 | | |
188 | 0 | if (buf1.left > 0) { |
189 | 0 | tsk_error_reset(); |
190 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
191 | 0 | tsk_error_set_errstr |
192 | 0 | ("ext2fs_jentry_walk: Buffer not fully copied"); |
193 | 0 | free(journ); |
194 | 0 | return 1; |
195 | 0 | } |
196 | | |
197 | | |
198 | | /* Process the journal |
199 | | * Cycle through each block |
200 | | */ |
201 | 0 | tsk_printf("JBlk\tDescription\n"); |
202 | | |
203 | | /* Note that 'i' is incremented when we find a descriptor block and |
204 | | * process its contents. */ |
205 | 0 | for (i = 0; i < jinfo->last_block; i++) { |
206 | 0 | ext2fs_journ_head *head; |
207 | | |
208 | | |
209 | | /* if there is no magic, then it is a normal block |
210 | | * These should be accounted for when we see its corresponding |
211 | | * descriptor. We get the 'unknown' when its desc has |
212 | | * been reused, it is in the next batch to be overwritten, |
213 | | * or if it has not been used before |
214 | | */ |
215 | 0 | head = (ext2fs_journ_head *) & journ[i * jinfo->bsize]; |
216 | 0 | if (big_tsk_getu32(head->magic) != EXT2_JMAGIC) { |
217 | 0 | if (i < jinfo->first_block) { |
218 | 0 | tsk_printf("%" PRIuDADDR ":\tUnused\n", i); |
219 | 0 | } |
220 | | |
221 | | #if 0 |
222 | | /* For now, we ignore the case of the iitial entries before a descriptor, it is too hard ... */ |
223 | | |
224 | | else if (b_desc_seen == 0) { |
225 | | ext2fs_journ_head *head2 = NULL; |
226 | | TSK_DADDR_T a; |
227 | | int next_head = 0, next_seq = 0; |
228 | | ext2fs_journ_dentry *dentry; |
229 | | |
230 | | /* This occurs when the log cycled around |
231 | | * We need to find out where the descriptor is |
232 | | * and where we need to end */ |
233 | | b_desc_seen = 1; |
234 | | |
235 | | for (a = i; a < jinfo->last_block; a++) { |
236 | | head2 = |
237 | | (ext2fs_journ_head *) & journ[a * jinfo->bsize]; |
238 | | if ((big_tsk_getu32(head2->magic) == EXT2_JMAGIC)) { |
239 | | next_head = a; |
240 | | next_seq = big_tsk_getu32(head2->entry_seq); |
241 | | break; |
242 | | } |
243 | | |
244 | | } |
245 | | if (next_head == 0) { |
246 | | tsk_printf("%" PRIuDADDR ":\tFS Block Unknown\n", i); |
247 | | } |
248 | | |
249 | | /* Find the last descr in the journ */ |
250 | | for (a = jinfo->last_block; a > i; a--) { |
251 | | head2 = |
252 | | (ext2fs_journ_head *) & journ[a * jinfo->bsize]; |
253 | | if ((big_tsk_getu32(head2->magic) == EXT2_JMAGIC) |
254 | | && (big_tsk_getu32(head2->entry_type) == |
255 | | EXT2_J_ETYPE_DESC) |
256 | | && (next_seq == big_tsk_getu32(head2->entry_seq))) { |
257 | | break; |
258 | | |
259 | | // @@@@ We should abort if we reach a commit before descriptor |
260 | | |
261 | | } |
262 | | } |
263 | | |
264 | | /* We did not find a descriptor in the journ! |
265 | | * print unknown for the rest of the journ |
266 | | */ |
267 | | if (a == i) { |
268 | | tsk_printf("%" PRIuDADDR ":\tFS Block Unknown\n", i); |
269 | | continue; |
270 | | } |
271 | | |
272 | | |
273 | | dentry = |
274 | | (ext2fs_journ_dentry *) ((uintptr_t) head2 + |
275 | | sizeof(ext2fs_journ_head));; |
276 | | |
277 | | |
278 | | /* Cycle through the descriptor entries */ |
279 | | while ((uintptr_t) dentry <= |
280 | | ((uintptr_t) head2 + jinfo->bsize - |
281 | | sizeof(ext2fs_journ_head))) { |
282 | | |
283 | | |
284 | | /* Only start to look after the index in the desc has looped */ |
285 | | if (++a <= jinfo->last_block) { |
286 | | ext2fs_journ_head *head3; |
287 | | |
288 | | /* Look at the block that this entry refers to */ |
289 | | head3 = |
290 | | (ext2fs_journ_head *) & journ[i * |
291 | | jinfo->bsize]; |
292 | | if ((big_tsk_getu32(head3->magic) == EXT2_JMAGIC)) { |
293 | | i--; |
294 | | break; |
295 | | } |
296 | | |
297 | | /* If it doesn't have the magic, then it is a |
298 | | * journal entry and we print the FS info */ |
299 | | tsk_printf("%" PRIuDADDR ":\tFS Block %" PRIu32 |
300 | | "\n", i, big_tsk_getu32(dentry->fs_blk)); |
301 | | |
302 | | /* Our counter is over the end of the journ */ |
303 | | if (++i > jinfo->last_block) |
304 | | break; |
305 | | |
306 | | } |
307 | | |
308 | | /* Increment to the next */ |
309 | | if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_LAST) |
310 | | break; |
311 | | |
312 | | /* If the SAMEID value is set, then we advance by the size of the entry, otherwise add 16 for the ID */ |
313 | | else if (big_tsk_getu32(dentry->flag) & |
314 | | EXT2_J_DENTRY_SAMEID) |
315 | | dentry = |
316 | | (ext2fs_journ_dentry *) ((uintptr_t) dentry + |
317 | | sizeof(ext2fs_journ_dentry)); |
318 | | |
319 | | else |
320 | | dentry = |
321 | | (ext2fs_journ_dentry *) ((uintptr_t) dentry + |
322 | | sizeof(ext2fs_journ_dentry) |
323 | | + 16); |
324 | | |
325 | | } |
326 | | } |
327 | | #endif |
328 | 0 | else { |
329 | 0 | tsk_printf("%" PRIuDADDR |
330 | 0 | ":\tUnallocated FS Block Unknown\n", i); |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | /* The super block */ |
335 | 0 | else if ((big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_SB1) || |
336 | 0 | (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_SB2)) { |
337 | 0 | tsk_printf("%" PRIuDADDR ":\tSuperblock (seq: %" PRIu32 ")\n", |
338 | 0 | i, big_tsk_getu32(head->entry_seq)); |
339 | 0 | journ_sb = (ext2fs_journ_sb *)head; |
340 | 0 | tsk_printf("sb version: %d\n", |
341 | 0 | big_tsk_getu32(head->entry_type)); |
342 | 0 | tsk_printf("sb version: %d\n", |
343 | 0 | big_tsk_getu32(journ_sb->entrytype)); |
344 | 0 | tsk_printf("sb feature_compat flags 0x%08X\n", |
345 | 0 | big_tsk_getu32(journ_sb->feature_compat)); |
346 | 0 | if (big_tsk_getu32(journ_sb-> |
347 | 0 | feature_compat) & JBD2_FEATURE_COMPAT_CHECKSUM) |
348 | 0 | tsk_printf("\tJOURNAL_CHECKSUMS\n"); |
349 | 0 | tsk_printf("sb feature_incompat flags 0x%08X\n", |
350 | 0 | big_tsk_getu32(journ_sb->feature_incompat)); |
351 | 0 | if (big_tsk_getu32(journ_sb-> |
352 | 0 | feature_incompat) & JBD2_FEATURE_INCOMPAT_REVOKE) |
353 | 0 | tsk_printf("\tJOURNAL_REVOKE\n"); |
354 | 0 | if (big_tsk_getu32(journ_sb-> |
355 | 0 | feature_incompat) & JBD2_FEATURE_INCOMPAT_64BIT) |
356 | 0 | tsk_printf("\tJOURNAL_64BIT\n"); |
357 | 0 | if (big_tsk_getu32(journ_sb-> |
358 | 0 | feature_incompat) & JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT) |
359 | 0 | tsk_printf("\tJOURNAL_ASYNC_COMMIT\n"); |
360 | 0 | tsk_printf("sb feature_ro_incompat flags 0x%08X\n", |
361 | 0 | big_tsk_getu32(journ_sb->feature_ro_incompat)); |
362 | 0 | } |
363 | | |
364 | | /* Revoke Block */ |
365 | 0 | else if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_REV) { |
366 | 0 | tsk_printf("%" PRIuDADDR ":\t%sRevoke Block (seq: %" PRIu32 |
367 | 0 | ")\n", i, ((i < jinfo->start_blk) |
368 | 0 | || (big_tsk_getu32(head->entry_seq) < |
369 | 0 | jinfo->start_seq)) ? "Unallocated " : "Allocated ", |
370 | 0 | big_tsk_getu32(head->entry_seq)); |
371 | 0 | } |
372 | | |
373 | | /* The commit is the end of the entries */ |
374 | 0 | else if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_COM) { |
375 | 0 | tsk_printf("%" PRIuDADDR ":\t%sCommit Block (seq: %" PRIu32, i, |
376 | 0 | ((i < jinfo->start_blk) |
377 | 0 | || (big_tsk_getu32(head->entry_seq) < |
378 | 0 | jinfo->start_seq)) ? "Unallocated " : "Allocated ", |
379 | 0 | big_tsk_getu32(head->entry_seq)); |
380 | 0 | commit_head = (ext4fs_journ_commit_head *)head; |
381 | | //tsk_printf("commit seq %" PRIu32 "\n", big_tsk_getu32(commit_head->c_header.entry_seq)); |
382 | 0 | if (big_tsk_getu32(journ_sb-> |
383 | 0 | feature_compat) & JBD2_FEATURE_COMPAT_CHECKSUM) { |
384 | 0 | int chksum_type = commit_head->chksum_type; |
385 | 0 | if (chksum_type) { |
386 | 0 | tsk_printf(", checksum_type: %d", |
387 | 0 | commit_head->chksum_type); |
388 | 0 | switch (commit_head->chksum_type) { |
389 | 0 | case JBD2_CRC32_CHKSUM: |
390 | 0 | tsk_printf("-CRC32"); |
391 | 0 | break; |
392 | 0 | case JBD2_MD5_CHKSUM: |
393 | 0 | tsk_printf("-MD5"); |
394 | 0 | break; |
395 | 0 | case JBD2_SHA1_CHKSUM: |
396 | 0 | tsk_printf("-SHA1"); |
397 | 0 | break; |
398 | 0 | default: |
399 | 0 | tsk_printf("-UNKOWN"); |
400 | 0 | break; |
401 | 0 | } |
402 | 0 | tsk_printf(", checksum_size: %d", |
403 | 0 | commit_head->chksum_size); |
404 | 0 | tsk_printf(", chksum: 0x%08X", |
405 | 0 | big_tsk_getu32(commit_head->chksum)); |
406 | 0 | } |
407 | 0 | } |
408 | 0 | tsk_printf(", sec: %llu.%u", tsk_getu64(TSK_BIG_ENDIAN, |
409 | 0 | commit_head->commit_sec), |
410 | 0 | NSEC_PER_SEC / 10 * tsk_getu32(TSK_BIG_ENDIAN, |
411 | 0 | commit_head->commit_nsec)); |
412 | 0 | tsk_printf(")\n"); |
413 | 0 | } |
414 | | |
415 | | /* The descriptor describes the FS blocks that follow it */ |
416 | 0 | else if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_DESC) { |
417 | 0 | ext2fs_journ_dentry *dentry; |
418 | 0 | int unalloc = 0; |
419 | | |
420 | | // b_desc_seen = 1; |
421 | | |
422 | | |
423 | | /* Is this an unallocated journ block or sequence */ |
424 | 0 | if ((i < jinfo->start_blk) || |
425 | 0 | (big_tsk_getu32(head->entry_seq) < jinfo->start_seq)) |
426 | 0 | unalloc = 1; |
427 | |
|
428 | 0 | tsk_printf("%" PRIuDADDR ":\t%sDescriptor Block (seq: %" PRIu32 |
429 | 0 | ")\n", i, (unalloc) ? "Unallocated " : "Allocated ", |
430 | 0 | big_tsk_getu32(head->entry_seq)); |
431 | |
|
432 | 0 | dentry = |
433 | 0 | (ext2fs_journ_dentry *) ((uintptr_t) head + |
434 | 0 | sizeof(ext2fs_journ_head));; |
435 | | |
436 | | /* Cycle through the descriptor entries to account for the journal blocks */ |
437 | 0 | while ((uintptr_t) dentry <= |
438 | 0 | ((uintptr_t) head + jinfo->bsize - |
439 | 0 | sizeof(ext2fs_journ_head))) { |
440 | 0 | ext2fs_journ_head *head2; |
441 | | |
442 | | |
443 | | /* Our counter is over the end of the journ */ |
444 | 0 | if (++i > jinfo->last_block) |
445 | 0 | break; |
446 | | |
447 | | |
448 | | /* Look at the block that this entry refers to */ |
449 | 0 | head2 = (ext2fs_journ_head *) & journ[i * jinfo->bsize]; |
450 | 0 | if ((big_tsk_getu32(head2->magic) == EXT2_JMAGIC) && |
451 | 0 | (big_tsk_getu32(head2->entry_seq) >= |
452 | 0 | big_tsk_getu32(head->entry_seq))) { |
453 | 0 | i--; |
454 | 0 | break; |
455 | 0 | } |
456 | | |
457 | | /* If it doesn't have the magic, then it is a |
458 | | * journal entry and we print the FS info */ |
459 | 0 | tsk_printf("%" PRIuDADDR ":\t%sFS Block %" PRIu32 "\n", i, |
460 | 0 | (unalloc) ? "Unallocated " : "Allocated ", |
461 | 0 | big_tsk_getu32(dentry->fs_blk)); |
462 | | |
463 | | /* Increment to the next */ |
464 | 0 | if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_LAST) |
465 | 0 | break; |
466 | | |
467 | | /* If the SAMEID value is set, then we advance by the size of the entry, otherwise add 16 for the ID */ |
468 | 0 | else if (big_tsk_getu32(dentry->flag) & |
469 | 0 | EXT2_J_DENTRY_SAMEID) |
470 | 0 | dentry = |
471 | 0 | (ext2fs_journ_dentry *) ((uintptr_t) dentry + |
472 | 0 | sizeof(ext2fs_journ_dentry)); |
473 | | |
474 | 0 | else |
475 | 0 | dentry = |
476 | 0 | (ext2fs_journ_dentry *) ((uintptr_t) dentry + |
477 | 0 | sizeof(ext2fs_journ_dentry) + 16); |
478 | 0 | } |
479 | 0 | } |
480 | 0 | } |
481 | | |
482 | 0 | free(journ); |
483 | 0 | return 0; |
484 | 0 | } |
485 | | |
486 | | |
487 | | |
488 | | |
489 | | |
490 | | /* |
491 | | * Limitations for 1st version: start must equal end and action is ignored |
492 | | * |
493 | | * Return 0 on success and 1 on error |
494 | | */ |
495 | | uint8_t |
496 | | ext2fs_jblk_walk( |
497 | | TSK_FS_INFO * fs, |
498 | | TSK_DADDR_T start, |
499 | | TSK_DADDR_T end, |
500 | | [[maybe_unused]] int flags, |
501 | | [[maybe_unused]] TSK_FS_JBLK_WALK_CB action, |
502 | | [[maybe_unused]] void *ptr) |
503 | 0 | { |
504 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
505 | 0 | EXT2FS_JINFO *jinfo = ext2fs->jinfo; |
506 | 0 | uint8_t *journ; |
507 | 0 | TSK_FS_LOAD_FILE buf1; |
508 | 0 | ext2fs_journ_head *head; |
509 | | |
510 | | // clean up any error messages that are lying around |
511 | 0 | tsk_error_reset(); |
512 | |
|
513 | 0 | if ((jinfo == NULL) || (jinfo->fs_file == NULL) |
514 | 0 | || (jinfo->fs_file->meta == NULL)) { |
515 | 0 | tsk_error_reset(); |
516 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
517 | 0 | tsk_error_set_errstr("ext2fs_jblk_walk: journal is not open"); |
518 | 0 | return 1; |
519 | 0 | } |
520 | | |
521 | 0 | if (jinfo->last_block < end) { |
522 | 0 | tsk_error_reset(); |
523 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
524 | 0 | tsk_error_set_errstr("ext2fs_jblk_walk: end is too large "); |
525 | 0 | return 1; |
526 | 0 | } |
527 | | |
528 | 0 | if (start != end) { |
529 | 0 | tsk_error_reset(); |
530 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
531 | 0 | tsk_error_set_errstr |
532 | 0 | ("ext2fs_blk_walk: only start == end is currently supported"); |
533 | 0 | return 1; |
534 | 0 | } |
535 | | |
536 | 0 | if ((TSK_DADDR_T)jinfo->fs_file->meta->size != |
537 | 0 | (jinfo->last_block + 1) * jinfo->bsize) { |
538 | 0 | tsk_error_reset(); |
539 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
540 | 0 | tsk_error_set_errstr |
541 | 0 | ("ext2fs_jblk_walk: journal file size is different from size reported in journal super block"); |
542 | 0 | return 1; |
543 | 0 | } |
544 | | |
545 | | |
546 | | /* Load into buffer and then process it |
547 | | * Only get the minimum needed |
548 | | */ |
549 | 0 | buf1.left = buf1.total = (size_t) ((end + 1) * jinfo->bsize); |
550 | 0 | buf1.cur = buf1.base = (char*) tsk_malloc(buf1.left); |
551 | 0 | journ = (uint8_t*) buf1.cur; |
552 | 0 | if (journ == NULL) { |
553 | 0 | return 1; |
554 | 0 | } |
555 | | |
556 | 0 | if (tsk_fs_file_walk(jinfo->fs_file, TSK_FS_FILE_WALK_FLAG_NONE, |
557 | 0 | tsk_fs_load_file_action, (void *) &buf1)) { |
558 | 0 | free(journ); |
559 | 0 | return 1; |
560 | 0 | } |
561 | | |
562 | 0 | if (buf1.left > 0) { |
563 | 0 | tsk_error_reset(); |
564 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
565 | 0 | tsk_error_set_errstr("ext2fs_jblk_walk: Buffer not fully copied"); |
566 | 0 | free(journ); |
567 | 0 | return 1; |
568 | 0 | } |
569 | | |
570 | 0 | head = (ext2fs_journ_head *) & journ[end * jinfo->bsize]; |
571 | | |
572 | | |
573 | | /* Check if our target block is a journal data structure. |
574 | | * |
575 | | * If not, |
576 | | * we need to look for its descriptor to see if it has been |
577 | | * escaped |
578 | | */ |
579 | 0 | if (big_tsk_getu32(head->magic) != EXT2_JMAGIC) { |
580 | 0 | TSK_DADDR_T i; |
581 | | |
582 | | /* cycle backwards until we find a desc block */ |
583 | 0 | for (i = end - 1; i > 0; i--) { |
584 | 0 | ext2fs_journ_dentry *dentry; |
585 | 0 | TSK_DADDR_T diff; |
586 | |
|
587 | 0 | head = (ext2fs_journ_head *) & journ[i * jinfo->bsize]; |
588 | |
|
589 | 0 | if (big_tsk_getu32(head->magic) != EXT2_JMAGIC) |
590 | 0 | continue; |
591 | | |
592 | | /* If we get a commit, then any desc we find will not |
593 | | * be for our block, so forget about it */ |
594 | 0 | if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_COM) |
595 | 0 | break; |
596 | | |
597 | | /* Skip any other data structure types */ |
598 | 0 | if (big_tsk_getu32(head->entry_type) != EXT2_J_ETYPE_DESC) |
599 | 0 | continue; |
600 | | |
601 | | /* We now have the previous descriptor |
602 | | * |
603 | | * NOTE: We have no clue if this is the correct |
604 | | * descriptor if it is not the current 'run' of |
605 | | * transactions, but this is the best we can do |
606 | | */ |
607 | 0 | diff = end - i; |
608 | |
|
609 | 0 | dentry = |
610 | 0 | (ext2fs_journ_dentry *) (&journ[i * jinfo->bsize] + |
611 | 0 | sizeof(ext2fs_journ_head)); |
612 | |
|
613 | 0 | while ((uintptr_t) dentry <= |
614 | 0 | ((uintptr_t) & journ[(i + 1) * jinfo->bsize] - |
615 | 0 | sizeof(ext2fs_journ_head))) { |
616 | |
|
617 | 0 | if (--diff == 0) { |
618 | 0 | if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_ESC) { |
619 | 0 | journ[end * jinfo->bsize] = 0xC0; |
620 | 0 | journ[end * jinfo->bsize + 1] = 0x3B; |
621 | 0 | journ[end * jinfo->bsize + 2] = 0x39; |
622 | 0 | journ[end * jinfo->bsize + 3] = 0x98; |
623 | 0 | } |
624 | 0 | break; |
625 | 0 | } |
626 | | |
627 | | /* If the SAMEID value is set, then we advance by the size of the entry, otherwise add 16 for the ID */ |
628 | 0 | if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_SAMEID) |
629 | 0 | dentry = |
630 | 0 | (ext2fs_journ_dentry *) ((uintptr_t) dentry + |
631 | 0 | sizeof(ext2fs_journ_dentry)); |
632 | 0 | else |
633 | 0 | dentry = |
634 | 0 | (ext2fs_journ_dentry *) ((uintptr_t) dentry + |
635 | 0 | sizeof(ext2fs_journ_dentry) + 16); |
636 | |
|
637 | 0 | } |
638 | 0 | break; |
639 | 0 | } |
640 | 0 | } |
641 | |
|
642 | 0 | if (fwrite(&journ[end * jinfo->bsize], jinfo->bsize, 1, stdout) != 1) { |
643 | 0 | tsk_error_reset(); |
644 | 0 | tsk_error_set_errno(TSK_ERR_FS_WRITE); |
645 | 0 | tsk_error_set_errstr |
646 | 0 | ("ext2fs_jblk_walk: error writing buffer block"); |
647 | 0 | free(journ); |
648 | 0 | return 1; |
649 | 0 | } |
650 | | |
651 | 0 | free(journ); |
652 | 0 | return 0; |
653 | 0 | } |