/src/sleuthkit/tsk/fs/ffs.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | /* TCT |
16 | | * LICENSE |
17 | | * This software is distributed under the IBM Public License. |
18 | | * AUTHOR(S) |
19 | | * Wietse Venema |
20 | | * IBM T.J. Watson Research |
21 | | * P.O. Box 704 |
22 | | * Yorktown Heights, NY 10598, USA |
23 | | --*/ |
24 | | |
25 | | /** |
26 | | * \file ffs.c |
27 | | * Contains the internal TSK UFS / FFS file system functions |
28 | | */ |
29 | | |
30 | | #include "tsk_fs_i.h" |
31 | | #include "tsk_ffs.h" |
32 | | |
33 | | #include <memory> |
34 | | |
35 | | /* ffs_group_load - load cylinder group descriptor info into cache |
36 | | * |
37 | | * Note: This routine assumes &ffs->lock is locked by the caller. |
38 | | * |
39 | | * return 1 on error and 0 on success |
40 | | * */ |
41 | | static uint8_t |
42 | | ffs_group_load(FFS_INFO * ffs, FFS_GRPNUM_T grp_num) |
43 | 0 | { |
44 | 0 | TSK_DADDR_T addr; |
45 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ffs->fs_info; |
46 | | |
47 | | /* |
48 | | * Sanity check |
49 | | */ |
50 | 0 | if (grp_num >= ffs->groups_count) { |
51 | 0 | tsk_error_reset(); |
52 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
53 | 0 | tsk_error_set_errstr |
54 | 0 | ("ffs_group_load: invalid cylinder group number: %" PRI_FFSGRP |
55 | 0 | "", grp_num); |
56 | 0 | return 1; |
57 | 0 | } |
58 | | |
59 | | /* |
60 | | * Allocate/read cylinder group info on the fly. Trust that a cylinder |
61 | | * group always fits within a logical disk block (as promised in the |
62 | | * 4.4BSD <ufs/ffs/fs.h> include file). |
63 | | */ |
64 | 0 | if (ffs->grp_buf == NULL) { |
65 | 0 | if ((ffs->grp_buf = (char*) tsk_malloc(ffs->ffsbsize_b)) == NULL) { |
66 | 0 | return 1; |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | 0 | addr = cgtod_lcl(fs, ffs->fs.sb1, grp_num); |
71 | 0 | if (ffs->grp_addr != addr) { |
72 | 0 | ffs_cgd *cg; |
73 | 0 | ssize_t cnt; |
74 | 0 | cnt = tsk_fs_read_block(fs, addr, ffs->grp_buf, ffs->ffsbsize_b); |
75 | 0 | if (cnt != ffs->ffsbsize_b) { |
76 | 0 | if (cnt >= 0) { |
77 | 0 | tsk_error_reset(); |
78 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
79 | 0 | } |
80 | 0 | tsk_error_set_errstr2("ffs_group_load: Group %" PRI_FFSGRP |
81 | 0 | " at %" PRIuDADDR, grp_num, addr); |
82 | 0 | return 1; |
83 | 0 | } |
84 | 0 | ffs->grp_addr = addr; |
85 | | |
86 | | /* Perform a sanity check on the data to make sure offsets are in range */ |
87 | 0 | cg = (ffs_cgd *) ffs->grp_buf; |
88 | 0 | if ((tsk_gets32(fs->endian, cg->cg_iusedoff) > (int)ffs->ffsbsize_b) |
89 | 0 | || (tsk_gets32(fs->endian, cg->cg_freeoff) > (int)ffs->ffsbsize_b)) { |
90 | 0 | tsk_error_reset(); |
91 | 0 | tsk_error_set_errno(TSK_ERR_FS_CORRUPT); |
92 | 0 | tsk_error_set_errstr2("ffs_group_load: Group %" PRI_FFSGRP |
93 | 0 | " descriptor offsets too large at %" PRIuDADDR, grp_num, |
94 | 0 | addr); |
95 | 0 | return 1; |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | 0 | ffs->grp_num = grp_num; |
100 | 0 | return 0; |
101 | 0 | } |
102 | | |
103 | | |
104 | | /* |
105 | | * ffs_dinode_load - read disk inode and load the data into ffs_inode structure |
106 | | * |
107 | | * Return 0 on success and 1 on error |
108 | | */ |
109 | | static uint8_t |
110 | | ffs_dinode_load(FFS_INFO * ffs, TSK_INUM_T inum, ffs_inode * dino_buf) |
111 | 0 | { |
112 | 0 | TSK_DADDR_T addr; |
113 | 0 | TSK_OFF_T offs; |
114 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ffs->fs_info; |
115 | | |
116 | | /* |
117 | | * Sanity check. |
118 | | * Use last_num-1 to account for virtual Orphan directory in last_inum. |
119 | | */ |
120 | 0 | if (inum < fs->first_inum || inum > fs->last_inum - 1) { |
121 | 0 | tsk_error_reset(); |
122 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); |
123 | 0 | tsk_error_set_errstr("ffs_dinode_load: address: %" PRIuINUM, inum); |
124 | 0 | return 1; |
125 | 0 | } |
126 | | |
127 | | /* |
128 | | * Allocate/read the inode table buffer on the fly. |
129 | | */ |
130 | | |
131 | | /* lock access to itbl_buf */ |
132 | 0 | tsk_take_lock(&ffs->lock); |
133 | |
|
134 | 0 | if (ffs->itbl_buf == NULL) { |
135 | 0 | if ((ffs->itbl_buf = (char*) tsk_malloc(ffs->ffsbsize_b)) == NULL) { |
136 | 0 | tsk_release_lock(&ffs->lock); |
137 | 0 | return 1; |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | |
142 | | /* UFS2 is different because it does not initialize all inodes |
143 | | * when the file system is created. Therefore we need to check |
144 | | * the group descriptor to find out if this is in the valid |
145 | | * range |
146 | | */ |
147 | 0 | if (fs->ftype == TSK_FS_TYPE_FFS2) { |
148 | 0 | ffs_cgd2 *cg2; |
149 | 0 | FFS_GRPNUM_T grp_num; |
150 | |
|
151 | 0 | if (dino_buf == NULL) { |
152 | 0 | tsk_release_lock(&ffs->lock); |
153 | 0 | return 1; |
154 | 0 | } |
155 | | |
156 | | /* Lookup the cylinder group descriptor if it isn't |
157 | | * cached |
158 | | */ |
159 | 0 | grp_num = (FFS_GRPNUM_T) itog_lcl(fs, ffs->fs.sb1, inum); |
160 | 0 | if (ffs_group_load(ffs, grp_num)) { |
161 | 0 | tsk_release_lock(&ffs->lock); |
162 | 0 | return 1; |
163 | 0 | } |
164 | | |
165 | 0 | cg2 = (ffs_cgd2 *) ffs->grp_buf; |
166 | | |
167 | | /* If the inode is not init, then do not worry about it */ |
168 | 0 | if ((inum - grp_num * tsk_getu32(fs->endian, |
169 | 0 | ffs->fs.sb2->cg_inode_num)) >= tsk_getu32(fs->endian, |
170 | 0 | cg2->cg_initediblk)) { |
171 | 0 | memset((char *) dino_buf, 0, sizeof(ffs_inode2)); |
172 | 0 | } |
173 | | |
174 | 0 | else { |
175 | | /* Get the base and offset addr for the inode in the tbl */ |
176 | 0 | addr = itod_lcl(fs, ffs->fs.sb1, inum); |
177 | |
|
178 | 0 | if (ffs->itbl_addr != addr) { |
179 | 0 | ssize_t cnt; |
180 | 0 | cnt = tsk_fs_read_block |
181 | 0 | (fs, addr, ffs->itbl_buf, ffs->ffsbsize_b); |
182 | 0 | if (cnt != ffs->ffsbsize_b) { |
183 | 0 | tsk_release_lock(&ffs->lock); |
184 | 0 | if (cnt >= 0) { |
185 | 0 | tsk_error_reset(); |
186 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
187 | 0 | } |
188 | 0 | tsk_error_set_errstr2 |
189 | 0 | ("ffs_dinode_load: FFS2 inode table at %" |
190 | 0 | PRIuDADDR, addr); |
191 | 0 | return 1; |
192 | 0 | } |
193 | 0 | ffs->itbl_addr = addr; |
194 | 0 | } |
195 | | |
196 | 0 | offs = itoo_lcl(fs, ffs->fs.sb2, inum) * sizeof(ffs_inode2); |
197 | |
|
198 | 0 | memcpy((char *) dino_buf, ffs->itbl_buf + offs, |
199 | 0 | sizeof(ffs_inode2)); |
200 | 0 | } |
201 | 0 | } |
202 | 0 | else { |
203 | 0 | if (dino_buf == NULL) { |
204 | 0 | tsk_release_lock(&ffs->lock); |
205 | 0 | return 1; |
206 | 0 | } |
207 | | |
208 | 0 | addr = itod_lcl(fs, ffs->fs.sb1, inum); |
209 | 0 | if (ffs->itbl_addr != addr) { |
210 | 0 | ssize_t cnt; |
211 | 0 | cnt = |
212 | 0 | tsk_fs_read_block(fs, addr, ffs->itbl_buf, |
213 | 0 | ffs->ffsbsize_b); |
214 | 0 | if (cnt != ffs->ffsbsize_b) { |
215 | 0 | tsk_release_lock(&ffs->lock); |
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 |
221 | 0 | ("ffs_dinode_load: FFS1 inode table at %" PRIuDADDR, |
222 | 0 | addr); |
223 | 0 | return 1; |
224 | 0 | } |
225 | 0 | ffs->itbl_addr = addr; |
226 | 0 | } |
227 | | |
228 | 0 | offs = itoo_lcl(fs, ffs->fs.sb1, inum) * sizeof(ffs_inode1); |
229 | |
|
230 | 0 | memcpy((char *) dino_buf, ffs->itbl_buf + offs, |
231 | 0 | sizeof(ffs_inode1)); |
232 | 0 | } |
233 | | |
234 | 0 | tsk_release_lock(&ffs->lock); |
235 | |
|
236 | 0 | return 0; |
237 | 0 | } |
238 | | |
239 | | |
240 | | static TSK_FS_META_TYPE_ENUM |
241 | | ffsmode2tsktype(uint16_t a_mode) |
242 | 0 | { |
243 | 0 | switch (a_mode & FFS_IN_FMT) { |
244 | 0 | case FFS_IN_REG: |
245 | 0 | return TSK_FS_META_TYPE_REG; |
246 | 0 | case FFS_IN_DIR: |
247 | 0 | return TSK_FS_META_TYPE_DIR; |
248 | 0 | case FFS_IN_SOCK: |
249 | 0 | return TSK_FS_META_TYPE_SOCK; |
250 | 0 | case FFS_IN_LNK: |
251 | 0 | return TSK_FS_META_TYPE_LNK; |
252 | 0 | case FFS_IN_BLK: |
253 | 0 | return TSK_FS_META_TYPE_BLK; |
254 | 0 | case FFS_IN_CHR: |
255 | 0 | return TSK_FS_META_TYPE_CHR; |
256 | 0 | case FFS_IN_FIFO: |
257 | 0 | return TSK_FS_META_TYPE_FIFO; |
258 | 0 | case FFS_IN_SHAD: |
259 | 0 | return TSK_FS_META_TYPE_SHAD; |
260 | 0 | case FFS_IN_WHT: |
261 | 0 | return TSK_FS_META_TYPE_WHT; |
262 | 0 | default: |
263 | 0 | return TSK_FS_META_TYPE_UNDEF; |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | | static TSK_FS_META_MODE_ENUM |
268 | | ffsmode2tskmode(uint16_t a_mode) |
269 | 0 | { |
270 | 0 | uint16_t mode = 0; |
271 | |
|
272 | 0 | if (a_mode & FFS_IN_ISUID) |
273 | 0 | mode |= TSK_FS_META_MODE_ISUID; |
274 | 0 | if (a_mode & FFS_IN_ISGID) |
275 | 0 | mode |= TSK_FS_META_MODE_ISGID; |
276 | 0 | if (a_mode & FFS_IN_ISVTX) |
277 | 0 | mode |= TSK_FS_META_MODE_ISVTX; |
278 | |
|
279 | 0 | if (a_mode & FFS_IN_IRUSR) |
280 | 0 | mode |= TSK_FS_META_MODE_IRUSR; |
281 | 0 | if (a_mode & FFS_IN_IWUSR) |
282 | 0 | mode |= TSK_FS_META_MODE_IWUSR; |
283 | 0 | if (a_mode & FFS_IN_IXUSR) |
284 | 0 | mode |= TSK_FS_META_MODE_IXUSR; |
285 | |
|
286 | 0 | if (a_mode & FFS_IN_IRGRP) |
287 | 0 | mode |= TSK_FS_META_MODE_IRGRP; |
288 | 0 | if (a_mode & FFS_IN_IWGRP) |
289 | 0 | mode |= TSK_FS_META_MODE_IWGRP; |
290 | 0 | if (a_mode & FFS_IN_IXGRP) |
291 | 0 | mode |= TSK_FS_META_MODE_IXGRP; |
292 | |
|
293 | 0 | if (a_mode & FFS_IN_IROTH) |
294 | 0 | mode |= TSK_FS_META_MODE_IROTH; |
295 | 0 | if (a_mode & FFS_IN_IWOTH) |
296 | 0 | mode |= TSK_FS_META_MODE_IWOTH; |
297 | 0 | if (a_mode & FFS_IN_IXOTH) |
298 | 0 | mode |= TSK_FS_META_MODE_IXOTH; |
299 | |
|
300 | 0 | return (TSK_FS_META_MODE_ENUM) mode; |
301 | 0 | } |
302 | | |
303 | | /* ffs_dinode_copy - copy cached disk inode to generic inode |
304 | | * |
305 | | * Return 1 on error and 0 on success |
306 | | */ |
307 | | static uint8_t |
308 | | ffs_dinode_copy(FFS_INFO * ffs, TSK_FS_META * fs_meta, |
309 | | TSK_INUM_T dino_inum, const ffs_inode * dino_buf) |
310 | 0 | { |
311 | 0 | int i, j; |
312 | 0 | unsigned int count; |
313 | 0 | TSK_FS_INFO *fs = &(ffs->fs_info); |
314 | 0 | FFS_GRPNUM_T grp_num; |
315 | 0 | ffs_cgd *cg; |
316 | 0 | unsigned char *inosused = NULL; |
317 | 0 | TSK_INUM_T ibase; |
318 | |
|
319 | 0 | if (dino_buf == NULL) { |
320 | 0 | tsk_error_reset(); |
321 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
322 | 0 | tsk_error_set_errstr("ffs_dinode_copy: dino_buf is NULL"); |
323 | 0 | return 1; |
324 | 0 | } |
325 | | |
326 | 0 | fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY; |
327 | 0 | if (fs_meta->attr) { |
328 | 0 | tsk_fs_attrlist_markunused(fs_meta->attr); |
329 | 0 | } |
330 | |
|
331 | 0 | fs_meta->flags = (TSK_FS_META_FLAG_ENUM) 0; |
332 | 0 | fs_meta->seq = 0; |
333 | | |
334 | | /* If the symlink field is set from a previous run, then free it */ |
335 | 0 | if (fs_meta->link) { |
336 | 0 | free(fs_meta->link); |
337 | 0 | fs_meta->link = NULL; |
338 | 0 | } |
339 | |
|
340 | 0 | fs_meta->addr = dino_inum; |
341 | | |
342 | | /* OpenBSD and FreeBSD style */ |
343 | 0 | if (fs->ftype == TSK_FS_TYPE_FFS1) { |
344 | 0 | ffs_inode1 *in = (ffs_inode1 *) dino_buf; |
345 | 0 | TSK_DADDR_T *addr_ptr; |
346 | |
|
347 | 0 | fs_meta->mode = |
348 | 0 | ffsmode2tskmode(tsk_getu16(fs->endian, in->di_mode)); |
349 | 0 | fs_meta->type = |
350 | 0 | ffsmode2tsktype(tsk_getu16(fs->endian, in->di_mode)); |
351 | |
|
352 | 0 | fs_meta->nlink = tsk_gets16(fs->endian, in->di_nlink); |
353 | 0 | fs_meta->size = tsk_getu64(fs->endian, in->di_size); |
354 | 0 | fs_meta->uid = tsk_getu32(fs->endian, in->di_uid); |
355 | 0 | fs_meta->gid = tsk_getu32(fs->endian, in->di_gid); |
356 | |
|
357 | 0 | fs_meta->mtime = tsk_gets32(fs->endian, in->di_mtime); |
358 | 0 | fs_meta->atime = tsk_gets32(fs->endian, in->di_atime); |
359 | 0 | fs_meta->ctime = tsk_gets32(fs->endian, in->di_ctime); |
360 | 0 | fs_meta->crtime = 0; |
361 | 0 | fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano = |
362 | 0 | fs_meta->crtime_nano = 0; |
363 | |
|
364 | 0 | if (fs_meta->content_len < FFS_FILE_CONTENT_LEN) { |
365 | 0 | if ((fs_meta = |
366 | 0 | tsk_fs_meta_realloc(fs_meta, |
367 | 0 | FFS_FILE_CONTENT_LEN)) == NULL) { |
368 | 0 | return 1; |
369 | 0 | } |
370 | 0 | } |
371 | 0 | addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr; |
372 | |
|
373 | 0 | for (i = 0; i < FFS_NDADDR; i++) |
374 | 0 | addr_ptr[i] = tsk_gets32(fs->endian, in->di_db[i]); |
375 | |
|
376 | 0 | for (i = 0; i < FFS_NIADDR; i++) |
377 | 0 | addr_ptr[FFS_NDADDR + i] = |
378 | 0 | tsk_gets32(fs->endian, in->di_ib[i]); |
379 | | |
380 | | |
381 | | /* set the link string (if the file is a link) |
382 | | * The size check is a sanity check so that we don't try and allocate |
383 | | * a huge amount of memory for a bad inode value |
384 | | */ |
385 | 0 | if ((fs_meta->type == TSK_FS_META_TYPE_LNK) |
386 | 0 | && (fs_meta->size < FFS_MAXPATHLEN) |
387 | 0 | && (fs_meta->size >= 0)) { |
388 | 0 | int i; |
389 | |
|
390 | 0 | fs_meta->link = (char*) tsk_malloc((size_t) fs_meta->size + 1); |
391 | 0 | if (fs_meta->link == NULL) { |
392 | 0 | return 1; |
393 | 0 | } |
394 | | |
395 | 0 | count = 0; /* index into the link array */ |
396 | | |
397 | | /* it is located directly in the pointers */ |
398 | 0 | if (fs_meta->size < 4 * (FFS_NDADDR + FFS_NIADDR)) { |
399 | 0 | char *ptr; |
400 | | |
401 | | /* Direct block pointer locations */ |
402 | 0 | for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) { |
403 | 0 | ptr = (char *) &in->di_db[i]; |
404 | 0 | for (j = 0; j < 4 && count < fs_meta->size; j++) |
405 | 0 | fs_meta->link[count++] = ptr[j]; |
406 | 0 | } |
407 | | |
408 | | /* indirect block pointers */ |
409 | 0 | for (i = 0; i < FFS_NIADDR && count < fs_meta->size; i++) { |
410 | 0 | ptr = (char *) &in->di_ib[i]; |
411 | 0 | for (j = 0; j < 4 && count < fs_meta->size; j++) |
412 | 0 | fs_meta->link[count++] = ptr[j]; |
413 | 0 | } |
414 | |
|
415 | 0 | fs_meta->link[count] = '\0'; |
416 | | |
417 | | /* clear the values to avoid other code from reading them */ |
418 | 0 | memset(fs_meta->content_ptr, 0, fs_meta->content_len); |
419 | 0 | } |
420 | | |
421 | | /* it is in blocks (the regular way) */ |
422 | 0 | else { |
423 | 0 | char *buf; |
424 | 0 | char *ptr = fs_meta->link; |
425 | |
|
426 | 0 | if ((buf = (char *) |
427 | 0 | tsk_malloc(fs->block_size)) == NULL) { |
428 | 0 | return 1; |
429 | 0 | } |
430 | | |
431 | | /* there is a max link length of 1000, so we should never |
432 | | * need the indirect blocks |
433 | | */ |
434 | 0 | for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) { |
435 | 0 | ssize_t cnt; |
436 | 0 | TSK_DADDR_T *addr_ptr = |
437 | 0 | (TSK_DADDR_T *) fs_meta->content_ptr; |
438 | | |
439 | | /* Do we need the entire block, or just part of it? */ |
440 | 0 | int read_count = |
441 | 0 | (fs_meta->size - count < |
442 | 0 | fs->block_size) ? (int) fs_meta->size - |
443 | 0 | count : fs->block_size; |
444 | |
|
445 | 0 | cnt = |
446 | 0 | tsk_fs_read_block(fs, addr_ptr[i], |
447 | 0 | buf, fs->block_size); |
448 | 0 | if (cnt != fs->block_size) { |
449 | 0 | if (cnt >= 0) { |
450 | 0 | tsk_error_reset(); |
451 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
452 | 0 | } |
453 | 0 | tsk_error_set_errstr2 |
454 | 0 | ("ffs_dinode_copy: FFS1A symlink dest at %" |
455 | 0 | PRIuDADDR, addr_ptr[i]); |
456 | 0 | free(buf); |
457 | 0 | return 1; |
458 | 0 | } |
459 | | |
460 | 0 | memcpy(ptr, buf, read_count); |
461 | 0 | count += read_count; |
462 | 0 | ptr = (char *) ((uintptr_t) ptr + read_count); |
463 | 0 | } |
464 | | /* terminate the string */ |
465 | 0 | *ptr = '\0'; |
466 | | |
467 | | /* Clean up name */ |
468 | 0 | i = 0; |
469 | 0 | while (fs_meta->link[i] != '\0') { |
470 | 0 | if (TSK_IS_CNTRL(fs_meta->link[i])) |
471 | 0 | fs_meta->link[i] = '^'; |
472 | 0 | i++; |
473 | 0 | } |
474 | |
|
475 | 0 | free(buf); |
476 | 0 | } |
477 | 0 | } /* end of symlink */ |
478 | 0 | } |
479 | | /* TSK_FS_TYPE_FFS1B - Solaris */ |
480 | 0 | else if (fs->ftype == TSK_FS_TYPE_FFS1B) { |
481 | 0 | ffs_inode1b *in = (ffs_inode1b *) dino_buf; |
482 | 0 | TSK_DADDR_T *addr_ptr; |
483 | |
|
484 | 0 | fs_meta->mode = |
485 | 0 | ffsmode2tskmode(tsk_getu16(fs->endian, in->di_mode)); |
486 | 0 | fs_meta->type = |
487 | 0 | ffsmode2tsktype(tsk_getu16(fs->endian, in->di_mode)); |
488 | |
|
489 | 0 | fs_meta->nlink = tsk_gets16(fs->endian, in->di_nlink); |
490 | 0 | fs_meta->size = tsk_getu64(fs->endian, in->di_size); |
491 | 0 | fs_meta->uid = tsk_getu32(fs->endian, in->di_uid); |
492 | 0 | fs_meta->gid = tsk_getu32(fs->endian, in->di_gid); |
493 | |
|
494 | 0 | fs_meta->mtime = tsk_gets32(fs->endian, in->di_mtime); |
495 | 0 | fs_meta->atime = tsk_gets32(fs->endian, in->di_atime); |
496 | 0 | fs_meta->ctime = tsk_gets32(fs->endian, in->di_ctime); |
497 | 0 | fs_meta->crtime = 0; |
498 | 0 | fs_meta->mtime_nano = fs_meta->atime_nano = fs_meta->ctime_nano = |
499 | 0 | fs_meta->crtime_nano = 0; |
500 | |
|
501 | 0 | if (fs_meta->content_len < FFS_FILE_CONTENT_LEN) { |
502 | 0 | if ((fs_meta = |
503 | 0 | tsk_fs_meta_realloc(fs_meta, |
504 | 0 | FFS_FILE_CONTENT_LEN)) == NULL) { |
505 | 0 | return 1; |
506 | 0 | } |
507 | 0 | } |
508 | 0 | addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr; |
509 | |
|
510 | 0 | for (i = 0; i < FFS_NDADDR; i++) |
511 | 0 | addr_ptr[i] = tsk_gets32(fs->endian, in->di_db[i]); |
512 | |
|
513 | 0 | for (i = 0; i < FFS_NIADDR; i++) |
514 | 0 | addr_ptr[FFS_NDADDR + i] = |
515 | 0 | tsk_gets32(fs->endian, in->di_ib[i]); |
516 | |
|
517 | 0 | if ((fs_meta->type == TSK_FS_META_TYPE_LNK) |
518 | 0 | && (fs_meta->size < FFS_MAXPATHLEN) |
519 | 0 | && (fs_meta->size >= 0)) { |
520 | |
|
521 | 0 | count = 0; /* index into the link array */ |
522 | | |
523 | | /* it is located directly in the pointers */ |
524 | 0 | if (fs_meta->size < 4 * (FFS_NDADDR + FFS_NIADDR)) { |
525 | 0 | char *ptr; |
526 | | |
527 | | /* Direct block pointer locations */ |
528 | 0 | for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) { |
529 | 0 | ptr = (char *) &in->di_db[i]; |
530 | 0 | for (j = 0; j < 4 && count < fs_meta->size; j++) |
531 | 0 | fs_meta->link[count++] = ptr[j]; |
532 | 0 | } |
533 | | |
534 | | /* indirect block pointers */ |
535 | 0 | for (i = 0; i < FFS_NIADDR && count < fs_meta->size; i++) { |
536 | 0 | ptr = (char *) &in->di_ib[i]; |
537 | 0 | for (j = 0; j < 4 && count < fs_meta->size; j++) |
538 | 0 | fs_meta->link[count++] = ptr[j]; |
539 | 0 | } |
540 | |
|
541 | 0 | fs_meta->link[count] = '\0'; |
542 | | |
543 | | /* clear the values to avoid other code from reading them */ |
544 | 0 | memset(fs_meta->content_ptr, 0, fs_meta->content_len); |
545 | 0 | } |
546 | | |
547 | | /* it is in blocks (the regular way) */ |
548 | 0 | else { |
549 | 0 | char *buf; |
550 | 0 | char *ptr; |
551 | |
|
552 | 0 | if ((buf = (char *) |
553 | 0 | tsk_malloc(fs->block_size)) == NULL) |
554 | 0 | return 1; |
555 | | |
556 | 0 | fs_meta->link = ptr = |
557 | 0 | (char*) tsk_malloc((size_t) fs_meta->size + 1); |
558 | 0 | if (fs_meta->link == NULL) { |
559 | 0 | free(buf); |
560 | 0 | return 1; |
561 | 0 | } |
562 | | |
563 | | /* there is a max link length of 1000, so we should never |
564 | | * need the indirect blocks |
565 | | */ |
566 | 0 | for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) { |
567 | 0 | ssize_t cnt; |
568 | 0 | TSK_DADDR_T *addr_ptr = |
569 | 0 | (TSK_DADDR_T *) fs_meta->content_ptr; |
570 | | |
571 | | /* Do we need the entire block, or just part of it? */ |
572 | 0 | int read_count = |
573 | 0 | (fs_meta->size - count < |
574 | 0 | fs->block_size) ? (int) fs_meta->size - |
575 | 0 | count : fs->block_size; |
576 | |
|
577 | 0 | cnt = |
578 | 0 | tsk_fs_read_block(fs, addr_ptr[i], |
579 | 0 | buf, fs->block_size); |
580 | 0 | if (cnt != fs->block_size) { |
581 | 0 | if (cnt >= 0) { |
582 | 0 | tsk_error_reset(); |
583 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
584 | 0 | } |
585 | 0 | tsk_error_set_errstr2 |
586 | 0 | ("ffs_dinode_copy: FFS1B symlink dest at %" |
587 | 0 | PRIuDADDR, addr_ptr[i]); |
588 | 0 | free(buf); |
589 | 0 | return 1; |
590 | 0 | } |
591 | | |
592 | 0 | memcpy(ptr, buf, read_count); |
593 | 0 | count += read_count; |
594 | 0 | ptr = (char *) ((uintptr_t) ptr + read_count); |
595 | 0 | } |
596 | | |
597 | | /* terminate the string */ |
598 | 0 | *ptr = '\0'; |
599 | |
|
600 | 0 | free(buf); |
601 | 0 | } |
602 | 0 | } |
603 | 0 | } |
604 | 0 | else if (fs->ftype == TSK_FS_TYPE_FFS2) { |
605 | 0 | ffs_inode2 *in = (ffs_inode2 *) dino_buf; |
606 | 0 | TSK_DADDR_T *addr_ptr; |
607 | |
|
608 | 0 | fs_meta->mode = |
609 | 0 | ffsmode2tskmode(tsk_getu16(fs->endian, in->di_mode)); |
610 | 0 | fs_meta->type = |
611 | 0 | ffsmode2tsktype(tsk_getu16(fs->endian, in->di_mode)); |
612 | |
|
613 | 0 | fs_meta->nlink = tsk_gets16(fs->endian, in->di_nlink); |
614 | 0 | fs_meta->size = tsk_getu64(fs->endian, in->di_size); |
615 | 0 | fs_meta->uid = tsk_getu32(fs->endian, in->di_uid); |
616 | 0 | fs_meta->gid = tsk_getu32(fs->endian, in->di_gid); |
617 | |
|
618 | 0 | fs_meta->mtime = (time_t) tsk_gets64(fs->endian, in->di_mtime); |
619 | 0 | fs_meta->atime = (time_t) tsk_gets64(fs->endian, in->di_atime); |
620 | 0 | fs_meta->ctime = (time_t) tsk_gets64(fs->endian, in->di_ctime); |
621 | 0 | fs_meta->crtime = 0; |
622 | 0 | fs_meta->mtime_nano = tsk_getu32(fs->endian, in->di_mtimensec); |
623 | 0 | fs_meta->atime_nano = tsk_getu32(fs->endian, in->di_atimensec); |
624 | 0 | fs_meta->ctime_nano = tsk_getu32(fs->endian, in->di_ctimensec); |
625 | 0 | fs_meta->crtime_nano = tsk_getu32(fs->endian, in->di_crtimensec); |
626 | |
|
627 | 0 | if (fs_meta->content_len < FFS_FILE_CONTENT_LEN) { |
628 | 0 | if ((fs_meta = |
629 | 0 | tsk_fs_meta_realloc(fs_meta, |
630 | 0 | FFS_FILE_CONTENT_LEN)) == NULL) { |
631 | 0 | return 1; |
632 | 0 | } |
633 | 0 | } |
634 | 0 | addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr; |
635 | |
|
636 | 0 | for (i = 0; i < FFS_NDADDR; i++) |
637 | 0 | addr_ptr[i] = tsk_gets64(fs->endian, in->di_db[i]); |
638 | |
|
639 | 0 | for (i = 0; i < FFS_NIADDR; i++) |
640 | 0 | addr_ptr[FFS_NDADDR + i] = |
641 | 0 | tsk_gets64(fs->endian, in->di_ib[i]); |
642 | | |
643 | | |
644 | | /* set the link string (if the file is a link) |
645 | | * The size check is a sanity check so that we don't try and allocate |
646 | | * a huge amount of memory for a bad inode value |
647 | | */ |
648 | 0 | if ((fs_meta->type == TSK_FS_META_TYPE_LNK) |
649 | 0 | && (fs_meta->size < FFS_MAXPATHLEN) |
650 | 0 | && (fs_meta->size >= 0)) { |
651 | |
|
652 | 0 | fs_meta->link = (char*) tsk_malloc((size_t) fs_meta->size + 1); |
653 | 0 | if (fs_meta->link == NULL) { |
654 | 0 | return 1; |
655 | 0 | } |
656 | | |
657 | 0 | count = 0; /* index into the link array */ |
658 | | |
659 | | /* it is located directly in the pointers |
660 | | * Only the new style inode has this "fast link" |
661 | | */ |
662 | 0 | if (fs_meta->size < 8 * (FFS_NDADDR + FFS_NIADDR)) { |
663 | 0 | char *ptr; |
664 | | |
665 | | /* Direct block pointer locations */ |
666 | 0 | for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) { |
667 | 0 | ptr = (char *) &in->di_db[i]; |
668 | 0 | for (j = 0; j < 8 && count < fs_meta->size; j++) |
669 | 0 | fs_meta->link[count++] = ptr[j]; |
670 | 0 | } |
671 | | |
672 | | /* indirect block pointers */ |
673 | 0 | for (i = 0; i < FFS_NIADDR && count < fs_meta->size; i++) { |
674 | 0 | ptr = (char *) &in->di_ib[i]; |
675 | 0 | for (j = 0; j < 8 && count < fs_meta->size; j++) |
676 | 0 | fs_meta->link[count++] = ptr[j]; |
677 | 0 | } |
678 | |
|
679 | 0 | fs_meta->link[count] = '\0'; |
680 | | |
681 | | /* clear the values to avoid other code from reading them */ |
682 | 0 | memset(fs_meta->content_ptr, 0, fs_meta->content_len); |
683 | 0 | } |
684 | | |
685 | | /* it is in blocks (the regular way) */ |
686 | 0 | else { |
687 | 0 | char *buf; |
688 | 0 | char *ptr = fs_meta->link; |
689 | |
|
690 | 0 | if ((buf = (char *) |
691 | 0 | tsk_malloc(fs->block_size)) == NULL) { |
692 | 0 | return 1; |
693 | 0 | } |
694 | | |
695 | | /* there is a max link length of 1000, so we should never |
696 | | * need the indirect blocks |
697 | | */ |
698 | 0 | for (i = 0; i < FFS_NDADDR && count < fs_meta->size; i++) { |
699 | 0 | ssize_t cnt; |
700 | 0 | TSK_DADDR_T *addr_ptr = (TSK_DADDR_T*) fs_meta->content_ptr; |
701 | | |
702 | | /* Do we need the entire block, or just part of it? */ |
703 | 0 | int read_count = |
704 | 0 | (fs_meta->size - count < |
705 | 0 | fs->block_size) ? (int) fs_meta->size - |
706 | 0 | count : fs->block_size; |
707 | |
|
708 | 0 | cnt = tsk_fs_read_block(fs, |
709 | 0 | addr_ptr[i], buf, fs->block_size); |
710 | 0 | if (cnt != fs->block_size) { |
711 | 0 | if (cnt >= 0) { |
712 | 0 | tsk_error_reset(); |
713 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
714 | 0 | } |
715 | 0 | tsk_error_set_errstr2 |
716 | 0 | ("ffs_dinode_copy: FFS2 symlink dest at %" |
717 | 0 | PRIuDADDR, addr_ptr[i]); |
718 | 0 | free(buf); |
719 | 0 | return 1; |
720 | 0 | } |
721 | | |
722 | 0 | memcpy(ptr, buf, read_count); |
723 | 0 | count += read_count; |
724 | 0 | ptr = (char *) ((uintptr_t) ptr + read_count); |
725 | 0 | } |
726 | | /* terminate the string */ |
727 | 0 | *ptr = '\0'; |
728 | |
|
729 | 0 | free(buf); |
730 | 0 | } |
731 | 0 | } /* end of symlink */ |
732 | 0 | } |
733 | 0 | else { |
734 | 0 | tsk_error_reset(); |
735 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
736 | 0 | tsk_error_set_errstr("ffs_dinode_copy: Unknown FFS Type"); |
737 | 0 | return 1; |
738 | 0 | } |
739 | | |
740 | | /* set the flags */ |
741 | 0 | grp_num = (FFS_GRPNUM_T) itog_lcl(fs, ffs->fs.sb1, dino_inum); |
742 | |
|
743 | 0 | tsk_take_lock(&ffs->lock); |
744 | 0 | if (ffs_group_load(ffs, grp_num)) { |
745 | 0 | tsk_release_lock(&ffs->lock); |
746 | 0 | return 1; |
747 | 0 | } |
748 | | |
749 | 0 | cg = (ffs_cgd *) ffs->grp_buf; |
750 | |
|
751 | 0 | inosused = (unsigned char *) cg_inosused_lcl(fs, cg); |
752 | 0 | ibase = grp_num * tsk_gets32(fs->endian, ffs->fs.sb1->cg_inode_num); |
753 | | |
754 | | /* get the alloc flag */ |
755 | 0 | fs_meta->flags = (isset(inosused, dino_inum - ibase) ? |
756 | 0 | TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC); |
757 | |
|
758 | 0 | tsk_release_lock(&ffs->lock); |
759 | | |
760 | | /* used/unused */ |
761 | 0 | fs_meta->flags = (TSK_FS_META_FLAG_ENUM) (fs_meta->flags | (fs_meta->ctime ? |
762 | 0 | TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED)); |
763 | |
|
764 | 0 | return 0; |
765 | 0 | } |
766 | | |
767 | | |
768 | | |
769 | | |
770 | | /* ffs_inode_lookup - lookup inode, external interface |
771 | | * |
772 | | * Return 1 on error |
773 | | * |
774 | | * */ |
775 | | static uint8_t |
776 | | ffs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file, |
777 | | TSK_INUM_T inum) |
778 | 0 | { |
779 | 0 | ffs_inode *dino_buf; |
780 | 0 | FFS_INFO *ffs = (FFS_INFO *) fs; |
781 | |
|
782 | 0 | if (a_fs_file == NULL) { |
783 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
784 | 0 | tsk_error_set_errstr("ffs_inode_lookup: fs_file is NULL"); |
785 | 0 | return 1; |
786 | 0 | } |
787 | | |
788 | | /* copy it to the TSK_FS_META structure */ |
789 | 0 | if (a_fs_file->meta == NULL) { |
790 | 0 | a_fs_file->meta = tsk_fs_meta_alloc(FFS_FILE_CONTENT_LEN); |
791 | 0 | if (a_fs_file->meta == NULL) |
792 | 0 | return 1; |
793 | 0 | } |
794 | 0 | else { |
795 | 0 | tsk_fs_meta_reset(a_fs_file->meta); |
796 | 0 | } |
797 | | |
798 | | // see if they are looking for the special "orphans" directory |
799 | 0 | if (inum == TSK_FS_ORPHANDIR_INUM(fs)) { |
800 | 0 | if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta)) |
801 | 0 | return 1; |
802 | 0 | else |
803 | 0 | return 0; |
804 | 0 | } |
805 | | |
806 | | /* Lookup the inode and store it in ffs */ |
807 | 0 | if ((dino_buf = (ffs_inode *) tsk_malloc(sizeof(ffs_inode2))) == NULL) |
808 | 0 | return 1; |
809 | | |
810 | 0 | if (ffs_dinode_load(ffs, inum, dino_buf)) { |
811 | 0 | free(dino_buf); |
812 | 0 | return 1; |
813 | 0 | } |
814 | | |
815 | 0 | if (ffs_dinode_copy(ffs, a_fs_file->meta, inum, dino_buf)) { |
816 | 0 | free(dino_buf); |
817 | 0 | return 1; |
818 | 0 | } |
819 | | |
820 | 0 | free(dino_buf); |
821 | |
|
822 | 0 | return 0; |
823 | 0 | } |
824 | | |
825 | | |
826 | | |
827 | | /************************************************************************** |
828 | | * |
829 | | * INODE WALKING |
830 | | * |
831 | | **************************************************************************/ |
832 | | |
833 | | |
834 | | |
835 | | /* ffs_inode_walk - inode iterator |
836 | | * |
837 | | * flags used: TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED, |
838 | | * TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, TSK_FS_META_FLAG_ORPHAN |
839 | | * |
840 | | * return 1 on error and 0 on success |
841 | | */ |
842 | | uint8_t |
843 | | ffs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, |
844 | | TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM a_flags, |
845 | | TSK_FS_META_WALK_CB action, void *ptr) |
846 | 0 | { |
847 | 0 | const char *myname = "ffs_inode_walk"; |
848 | 0 | FFS_INFO *ffs = (FFS_INFO *) fs; |
849 | 0 | ffs_cgd *cg = NULL; |
850 | 0 | TSK_INUM_T inum; |
851 | 0 | unsigned char *inosused = NULL; |
852 | 0 | unsigned int myflags; |
853 | 0 | TSK_INUM_T ibase = 0; |
854 | 0 | TSK_INUM_T end_inum_tmp; |
855 | 0 | ffs_inode *dino_buf; |
856 | | |
857 | | // clean up any error messages that are lying around |
858 | 0 | tsk_error_reset(); |
859 | | |
860 | | /* |
861 | | * Sanity checks. |
862 | | */ |
863 | 0 | if (start_inum < fs->first_inum || start_inum > fs->last_inum) { |
864 | 0 | tsk_error_reset(); |
865 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
866 | 0 | tsk_error_set_errstr("%s: Start inode: %" PRIuINUM "", myname, |
867 | 0 | start_inum); |
868 | 0 | return 1; |
869 | 0 | } |
870 | 0 | else if (end_inum < fs->first_inum || end_inum > fs->last_inum |
871 | 0 | || end_inum < start_inum) { |
872 | 0 | tsk_error_reset(); |
873 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
874 | 0 | tsk_error_set_errstr("%s: End inode: %" PRIuINUM "", myname, |
875 | 0 | end_inum); |
876 | 0 | return 1; |
877 | 0 | } |
878 | | |
879 | | /* If ORPHAN is wanted, then make sure that the flags are correct */ |
880 | 0 | if (a_flags & TSK_FS_META_FLAG_ORPHAN) { |
881 | 0 | a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_UNALLOC); |
882 | 0 | a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags & ~TSK_FS_META_FLAG_ALLOC); |
883 | 0 | a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_USED); |
884 | 0 | a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags & ~TSK_FS_META_FLAG_UNUSED); |
885 | 0 | } |
886 | 0 | else { |
887 | 0 | if (((a_flags & TSK_FS_META_FLAG_ALLOC) == 0) && |
888 | 0 | ((a_flags & TSK_FS_META_FLAG_UNALLOC) == 0)) { |
889 | 0 | a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC); |
890 | 0 | } |
891 | | |
892 | | /* If neither of the USED or UNUSED flags are set, then set them |
893 | | * both |
894 | | */ |
895 | 0 | if (((a_flags & TSK_FS_META_FLAG_USED) == 0) && |
896 | 0 | ((a_flags & TSK_FS_META_FLAG_UNUSED) == 0)) { |
897 | 0 | a_flags = (TSK_FS_META_FLAG_ENUM) (a_flags | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED); |
898 | 0 | } |
899 | 0 | } |
900 | | |
901 | | /* If we are looking for orphan files and have not yet filled |
902 | | * in the list of unalloc inodes that are pointed to, then fill |
903 | | * in the list |
904 | | * */ |
905 | 0 | if ((a_flags & TSK_FS_META_FLAG_ORPHAN)) { |
906 | 0 | if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) { |
907 | 0 | tsk_error_errstr2_concat |
908 | 0 | ("- ffs_inode_walk: identifying inodes allocated by file names"); |
909 | 0 | return 1; |
910 | 0 | } |
911 | 0 | } |
912 | | |
913 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
914 | 0 | tsk_fs_file_alloc(fs), |
915 | 0 | tsk_fs_file_close |
916 | 0 | }; |
917 | |
|
918 | 0 | if (!fs_file) { |
919 | 0 | return 1; |
920 | 0 | } |
921 | | |
922 | 0 | if ((fs_file->meta = tsk_fs_meta_alloc(FFS_FILE_CONTENT_LEN)) == NULL) |
923 | 0 | return 1; |
924 | | |
925 | | // we need to handle fs->last_inum specially because it is for the |
926 | | // virtual ORPHANS directory. Handle it outside of the loop. |
927 | 0 | if (end_inum == TSK_FS_ORPHANDIR_INUM(fs)) |
928 | 0 | end_inum_tmp = end_inum - 1; |
929 | 0 | else |
930 | 0 | end_inum_tmp = end_inum; |
931 | |
|
932 | 0 | if ((dino_buf = (ffs_inode *) tsk_malloc(sizeof(ffs_inode2))) == NULL) |
933 | 0 | return 1; |
934 | | |
935 | | /* |
936 | | * Iterate. This is easy because inode numbers are contiguous, unlike |
937 | | * data blocks which are interleaved with cylinder group blocks. |
938 | | */ |
939 | 0 | for (inum = start_inum; inum <= end_inum_tmp; inum++) { |
940 | 0 | int retval; |
941 | 0 | FFS_GRPNUM_T grp_num; |
942 | | |
943 | | /* |
944 | | * Be sure to use the proper cylinder group data. |
945 | | */ |
946 | 0 | grp_num = itog_lcl(fs, ffs->fs.sb1, inum); |
947 | |
|
948 | 0 | tsk_take_lock(&ffs->lock); |
949 | 0 | if (ffs_group_load(ffs, grp_num)) { |
950 | 0 | tsk_release_lock(&ffs->lock); |
951 | 0 | free(dino_buf); |
952 | 0 | return 1; |
953 | 0 | } |
954 | 0 | cg = (ffs_cgd *) ffs->grp_buf; |
955 | 0 | inosused = (unsigned char *) cg_inosused_lcl(fs, cg); |
956 | 0 | ibase = |
957 | 0 | grp_num * tsk_gets32(fs->endian, ffs->fs.sb1->cg_inode_num); |
958 | | |
959 | | /* |
960 | | * Apply the allocated/unallocated restriction. |
961 | | */ |
962 | 0 | myflags = (isset(inosused, inum - ibase) ? |
963 | 0 | TSK_FS_META_FLAG_ALLOC : TSK_FS_META_FLAG_UNALLOC); |
964 | |
|
965 | 0 | tsk_release_lock(&ffs->lock); |
966 | |
|
967 | 0 | if ((a_flags & myflags) != myflags) |
968 | 0 | continue; |
969 | | |
970 | | |
971 | 0 | if (ffs_dinode_load(ffs, inum, dino_buf)) { |
972 | 0 | free(dino_buf); |
973 | 0 | return 1; |
974 | 0 | } |
975 | | |
976 | 0 | if (fs->ftype == TSK_FS_TYPE_FFS1 |
977 | 0 | || fs->ftype == TSK_FS_TYPE_FFS1B) { |
978 | | /* both inode forms are the same for the required fields */ |
979 | 0 | ffs_inode1 *in1 = (ffs_inode1 *) dino_buf; |
980 | | |
981 | | /* |
982 | | * Apply the used/unused restriction. |
983 | | */ |
984 | 0 | myflags |= (tsk_gets32(fs->endian, in1->di_ctime) ? |
985 | 0 | TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED); |
986 | 0 | if ((a_flags & myflags) != myflags) |
987 | 0 | continue; |
988 | 0 | } |
989 | 0 | else { |
990 | 0 | ffs_inode2 *in2 = (ffs_inode2 *) dino_buf; |
991 | | |
992 | | /* |
993 | | * Apply the used/unused restriction. |
994 | | */ |
995 | 0 | myflags |= (tsk_gets64(fs->endian, in2->di_ctime) ? |
996 | 0 | TSK_FS_META_FLAG_USED : TSK_FS_META_FLAG_UNUSED); |
997 | 0 | if ((a_flags & myflags) != myflags) |
998 | 0 | continue; |
999 | 0 | } |
1000 | | |
1001 | | /* If we want only orphans, then check if this |
1002 | | * inode is in the seen list |
1003 | | */ |
1004 | 0 | if ((myflags & TSK_FS_META_FLAG_UNALLOC) && |
1005 | 0 | (a_flags & TSK_FS_META_FLAG_ORPHAN) && |
1006 | 0 | (tsk_fs_dir_find_inum_named(fs, inum))) { |
1007 | 0 | continue; |
1008 | 0 | } |
1009 | | |
1010 | | |
1011 | | /* |
1012 | | * Fill in a file system-independent inode structure and pass control |
1013 | | * to the application. |
1014 | | */ |
1015 | 0 | if (ffs_dinode_copy(ffs, fs_file->meta, inum, dino_buf)) { |
1016 | 0 | free(dino_buf); |
1017 | 0 | return 1; |
1018 | 0 | } |
1019 | | |
1020 | 0 | retval = action(fs_file.get(), ptr); |
1021 | 0 | if (retval == TSK_WALK_STOP) { |
1022 | 0 | free(dino_buf); |
1023 | 0 | return 0; |
1024 | 0 | } |
1025 | 0 | else if (retval == TSK_WALK_ERROR) { |
1026 | 0 | free(dino_buf); |
1027 | 0 | return 1; |
1028 | 0 | } |
1029 | 0 | } |
1030 | | |
1031 | | // handle the virtual orphans folder if they asked for it |
1032 | 0 | if ((end_inum == TSK_FS_ORPHANDIR_INUM(fs)) |
1033 | 0 | && (a_flags & TSK_FS_META_FLAG_ALLOC) |
1034 | 0 | && (a_flags & TSK_FS_META_FLAG_USED)) { |
1035 | 0 | int retval; |
1036 | |
|
1037 | 0 | if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) { |
1038 | 0 | free(dino_buf); |
1039 | 0 | return 1; |
1040 | 0 | } |
1041 | | /* call action */ |
1042 | 0 | retval = action(fs_file.get(), ptr); |
1043 | 0 | if (retval == TSK_WALK_STOP) { |
1044 | 0 | free(dino_buf); |
1045 | 0 | return 0; |
1046 | 0 | } |
1047 | 0 | else if (retval == TSK_WALK_ERROR) { |
1048 | 0 | free(dino_buf); |
1049 | 0 | return 1; |
1050 | 0 | } |
1051 | 0 | } |
1052 | | |
1053 | | /* |
1054 | | * Cleanup. |
1055 | | */ |
1056 | 0 | free(dino_buf); |
1057 | |
|
1058 | 0 | return 0; |
1059 | 0 | } |
1060 | | |
1061 | | TSK_FS_BLOCK_FLAG_ENUM |
1062 | | ffs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr) |
1063 | 0 | { |
1064 | 0 | FFS_INFO *ffs = (FFS_INFO *) a_fs; |
1065 | 0 | FFS_GRPNUM_T grp_num; |
1066 | 0 | ffs_cgd *cg = 0; |
1067 | 0 | TSK_DADDR_T frag_base = 0; |
1068 | 0 | TSK_DADDR_T dblock_addr = 0; /* first data block in group */ |
1069 | 0 | TSK_DADDR_T sblock_addr = 0; /* super block in group */ |
1070 | 0 | unsigned char *freeblocks = NULL; |
1071 | 0 | int flags; |
1072 | | |
1073 | | // sparse |
1074 | 0 | if (a_addr == 0) |
1075 | 0 | return (TSK_FS_BLOCK_FLAG_ENUM) (TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC); |
1076 | | |
1077 | 0 | grp_num = dtog_lcl(a_fs, ffs->fs.sb1, a_addr); |
1078 | |
|
1079 | 0 | tsk_take_lock(&ffs->lock); |
1080 | 0 | if (ffs_group_load(ffs, grp_num)) { |
1081 | 0 | tsk_release_lock(&ffs->lock); |
1082 | 0 | return TSK_FS_BLOCK_FLAG_UNUSED; |
1083 | 0 | } |
1084 | | |
1085 | 0 | cg = (ffs_cgd *) ffs->grp_buf; |
1086 | 0 | freeblocks = (unsigned char *) cg_blksfree_lcl(a_fs, cg); |
1087 | | |
1088 | | // get the base fragment for the group |
1089 | 0 | frag_base = cgbase_lcl(a_fs, ffs->fs.sb1, grp_num); |
1090 | | |
1091 | | // address of first data block in group |
1092 | 0 | dblock_addr = cgdmin_lcl(a_fs, ffs->fs.sb1, grp_num); |
1093 | | |
1094 | | // address of super block in group |
1095 | 0 | sblock_addr = cgsblock_lcl(a_fs, ffs->fs.sb1, grp_num); |
1096 | | |
1097 | | /* get the flags for this fragment |
1098 | | * |
1099 | | * Beware: FFS stores file data in the blocks between the start of a |
1100 | | * cylinder group and the start of its super block. |
1101 | | */ |
1102 | 0 | flags = (isset(freeblocks, a_addr - frag_base) ? |
1103 | 0 | TSK_FS_BLOCK_FLAG_UNALLOC : TSK_FS_BLOCK_FLAG_ALLOC); |
1104 | |
|
1105 | 0 | tsk_release_lock(&ffs->lock); |
1106 | |
|
1107 | 0 | if (a_addr >= sblock_addr && a_addr < dblock_addr) |
1108 | 0 | flags |= TSK_FS_BLOCK_FLAG_META; |
1109 | 0 | else |
1110 | 0 | flags |= TSK_FS_BLOCK_FLAG_CONT; |
1111 | |
|
1112 | 0 | return (TSK_FS_BLOCK_FLAG_ENUM) flags; |
1113 | 0 | } |
1114 | | |
1115 | | /************************************************************************** |
1116 | | * |
1117 | | * BLOCK WALKING |
1118 | | * |
1119 | | **************************************************************************/ |
1120 | | |
1121 | | /* ffs_block_walk - block iterator |
1122 | | * |
1123 | | * flags: TSK_FS_BLOCK_FLAG_ALLOC, TSK_FS_BLOCK_FLAG_UNALLOC, TSK_FS_BLOCK_FLAG_CONT, |
1124 | | * TSK_FS_BLOCK_FLAG_META |
1125 | | * |
1126 | | * return 1 on error and 0 on success |
1127 | | */ |
1128 | | |
1129 | | uint8_t |
1130 | | ffs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk, |
1131 | | TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, |
1132 | | TSK_FS_BLOCK_WALK_CB action, void *ptr) |
1133 | 0 | { |
1134 | 0 | const char *myname = "ffs_block_walk"; |
1135 | 0 | FFS_INFO *ffs = (FFS_INFO *) fs; |
1136 | 0 | TSK_FS_BLOCK *fs_block; |
1137 | 0 | TSK_DADDR_T addr; |
1138 | |
|
1139 | 0 | char *cache_blk_buf; // buffer used for local read cache |
1140 | 0 | TSK_DADDR_T cache_addr; // base address in local cache |
1141 | 0 | int cache_len_f; // amount of data read into cache (in fragments) |
1142 | | |
1143 | | // clean up any error messages that are lying around |
1144 | 0 | tsk_error_reset(); |
1145 | | |
1146 | | /* |
1147 | | * Sanity checks on input bounds |
1148 | | */ |
1149 | 0 | if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) { |
1150 | 0 | tsk_error_reset(); |
1151 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1152 | 0 | tsk_error_set_errstr("%s: Start block: %" PRIuDADDR "", myname, |
1153 | 0 | a_start_blk); |
1154 | 0 | return 1; |
1155 | 0 | } |
1156 | | |
1157 | 0 | if (a_end_blk < fs->first_block || a_end_blk > fs->last_block |
1158 | 0 | || a_end_blk < a_start_blk) { |
1159 | 0 | tsk_error_reset(); |
1160 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
1161 | 0 | tsk_error_set_errstr("%s: End block: %" PRIuDADDR "", myname, |
1162 | 0 | a_end_blk); |
1163 | 0 | return 1; |
1164 | 0 | } |
1165 | | |
1166 | | /* Sanity check on flags -- make sure at least one ALLOC is set */ |
1167 | 0 | if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) && |
1168 | 0 | ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) { |
1169 | 0 | a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM) |
1170 | 0 | (a_flags | TSK_FS_BLOCK_WALK_FLAG_ALLOC | |
1171 | 0 | TSK_FS_BLOCK_WALK_FLAG_UNALLOC); |
1172 | 0 | } |
1173 | 0 | if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) && |
1174 | 0 | ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) { |
1175 | 0 | a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM) |
1176 | 0 | (a_flags | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META); |
1177 | 0 | } |
1178 | | |
1179 | | |
1180 | | /* Other initialization */ |
1181 | 0 | if ((fs_block = tsk_fs_block_alloc(fs)) == NULL) { |
1182 | 0 | return 1; |
1183 | 0 | } |
1184 | 0 | if ((cache_blk_buf = (char*) tsk_malloc(ffs->ffsbsize_b)) == NULL) { |
1185 | 0 | return 1; |
1186 | 0 | } |
1187 | 0 | cache_len_f = 0; |
1188 | 0 | cache_addr = 0; |
1189 | | |
1190 | | /* Cycle through the fragment range requested */ |
1191 | 0 | for (addr = a_start_blk; addr <= a_end_blk; addr++) { |
1192 | 0 | int retval; |
1193 | 0 | size_t cache_offset = 0; |
1194 | 0 | int myflags = ffs_block_getflags(fs, addr); |
1195 | |
|
1196 | 0 | if ((tsk_verbose) && (myflags & TSK_FS_BLOCK_FLAG_META) |
1197 | 0 | && (myflags & TSK_FS_BLOCK_FLAG_UNALLOC)) |
1198 | 0 | tsk_fprintf(stderr, |
1199 | 0 | "impossible: unallocated meta block %" PRIuDADDR, addr); |
1200 | | |
1201 | | // test if we should call the callback with this one |
1202 | 0 | if ((myflags & TSK_FS_BLOCK_FLAG_META) |
1203 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_META))) |
1204 | 0 | continue; |
1205 | 0 | else if ((myflags & TSK_FS_BLOCK_FLAG_CONT) |
1206 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT))) |
1207 | 0 | continue; |
1208 | 0 | else if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC) |
1209 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC))) |
1210 | 0 | continue; |
1211 | 0 | else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC) |
1212 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC))) |
1213 | 0 | continue; |
1214 | | |
1215 | | |
1216 | 0 | if ((a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY) == 0) { |
1217 | | /* we read in block-sized chunks and cache the result for later |
1218 | | * calls. See if this fragment is in our cache */ |
1219 | 0 | if ((cache_len_f == 0) || (addr >= cache_addr + cache_len_f)) { |
1220 | 0 | ssize_t cnt; |
1221 | 0 | int frags; |
1222 | | |
1223 | | /* Ideally, we want to read in block sized chunks, verify we can do that */ |
1224 | 0 | frags = a_end_blk > addr + ffs->ffsbsize_f - 1 ? |
1225 | 0 | ffs->ffsbsize_f : a_end_blk + 1 - addr; |
1226 | |
|
1227 | 0 | cnt = |
1228 | 0 | tsk_fs_read_block(fs, addr, cache_blk_buf, |
1229 | 0 | fs->block_size * frags); |
1230 | 0 | if (cnt != fs->block_size * frags) { |
1231 | 0 | if (cnt >= 0) { |
1232 | 0 | tsk_error_reset(); |
1233 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1234 | 0 | } |
1235 | 0 | tsk_error_set_errstr2("ffs_block_walk: Block %" |
1236 | 0 | PRIuDADDR, addr); |
1237 | 0 | tsk_fs_block_free(fs_block); |
1238 | 0 | free(cache_blk_buf); |
1239 | 0 | return 1; |
1240 | 0 | } |
1241 | 0 | cache_len_f = frags; |
1242 | 0 | cache_addr = addr; |
1243 | 0 | } |
1244 | 0 | cache_offset = (size_t) ((addr - cache_addr) * fs->block_size); |
1245 | 0 | } |
1246 | | |
1247 | 0 | if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY) |
1248 | 0 | myflags |= TSK_FS_BLOCK_FLAG_AONLY; |
1249 | | |
1250 | | // call the callback |
1251 | 0 | tsk_fs_block_set(fs, fs_block, addr, |
1252 | 0 | (TSK_FS_BLOCK_FLAG_ENUM) (myflags | TSK_FS_BLOCK_FLAG_RAW), |
1253 | 0 | &cache_blk_buf[cache_offset]); |
1254 | 0 | retval = action(fs_block, ptr); |
1255 | 0 | if (retval == TSK_WALK_STOP) { |
1256 | 0 | break; |
1257 | 0 | } |
1258 | 0 | else if (retval == TSK_WALK_ERROR) { |
1259 | 0 | tsk_fs_block_free(fs_block); |
1260 | 0 | free(cache_blk_buf); |
1261 | 0 | return 1; |
1262 | 0 | } |
1263 | 0 | } |
1264 | | |
1265 | | /* Cleanup */ |
1266 | 0 | tsk_fs_block_free(fs_block); |
1267 | 0 | free(cache_blk_buf); |
1268 | 0 | return 0; |
1269 | 0 | } |
1270 | | |
1271 | | |
1272 | | |
1273 | | /* |
1274 | | * return 1 on error and 0 on success |
1275 | | */ |
1276 | | static uint8_t |
1277 | | ffs_fscheck( |
1278 | | [[maybe_unused]] TSK_FS_INFO * fs, |
1279 | | [[maybe_unused]] FILE * hFile) |
1280 | 0 | { |
1281 | 0 | tsk_error_reset(); |
1282 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
1283 | 0 | tsk_error_set_errstr("fscheck not implemented for ffs yet"); |
1284 | 0 | return 1; |
1285 | 0 | } |
1286 | | |
1287 | | |
1288 | | /** |
1289 | | * Print details about the file system to a file handle. |
1290 | | * |
1291 | | * @param fs File system to print details on |
1292 | | * @param hFile File handle to print text to |
1293 | | * |
1294 | | * @returns 1 on error and 0 on success |
1295 | | */ |
1296 | | static uint8_t |
1297 | | ffs_fsstat(TSK_FS_INFO * fs, FILE * hFile) |
1298 | 0 | { |
1299 | 0 | unsigned int i; |
1300 | 0 | time_t tmptime; |
1301 | 0 | ffs_csum1 *csum1 = NULL; |
1302 | 0 | ffs_cgd *cgd = NULL; |
1303 | |
|
1304 | 0 | FFS_INFO *ffs = (FFS_INFO *) fs; |
1305 | 0 | ffs_sb1 *sb1 = ffs->fs.sb1; |
1306 | 0 | ffs_sb2 *sb2 = ffs->fs.sb2; |
1307 | 0 | int flags; |
1308 | 0 | char timeBuf[128]; |
1309 | | |
1310 | | // clean up any error messages that are lying around |
1311 | 0 | tsk_error_reset(); |
1312 | |
|
1313 | 0 | tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n"); |
1314 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
1315 | |
|
1316 | 0 | if ((fs->ftype == TSK_FS_TYPE_FFS1) |
1317 | 0 | || (fs->ftype == TSK_FS_TYPE_FFS1B)) { |
1318 | 0 | tsk_fprintf(hFile, "File System Type: UFS 1\n"); |
1319 | 0 | tmptime = tsk_getu32(fs->endian, sb1->wtime); |
1320 | 0 | tsk_fprintf(hFile, "Last Written: %s\n", |
1321 | 0 | (tmptime > 0) ? tsk_fs_time_to_str(tmptime, |
1322 | 0 | timeBuf) : "empty"); |
1323 | 0 | tsk_fprintf(hFile, "Last Mount Point: %s\n", sb1->last_mnt); |
1324 | |
|
1325 | 0 | flags = sb1->fs_flags; |
1326 | 0 | } |
1327 | 0 | else { |
1328 | 0 | tsk_fprintf(hFile, "File System Type: UFS 2\n"); |
1329 | 0 | tmptime = tsk_getu32(fs->endian, sb2->wtime); |
1330 | 0 | tsk_fprintf(hFile, "Last Written: %s\n", |
1331 | 0 | (tmptime > 0) ? tsk_fs_time_to_str(tmptime, |
1332 | 0 | timeBuf) : "empty"); |
1333 | 0 | tsk_fprintf(hFile, "Last Mount Point: %s\n", sb2->last_mnt); |
1334 | 0 | tsk_fprintf(hFile, "Volume Name: %s\n", sb2->volname); |
1335 | 0 | tsk_fprintf(hFile, "System UID: %" PRIu64 "\n", |
1336 | 0 | tsk_getu64(fs->endian, sb2->swuid)); |
1337 | 0 | flags = tsk_getu32(fs->endian, sb2->fs_flags); |
1338 | 0 | } |
1339 | |
|
1340 | 0 | if (flags) { |
1341 | 0 | int cnt = 0; |
1342 | |
|
1343 | 0 | tsk_fprintf(hFile, "Flags: "); |
1344 | |
|
1345 | 0 | if (flags & FFS_SB_FLAG_UNCLEAN) |
1346 | 0 | tsk_fprintf(hFile, "%s Unclean", (cnt++ == 0 ? "" : ",")); |
1347 | |
|
1348 | 0 | if (flags & FFS_SB_FLAG_SOFTDEP) |
1349 | 0 | tsk_fprintf(hFile, "%s Soft Dependencies", |
1350 | 0 | (cnt++ == 0 ? "" : ",")); |
1351 | |
|
1352 | 0 | if (flags & FFS_SB_FLAG_NEEDFSCK) |
1353 | 0 | tsk_fprintf(hFile, "%s Needs fsck", (cnt++ == 0 ? "" : ",")); |
1354 | |
|
1355 | 0 | if (flags & FFS_SB_FLAG_INDEXDIR) |
1356 | 0 | tsk_fprintf(hFile, "%s Index directories", |
1357 | 0 | (cnt++ == 0 ? "" : ",")); |
1358 | |
|
1359 | 0 | if (flags & FFS_SB_FLAG_ACL) |
1360 | 0 | tsk_fprintf(hFile, "%s ACLs", (cnt++ == 0 ? "" : ",")); |
1361 | |
|
1362 | 0 | if (flags & FFS_SB_FLAG_MULTILABEL) |
1363 | 0 | tsk_fprintf(hFile, "%s TrustedBSD MAC Multi-label", |
1364 | 0 | (cnt++ == 0 ? "" : ",")); |
1365 | |
|
1366 | 0 | if (flags & FFS_SB_FLAG_UPDATED) |
1367 | 0 | tsk_fprintf(hFile, "%s Updated Flag Location", |
1368 | 0 | (cnt++ == 0 ? "" : ",")); |
1369 | |
|
1370 | 0 | tsk_fprintf(hFile, "\n"); |
1371 | 0 | } |
1372 | | |
1373 | | |
1374 | |
|
1375 | 0 | tsk_fprintf(hFile, "\nMETADATA INFORMATION\n"); |
1376 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
1377 | |
|
1378 | 0 | tsk_fprintf(hFile, "Inode Range: %" PRIuINUM " - %" PRIuINUM "\n", |
1379 | 0 | fs->first_inum, fs->last_inum); |
1380 | 0 | tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum); |
1381 | 0 | if ((fs->ftype == TSK_FS_TYPE_FFS1) |
1382 | 0 | || (fs->ftype == TSK_FS_TYPE_FFS1B)) { |
1383 | 0 | tsk_fprintf(hFile, "Num of Avail Inodes: %" PRIu32 "\n", |
1384 | 0 | tsk_getu32(fs->endian, sb1->cstotal.ino_free)); |
1385 | 0 | tsk_fprintf(hFile, "Num of Directories: %" PRIu32 "\n", |
1386 | 0 | tsk_getu32(fs->endian, sb1->cstotal.dir_num)); |
1387 | 0 | } |
1388 | 0 | else { |
1389 | 0 | tsk_fprintf(hFile, "Num of Avail Inodes: %" PRIu64 "\n", |
1390 | 0 | tsk_getu64(fs->endian, sb2->cstotal.ino_free)); |
1391 | 0 | tsk_fprintf(hFile, "Num of Directories: %" PRIu64 "\n", |
1392 | 0 | tsk_getu64(fs->endian, sb2->cstotal.dir_num)); |
1393 | 0 | } |
1394 | | |
1395 | |
|
1396 | 0 | tsk_fprintf(hFile, "\nCONTENT INFORMATION\n"); |
1397 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
1398 | |
|
1399 | 0 | tsk_fprintf(hFile, "Fragment Range: %" PRIuDADDR " - %" PRIuDADDR "\n", |
1400 | 0 | fs->first_block, fs->last_block); |
1401 | |
|
1402 | 0 | if (fs->last_block != fs->last_block_act) |
1403 | 0 | tsk_fprintf(hFile, |
1404 | 0 | "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n", |
1405 | 0 | fs->first_block, fs->last_block_act); |
1406 | |
|
1407 | 0 | tsk_fprintf(hFile, "Block Size: %u\n", ffs->ffsbsize_b); |
1408 | 0 | tsk_fprintf(hFile, "Fragment Size: %u\n", fs->block_size); |
1409 | |
|
1410 | 0 | if ((fs->ftype == TSK_FS_TYPE_FFS1) |
1411 | 0 | || (fs->ftype == TSK_FS_TYPE_FFS1B)) { |
1412 | 0 | tsk_fprintf(hFile, "Num of Avail Full Blocks: %" PRIu32 "\n", |
1413 | 0 | tsk_getu32(fs->endian, sb1->cstotal.blk_free)); |
1414 | 0 | tsk_fprintf(hFile, "Num of Avail Fragments: %" PRIu32 "\n", |
1415 | 0 | tsk_getu32(fs->endian, sb1->cstotal.frag_free)); |
1416 | 0 | } |
1417 | 0 | else { |
1418 | 0 | tsk_fprintf(hFile, "Num of Avail Full Blocks: %" PRIu64 "\n", |
1419 | 0 | tsk_getu64(fs->endian, sb2->cstotal.blk_free)); |
1420 | 0 | tsk_fprintf(hFile, "Num of Avail Fragments: %" PRIu64 "\n", |
1421 | 0 | tsk_getu64(fs->endian, sb2->cstotal.frag_free)); |
1422 | 0 | } |
1423 | |
|
1424 | 0 | tsk_fprintf(hFile, "\nCYLINDER GROUP INFORMATION\n"); |
1425 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
1426 | |
|
1427 | 0 | tsk_fprintf(hFile, "Number of Cylinder Groups: %" PRIu32 "\n", |
1428 | 0 | ffs->groups_count); |
1429 | 0 | tsk_fprintf(hFile, "Inodes per group: %" PRId32 "\n", |
1430 | 0 | tsk_gets32(fs->endian, sb1->cg_inode_num)); |
1431 | 0 | tsk_fprintf(hFile, "Fragments per group: %" PRId32 "\n", |
1432 | 0 | tsk_gets32(fs->endian, sb1->cg_frag_num)); |
1433 | | |
1434 | | |
1435 | | /* UFS 1 and 2 use the same ssize field and use the same csum1 */ |
1436 | 0 | if (tsk_getu32(fs->endian, sb1->cg_ssize_b)) { |
1437 | 0 | ssize_t cnt; |
1438 | 0 | csum1 = |
1439 | 0 | (ffs_csum1 *) tsk_malloc(tsk_getu32(fs->endian, |
1440 | 0 | sb1->cg_ssize_b)); |
1441 | 0 | if (csum1 == NULL) |
1442 | 0 | return 1; |
1443 | | |
1444 | 0 | if ((fs->ftype == TSK_FS_TYPE_FFS1) |
1445 | 0 | || (fs->ftype == TSK_FS_TYPE_FFS1B)) { |
1446 | 0 | cnt = |
1447 | 0 | tsk_fs_read_block(fs, (TSK_DADDR_T) tsk_getu32(fs->endian, |
1448 | 0 | sb1->cg_saddr), (char *) csum1, tsk_getu32(fs->endian, |
1449 | 0 | sb1->cg_ssize_b)); |
1450 | |
|
1451 | 0 | if (cnt != tsk_getu32(fs->endian, sb1->cg_ssize_b)) { |
1452 | 0 | if (cnt >= 0) { |
1453 | 0 | tsk_error_reset(); |
1454 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1455 | 0 | } |
1456 | 0 | tsk_error_set_errstr2 |
1457 | 0 | ("ffs_fsstat: FFS1 group descriptor at %" PRIu32, |
1458 | 0 | tsk_getu32(fs->endian, sb1->cg_saddr)); |
1459 | 0 | return 1; |
1460 | 0 | } |
1461 | 0 | } |
1462 | 0 | else { |
1463 | 0 | cnt = tsk_fs_read_block |
1464 | 0 | (fs, (TSK_DADDR_T) tsk_getu64(fs->endian, |
1465 | 0 | sb2->cg_saddr), (char *) csum1, tsk_getu32(fs->endian, |
1466 | 0 | sb2->cg_ssize_b)); |
1467 | 0 | if (cnt != tsk_getu32(fs->endian, sb2->cg_ssize_b)) { |
1468 | 0 | if (cnt >= 0) { |
1469 | 0 | tsk_error_reset(); |
1470 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1471 | 0 | } |
1472 | 0 | tsk_error_set_errstr2 |
1473 | 0 | ("ffs_fsstat: FFS2 group descriptor at %" PRIu64, |
1474 | 0 | tsk_getu64(fs->endian, sb2->cg_saddr)); |
1475 | 0 | return 1; |
1476 | 0 | } |
1477 | 0 | } |
1478 | 0 | } |
1479 | | |
1480 | 0 | for (i = 0; i < ffs->groups_count; i++) { |
1481 | |
|
1482 | 0 | tsk_take_lock(&ffs->lock); |
1483 | 0 | if (ffs_group_load(ffs, i)) { |
1484 | 0 | tsk_release_lock(&ffs->lock); |
1485 | 0 | return 1; |
1486 | 0 | } |
1487 | 0 | cgd = (ffs_cgd *) ffs->grp_buf; |
1488 | |
|
1489 | 0 | tsk_fprintf(hFile, "\nGroup %d:\n", i); |
1490 | 0 | if (cgd) { |
1491 | 0 | if ((fs->ftype == TSK_FS_TYPE_FFS1) |
1492 | 0 | || (fs->ftype == TSK_FS_TYPE_FFS1B)) { |
1493 | 0 | tmptime = tsk_getu32(fs->endian, cgd->wtime); |
1494 | 0 | } |
1495 | 0 | else { |
1496 | 0 | ffs_cgd2 *cgd2 = (ffs_cgd2 *) cgd; |
1497 | 0 | tmptime = (uint32_t) tsk_getu64(fs->endian, cgd2->wtime); |
1498 | 0 | } |
1499 | 0 | tsk_fprintf(hFile, " Last Written: %s\n", |
1500 | 0 | (tmptime > 0) ? tsk_fs_time_to_str(tmptime, |
1501 | 0 | timeBuf) : "empty"); |
1502 | 0 | } |
1503 | 0 | tsk_release_lock(&ffs->lock); |
1504 | |
|
1505 | 0 | tsk_fprintf(hFile, " Inode Range: %" PRIu32 " - %" PRIu32 "\n", |
1506 | 0 | (tsk_gets32(fs->endian, sb1->cg_inode_num) * i), |
1507 | 0 | ((uint32_t) ((tsk_gets32(fs->endian, |
1508 | 0 | sb1->cg_inode_num) * (i + 1)) - 1) |
1509 | 0 | < fs->last_inum) ? (uint32_t) ((tsk_gets32(fs->endian, |
1510 | 0 | sb1->cg_inode_num) * (i + 1)) - |
1511 | 0 | 1) : (uint32_t) fs->last_inum); |
1512 | |
|
1513 | 0 | tsk_fprintf(hFile, |
1514 | 0 | " Fragment Range: %" PRIuDADDR " - %" PRIuDADDR "\n", |
1515 | 0 | cgbase_lcl(fs, sb1, i), |
1516 | 0 | ((cgbase_lcl(fs, sb1, i + 1) - 1) < |
1517 | 0 | fs->last_block) ? (cgbase_lcl(fs, sb1, |
1518 | 0 | i + 1) - 1) : fs->last_block); |
1519 | | |
1520 | | /* The first group is special because the first 16 sectors are |
1521 | | * reserved for the boot block. |
1522 | | * the next contains the primary Super Block |
1523 | | */ |
1524 | 0 | if (!i) { |
1525 | 0 | tsk_fprintf(hFile, " Boot Block: 0 - %" PRIu32 "\n", |
1526 | 0 | (uint32_t) (15 * 512 / fs->block_size)); |
1527 | | |
1528 | |
|
1529 | 0 | tsk_fprintf(hFile, |
1530 | 0 | " Super Block: %" PRIu32 " - %" PRIu32 "\n", |
1531 | 0 | (uint32_t) (16 * 512 / fs->block_size), |
1532 | 0 | (uint32_t) ((16 * 512 / fs->block_size) + ffs->ffsbsize_f - |
1533 | 0 | 1)); |
1534 | 0 | } |
1535 | |
|
1536 | 0 | tsk_fprintf(hFile, |
1537 | 0 | " Super Block: %" PRIuDADDR " - %" PRIuDADDR "\n", |
1538 | 0 | cgsblock_lcl(fs, sb1, i), |
1539 | 0 | (cgsblock_lcl(fs, sb1, i) + ffs->ffsbsize_f - 1)); |
1540 | | |
1541 | |
|
1542 | 0 | tsk_fprintf(hFile, |
1543 | 0 | " Group Desc: %" PRIuDADDR " - %" PRIuDADDR "\n", |
1544 | 0 | cgtod_lcl(fs, sb1, i), (cgtod_lcl(fs, sb1, |
1545 | 0 | i) + ffs->ffsbsize_f - 1)); |
1546 | | |
1547 | |
|
1548 | 0 | if (fs->ftype == TSK_FS_TYPE_FFS2) { |
1549 | 0 | tsk_fprintf(hFile, |
1550 | 0 | " Inode Table: %" PRIuDADDR " - %" PRIuDADDR "\n", |
1551 | 0 | cgimin_lcl(fs, sb1, i), |
1552 | 0 | (cgimin_lcl(fs, sb1, i) + |
1553 | 0 | ((roundup |
1554 | 0 | (tsk_gets32(fs->endian, |
1555 | 0 | sb1->cg_inode_num) * |
1556 | 0 | sizeof(ffs_inode2), fs->block_size) |
1557 | 0 | / fs->block_size) - 1))); |
1558 | 0 | } |
1559 | 0 | else { |
1560 | 0 | tsk_fprintf(hFile, |
1561 | 0 | " Inode Table: %" PRIuDADDR " - %" PRIuDADDR "\n", |
1562 | 0 | cgimin_lcl(fs, sb1, i), |
1563 | 0 | (cgimin_lcl(fs, sb1, i) + |
1564 | 0 | ((roundup |
1565 | 0 | (tsk_gets32(fs->endian, |
1566 | 0 | sb1->cg_inode_num) * |
1567 | 0 | sizeof(ffs_inode1), fs->block_size) |
1568 | 0 | / fs->block_size) - 1))); |
1569 | 0 | } |
1570 | |
|
1571 | 0 | tsk_fprintf(hFile, " Data Fragments: "); |
1572 | | |
1573 | | /* For all groups besides the first, the space before the |
1574 | | * super block is also used for data |
1575 | | */ |
1576 | 0 | if (i) |
1577 | 0 | tsk_fprintf(hFile, "%" PRIuDADDR " - %" PRIuDADDR ", ", |
1578 | 0 | cgbase_lcl(fs, sb1, i), cgsblock_lcl(fs, sb1, i) - 1); |
1579 | |
|
1580 | 0 | tsk_fprintf(hFile, "%" PRIuDADDR " - %" PRIuDADDR "\n", |
1581 | 0 | cgdmin_lcl(fs, sb1, i), |
1582 | 0 | ((cgbase_lcl(fs, sb1, i + 1) - 1) < fs->last_block) ? |
1583 | 0 | (cgbase_lcl(fs, sb1, i + 1) - 1) : fs->last_block); |
1584 | | |
1585 | |
|
1586 | 0 | if ((csum1) |
1587 | 0 | && ((i + 1) * sizeof(ffs_csum1) < tsk_getu32(fs->endian, |
1588 | 0 | sb1->cg_ssize_b))) { |
1589 | 0 | tsk_fprintf(hFile, |
1590 | 0 | " Global Summary (from the superblock summary area):\n"); |
1591 | 0 | tsk_fprintf(hFile, " Num of Dirs: %" PRIu32 "\n", |
1592 | 0 | tsk_getu32(fs->endian, &csum1[i].dir_num)); |
1593 | 0 | tsk_fprintf(hFile, " Num of Avail Blocks: %" PRIu32 "\n", |
1594 | 0 | tsk_getu32(fs->endian, &csum1[i].blk_free)); |
1595 | 0 | tsk_fprintf(hFile, " Num of Avail Inodes: %" PRIu32 "\n", |
1596 | 0 | tsk_getu32(fs->endian, &csum1[i].ino_free)); |
1597 | 0 | tsk_fprintf(hFile, " Num of Avail Frags: %" PRIu32 "\n", |
1598 | 0 | tsk_getu32(fs->endian, &csum1[i].frag_free)); |
1599 | 0 | } |
1600 | |
|
1601 | 0 | if (cgd) { |
1602 | 0 | tsk_fprintf(hFile, |
1603 | 0 | " Local Summary (from the group descriptor):\n"); |
1604 | 0 | tsk_fprintf(hFile, " Num of Dirs: %" PRIu32 "\n", |
1605 | 0 | tsk_getu32(fs->endian, &cgd->cs.dir_num)); |
1606 | 0 | tsk_fprintf(hFile, " Num of Avail Blocks: %" PRIu32 "\n", |
1607 | 0 | tsk_getu32(fs->endian, &cgd->cs.blk_free)); |
1608 | 0 | tsk_fprintf(hFile, " Num of Avail Inodes: %" PRIu32 "\n", |
1609 | 0 | tsk_getu32(fs->endian, &cgd->cs.ino_free)); |
1610 | 0 | tsk_fprintf(hFile, " Num of Avail Frags: %" PRIu32 "\n", |
1611 | 0 | tsk_getu32(fs->endian, &cgd->cs.frag_free)); |
1612 | 0 | tsk_fprintf(hFile, |
1613 | 0 | " Last Block Allocated: %" PRIuDADDR "\n", |
1614 | 0 | tsk_getu32(fs->endian, |
1615 | 0 | &cgd->last_alloc_blk) + cgbase_lcl(fs, sb1, i)); |
1616 | 0 | tsk_fprintf(hFile, |
1617 | 0 | " Last Fragment Allocated: %" PRIuDADDR "\n", |
1618 | 0 | tsk_getu32(fs->endian, |
1619 | 0 | &cgd->last_alloc_frag) + cgbase_lcl(fs, sb1, i)); |
1620 | 0 | tsk_fprintf(hFile, " Last Inode Allocated: %" PRIu32 "\n", |
1621 | 0 | tsk_getu32(fs->endian, |
1622 | 0 | &cgd->last_alloc_ino) + (tsk_gets32(fs->endian, |
1623 | 0 | sb1->cg_inode_num) * i)); |
1624 | 0 | } |
1625 | 0 | } |
1626 | 0 | return 0; |
1627 | 0 | } |
1628 | | |
1629 | | |
1630 | | |
1631 | | /************************* istat *******************************/ |
1632 | | |
1633 | | typedef struct { |
1634 | | FILE *hFile; |
1635 | | int idx; |
1636 | | } FFS_PRINT_ADDR; |
1637 | | |
1638 | | |
1639 | | static TSK_WALK_RET_ENUM |
1640 | | print_addr_act( |
1641 | | [[maybe_unused]] TSK_FS_FILE * fs_file, |
1642 | | [[maybe_unused]] TSK_OFF_T a_off, |
1643 | | TSK_DADDR_T addr, |
1644 | | [[maybe_unused]] char *buf, |
1645 | | size_t size, |
1646 | | TSK_FS_BLOCK_FLAG_ENUM a_flags, |
1647 | | void *ptr) |
1648 | 0 | { |
1649 | 0 | TSK_FS_INFO *fs = fs_file->fs_info; |
1650 | 0 | FFS_PRINT_ADDR *print = (FFS_PRINT_ADDR *) ptr; |
1651 | |
|
1652 | 0 | if (a_flags & TSK_FS_BLOCK_FLAG_CONT) { |
1653 | 0 | int i, s; |
1654 | | /* cycle through the fragments if they exist */ |
1655 | 0 | for (i = 0, s = (int) size; s > 0; s -= fs->block_size, i++) { |
1656 | | |
1657 | | /* sparse file */ |
1658 | 0 | if (addr) |
1659 | 0 | tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr + i); |
1660 | 0 | else |
1661 | 0 | tsk_fprintf(print->hFile, "0 "); |
1662 | |
|
1663 | 0 | if (++(print->idx) == 8) { |
1664 | 0 | tsk_fprintf(print->hFile, "\n"); |
1665 | 0 | print->idx = 0; |
1666 | 0 | } |
1667 | 0 | } |
1668 | 0 | } |
1669 | |
|
1670 | 0 | return TSK_WALK_CONT; |
1671 | 0 | } |
1672 | | |
1673 | | |
1674 | | |
1675 | | /** |
1676 | | * Print details on a specific file to a file handle. |
1677 | | * |
1678 | | * @param fs File system file is located in |
1679 | | * @param hFile File handle to print text to |
1680 | | * @param inum Address of file in file system |
1681 | | * @param numblock The number of blocks in file to force print (can go beyond file size) |
1682 | | * @param sec_skew Clock skew in seconds to also print times in |
1683 | | * |
1684 | | * @returns 1 on error and 0 on success |
1685 | | */ |
1686 | | static uint8_t |
1687 | | ffs_istat(TSK_FS_INFO * fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * hFile, TSK_INUM_T inum, |
1688 | | TSK_DADDR_T numblock, int32_t sec_skew) |
1689 | 0 | { |
1690 | 0 | FFS_INFO *ffs = (FFS_INFO *) fs; |
1691 | 0 | TSK_FS_META *fs_meta; |
1692 | 0 | char ls[12]; |
1693 | 0 | FFS_PRINT_ADDR print; |
1694 | 0 | const TSK_FS_ATTR *fs_attr_indir; |
1695 | 0 | char *dino_buf; |
1696 | 0 | char timeBuf[128]; |
1697 | | |
1698 | | // clean up any error messages that are lying around |
1699 | 0 | tsk_error_reset(); |
1700 | |
|
1701 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
1702 | 0 | tsk_fs_file_open_meta(fs, NULL, inum), |
1703 | 0 | tsk_fs_file_close |
1704 | 0 | }; |
1705 | |
|
1706 | 0 | if (!fs_file) { |
1707 | 0 | return 1; |
1708 | 0 | } |
1709 | 0 | fs_meta = fs_file->meta; |
1710 | |
|
1711 | 0 | tsk_fprintf(hFile, "inode: %" PRIuINUM "\n", inum); |
1712 | 0 | tsk_fprintf(hFile, "%sAllocated\n", |
1713 | 0 | (fs_meta->flags & TSK_FS_META_FLAG_ALLOC) ? "" : "Not "); |
1714 | |
|
1715 | 0 | tsk_take_lock(&ffs->lock); |
1716 | 0 | tsk_fprintf(hFile, "Group: %" PRI_FFSGRP "\n", ffs->grp_num); |
1717 | 0 | tsk_release_lock(&ffs->lock); |
1718 | |
|
1719 | 0 | if (fs_meta->link) |
1720 | 0 | tsk_fprintf(hFile, "symbolic link to: %s\n", fs_meta->link); |
1721 | |
|
1722 | 0 | tsk_fprintf(hFile, "uid / gid: %" PRIuUID " / %" PRIuGID "\n", |
1723 | 0 | fs_meta->uid, fs_meta->gid); |
1724 | | |
1725 | |
|
1726 | 0 | tsk_fs_meta_make_ls(fs_meta, ls, sizeof(ls)); |
1727 | 0 | tsk_fprintf(hFile, "mode: %s\n", ls); |
1728 | |
|
1729 | 0 | tsk_fprintf(hFile, "size: %" PRIdOFF "\n", fs_meta->size); |
1730 | 0 | tsk_fprintf(hFile, "num of links: %u\n", fs_meta->nlink); |
1731 | | |
1732 | |
|
1733 | 0 | if (sec_skew != 0) { |
1734 | 0 | tsk_fprintf(hFile, "\nAdjusted Inode Times:\n"); |
1735 | 0 | if (fs_meta->mtime) |
1736 | 0 | fs_meta->mtime -= sec_skew; |
1737 | 0 | if (fs_meta->atime) |
1738 | 0 | fs_meta->atime -= sec_skew; |
1739 | 0 | if (fs_meta->ctime) |
1740 | 0 | fs_meta->ctime -= sec_skew; |
1741 | |
|
1742 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
1743 | 0 | tsk_fs_time_to_str(fs_meta->atime, timeBuf)); |
1744 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
1745 | 0 | tsk_fs_time_to_str(fs_meta->mtime, timeBuf)); |
1746 | 0 | tsk_fprintf(hFile, "Inode Modified:\t%s\n", |
1747 | 0 | tsk_fs_time_to_str(fs_meta->ctime, timeBuf)); |
1748 | |
|
1749 | 0 | if (fs_meta->mtime) |
1750 | 0 | fs_meta->mtime += sec_skew; |
1751 | 0 | if (fs_meta->atime) |
1752 | 0 | fs_meta->atime += sec_skew; |
1753 | 0 | if (fs_meta->ctime) |
1754 | 0 | fs_meta->ctime += sec_skew; |
1755 | |
|
1756 | 0 | tsk_fprintf(hFile, "\nOriginal Inode Times:\n"); |
1757 | 0 | } |
1758 | 0 | else { |
1759 | 0 | tsk_fprintf(hFile, "\nInode Times:\n"); |
1760 | 0 | } |
1761 | |
|
1762 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
1763 | 0 | tsk_fs_time_to_str(fs_meta->atime, timeBuf)); |
1764 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
1765 | 0 | tsk_fs_time_to_str(fs_meta->mtime, timeBuf)); |
1766 | 0 | tsk_fprintf(hFile, "Inode Modified:\t%s\n", |
1767 | 0 | tsk_fs_time_to_str(fs_meta->ctime, timeBuf)); |
1768 | |
|
1769 | 0 | if ((dino_buf = (char *) tsk_malloc(sizeof(ffs_inode2))) == NULL) |
1770 | 0 | return 1; |
1771 | | // we won't have dino_buf for "virtual" files |
1772 | 0 | if ((fs->ftype == TSK_FS_TYPE_FFS2) && (dino_buf)) { |
1773 | 0 | ffs_inode2 *in = (ffs_inode2 *) dino_buf; |
1774 | | /* Are there extended attributes */ |
1775 | 0 | if (tsk_getu32(fs->endian, in->di_extsize) > 0) { |
1776 | 0 | ffs_extattr *ea; |
1777 | 0 | uint32_t size; |
1778 | 0 | char name[257]; |
1779 | 0 | char *blk_buf; |
1780 | |
|
1781 | 0 | if ((blk_buf = (char*) tsk_malloc(ffs->ffsbsize_b)) == NULL) { |
1782 | 0 | free(dino_buf); |
1783 | 0 | return 1; |
1784 | 0 | } |
1785 | | |
1786 | 0 | size = tsk_getu32(fs->endian, in->di_extsize); |
1787 | 0 | tsk_fprintf(hFile, "\nExtended Attributes:\n"); |
1788 | 0 | tsk_fprintf(hFile, |
1789 | 0 | "Size: %" PRIu32 " (%" PRIu64 ", %" PRIu64 ")\n", size, |
1790 | 0 | tsk_getu64(fs->endian, in->di_extb[0]), |
1791 | 0 | tsk_getu64(fs->endian, in->di_extb[1])); |
1792 | | |
1793 | | |
1794 | | /* Process first block */ |
1795 | | // @@@ Incorporate values into this as well |
1796 | 0 | if ((tsk_getu64(fs->endian, in->di_extb[0]) >= fs->first_block) |
1797 | 0 | && (tsk_getu64(fs->endian, |
1798 | 0 | in->di_extb[0]) <= fs->last_block)) { |
1799 | 0 | uintptr_t end; |
1800 | 0 | ssize_t cnt; |
1801 | |
|
1802 | 0 | cnt = |
1803 | 0 | tsk_fs_read_block(fs, tsk_getu64(fs->endian, |
1804 | 0 | in->di_extb[0]), blk_buf, ffs->ffsbsize_b); |
1805 | 0 | if (cnt != ffs->ffsbsize_b) { |
1806 | 0 | if (cnt >= 0) { |
1807 | 0 | tsk_error_reset(); |
1808 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1809 | 0 | } |
1810 | 0 | tsk_error_set_errstr2 |
1811 | 0 | ("ffs_istat: FFS2 extended attribute 0 at %" |
1812 | 0 | PRIu64, tsk_getu64(fs->endian, in->di_extb[0])); |
1813 | 0 | free(blk_buf); |
1814 | 0 | free(dino_buf); |
1815 | 0 | return 1; |
1816 | 0 | } |
1817 | | |
1818 | 0 | ea = (ffs_extattr *) blk_buf; |
1819 | |
|
1820 | 0 | if (size > ffs->ffsbsize_b) { |
1821 | 0 | end = (uintptr_t) ea + ffs->ffsbsize_b; |
1822 | 0 | size -= ffs->ffsbsize_b; |
1823 | 0 | } |
1824 | 0 | else { |
1825 | 0 | end = (uintptr_t) ea + size; |
1826 | 0 | size = 0; |
1827 | 0 | } |
1828 | |
|
1829 | 0 | for (; (uintptr_t) ea < end; |
1830 | 0 | ea = |
1831 | 0 | (ffs_extattr *) ((uintptr_t) ea + |
1832 | 0 | tsk_getu32(fs->endian, ea->reclen))) { |
1833 | 0 | memcpy(name, ea->name, ea->nlen); |
1834 | 0 | name[ea->nlen] = '\0'; |
1835 | 0 | tsk_fprintf(hFile, "%s\n", name); |
1836 | 0 | } |
1837 | 0 | } |
1838 | 0 | if ((tsk_getu64(fs->endian, in->di_extb[1]) >= fs->first_block) |
1839 | 0 | && (tsk_getu64(fs->endian, |
1840 | 0 | in->di_extb[1]) <= fs->last_block)) { |
1841 | 0 | uintptr_t end; |
1842 | 0 | ssize_t cnt; |
1843 | |
|
1844 | 0 | cnt = |
1845 | 0 | tsk_fs_read_block(fs, tsk_getu64(fs->endian, |
1846 | 0 | in->di_extb[1]), blk_buf, ffs->ffsbsize_b); |
1847 | 0 | if (cnt != ffs->ffsbsize_b) { |
1848 | 0 | if (cnt >= 0) { |
1849 | 0 | tsk_error_reset(); |
1850 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
1851 | 0 | } |
1852 | 0 | tsk_error_set_errstr2 |
1853 | 0 | ("ffs_istat: FFS2 extended attribute 1 at %" |
1854 | 0 | PRIu64, tsk_getu64(fs->endian, in->di_extb[1])); |
1855 | 0 | free(blk_buf); |
1856 | 0 | free(dino_buf); |
1857 | 0 | return 1; |
1858 | 0 | } |
1859 | | |
1860 | 0 | ea = (ffs_extattr *) blk_buf; |
1861 | |
|
1862 | 0 | if (size > ffs->ffsbsize_b) |
1863 | 0 | end = (uintptr_t) ea + ffs->ffsbsize_b; |
1864 | 0 | else |
1865 | 0 | end = (uintptr_t) ea + size; |
1866 | |
|
1867 | 0 | for (; (uintptr_t) ea < end; |
1868 | 0 | ea = |
1869 | 0 | (ffs_extattr *) ((uintptr_t) ea + |
1870 | 0 | tsk_getu32(fs->endian, ea->reclen))) { |
1871 | 0 | memcpy(name, ea->name, ea->nlen); |
1872 | 0 | name[ea->nlen] = '\0'; |
1873 | 0 | tsk_fprintf(hFile, "%s\n", name); |
1874 | 0 | } |
1875 | 0 | } |
1876 | 0 | free(blk_buf); |
1877 | 0 | } |
1878 | 0 | free(dino_buf); |
1879 | 0 | } |
1880 | | |
1881 | | |
1882 | | /* A bad hack to force a specified number of blocks */ |
1883 | 0 | if (numblock > 0) |
1884 | 0 | fs_meta->size = numblock * ffs->ffsbsize_b; |
1885 | |
|
1886 | 0 | tsk_fprintf(hFile, "\nDirect Blocks:\n"); |
1887 | |
|
1888 | 0 | if (istat_flags & TSK_FS_ISTAT_RUNLIST) { |
1889 | 0 | const TSK_FS_ATTR * fs_attr_direct = tsk_fs_file_attr_get_type(fs_file.get(), |
1890 | 0 | TSK_FS_ATTR_TYPE_DEFAULT, 0, 0); |
1891 | 0 | if (fs_attr_direct && (fs_attr_direct->flags & TSK_FS_ATTR_NONRES)) { |
1892 | 0 | if (tsk_fs_attr_print(fs_attr_direct, hFile)) { |
1893 | 0 | tsk_fprintf(hFile, "\nError creating run lists\n"); |
1894 | 0 | tsk_error_print(hFile); |
1895 | 0 | tsk_error_reset(); |
1896 | 0 | } |
1897 | 0 | } |
1898 | 0 | } |
1899 | 0 | else { |
1900 | 0 | print.idx = 0; |
1901 | 0 | print.hFile = hFile; |
1902 | |
|
1903 | 0 | if (tsk_fs_file_walk(fs_file.get(), TSK_FS_FILE_WALK_FLAG_AONLY, |
1904 | 0 | print_addr_act, (void *)&print)) { |
1905 | 0 | tsk_fprintf(hFile, "\nError reading blocks in file\n"); |
1906 | 0 | tsk_error_print(hFile); |
1907 | 0 | return 1; |
1908 | 0 | } |
1909 | | |
1910 | 0 | if (print.idx != 0) |
1911 | 0 | tsk_fprintf(hFile, "\n"); |
1912 | 0 | } |
1913 | | |
1914 | 0 | fs_attr_indir = tsk_fs_file_attr_get_type(fs_file.get(), |
1915 | 0 | TSK_FS_ATTR_TYPE_UNIX_INDIR, 0, 0); |
1916 | 0 | if (fs_attr_indir) { |
1917 | 0 | tsk_fprintf(hFile, "\nIndirect Blocks:\n"); |
1918 | 0 | if (istat_flags & TSK_FS_ISTAT_RUNLIST) { |
1919 | 0 | if (tsk_fs_attr_print(fs_attr_indir, hFile)) { |
1920 | 0 | tsk_fprintf(hFile, "\nError creating run lists\n"); |
1921 | 0 | tsk_error_print(hFile); |
1922 | 0 | tsk_error_reset(); |
1923 | 0 | } |
1924 | 0 | } |
1925 | 0 | else { |
1926 | 0 | print.idx = 0; |
1927 | |
|
1928 | 0 | if (tsk_fs_attr_walk(fs_attr_indir, TSK_FS_FILE_WALK_FLAG_AONLY, |
1929 | 0 | print_addr_act, (void *)&print)) { |
1930 | 0 | tsk_fprintf(hFile, "\nError reading indirect attribute: "); |
1931 | 0 | tsk_error_print(hFile); |
1932 | 0 | tsk_error_reset(); |
1933 | 0 | } |
1934 | 0 | else if (print.idx != 0) { |
1935 | 0 | tsk_fprintf(hFile, "\n"); |
1936 | 0 | } |
1937 | 0 | } |
1938 | 0 | } |
1939 | |
|
1940 | 0 | return 0; |
1941 | 0 | } |
1942 | | |
1943 | | /* Return 1 on error and 0 on success */ |
1944 | | uint8_t |
1945 | | ffs_jopen( |
1946 | | [[maybe_unused]] TSK_FS_INFO * fs, |
1947 | | [[maybe_unused]] TSK_INUM_T inum) |
1948 | 0 | { |
1949 | 0 | tsk_error_reset(); |
1950 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
1951 | 0 | tsk_error_set_errstr("UFS does not have a journal"); |
1952 | 0 | return 1; |
1953 | 0 | } |
1954 | | |
1955 | | uint8_t |
1956 | | ffs_jentry_walk( |
1957 | | [[maybe_unused]] TSK_FS_INFO * fs, |
1958 | | [[maybe_unused]] int a_flags, |
1959 | | [[maybe_unused]] TSK_FS_JENTRY_WALK_CB action, |
1960 | | [[maybe_unused]] void *ptr) |
1961 | 0 | { |
1962 | 0 | tsk_error_reset(); |
1963 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
1964 | 0 | tsk_error_set_errstr("UFS does not have a journal"); |
1965 | 0 | return 1; |
1966 | 0 | } |
1967 | | |
1968 | | |
1969 | | uint8_t |
1970 | | ffs_jblk_walk( |
1971 | | [[maybe_unused]] TSK_FS_INFO * fs, |
1972 | | [[maybe_unused]] TSK_DADDR_T start, |
1973 | | [[maybe_unused]] TSK_DADDR_T end, |
1974 | | [[maybe_unused]] int a_flags, |
1975 | | [[maybe_unused]] TSK_FS_JBLK_WALK_CB action, |
1976 | | [[maybe_unused]] void *ptr) |
1977 | 0 | { |
1978 | 0 | tsk_error_reset(); |
1979 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
1980 | 0 | tsk_error_set_errstr("UFS does not have a journal"); |
1981 | 0 | return 1; |
1982 | 0 | } |
1983 | | |
1984 | | |
1985 | | /* ffs_close - close a fast file system */ |
1986 | | static void |
1987 | | ffs_close(TSK_FS_INFO * fs) |
1988 | 0 | { |
1989 | 0 | FFS_INFO *ffs = (FFS_INFO *) fs; |
1990 | |
|
1991 | 0 | fs->tag = 0; |
1992 | |
|
1993 | 0 | free(ffs->grp_buf); |
1994 | 0 | free(ffs->itbl_buf); |
1995 | |
|
1996 | 0 | tsk_deinit_lock(&ffs->lock); |
1997 | |
|
1998 | 0 | free(ffs->fs.sb1); |
1999 | 0 | tsk_fs_free(fs); |
2000 | 0 | } |
2001 | | |
2002 | | /** |
2003 | | * \internal |
2004 | | * Open part of a disk image as a FFS/UFS file system. |
2005 | | * |
2006 | | * @param img_info Disk image to analyze |
2007 | | * @param offset Byte offset where file system starts |
2008 | | * @param ftype Specific type of file system |
2009 | | * @param a_pass NOT USED |
2010 | | * @param test NOT USED |
2011 | | * @returns NULL on error or if data is not a FFS file system |
2012 | | */ |
2013 | | TSK_FS_INFO * |
2014 | | ffs_open( |
2015 | | TSK_IMG_INFO * img_info, |
2016 | | TSK_OFF_T offset, |
2017 | | TSK_FS_TYPE_ENUM ftype, |
2018 | | [[maybe_unused]] const char* a_pass, |
2019 | | [[maybe_unused]] uint8_t test) |
2020 | 0 | { |
2021 | 0 | const char *myname = "ffs_open"; |
2022 | 0 | FFS_INFO *ffs; |
2023 | 0 | unsigned int len; |
2024 | 0 | TSK_FS_INFO *fs; |
2025 | 0 | ssize_t cnt; |
2026 | | |
2027 | | // clean up any error messages that are lying around |
2028 | 0 | tsk_error_reset(); |
2029 | |
|
2030 | 0 | if (TSK_FS_TYPE_ISFFS(ftype) == 0) { |
2031 | 0 | tsk_error_reset(); |
2032 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
2033 | 0 | tsk_error_set_errstr("Invalid FS Type in ffs_open"); |
2034 | 0 | return NULL; |
2035 | 0 | } |
2036 | | |
2037 | 0 | if (img_info->sector_size == 0) { |
2038 | 0 | tsk_error_reset(); |
2039 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
2040 | 0 | tsk_error_set_errstr("ffs_open: sector size is 0"); |
2041 | 0 | return NULL; |
2042 | 0 | } |
2043 | | |
2044 | 0 | if ((ffs = (FFS_INFO *) tsk_fs_malloc(sizeof(*ffs))) == NULL) |
2045 | 0 | return NULL; |
2046 | | |
2047 | 0 | fs = &(ffs->fs_info); |
2048 | |
|
2049 | 0 | fs->ftype = ftype; |
2050 | 0 | fs->flags = TSK_FS_INFO_FLAG_NONE; |
2051 | 0 | fs->duname = "Fragment"; |
2052 | 0 | fs->tag = TSK_FS_INFO_TAG; |
2053 | |
|
2054 | 0 | fs->img_info = img_info; |
2055 | 0 | fs->offset = offset; |
2056 | | |
2057 | | /* Both sbs are the same size */ |
2058 | 0 | len = roundup(sizeof(ffs_sb1), img_info->sector_size); |
2059 | 0 | ffs->fs.sb1 = (ffs_sb1 *) tsk_malloc(len); |
2060 | 0 | if (ffs->fs.sb1 == NULL) { |
2061 | 0 | fs->tag = 0; |
2062 | 0 | tsk_fs_free((TSK_FS_INFO *)ffs); |
2063 | 0 | return NULL; |
2064 | 0 | } |
2065 | | |
2066 | | /* check the magic and figure out the endian ordering */ |
2067 | | |
2068 | | /* Try UFS2 first - I read somewhere that some upgrades |
2069 | | * kept the original UFS1 superblock in addition to |
2070 | | * the new one */ |
2071 | 0 | cnt = tsk_fs_read |
2072 | 0 | (fs, (TSK_OFF_T) UFS2_SBOFF, (char *) ffs->fs.sb2, |
2073 | 0 | sizeof(ffs_sb2)); |
2074 | 0 | if (cnt != sizeof(ffs_sb2)) { |
2075 | 0 | if (cnt >= 0) { |
2076 | 0 | tsk_error_reset(); |
2077 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
2078 | 0 | } |
2079 | 0 | tsk_error_set_errstr("%s: Superblock at %" PRIuDADDR, myname, |
2080 | 0 | (TSK_OFF_T) UFS2_SBOFF); |
2081 | 0 | fs->tag = 0; |
2082 | 0 | free(ffs->fs.sb1); |
2083 | 0 | tsk_fs_free((TSK_FS_INFO *)ffs); |
2084 | 0 | return NULL; |
2085 | 0 | } |
2086 | | |
2087 | | /* If that didn't work, try the 256KB UFS2 location */ |
2088 | 0 | if (tsk_fs_guessu32(fs, ffs->fs.sb2->magic, UFS2_FS_MAGIC)) { |
2089 | 0 | if (tsk_verbose) |
2090 | 0 | fprintf(stderr, "ufs_open: Trying 256KB UFS2 location\n"); |
2091 | |
|
2092 | 0 | cnt = tsk_fs_read |
2093 | 0 | (fs, (TSK_OFF_T) UFS2_SBOFF2, (char *) ffs->fs.sb2, |
2094 | 0 | sizeof(ffs_sb2)); |
2095 | 0 | if (cnt != sizeof(ffs_sb2)) { |
2096 | 0 | if (cnt >= 0) { |
2097 | 0 | tsk_error_reset(); |
2098 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
2099 | 0 | } |
2100 | 0 | tsk_error_set_errstr2("%s: Superblock at %" PRIuDADDR, |
2101 | 0 | myname, (TSK_OFF_T) UFS2_SBOFF2); |
2102 | 0 | fs->tag = 0; |
2103 | 0 | free(ffs->fs.sb1); |
2104 | 0 | tsk_fs_free((TSK_FS_INFO *)ffs); |
2105 | 0 | return NULL; |
2106 | 0 | } |
2107 | | |
2108 | | /* Try UFS1 if that did not work */ |
2109 | 0 | if (tsk_fs_guessu32(fs, ffs->fs.sb2->magic, UFS2_FS_MAGIC)) { |
2110 | 0 | if (tsk_verbose) |
2111 | 0 | fprintf(stderr, "ufs_open: Trying UFS1 location\n"); |
2112 | |
|
2113 | 0 | cnt = tsk_fs_read |
2114 | 0 | (fs, (TSK_OFF_T) UFS1_SBOFF, (char *) ffs->fs.sb1, len); |
2115 | 0 | if (cnt != len) { |
2116 | 0 | if (cnt >= 0) { |
2117 | 0 | tsk_error_reset(); |
2118 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
2119 | 0 | } |
2120 | 0 | tsk_error_set_errstr2("%s: Superblock at %" PRIuDADDR, |
2121 | 0 | myname, (TSK_OFF_T) UFS1_SBOFF); |
2122 | 0 | fs->tag = 0; |
2123 | 0 | free(ffs->fs.sb1); |
2124 | 0 | tsk_fs_free((TSK_FS_INFO *)ffs); |
2125 | 0 | return NULL; |
2126 | 0 | } |
2127 | 0 | if (tsk_fs_guessu32(fs, ffs->fs.sb1->magic, UFS1_FS_MAGIC)) { |
2128 | 0 | tsk_error_reset(); |
2129 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
2130 | 0 | tsk_error_set_errstr("No UFS Magic Found"); |
2131 | 0 | if (tsk_verbose) |
2132 | 0 | fprintf(stderr, "ufs_open: No UFS magic found\n"); |
2133 | 0 | fs->tag = 0; |
2134 | 0 | free(ffs->fs.sb1); |
2135 | 0 | tsk_fs_free((TSK_FS_INFO *)ffs); |
2136 | 0 | return NULL; |
2137 | 0 | } |
2138 | 0 | else { |
2139 | | // @@@ NEED TO DIFFERENTIATE BETWEEN A & B - UID/GID location in inode |
2140 | 0 | fs->ftype = TSK_FS_TYPE_FFS1; |
2141 | 0 | } |
2142 | 0 | } |
2143 | 0 | else { |
2144 | 0 | fs->ftype = TSK_FS_TYPE_FFS2; |
2145 | 0 | } |
2146 | 0 | } |
2147 | 0 | else { |
2148 | 0 | fs->ftype = TSK_FS_TYPE_FFS2; |
2149 | 0 | } |
2150 | | |
2151 | | |
2152 | | /* |
2153 | | * Translate some filesystem-specific information to generic form. |
2154 | | */ |
2155 | | |
2156 | 0 | if (fs->ftype == TSK_FS_TYPE_FFS2) { |
2157 | 0 | fs->block_count = tsk_gets64(fs->endian, ffs->fs.sb2->frag_num); |
2158 | 0 | fs->block_size = tsk_gets32(fs->endian, ffs->fs.sb2->fsize_b); |
2159 | 0 | ffs->ffsbsize_b = tsk_gets32(fs->endian, ffs->fs.sb2->bsize_b); |
2160 | 0 | ffs->ffsbsize_f = tsk_gets32(fs->endian, ffs->fs.sb2->bsize_frag); |
2161 | 0 | ffs->groups_count = tsk_gets32(fs->endian, ffs->fs.sb2->cg_num); |
2162 | 0 | } |
2163 | 0 | else { |
2164 | 0 | fs->block_count = tsk_gets32(fs->endian, ffs->fs.sb1->frag_num); |
2165 | 0 | fs->block_size = tsk_gets32(fs->endian, ffs->fs.sb1->fsize_b); |
2166 | 0 | ffs->ffsbsize_b = tsk_gets32(fs->endian, ffs->fs.sb1->bsize_b); |
2167 | 0 | ffs->ffsbsize_f = tsk_gets32(fs->endian, ffs->fs.sb1->bsize_frag); |
2168 | 0 | ffs->groups_count = tsk_gets32(fs->endian, ffs->fs.sb1->cg_num); |
2169 | 0 | } |
2170 | | |
2171 | | // apply some sanity checks before we start using these numbers |
2172 | 0 | if ((fs->block_size == 0) || (ffs->ffsbsize_b == 0) || (ffs->ffsbsize_f == 0) |
2173 | 0 | || (fs->block_size % 512) || (ffs->ffsbsize_b % 512)) { |
2174 | 0 | tsk_error_reset(); |
2175 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
2176 | 0 | tsk_error_set_errstr |
2177 | 0 | ("Not a UFS FS (invalid fragment or block size)"); |
2178 | 0 | if (tsk_verbose) |
2179 | 0 | fprintf(stderr, "ufs_open: invalid fragment or block size\n"); |
2180 | 0 | fs->tag = 0; |
2181 | 0 | free(ffs->fs.sb1); |
2182 | 0 | tsk_fs_free((TSK_FS_INFO *)ffs); |
2183 | 0 | return NULL; |
2184 | 0 | } |
2185 | | |
2186 | 0 | if ((ffs->ffsbsize_b / fs->block_size) != ffs->ffsbsize_f) { |
2187 | 0 | tsk_error_reset(); |
2188 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
2189 | 0 | tsk_error_set_errstr("Not a UFS FS (frag / block size mismatch)"); |
2190 | 0 | if (tsk_verbose) |
2191 | 0 | fprintf(stderr, "ufs_open: fragment / block size mismatch\n"); |
2192 | 0 | fs->tag = 0; |
2193 | 0 | free(ffs->fs.sb1); |
2194 | 0 | tsk_fs_free((TSK_FS_INFO *)ffs); |
2195 | 0 | return NULL; |
2196 | 0 | } |
2197 | | |
2198 | | /* |
2199 | | * Block calculations |
2200 | | */ |
2201 | 0 | fs->first_block = 0; |
2202 | 0 | fs->last_block = fs->last_block_act = fs->block_count - 1; |
2203 | 0 | fs->dev_bsize = img_info->sector_size; |
2204 | | |
2205 | | // determine the last block we have in this image |
2206 | 0 | if ((TSK_DADDR_T) ((img_info->size - offset) / fs->block_size) < |
2207 | 0 | fs->block_count) |
2208 | 0 | fs->last_block_act = |
2209 | 0 | (img_info->size - offset) / fs->block_size - 1; |
2210 | | |
2211 | | |
2212 | | |
2213 | | // Inode / meta data calculations |
2214 | 0 | if (fs->ftype == TSK_FS_TYPE_FFS2) { |
2215 | 0 | fs->inum_count = ffs->groups_count * tsk_gets32(fs->endian, ffs->fs.sb2->cg_inode_num) + 1; // we are adding 1 in this calc to account for Orphans directory |
2216 | 0 | } |
2217 | 0 | else { |
2218 | 0 | fs->inum_count = ffs->groups_count * tsk_gets32(fs->endian, ffs->fs.sb1->cg_inode_num) + 1; // we are adding 1 in this calc to account for Orphans directory |
2219 | 0 | } |
2220 | |
|
2221 | 0 | fs->root_inum = FFS_ROOTINO; |
2222 | 0 | fs->first_inum = FFS_FIRSTINO; |
2223 | 0 | fs->last_inum = fs->inum_count - 1; |
2224 | | |
2225 | | /* Volume ID - in the same place for both types */ |
2226 | 0 | for (fs->fs_id_used = 0; fs->fs_id_used < 8; fs->fs_id_used++) { |
2227 | 0 | fs->fs_id[fs->fs_id_used] = ffs->fs.sb1->fs_id[fs->fs_id_used]; |
2228 | 0 | } |
2229 | | |
2230 | | // set the function pointers |
2231 | 0 | fs->inode_walk = ffs_inode_walk; |
2232 | 0 | fs->block_walk = ffs_block_walk; |
2233 | 0 | fs->block_getflags = ffs_block_getflags; |
2234 | |
|
2235 | 0 | fs->get_default_attr_type = tsk_fs_unix_get_default_attr_type; |
2236 | 0 | fs->load_attrs = tsk_fs_unix_make_data_run; |
2237 | 0 | fs->name_cmp = tsk_fs_unix_name_cmp; |
2238 | |
|
2239 | 0 | fs->file_add_meta = ffs_inode_lookup; |
2240 | 0 | fs->dir_open_meta = ffs_dir_open_meta; |
2241 | 0 | fs->fsstat = ffs_fsstat; |
2242 | 0 | fs->fscheck = ffs_fscheck; |
2243 | 0 | fs->istat = ffs_istat; |
2244 | 0 | fs->close = ffs_close; |
2245 | 0 | fs->jblk_walk = ffs_jblk_walk; |
2246 | 0 | fs->jentry_walk = ffs_jentry_walk; |
2247 | 0 | fs->jopen = ffs_jopen; |
2248 | 0 | fs->journ_inum = 0; |
2249 | | |
2250 | | // initialize caches |
2251 | 0 | ffs->grp_buf = NULL; |
2252 | 0 | ffs->grp_num = 0xffffffff; |
2253 | 0 | ffs->grp_addr = 0; |
2254 | |
|
2255 | 0 | ffs->itbl_buf = NULL; |
2256 | 0 | ffs->itbl_addr = 0; |
2257 | | |
2258 | | /* |
2259 | | * Print some stats. |
2260 | | */ |
2261 | 0 | if (tsk_verbose) |
2262 | 0 | tsk_fprintf(stderr, |
2263 | 0 | "inodes %" PRIuINUM " root ino %" PRIuINUM " cyl groups %" |
2264 | 0 | PRId32 " blocks %" PRIuDADDR "\n", fs->inum_count, |
2265 | 0 | fs->root_inum, ffs->groups_count, fs->block_count); |
2266 | |
|
2267 | 0 | tsk_init_lock(&ffs->lock); |
2268 | |
|
2269 | 0 | return fs; |
2270 | 0 | } |