1 /*
2 ** ffs_dent
3 ** The Sleuth Kit
4 **
5 ** File name layer for a FFS/UFS image
6 **
7 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
8 ** Copyright (c) 2006-2011 Brian Carrier, Basis Technology. All Rights reserved
9 ** Copyright (c) 2003-2006 Brian Carrier. All rights reserved
10 **
11 ** TASK
12 ** Copyright (c) 2002 Brian Carrier, @stake Inc. All rights reserved
13 **
14 ** TCTUTILs
15 ** Copyright (c) 2001 Brian Carrier. All rights reserved
16 **
17 **
18 ** This software is distributed under the Common Public License 1.0
19 **
20 */
21
22 /**
23 * \file ffs_dent.c
24 * Contains the internal TSK UFS/FFS file name (directory entry) processing functions
25 */
26
27 #include <ctype.h>
28 #include "tsk_fs_i.h"
29 #include "tsk_ffs.h"
30
31
32 static uint8_t
ffs_dent_copy(FFS_INFO * ffs,char * ffs_dent,TSK_FS_NAME * fs_name)33 ffs_dent_copy(FFS_INFO * ffs, char *ffs_dent, TSK_FS_NAME * fs_name)
34 {
35 TSK_FS_INFO *a_fs = &(ffs->fs_info);
36
37 /* this one has the type field */
38 if ((a_fs->ftype == TSK_FS_TYPE_FFS1)
39 || (a_fs->ftype == TSK_FS_TYPE_FFS2)) {
40 ffs_dentry1 *dir = (ffs_dentry1 *) ffs_dent;
41
42 fs_name->meta_addr = tsk_getu32(a_fs->endian, dir->d_ino);
43
44 if (fs_name->name_size != FFS_MAXNAMLEN) {
45 if (tsk_fs_name_realloc(fs_name, FFS_MAXNAMLEN))
46 return 1;
47 }
48
49 /* ffs null terminates so we can strncpy */
50 strncpy(fs_name->name, dir->d_name, fs_name->name_size);
51
52 switch (dir->d_type) {
53 case FFS_DT_REG:
54 fs_name->type = TSK_FS_NAME_TYPE_REG;
55 break;
56 case FFS_DT_DIR:
57 fs_name->type = TSK_FS_NAME_TYPE_DIR;
58 break;
59 case FFS_DT_CHR:
60 fs_name->type = TSK_FS_NAME_TYPE_CHR;
61 break;
62 case FFS_DT_BLK:
63 fs_name->type = TSK_FS_NAME_TYPE_BLK;
64 break;
65 case FFS_DT_FIFO:
66 fs_name->type = TSK_FS_NAME_TYPE_FIFO;
67 break;
68 case FFS_DT_SOCK:
69 fs_name->type = TSK_FS_NAME_TYPE_SOCK;
70 break;
71 case FFS_DT_LNK:
72 fs_name->type = TSK_FS_NAME_TYPE_LNK;
73 break;
74 case FFS_DT_WHT:
75 fs_name->type = TSK_FS_NAME_TYPE_WHT;
76 break;
77 case FFS_DT_UNKNOWN:
78 default:
79 fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
80 break;
81 }
82 }
83 else if (a_fs->ftype == TSK_FS_TYPE_FFS1B) {
84 ffs_dentry2 *dir = (ffs_dentry2 *) ffs_dent;
85
86 fs_name->meta_addr = tsk_getu32(a_fs->endian, dir->d_ino);
87
88 if (fs_name->name_size != FFS_MAXNAMLEN) {
89 if (tsk_fs_name_realloc(fs_name, FFS_MAXNAMLEN))
90 return 1;
91 }
92
93 /* ffs null terminates so we can strncpy */
94 strncpy(fs_name->name, dir->d_name, fs_name->name_size);
95
96 fs_name->type = TSK_FS_NAME_TYPE_UNDEF;
97 }
98 else {
99 tsk_error_reset();
100 tsk_error_set_errno(TSK_ERR_FS_ARG);
101 tsk_error_set_errstr("ffs_dent_copy: Unknown FS type");
102 return 1;
103 }
104
105 fs_name->flags = 0;
106 return 0;
107 }
108
109
110 /*
111 * @param a_is_del Set to 1 if block is from a deleted directory.
112 */
113 static TSK_RETVAL_ENUM
ffs_dent_parse_block(FFS_INFO * ffs,TSK_FS_DIR * fs_dir,uint8_t a_is_del,char * buf,unsigned int len)114 ffs_dent_parse_block(FFS_INFO * ffs, TSK_FS_DIR * fs_dir, uint8_t a_is_del,
115 char *buf, unsigned int len)
116 {
117 unsigned int idx;
118 unsigned int inode = 0, dellen = 0, reclen = 0;
119 unsigned int minreclen = 4;
120 TSK_FS_INFO *fs = &(ffs->fs_info);
121
122 char *dirPtr;
123 TSK_FS_NAME *fs_name;
124
125 if ((fs_name = tsk_fs_name_alloc(FFS_MAXNAMLEN + 1, 0)) == NULL)
126 return TSK_ERR;
127
128 /* update each time by the actual length instead of the
129 ** recorded length so we can view the deleted entries
130 */
131 for (idx = 0; idx <= len - FFS_DIRSIZ_lcl(1); idx += minreclen) {
132 unsigned int namelen = 0;
133
134 dirPtr = (char *) &buf[idx];
135
136 /* copy to local variables */
137 if ((fs->ftype == TSK_FS_TYPE_FFS1)
138 || (fs->ftype == TSK_FS_TYPE_FFS2)) {
139 ffs_dentry1 *dir = (ffs_dentry1 *) dirPtr;
140 inode = tsk_getu32(fs->endian, dir->d_ino);
141 namelen = dir->d_namlen;
142 reclen = tsk_getu16(fs->endian, dir->d_reclen);
143 }
144 /* TSK_FS_TYPE_FFS1B */
145 else if (fs->ftype == TSK_FS_TYPE_FFS1B) {
146 ffs_dentry2 *dir = (ffs_dentry2 *) dirPtr;
147 inode = tsk_getu32(fs->endian, dir->d_ino);
148 namelen = tsk_getu16(fs->endian, dir->d_namlen);
149 reclen = tsk_getu16(fs->endian, dir->d_reclen);
150 }
151
152 /* what is the minimum size needed for this entry */
153 minreclen = FFS_DIRSIZ_lcl(namelen);
154
155 /* Perform a couple sanity checks
156 ** OpenBSD never zeros the inode number, but solaris
157 ** does. These checks will hopefully catch all non
158 ** entries
159 */
160 if ((inode > fs->last_inum) || // inode is unsigned
161 (namelen > FFS_MAXNAMLEN) || // namelen is unsigned
162 (namelen == 0) ||
163 (reclen < minreclen) || (reclen % 4) || (idx + reclen > len)) {
164
165 /* we don't have a valid entry, so skip ahead 4 */
166 minreclen = 4;
167 if (dellen > 0)
168 dellen -= 4;
169 continue;
170 }
171
172 /* Before we process an entry in unallocated space, make
173 * sure that it also ends in the unalloc space */
174 if ((dellen) && (dellen < minreclen)) {
175 minreclen = 4;
176 dellen -= 4;
177 continue;
178 }
179
180 /* the entry is valid */
181 if (ffs_dent_copy(ffs, dirPtr, fs_name)) {
182 tsk_fs_name_free(fs_name);
183 return TSK_ERR;
184 }
185
186
187 /* Do we have a deleted entry? (are we in a deleted space) */
188 if ((dellen > 0) || (inode == 0) || (a_is_del)) {
189 fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC;
190 if (dellen)
191 dellen -= minreclen;
192 }
193 else {
194 fs_name->flags = TSK_FS_NAME_FLAG_ALLOC;
195 }
196 if (tsk_fs_dir_add(fs_dir, fs_name)) {
197 tsk_fs_name_free(fs_name);
198 return TSK_ERR;
199 }
200
201 /* If we have some slack and an entry could exist in it, the set dellen */
202 if (dellen <= 0) {
203 if (reclen - minreclen >= FFS_DIRSIZ_lcl(1))
204 dellen = reclen - minreclen;
205 else
206 minreclen = reclen;
207 }
208 }
209
210 tsk_fs_name_free(fs_name);
211 return TSK_OK;
212 } /* end ffs_dent_parse_block */
213
214 /** \internal
215 * Process a directory and load up FS_DIR with the entries. If a pointer to
216 * an already allocated FS_DIR structure is given, it will be cleared. If no existing
217 * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return
218 * value is error or corruption, then the FS_DIR structure could
219 * have entries (depending on when the error occurred).
220 *
221 * @param a_fs File system to analyze
222 * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated
223 * structure or a new structure.
224 * @param a_addr Address of directory to process.
225 * @returns error, corruption, ok etc.
226 */
227 TSK_RETVAL_ENUM
ffs_dir_open_meta(TSK_FS_INFO * a_fs,TSK_FS_DIR ** a_fs_dir,TSK_INUM_T a_addr)228 ffs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir,
229 TSK_INUM_T a_addr)
230 {
231 TSK_OFF_T size;
232 FFS_INFO *ffs = (FFS_INFO *) a_fs;
233 char *dirbuf;
234 int nchnk, cidx;
235 TSK_FS_DIR *fs_dir;
236
237 /* If we get corruption in one of the blocks, then continue processing.
238 * retval_final will change when corruption is detected. Errors are
239 * returned immediately. */
240 TSK_RETVAL_ENUM retval_tmp;
241 TSK_RETVAL_ENUM retval_final = TSK_OK;
242
243
244 if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) {
245 tsk_error_reset();
246 tsk_error_set_errno(TSK_ERR_FS_WALK_RNG);
247 tsk_error_set_errstr("ffs_dir_open_meta: Invalid inode value: %"
248 PRIuINUM, a_addr);
249 return TSK_ERR;
250 }
251 else if (a_fs_dir == NULL) {
252 tsk_error_reset();
253 tsk_error_set_errno(TSK_ERR_FS_ARG);
254 tsk_error_set_errstr
255 ("ffs_dir_open_meta: NULL fs_attr argument given");
256 return TSK_ERR;
257 }
258
259 if (tsk_verbose)
260 tsk_fprintf(stderr,
261 "ffs_dir_open_meta: Processing directory %" PRIuINUM "\n",
262 a_addr);
263
264 fs_dir = *a_fs_dir;
265 if (fs_dir) {
266 tsk_fs_dir_reset(fs_dir);
267 fs_dir->addr = a_addr;
268 }
269 else {
270 if ((*a_fs_dir = fs_dir =
271 tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) {
272 return TSK_ERR;
273 }
274 }
275
276 // handle the orphan directory if its contents were requested
277 if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) {
278 return tsk_fs_dir_find_orphans(a_fs, fs_dir);
279 }
280
281 if ((fs_dir->fs_file =
282 tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) {
283 tsk_error_reset();
284 tsk_error_errstr2_concat("- ffs_dir_open_meta");
285 return TSK_COR;
286 }
287
288 /* dirbuf will only be used to process one block at a time */
289 size = roundup(fs_dir->fs_file->meta->size, FFS_DIRBLKSIZ);
290 if ((dirbuf = tsk_malloc((size_t)FFS_DIRBLKSIZ)) == NULL) {
291 return TSK_ERR;
292 }
293
294 /* Directory entries are written in chunks of DIRBLKSIZ
295 ** determine how many chunks of this size we have to read to
296 ** get a full block
297 **
298 ** Entries do not cross over the DIRBLKSIZ boundary
299 */
300 nchnk = (int) (size) / (FFS_DIRBLKSIZ) + 1;
301
302 TSK_OFF_T offset = 0;
303 for (cidx = 0; cidx < nchnk && (int64_t) size > 0; cidx++) {
304 int len = (FFS_DIRBLKSIZ < size) ? FFS_DIRBLKSIZ : (int) size;
305
306 ssize_t cnt = tsk_fs_file_read(fs_dir->fs_file, offset, dirbuf, len, (TSK_FS_FILE_READ_FLAG_ENUM)0);
307 if (cnt != len) {
308 tsk_error_reset();
309 tsk_error_set_errno(TSK_ERR_FS_FWALK);
310 tsk_error_set_errstr
311 ("ffs_dir_open_meta: Error reading directory contents: %"
312 PRIuINUM "\n", a_addr);
313 free(dirbuf);
314 return TSK_COR;
315 }
316
317 retval_tmp =
318 ffs_dent_parse_block(ffs, fs_dir,
319 (fs_dir->fs_file->meta->
320 flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0,
321 dirbuf, len);
322
323 if (retval_tmp == TSK_ERR) {
324 retval_final = TSK_ERR;
325 break;
326 }
327 else if (retval_tmp == TSK_COR) {
328 retval_final = TSK_COR;
329 }
330 size -= len;
331 offset += len;
332 }
333 free(dirbuf);
334
335 // if we are listing the root directory, add the Orphan directory entry
336 if (a_addr == a_fs->root_inum) {
337 TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0);
338 if (fs_name == NULL)
339 return TSK_ERR;
340
341 if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) {
342 tsk_fs_name_free(fs_name);
343 return TSK_ERR;
344 }
345
346 if (tsk_fs_dir_add(fs_dir, fs_name)) {
347 tsk_fs_name_free(fs_name);
348 return TSK_ERR;
349 }
350 tsk_fs_name_free(fs_name);
351 }
352
353 return retval_final;
354 }
355