/src/sleuthkit/tsk/fs/ext2fs.cpp
Line | Count | Source |
1 | | /* |
2 | | ** The Sleuth Kit |
3 | | ** |
4 | | ** Brian Carrier [carrier <at> sleuthkit [dot] org] |
5 | | ** Copyright (c) 2006-2011 Brian Carrier, Basis Technology. All Rights reserved |
6 | | ** Copyright (c) 2003-2005 Brian Carrier. All rights reserved |
7 | | ** |
8 | | ** TASK |
9 | | ** Copyright (c) 2002-2003 Brian Carrier, @stake Inc. All rights reserved |
10 | | ** |
11 | | ** Copyright (c) 1997,1998,1999, International Business Machines |
12 | | ** Corporation and others. All Rights Reserved. |
13 | | */ |
14 | | |
15 | | /** |
16 | | *\file ext2fs.c |
17 | | * Contains the internal TSK ext2/ext3/ext4 file system functions. |
18 | | */ |
19 | | |
20 | | /* TCT |
21 | | * LICENSE |
22 | | * This software is distributed under the IBM Public License. |
23 | | * AUTHOR(S) |
24 | | * Wietse Venema |
25 | | * IBM T.J. Watson Research |
26 | | * P.O. Box 704 |
27 | | * Yorktown Heights, NY 10598, USA |
28 | | --*/ |
29 | | |
30 | | #include "tsk_fs_i.h" |
31 | | #include "tsk_ext2fs.h" |
32 | | #include "tsk/base/crc.h" |
33 | | |
34 | | #include <stddef.h> |
35 | | |
36 | | #include <memory> |
37 | | |
38 | | //#define Ext4_DBG 1 |
39 | | //#define EXT4_CHECKSUMS 1 |
40 | | |
41 | | |
42 | | #ifdef Ext4_DBG |
43 | | static uint8_t |
44 | | debug_print_buf(unsigned char *buf, int len) |
45 | | { |
46 | | int i = 0; |
47 | | for (i = 0; i < len; i++) { |
48 | | if (i % 8 == 0) |
49 | | printf("%08X:\t", i); |
50 | | printf("0x%02X ", buf[i]); |
51 | | if ((i + 1) % 8 == 0) |
52 | | printf("\n"); |
53 | | } |
54 | | printf("\n"); |
55 | | return 0; |
56 | | } |
57 | | #endif |
58 | | |
59 | | |
60 | | /** \internal |
61 | | test_root - tests to see if a is power of b |
62 | | @param a the number being investigated |
63 | | @param b the root |
64 | | @return 1 if a is a power of b, otherwise 0 |
65 | | */ |
66 | | static uint8_t |
67 | | test_root(uint32_t a, uint32_t b) |
68 | 0 | { |
69 | 0 | if (a == 0) { |
70 | 0 | return b == 0; |
71 | 0 | } |
72 | 0 | else if (b == 0) { |
73 | 0 | return 0; |
74 | 0 | } |
75 | 0 | else if (a == 1) { |
76 | | // anything to power of 0 is 1 |
77 | 0 | return 1; |
78 | 0 | } |
79 | 0 | else if (b == 1) { |
80 | 0 | return 0; |
81 | 0 | } |
82 | | |
83 | | // keep on multiplying b by itself |
84 | 0 | uint32_t b2; |
85 | 0 | for (b2 = b; b2 < a; b2 *= b) {} |
86 | | |
87 | | // was it an exact match? |
88 | 0 | return b2 == a; |
89 | 0 | } |
90 | | |
91 | | /** \internal |
92 | | * Test if block group has a super block in it. |
93 | | * |
94 | | * @return 1 if block group has superblock, otherwise 0 |
95 | | */ |
96 | | static uint32_t |
97 | | ext2fs_is_super_bg(uint32_t feature_ro_compat, uint32_t group_block) |
98 | 0 | { |
99 | | // if no sparse feature, then it has super block |
100 | 0 | if (!(feature_ro_compat & EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER)) |
101 | 0 | return 1; |
102 | | |
103 | | // group 0 always has super block |
104 | 0 | if (group_block == 0) |
105 | 0 | return 1; |
106 | | |
107 | | // Sparse FS put super blocks in groups that are powers of 3, 5, 7 |
108 | 0 | if (test_root(group_block, 3) || |
109 | 0 | (test_root(group_block, 5)) || |
110 | 0 | (test_root(group_block, 7))) { |
111 | 0 | return 1; |
112 | 0 | } |
113 | | |
114 | 0 | return 0; |
115 | 0 | } |
116 | | |
117 | | |
118 | | |
119 | | |
120 | | |
121 | | /* ext2fs_group_load - load 32-bit or 64-bit block group descriptor into cache |
122 | | * |
123 | | * Note: This routine assumes &ext2fs->lock is locked by the caller. |
124 | | * |
125 | | * return 1 on error and 0 on success. On success one of either ext2fs->grp_buf or ext2fs->ext4_grp_buf will |
126 | | * be non-null and contain the valid data. Because Ext4 can have 32-bit group descriptors, check which buffer is |
127 | | * non-null to determine what to read instead of duplicating the logic everywhere. |
128 | | * |
129 | | * */ |
130 | | static uint8_t |
131 | | ext2fs_group_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num) |
132 | 0 | { |
133 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) ext2fs; |
134 | 0 | size_t gd_size = tsk_getu16(fs->endian, ext2fs->fs->s_desc_size); |
135 | | |
136 | | /* |
137 | | * Sanity check |
138 | | */ |
139 | 0 | if (grp_num >= ext2fs->groups_count) { |
140 | 0 | tsk_error_reset(); |
141 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
142 | 0 | tsk_error_set_errstr |
143 | 0 | ("ext2fs_group_load: invalid cylinder group number: %" |
144 | 0 | PRI_EXT2GRP "", grp_num); |
145 | 0 | return 1; |
146 | 0 | } |
147 | | // already loaded |
148 | 0 | else if (ext2fs->grp_num == grp_num) { |
149 | 0 | return 0; |
150 | 0 | } |
151 | | |
152 | | // 64-bit version. |
153 | 0 | if (((fs->ftype == TSK_FS_TYPE_EXT4)) && (EXT2FS_HAS_INCOMPAT_FEATURE(fs, ext2fs->fs, |
154 | 0 | EXT2FS_FEATURE_INCOMPAT_64BIT) |
155 | 0 | && (tsk_getu16(fs->endian, ext2fs->fs->s_desc_size) >= 64))) { |
156 | 0 | TSK_OFF_T offs; |
157 | 0 | ssize_t cnt; |
158 | |
|
159 | 0 | if (gd_size < (int)sizeof(ext4fs_gd)) |
160 | 0 | gd_size = sizeof(ext4fs_gd); |
161 | |
|
162 | 0 | if (ext2fs->ext4_grp_buf == NULL) { |
163 | 0 | if ((ext2fs->ext4_grp_buf = (ext4fs_gd *) tsk_malloc(gd_size)) == NULL) |
164 | 0 | return 1; |
165 | 0 | } |
166 | 0 | offs = ext2fs->groups_offset + grp_num * gd_size; |
167 | |
|
168 | 0 | cnt = tsk_fs_read(&ext2fs->fs_info, offs, (char *) ext2fs->ext4_grp_buf, gd_size); |
169 | |
|
170 | | #ifdef Ext4_DBG |
171 | | debug_print_buf((char *) ext2fs->ext4_grp_buf, gd_size); |
172 | | #endif |
173 | 0 | if (cnt != (ssize_t) gd_size) { |
174 | 0 | if (cnt >= 0) { |
175 | 0 | tsk_error_reset(); |
176 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
177 | 0 | } |
178 | 0 | tsk_error_set_errstr2("ext2fs_group_load: Group descriptor %" |
179 | 0 | PRI_EXT2GRP " at %" PRIdOFF, grp_num, offs); |
180 | 0 | return 1; |
181 | 0 | } |
182 | | |
183 | | // sanity checks |
184 | 0 | if ((ext4_getu64(fs->endian, |
185 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_hi, |
186 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_lo) > fs->last_block) || |
187 | 0 | (ext4_getu64(fs->endian, |
188 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_hi, |
189 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_lo) > fs->last_block) || |
190 | 0 | (ext4_getu64(fs->endian, |
191 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_hi, |
192 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_lo) > fs->last_block)) { |
193 | 0 | tsk_error_reset(); |
194 | 0 | tsk_error_set_errno(TSK_ERR_FS_CORRUPT); |
195 | 0 | tsk_error_set_errstr("extXfs_group_load: Ext4 Group %" PRI_EXT2GRP |
196 | 0 | " descriptor block locations too large at byte offset %" |
197 | 0 | PRIuDADDR, grp_num, offs); |
198 | 0 | return 1; |
199 | 0 | } |
200 | 0 | } |
201 | 0 | else { |
202 | 0 | TSK_OFF_T offs; |
203 | 0 | ssize_t cnt; |
204 | 0 | if (gd_size < (int)sizeof(ext2fs_gd)) |
205 | 0 | gd_size = sizeof(ext2fs_gd); |
206 | |
|
207 | 0 | if (ext2fs->grp_buf == NULL) { |
208 | 0 | if ((ext2fs->grp_buf = (ext2fs_gd *) tsk_malloc(gd_size)) == NULL) |
209 | 0 | return 1; |
210 | 0 | } |
211 | 0 | offs = ext2fs->groups_offset + grp_num * gd_size; |
212 | |
|
213 | 0 | cnt = tsk_fs_read(&ext2fs->fs_info, offs, (char *) ext2fs->grp_buf, gd_size); |
214 | |
|
215 | 0 | if (cnt != (ssize_t) gd_size) { |
216 | 0 | if (cnt >= 0) { |
217 | 0 | tsk_error_reset(); |
218 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
219 | 0 | } |
220 | 0 | tsk_error_set_errstr2("ext2fs_group_load: Group descriptor %" |
221 | 0 | PRI_EXT2GRP " at %" PRIdOFF, grp_num, offs); |
222 | 0 | return 1; |
223 | 0 | } |
224 | | |
225 | | // sanity checks |
226 | 0 | if ((tsk_getu32(fs->endian, |
227 | 0 | ext2fs->grp_buf->bg_block_bitmap) > fs->last_block) || |
228 | 0 | (tsk_getu32(fs->endian, |
229 | 0 | ext2fs->grp_buf->bg_inode_bitmap) > fs->last_block) || |
230 | 0 | (tsk_getu32(fs->endian, |
231 | 0 | ext2fs->grp_buf->bg_inode_table) > fs->last_block)) { |
232 | 0 | tsk_error_reset(); |
233 | 0 | tsk_error_set_errno(TSK_ERR_FS_CORRUPT); |
234 | 0 | tsk_error_set_errstr("extXfs_group_load: Group %" PRI_EXT2GRP |
235 | 0 | " descriptor block locations too large at byte offset %" |
236 | 0 | PRIuDADDR, grp_num, offs); |
237 | 0 | return 1; |
238 | 0 | } |
239 | | |
240 | 0 | if (tsk_verbose) { |
241 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info; |
242 | 0 | tsk_fprintf(stderr, |
243 | 0 | "\tgroup %" PRI_EXT2GRP ": %" PRIu16 "/%" PRIu16 |
244 | 0 | " free blocks/inodes\n", grp_num, tsk_getu16(fs->endian, |
245 | 0 | ext2fs->grp_buf->bg_free_blocks_count), |
246 | 0 | tsk_getu16(fs->endian, ext2fs->grp_buf->bg_free_inodes_count)); |
247 | 0 | } |
248 | 0 | } |
249 | 0 | ext2fs->grp_num = grp_num; |
250 | |
|
251 | 0 | return 0; |
252 | 0 | } |
253 | | |
254 | | #ifdef EXT4_CHECKSUMS |
255 | | /** |
256 | | * ext4_group_desc_csum - Calculates the checksum of a group descriptor |
257 | | * Ported from linux/fs/ext4/super.c |
258 | | * @ext4_sb: pointer to ext2 super block structure |
259 | | * @block_group: group descriptor number |
260 | | * @gdp: pointer to group descriptor to calculate checksum for |
261 | | * returns the checksum value |
262 | | */ |
263 | | static uint16_t |
264 | | ext4_group_desc_csum(ext2fs_sb * ext4_sb, uint32_t block_group, |
265 | | struct ext4fs_gd *gdp) |
266 | | { |
267 | | cm_t CRC16_CTX; |
268 | | CRC16_CTX.cm_width = 16; |
269 | | CRC16_CTX.cm_poly = 0x8005L; |
270 | | CRC16_CTX.cm_init = 0xFFFFL; |
271 | | CRC16_CTX.cm_refin = TRUE; |
272 | | CRC16_CTX.cm_refot = TRUE; |
273 | | CRC16_CTX.cm_xorot = 0x0000L; |
274 | | cm_ini(&CRC16_CTX); |
275 | | if (*ext4_sb->s_feature_ro_compat & EXT2FS_FEATURE_RO_COMPAT_GDT_CSUM) { |
276 | | int offset = offsetof(struct ext4fs_gd, bg_checksum); |
277 | | uint32_t le_group = tsk_getu32(TSK_LIT_ENDIAN, &block_group); |
278 | | crc16(&CRC16_CTX, ext4_sb->s_uuid, sizeof(ext4_sb->s_uuid)); |
279 | | crc16(&CRC16_CTX, (uint8_t *) & le_group, sizeof(le_group)); |
280 | | crc16(&CRC16_CTX, (uint8_t *) gdp, offset); |
281 | | offset += sizeof(gdp->bg_checksum); /* skip checksum */ |
282 | | /* for checksum of struct ext4_group_desc do the rest... */ |
283 | | if ((*ext4_sb->s_feature_incompat & |
284 | | EXT2FS_FEATURE_INCOMPAT_64BIT) && |
285 | | offset < *ext4_sb->s_desc_size) { |
286 | | crc16(&CRC16_CTX, (uint8_t *) gdp + offset, |
287 | | *ext4_sb->s_desc_size - offset); |
288 | | } |
289 | | } |
290 | | |
291 | | return cm_crc(&CRC16_CTX); |
292 | | } |
293 | | #endif |
294 | | |
295 | | |
296 | | /* ext2fs_print_map - print a bitmap */ |
297 | | |
298 | | static void |
299 | | ext2fs_print_map(uint8_t * map, int len) |
300 | 0 | { |
301 | 0 | int i; |
302 | |
|
303 | 0 | for (i = 0; i < len; i++) { |
304 | 0 | if (i > 0 && i % 10 == 0) |
305 | 0 | putc('|', stderr); |
306 | 0 | putc(isset(map, i) ? '1' : '.', stderr); |
307 | 0 | } |
308 | 0 | putc('\n', stderr); |
309 | 0 | } |
310 | | |
311 | | #define INODE_TABLE_SIZE(ext2fs) \ |
312 | 0 | ((tsk_getu32(ext2fs->fs_info.endian, ext2fs->fs->s_inodes_per_group) * ext2fs->inode_size - 1) \ |
313 | 0 | / ext2fs->fs_info.block_size + 1) |
314 | | |
315 | | /* ext2fs_bmap_load - look up block bitmap & load into cache |
316 | | * |
317 | | * Note: This routine assumes &ext2fs->lock is locked by the caller. |
318 | | * |
319 | | * return 1 on error and 0 on success |
320 | | * */ |
321 | | static uint8_t |
322 | | ext2fs_bmap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num) |
323 | 0 | { |
324 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info; |
325 | 0 | ssize_t cnt; |
326 | 0 | TSK_DADDR_T addr; |
327 | | |
328 | | /* |
329 | | * Look up the group descriptor info. The load will do the sanity check. |
330 | | */ |
331 | 0 | if (ext2fs_group_load(ext2fs, grp_num)) { |
332 | 0 | return 1; |
333 | 0 | } |
334 | | |
335 | 0 | if (ext2fs->bmap_buf == NULL) { |
336 | 0 | if ((ext2fs->bmap_buf = |
337 | 0 | (uint8_t *) tsk_malloc(fs->block_size)) == NULL) { |
338 | 0 | return 1; |
339 | 0 | } |
340 | 0 | } |
341 | 0 | else if (ext2fs->bmap_grp_num == grp_num) { |
342 | 0 | return 0; |
343 | 0 | } |
344 | | |
345 | 0 | if (ext2fs->ext4_grp_buf != NULL) { |
346 | 0 | addr = ext4_getu64(fs->endian, |
347 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_hi, |
348 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_lo); |
349 | 0 | } |
350 | 0 | else { |
351 | 0 | addr = (TSK_DADDR_T) tsk_getu32(fs->endian, ext2fs->grp_buf->bg_block_bitmap); |
352 | 0 | } |
353 | |
|
354 | 0 | if (addr > fs->last_block) { |
355 | 0 | tsk_error_reset(); |
356 | 0 | tsk_error_set_errno(TSK_ERR_FS_BLK_NUM); |
357 | 0 | tsk_error_set_errstr |
358 | 0 | ("ext2fs_bmap_load: Block too large for image: %" PRIu64, addr); |
359 | 0 | return 1; |
360 | 0 | } |
361 | | |
362 | 0 | cnt = tsk_fs_read(fs, addr * fs->block_size, |
363 | 0 | (char *) ext2fs->bmap_buf, ext2fs->fs_info.block_size); |
364 | |
|
365 | 0 | if (cnt != ext2fs->fs_info.block_size) { |
366 | 0 | if (cnt >= 0) { |
367 | 0 | tsk_error_reset(); |
368 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
369 | 0 | } |
370 | 0 | tsk_error_set_errstr2("ext2fs_bmap_load: block bitmap %" |
371 | 0 | PRI_EXT2GRP " at %" PRIu64, grp_num, addr); |
372 | 0 | return 1; |
373 | 0 | } |
374 | | |
375 | 0 | ext2fs->bmap_grp_num = grp_num; |
376 | 0 | if (tsk_verbose > 1) |
377 | 0 | ext2fs_print_map(ext2fs->bmap_buf, |
378 | 0 | tsk_getu32(fs->endian, ext2fs->fs->s_blocks_per_group)); |
379 | 0 | return 0; |
380 | 0 | } |
381 | | |
382 | | |
383 | | /* ext2fs_imap_load - look up inode bitmap & load into cache |
384 | | * |
385 | | * Note: This routine assumes &ext2fs->lock is locked by the caller. |
386 | | * |
387 | | * return 0 on success and 1 on error |
388 | | * */ |
389 | | static uint8_t |
390 | | ext2fs_imap_load(EXT2FS_INFO * ext2fs, EXT2_GRPNUM_T grp_num) |
391 | 0 | { |
392 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info; |
393 | 0 | ssize_t cnt; |
394 | 0 | TSK_DADDR_T addr; |
395 | | |
396 | | /* |
397 | | * Look up the group descriptor info. |
398 | | */ |
399 | 0 | if (ext2fs_group_load(ext2fs, grp_num)) { |
400 | 0 | return 1; |
401 | 0 | } |
402 | | |
403 | | /* Allocate the cache buffer and exit if map is already loaded */ |
404 | 0 | if (ext2fs->imap_buf == NULL) { |
405 | 0 | if ((ext2fs->imap_buf = |
406 | 0 | (uint8_t *) tsk_malloc(fs->block_size)) == NULL) { |
407 | 0 | return 1; |
408 | 0 | } |
409 | 0 | } |
410 | 0 | else if (ext2fs->imap_grp_num == grp_num) { |
411 | 0 | return 0; |
412 | 0 | } |
413 | | |
414 | | /* |
415 | | * Look up the inode allocation bitmap. |
416 | | */ |
417 | 0 | if (ext2fs->ext4_grp_buf != NULL) { |
418 | 0 | addr = ext4_getu64(fs->endian, |
419 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_hi, |
420 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_lo); |
421 | 0 | } |
422 | 0 | else { |
423 | 0 | addr = (TSK_DADDR_T) tsk_getu32(fs->endian, ext2fs->grp_buf->bg_inode_bitmap); |
424 | 0 | } |
425 | |
|
426 | 0 | if (addr > fs->last_block) { |
427 | 0 | tsk_error_reset(); |
428 | 0 | tsk_error_set_errno(TSK_ERR_FS_BLK_NUM); |
429 | 0 | tsk_error_set_errstr |
430 | 0 | ("ext2fs_imap_load: Block too large for image: %" PRIu64, addr); |
431 | 0 | return 1; |
432 | 0 | } |
433 | | |
434 | | // Ensure the bitmap buffer is initialized. |
435 | 0 | memset(ext2fs->imap_buf, 0, fs->block_size); |
436 | |
|
437 | 0 | cnt = tsk_fs_read(fs, addr * fs->block_size, |
438 | 0 | (char *) ext2fs->imap_buf, ext2fs->fs_info.block_size); |
439 | |
|
440 | 0 | if (cnt != ext2fs->fs_info.block_size) { |
441 | 0 | if (cnt >= 0) { |
442 | 0 | tsk_error_reset(); |
443 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
444 | 0 | } |
445 | 0 | tsk_error_set_errstr2("ext2fs_imap_load: Inode bitmap %" |
446 | 0 | PRI_EXT2GRP " at %" PRIu64, grp_num, addr); |
447 | 0 | return 1; |
448 | 0 | } |
449 | | |
450 | 0 | ext2fs->imap_grp_num = grp_num; |
451 | 0 | if (tsk_verbose > 1) |
452 | 0 | ext2fs_print_map(ext2fs->imap_buf, |
453 | 0 | tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group)); |
454 | |
|
455 | 0 | return 0; |
456 | 0 | } |
457 | | |
458 | | /* ext2fs_dinode_load - look up disk inode & load into ext2fs_inode structure |
459 | | * @param ext2fs A ext2fs file system information structure |
460 | | * @param dino_inum Metadata address |
461 | | * @param dino_buf The buffer to store the block in (must be size of ext2fs->inode_size or larger) |
462 | | * @param ea_buf The buffer to hold the extended attribute data |
463 | | * |
464 | | * return 1 on error and 0 on success |
465 | | * */ |
466 | | |
467 | | static uint8_t |
468 | | ext2fs_dinode_load(EXT2FS_INFO * ext2fs, TSK_INUM_T dino_inum, |
469 | | ext2fs_inode * dino_buf, uint8_t ** ea_buf, size_t * ea_buf_len) |
470 | 0 | { |
471 | 0 | EXT2_GRPNUM_T grp_num; |
472 | 0 | TSK_OFF_T addr; |
473 | 0 | ssize_t cnt; |
474 | 0 | TSK_INUM_T rel_inum; |
475 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info; |
476 | | |
477 | | /* |
478 | | * Sanity check. |
479 | | * Use last_num-1 to account for virtual Orphan directory in last_inum. |
480 | | */ |
481 | 0 | if ((dino_inum < fs->first_inum) || (dino_inum > fs->last_inum - 1)) { |
482 | 0 | tsk_error_reset(); |
483 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); |
484 | 0 | tsk_error_set_errstr("ext2fs_dinode_load: address: %" PRIuINUM, |
485 | 0 | dino_inum); |
486 | 0 | return 1; |
487 | 0 | } |
488 | | |
489 | 0 | if (dino_buf == NULL) { |
490 | 0 | tsk_error_reset(); |
491 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
492 | 0 | tsk_error_set_errstr("ext2fs_dinode_load: dino_buf is NULL"); |
493 | 0 | return 1; |
494 | 0 | } |
495 | | |
496 | | /* |
497 | | * Look up the group descriptor for this inode. |
498 | | */ |
499 | 0 | grp_num = (EXT2_GRPNUM_T) ((dino_inum - fs->first_inum) / |
500 | 0 | tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group)); |
501 | | |
502 | | /* lock access to grp_buf */ |
503 | 0 | tsk_take_lock(&ext2fs->lock); |
504 | |
|
505 | 0 | if (ext2fs_group_load(ext2fs, grp_num)) { |
506 | 0 | tsk_release_lock(&ext2fs->lock); |
507 | 0 | return 1; |
508 | 0 | } |
509 | | |
510 | | /* |
511 | | * Look up the inode table block for this inode. |
512 | | */ |
513 | 0 | rel_inum = |
514 | 0 | (dino_inum - 1) - tsk_getu32(fs->endian, |
515 | 0 | ext2fs->fs->s_inodes_per_group) * grp_num; |
516 | 0 | if (ext2fs->ext4_grp_buf != NULL) { |
517 | | #ifdef Ext4_DBG |
518 | | printf("DEBUG: d_inode_load 64bit gd_size=%d\n", |
519 | | tsk_getu16(fs->endian, ext2fs->fs->s_desc_size)); |
520 | | #endif |
521 | | /* Test for possible overflow */ |
522 | 0 | if (ext4_getu64(fs->endian, ext2fs->ext4_grp_buf->bg_inode_table_hi, ext2fs->ext4_grp_buf->bg_inode_table_lo) |
523 | 0 | >= LLONG_MAX / fs->block_size) { |
524 | 0 | tsk_release_lock(&ext2fs->lock); |
525 | |
|
526 | 0 | tsk_error_reset(); |
527 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
528 | 0 | tsk_error_set_errstr |
529 | 0 | ("ext2fs_dinode_load: Overflow when calculating address"); |
530 | 0 | return 1; |
531 | 0 | } |
532 | | |
533 | 0 | addr = |
534 | 0 | (TSK_OFF_T) ext4_getu64(fs->endian, |
535 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_hi, |
536 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_lo) |
537 | 0 | * (TSK_OFF_T) fs->block_size + |
538 | 0 | rel_inum * (TSK_OFF_T) ext2fs->inode_size; |
539 | 0 | } |
540 | 0 | else { |
541 | 0 | addr = |
542 | 0 | (TSK_OFF_T) tsk_getu32(fs->endian, |
543 | 0 | ext2fs->grp_buf->bg_inode_table) * (TSK_OFF_T) fs->block_size + |
544 | 0 | rel_inum * (TSK_OFF_T) ext2fs->inode_size; |
545 | 0 | } |
546 | 0 | tsk_release_lock(&ext2fs->lock); |
547 | |
|
548 | 0 | cnt = tsk_fs_read(fs, addr, (char *) dino_buf, ext2fs->inode_size); |
549 | |
|
550 | 0 | if (cnt != ext2fs->inode_size) { |
551 | 0 | if (cnt >= 0) { |
552 | 0 | tsk_error_reset(); |
553 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
554 | 0 | } |
555 | 0 | tsk_error_set_errstr2("ext2fs_dinode_load: Inode %" PRIuINUM |
556 | 0 | " from %" PRIdOFF, dino_inum, addr); |
557 | 0 | return 1; |
558 | 0 | } |
559 | | //DEBUG printf("Inode Size: %d, %d, %d, %d\n", sizeof(ext2fs_inode), *ext2fs->fs->s_inode_size, ext2fs->inode_size, *ext2fs->fs->s_want_extra_isize); |
560 | | //DEBUG debug_print_buf((char *)dino_buf, ext2fs->inode_size); |
561 | | |
562 | | // Check if we have an extended attribute in the inode |
563 | 0 | if (ext2fs->inode_size > EXT2_EA_INODE_OFFSET) { |
564 | | // The extended attribute data immediatly follows the standard inode data |
565 | 0 | *ea_buf = (uint8_t*)dino_buf + EXT2_EA_INODE_OFFSET; |
566 | 0 | *ea_buf_len = ext2fs->inode_size - EXT2_EA_INODE_OFFSET; |
567 | 0 | } |
568 | 0 | else { |
569 | 0 | *ea_buf = NULL; |
570 | 0 | } |
571 | |
|
572 | 0 | if (tsk_verbose) { |
573 | 0 | tsk_fprintf(stderr, |
574 | 0 | "%" PRIuINUM " m/l/s=%o/%d/%" PRIu32 |
575 | 0 | " u/g=%d/%d macd=%" PRIu32 "/%" PRIu32 "/%" PRIu32 "/%" PRIu32 |
576 | 0 | "\n", dino_inum, tsk_getu16(fs->endian, dino_buf->i_mode), |
577 | 0 | tsk_getu16(fs->endian, dino_buf->i_nlink), |
578 | 0 | (tsk_getu32(fs->endian, |
579 | 0 | dino_buf->i_size) + (tsk_getu16(fs->endian, |
580 | 0 | dino_buf->i_mode) & EXT2_IN_REG) ? (uint64_t) |
581 | 0 | tsk_getu32(fs->endian, dino_buf->i_size_high) << 32 : 0), |
582 | 0 | tsk_getu16(fs->endian, |
583 | 0 | dino_buf->i_uid) + (tsk_getu16(fs->endian, |
584 | 0 | dino_buf->i_uid_high) << 16), tsk_getu16(fs->endian, |
585 | 0 | dino_buf->i_gid) + (tsk_getu16(fs->endian, |
586 | 0 | dino_buf->i_gid_high) << 16), tsk_getu32(fs->endian, |
587 | 0 | dino_buf->i_mtime), tsk_getu32(fs->endian, |
588 | 0 | dino_buf->i_atime), tsk_getu32(fs->endian, |
589 | 0 | dino_buf->i_ctime), tsk_getu32(fs->endian, |
590 | 0 | dino_buf->i_dtime)); |
591 | 0 | } |
592 | |
|
593 | 0 | return 0; |
594 | 0 | } |
595 | | |
596 | | /** |
597 | | * \internal |
598 | | * Loads attribute for Ext4 inline storage method. |
599 | | * @param fs_file File to load attrs |
600 | | * @param ea_buf Extended attribute buffer |
601 | | * @param ea_buf_len Extended attribute buffer length |
602 | | * @returns 0 on success, 1 otherwise |
603 | | */ |
604 | | static uint8_t |
605 | | ext4_load_attrs_inline(TSK_FS_FILE *fs_file, const uint8_t * ea_buf, size_t ea_buf_len) |
606 | 0 | { |
607 | 0 | TSK_FS_META *fs_meta = fs_file->meta; |
608 | 0 | TSK_FS_ATTR *fs_attr; |
609 | | |
610 | | // see if we have already loaded the attr |
611 | 0 | if ((fs_meta->attr != NULL) |
612 | 0 | && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) { |
613 | 0 | return 0; |
614 | 0 | } |
615 | | |
616 | 0 | if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) { |
617 | 0 | return 1; |
618 | 0 | } |
619 | | |
620 | | // First load the data from the extended attr (if present) |
621 | 0 | const char *ea_inline_data = NULL; |
622 | 0 | uint32_t ea_inline_data_len = 0; |
623 | 0 | if ((ea_buf != NULL) && (ea_buf_len > 4 + sizeof(ext2fs_ea_entry)) |
624 | 0 | && (tsk_getu32(fs_file->fs_info->endian, ea_buf) == EXT2_EA_MAGIC)) { |
625 | | |
626 | | // First entry starts after the four byte header |
627 | 0 | size_t index = 4; |
628 | 0 | ext2fs_ea_entry *ea_entry = (ext2fs_ea_entry*) &(ea_buf[index]); |
629 | | |
630 | | // The end of the list of entries is marked by two null bytes |
631 | 0 | while ((ea_entry->nlen != 0) || (ea_entry->nidx != 0)) { |
632 | | |
633 | | // It looks like the continuation of inline data is stored in system.data. |
634 | | // Make sure we have room to read the attr name 'data'. |
635 | 0 | if ((ea_entry->nidx == EXT2_EA_IDX_SYSTEM) |
636 | 0 | && (ea_entry->nlen == 4) |
637 | 0 | && (index + sizeof(ext2fs_ea_entry) + strlen("data") < ea_buf_len) |
638 | 0 | && (strncmp((const char*) &(ea_entry->name), "data", 4)) == 0) { |
639 | | |
640 | | // This is the right attribute. Check that the length and offset are valid. |
641 | | // The offset is from the beginning of the entries, i.e., four bytes into the buffer. |
642 | 0 | uint16_t offset = tsk_getu16(fs_file->fs_info->endian, ea_entry->val_off); |
643 | 0 | uint32_t size = tsk_getu32(fs_file->fs_info->endian, ea_entry->val_size); |
644 | 0 | if ((ea_buf_len >= 4) && (offset < ea_buf_len - 4) && (size <= ea_buf_len - 4 - offset)) { |
645 | 0 | ea_inline_data = (const char*) &(ea_buf[4 + offset]); |
646 | 0 | ea_inline_data_len = size; |
647 | 0 | break; |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | | // Prepare to load the next entry. |
652 | | // The entry size is the size of the struct plus the length of the name, minus one |
653 | | // because the struct contains the first character of the name. |
654 | 0 | index += sizeof(ext2fs_ea_entry) + ea_entry->nlen - 1; |
655 | | |
656 | | // Make sure there's room for the next entry plus the 'data' name we're looking for. |
657 | 0 | if (index + sizeof(ext2fs_ea_entry) + strlen("data") > ea_buf_len) { |
658 | 0 | break; |
659 | 0 | } |
660 | 0 | ea_entry = (ext2fs_ea_entry*) &(ea_buf[index]); |
661 | 0 | } |
662 | 0 | } |
663 | | |
664 | | // Check if resident data size exceeds maximum inode size (1024) - ext4 inode resident data offset (156) |
665 | 0 | if ((fs_meta->size == 0) || (fs_meta->size > (1024 - 156))) { |
666 | 0 | return 1; |
667 | 0 | } |
668 | | |
669 | | // Combine the two parts of the inline data for the resident attribute. For now, make a |
670 | | // buffer for the full file size - this may be different than the length of the data |
671 | | // from the inode if we have sparse data. |
672 | 0 | uint8_t *resident_data = (uint8_t*)tsk_malloc(fs_meta->size); |
673 | 0 | if (resident_data == NULL) { |
674 | 0 | return 1; |
675 | 0 | } |
676 | 0 | memset(resident_data, 0, fs_meta->size); |
677 | | |
678 | | // Copy the data from the inode. |
679 | 0 | size_t inode_data_len = (fs_meta->size < EXT2_INLINE_MAX_DATA_LEN) ? fs_meta->size : EXT2_INLINE_MAX_DATA_LEN; |
680 | 0 | memcpy(resident_data, fs_meta->content_ptr, inode_data_len); |
681 | | |
682 | | // If we need more data and found an extended attribute, append that data |
683 | 0 | if ((fs_meta->size > EXT2_INLINE_MAX_DATA_LEN) && (ea_inline_data_len > 0)) { |
684 | | // Don't go beyond the size of the file |
685 | 0 | size_t ea_data_len = (ea_inline_data_len < (uint64_t)fs_meta->size - inode_data_len) ? ea_inline_data_len : fs_meta->size - inode_data_len; |
686 | 0 | memcpy(resident_data + inode_data_len, ea_inline_data, ea_data_len); |
687 | 0 | } |
688 | |
|
689 | 0 | if (fs_meta->attr == NULL) { |
690 | 0 | fs_meta->attr = tsk_fs_attrlist_alloc(); |
691 | 0 | } |
692 | 0 | if ((fs_attr = |
693 | 0 | tsk_fs_attrlist_getnew(fs_meta->attr, |
694 | 0 | TSK_FS_ATTR_RES)) == NULL) { |
695 | 0 | free(resident_data); |
696 | 0 | return 1; |
697 | 0 | } |
698 | | |
699 | | // Set the details in the fs_attr structure |
700 | 0 | if (tsk_fs_attr_set_str(fs_file, fs_attr, "DATA", |
701 | 0 | TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, |
702 | 0 | (void*)resident_data, |
703 | 0 | fs_meta->size)) { |
704 | 0 | free(resident_data); |
705 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_ERROR; |
706 | 0 | return 1; |
707 | 0 | } |
708 | | |
709 | 0 | free(resident_data); |
710 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
711 | 0 | return 0; |
712 | 0 | } |
713 | | |
714 | | /* ext2fs_dinode_copy - copy cached disk inode into generic inode |
715 | | * |
716 | | * returns 1 on error and 0 on success |
717 | | * */ |
718 | | static uint8_t |
719 | | ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_FILE * fs_file, |
720 | | TSK_INUM_T inum, const ext2fs_inode * dino_buf, const uint8_t * ea_buf, size_t ea_buf_len) |
721 | 0 | { |
722 | 0 | int i; |
723 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info; |
724 | 0 | TSK_FS_META * fs_meta = fs_file->meta; |
725 | 0 | ext2fs_sb *sb = ext2fs->fs; |
726 | 0 | EXT2_GRPNUM_T grp_num; |
727 | 0 | TSK_INUM_T ibase = 0; |
728 | |
|
729 | 0 | if (dino_buf == NULL) { |
730 | 0 | tsk_error_reset(); |
731 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
732 | 0 | tsk_error_set_errstr("ext2fs_dinode_copy: dino_buf is NULL"); |
733 | 0 | return 1; |
734 | 0 | } |
735 | | |
736 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY; |
737 | 0 | if (fs_meta->attr) { |
738 | 0 | tsk_fs_attrlist_markunused(fs_meta->attr); |
739 | 0 | } |
740 | | |
741 | | // set the type |
742 | 0 | switch (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_FMT) { |
743 | 0 | case EXT2_IN_REG: |
744 | 0 | fs_meta->type = TSK_FS_META_TYPE_REG; |
745 | 0 | break; |
746 | 0 | case EXT2_IN_DIR: |
747 | 0 | fs_meta->type = TSK_FS_META_TYPE_DIR; |
748 | 0 | break; |
749 | 0 | case EXT2_IN_SOCK: |
750 | 0 | fs_meta->type = TSK_FS_META_TYPE_SOCK; |
751 | 0 | break; |
752 | 0 | case EXT2_IN_LNK: |
753 | 0 | fs_meta->type = TSK_FS_META_TYPE_LNK; |
754 | 0 | break; |
755 | 0 | case EXT2_IN_BLK: |
756 | 0 | fs_meta->type = TSK_FS_META_TYPE_BLK; |
757 | 0 | break; |
758 | 0 | case EXT2_IN_CHR: |
759 | 0 | fs_meta->type = TSK_FS_META_TYPE_CHR; |
760 | 0 | break; |
761 | 0 | case EXT2_IN_FIFO: |
762 | 0 | fs_meta->type = TSK_FS_META_TYPE_FIFO; |
763 | 0 | break; |
764 | 0 | default: |
765 | 0 | fs_meta->type = TSK_FS_META_TYPE_UNDEF; |
766 | 0 | break; |
767 | 0 | } |
768 | | |
769 | | // set the mode |
770 | 0 | fs_meta->mode = TSK_FS_META_MODE_UNSPECIFIED; |
771 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_ISUID) |
772 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_ISUID); |
773 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_ISGID) |
774 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_ISGID); |
775 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_ISVTX) |
776 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_ISVTX); |
777 | |
|
778 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IRUSR) |
779 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IRUSR); |
780 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IWUSR) |
781 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IWUSR); |
782 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IXUSR) |
783 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IXUSR); |
784 | |
|
785 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IRGRP) |
786 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IRGRP); |
787 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IWGRP) |
788 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IWGRP); |
789 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IXGRP) |
790 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IXGRP); |
791 | |
|
792 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IROTH) |
793 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IROTH); |
794 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IWOTH) |
795 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IWOTH); |
796 | 0 | if (tsk_getu16(fs->endian, dino_buf->i_mode) & EXT2_IN_IXOTH) |
797 | 0 | fs_meta->mode = (TSK_FS_META_MODE_ENUM) (fs_meta->mode | TSK_FS_META_MODE_IXOTH); |
798 | |
|
799 | 0 | fs_meta->nlink = tsk_getu16(fs->endian, dino_buf->i_nlink); |
800 | |
|
801 | 0 | fs_meta->size = tsk_getu32(fs->endian, dino_buf->i_size); |
802 | |
|
803 | 0 | fs_meta->addr = inum; |
804 | | |
805 | | /* the general size value in the inode is only 32-bits, |
806 | | * but the i_dir_acl value is used for regular files to |
807 | | * hold the upper 32-bits |
808 | | * |
809 | | * The RO_COMPAT_LARGE_FILE flag in the super block will identify |
810 | | * if there are any large files in the file system |
811 | | */ |
812 | 0 | if ((fs_meta->type == TSK_FS_META_TYPE_REG) && |
813 | 0 | (tsk_getu32(fs->endian, sb->s_feature_ro_compat) & |
814 | 0 | EXT2FS_FEATURE_RO_COMPAT_LARGE_FILE)) { |
815 | 0 | fs_meta->size += |
816 | 0 | ((uint64_t) tsk_getu32(fs->endian, |
817 | 0 | dino_buf->i_size_high) << 32); |
818 | 0 | } |
819 | |
|
820 | 0 | fs_meta->uid = |
821 | 0 | tsk_getu16(fs->endian, dino_buf->i_uid) + ((TSK_UID_T)tsk_getu16(fs->endian, |
822 | 0 | dino_buf->i_uid_high) << 16); |
823 | 0 | fs_meta->gid = |
824 | 0 | tsk_getu16(fs->endian, dino_buf->i_gid) + ((TSK_GID_T)tsk_getu16(fs->endian, |
825 | 0 | dino_buf->i_gid_high) << 16); |
826 | 0 | fs_meta->mtime = tsk_getu32(fs->endian, dino_buf->i_mtime); |
827 | 0 | fs_meta->atime = tsk_getu32(fs->endian, dino_buf->i_atime); |
828 | 0 | fs_meta->ctime = tsk_getu32(fs->endian, dino_buf->i_ctime); |
829 | 0 | fs_meta->time2.ext2.dtime = tsk_getu32(fs->endian, dino_buf->i_dtime); |
830 | 0 | if (fs->ftype == TSK_FS_TYPE_EXT4) { |
831 | 0 | fs_meta->mtime_nano = |
832 | 0 | tsk_getu32(fs->endian, dino_buf->i_mtime_extra) >> 2; |
833 | 0 | fs_meta->atime_nano = |
834 | 0 | tsk_getu32(fs->endian, dino_buf->i_atime_extra) >> 2; |
835 | 0 | fs_meta->ctime_nano = |
836 | 0 | tsk_getu32(fs->endian, dino_buf->i_ctime_extra) >> 2; |
837 | 0 | fs_meta->crtime = tsk_getu32(fs->endian, dino_buf->i_crtime); |
838 | 0 | fs_meta->crtime_nano = |
839 | 0 | tsk_getu32(fs->endian, dino_buf->i_crtime_extra) >> 2; |
840 | 0 | } |
841 | 0 | else { |
842 | 0 | fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano = 0; |
843 | 0 | fs_meta->crtime = 0; |
844 | 0 | } |
845 | 0 | fs_meta->time2.ext2.dtime_nano = 0; |
846 | 0 | fs_meta->seq = 0; |
847 | |
|
848 | 0 | if (fs_meta->link) { |
849 | 0 | free(fs_meta->link); |
850 | 0 | fs_meta->link = NULL; |
851 | 0 | } |
852 | |
|
853 | 0 | if (fs_meta->content_len != EXT2FS_FILE_CONTENT_LEN) { |
854 | 0 | if ((fs_meta = |
855 | 0 | tsk_fs_meta_realloc(fs_meta, |
856 | 0 | EXT2FS_FILE_CONTENT_LEN)) == NULL) { |
857 | 0 | return 1; |
858 | 0 | } |
859 | 0 | } |
860 | | |
861 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_EXTENTS) { |
862 | 0 | uint32_t *addr_ptr; |
863 | 0 | fs_meta->content_type = TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS; |
864 | | /* NOTE TSK_DADDR_T != uint32_t, so lets make sure we use uint32_t */ |
865 | 0 | addr_ptr = (uint32_t *) fs_meta->content_ptr; |
866 | 0 | for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++) { |
867 | 0 | addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]); |
868 | 0 | } |
869 | 0 | } |
870 | 0 | else if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_INLINE_DATA) { |
871 | 0 | uint32_t *addr_ptr; |
872 | 0 | fs_meta->content_type = TSK_FS_META_CONTENT_TYPE_EXT4_INLINE; |
873 | 0 | addr_ptr = (uint32_t *)fs_meta->content_ptr; |
874 | 0 | for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++) { |
875 | 0 | addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]); |
876 | 0 | } |
877 | | |
878 | | // For inline data we create the default attribute now |
879 | 0 | ext4_load_attrs_inline(fs_file, ea_buf, ea_buf_len); |
880 | 0 | } |
881 | 0 | else { |
882 | 0 | TSK_DADDR_T *addr_ptr; |
883 | 0 | addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr; |
884 | 0 | for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++) |
885 | 0 | addr_ptr[i] = tsk_gets32(fs->endian, dino_buf->i_block[i]); |
886 | | |
887 | | /* set the link string |
888 | | * the size check prevents us from trying to allocate a huge amount of |
889 | | * memory for a bad inode value |
890 | | */ |
891 | 0 | if ((fs_meta->type == TSK_FS_META_TYPE_LNK) |
892 | 0 | && (fs_meta->size < EXT2FS_MAXPATHLEN) && (fs_meta->size >= 0)) { |
893 | 0 | int i; |
894 | |
|
895 | 0 | if ((fs_meta->link = |
896 | 0 | (char*) tsk_malloc((size_t) (fs_meta->size + 1))) == NULL) |
897 | 0 | return 1; |
898 | | |
899 | | /* it is located directly in the pointers */ |
900 | 0 | if (fs_meta->size < 4 * (EXT2FS_NDADDR + EXT2FS_NIADDR)) { |
901 | 0 | unsigned int j; |
902 | 0 | unsigned int count = 0; |
903 | |
|
904 | 0 | for (i = 0; i < (EXT2FS_NDADDR + EXT2FS_NIADDR) && |
905 | 0 | count < fs_meta->size; i++) { |
906 | 0 | char *a_ptr = (char *) &dino_buf->i_block[i]; |
907 | 0 | for (j = 0; j < 4 && count < fs_meta->size; j++) { |
908 | 0 | fs_meta->link[count++] = a_ptr[j]; |
909 | 0 | } |
910 | 0 | } |
911 | 0 | fs_meta->link[count] = '\0'; |
912 | | |
913 | | /* clear the content pointer data to avoid the prog from reading them */ |
914 | 0 | memset(fs_meta->content_ptr, 0, fs_meta->content_len); |
915 | 0 | } |
916 | | |
917 | | /* it is in blocks */ |
918 | 0 | else { |
919 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ext2fs->fs_info; |
920 | 0 | char *data_buf = NULL; |
921 | 0 | char *a_ptr = fs_meta->link; |
922 | 0 | unsigned int total_read = 0; |
923 | 0 | TSK_DADDR_T *addr_ptr = (TSK_DADDR_T*) fs_meta->content_ptr;; |
924 | |
|
925 | 0 | if ((data_buf = (char*) tsk_malloc(fs->block_size)) == NULL) { |
926 | 0 | return 1; |
927 | 0 | } |
928 | | |
929 | | /* we only need to do the direct blocks due to the limit |
930 | | * on path length */ |
931 | 0 | for (i = 0; i < EXT2FS_NDADDR && total_read < fs_meta->size; |
932 | 0 | i++) { |
933 | 0 | ssize_t cnt; |
934 | |
|
935 | 0 | cnt = tsk_fs_read_block(fs, |
936 | 0 | addr_ptr[i], data_buf, fs->block_size); |
937 | |
|
938 | 0 | if (cnt != fs->block_size) { |
939 | 0 | if (cnt >= 0) { |
940 | 0 | tsk_error_reset(); |
941 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
942 | 0 | } |
943 | 0 | tsk_error_set_errstr2 |
944 | 0 | ("ext2fs_dinode_copy: symlink destination from %" |
945 | 0 | PRIuDADDR, addr_ptr[i]); |
946 | 0 | free(data_buf); |
947 | 0 | return 1; |
948 | 0 | } |
949 | | |
950 | 0 | int copy_len = |
951 | 0 | (fs_meta->size - total_read < |
952 | 0 | fs->block_size) ? (int) (fs_meta->size - |
953 | 0 | total_read) : (int) (fs->block_size); |
954 | |
|
955 | 0 | memcpy(a_ptr, data_buf, copy_len); |
956 | 0 | total_read += copy_len; |
957 | 0 | a_ptr = (char *) ((uintptr_t) a_ptr + copy_len); |
958 | 0 | } |
959 | | |
960 | | /* terminate the string */ |
961 | 0 | *a_ptr = '\0'; |
962 | 0 | free(data_buf); |
963 | 0 | } |
964 | | |
965 | | /* Clean up name */ |
966 | 0 | i = 0; |
967 | 0 | while (fs_meta->link[i] != '\0') { |
968 | 0 | if (TSK_IS_CNTRL(fs_meta->link[i])) |
969 | 0 | fs_meta->link[i] = '^'; |
970 | 0 | i++; |
971 | 0 | } |
972 | 0 | } |
973 | 0 | } |
974 | | |
975 | | /* Fill in the flags value */ |
976 | 0 | grp_num = (EXT2_GRPNUM_T) ((inum - fs->first_inum) / |
977 | 0 | tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group)); |
978 | | |
979 | |
|
980 | 0 | tsk_take_lock(&ext2fs->lock); |
981 | |
|
982 | 0 | if (ext2fs_imap_load(ext2fs, grp_num)) { |
983 | 0 | tsk_release_lock(&ext2fs->lock); |
984 | 0 | return 1; |
985 | 0 | } |
986 | | |
987 | 0 | ibase = |
988 | 0 | grp_num * tsk_getu32(fs->endian, |
989 | 0 | ext2fs->fs->s_inodes_per_group) + fs->first_inum; |
990 | | |
991 | | |
992 | | /* |
993 | | * Ensure that inum - ibase refers to a valid bit offset in imap_buf. |
994 | | */ |
995 | 0 | if ((ibase > inum) || (inum - ibase) >= (fs->block_size * 8)) { |
996 | 0 | tsk_release_lock(&ext2fs->lock); |
997 | 0 | tsk_error_reset(); |
998 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
999 | 0 | tsk_error_set_errstr("ext2fs_dinode_copy: Invalid offset into imap_buf (inum %" PRIuINUM " - ibase %" PRIuINUM ")", |
1000 | 0 | inum, ibase); |
1001 | 0 | return 1; |
1002 | 0 | } |
1003 | | |
1004 | | |
1005 | | /* |
1006 | | * Apply the allocated/unallocated restriction. |
1007 | | */ |
1008 | 0 | fs_meta->flags = (isset(ext2fs->imap_buf, inum - ibase) ? |
1009 | 0 | TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC); |
1010 | |
|
1011 | 0 | tsk_release_lock(&ext2fs->lock); |
1012 | | |
1013 | | |
1014 | | /* |
1015 | | * Apply the used/unused restriction. |
1016 | | */ |
1017 | 0 | fs_meta->flags = (TSK_FS_META_FLAG_ENUM) (fs_meta->flags | (fs_meta->ctime ? |
1018 | 0 | TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED)); |
1019 | |
|
1020 | 0 | return 0; |
1021 | 0 | } |
1022 | | |
1023 | | |
1024 | | |
1025 | | /* ext2fs_inode_lookup - lookup inode, external interface |
1026 | | * |
1027 | | * Returns 1 on error and 0 on success |
1028 | | * |
1029 | | */ |
1030 | | |
1031 | | static uint8_t |
1032 | | ext2fs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file, |
1033 | | TSK_INUM_T inum) |
1034 | 0 | { |
1035 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
1036 | 0 | ext2fs_inode *dino_buf = NULL; |
1037 | 0 | uint8_t *ea_buf = NULL; |
1038 | 0 | size_t ea_buf_len = 0; |
1039 | 0 | unsigned int size = 0; |
1040 | |
|
1041 | 0 | if (a_fs_file == NULL) { |
1042 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1043 | 0 | tsk_error_set_errstr("ext2fs_inode_lookup: fs_file is NULL"); |
1044 | 0 | return 1; |
1045 | 0 | } |
1046 | | |
1047 | 0 | if (a_fs_file->meta == NULL) { |
1048 | 0 | if ((a_fs_file->meta = |
1049 | 0 | tsk_fs_meta_alloc(EXT2FS_FILE_CONTENT_LEN)) == NULL) |
1050 | 0 | return 1; |
1051 | 0 | } |
1052 | 0 | else { |
1053 | 0 | tsk_fs_meta_reset(a_fs_file->meta); |
1054 | 0 | } |
1055 | | |
1056 | | // see if they are looking for the special "orphans" directory |
1057 | 0 | if (inum == TSK_FS_ORPHANDIR_INUM(fs)) { |
1058 | 0 | if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta)) |
1059 | 0 | return 1; |
1060 | 0 | else |
1061 | 0 | return 0; |
1062 | 0 | } |
1063 | | |
1064 | 0 | size = |
1065 | 0 | ext2fs->inode_size > |
1066 | 0 | sizeof(ext2fs_inode) ? ext2fs->inode_size : sizeof(ext2fs_inode); |
1067 | 0 | if ((dino_buf = (ext2fs_inode *) tsk_malloc(size)) == NULL) { |
1068 | 0 | return 1; |
1069 | 0 | } |
1070 | | |
1071 | 0 | if (ext2fs_dinode_load(ext2fs, inum, dino_buf, &ea_buf, &ea_buf_len)) { |
1072 | 0 | free(dino_buf); |
1073 | 0 | return 1; |
1074 | 0 | } |
1075 | | |
1076 | 0 | if (ext2fs_dinode_copy(ext2fs, a_fs_file, inum, dino_buf, ea_buf, ea_buf_len)) { |
1077 | 0 | free(dino_buf); |
1078 | 0 | return 1; |
1079 | 0 | } |
1080 | | |
1081 | 0 | free(dino_buf); |
1082 | 0 | return 0; |
1083 | 0 | } |
1084 | | |
1085 | | |
1086 | | |
1087 | | /* ext2fs_inode_walk - inode iterator |
1088 | | * |
1089 | | * flags used: TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED, |
1090 | | * TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN |
1091 | | * |
1092 | | * Return 1 on error and 0 on success |
1093 | | */ |
1094 | | |
1095 | | uint8_t |
1096 | | ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, |
1097 | | TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM flags, |
1098 | | TSK_FS_META_WALK_CB a_action, void *a_ptr) |
1099 | 0 | { |
1100 | 0 | const char *myname = "extXfs_inode_walk"; |
1101 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
1102 | 0 | TSK_INUM_T inum; |
1103 | 0 | TSK_INUM_T end_inum_tmp; |
1104 | 0 | TSK_INUM_T ibase = 0; |
1105 | 0 | unsigned int myflags; |
1106 | 0 | ext2fs_inode *dino_buf = NULL; |
1107 | 0 | uint8_t *ea_buf = NULL; |
1108 | 0 | size_t ea_buf_len = 0; |
1109 | 0 | unsigned int size = 0; |
1110 | | |
1111 | | // clean up any error messages that are lying around |
1112 | 0 | tsk_error_reset(); |
1113 | | |
1114 | | /* |
1115 | | * Sanity checks. |
1116 | | */ |
1117 | 0 | if (start_inum < fs->first_inum || start_inum > fs->last_inum) { |
1118 | 0 | tsk_error_reset(); |
1119 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1120 | 0 | tsk_error_set_errstr("%s: start inode: %" PRIuINUM "", myname, |
1121 | 0 | start_inum); |
1122 | 0 | return 1; |
1123 | 0 | } |
1124 | | |
1125 | 0 | if (end_inum < fs->first_inum || end_inum > fs->last_inum |
1126 | 0 | || end_inum < start_inum) { |
1127 | 0 | tsk_error_reset(); |
1128 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1129 | 0 | tsk_error_set_errstr("%s: end inode: %" PRIuINUM "", myname, |
1130 | 0 | end_inum); |
1131 | 0 | return 1; |
1132 | 0 | } |
1133 | | |
1134 | | /* If ORPHAN is wanted, then make sure that the flags are correct */ |
1135 | 0 | if (flags & TSK_FS_META_FLAG_ORPHAN) { |
1136 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_UNALLOC); |
1137 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_ALLOC); |
1138 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED); |
1139 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_UNUSED); |
1140 | 0 | } |
1141 | 0 | else { |
1142 | 0 | if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) && |
1143 | 0 | ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) { |
1144 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC); |
1145 | 0 | } |
1146 | | |
1147 | | /* If neither of the USED or UNUSED flags are set, then set them |
1148 | | * both |
1149 | | */ |
1150 | 0 | if (((flags & TSK_FS_META_FLAG_USED) == 0) && |
1151 | 0 | ((flags & TSK_FS_META_FLAG_UNUSED) == 0)) { |
1152 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED); |
1153 | 0 | } |
1154 | 0 | } |
1155 | | |
1156 | | |
1157 | | |
1158 | | /* If we are looking for orphan files and have not yet filled |
1159 | | * in the list of unalloc inodes that are pointed to, then fill |
1160 | | * in the list |
1161 | | */ |
1162 | 0 | if ((flags & TSK_FS_META_FLAG_ORPHAN)) { |
1163 | 0 | if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) { |
1164 | 0 | tsk_error_errstr2_concat |
1165 | 0 | ("- ext2fs_inode_walk: identifying inodes allocated by file names"); |
1166 | 0 | return 1; |
1167 | 0 | } |
1168 | |
|
1169 | 0 | } |
1170 | | |
1171 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
1172 | 0 | tsk_fs_file_alloc(fs), |
1173 | 0 | tsk_fs_file_close |
1174 | 0 | }; |
1175 | |
|
1176 | 0 | if (!fs_file) |
1177 | 0 | return 1; |
1178 | | |
1179 | 0 | fs_file->meta = tsk_fs_meta_alloc(EXT2FS_FILE_CONTENT_LEN); |
1180 | 0 | if (fs_file->meta == NULL) { |
1181 | 0 | return 1; |
1182 | 0 | } |
1183 | | |
1184 | | // we need to handle fs->last_inum specially because it is for the |
1185 | | // virtual ORPHANS directory. Handle it outside of the loop. |
1186 | 0 | if (end_inum == TSK_FS_ORPHANDIR_INUM(fs)) |
1187 | 0 | end_inum_tmp = end_inum - 1; |
1188 | 0 | else |
1189 | 0 | end_inum_tmp = end_inum; |
1190 | | |
1191 | | /* |
1192 | | * Iterate. |
1193 | | */ |
1194 | 0 | size = |
1195 | 0 | ext2fs->inode_size > |
1196 | 0 | sizeof(ext2fs_inode) ? ext2fs->inode_size : sizeof(ext2fs_inode); |
1197 | |
|
1198 | 0 | dino_buf = (ext2fs_inode *) tsk_malloc(size); |
1199 | 0 | if (dino_buf == NULL) { |
1200 | 0 | return 1; |
1201 | 0 | } |
1202 | | |
1203 | 0 | for (inum = start_inum; inum <= end_inum_tmp; inum++) { |
1204 | 0 | int retval; |
1205 | 0 | EXT2_GRPNUM_T grp_num; |
1206 | | |
1207 | | /* |
1208 | | * Be sure to use the proper group descriptor data. XXX Linux inodes |
1209 | | * start at 1, as in Fortran. |
1210 | | */ |
1211 | 0 | grp_num = |
1212 | 0 | (EXT2_GRPNUM_T) ((inum - 1) / tsk_getu32(fs->endian, |
1213 | 0 | ext2fs->fs->s_inodes_per_group)); |
1214 | | |
1215 | | /* lock access to imap_buf */ |
1216 | 0 | tsk_take_lock(&ext2fs->lock); |
1217 | |
|
1218 | 0 | if (ext2fs_imap_load(ext2fs, grp_num)) { |
1219 | 0 | tsk_release_lock(&ext2fs->lock); |
1220 | 0 | free(dino_buf); |
1221 | 0 | return 1; |
1222 | 0 | } |
1223 | 0 | ibase = |
1224 | 0 | grp_num * tsk_getu32(fs->endian, |
1225 | 0 | ext2fs->fs->s_inodes_per_group) + 1; |
1226 | | |
1227 | | /* |
1228 | | * Ensure that inum - ibase refers to a valid bit offset in imap_buf. |
1229 | | */ |
1230 | 0 | if ((ibase > inum) || (inum - ibase) >= (fs->block_size * 8)) { |
1231 | 0 | tsk_release_lock(&ext2fs->lock); |
1232 | 0 | free(dino_buf); |
1233 | |
|
1234 | 0 | tsk_error_reset(); |
1235 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1236 | 0 | tsk_error_set_errstr("%s: Invalid offset into imap_buf (inum %" PRIuINUM " - ibase %" PRIuINUM ")", |
1237 | 0 | myname, inum, ibase); |
1238 | 0 | return 1; |
1239 | 0 | } |
1240 | | |
1241 | | /* |
1242 | | * Apply the allocated/unallocated restriction. |
1243 | | */ |
1244 | 0 | myflags = (isset(ext2fs->imap_buf, inum - ibase) ? |
1245 | 0 | TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC); |
1246 | |
|
1247 | 0 | tsk_release_lock(&ext2fs->lock); |
1248 | |
|
1249 | 0 | if ((flags & myflags) != myflags) |
1250 | 0 | continue; |
1251 | | |
1252 | 0 | if (ext2fs_dinode_load(ext2fs, inum, dino_buf, &ea_buf, &ea_buf_len)) { |
1253 | 0 | free(dino_buf); |
1254 | 0 | return 1; |
1255 | 0 | } |
1256 | | |
1257 | | |
1258 | | /* |
1259 | | * Apply the used/unused restriction. |
1260 | | */ |
1261 | 0 | myflags |= (tsk_getu32(fs->endian, dino_buf->i_ctime) ? |
1262 | 0 | TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED); |
1263 | |
|
1264 | 0 | if ((flags & myflags) != myflags) |
1265 | 0 | continue; |
1266 | | |
1267 | | /* If we want only orphans, then check if this |
1268 | | * inode is in the seen list |
1269 | | */ |
1270 | 0 | if ((myflags & TSK_FS_META_FLAG_UNALLOC) && |
1271 | 0 | (flags & TSK_FS_META_FLAG_ORPHAN) && |
1272 | 0 | (tsk_fs_dir_find_inum_named(fs, inum))) { |
1273 | 0 | continue; |
1274 | 0 | } |
1275 | | |
1276 | | /* |
1277 | | * Fill in a file system-independent inode structure and pass control |
1278 | | * to the application. |
1279 | | */ |
1280 | 0 | if (ext2fs_dinode_copy(ext2fs, fs_file.get(), inum, dino_buf, ea_buf, ea_buf_len)) { |
1281 | 0 | free(dino_buf); |
1282 | 0 | return 1; |
1283 | 0 | } |
1284 | | |
1285 | 0 | retval = a_action(fs_file.get(), a_ptr); |
1286 | 0 | if (retval == TSK_WALK_STOP) { |
1287 | 0 | free(dino_buf); |
1288 | 0 | return 0; |
1289 | 0 | } |
1290 | 0 | else if (retval == TSK_WALK_ERROR) { |
1291 | 0 | free(dino_buf); |
1292 | 0 | return 1; |
1293 | 0 | } |
1294 | 0 | } |
1295 | | |
1296 | | // handle the virtual orphans folder if they asked for it |
1297 | 0 | if ((end_inum == TSK_FS_ORPHANDIR_INUM(fs)) |
1298 | 0 | && (flags & TSK_FS_META_FLAG_ALLOC) |
1299 | 0 | && (flags & TSK_FS_META_FLAG_USED)) { |
1300 | 0 | int retval; |
1301 | |
|
1302 | 0 | if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) { |
1303 | 0 | free(dino_buf); |
1304 | 0 | return 1; |
1305 | 0 | } |
1306 | | /* call action */ |
1307 | 0 | retval = a_action(fs_file.get(), a_ptr); |
1308 | 0 | if (retval == TSK_WALK_STOP) { |
1309 | 0 | free(dino_buf); |
1310 | 0 | return 0; |
1311 | 0 | } |
1312 | 0 | else if (retval == TSK_WALK_ERROR) { |
1313 | 0 | free(dino_buf); |
1314 | 0 | return 1; |
1315 | 0 | } |
1316 | 0 | } |
1317 | | |
1318 | | /* |
1319 | | * Cleanup. |
1320 | | */ |
1321 | 0 | free(dino_buf); |
1322 | |
|
1323 | 0 | return 0; |
1324 | 0 | } |
1325 | | |
1326 | | |
1327 | | |
1328 | | TSK_FS_BLOCK_FLAG_ENUM |
1329 | | ext2fs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr) |
1330 | 0 | { |
1331 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) a_fs; |
1332 | 0 | int flags; |
1333 | 0 | EXT2_GRPNUM_T grp_num; |
1334 | 0 | TSK_DADDR_T dbase = 0; /* first block number in group */ |
1335 | 0 | TSK_DADDR_T dmin = 0; /* first block after inodes */ |
1336 | | |
1337 | | // these blocks are not described in the group descriptors |
1338 | | // sparse |
1339 | 0 | if (a_addr == 0) |
1340 | 0 | return (TSK_FS_BLOCK_FLAG_ENUM) (TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC); |
1341 | 0 | if (a_addr < ext2fs->first_data_block) |
1342 | 0 | return (TSK_FS_BLOCK_FLAG_ENUM) (TSK_FS_BLOCK_FLAG_META | TSK_FS_BLOCK_FLAG_ALLOC); |
1343 | | |
1344 | 0 | grp_num = ext2_dtog_lcl(a_fs, ext2fs->fs, a_addr); |
1345 | | |
1346 | | /* lock access to bmap_buf */ |
1347 | 0 | tsk_take_lock(&ext2fs->lock); |
1348 | | |
1349 | | /* Lookup bitmap if not loaded */ |
1350 | 0 | if (ext2fs_bmap_load(ext2fs, grp_num)) { |
1351 | 0 | tsk_release_lock(&ext2fs->lock); |
1352 | 0 | return TSK_FS_BLOCK_FLAG_UNUSED; |
1353 | 0 | } |
1354 | | |
1355 | | /* |
1356 | | * Be sure to use the right group descriptor information. XXX There |
1357 | | * appears to be an off-by-one discrepancy between bitmap offsets and |
1358 | | * disk block numbers. |
1359 | | * |
1360 | | * Addendum: this offset is controlled by the super block's |
1361 | | * s_first_data_block field. |
1362 | | */ |
1363 | 0 | dbase = ext2_cgbase_lcl(a_fs, ext2fs->fs, grp_num); |
1364 | 0 | flags = (isset(ext2fs->bmap_buf, a_addr - dbase) ? |
1365 | 0 | TSK_FS_BLOCK_FLAG_ALLOC : TSK_FS_BLOCK_FLAG_UNALLOC); |
1366 | | |
1367 | | /* |
1368 | | * Identify meta blocks |
1369 | | * (any blocks that can't be allocated for file/directory data). |
1370 | | * |
1371 | | * XXX With sparse superblock placement, most block groups have the |
1372 | | * block and inode bitmaps where one would otherwise find the backup |
1373 | | * superblock and the backup group descriptor blocks. The inode |
1374 | | * blocks are in the normal place, though. This leaves little gaps |
1375 | | * between the bitmaps and the inode table - and ext2fs will use |
1376 | | * those blocks for file/directory data blocks. So we must properly |
1377 | | * account for those gaps between meta blocks. |
1378 | | * |
1379 | | * Thus, superblocks and group descriptor blocks are sometimes overlaid |
1380 | | * by bitmap blocks. This means that one can still assume that the |
1381 | | * locations of superblocks and group descriptor blocks are reserved. |
1382 | | * They just happen to be reserved for something else :-) |
1383 | | */ |
1384 | |
|
1385 | 0 | if (ext2fs->ext4_grp_buf != NULL) { |
1386 | 0 | dmin = ext4_getu64(a_fs->endian, ext2fs->ext4_grp_buf->bg_inode_table_hi, |
1387 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_lo) + + INODE_TABLE_SIZE(ext2fs); |
1388 | |
|
1389 | 0 | if ((a_addr >= dbase |
1390 | 0 | && a_addr < ext4_getu64(a_fs->endian, |
1391 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_hi, |
1392 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_lo)) |
1393 | 0 | || (a_addr == ext4_getu64(a_fs->endian, |
1394 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_hi, |
1395 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_lo)) |
1396 | 0 | || (a_addr == ext4_getu64(a_fs->endian, |
1397 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_hi, |
1398 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_lo)) |
1399 | 0 | || (a_addr >= ext4_getu64(a_fs->endian, |
1400 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_hi, |
1401 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_lo) |
1402 | 0 | && a_addr < dmin)) |
1403 | 0 | flags |= TSK_FS_BLOCK_FLAG_META; |
1404 | 0 | else |
1405 | 0 | flags |= TSK_FS_BLOCK_FLAG_CONT; |
1406 | |
|
1407 | 0 | } |
1408 | 0 | else { |
1409 | 0 | dmin = |
1410 | 0 | tsk_getu32(a_fs->endian, |
1411 | 0 | ext2fs->grp_buf->bg_inode_table) + INODE_TABLE_SIZE(ext2fs); |
1412 | |
|
1413 | 0 | if ((a_addr >= dbase |
1414 | 0 | && a_addr < tsk_getu32(a_fs->endian, |
1415 | 0 | ext2fs->grp_buf->bg_block_bitmap)) |
1416 | 0 | || (a_addr == tsk_getu32(a_fs->endian, |
1417 | 0 | ext2fs->grp_buf->bg_block_bitmap)) |
1418 | 0 | || (a_addr == tsk_getu32(a_fs->endian, |
1419 | 0 | ext2fs->grp_buf->bg_inode_bitmap)) |
1420 | 0 | || (a_addr >= tsk_getu32(a_fs->endian, |
1421 | 0 | ext2fs->grp_buf->bg_inode_table) |
1422 | 0 | && a_addr < dmin)) |
1423 | 0 | flags |= TSK_FS_BLOCK_FLAG_META; |
1424 | 0 | else |
1425 | 0 | flags |= TSK_FS_BLOCK_FLAG_CONT; |
1426 | 0 | } |
1427 | |
|
1428 | 0 | tsk_release_lock(&ext2fs->lock); |
1429 | 0 | return (TSK_FS_BLOCK_FLAG_ENUM)flags; |
1430 | 0 | } |
1431 | | |
1432 | | |
1433 | | /* ext2fs_block_walk - block iterator |
1434 | | * |
1435 | | * flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_CONT, |
1436 | | * TSK_FS_BLOCK_FLAG_META |
1437 | | * |
1438 | | * Return 1 on error and 0 on success |
1439 | | */ |
1440 | | |
1441 | | uint8_t |
1442 | | ext2fs_block_walk(TSK_FS_INFO * a_fs, TSK_DADDR_T a_start_blk, |
1443 | | TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, |
1444 | | TSK_FS_BLOCK_WALK_CB a_action, void *a_ptr) |
1445 | 0 | { |
1446 | 0 | const char *myname = "extXfs_block_walk"; |
1447 | 0 | TSK_FS_BLOCK *fs_block; |
1448 | 0 | TSK_DADDR_T addr; |
1449 | | |
1450 | | // clean up any error messages that are lying around |
1451 | 0 | tsk_error_reset(); |
1452 | | |
1453 | | /* |
1454 | | * Sanity checks. |
1455 | | */ |
1456 | 0 | if (a_start_blk < a_fs->first_block || a_start_blk > a_fs->last_block) { |
1457 | 0 | tsk_error_reset(); |
1458 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1459 | 0 | tsk_error_set_errstr("%s: start block: %" PRIuDADDR, myname, |
1460 | 0 | a_start_blk); |
1461 | 0 | return 1; |
1462 | 0 | } |
1463 | 0 | if (a_end_blk < a_fs->first_block || a_end_blk > a_fs->last_block |
1464 | 0 | || a_end_blk < a_start_blk) { |
1465 | 0 | tsk_error_reset(); |
1466 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1467 | 0 | tsk_error_set_errstr("%s: end block: %" PRIuDADDR, myname, |
1468 | 0 | a_end_blk); |
1469 | 0 | return 1; |
1470 | 0 | } |
1471 | | |
1472 | | /* Sanity check on a_flags -- make sure at least one ALLOC is set */ |
1473 | 0 | if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) && |
1474 | 0 | ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) { |
1475 | 0 | a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM) |
1476 | 0 | (a_flags | TSK_FS_BLOCK_WALK_FLAG_ALLOC | |
1477 | 0 | TSK_FS_BLOCK_WALK_FLAG_UNALLOC); |
1478 | 0 | } |
1479 | 0 | if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) && |
1480 | 0 | ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) { |
1481 | 0 | a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM) |
1482 | 0 | (a_flags | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META); |
1483 | 0 | } |
1484 | | |
1485 | |
|
1486 | 0 | if ((fs_block = tsk_fs_block_alloc(a_fs)) == NULL) { |
1487 | 0 | return 1; |
1488 | 0 | } |
1489 | | |
1490 | | /* |
1491 | | * Iterate. This is not as tricky as it could be, because the free list |
1492 | | * map covers the entire disk partition, including blocks occupied by |
1493 | | * group descriptor blocks, bit maps, and other non-data blocks. |
1494 | | */ |
1495 | 0 | for (addr = a_start_blk; addr <= a_end_blk; addr++) { |
1496 | 0 | int retval; |
1497 | 0 | int myflags; |
1498 | |
|
1499 | 0 | myflags = ext2fs_block_getflags(a_fs, addr); |
1500 | | |
1501 | | // test if we should call the callback with this one |
1502 | 0 | if ((myflags & TSK_FS_BLOCK_FLAG_META) |
1503 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_META))) |
1504 | 0 | continue; |
1505 | 0 | else if ((myflags & TSK_FS_BLOCK_FLAG_CONT) |
1506 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT))) |
1507 | 0 | continue; |
1508 | 0 | else if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC) |
1509 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC))) |
1510 | 0 | continue; |
1511 | 0 | else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC) |
1512 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC))) |
1513 | 0 | continue; |
1514 | | |
1515 | 0 | if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY) |
1516 | 0 | myflags |= TSK_FS_BLOCK_FLAG_AONLY; |
1517 | |
|
1518 | 0 | if (tsk_fs_block_get_flag(a_fs, fs_block, addr, (TSK_FS_BLOCK_FLAG_ENUM) myflags) == NULL) { |
1519 | 0 | tsk_error_set_errstr2("ext2fs_block_walk: block %" PRIuDADDR, |
1520 | 0 | addr); |
1521 | 0 | tsk_fs_block_free(fs_block); |
1522 | 0 | return 1; |
1523 | 0 | } |
1524 | | |
1525 | 0 | retval = a_action(fs_block, a_ptr); |
1526 | 0 | if (retval == TSK_WALK_STOP) { |
1527 | 0 | break; |
1528 | 0 | } |
1529 | 0 | else if (retval == TSK_WALK_ERROR) { |
1530 | 0 | tsk_fs_block_free(fs_block); |
1531 | 0 | return 1; |
1532 | 0 | } |
1533 | 0 | } |
1534 | | |
1535 | | /* |
1536 | | * Cleanup. |
1537 | | */ |
1538 | 0 | tsk_fs_block_free(fs_block); |
1539 | 0 | return 0; |
1540 | 0 | } |
1541 | | |
1542 | | static uint8_t |
1543 | | ext2fs_fscheck( |
1544 | | [[maybe_unused]] TSK_FS_INFO * fs, |
1545 | | [[maybe_unused]] FILE * hFile) |
1546 | 0 | { |
1547 | 0 | tsk_error_reset(); |
1548 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
1549 | 0 | tsk_error_set_errstr("fscheck not implemented yet for Ext3"); |
1550 | 0 | return 1; |
1551 | 0 | } |
1552 | | |
1553 | | /** \internal |
1554 | | * Add a single extent -- that is, a single data ran -- to the file data attribute. |
1555 | | * @return 0 on success, 1 on error. |
1556 | | */ |
1557 | | static TSK_OFF_T |
1558 | | ext2fs_make_data_run_extent(TSK_FS_INFO * fs_info, TSK_FS_ATTR * fs_attr, |
1559 | | ext2fs_extent * extent) |
1560 | 0 | { |
1561 | 0 | TSK_FS_ATTR_RUN *data_run; |
1562 | 0 | data_run = tsk_fs_attr_run_alloc(); |
1563 | 0 | if (data_run == NULL) { |
1564 | 0 | return 1; |
1565 | 0 | } |
1566 | | |
1567 | 0 | data_run->offset = tsk_getu32(fs_info->endian, extent->ee_block); |
1568 | | |
1569 | | // Check if the extent is initialized or uninitialized |
1570 | 0 | if (tsk_getu16(fs_info->endian, extent->ee_len) <= EXT2_MAX_INIT_EXTENT_LENGTH) { |
1571 | | // Extent is initalized - process normally |
1572 | 0 | data_run->addr = |
1573 | 0 | (((uint32_t)tsk_getu16(fs_info->endian, |
1574 | 0 | extent->ee_start_hi)) << 16) | tsk_getu32(fs_info->endian, |
1575 | 0 | extent->ee_start_lo); |
1576 | 0 | data_run->len = tsk_getu16(fs_info->endian, extent->ee_len); |
1577 | 0 | } |
1578 | 0 | else { |
1579 | | // Extent is uninitialized - make a sparse run |
1580 | 0 | data_run->len = tsk_getu16(fs_info->endian, extent->ee_len) - EXT2_MAX_INIT_EXTENT_LENGTH; |
1581 | 0 | data_run->addr = 0; |
1582 | 0 | data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE; |
1583 | 0 | } |
1584 | | |
1585 | | // save the run |
1586 | 0 | if (tsk_fs_attr_add_run(fs_info, fs_attr, data_run)) { |
1587 | 0 | tsk_fs_attr_run_free(data_run); |
1588 | 0 | return 1; |
1589 | 0 | } |
1590 | | |
1591 | 0 | return 0; |
1592 | 0 | } |
1593 | | |
1594 | | |
1595 | | /** \internal |
1596 | | * Given a block that contains an extent node (which starts with extent_header), |
1597 | | * walk it, and add everything encountered to the appropriate attributes. |
1598 | | * @return 0 on success, 1 on error. |
1599 | | */ |
1600 | | static TSK_OFF_T |
1601 | | ext2fs_make_data_run_extent_index(TSK_FS_INFO * fs_info, |
1602 | | TSK_FS_ATTR * fs_attr, TSK_FS_ATTR * fs_attr_extent, |
1603 | | TSK_DADDR_T idx_block, TSK_DADDR_T * idx_offset) |
1604 | 0 | { |
1605 | 0 | ext2fs_extent_header *header; |
1606 | 0 | TSK_FS_ATTR_RUN *data_run; |
1607 | 0 | uint8_t *buf; |
1608 | 0 | ssize_t cnt; |
1609 | 0 | unsigned int i; |
1610 | | |
1611 | | /* first, read the block specified by the parameter */ |
1612 | 0 | int fs_blocksize = fs_info->block_size; |
1613 | 0 | if ((buf = (uint8_t *) tsk_malloc(fs_blocksize)) == NULL) { |
1614 | 0 | return 1; |
1615 | 0 | } |
1616 | | |
1617 | 0 | cnt = |
1618 | 0 | tsk_fs_read_block(fs_info, idx_block, (char *) buf, fs_blocksize); |
1619 | 0 | if (cnt != fs_blocksize) { |
1620 | 0 | if (cnt >= 0) { |
1621 | 0 | tsk_error_reset(); |
1622 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1623 | 0 | } |
1624 | 0 | tsk_error_set_errstr("ext2fs_make_data_run_extent_index: Block %" |
1625 | 0 | PRIuDADDR, idx_block); |
1626 | 0 | free(buf); |
1627 | 0 | return 1; |
1628 | 0 | } |
1629 | 0 | header = (ext2fs_extent_header *) buf; |
1630 | | |
1631 | | /* add it to the extent attribute */ |
1632 | 0 | if (tsk_getu16(fs_info->endian, header->eh_magic) != 0xF30A) { |
1633 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
1634 | 0 | tsk_error_set_errstr |
1635 | 0 | ("ext2fs_make_data_run_extent_index: extent header magic valid incorrect!"); |
1636 | 0 | free(buf); |
1637 | 0 | return 1; |
1638 | 0 | } |
1639 | | |
1640 | 0 | data_run = tsk_fs_attr_run_alloc(); |
1641 | 0 | if (data_run == NULL) { |
1642 | 0 | free(buf); |
1643 | 0 | return 1; |
1644 | 0 | } |
1645 | 0 | data_run->offset = *idx_offset; |
1646 | 0 | ++*idx_offset; |
1647 | 0 | data_run->addr = idx_block; |
1648 | 0 | data_run->len = 1; |
1649 | |
|
1650 | 0 | if (tsk_fs_attr_add_run(fs_info, fs_attr_extent, data_run)) { |
1651 | 0 | tsk_fs_attr_run_free(data_run); |
1652 | 0 | free(buf); |
1653 | 0 | return 1; |
1654 | 0 | } |
1655 | | |
1656 | | /* process leaf nodes */ |
1657 | 0 | if (tsk_getu16(fs_info->endian, header->eh_depth) == 0) { |
1658 | 0 | uint16_t num_entries = tsk_getu16(fs_info->endian, header->eh_entries); |
1659 | | |
1660 | | // Ensure buf is sufficiently large |
1661 | | // Otherwise extents[i] below can cause an OOB read |
1662 | 0 | if (((unsigned long)fs_blocksize < sizeof(ext2fs_extent_header)) || (num_entries > (fs_blocksize - sizeof(ext2fs_extent_header)) / sizeof(ext2fs_extent))) { |
1663 | 0 | free(buf); |
1664 | 0 | return 1; |
1665 | 0 | } |
1666 | 0 | ext2fs_extent *extents = (ext2fs_extent *) (header + 1); |
1667 | 0 | for (i = 0; i < num_entries; i++) { |
1668 | 0 | ext2fs_extent extent = extents[i]; |
1669 | 0 | if (ext2fs_make_data_run_extent(fs_info, fs_attr, &extent)) { |
1670 | 0 | free(buf); |
1671 | 0 | return 1; |
1672 | 0 | } |
1673 | 0 | } |
1674 | 0 | } |
1675 | | /* recurse on interior nodes */ |
1676 | 0 | else { |
1677 | 0 | uint16_t num_entries = tsk_getu16(fs_info->endian, header->eh_entries); |
1678 | | |
1679 | | // Ensure buf is sufficiently large |
1680 | | // Otherwise indices[i] below can cause an OOB read |
1681 | 0 | if (((unsigned long)fs_blocksize < sizeof(ext2fs_extent_header)) || (num_entries > (fs_blocksize - sizeof(ext2fs_extent_header)) / sizeof(ext2fs_extent_idx))) { |
1682 | 0 | free(buf); |
1683 | 0 | return 1; |
1684 | 0 | } |
1685 | 0 | ext2fs_extent_idx *indices = (ext2fs_extent_idx *) (header + 1); |
1686 | 0 | for (i = 0; i < num_entries; i++) { |
1687 | 0 | ext2fs_extent_idx *index = &indices[i]; |
1688 | 0 | TSK_DADDR_T child_block = |
1689 | 0 | (((uint32_t) tsk_getu16(fs_info->endian, |
1690 | 0 | index->ei_leaf_hi)) << 16) | tsk_getu32(fs_info-> |
1691 | 0 | endian, index->ei_leaf_lo); |
1692 | 0 | if (ext2fs_make_data_run_extent_index(fs_info, fs_attr, |
1693 | 0 | fs_attr_extent, child_block, idx_offset)) { |
1694 | 0 | free(buf); |
1695 | 0 | return 1; |
1696 | 0 | } |
1697 | 0 | } |
1698 | 0 | } |
1699 | | |
1700 | 0 | free(buf); |
1701 | 0 | return 0; |
1702 | 0 | } |
1703 | | |
1704 | | /** \internal |
1705 | | * Get the number of extent blocks rooted at the given extent_header. |
1706 | | * The count does not include the extent header passed as a parameter. |
1707 | | * |
1708 | | * @return the number of extent blocks, or -1 on error. |
1709 | | */ |
1710 | | static int32_t |
1711 | | ext2fs_extent_tree_index_count(TSK_FS_INFO * fs_info, |
1712 | | TSK_FS_META * fs_meta, ext2fs_extent_header * header, int recursion_depth) |
1713 | 0 | { |
1714 | 0 | int fs_blocksize = fs_info->block_size; |
1715 | 0 | ext2fs_extent_idx *indices; |
1716 | 0 | int count = 0; |
1717 | 0 | uint8_t *buf; |
1718 | 0 | int i; |
1719 | | |
1720 | | // 32 is an arbitrary chosen value. |
1721 | 0 | if (recursion_depth > 32) { |
1722 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
1723 | 0 | tsk_error_set_errstr |
1724 | 0 | ("ext2fs_load_attrs: exceeded maximum recursion depth!"); |
1725 | 0 | return -1; |
1726 | 0 | } |
1727 | 0 | if (tsk_getu16(fs_info->endian, header->eh_magic) != 0xF30A) { |
1728 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
1729 | 0 | tsk_error_set_errstr |
1730 | 0 | ("ext2fs_load_attrs: extent header magic valid incorrect!"); |
1731 | 0 | return -1; |
1732 | 0 | } |
1733 | | |
1734 | 0 | if (tsk_getu16(fs_info->endian, header->eh_depth) == 0) { |
1735 | 0 | return 0; |
1736 | 0 | } |
1737 | | |
1738 | 0 | buf = (uint8_t *) tsk_malloc(fs_blocksize); |
1739 | 0 | if (buf == NULL) { |
1740 | 0 | return -1; |
1741 | 0 | } |
1742 | | |
1743 | 0 | indices = (ext2fs_extent_idx *) (header + 1); |
1744 | 0 | for (i = 0; i < tsk_getu16(fs_info->endian, header->eh_entries); i++) { |
1745 | 0 | ext2fs_extent_idx *index = &indices[i]; |
1746 | 0 | TSK_DADDR_T block = |
1747 | 0 | (((uint32_t) tsk_getu16(fs_info->endian, |
1748 | 0 | index->ei_leaf_hi)) << 16) | tsk_getu32(fs_info-> |
1749 | 0 | endian, index->ei_leaf_lo); |
1750 | 0 | ssize_t cnt = |
1751 | 0 | tsk_fs_read_block(fs_info, block, (char *) buf, fs_blocksize); |
1752 | 0 | int ret; |
1753 | |
|
1754 | 0 | if (cnt != fs_blocksize) { |
1755 | 0 | if (cnt >= 0) { |
1756 | 0 | tsk_error_reset(); |
1757 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1758 | 0 | } |
1759 | 0 | tsk_error_set_errstr2("ext2fs_extent_tree_index_count: Block %" |
1760 | 0 | PRIuDADDR, block); |
1761 | 0 | free(buf); |
1762 | 0 | return -1; |
1763 | 0 | } |
1764 | | |
1765 | 0 | if ((ret = |
1766 | 0 | ext2fs_extent_tree_index_count(fs_info, fs_meta, |
1767 | 0 | (ext2fs_extent_header *) buf, recursion_depth + 1)) < 0) { |
1768 | 0 | free(buf); |
1769 | 0 | return -1; |
1770 | 0 | } |
1771 | 0 | count += ret; |
1772 | 0 | count++; |
1773 | 0 | } |
1774 | | |
1775 | 0 | free(buf); |
1776 | 0 | return count; |
1777 | 0 | } |
1778 | | |
1779 | | /** \internal |
1780 | | * If the file length is longer than what is in the attr runs, add a sparse |
1781 | | * data run to cover the rest of the file. |
1782 | | * |
1783 | | * @return 0 if successful or 1 on error. |
1784 | | */ |
1785 | | static uint8_t |
1786 | 0 | ext2fs_handle_implicit_sparse_data_run(TSK_FS_INFO * fs_info, TSK_FS_ATTR * fs_attr) { |
1787 | 0 | TSK_FS_FILE *fs_file = fs_attr->fs_file; |
1788 | |
|
1789 | 0 | if (fs_file == NULL) { |
1790 | 0 | return 1; |
1791 | 0 | } |
1792 | | |
1793 | 0 | TSK_DADDR_T end_of_runs; |
1794 | 0 | TSK_DADDR_T total_file_blocks = roundup(fs_file->meta->size, fs_info->block_size) / fs_info->block_size; |
1795 | |
|
1796 | 0 | if (fs_attr->nrd.run_end) { |
1797 | 0 | end_of_runs = fs_attr->nrd.run_end->offset + fs_attr->nrd.run_end->len; |
1798 | 0 | } |
1799 | 0 | else { |
1800 | 0 | end_of_runs = 0; |
1801 | 0 | } |
1802 | |
|
1803 | 0 | if (end_of_runs < total_file_blocks) { |
1804 | | // Make sparse run. |
1805 | 0 | TSK_FS_ATTR_RUN *data_run; |
1806 | 0 | data_run = tsk_fs_attr_run_alloc(); |
1807 | 0 | if (data_run == NULL) { |
1808 | 0 | return 1; |
1809 | 0 | } |
1810 | 0 | data_run->offset = end_of_runs; |
1811 | 0 | data_run->addr = 0; |
1812 | 0 | data_run->len = total_file_blocks - end_of_runs; |
1813 | 0 | data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE; |
1814 | | |
1815 | | // Save the run. |
1816 | 0 | if (tsk_fs_attr_add_run(fs_info, fs_attr, data_run)) { |
1817 | |
|
1818 | 0 | return 1; |
1819 | 0 | } |
1820 | 0 | } |
1821 | 0 | return 0; |
1822 | 0 | } |
1823 | | |
1824 | | /** |
1825 | | * \internal |
1826 | | * Loads attribute for Ext4 Extents-based storage method. |
1827 | | * @param fs_file File system to analyze |
1828 | | * @returns 0 on success, 1 otherwise |
1829 | | */ |
1830 | | static uint8_t |
1831 | | ext4_load_attrs_extents(TSK_FS_FILE *fs_file) |
1832 | 0 | { |
1833 | 0 | TSK_FS_META *fs_meta = fs_file->meta; |
1834 | 0 | TSK_FS_INFO *fs_info = fs_file->fs_info; |
1835 | 0 | TSK_OFF_T length = 0; |
1836 | 0 | TSK_FS_ATTR *fs_attr; |
1837 | 0 | int i; |
1838 | 0 | ext2fs_extent *extents = NULL; |
1839 | 0 | ext2fs_extent_idx *indices = NULL; |
1840 | |
|
1841 | 0 | ext2fs_extent_header *header = (ext2fs_extent_header *) fs_meta->content_ptr; |
1842 | 0 | uint16_t num_entries = tsk_getu16(fs_info->endian, header->eh_entries); |
1843 | 0 | uint16_t depth = tsk_getu16(fs_info->endian, header->eh_depth); |
1844 | |
|
1845 | 0 | if (tsk_getu16(fs_info->endian, header->eh_magic) != 0xF30A) { |
1846 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
1847 | 0 | tsk_error_set_errstr |
1848 | 0 | ("ext2fs_load_attrs: extent header magic valid incorrect!"); |
1849 | 0 | return 1; |
1850 | 0 | } |
1851 | | |
1852 | 0 | if ((fs_meta->attr != NULL) |
1853 | 0 | && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) { |
1854 | 0 | return 0; |
1855 | 0 | } |
1856 | 0 | else if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) { |
1857 | 0 | return 1; |
1858 | 0 | } |
1859 | | |
1860 | 0 | if (fs_meta->attr != NULL) { |
1861 | 0 | tsk_fs_attrlist_markunused(fs_meta->attr); |
1862 | 0 | } |
1863 | 0 | else { |
1864 | 0 | fs_meta->attr = tsk_fs_attrlist_alloc(); |
1865 | 0 | } |
1866 | |
|
1867 | 0 | if (TSK_FS_TYPE_ISEXT(fs_info->ftype) == 0) { |
1868 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
1869 | 0 | tsk_error_set_errstr |
1870 | 0 | ("ext2fs_load_attr: Called with non-ExtX file system: %x", |
1871 | 0 | fs_info->ftype); |
1872 | 0 | return 1; |
1873 | 0 | } |
1874 | | |
1875 | 0 | length = roundup(fs_meta->size, fs_info->block_size); |
1876 | |
|
1877 | 0 | if ((fs_attr = |
1878 | 0 | tsk_fs_attrlist_getnew(fs_meta->attr, |
1879 | 0 | TSK_FS_ATTR_NONRES)) == NULL) { |
1880 | 0 | return 1; |
1881 | 0 | } |
1882 | | |
1883 | 0 | if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, NULL, |
1884 | 0 | TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, |
1885 | 0 | fs_meta->size, fs_meta->size, length, TSK_FS_ATTR_FLAG_NONE, 0)) { |
1886 | 0 | return 1; |
1887 | 0 | } |
1888 | | |
1889 | 0 | if (num_entries == 0) { |
1890 | 0 | if (fs_meta->size == 0) { |
1891 | | // Empty file |
1892 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
1893 | 0 | return 0; |
1894 | 0 | } |
1895 | | |
1896 | | // The entire file is sparse |
1897 | 0 | if (ext2fs_handle_implicit_sparse_data_run(fs_info, fs_attr)) { |
1898 | 0 | return 1; |
1899 | 0 | } |
1900 | | |
1901 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
1902 | 0 | return 0; |
1903 | 0 | } |
1904 | | |
1905 | 0 | if (depth == 0) { /* leaf node */ |
1906 | | // Ensure fs_meta->content_ptr is sufficiently large |
1907 | | // Otherwise extents[i] below can cause an OOB read |
1908 | 0 | if ((fs_meta->content_len < sizeof(ext2fs_extent_header)) || (num_entries > (fs_meta->content_len - sizeof(ext2fs_extent_header)) / sizeof(ext2fs_extent))) { |
1909 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
1910 | 0 | tsk_error_set_errstr |
1911 | 0 | ("ext2fs_load_attr: Inode reports too many extents"); |
1912 | 0 | return 1; |
1913 | 0 | } |
1914 | | |
1915 | 0 | extents = (ext2fs_extent *) (header + 1); |
1916 | 0 | for (i = 0; i < num_entries; i++) { |
1917 | 0 | ext2fs_extent extent = extents[i]; |
1918 | 0 | if (ext2fs_make_data_run_extent(fs_info, fs_attr, &extent)) { |
1919 | 0 | return 1; |
1920 | 0 | } |
1921 | 0 | } |
1922 | 0 | } |
1923 | 0 | else { /* interior node */ |
1924 | 0 | TSK_FS_ATTR *fs_attr_extent; |
1925 | 0 | int32_t extent_index_size; |
1926 | 0 | TSK_DADDR_T idx_offset; |
1927 | | |
1928 | | // Ensure fs_meta->content_ptr is sufficiently large |
1929 | | // Otherwise indices[i] below can cause an OOB read |
1930 | 0 | if ((fs_meta->content_len < sizeof(ext2fs_extent_header)) || (num_entries > (fs_meta->content_len - sizeof(ext2fs_extent_header)) / sizeof(ext2fs_extent_idx))) { |
1931 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
1932 | 0 | tsk_error_set_errstr |
1933 | 0 | ("ext2fs_load_attr: Inode reports too many extent indices"); |
1934 | 0 | return 1; |
1935 | 0 | } |
1936 | | |
1937 | 0 | if ((fs_attr_extent = |
1938 | 0 | tsk_fs_attrlist_getnew(fs_meta->attr, |
1939 | 0 | TSK_FS_ATTR_NONRES)) == NULL) { |
1940 | 0 | return 1; |
1941 | 0 | } |
1942 | | |
1943 | 0 | extent_index_size = |
1944 | 0 | ext2fs_extent_tree_index_count(fs_info, fs_meta, header, 0); |
1945 | 0 | if (extent_index_size < 0) { |
1946 | 0 | return 1; |
1947 | 0 | } |
1948 | | |
1949 | 0 | if (tsk_fs_attr_set_run(fs_file, fs_attr_extent, NULL, NULL, |
1950 | 0 | TSK_FS_ATTR_TYPE_UNIX_EXTENT, TSK_FS_ATTR_ID_DEFAULT, |
1951 | 0 | fs_info->block_size * extent_index_size, |
1952 | 0 | fs_info->block_size * extent_index_size, |
1953 | 0 | fs_info->block_size * extent_index_size, TSK_FS_ATTR_FLAG_NONE, 0)) { |
1954 | 0 | return 1; |
1955 | 0 | } |
1956 | | |
1957 | 0 | indices = (ext2fs_extent_idx *) (header + 1); |
1958 | 0 | idx_offset = 0; |
1959 | 0 | for (i = 0; i < num_entries; i++) { |
1960 | 0 | ext2fs_extent_idx *index = &indices[i]; |
1961 | 0 | TSK_DADDR_T child_block = |
1962 | 0 | (((uint32_t) tsk_getu16(fs_info->endian, |
1963 | 0 | index-> |
1964 | 0 | ei_leaf_hi)) << 16) | tsk_getu32(fs_info-> |
1965 | 0 | endian, index->ei_leaf_lo); |
1966 | 0 | if (ext2fs_make_data_run_extent_index(fs_info, fs_attr, |
1967 | 0 | fs_attr_extent, child_block, &idx_offset)) { |
1968 | 0 | return 1; |
1969 | 0 | } |
1970 | 0 | } |
1971 | 0 | } |
1972 | | |
1973 | | // There may be implicit sparse blocks at the end of the file |
1974 | 0 | if (ext2fs_handle_implicit_sparse_data_run(fs_info, fs_attr)) { |
1975 | 0 | return 1; |
1976 | 0 | } |
1977 | | |
1978 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
1979 | |
|
1980 | 0 | return 0; |
1981 | 0 | } |
1982 | | |
1983 | | /** \internal |
1984 | | * Add the data runs and extents to the file attributes. |
1985 | | * |
1986 | | * @param fs_file File system to analyze |
1987 | | * @returns 0 on success, 1 otherwise |
1988 | | */ |
1989 | | static uint8_t |
1990 | | ext2fs_load_attrs(TSK_FS_FILE * fs_file) |
1991 | 0 | { |
1992 | | /* EXT4 extents-based storage is dealt with differently than |
1993 | | * the traditional pointer lists. */ |
1994 | 0 | if (fs_file->meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS) { |
1995 | 0 | return ext4_load_attrs_extents(fs_file); |
1996 | 0 | } |
1997 | 0 | else if (fs_file->meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_INLINE) { |
1998 | | // Inline attributes are loaded in dinode_copy |
1999 | 0 | return 0; |
2000 | 0 | } |
2001 | 0 | else { |
2002 | 0 | return tsk_fs_unix_make_data_run(fs_file); |
2003 | 0 | } |
2004 | 0 | } |
2005 | | |
2006 | | |
2007 | | static void |
2008 | | ext4_fsstat_datablock_helper(TSK_FS_INFO * fs, FILE * hFile, |
2009 | | unsigned int i, TSK_DADDR_T cg_base, int gd_size) |
2010 | 0 | { |
2011 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
2012 | 0 | ext2fs_sb *sb = ext2fs->fs; |
2013 | 0 | unsigned int gpfbg = (1 << sb->s_log_groups_per_flex); |
2014 | 0 | unsigned int ibpg, gd_blocks; |
2015 | 0 | unsigned int num_flex_bg, curr_flex_bg; |
2016 | 0 | uint64_t last_block; |
2017 | 0 | ext4fs_gd *ext4_gd = ext2fs->ext4_grp_buf; |
2018 | 0 | uint64_t db_offset = 0; |
2019 | |
|
2020 | 0 | if (ext4_gd == NULL) { |
2021 | 0 | return; |
2022 | 0 | } |
2023 | | |
2024 | | #ifdef Ext4_DBG |
2025 | | printf("\nDEBUG 64bit:%d, gd_size %d, combined %d\n", |
2026 | | EXT2FS_HAS_INCOMPAT_FEATURE(fs, sb, EXT2FS_FEATURE_INCOMPAT_64BIT), |
2027 | | gd_size >= 64, |
2028 | | EXT2FS_HAS_INCOMPAT_FEATURE(fs, sb, EXT2FS_FEATURE_INCOMPAT_64BIT) |
2029 | | && gd_size >= 64); |
2030 | | #endif |
2031 | | /* number of blocks the inodes consume */ |
2032 | 0 | ibpg = |
2033 | 0 | (tsk_getu32(fs->endian, |
2034 | 0 | sb->s_inodes_per_group) * ext2fs->inode_size + fs->block_size - |
2035 | 0 | 1) / fs->block_size; |
2036 | | /* number of blocks group descriptors consume */ |
2037 | 0 | gd_blocks = |
2038 | 0 | (unsigned int)((gd_size * ext2fs->groups_count + fs->block_size - |
2039 | 0 | 1) / fs->block_size); |
2040 | 0 | num_flex_bg = (unsigned int)(ext2fs->groups_count / gpfbg); |
2041 | 0 | if (ext2fs->groups_count % gpfbg) |
2042 | 0 | num_flex_bg++; |
2043 | 0 | curr_flex_bg = i / gpfbg; |
2044 | |
|
2045 | 0 | last_block = |
2046 | 0 | cg_base + tsk_getu32(fs->endian, sb->s_blocks_per_group) - 1; |
2047 | 0 | if (last_block > fs->last_block) { |
2048 | 0 | last_block = fs->last_block; |
2049 | 0 | } |
2050 | | |
2051 | | //DEBUG printf("ibpg %d cur_flex: %d, flex_bgs: %d : %d, %d",ibpg, i/gpfbg, num_flex_bg, ext2fs->groups_count/gpfbg, ext2fs->groups_count%gpfbg); |
2052 | |
|
2053 | | #ifdef Ext4_DBG |
2054 | | printf("\nDEBUG: Flex BG PROCESSING cg_base: %" PRIuDADDR |
2055 | | ", gpfbg: %d, ibpg: %d \n", cg_base, gpfbg, ibpg); |
2056 | | #endif |
2057 | | /*If this is the 1st bg in a flex bg then it contains the bitmaps and inode tables */ |
2058 | 0 | if (i % gpfbg == 0) { |
2059 | 0 | if (curr_flex_bg == (num_flex_bg - 1)) { |
2060 | 0 | unsigned int num_groups = 0; |
2061 | 0 | unsigned int left_over = 0; |
2062 | |
|
2063 | 0 | num_groups = (unsigned int) |
2064 | 0 | (fs->last_block / tsk_getu32(fs->endian, |
2065 | 0 | sb->s_blocks_per_group)); |
2066 | 0 | if (num_groups % tsk_getu32(fs->endian, |
2067 | 0 | sb->s_blocks_per_group)) |
2068 | 0 | num_groups++; |
2069 | 0 | left_over = (num_groups % gpfbg); |
2070 | |
|
2071 | 0 | tsk_fprintf(hFile, " Uninit Data Bitmaps: "); |
2072 | 0 | tsk_fprintf(hFile, "%" PRIu64 " - %" PRIu64 "\n", |
2073 | 0 | ext4_getu64(fs->endian, ext4_gd->bg_block_bitmap_hi, |
2074 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_lo) |
2075 | 0 | + (left_over), ext4_getu64(fs->endian, |
2076 | 0 | ext4_gd->bg_block_bitmap_hi, |
2077 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_lo) |
2078 | 0 | + gpfbg - 1); |
2079 | 0 | tsk_fprintf(hFile, " Uninit Inode Bitmaps: "); |
2080 | 0 | tsk_fprintf(hFile, "%" PRIu64 " - %" PRIu64 "\n", |
2081 | 0 | ext4_getu64(fs->endian, ext4_gd->bg_inode_bitmap_hi, |
2082 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_lo) |
2083 | 0 | + (left_over), ext4_getu64(fs->endian, |
2084 | 0 | ext4_gd->bg_inode_bitmap_hi, |
2085 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_lo) |
2086 | 0 | + gpfbg - 1); |
2087 | 0 | tsk_fprintf(hFile, " Uninit Inode Table: "); |
2088 | 0 | tsk_fprintf(hFile, "%" PRIu64 " - %" PRIu64 "\n", |
2089 | 0 | ext4_getu64(fs->endian, ext4_gd->bg_inode_table_hi, |
2090 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_lo) |
2091 | 0 | + ((left_over) * ibpg), ext4_getu64(fs->endian, |
2092 | 0 | ext4_gd->bg_inode_table_hi, |
2093 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_lo) |
2094 | 0 | + (gpfbg * ibpg) - 1); |
2095 | |
|
2096 | 0 | } |
2097 | 0 | tsk_fprintf(hFile, " Data Blocks: "); |
2098 | 0 | db_offset = 0; |
2099 | 0 | if (ext2fs_is_super_bg(tsk_getu32(fs->endian, |
2100 | 0 | sb->s_feature_ro_compat), i)) { |
2101 | 0 | db_offset = cg_base + (gpfbg * 2) //To account for the bitmaps |
2102 | 0 | + (ibpg * gpfbg) //Combined inode tables |
2103 | 0 | + tsk_getu16(fs->endian, ext2fs->fs->pad_or_gdt.s_reserved_gdt_blocks) + gd_blocks //group descriptors |
2104 | 0 | + 1; //superblock |
2105 | 0 | } |
2106 | 0 | else { |
2107 | 0 | db_offset = cg_base + (gpfbg * 2) //To account for the bitmaps |
2108 | 0 | + (ibpg * gpfbg); //Combined inode tables |
2109 | 0 | } |
2110 | 0 | tsk_fprintf(hFile, "%" PRIuDADDR " - %" PRIuDADDR "\n", |
2111 | 0 | db_offset, last_block); |
2112 | 0 | } |
2113 | 0 | else { |
2114 | 0 | tsk_fprintf(hFile, " Data Blocks: "); |
2115 | 0 | db_offset = 0; |
2116 | 0 | if (ext2fs_is_super_bg(tsk_getu32(fs->endian, |
2117 | 0 | sb->s_feature_ro_compat), i)) { |
2118 | 0 | db_offset = cg_base + tsk_getu16(fs->endian, ext2fs->fs->pad_or_gdt.s_reserved_gdt_blocks) + gd_blocks //group descriptors |
2119 | 0 | + 1; //superblock |
2120 | 0 | } |
2121 | 0 | else { |
2122 | 0 | db_offset = cg_base; |
2123 | 0 | } |
2124 | 0 | tsk_fprintf(hFile, "%" PRIuDADDR " - %" PRIuDADDR "\n", |
2125 | 0 | db_offset, last_block); |
2126 | 0 | } |
2127 | |
|
2128 | 0 | } |
2129 | | |
2130 | | /** |
2131 | | * Print details about the file system to a file handle. |
2132 | | * |
2133 | | * @param fs File system to print details on |
2134 | | * @param hFile File handle to print text to |
2135 | | * |
2136 | | * @returns 1 on error and 0 on success |
2137 | | */ |
2138 | | static uint8_t |
2139 | | ext2fs_fsstat(TSK_FS_INFO * fs, FILE * hFile) |
2140 | 0 | { |
2141 | 0 | unsigned int i; |
2142 | | // unsigned int gpfbg; |
2143 | | // unsigned int gd_blocks; |
2144 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
2145 | 0 | ext2fs_sb *sb = ext2fs->fs; |
2146 | 0 | int ibpg; |
2147 | 0 | int gd_size; |
2148 | 0 | time_t tmptime; |
2149 | 0 | char timeBuf[128]; |
2150 | 0 | const char *tmptypename; |
2151 | | |
2152 | | |
2153 | | // clean up any error messages that are lying around |
2154 | 0 | tsk_error_reset(); |
2155 | |
|
2156 | 0 | tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n"); |
2157 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
2158 | |
|
2159 | 0 | switch (fs->ftype) { |
2160 | 0 | case TSK_FS_TYPE_EXT3: |
2161 | 0 | tmptypename = "Ext3"; |
2162 | 0 | gd_size = sizeof(ext2fs_gd); |
2163 | 0 | break; |
2164 | 0 | case TSK_FS_TYPE_EXT4: |
2165 | 0 | tmptypename = "Ext4"; |
2166 | 0 | if (EXT2FS_HAS_INCOMPAT_FEATURE(fs, sb, |
2167 | 0 | EXT2FS_FEATURE_INCOMPAT_64BIT)) |
2168 | 0 | gd_size = sizeof(ext4fs_gd); |
2169 | 0 | else |
2170 | 0 | gd_size = sizeof(ext2fs_gd); |
2171 | 0 | break; |
2172 | 0 | default: |
2173 | 0 | tmptypename = "Ext2"; |
2174 | 0 | gd_size = sizeof(ext2fs_gd); |
2175 | 0 | } |
2176 | 0 | tsk_fprintf(hFile, "File System Type: %s\n", tmptypename); |
2177 | 0 | tsk_fprintf(hFile, "Volume Name: %s\n", sb->s_volume_name); |
2178 | 0 | tsk_fprintf(hFile, "Volume ID: %" PRIx64 "%" PRIx64 "\n", |
2179 | 0 | tsk_getu64(fs->endian, &sb->s_uuid[8]), tsk_getu64(fs->endian, |
2180 | 0 | &sb->s_uuid[0])); |
2181 | |
|
2182 | 0 | tmptime = tsk_getu32(fs->endian, sb->s_wtime); |
2183 | 0 | tsk_fprintf(hFile, "\nLast Written at: %s\n", |
2184 | 0 | (tmptime > 0) ? tsk_fs_time_to_str(tmptime, timeBuf) : "empty"); |
2185 | 0 | tmptime = tsk_getu32(fs->endian, sb->s_lastcheck); |
2186 | 0 | tsk_fprintf(hFile, "Last Checked at: %s\n", |
2187 | 0 | (tmptime > 0) ? tsk_fs_time_to_str(tmptime, timeBuf) : "empty"); |
2188 | |
|
2189 | 0 | tmptime = tsk_getu32(fs->endian, sb->s_mtime); |
2190 | 0 | tsk_fprintf(hFile, "\nLast Mounted at: %s\n", |
2191 | 0 | (tmptime > 0) ? tsk_fs_time_to_str(tmptime, timeBuf) : "empty"); |
2192 | | |
2193 | | /* State of the file system */ |
2194 | 0 | if (tsk_getu16(fs->endian, sb->s_state) & EXT2FS_STATE_VALID) |
2195 | 0 | tsk_fprintf(hFile, "Unmounted properly\n"); |
2196 | 0 | else |
2197 | 0 | tsk_fprintf(hFile, "Unmounted Improperly\n"); |
2198 | |
|
2199 | 0 | if (sb->s_last_mounted[0] != '\0') |
2200 | 0 | tsk_fprintf(hFile, "Last mounted on: %s\n", sb->s_last_mounted); |
2201 | |
|
2202 | 0 | tsk_fprintf(hFile, "\nSource OS: "); |
2203 | 0 | switch (tsk_getu32(fs->endian, sb->s_creator_os)) { |
2204 | 0 | case EXT2FS_OS_LINUX: |
2205 | 0 | tsk_fprintf(hFile, "Linux\n"); |
2206 | 0 | break; |
2207 | 0 | case EXT2FS_OS_HURD: |
2208 | 0 | tsk_fprintf(hFile, "HURD\n"); |
2209 | 0 | break; |
2210 | 0 | case EXT2FS_OS_MASIX: |
2211 | 0 | tsk_fprintf(hFile, "MASIX\n"); |
2212 | 0 | break; |
2213 | 0 | case EXT2FS_OS_FREEBSD: |
2214 | 0 | tsk_fprintf(hFile, "FreeBSD\n"); |
2215 | 0 | break; |
2216 | 0 | case EXT2FS_OS_LITES: |
2217 | 0 | tsk_fprintf(hFile, "LITES\n"); |
2218 | 0 | break; |
2219 | 0 | default: |
2220 | 0 | tsk_fprintf(hFile, "%" PRIx32 "\n", tsk_getu32(fs->endian, |
2221 | 0 | sb->s_creator_os)); |
2222 | 0 | break; |
2223 | 0 | } |
2224 | | |
2225 | 0 | if (tsk_getu32(fs->endian, sb->s_rev_level) == EXT2FS_REV_ORIG) |
2226 | 0 | tsk_fprintf(hFile, "Static Structure\n"); |
2227 | 0 | else |
2228 | 0 | tsk_fprintf(hFile, "Dynamic Structure\n"); |
2229 | | |
2230 | | |
2231 | | /* add features */ |
2232 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_compat)) { |
2233 | 0 | tsk_fprintf(hFile, "Compat Features: "); |
2234 | |
|
2235 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_compat) & |
2236 | 0 | EXT2FS_FEATURE_COMPAT_DIR_PREALLOC) |
2237 | 0 | tsk_fprintf(hFile, "Dir Prealloc, "); |
2238 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_compat) & |
2239 | 0 | EXT2FS_FEATURE_COMPAT_IMAGIC_INODES) |
2240 | 0 | tsk_fprintf(hFile, "iMagic inodes, "); |
2241 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_compat) & |
2242 | 0 | EXT2FS_FEATURE_COMPAT_HAS_JOURNAL) |
2243 | 0 | tsk_fprintf(hFile, "Journal, "); |
2244 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_compat) & |
2245 | 0 | EXT2FS_FEATURE_COMPAT_EXT_ATTR) |
2246 | 0 | tsk_fprintf(hFile, "Ext Attributes, "); |
2247 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_compat) & |
2248 | 0 | EXT2FS_FEATURE_COMPAT_RESIZE_INO) |
2249 | 0 | tsk_fprintf(hFile, "Resize Inode, "); |
2250 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_compat) & |
2251 | 0 | EXT2FS_FEATURE_COMPAT_DIR_INDEX) |
2252 | 0 | tsk_fprintf(hFile, "Dir Index"); |
2253 | |
|
2254 | 0 | tsk_fprintf(hFile, "\n"); |
2255 | 0 | } |
2256 | |
|
2257 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat)) { |
2258 | 0 | tsk_fprintf(hFile, "InCompat Features: "); |
2259 | |
|
2260 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2261 | 0 | EXT2FS_FEATURE_INCOMPAT_COMPRESSION) |
2262 | 0 | tsk_fprintf(hFile, "Compression, "); |
2263 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2264 | 0 | EXT2FS_FEATURE_INCOMPAT_FILETYPE) |
2265 | 0 | tsk_fprintf(hFile, "Filetype, "); |
2266 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2267 | 0 | EXT2FS_FEATURE_INCOMPAT_RECOVER) |
2268 | 0 | tsk_fprintf(hFile, "Needs Recovery, "); |
2269 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2270 | 0 | EXT2FS_FEATURE_INCOMPAT_JOURNAL_DEV) |
2271 | 0 | tsk_fprintf(hFile, "Journal Dev"); |
2272 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2273 | 0 | EXT2FS_FEATURE_INCOMPAT_META_BG) |
2274 | 0 | tsk_fprintf(hFile, "Meta Block Groups, "); |
2275 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2276 | 0 | EXT2FS_FEATURE_INCOMPAT_EXTENTS) |
2277 | 0 | tsk_fprintf(hFile, "Extents, "); |
2278 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2279 | 0 | EXT2FS_FEATURE_INCOMPAT_64BIT) |
2280 | 0 | tsk_fprintf(hFile, "64bit, "); |
2281 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2282 | 0 | EXT2FS_FEATURE_INCOMPAT_MMP) |
2283 | 0 | tsk_fprintf(hFile, "Multiple Mount Protection, "); |
2284 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2285 | 0 | EXT2FS_FEATURE_INCOMPAT_FLEX_BG) |
2286 | 0 | tsk_fprintf(hFile, "Flexible Block Groups, "); |
2287 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2288 | 0 | EXT2FS_FEATURE_INCOMPAT_EA_INODE) |
2289 | 0 | tsk_fprintf(hFile, "Extended Attributes, "); |
2290 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_incompat) & |
2291 | 0 | EXT2FS_FEATURE_INCOMPAT_DIRDATA) |
2292 | 0 | tsk_fprintf(hFile, "Directory Entry Data"); |
2293 | |
|
2294 | 0 | tsk_fprintf(hFile, "\n"); |
2295 | 0 | } |
2296 | |
|
2297 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_ro_compat)) { |
2298 | 0 | tsk_fprintf(hFile, "Read Only Compat Features: "); |
2299 | |
|
2300 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_ro_compat) & |
2301 | 0 | EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER) |
2302 | 0 | tsk_fprintf(hFile, "Sparse Super, "); |
2303 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_ro_compat) & |
2304 | 0 | EXT2FS_FEATURE_RO_COMPAT_LARGE_FILE) |
2305 | 0 | tsk_fprintf(hFile, "Large File, "); |
2306 | 0 | if (EXT2FS_HAS_RO_COMPAT_FEATURE(fs, sb, |
2307 | 0 | EXT2FS_FEATURE_RO_COMPAT_HUGE_FILE)) |
2308 | 0 | tsk_fprintf(hFile, "Huge File, "); |
2309 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_ro_compat) & |
2310 | 0 | EXT2FS_FEATURE_RO_COMPAT_BTREE_DIR) |
2311 | 0 | tsk_fprintf(hFile, "Btree Dir, "); |
2312 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_ro_compat) & |
2313 | 0 | EXT2FS_FEATURE_RO_COMPAT_EXTRA_ISIZE) |
2314 | 0 | tsk_fprintf(hFile, "Extra Inode Size"); |
2315 | |
|
2316 | 0 | tsk_fprintf(hFile, "\n"); |
2317 | 0 | } |
2318 | | |
2319 | | /* Print journal information */ |
2320 | 0 | if (tsk_getu32(fs->endian, sb->s_feature_compat) & |
2321 | 0 | EXT2FS_FEATURE_COMPAT_HAS_JOURNAL) { |
2322 | |
|
2323 | 0 | tsk_fprintf(hFile, "\nJournal ID: %" PRIx64 "%" PRIx64 "\n", |
2324 | 0 | tsk_getu64(fs->endian, &sb->s_journal_uuid[8]), |
2325 | 0 | tsk_getu64(fs->endian, &sb->s_journal_uuid[0])); |
2326 | |
|
2327 | 0 | if (tsk_getu32(fs->endian, sb->s_journal_inum) != 0) |
2328 | 0 | tsk_fprintf(hFile, "Journal Inode: %" PRIu32 "\n", |
2329 | 0 | tsk_getu32(fs->endian, sb->s_journal_inum)); |
2330 | |
|
2331 | 0 | if (tsk_getu32(fs->endian, sb->s_journal_dev) != 0) |
2332 | 0 | tsk_fprintf(hFile, "Journal Device: %" PRIu32 "\n", |
2333 | 0 | tsk_getu32(fs->endian, sb->s_journal_dev)); |
2334 | | |
2335 | |
|
2336 | 0 | } |
2337 | |
|
2338 | 0 | tsk_fprintf(hFile, "\nMETADATA INFORMATION\n"); |
2339 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
2340 | |
|
2341 | 0 | tsk_fprintf(hFile, "Inode Range: %" PRIuINUM " - %" PRIuINUM "\n", |
2342 | 0 | fs->first_inum, fs->last_inum); |
2343 | 0 | tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum); |
2344 | |
|
2345 | 0 | tsk_fprintf(hFile, "Free Inodes: %" PRIu32 "\n", |
2346 | 0 | tsk_getu32(fs->endian, sb->s_free_inode_count)); |
2347 | | /* |
2348 | | Only print size of inode for Ext4 |
2349 | | This determines if you will get nanosecs and crtime |
2350 | | */ |
2351 | 0 | if (!strcmp(tmptypename, "Ext4")) { |
2352 | 0 | tsk_fprintf(hFile, "Inode Size: %" PRIu16 "\n", |
2353 | 0 | tsk_getu16(fs->endian, sb->s_inode_size)); |
2354 | 0 | } |
2355 | | |
2356 | |
|
2357 | 0 | if (tsk_getu32(fs->endian, sb->s_last_orphan)) { |
2358 | 0 | uint32_t or_in; |
2359 | 0 | tsk_fprintf(hFile, "Orphan Inodes: "); |
2360 | 0 | or_in = tsk_getu32(fs->endian, sb->s_last_orphan); |
2361 | |
|
2362 | 0 | while (or_in) { |
2363 | 0 | if ((or_in > fs->last_inum) || (or_in < fs->first_inum)) |
2364 | 0 | break; |
2365 | | |
2366 | 0 | tsk_fprintf(hFile, "%" PRIu32 ", ", or_in); |
2367 | |
|
2368 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
2369 | 0 | tsk_fs_file_alloc(fs), |
2370 | 0 | tsk_fs_file_close |
2371 | 0 | }; |
2372 | |
|
2373 | 0 | if (!fs_file) { |
2374 | | /* Ignore this error */ |
2375 | 0 | tsk_error_reset(); |
2376 | 0 | break; |
2377 | 0 | } |
2378 | | |
2379 | | /* Get the next one */ |
2380 | 0 | if (ext2fs_inode_lookup(fs, fs_file.get(), or_in)) { |
2381 | | /* Ignore this error */ |
2382 | 0 | tsk_error_reset(); |
2383 | 0 | break; |
2384 | 0 | } |
2385 | | |
2386 | 0 | or_in = (uint32_t) fs_file->meta->time2.ext2.dtime; |
2387 | 0 | } |
2388 | 0 | tsk_fprintf(hFile, "\n"); |
2389 | 0 | } |
2390 | |
|
2391 | 0 | tsk_fprintf(hFile, "\nCONTENT INFORMATION\n"); |
2392 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
2393 | |
|
2394 | 0 | if (fs->ftype == TSK_FS_TYPE_EXT4) { |
2395 | 0 | tsk_fprintf(hFile, "Block Groups Per Flex Group: %" PRIu32 "\n", |
2396 | 0 | (1 << sb->s_log_groups_per_flex)); |
2397 | | // gpfbg = (1 << sb->s_log_groups_per_flex); |
2398 | 0 | } |
2399 | |
|
2400 | 0 | tsk_fprintf(hFile, "Block Range: %" PRIuDADDR " - %" PRIuDADDR "\n", |
2401 | 0 | fs->first_block, fs->last_block); |
2402 | |
|
2403 | 0 | if (fs->last_block != fs->last_block_act) |
2404 | 0 | tsk_fprintf(hFile, |
2405 | 0 | "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n", |
2406 | 0 | fs->first_block, fs->last_block_act); |
2407 | |
|
2408 | 0 | tsk_fprintf(hFile, "Block Size: %u\n", fs->block_size); |
2409 | |
|
2410 | 0 | if (tsk_getu32(fs->endian, sb->s_first_data_block)) |
2411 | 0 | tsk_fprintf(hFile, |
2412 | 0 | "Reserved Blocks Before Block Groups: %" PRIu32 "\n", |
2413 | 0 | tsk_getu32(fs->endian, sb->s_first_data_block)); |
2414 | |
|
2415 | 0 | tsk_fprintf(hFile, "Free Blocks: %" PRIu32 "\n", |
2416 | 0 | tsk_getu32(fs->endian, sb->s_free_blocks_count)); |
2417 | |
|
2418 | 0 | tsk_fprintf(hFile, "\nBLOCK GROUP INFORMATION\n"); |
2419 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
2420 | |
|
2421 | 0 | tsk_fprintf(hFile, "Number of Block Groups: %" PRI_EXT2GRP "\n", |
2422 | 0 | ext2fs->groups_count); |
2423 | |
|
2424 | 0 | tsk_fprintf(hFile, "Inodes per group: %" PRIu32 "\n", |
2425 | 0 | tsk_getu32(fs->endian, sb->s_inodes_per_group)); |
2426 | 0 | tsk_fprintf(hFile, "Blocks per group: %" PRIu32 "\n", |
2427 | 0 | tsk_getu32(fs->endian, sb->s_blocks_per_group)); |
2428 | | |
2429 | | |
2430 | | /* number of blocks the inodes consume */ |
2431 | 0 | ibpg = |
2432 | 0 | (tsk_getu32(fs->endian, |
2433 | 0 | sb->s_inodes_per_group) * ext2fs->inode_size + fs->block_size - |
2434 | 0 | 1) / fs->block_size; |
2435 | | /* number of blocks group descriptors consume */ |
2436 | | // gd_blocks = |
2437 | | // (unsigned int)((gd_size * ext2fs->groups_count + fs->block_size - |
2438 | | // 1) / fs->block_size); |
2439 | |
|
2440 | | #ifdef Ext4_DBG |
2441 | | tsk_fprintf(hFile, "\n\tDEBUG: Group Descriptor Size: %d\n", gd_size); //DEBUG |
2442 | | tsk_fprintf(hFile, "\n\tDEBUG: Group Descriptor Size: %d\n", *sb->s_desc_size); //DEBUG |
2443 | | debug_print_buf((unsigned char *) &sb->pad_or_gdt, 16); |
2444 | | printf("\n\tDEBUG: gdt_growth: %d\n", tsk_getu16(fs->endian, |
2445 | | sb->pad_or_gdt.s_reserved_gdt_blocks)); |
2446 | | #endif |
2447 | |
|
2448 | 0 | for (i = 0; i < ext2fs->groups_count; i++) { |
2449 | 0 | TSK_DADDR_T cg_base; |
2450 | 0 | TSK_INUM_T inum; |
2451 | | |
2452 | | /* lock access to grp_buf */ |
2453 | 0 | tsk_take_lock(&ext2fs->lock); |
2454 | |
|
2455 | 0 | if (ext2fs_group_load(ext2fs, i)) { |
2456 | 0 | tsk_release_lock(&ext2fs->lock); |
2457 | 0 | return 1; |
2458 | 0 | } |
2459 | 0 | tsk_fprintf(hFile, "\nGroup: %d:\n", i); |
2460 | 0 | if (ext2fs->ext4_grp_buf != NULL) { |
2461 | 0 | tsk_fprintf(hFile, " Block Group Flags: ["); |
2462 | 0 | if (EXT4BG_HAS_FLAG(fs, ext2fs->ext4_grp_buf, |
2463 | 0 | EXT4_BG_INODE_UNINIT)) |
2464 | 0 | tsk_fprintf(hFile, "INODE_UNINIT, "); |
2465 | 0 | if (EXT4BG_HAS_FLAG(fs, ext2fs->ext4_grp_buf, |
2466 | 0 | EXT4_BG_BLOCK_UNINIT)) |
2467 | 0 | tsk_fprintf(hFile, "BLOCK_UNINIT, "); |
2468 | 0 | if (EXT4BG_HAS_FLAG(fs, ext2fs->ext4_grp_buf, |
2469 | 0 | EXT4_BG_INODE_ZEROED)) |
2470 | 0 | tsk_fprintf(hFile, "INODE_ZEROED, "); |
2471 | 0 | tsk_fprintf(hFile, "\b\b]\n"); |
2472 | 0 | } |
2473 | 0 | inum = |
2474 | 0 | fs->first_inum + tsk_gets32(fs->endian, |
2475 | 0 | sb->s_inodes_per_group) * i; |
2476 | 0 | tsk_fprintf(hFile, " Inode Range: %" PRIuINUM " - ", inum); |
2477 | |
|
2478 | 0 | if ((inum + tsk_gets32(fs->endian, sb->s_inodes_per_group) - 1) < |
2479 | 0 | fs->last_inum) |
2480 | 0 | tsk_fprintf(hFile, "%" PRIuINUM "\n", |
2481 | 0 | inum + tsk_gets32(fs->endian, sb->s_inodes_per_group) - 1); |
2482 | 0 | else |
2483 | 0 | tsk_fprintf(hFile, "%" PRIuINUM "\n", fs->last_inum); |
2484 | |
|
2485 | 0 | if (tsk_getu32(fs->endian, |
2486 | 0 | ext2fs->fs-> |
2487 | 0 | s_feature_incompat) & EXT2FS_FEATURE_INCOMPAT_64BIT) { |
2488 | 0 | cg_base = ext4_cgbase_lcl(fs, sb, i); |
2489 | | #ifdef Ext4_DBG |
2490 | | printf("DEBUG64: ext2_cgbase_lcl %" PRIuDADDR "\n", cg_base); |
2491 | | printf("DEBUG64: fs->s_first_data_block %" PRIuDADDR "\n", |
2492 | | tsk_getu32(fs->endian, sb->s_first_data_block)); |
2493 | | printf("DEBUG64: blocks_per_group %" PRIuDADDR "\n", |
2494 | | tsk_getu32(fs->endian, sb->s_blocks_per_group)); |
2495 | | printf("DEBUG64: i %" PRIuDADDR " %" PRIuDADDR " %" PRIuDADDR |
2496 | | "\n", i, tsk_getu32(fs->endian, sb->s_blocks_per_group), |
2497 | | (uint64_t) i * (uint64_t) tsk_getu32(fs->endian, |
2498 | | sb->s_blocks_per_group)); |
2499 | | //printf("DEBUG: calculated %"PRIuDADDR"\n", ) |
2500 | | #endif |
2501 | 0 | tsk_fprintf(hFile, |
2502 | 0 | " Block Range: %" PRIuDADDR " - %" PRIuDADDR "\n", |
2503 | 0 | cg_base, ((ext4_cgbase_lcl(fs, sb, |
2504 | 0 | i + 1) - 1) < |
2505 | 0 | fs->last_block) ? (ext4_cgbase_lcl(fs, sb, |
2506 | 0 | i + 1) - 1) : fs->last_block); |
2507 | 0 | } |
2508 | 0 | else { |
2509 | 0 | cg_base = ext2_cgbase_lcl(fs, sb, i); |
2510 | | #ifdef Ext4_DBG |
2511 | | debug_print_buf(sb, 100); |
2512 | | printf("DEBUG32: ext2_cgbase_lcl %" PRIuDADDR "\n", cg_base); |
2513 | | printf("DEBUG32: fs->s_first_data_block %" PRIu32 "\n", |
2514 | | tsk_getu32(fs->endian, sb->s_first_data_block)); |
2515 | | printf("DEBUG32: blocks_per_group %" PRIu32 "\n", |
2516 | | tsk_getu32(fs->endian, sb->s_blocks_per_group)); |
2517 | | printf("DEBUG32: i: %" PRIu32 " blocks per group: %" PRIu32 |
2518 | | " i*blocks_per_group: %" PRIu32 "\n", |
2519 | | i, tsk_getu32(fs->endian, sb->s_blocks_per_group), |
2520 | | (uint64_t) i * (uint64_t) tsk_getu32(fs->endian, |
2521 | | sb->s_blocks_per_group)); |
2522 | | //printf("DEBUG: calculated %"PRIuDADDR"\n", ) |
2523 | | #endif |
2524 | 0 | tsk_fprintf(hFile, |
2525 | 0 | " Block Range: %" PRIuDADDR " - %" PRIuDADDR "\n", |
2526 | 0 | cg_base, ((ext2_cgbase_lcl(fs, sb, |
2527 | 0 | i + 1) - 1) < |
2528 | 0 | fs->last_block) ? (ext2_cgbase_lcl(fs, sb, |
2529 | 0 | i + 1) - 1) : fs->last_block); |
2530 | 0 | } |
2531 | | |
2532 | | |
2533 | |
|
2534 | 0 | tsk_fprintf(hFile, " Layout:\n"); |
2535 | | |
2536 | | /* only print the super block data if we are not in a sparse |
2537 | | * group |
2538 | | */ |
2539 | | #ifdef Ext4_DBG |
2540 | | printf("DEBUG: ext2fs_super: %d\n", |
2541 | | ext2fs_is_super_bg(tsk_getu32(fs->endian, |
2542 | | sb->s_feature_ro_compat), i)); |
2543 | | #endif |
2544 | | /* if (((tsk_getu32(fs->endian, ext2fs->fs->s_feature_ro_compat) & |
2545 | | EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER) && |
2546 | | (cg_base != tsk_getu32(fs->endian, |
2547 | | ext2fs->grp_buf->bg_block_bitmap))) |
2548 | | || ((tsk_getu32(fs->endian, |
2549 | | ext2fs->fs->s_feature_ro_compat) & |
2550 | | EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER) == 0)) { |
2551 | | */ |
2552 | 0 | if (ext2fs_is_super_bg(tsk_getu32(fs->endian, |
2553 | 0 | sb->s_feature_ro_compat), i)) { |
2554 | 0 | TSK_OFF_T boff; |
2555 | | |
2556 | | /* the super block is the first 1024 bytes */ |
2557 | 0 | tsk_fprintf(hFile, |
2558 | 0 | " Super Block: %" PRIuDADDR " - %" PRIuDADDR "\n", |
2559 | 0 | cg_base, |
2560 | 0 | cg_base + |
2561 | 0 | ((sizeof(ext2fs_sb) + fs->block_size - |
2562 | 0 | 1) / fs->block_size) - 1); |
2563 | |
|
2564 | 0 | boff = roundup(sizeof(ext2fs_sb), fs->block_size); |
2565 | | |
2566 | | /* Group Descriptors */ |
2567 | 0 | tsk_fprintf(hFile, |
2568 | 0 | " Group Descriptor Table: %" PRIuDADDR " - ", |
2569 | 0 | (cg_base + (boff + fs->block_size - 1) / fs->block_size)); |
2570 | | |
2571 | | // printf("DEBUG: Groups Count: %u * gd_size: %u = %u\n", ext2fs->groups_count, gd_size, ext2fs->groups_count * gd_size); |
2572 | 0 | boff += (ext2fs->groups_count * gd_size); |
2573 | 0 | tsk_fprintf(hFile, "%" PRIuDADDR "\n", |
2574 | 0 | ((cg_base + |
2575 | 0 | (boff + fs->block_size - 1) / fs->block_size) - |
2576 | 0 | 1)); |
2577 | 0 | if (fs->ftype == TSK_FS_TYPE_EXT4) { |
2578 | 0 | tsk_fprintf(hFile, |
2579 | 0 | " Group Descriptor Growth Blocks: %" PRIuDADDR |
2580 | 0 | " - ", |
2581 | 0 | cg_base + (boff + fs->block_size - |
2582 | 0 | 1) / fs->block_size); |
2583 | 0 | boff += |
2584 | 0 | tsk_getu16(fs->endian, |
2585 | 0 | ext2fs->fs->pad_or_gdt.s_reserved_gdt_blocks) * |
2586 | 0 | fs->block_size; |
2587 | 0 | tsk_fprintf(hFile, "%" PRIuDADDR "\n", |
2588 | 0 | ((cg_base + (boff + fs->block_size - |
2589 | 0 | 1) / fs->block_size) - 1)); |
2590 | 0 | } |
2591 | 0 | } |
2592 | | |
2593 | |
|
2594 | 0 | if (ext2fs->ext4_grp_buf != NULL) { |
2595 | | /* The block bitmap is a full block */ |
2596 | 0 | tsk_fprintf(hFile, |
2597 | 0 | " Data bitmap: %" PRIu64 " - %" PRIu64 "\n", |
2598 | 0 | ext4_getu64(fs->endian, |
2599 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_hi, |
2600 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_lo), |
2601 | 0 | ext4_getu64(fs->endian, |
2602 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_hi, |
2603 | 0 | ext2fs->ext4_grp_buf->bg_block_bitmap_lo)); |
2604 | | |
2605 | | |
2606 | | /* The inode bitmap is a full block */ |
2607 | 0 | tsk_fprintf(hFile, |
2608 | 0 | " Inode bitmap: %" PRIu64 " - %" PRIu64 "\n", |
2609 | 0 | ext4_getu64(fs->endian, |
2610 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_hi, |
2611 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_lo), |
2612 | 0 | ext4_getu64(fs->endian, |
2613 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_hi, |
2614 | 0 | ext2fs->ext4_grp_buf->bg_inode_bitmap_lo)); |
2615 | | |
2616 | |
|
2617 | 0 | tsk_fprintf(hFile, |
2618 | 0 | " Inode Table: %" PRIu64 " - %" PRIu64 "\n", |
2619 | 0 | ext4_getu64(fs->endian, |
2620 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_hi, |
2621 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_lo), |
2622 | 0 | ext4_getu64(fs->endian, |
2623 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_hi, |
2624 | 0 | ext2fs->ext4_grp_buf->bg_inode_table_lo) |
2625 | 0 | + ibpg - 1); |
2626 | |
|
2627 | 0 | ext4_fsstat_datablock_helper(fs, hFile, i, cg_base, gd_size); |
2628 | 0 | } |
2629 | 0 | else { |
2630 | | /* The block bitmap is a full block */ |
2631 | 0 | tsk_fprintf(hFile, |
2632 | 0 | " Data bitmap: %" PRIu32 " - %" PRIu32 "\n", |
2633 | 0 | tsk_getu32(fs->endian, ext2fs->grp_buf->bg_block_bitmap), |
2634 | 0 | tsk_getu32(fs->endian, ext2fs->grp_buf->bg_block_bitmap)); |
2635 | | |
2636 | | |
2637 | | /* The inode bitmap is a full block */ |
2638 | 0 | tsk_fprintf(hFile, |
2639 | 0 | " Inode bitmap: %" PRIu32 " - %" PRIu32 "\n", |
2640 | 0 | tsk_getu32(fs->endian, ext2fs->grp_buf->bg_inode_bitmap), |
2641 | 0 | tsk_getu32(fs->endian, ext2fs->grp_buf->bg_inode_bitmap)); |
2642 | | |
2643 | |
|
2644 | 0 | tsk_fprintf(hFile, |
2645 | 0 | " Inode Table: %" PRIu32 " - %" PRIu32 "\n", |
2646 | 0 | tsk_getu32(fs->endian, ext2fs->grp_buf->bg_inode_table), |
2647 | 0 | tsk_getu32(fs->endian, |
2648 | 0 | ext2fs->grp_buf->bg_inode_table) + ibpg - 1); |
2649 | |
|
2650 | 0 | tsk_fprintf(hFile, " Data Blocks: "); |
2651 | | // BC: Commented out from Ext4 commit because it produced |
2652 | | // bad data on Ext2 test image. |
2653 | | //if (ext2fs_is_super_bg(tsk_getu32(fs->endian, |
2654 | | // sb->s_feature_ro_compat), i)) { |
2655 | 0 | if ((tsk_getu32(fs->endian, ext2fs->fs->s_feature_ro_compat) & |
2656 | 0 | EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER) && |
2657 | 0 | (cg_base == tsk_getu32(fs->endian, |
2658 | 0 | ext2fs->grp_buf->bg_block_bitmap))) { |
2659 | | |
2660 | | /* it goes from the end of the inode bitmap to before the |
2661 | | * table |
2662 | | * |
2663 | | * This hard coded aspect does not scale ... |
2664 | | */ |
2665 | |
|
2666 | 0 | tsk_fprintf(hFile, "%" PRIu32 " - %" PRIu32 ", ", |
2667 | 0 | tsk_getu32(fs->endian, |
2668 | 0 | ext2fs->grp_buf->bg_inode_bitmap) + 1, |
2669 | 0 | tsk_getu32(fs->endian, |
2670 | 0 | ext2fs->grp_buf->bg_inode_table) - 1); |
2671 | 0 | } |
2672 | |
|
2673 | 0 | tsk_fprintf(hFile, "%" PRIu32 " - %" PRIu32 "\n", |
2674 | 0 | (uint64_t) tsk_getu32(fs->endian, |
2675 | 0 | ext2fs->grp_buf->bg_inode_table) + ibpg, |
2676 | 0 | ((ext2_cgbase_lcl(fs, sb, i + 1) - 1) < |
2677 | 0 | fs->last_block) ? (ext2_cgbase_lcl(fs, sb, |
2678 | 0 | i + 1) - 1) : fs->last_block); |
2679 | 0 | } |
2680 | | |
2681 | | /* Print the free info */ |
2682 | | |
2683 | | /* The last group may not have a full number of blocks */ |
2684 | 0 | if (i != (ext2fs->groups_count - 1)) { |
2685 | 0 | uint64_t tmpInt; |
2686 | |
|
2687 | 0 | if (ext2fs->ext4_grp_buf != NULL) |
2688 | | // @@@ Should be 32-bit |
2689 | 0 | tmpInt = tsk_getu16(fs->endian, |
2690 | 0 | ext2fs->ext4_grp_buf->bg_free_inodes_count_lo); |
2691 | 0 | else |
2692 | 0 | tmpInt = tsk_getu16(fs->endian, |
2693 | 0 | ext2fs->grp_buf->bg_free_inodes_count); |
2694 | |
|
2695 | 0 | tsk_fprintf(hFile, |
2696 | 0 | " Free Inodes: %" PRIu32 " (%" PRIu32 "%%)\n", |
2697 | 0 | tmpInt, (100 * tmpInt) / |
2698 | 0 | tsk_getu32(fs->endian, sb->s_inodes_per_group)); |
2699 | | |
2700 | |
|
2701 | 0 | if (ext2fs->ext4_grp_buf != NULL) |
2702 | | // @@@ Should be 32-bit |
2703 | 0 | tmpInt = tsk_getu16(fs->endian, |
2704 | 0 | ext2fs->ext4_grp_buf->bg_free_blocks_count_lo); |
2705 | 0 | else |
2706 | 0 | tmpInt = tsk_getu16(fs->endian, |
2707 | 0 | ext2fs->grp_buf->bg_free_blocks_count); |
2708 | |
|
2709 | 0 | tsk_fprintf(hFile, |
2710 | 0 | " Free Blocks: %" PRIu32 " (%" PRIu32 "%%)\n", |
2711 | 0 | tmpInt, |
2712 | 0 | (100 * tmpInt) / |
2713 | 0 | tsk_getu32(fs->endian, sb->s_blocks_per_group)); |
2714 | 0 | } |
2715 | 0 | else { |
2716 | 0 | TSK_INUM_T inum_left; |
2717 | 0 | TSK_DADDR_T blk_left; |
2718 | 0 | uint64_t tmpInt; |
2719 | |
|
2720 | 0 | inum_left = |
2721 | 0 | (fs->last_inum % tsk_gets32(fs->endian, |
2722 | 0 | sb->s_inodes_per_group)) - 1; |
2723 | |
|
2724 | 0 | if (inum_left == 0) |
2725 | 0 | inum_left = tsk_getu32(fs->endian, sb->s_inodes_per_group); |
2726 | |
|
2727 | 0 | if (ext2fs->ext4_grp_buf != NULL) |
2728 | | // @@@ Should be 32-bit |
2729 | 0 | tmpInt = tsk_getu16(fs->endian, |
2730 | 0 | ext2fs->ext4_grp_buf->bg_free_inodes_count_lo); |
2731 | 0 | else |
2732 | 0 | tmpInt = tsk_getu16(fs->endian, |
2733 | 0 | ext2fs->grp_buf->bg_free_inodes_count); |
2734 | |
|
2735 | 0 | tsk_fprintf(hFile, " Free Inodes: %" PRIu32 " (%d%%)\n", |
2736 | 0 | tmpInt, 100 * tmpInt / inum_left); |
2737 | | |
2738 | | /* Now blocks */ |
2739 | 0 | blk_left = |
2740 | 0 | fs->block_count % tsk_getu32(fs->endian, |
2741 | 0 | sb->s_blocks_per_group); |
2742 | 0 | if (blk_left == 0) |
2743 | 0 | blk_left = tsk_getu32(fs->endian, sb->s_blocks_per_group); |
2744 | |
|
2745 | 0 | if (ext2fs->ext4_grp_buf != NULL) |
2746 | | // @@@ Should be 32-bit |
2747 | 0 | tmpInt = tsk_getu16(fs->endian, |
2748 | 0 | ext2fs->ext4_grp_buf->bg_free_blocks_count_lo); |
2749 | 0 | else |
2750 | 0 | tmpInt = tsk_getu16(fs->endian, |
2751 | 0 | ext2fs->grp_buf->bg_free_blocks_count); |
2752 | |
|
2753 | 0 | tsk_fprintf(hFile, " Free Blocks: %" PRIu32 " (%d%%)\n", |
2754 | 0 | tmpInt, 100 * tmpInt / blk_left); |
2755 | 0 | } |
2756 | | |
2757 | |
|
2758 | 0 | if (ext2fs->ext4_grp_buf != NULL) { |
2759 | | // @@@@ Sould be 32-bit |
2760 | 0 | tsk_fprintf(hFile, " Total Directories: %" PRIu16 "\n", |
2761 | 0 | tsk_getu16(fs->endian, ext2fs->ext4_grp_buf->bg_used_dirs_count_lo)); |
2762 | |
|
2763 | 0 | tsk_fprintf(hFile, " Stored Checksum: 0x%04" PRIX16 "\n", |
2764 | 0 | tsk_getu16(fs->endian, ext2fs->ext4_grp_buf->bg_checksum)); |
2765 | | #ifdef EXT4_CHECKSUMS |
2766 | | //Need Non-GPL CRC16 |
2767 | | tsk_fprintf(hFile, " Calculated Checksum: 0x%04" PRIX16 "\n", |
2768 | | ext4_group_desc_csum(ext2fs->fs, i, ext2fs->ext4_grp_buf)); |
2769 | | #endif |
2770 | 0 | } |
2771 | 0 | else { |
2772 | 0 | tsk_fprintf(hFile, " Total Directories: %" PRIu16 "\n", |
2773 | 0 | tsk_getu16(fs->endian, ext2fs->grp_buf->bg_used_dirs_count)); |
2774 | 0 | } |
2775 | |
|
2776 | 0 | tsk_release_lock(&ext2fs->lock); |
2777 | 0 | } |
2778 | | |
2779 | 0 | return 0; |
2780 | 0 | } |
2781 | | |
2782 | | |
2783 | | /************************* istat *******************************/ |
2784 | | |
2785 | | static void |
2786 | | ext2fs_make_acl_str(char *str, int len, uint16_t perm) |
2787 | 0 | { |
2788 | 0 | int i = 0; |
2789 | |
|
2790 | 0 | if (perm & EXT2_PACL_PERM_READ) { |
2791 | 0 | snprintf(&str[i], len - 1, "Read"); |
2792 | 0 | i += 4; |
2793 | 0 | } |
2794 | 0 | if (perm & EXT2_PACL_PERM_WRITE) { |
2795 | 0 | if (i) { |
2796 | 0 | snprintf(&str[i], len - i - 1, ", "); |
2797 | 0 | i += 2; |
2798 | 0 | } |
2799 | 0 | snprintf(&str[i], len - i - 1, "Write"); |
2800 | 0 | i += 5; |
2801 | 0 | } |
2802 | 0 | if (perm & EXT2_PACL_PERM_EXEC) { |
2803 | 0 | if (i) { |
2804 | 0 | snprintf(&str[i], len - i - 1, ", "); |
2805 | 0 | i += 2; |
2806 | 0 | } |
2807 | 0 | snprintf(&str[i], len - i - 1, "Execute"); |
2808 | 0 | i += 7; |
2809 | 0 | } |
2810 | 0 | } |
2811 | | |
2812 | | |
2813 | | typedef struct { |
2814 | | FILE *hFile; |
2815 | | int idx; |
2816 | | } EXT2FS_PRINT_ADDR; |
2817 | | |
2818 | | |
2819 | | /* Callback for istat to print the block addresses */ |
2820 | | static TSK_WALK_RET_ENUM |
2821 | | print_addr_act( |
2822 | | TSK_FS_FILE * fs_file, |
2823 | | [[maybe_unused]] TSK_OFF_T a_off, |
2824 | | TSK_DADDR_T addr, |
2825 | | [[maybe_unused]] char *buf, |
2826 | | size_t size, |
2827 | | TSK_FS_BLOCK_FLAG_ENUM flags, |
2828 | | void *a_ptr) |
2829 | 0 | { |
2830 | 0 | TSK_FS_INFO *fs = fs_file->fs_info; |
2831 | 0 | EXT2FS_PRINT_ADDR *print = (EXT2FS_PRINT_ADDR *) a_ptr; |
2832 | |
|
2833 | 0 | if (flags & TSK_FS_BLOCK_FLAG_CONT) { |
2834 | 0 | int i, s; |
2835 | | /* cycle through the blocks if they exist */ |
2836 | 0 | for (i = 0, s = (int) size; s > 0; s -= fs->block_size, i++) { |
2837 | | |
2838 | | /* sparse file */ |
2839 | 0 | if (addr) |
2840 | 0 | tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr + i); |
2841 | 0 | else |
2842 | 0 | tsk_fprintf(print->hFile, "0 "); |
2843 | |
|
2844 | 0 | if (++(print->idx) == 8) { |
2845 | 0 | tsk_fprintf(print->hFile, "\n"); |
2846 | 0 | print->idx = 0; |
2847 | 0 | } |
2848 | 0 | } |
2849 | 0 | } |
2850 | |
|
2851 | 0 | return TSK_WALK_CONT; |
2852 | 0 | } |
2853 | | |
2854 | | |
2855 | | /** |
2856 | | * Print details on a specific file to a file handle. |
2857 | | * |
2858 | | * @param fs File system file is located in |
2859 | | * @param hFile File handle to print text to |
2860 | | * @param inum Address of file in file system |
2861 | | * @param numblock The number of blocks in file to force print (can go beyond file size) |
2862 | | * @param sec_skew Clock skew in seconds to also print times in |
2863 | | * |
2864 | | * @returns 1 on error and 0 on success |
2865 | | */ |
2866 | | static uint8_t |
2867 | | ext2fs_istat(TSK_FS_INFO * fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * hFile, TSK_INUM_T inum, |
2868 | | TSK_DADDR_T numblock, int32_t sec_skew) |
2869 | 0 | { |
2870 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
2871 | 0 | TSK_FS_META *fs_meta; |
2872 | 0 | char ls[12]; |
2873 | 0 | EXT2FS_PRINT_ADDR print; |
2874 | 0 | const TSK_FS_ATTR *fs_attr_indir; |
2875 | 0 | ext2fs_inode *dino_buf = NULL; |
2876 | 0 | uint8_t *ea_buf = NULL; |
2877 | 0 | size_t ea_buf_len = 0; |
2878 | 0 | char timeBuf[128]; |
2879 | 0 | unsigned int size; |
2880 | 0 | unsigned int large_inodes; |
2881 | | |
2882 | | // clean up any error messages that are lying around |
2883 | 0 | tsk_error_reset(); |
2884 | 0 | if (ext2fs->inode_size > 128) { |
2885 | 0 | large_inodes = 1; |
2886 | 0 | } |
2887 | 0 | else { |
2888 | 0 | large_inodes = 0; |
2889 | 0 | } |
2890 | |
|
2891 | 0 | size = |
2892 | 0 | ext2fs->inode_size > |
2893 | 0 | sizeof(ext2fs_inode) ? ext2fs->inode_size : sizeof(ext2fs_inode); |
2894 | 0 | if ((dino_buf = (ext2fs_inode *) tsk_malloc(size)) == NULL) { |
2895 | 0 | return 1; |
2896 | 0 | } |
2897 | | |
2898 | 0 | if (ext2fs_dinode_load(ext2fs, inum, dino_buf, &ea_buf, &ea_buf_len)) { |
2899 | 0 | free(dino_buf); |
2900 | 0 | return 1; |
2901 | 0 | } |
2902 | | |
2903 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
2904 | 0 | tsk_fs_file_open_meta(fs, NULL, inum), |
2905 | 0 | tsk_fs_file_close |
2906 | 0 | }; |
2907 | |
|
2908 | 0 | if (!fs_file) { |
2909 | 0 | free(dino_buf); |
2910 | 0 | return 1; |
2911 | 0 | } |
2912 | 0 | fs_meta = fs_file->meta; |
2913 | |
|
2914 | 0 | tsk_fprintf(hFile, "inode: %" PRIuINUM "\n", inum); |
2915 | 0 | tsk_fprintf(hFile, "%sAllocated\n", |
2916 | 0 | (fs_meta->flags & TSK_FS_META_FLAG_ALLOC) ? "" : "Not "); |
2917 | |
|
2918 | 0 | tsk_fprintf(hFile, "Group: %" PRIuGID "\n", ext2fs->grp_num); |
2919 | | |
2920 | | // Note that if this is a "virtual file", then ext2fs->dino_buf may not be set. |
2921 | 0 | tsk_fprintf(hFile, "Generation Id: %" PRIu32 "\n", |
2922 | 0 | tsk_getu32(fs->endian, dino_buf->i_generation)); |
2923 | |
|
2924 | 0 | if (fs_meta->link) |
2925 | 0 | tsk_fprintf(hFile, "symbolic link to: %s\n", fs_meta->link); |
2926 | |
|
2927 | 0 | tsk_fprintf(hFile, "uid / gid: %" PRIuUID " / %" PRIuGID "\n", |
2928 | 0 | fs_meta->uid, fs_meta->gid); |
2929 | |
|
2930 | 0 | tsk_fs_meta_make_ls(fs_meta, ls, sizeof(ls)); |
2931 | 0 | tsk_fprintf(hFile, "mode: %s\n", ls); |
2932 | | |
2933 | | /* Print the device ids */ |
2934 | 0 | if ((fs_meta->type == TSK_FS_META_TYPE_BLK) |
2935 | 0 | || (fs_meta->type == TSK_FS_META_TYPE_CHR)) { |
2936 | 0 | tsk_fprintf(hFile, |
2937 | 0 | "Device Major: %" PRIu8 " Minor: %" PRIu8 "\n", |
2938 | 0 | dino_buf->i_block[0][1], dino_buf->i_block[0][0]); |
2939 | 0 | } |
2940 | |
|
2941 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags)) { |
2942 | 0 | tsk_fprintf(hFile, "Flags: "); |
2943 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_SECDEL) |
2944 | 0 | tsk_fprintf(hFile, "Secure Delete, "); |
2945 | |
|
2946 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_UNRM) |
2947 | 0 | tsk_fprintf(hFile, "Undelete, "); |
2948 | |
|
2949 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_COMP) |
2950 | 0 | tsk_fprintf(hFile, "Compressed, "); |
2951 | |
|
2952 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_SYNC) |
2953 | 0 | tsk_fprintf(hFile, "Sync Updates, "); |
2954 | |
|
2955 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_IMM) |
2956 | 0 | tsk_fprintf(hFile, "Immutable, "); |
2957 | |
|
2958 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_APPEND) |
2959 | 0 | tsk_fprintf(hFile, "Append Only, "); |
2960 | |
|
2961 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_NODUMP) |
2962 | 0 | tsk_fprintf(hFile, "Do Not Dump, "); |
2963 | |
|
2964 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_NOA) |
2965 | 0 | tsk_fprintf(hFile, "No A-Time, "); |
2966 | |
|
2967 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_DIRTY) |
2968 | 0 | tsk_fprintf(hFile, "Dirty Compressed File, "); |
2969 | |
|
2970 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_COMPRBLK) |
2971 | 0 | tsk_fprintf(hFile, "Compressed Clusters, "); |
2972 | |
|
2973 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_NOCOMPR) |
2974 | 0 | tsk_fprintf(hFile, "Do Not Compress, "); |
2975 | |
|
2976 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_ECOMPR) |
2977 | 0 | tsk_fprintf(hFile, "Compression Error, "); |
2978 | |
|
2979 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_INDEX) |
2980 | 0 | tsk_fprintf(hFile, "Hash Indexed Directory, "); |
2981 | |
|
2982 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_IMAGIC) |
2983 | 0 | tsk_fprintf(hFile, "AFS Magic Directory, "); |
2984 | |
|
2985 | 0 | if (tsk_getu32(fs->endian, |
2986 | 0 | dino_buf->i_flags) & EXT2_IN_JOURNAL_DATA) |
2987 | 0 | tsk_fprintf(hFile, "Journal Data, "); |
2988 | |
|
2989 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_NOTAIL) |
2990 | 0 | tsk_fprintf(hFile, "Do Not Merge Tail, "); |
2991 | |
|
2992 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_DIRSYNC) |
2993 | 0 | tsk_fprintf(hFile, "Directory Sync, "); |
2994 | |
|
2995 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_TOPDIR) |
2996 | 0 | tsk_fprintf(hFile, "Top Directory, "); |
2997 | |
|
2998 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_HUGE_FILE) |
2999 | 0 | tsk_fprintf(hFile, "Huge File, "); |
3000 | |
|
3001 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_EXTENTS) |
3002 | 0 | tsk_fprintf(hFile, "Extents, "); |
3003 | |
|
3004 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_EA_INODE) |
3005 | 0 | tsk_fprintf(hFile, "Large Extended Attribute, "); |
3006 | |
|
3007 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_IN_EOFBLOCKS) |
3008 | 0 | tsk_fprintf(hFile, "Blocks Allocated Beyond EOF, "); |
3009 | |
|
3010 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_SNAPFILE) |
3011 | 0 | tsk_fprintf(hFile, "Snapshot, "); |
3012 | |
|
3013 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_SNAPFILE_DELETED) |
3014 | 0 | tsk_fprintf(hFile, "Deleted Snapshot, "); |
3015 | |
|
3016 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_SNAPFILE_SHRUNK) |
3017 | 0 | tsk_fprintf(hFile, "Shrunk Snapshot, "); |
3018 | |
|
3019 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_INLINE_DATA) |
3020 | 0 | tsk_fprintf(hFile, "Inline Data, "); |
3021 | |
|
3022 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_flags) & EXT2_PROJINHERIT) |
3023 | 0 | tsk_fprintf(hFile, "Inherited project ID, "); |
3024 | | |
3025 | |
|
3026 | 0 | tsk_fprintf(hFile, "\n"); |
3027 | 0 | } |
3028 | |
|
3029 | 0 | tsk_fprintf(hFile, "size: %" PRIdOFF "\n", fs_meta->size); |
3030 | 0 | tsk_fprintf(hFile, "num of links: %d\n", fs_meta->nlink); |
3031 | | |
3032 | | /* Ext attribute are stored in a block with a header and a list |
3033 | | * of entries that are aligned to 4-byte boundaries. The attr |
3034 | | * value is stored at the end of the block. There are 4 null bytes |
3035 | | * in between the headers and values |
3036 | | */ |
3037 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_file_acl) != 0) { |
3038 | 0 | char *buf; |
3039 | 0 | ext2fs_ea_header *ea_head; |
3040 | 0 | ext2fs_ea_entry *ea_entry; |
3041 | 0 | ssize_t cnt; |
3042 | |
|
3043 | 0 | if ((buf = (char*) tsk_malloc(fs->block_size)) == NULL) { |
3044 | 0 | free(dino_buf); |
3045 | 0 | return 1; |
3046 | 0 | } |
3047 | | |
3048 | 0 | tsk_fprintf(hFile, |
3049 | 0 | "\nExtended Attributes (Block: %" PRIu32 ")\n", |
3050 | 0 | tsk_getu32(fs->endian, dino_buf->i_file_acl)); |
3051 | | |
3052 | | /* Is the value too big? */ |
3053 | 0 | if (tsk_getu32(fs->endian, dino_buf->i_file_acl) > fs->last_block) { |
3054 | 0 | tsk_fprintf(hFile, |
3055 | 0 | "Extended Attributes block is larger than file system\n"); |
3056 | 0 | goto egress_ea; |
3057 | 0 | } |
3058 | | |
3059 | 0 | cnt = tsk_fs_read(fs, |
3060 | 0 | (TSK_DADDR_T) tsk_getu32(fs->endian, |
3061 | 0 | dino_buf->i_file_acl) * fs->block_size, |
3062 | 0 | buf, fs->block_size); |
3063 | |
|
3064 | 0 | if (cnt != fs->block_size) { |
3065 | 0 | if (cnt >= 0) { |
3066 | 0 | tsk_error_reset(); |
3067 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
3068 | 0 | } |
3069 | 0 | tsk_error_set_errstr2("ext2fs_istat: ACL block %" PRIu32, |
3070 | 0 | tsk_getu32(fs->endian, dino_buf->i_file_acl)); |
3071 | 0 | free(buf); |
3072 | 0 | free(dino_buf); |
3073 | 0 | return 1; |
3074 | 0 | } |
3075 | | |
3076 | | |
3077 | | /* Check the header */ |
3078 | 0 | ea_head = (ext2fs_ea_header *) buf; |
3079 | 0 | if (tsk_getu32(fs->endian, ea_head->magic) != EXT2_EA_MAGIC) { |
3080 | 0 | tsk_fprintf(hFile, |
3081 | 0 | "Incorrect extended attribute header: %" PRIx32 "\n", |
3082 | 0 | tsk_getu32(fs->endian, ea_head->magic)); |
3083 | 0 | } |
3084 | | |
3085 | | |
3086 | | /* Cycle through each entry - at the top of the block */ |
3087 | 0 | for (ea_entry = (ext2fs_ea_entry *) & ea_head->entry; |
3088 | 0 | ((uintptr_t) ea_entry < |
3089 | 0 | ((uintptr_t) buf + fs->block_size - |
3090 | 0 | sizeof(ext2fs_ea_entry))); |
3091 | 0 | ea_entry = |
3092 | 0 | (ext2fs_ea_entry *) ((uintptr_t) ea_entry + |
3093 | 0 | EXT2_EA_LEN(ea_entry->nlen))) { |
3094 | |
|
3095 | 0 | char name[256]; |
3096 | | |
3097 | | /* Stop if the first four bytes are NULL */ |
3098 | 0 | if ((ea_entry->nlen == 0) && (ea_entry->nidx == 0) && |
3099 | 0 | (tsk_getu16(fs->endian, ea_entry->val_off) == 0)) |
3100 | 0 | break; |
3101 | | |
3102 | | /* The Linux src does not allow this */ |
3103 | 0 | if (tsk_getu32(fs->endian, ea_entry->val_blk) != 0) { |
3104 | 0 | tsk_fprintf(hFile, |
3105 | 0 | "Attribute has non-zero value block - skipping\n"); |
3106 | 0 | continue; |
3107 | 0 | } |
3108 | | |
3109 | | |
3110 | | /* Is the value location and size valid? */ |
3111 | | //if ((tsk_getu32(fs->endian, |
3112 | 0 | if ((tsk_getu16(fs->endian, |
3113 | 0 | ea_entry->val_off) > fs->block_size) |
3114 | 0 | || ((tsk_getu16(fs->endian, |
3115 | 0 | ea_entry->val_off) + tsk_getu32(fs->endian, |
3116 | 0 | ea_entry->val_size)) > fs->block_size)) { |
3117 | 0 | continue; |
3118 | 0 | } |
3119 | | |
3120 | | |
3121 | | /* Copy the name into a buffer - not NULL term */ |
3122 | 0 | strncpy(name, (char *) &ea_entry->name, ea_entry->nlen); |
3123 | 0 | name[ea_entry->nlen] = '\0'; |
3124 | | |
3125 | | |
3126 | | /* User assigned attributes - setfattr / getfattr */ |
3127 | 0 | if ((ea_entry->nidx == EXT2_EA_IDX_USER) || |
3128 | 0 | (ea_entry->nidx == EXT2_EA_IDX_TRUSTED) || |
3129 | 0 | (ea_entry->nidx == EXT2_EA_IDX_SECURITY)) { |
3130 | 0 | char val[256]; |
3131 | |
|
3132 | 0 | strncpy(val, |
3133 | 0 | &buf[tsk_getu16(fs->endian, ea_entry->val_off)], |
3134 | 0 | tsk_getu32(fs->endian, |
3135 | 0 | ea_entry->val_size) > |
3136 | 0 | 256 ? 256 : tsk_getu32(fs->endian, |
3137 | 0 | ea_entry->val_size)); |
3138 | |
|
3139 | 0 | val[tsk_getu32(fs->endian, ea_entry->val_size) > 255 ? |
3140 | 0 | 255 : tsk_getu32(fs->endian, ea_entry->val_size)] = |
3141 | 0 | '\0'; |
3142 | |
|
3143 | 0 | if (ea_entry->nidx == EXT2_EA_IDX_USER) |
3144 | 0 | tsk_fprintf(hFile, "user.%s=%s\n", name, val); |
3145 | 0 | else if (ea_entry->nidx == EXT2_EA_IDX_TRUSTED) |
3146 | 0 | tsk_fprintf(hFile, "trust.%s=%s\n", name, val); |
3147 | 0 | else if (ea_entry->nidx == EXT2_EA_IDX_SECURITY) |
3148 | 0 | tsk_fprintf(hFile, "security.%s=%s\n", name, val); |
3149 | |
|
3150 | 0 | } |
3151 | | |
3152 | | |
3153 | | /* POSIX ACL - setfacl / getfacl stuff */ |
3154 | 0 | else if ((ea_entry->nidx == EXT2_EA_IDX_POSIX_ACL_ACCESS) |
3155 | 0 | || (ea_entry->nidx == EXT2_EA_IDX_POSIX_ACL_DEFAULT)) { |
3156 | |
|
3157 | 0 | ext2fs_pos_acl_entry_lo *acl_lo; |
3158 | 0 | ext2fs_pos_acl_head *acl_head; |
3159 | |
|
3160 | 0 | if (ea_entry->nidx == EXT2_EA_IDX_POSIX_ACL_ACCESS) |
3161 | 0 | tsk_fprintf(hFile, |
3162 | 0 | "POSIX Access Control List Entries:\n"); |
3163 | 0 | else if (ea_entry->nidx == EXT2_EA_IDX_POSIX_ACL_DEFAULT) |
3164 | 0 | tsk_fprintf(hFile, |
3165 | 0 | "POSIX Default Access Control List Entries:\n"); |
3166 | | |
3167 | | /* examine the header */ |
3168 | 0 | acl_head = |
3169 | 0 | (ext2fs_pos_acl_head *) & |
3170 | 0 | buf[tsk_getu16(fs->endian, ea_entry->val_off)]; |
3171 | |
|
3172 | 0 | if (tsk_getu32(fs->endian, acl_head->ver) != 1) { |
3173 | 0 | tsk_fprintf(hFile, |
3174 | 0 | "Invalid ACL Header Version: %" PRIu32 "\n", |
3175 | 0 | tsk_getu32(fs->endian, acl_head->ver)); |
3176 | 0 | continue; |
3177 | 0 | } |
3178 | | |
3179 | | /* The first entry starts after the header */ |
3180 | 0 | acl_lo = |
3181 | 0 | (ext2fs_pos_acl_entry_lo *) ((uintptr_t) acl_head + |
3182 | 0 | sizeof(ext2fs_pos_acl_head)); |
3183 | | |
3184 | | |
3185 | | /* Cycle through the values */ |
3186 | 0 | while ((uintptr_t) acl_lo < |
3187 | 0 | ((uintptr_t) buf + |
3188 | 0 | tsk_getu16(fs->endian, |
3189 | 0 | ea_entry->val_off) + tsk_getu32(fs->endian, |
3190 | 0 | ea_entry->val_size))) { |
3191 | |
|
3192 | 0 | char perm[64]; |
3193 | 0 | int len; |
3194 | | |
3195 | | /* Make a string from the permissions */ |
3196 | 0 | ext2fs_make_acl_str(perm, 64, |
3197 | 0 | tsk_getu16(fs->endian, acl_lo->perm)); |
3198 | |
|
3199 | 0 | switch (tsk_getu16(fs->endian, acl_lo->tag)) { |
3200 | 0 | case EXT2_PACL_TAG_USERO: |
3201 | 0 | tsk_fprintf(hFile, " uid: %" PRIuUID ": %s\n", |
3202 | 0 | fs_meta->uid, perm); |
3203 | 0 | len = sizeof(ext2fs_pos_acl_entry_sh); |
3204 | 0 | break; |
3205 | | |
3206 | 0 | case EXT2_PACL_TAG_GRPO: |
3207 | 0 | tsk_fprintf(hFile, " gid: %" PRIuGID ": %s\n", |
3208 | 0 | fs_meta->gid, perm); |
3209 | 0 | len = sizeof(ext2fs_pos_acl_entry_sh); |
3210 | 0 | break; |
3211 | 0 | case EXT2_PACL_TAG_OTHER: |
3212 | 0 | tsk_fprintf(hFile, " other: %s\n", perm); |
3213 | 0 | len = sizeof(ext2fs_pos_acl_entry_sh); |
3214 | 0 | break; |
3215 | 0 | case EXT2_PACL_TAG_MASK: |
3216 | 0 | tsk_fprintf(hFile, " mask: %s\n", perm); |
3217 | 0 | len = sizeof(ext2fs_pos_acl_entry_sh); |
3218 | 0 | break; |
3219 | | |
3220 | | |
3221 | 0 | case EXT2_PACL_TAG_GRP: |
3222 | 0 | tsk_fprintf(hFile, " gid: %" PRIu32 ": %s\n", |
3223 | 0 | tsk_getu32(fs->endian, acl_lo->id), perm); |
3224 | 0 | len = sizeof(ext2fs_pos_acl_entry_lo); |
3225 | 0 | break; |
3226 | | |
3227 | 0 | case EXT2_PACL_TAG_USER: |
3228 | 0 | tsk_fprintf(hFile, " uid: %" PRIu32 ": %s\n", |
3229 | 0 | tsk_getu32(fs->endian, acl_lo->id), perm); |
3230 | |
|
3231 | 0 | len = sizeof(ext2fs_pos_acl_entry_lo); |
3232 | 0 | break; |
3233 | | |
3234 | 0 | default: |
3235 | 0 | tsk_fprintf(hFile, "Unknown ACL tag: %d\n", |
3236 | 0 | tsk_getu16(fs->endian, acl_lo->tag)); |
3237 | 0 | len = sizeof(ext2fs_pos_acl_entry_sh); |
3238 | 0 | break; |
3239 | 0 | } |
3240 | 0 | acl_lo = |
3241 | 0 | (ext2fs_pos_acl_entry_lo *) ((uintptr_t) acl_lo |
3242 | 0 | + len); |
3243 | 0 | } |
3244 | 0 | } |
3245 | 0 | else { |
3246 | 0 | tsk_fprintf(hFile, |
3247 | 0 | "Unsupported Extended Attr Type: %d\n", |
3248 | 0 | ea_entry->nidx); |
3249 | 0 | } |
3250 | 0 | } |
3251 | 0 | egress_ea: |
3252 | |
|
3253 | 0 | free(buf); |
3254 | 0 | } |
3255 | | |
3256 | 0 | if (sec_skew != 0) { |
3257 | 0 | tsk_fprintf(hFile, "\nAdjusted Inode Times:\n"); |
3258 | 0 | if (fs_meta->mtime) |
3259 | 0 | fs_meta->mtime -= sec_skew; |
3260 | 0 | if (fs_meta->atime) |
3261 | 0 | fs_meta->atime -= sec_skew; |
3262 | 0 | if (fs_meta->ctime) |
3263 | 0 | fs_meta->ctime -= sec_skew; |
3264 | | |
3265 | |
|
3266 | 0 | if (fs->ftype == TSK_FS_TYPE_EXT4 && large_inodes) { |
3267 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
3268 | 0 | tsk_fs_time_to_str_subsecs(fs_meta->atime, |
3269 | 0 | fs_meta->atime_nano, timeBuf)); |
3270 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
3271 | 0 | tsk_fs_time_to_str_subsecs(fs_meta->mtime, |
3272 | 0 | fs_meta->mtime_nano, timeBuf)); |
3273 | 0 | tsk_fprintf(hFile, "Inode Modified:\t%s\n", |
3274 | 0 | tsk_fs_time_to_str_subsecs(fs_meta->ctime, |
3275 | 0 | fs_meta->ctime_nano, timeBuf)); |
3276 | 0 | } |
3277 | 0 | else { |
3278 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
3279 | 0 | tsk_fs_time_to_str(fs_meta->atime, timeBuf)); |
3280 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
3281 | 0 | tsk_fs_time_to_str(fs_meta->mtime, timeBuf)); |
3282 | 0 | tsk_fprintf(hFile, "Inode Modified:\t%s\n", |
3283 | 0 | tsk_fs_time_to_str(fs_meta->ctime, timeBuf)); |
3284 | 0 | } |
3285 | |
|
3286 | 0 | if (fs->ftype == TSK_FS_TYPE_EXT4 && large_inodes) { |
3287 | 0 | fs_meta->crtime -= sec_skew; |
3288 | 0 | tsk_fprintf(hFile, "File Created:\t%s\n", |
3289 | 0 | tsk_fs_time_to_str(fs_meta->crtime, timeBuf)); |
3290 | 0 | fs_meta->crtime += sec_skew; |
3291 | |
|
3292 | 0 | } |
3293 | |
|
3294 | 0 | if (fs_meta->time2.ext2.dtime) { |
3295 | 0 | fs_meta->time2.ext2.dtime -= sec_skew; |
3296 | 0 | tsk_fprintf(hFile, "Deleted:\t%s", |
3297 | 0 | tsk_fs_time_to_str(fs_meta->time2.ext2.dtime, timeBuf)); |
3298 | 0 | fs_meta->time2.ext2.dtime += sec_skew; |
3299 | 0 | } |
3300 | |
|
3301 | 0 | if (fs_meta->mtime) |
3302 | 0 | fs_meta->mtime += sec_skew; |
3303 | 0 | if (fs_meta->atime) |
3304 | 0 | fs_meta->atime += sec_skew; |
3305 | 0 | if (fs_meta->ctime) |
3306 | 0 | fs_meta->ctime += sec_skew; |
3307 | |
|
3308 | 0 | tsk_fprintf(hFile, "\nOriginal Inode Times:\n"); |
3309 | 0 | } |
3310 | 0 | else { |
3311 | 0 | tsk_fprintf(hFile, "\nInode Times:\n"); |
3312 | 0 | } |
3313 | |
|
3314 | 0 | if (fs->ftype == TSK_FS_TYPE_EXT4 && large_inodes) { |
3315 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
3316 | 0 | tsk_fs_time_to_str_subsecs(fs_meta->atime, fs_meta->atime_nano, |
3317 | 0 | timeBuf)); |
3318 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
3319 | 0 | tsk_fs_time_to_str_subsecs(fs_meta->mtime, fs_meta->mtime_nano, |
3320 | 0 | timeBuf)); |
3321 | 0 | tsk_fprintf(hFile, "Inode Modified:\t%s\n", |
3322 | 0 | tsk_fs_time_to_str_subsecs(fs_meta->ctime, fs_meta->ctime_nano, |
3323 | 0 | timeBuf)); |
3324 | 0 | } |
3325 | 0 | else { |
3326 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
3327 | 0 | tsk_fs_time_to_str(fs_meta->atime, timeBuf)); |
3328 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
3329 | 0 | tsk_fs_time_to_str(fs_meta->mtime, timeBuf)); |
3330 | 0 | tsk_fprintf(hFile, "Inode Modified:\t%s\n", |
3331 | 0 | tsk_fs_time_to_str(fs_meta->ctime, timeBuf)); |
3332 | 0 | } |
3333 | | |
3334 | | |
3335 | |
|
3336 | 0 | if (fs->ftype == TSK_FS_TYPE_EXT4 && large_inodes) { |
3337 | 0 | tsk_fprintf(hFile, "File Created:\t%s\n", |
3338 | 0 | tsk_fs_time_to_str_subsecs(fs_meta->crtime, |
3339 | 0 | fs_meta->crtime_nano, timeBuf)); |
3340 | 0 | } |
3341 | 0 | if (fs_meta->time2.ext2.dtime) |
3342 | 0 | tsk_fprintf(hFile, "Deleted:\t%s\n", |
3343 | 0 | tsk_fs_time_to_str(fs_meta->time2.ext2.dtime, timeBuf)); |
3344 | |
|
3345 | 0 | if (numblock > 0) |
3346 | 0 | fs_meta->size = numblock * fs->block_size; |
3347 | |
|
3348 | 0 | if (fs_meta->content_type != TSK_FS_META_CONTENT_TYPE_EXT4_INLINE) { |
3349 | 0 | tsk_fprintf(hFile, "\nDirect Blocks:\n"); |
3350 | |
|
3351 | 0 | if (istat_flags & TSK_FS_ISTAT_RUNLIST) { |
3352 | 0 | const TSK_FS_ATTR *fs_attr_default = |
3353 | 0 | tsk_fs_file_attr_get_type(fs_file.get(), |
3354 | 0 | TSK_FS_ATTR_TYPE_DEFAULT, 0, 0); |
3355 | 0 | if (fs_attr_default && (fs_attr_default->flags & TSK_FS_ATTR_NONRES)) { |
3356 | 0 | if (tsk_fs_attr_print(fs_attr_default, hFile)) { |
3357 | 0 | tsk_fprintf(hFile, "\nError creating run lists\n"); |
3358 | 0 | tsk_error_print(hFile); |
3359 | 0 | tsk_error_reset(); |
3360 | 0 | } |
3361 | 0 | } |
3362 | 0 | } |
3363 | 0 | else { |
3364 | 0 | print.idx = 0; |
3365 | 0 | print.hFile = hFile; |
3366 | |
|
3367 | 0 | if (tsk_fs_file_walk(fs_file.get(), TSK_FS_FILE_WALK_FLAG_AONLY, |
3368 | 0 | print_addr_act, (void *)&print)) { |
3369 | 0 | tsk_fprintf(hFile, "\nError reading file: "); |
3370 | 0 | tsk_error_print(hFile); |
3371 | 0 | tsk_error_reset(); |
3372 | 0 | } |
3373 | 0 | else if (print.idx != 0) { |
3374 | 0 | tsk_fprintf(hFile, "\n"); |
3375 | 0 | } |
3376 | 0 | } |
3377 | |
|
3378 | 0 | if (fs_meta->content_type == TSK_FS_META_CONTENT_TYPE_EXT4_EXTENTS) { |
3379 | 0 | const TSK_FS_ATTR *fs_attr_extent = |
3380 | 0 | tsk_fs_file_attr_get_type(fs_file.get(), |
3381 | 0 | TSK_FS_ATTR_TYPE_UNIX_EXTENT, 0, 0); |
3382 | 0 | if (fs_attr_extent) { |
3383 | 0 | tsk_fprintf(hFile, "\nExtent Blocks:\n"); |
3384 | |
|
3385 | 0 | if (istat_flags & TSK_FS_ISTAT_RUNLIST) { |
3386 | 0 | if (tsk_fs_attr_print(fs_attr_extent, hFile)) { |
3387 | 0 | tsk_fprintf(hFile, "\nError creating run lists\n"); |
3388 | 0 | tsk_error_print(hFile); |
3389 | 0 | tsk_error_reset(); |
3390 | 0 | } |
3391 | 0 | } |
3392 | 0 | else { |
3393 | 0 | print.idx = 0; |
3394 | |
|
3395 | 0 | if (tsk_fs_attr_walk(fs_attr_extent, |
3396 | 0 | TSK_FS_FILE_WALK_FLAG_AONLY, print_addr_act, |
3397 | 0 | (void *)&print)) { |
3398 | 0 | tsk_fprintf(hFile, |
3399 | 0 | "\nError reading indirect attribute: "); |
3400 | 0 | tsk_error_print(hFile); |
3401 | 0 | tsk_error_reset(); |
3402 | 0 | } |
3403 | 0 | else if (print.idx != 0) { |
3404 | 0 | tsk_fprintf(hFile, "\n"); |
3405 | 0 | } |
3406 | 0 | } |
3407 | 0 | } |
3408 | 0 | } |
3409 | 0 | else { |
3410 | 0 | fs_attr_indir = tsk_fs_file_attr_get_type(fs_file.get(), |
3411 | 0 | TSK_FS_ATTR_TYPE_UNIX_INDIR, 0, 0); |
3412 | 0 | if (fs_attr_indir) { |
3413 | 0 | tsk_fprintf(hFile, "\nIndirect Blocks:\n"); |
3414 | 0 | if (istat_flags & TSK_FS_ISTAT_RUNLIST) { |
3415 | 0 | tsk_fs_attr_print(fs_attr_indir, hFile); |
3416 | 0 | } |
3417 | 0 | else { |
3418 | 0 | print.idx = 0; |
3419 | |
|
3420 | 0 | if (tsk_fs_attr_walk(fs_attr_indir, |
3421 | 0 | TSK_FS_FILE_WALK_FLAG_AONLY, print_addr_act, |
3422 | 0 | (void *)&print)) { |
3423 | 0 | tsk_fprintf(hFile, |
3424 | 0 | "\nError reading indirect attribute: "); |
3425 | 0 | tsk_error_print(hFile); |
3426 | 0 | tsk_error_reset(); |
3427 | 0 | } |
3428 | 0 | else if (print.idx != 0) { |
3429 | 0 | tsk_fprintf(hFile, "\n"); |
3430 | 0 | } |
3431 | 0 | } |
3432 | 0 | } |
3433 | 0 | } |
3434 | 0 | } |
3435 | |
|
3436 | 0 | free(dino_buf); |
3437 | 0 | return 0; |
3438 | 0 | } |
3439 | | |
3440 | | |
3441 | | /* ext2fs_close - close an ext2fs file system */ |
3442 | | |
3443 | | static void |
3444 | | ext2fs_close(TSK_FS_INFO * fs) |
3445 | 0 | { |
3446 | 0 | EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; |
3447 | |
|
3448 | 0 | fs->tag = 0; |
3449 | 0 | free(ext2fs->fs); |
3450 | 0 | free(ext2fs->grp_buf); |
3451 | 0 | free(ext2fs->ext4_grp_buf); |
3452 | 0 | free(ext2fs->bmap_buf); |
3453 | 0 | free(ext2fs->imap_buf); |
3454 | |
|
3455 | 0 | tsk_deinit_lock(&ext2fs->lock); |
3456 | |
|
3457 | 0 | tsk_fs_free(fs); |
3458 | 0 | } |
3459 | | |
3460 | | /** |
3461 | | * \internal |
3462 | | * Open part of a disk image as a Ext2/3 file system. |
3463 | | * |
3464 | | * @param img_info Disk image to analyze |
3465 | | * @param offset Byte offset where file system starts |
3466 | | * @param ftype Specific type of file system |
3467 | | * @param a_pass NOT USED |
3468 | | * @param test NOT USED |
3469 | | * @returns NULL on error or if data is not an Ext2/3 file system |
3470 | | */ |
3471 | | TSK_FS_INFO * |
3472 | | ext2fs_open( |
3473 | | TSK_IMG_INFO * img_info, |
3474 | | TSK_OFF_T offset, |
3475 | | TSK_FS_TYPE_ENUM ftype, |
3476 | | [[maybe_unused]] const char* a_pass, |
3477 | | [[maybe_unused]] uint8_t test) |
3478 | 0 | { |
3479 | 0 | EXT2FS_INFO *ext2fs; |
3480 | 0 | unsigned int len; |
3481 | 0 | TSK_FS_INFO *fs; |
3482 | 0 | ssize_t cnt; |
3483 | | |
3484 | | // clean up any error messages that are lying around |
3485 | 0 | tsk_error_reset(); |
3486 | |
|
3487 | 0 | if (TSK_FS_TYPE_ISEXT(ftype) == 0) { |
3488 | 0 | tsk_error_reset(); |
3489 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
3490 | 0 | tsk_error_set_errstr("Invalid FS Type in ext2fs_open"); |
3491 | 0 | return NULL; |
3492 | 0 | } |
3493 | | |
3494 | 0 | if (img_info->sector_size == 0) { |
3495 | 0 | tsk_error_reset(); |
3496 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
3497 | 0 | tsk_error_set_errstr("ext2fs_open: sector size is 0"); |
3498 | 0 | return NULL; |
3499 | 0 | } |
3500 | | |
3501 | 0 | if ((ext2fs = (EXT2FS_INFO *) tsk_fs_malloc(sizeof(*ext2fs))) == NULL) |
3502 | 0 | return NULL; |
3503 | | |
3504 | 0 | fs = &(ext2fs->fs_info); |
3505 | |
|
3506 | 0 | fs->ftype = ftype; |
3507 | 0 | fs->flags = TSK_FS_INFO_FLAG_NONE; |
3508 | 0 | fs->img_info = img_info; |
3509 | 0 | fs->offset = offset; |
3510 | 0 | fs->tag = TSK_FS_INFO_TAG; |
3511 | | |
3512 | | /* |
3513 | | * Read the superblock. |
3514 | | */ |
3515 | 0 | len = sizeof(ext2fs_sb); |
3516 | 0 | if ((ext2fs->fs = (ext2fs_sb *) tsk_malloc(len)) == NULL) { |
3517 | 0 | fs->tag = 0; |
3518 | 0 | tsk_fs_free((TSK_FS_INFO *)ext2fs); |
3519 | 0 | return NULL; |
3520 | 0 | } |
3521 | | |
3522 | 0 | cnt = tsk_fs_read(fs, EXT2FS_SBOFF, (char *) ext2fs->fs, len); |
3523 | 0 | if (cnt != len) { |
3524 | 0 | if (cnt >= 0) { |
3525 | 0 | tsk_error_reset(); |
3526 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
3527 | 0 | } |
3528 | 0 | tsk_error_set_errstr2("ext2fs_open: superblock"); |
3529 | 0 | fs->tag = 0; |
3530 | 0 | free(ext2fs->fs); |
3531 | 0 | tsk_fs_free((TSK_FS_INFO *)ext2fs); |
3532 | 0 | return NULL; |
3533 | 0 | } |
3534 | | |
3535 | | /* |
3536 | | * Verify we are looking at an EXTxFS image |
3537 | | */ |
3538 | 0 | if (tsk_fs_guessu16(fs, ext2fs->fs->s_magic, EXT2FS_FS_MAGIC)) { |
3539 | 0 | fs->tag = 0; |
3540 | 0 | free(ext2fs->fs); |
3541 | 0 | tsk_fs_free((TSK_FS_INFO *)ext2fs); |
3542 | 0 | tsk_error_reset(); |
3543 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
3544 | 0 | tsk_error_set_errstr("not an EXTxFS file system (magic)"); |
3545 | 0 | if (tsk_verbose) |
3546 | 0 | fprintf(stderr, "ext2fs_open: invalid magic\n"); |
3547 | 0 | return NULL; |
3548 | 0 | } |
3549 | | |
3550 | 0 | if (tsk_verbose) { |
3551 | 0 | if (tsk_getu32(fs->endian, ext2fs->fs->s_feature_ro_compat) & |
3552 | 0 | EXT2FS_FEATURE_RO_COMPAT_SPARSE_SUPER) |
3553 | 0 | tsk_fprintf(stderr, "File system has sparse super blocks\n"); |
3554 | |
|
3555 | 0 | tsk_fprintf(stderr, "First data block is %" PRIu32 "\n", |
3556 | 0 | tsk_getu32(fs->endian, ext2fs->fs->s_first_data_block)); |
3557 | 0 | } |
3558 | | |
3559 | | /* If autodetect was given, look for the journal */ |
3560 | 0 | if (ftype == TSK_FS_TYPE_EXT_DETECT) { |
3561 | 0 | if (tsk_getu32(fs->endian, ext2fs->fs->s_feature_incompat) & |
3562 | 0 | EXT2FS_FEATURE_INCOMPAT_EXTENTS) { |
3563 | 0 | fs->ftype = TSK_FS_TYPE_EXT4; |
3564 | 0 | fs->flags = (TSK_FS_INFO_FLAG_ENUM) (fs->flags | TSK_FS_INFO_FLAG_HAVE_NANOSEC); |
3565 | 0 | } |
3566 | 0 | else if (tsk_getu32(fs->endian, ext2fs->fs->s_feature_compat) & |
3567 | 0 | EXT2FS_FEATURE_COMPAT_HAS_JOURNAL) |
3568 | 0 | fs->ftype = TSK_FS_TYPE_EXT3; |
3569 | 0 | else |
3570 | 0 | fs->ftype = TSK_FS_TYPE_EXT2; |
3571 | 0 | } |
3572 | 0 | fs->duname = "Fragment"; |
3573 | | |
3574 | | |
3575 | | /* we need to figure out if dentries are v1 or v2 */ |
3576 | 0 | if (tsk_getu32(fs->endian, ext2fs->fs->s_feature_incompat) & |
3577 | 0 | EXT2FS_FEATURE_INCOMPAT_FILETYPE) |
3578 | 0 | ext2fs->deentry_type = EXT2_DE_V2; |
3579 | 0 | else |
3580 | 0 | ext2fs->deentry_type = EXT2_DE_V1; |
3581 | | |
3582 | | |
3583 | | /* |
3584 | | * Calculate the meta data info |
3585 | | */ |
3586 | 0 | fs->inum_count = tsk_getu32(fs->endian, ext2fs->fs->s_inodes_count) + 1; // we are adding 1 in this calc to account for Orphans directory |
3587 | 0 | fs->last_inum = fs->inum_count; |
3588 | 0 | fs->first_inum = EXT2FS_FIRSTINO; |
3589 | 0 | fs->root_inum = EXT2FS_ROOTINO; |
3590 | |
|
3591 | 0 | if (fs->inum_count < 10) { |
3592 | 0 | fs->tag = 0; |
3593 | 0 | free(ext2fs->fs); |
3594 | 0 | tsk_fs_free((TSK_FS_INFO *)ext2fs); |
3595 | 0 | tsk_error_reset(); |
3596 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
3597 | 0 | tsk_error_set_errstr("Not an EXTxFS file system (inum count)"); |
3598 | 0 | if (tsk_verbose) |
3599 | 0 | fprintf(stderr, "ext2fs_open: two few inodes\n"); |
3600 | 0 | return NULL; |
3601 | 0 | } |
3602 | | |
3603 | | |
3604 | | /* Set the size of the inode, but default to our data structure |
3605 | | * size if it is larger */ |
3606 | 0 | ext2fs->inode_size = tsk_getu16(fs->endian, ext2fs->fs->s_inode_size); |
3607 | 0 | if (ext2fs->inode_size < sizeof(ext2fs_inode)) { |
3608 | 0 | if (tsk_verbose) |
3609 | 0 | tsk_fprintf(stderr, "SB inode size is small"); |
3610 | 0 | } |
3611 | | |
3612 | | |
3613 | | /* |
3614 | | * Calculate the block info |
3615 | | */ |
3616 | 0 | fs->dev_bsize = img_info->sector_size; |
3617 | 0 | if (tsk_getu32(fs->endian, |
3618 | 0 | ext2fs->fs-> |
3619 | 0 | s_feature_incompat) & EXT2FS_FEATURE_INCOMPAT_64BIT) { |
3620 | | // printf("DEBUG fs_open: 64bit file system\n"); |
3621 | 0 | fs->block_count = |
3622 | 0 | ext4_getu64(fs->endian, ext2fs->fs->s_blocks_count_hi, |
3623 | 0 | ext2fs->fs->s_blocks_count); |
3624 | 0 | } |
3625 | 0 | else { |
3626 | 0 | fs->block_count = |
3627 | 0 | tsk_getu32(fs->endian, ext2fs->fs->s_blocks_count); |
3628 | 0 | } |
3629 | 0 | fs->first_block = 0; |
3630 | 0 | fs->last_block_act = fs->last_block = fs->block_count - 1; |
3631 | 0 | ext2fs->first_data_block = |
3632 | 0 | tsk_getu32(fs->endian, ext2fs->fs->s_first_data_block); |
3633 | |
|
3634 | 0 | if (tsk_getu32(fs->endian, ext2fs->fs->s_log_block_size) != |
3635 | 0 | tsk_getu32(fs->endian, ext2fs->fs->s_log_frag_size)) { |
3636 | 0 | fs->tag = 0; |
3637 | 0 | free(ext2fs->fs); |
3638 | 0 | tsk_fs_free((TSK_FS_INFO *)ext2fs); |
3639 | 0 | tsk_error_reset(); |
3640 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
3641 | 0 | tsk_error_set_errstr |
3642 | 0 | ("This file system has fragments that are a different size than blocks, which is not currently supported\nContact brian with details of the system that created this image"); |
3643 | 0 | if (tsk_verbose) |
3644 | 0 | fprintf(stderr, |
3645 | 0 | "ext2fs_open: fragment size not equal to block size\n"); |
3646 | 0 | return NULL; |
3647 | 0 | } |
3648 | | |
3649 | 0 | if (tsk_getu32(fs->endian, ext2fs->fs->s_log_block_size) >= sizeof(uint32_t) * 8) { |
3650 | 0 | free(ext2fs->fs); |
3651 | 0 | tsk_fs_free((TSK_FS_INFO *)ext2fs); |
3652 | 0 | tsk_error_reset(); |
3653 | 0 | tsk_error_set_errno(TSK_ERR_FS_CORRUPT); |
3654 | 0 | tsk_error_set_errstr("Block size too large"); |
3655 | 0 | if (tsk_verbose) |
3656 | 0 | fprintf(stderr, |
3657 | 0 | "ext2fs_open: block size too large\n"); |
3658 | 0 | return NULL; |
3659 | 0 | } |
3660 | | |
3661 | 0 | fs->block_size = |
3662 | 0 | EXT2FS_MIN_BLOCK_SIZE << tsk_getu32(fs->endian, |
3663 | 0 | ext2fs->fs->s_log_block_size); |
3664 | | |
3665 | | // sanity check |
3666 | 0 | if ((fs->block_size == 0) || (fs->block_size % 512)) { |
3667 | 0 | fs->tag = 0; |
3668 | 0 | free(ext2fs->fs); |
3669 | 0 | tsk_fs_free((TSK_FS_INFO *)ext2fs); |
3670 | 0 | tsk_error_reset(); |
3671 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
3672 | 0 | tsk_error_set_errstr("Not an EXTxFS file system (block size)"); |
3673 | 0 | if (tsk_verbose) |
3674 | 0 | fprintf(stderr, "ext2fs_open: invalid block size\n"); |
3675 | 0 | return NULL; |
3676 | 0 | } |
3677 | | |
3678 | | // determine the last block we have in this image |
3679 | 0 | if ((TSK_DADDR_T) ((img_info->size - offset) / fs->block_size) < |
3680 | 0 | fs->block_count) |
3681 | 0 | fs->last_block_act = |
3682 | 0 | (img_info->size - offset) / fs->block_size - 1; |
3683 | | |
3684 | | |
3685 | | /* The group descriptors are located in the block following the |
3686 | | * super block */ |
3687 | 0 | ext2fs->groups_offset = |
3688 | 0 | roundup((EXT2FS_SBOFF + sizeof(ext2fs_sb)), fs->block_size); |
3689 | | |
3690 | | // sanity check to avoid divide by zero issues |
3691 | 0 | if (tsk_getu32(fs->endian, ext2fs->fs->s_blocks_per_group) == 0) { |
3692 | 0 | fs->tag = 0; |
3693 | 0 | free(ext2fs->fs); |
3694 | 0 | tsk_fs_free((TSK_FS_INFO *)ext2fs); |
3695 | 0 | tsk_error_reset(); |
3696 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
3697 | 0 | tsk_error_set_errstr("Not an EXTxFS file system (blocks per group)"); |
3698 | 0 | if (tsk_verbose) |
3699 | 0 | fprintf(stderr, "ext2fs_open: blocks per group is 0\n"); |
3700 | 0 | return NULL; |
3701 | 0 | } |
3702 | 0 | if (tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group) == 0) { |
3703 | 0 | fs->tag = 0; |
3704 | 0 | free(ext2fs->fs); |
3705 | 0 | tsk_fs_free((TSK_FS_INFO *)ext2fs); |
3706 | 0 | tsk_error_reset(); |
3707 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
3708 | 0 | tsk_error_set_errstr("Not an EXTxFS file system (inodes per group)"); |
3709 | 0 | if (tsk_verbose) |
3710 | 0 | fprintf(stderr, "ext2fs_open: inodes per group is 0\n"); |
3711 | 0 | return NULL; |
3712 | 0 | } |
3713 | | |
3714 | 0 | if (tsk_getu32(fs->endian, |
3715 | 0 | ext2fs->fs-> |
3716 | 0 | s_feature_incompat) & EXT2FS_FEATURE_INCOMPAT_64BIT) { |
3717 | 0 | ext2fs->groups_count = |
3718 | 0 | (EXT2_GRPNUM_T) ((ext4_getu64(fs->endian, |
3719 | 0 | ext2fs->fs->s_blocks_count_hi, |
3720 | 0 | ext2fs->fs->s_blocks_count) |
3721 | 0 | - ext2fs->first_data_block + tsk_getu32(fs->endian, |
3722 | 0 | ext2fs->fs->s_blocks_per_group) - 1) |
3723 | 0 | / tsk_getu32(fs->endian, ext2fs->fs->s_blocks_per_group)); |
3724 | 0 | } |
3725 | 0 | else { |
3726 | 0 | ext2fs->groups_count = |
3727 | 0 | (EXT2_GRPNUM_T) ((tsk_getu32(fs->endian, |
3728 | 0 | ext2fs->fs->s_blocks_count) - |
3729 | 0 | ext2fs->first_data_block + tsk_getu32(fs->endian, |
3730 | 0 | ext2fs->fs->s_blocks_per_group) - |
3731 | 0 | 1) / tsk_getu32(fs->endian, |
3732 | 0 | ext2fs->fs->s_blocks_per_group)); |
3733 | 0 | } |
3734 | | |
3735 | | /* Volume ID */ |
3736 | 0 | for (fs->fs_id_used = 0; fs->fs_id_used < 16; fs->fs_id_used++) { |
3737 | 0 | fs->fs_id[fs->fs_id_used] = ext2fs->fs->s_uuid[fs->fs_id_used]; |
3738 | 0 | } |
3739 | | |
3740 | | /* Set the generic function pointers */ |
3741 | 0 | fs->inode_walk = ext2fs_inode_walk; |
3742 | 0 | fs->block_walk = ext2fs_block_walk; |
3743 | 0 | fs->block_getflags = ext2fs_block_getflags; |
3744 | |
|
3745 | 0 | fs->get_default_attr_type = tsk_fs_unix_get_default_attr_type; |
3746 | | //fs->load_attrs = tsk_fs_unix_make_data_run; |
3747 | 0 | fs->load_attrs = ext2fs_load_attrs; |
3748 | |
|
3749 | 0 | fs->file_add_meta = ext2fs_inode_lookup; |
3750 | 0 | fs->dir_open_meta = ext2fs_dir_open_meta; |
3751 | 0 | fs->fsstat = ext2fs_fsstat; |
3752 | 0 | fs->fscheck = ext2fs_fscheck; |
3753 | 0 | fs->istat = ext2fs_istat; |
3754 | 0 | fs->name_cmp = tsk_fs_unix_name_cmp; |
3755 | 0 | fs->close = ext2fs_close; |
3756 | | |
3757 | | |
3758 | | /* Journal */ |
3759 | 0 | fs->journ_inum = tsk_getu32(fs->endian, ext2fs->fs->s_journal_inum); |
3760 | 0 | fs->jblk_walk = ext2fs_jblk_walk; |
3761 | 0 | fs->jentry_walk = ext2fs_jentry_walk; |
3762 | 0 | fs->jopen = ext2fs_jopen; |
3763 | | |
3764 | | /* initialize the caches */ |
3765 | | /* inode map */ |
3766 | 0 | ext2fs->imap_buf = NULL; |
3767 | 0 | ext2fs->imap_grp_num = 0xffffffff; |
3768 | | |
3769 | | /* block map */ |
3770 | 0 | ext2fs->bmap_buf = NULL; |
3771 | 0 | ext2fs->bmap_grp_num = 0xffffffff; |
3772 | | |
3773 | | /* group descriptor */ |
3774 | 0 | ext2fs->grp_buf = NULL; |
3775 | 0 | ext2fs->grp_num = 0xffffffff; |
3776 | | |
3777 | | |
3778 | | /* |
3779 | | * Print some stats. |
3780 | | */ |
3781 | 0 | if (tsk_verbose) |
3782 | 0 | tsk_fprintf(stderr, |
3783 | 0 | "inodes %" PRIu32 " root ino %" PRIuINUM " blocks %" PRIu32 |
3784 | 0 | " blocks/group %" PRIu32 "\n", tsk_getu32(fs->endian, |
3785 | 0 | ext2fs->fs->s_inodes_count), |
3786 | 0 | fs->root_inum, tsk_getu32(fs->endian, |
3787 | 0 | ext2fs->fs->s_blocks_count), tsk_getu32(fs->endian, |
3788 | 0 | ext2fs->fs->s_blocks_per_group)); |
3789 | |
|
3790 | 0 | tsk_init_lock(&ext2fs->lock); |
3791 | |
|
3792 | 0 | return fs; |
3793 | 0 | } |