/src/sleuthkit/tsk/fs/hfs_dent.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** The Sleuth Kit |
3 | | ** |
4 | | ** This software is subject to the IBM Public License ver. 1.0, |
5 | | ** which was displayed prior to download and is included in the readme.txt |
6 | | ** file accompanying the Sleuth Kit files. It may also be requested from: |
7 | | ** Crucial Security Inc. |
8 | | ** 14900 Conference Center Drive |
9 | | ** Chantilly, VA 20151 |
10 | | ** |
11 | | ** Copyright (c) 2009-2011 Brian Carrier. All rights reserved. |
12 | | ** |
13 | | ** Judson Powers [jpowers@atc-nycorp.com] |
14 | | ** Matt Stillerman [matt@atc-nycorp.com] |
15 | | ** Copyright (c) 2008, 2012 ATC-NY. All rights reserved. |
16 | | ** This file contains data developed with support from the National |
17 | | ** Institute of Justice, Office of Justice Programs, U.S. Department of Justice. |
18 | | ** |
19 | | ** Wyatt Banks [wbanks@crucialsecurity.com] |
20 | | ** Copyright (c) 2005 Crucial Security Inc. All rights reserved. |
21 | | ** |
22 | | ** Brian Carrier [carrier@sleuthkit.org] |
23 | | ** Copyright (c) 2003-2005 Brian Carrier. All rights reserved |
24 | | ** |
25 | | ** Copyright (c) 1997,1998,1999, International Business Machines |
26 | | ** Corporation and others. All Rights Reserved. |
27 | | */ |
28 | | |
29 | | /* TCT |
30 | | * LICENSE |
31 | | * This software is distributed under the IBM Public License. |
32 | | * AUTHOR(S) |
33 | | * Wietse Venema |
34 | | * IBM T.J. Watson Research |
35 | | * P.O. Box 704 |
36 | | * Yorktown Heights, NY 10598, USA |
37 | | --*/ |
38 | | |
39 | | /* |
40 | | ** You may distribute the Sleuth Kit, or other software that incorporates |
41 | | ** part of all of the Sleuth Kit, in object code form under a license agreement, |
42 | | ** provided that: |
43 | | ** a) you comply with the terms and conditions of the IBM Public License |
44 | | ** ver 1.0; and |
45 | | ** b) the license agreement |
46 | | ** i) effectively disclaims on behalf of all Contributors all warranties |
47 | | ** and conditions, express and implied, including warranties or |
48 | | ** conditions of title and non-infringement, and implied warranties |
49 | | ** or conditions of merchantability and fitness for a particular |
50 | | ** purpose. |
51 | | ** ii) effectively excludes on behalf of all Contributors liability for |
52 | | ** damages, including direct, indirect, special, incidental and |
53 | | ** consequential damages such as lost profits. |
54 | | ** iii) states that any provisions which differ from IBM Public License |
55 | | ** ver. 1.0 are offered by that Contributor alone and not by any |
56 | | ** other party; and |
57 | | ** iv) states that the source code for the program is available from you, |
58 | | ** and informs licensees how to obtain it in a reasonable manner on or |
59 | | ** through a medium customarily used for software exchange. |
60 | | ** |
61 | | ** When the Sleuth Kit or other software that incorporates part or all of |
62 | | ** the Sleuth Kit is made available in source code form: |
63 | | ** a) it must be made available under IBM Public License ver. 1.0; and |
64 | | ** b) a copy of the IBM Public License ver. 1.0 must be included with |
65 | | ** each copy of the program. |
66 | | */ |
67 | | |
68 | | /** \file hfs_dent.c |
69 | | * Contains the file name layer code for HFS+ file systems -- not included in |
70 | | * code by default. |
71 | | */ |
72 | | |
73 | | #include "tsk_fs_i.h" |
74 | | #include "tsk_hfs.h" |
75 | | |
76 | | /* convert HFS+'s UTF16 to UTF8 |
77 | | * replaces null characters with another character (0xfffd) |
78 | | * replaces slashes (permitted by HFS+ but causes problems with TSK) |
79 | | * with colons (generally not allowed by Mac OS X) |
80 | | * note that at least one directory on HFS+ volumes begins with |
81 | | * four nulls, so we do need to handle nulls; also, Apple chooses |
82 | | * to encode nulls as UTF8 \xC0\x80, which is not a valid UTF8 sequence |
83 | | * |
84 | | * @param fs the file system |
85 | | * @param uni the UTF16 string as a sequence of bytes |
86 | | * @param ulen then length of the UTF16 string in characters |
87 | | * @param asc a buffer to hold the UTF8 result |
88 | | * @param alen the length of that buffer |
89 | | * @param flags control some aspects of the conversion |
90 | | * @return 0 on success, 1 on failure; sets up to error string 1 |
91 | | * |
92 | | * HFS_U16U8_FLAG_REPLACE_SLASH if this flag is set, then slashes will be replaced |
93 | | * by colons. Otherwise, they will not be replaced. |
94 | | * |
95 | | * HFS_U16U8_FLAG_REPLACE_CONTROL if this flag is set, then all control characters |
96 | | * will be replaced by the UTF16_NULL_REPLACE character. N.B., always replaces |
97 | | * null characters regardless of this flag. |
98 | | */ |
99 | | uint8_t |
100 | | hfs_UTF16toUTF8(TSK_FS_INFO * fs, uint8_t * uni, int ulen, char *asc, |
101 | | int alen, uint32_t flags) |
102 | 0 | { |
103 | 0 | UTF8 *ptr8; |
104 | 0 | uint8_t *uniclean; |
105 | 0 | UTF16 *ptr16; |
106 | 0 | int i; |
107 | 0 | TSKConversionResult r; |
108 | | |
109 | | // remove nulls from the Unicode string |
110 | | // convert / to : |
111 | 0 | uniclean = (uint8_t *) tsk_malloc(ulen * 2); |
112 | 0 | if (!uniclean) |
113 | 0 | return 1; |
114 | | |
115 | 0 | memcpy(uniclean, uni, ulen * 2); |
116 | |
|
117 | 0 | for (i = 0; i < ulen; ++i) { |
118 | 0 | uint16_t uc = tsk_getu16(fs->endian, uniclean + i * 2); |
119 | | |
120 | |
|
121 | 0 | int changed = 0; |
122 | 0 | if (uc == UTF16_NULL) { |
123 | 0 | uc = UTF16_NULL_REPLACE; |
124 | 0 | changed = 1; |
125 | 0 | } |
126 | 0 | else if ((flags & HFS_U16U8_FLAG_REPLACE_SLASH) |
127 | 0 | && uc == UTF16_SLASH) { |
128 | 0 | uc = UTF16_COLON; |
129 | 0 | changed = 1; |
130 | 0 | } |
131 | | |
132 | 0 | else if ((flags & HFS_U16U8_FLAG_REPLACE_CONTROL) |
133 | 0 | && uc < UTF16_LEAST_PRINTABLE) { |
134 | 0 | uc = (uint16_t) UTF16_NULL_REPLACE; |
135 | 0 | changed = 1; |
136 | 0 | } |
137 | |
|
138 | 0 | if (changed) |
139 | 0 | *((uint16_t *) (uniclean + i * 2)) = |
140 | 0 | tsk_getu16(fs->endian, (uint8_t *) & uc); |
141 | 0 | } |
142 | | |
143 | | // convert to UTF-8 |
144 | 0 | memset(asc, 0, alen); |
145 | |
|
146 | 0 | ptr8 = (UTF8 *) asc; |
147 | 0 | ptr16 = (UTF16 *) uniclean; |
148 | 0 | r = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &ptr16, |
149 | 0 | (const UTF16 *) (&uniclean[ulen * 2]), &ptr8, |
150 | 0 | (UTF8 *) & asc[alen], TSKstrictConversion); |
151 | |
|
152 | 0 | free(uniclean); |
153 | 0 | if (r != TSKconversionOK) { |
154 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNICODE); |
155 | 0 | tsk_error_set_errstr |
156 | 0 | ("hfs_UTF16toUTF8: unicode conversion failed (%d)", (int) r); |
157 | 0 | return 1; |
158 | 0 | } |
159 | | |
160 | 0 | return 0; |
161 | 0 | } |
162 | | |
163 | | static TSK_FS_NAME_TYPE_ENUM |
164 | | hfsmode2tsknametype(uint16_t a_mode) |
165 | 0 | { |
166 | 0 | switch (a_mode & HFS_IN_IFMT) { |
167 | 0 | case HFS_IN_IFIFO: |
168 | 0 | return TSK_FS_NAME_TYPE_FIFO; |
169 | 0 | case HFS_IN_IFCHR: |
170 | 0 | return TSK_FS_NAME_TYPE_CHR; |
171 | 0 | case HFS_IN_IFDIR: |
172 | 0 | return TSK_FS_NAME_TYPE_DIR; |
173 | 0 | case HFS_IN_IFBLK: |
174 | 0 | return TSK_FS_NAME_TYPE_BLK; |
175 | 0 | case HFS_IN_IFREG: |
176 | 0 | return TSK_FS_NAME_TYPE_REG; |
177 | 0 | case HFS_IN_IFLNK: |
178 | 0 | return TSK_FS_NAME_TYPE_LNK; |
179 | 0 | case HFS_IN_IFSOCK: |
180 | 0 | return TSK_FS_NAME_TYPE_SOCK; |
181 | 0 | case HFS_IFWHT: |
182 | 0 | return TSK_FS_NAME_TYPE_WHT; |
183 | 0 | case HFS_IFXATTR: |
184 | 0 | return TSK_FS_NAME_TYPE_UNDEF; |
185 | 0 | default: |
186 | | /* error */ |
187 | 0 | return TSK_FS_NAME_TYPE_UNDEF; |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | |
192 | | // used to pass data to the callback |
193 | | typedef struct { |
194 | | TSK_FS_DIR *fs_dir; |
195 | | TSK_FS_NAME *fs_name; |
196 | | uint32_t cnid; |
197 | | } HFS_DIR_OPEN_META_INFO; |
198 | | |
199 | | static uint8_t |
200 | | hfs_dir_open_meta_cb(HFS_INFO * hfs, int8_t level_type, |
201 | | const hfs_btree_key_cat * cur_key, int cur_keylen, size_t nodesize, |
202 | | TSK_OFF_T key_off, void *ptr) |
203 | 0 | { |
204 | 0 | HFS_DIR_OPEN_META_INFO *info = (HFS_DIR_OPEN_META_INFO *) ptr; |
205 | 0 | TSK_FS_INFO *fs = &hfs->fs_info; |
206 | |
|
207 | 0 | if (tsk_verbose) |
208 | 0 | fprintf(stderr, |
209 | 0 | "hfs_dir_open_meta_cb: want %" PRIu32 " vs got %" PRIu32 |
210 | 0 | " (%s node)\n", info->cnid, tsk_getu32(hfs->fs_info.endian, |
211 | 0 | cur_key->parent_cnid), |
212 | 0 | (level_type == HFS_BT_NODE_TYPE_IDX) ? "Index" : "Leaf"); |
213 | |
|
214 | 0 | if (level_type == HFS_BT_NODE_TYPE_IDX) { |
215 | 0 | if (tsk_getu32(hfs->fs_info.endian, |
216 | 0 | cur_key->parent_cnid) < info->cnid) { |
217 | 0 | return HFS_BTREE_CB_IDX_LT; |
218 | 0 | } |
219 | 0 | else { |
220 | 0 | return HFS_BTREE_CB_IDX_EQGT; |
221 | 0 | } |
222 | 0 | } |
223 | 0 | else { |
224 | 0 | uint8_t *rec_buf = (uint8_t *) cur_key; |
225 | 0 | uint16_t rec_type; |
226 | 0 | size_t rec_off2; |
227 | |
|
228 | 0 | if (tsk_getu32(hfs->fs_info.endian, |
229 | 0 | cur_key->parent_cnid) < info->cnid) { |
230 | 0 | return HFS_BTREE_CB_LEAF_GO; |
231 | 0 | } |
232 | 0 | else if (tsk_getu32(hfs->fs_info.endian, |
233 | 0 | cur_key->parent_cnid) > info->cnid) { |
234 | 0 | return HFS_BTREE_CB_LEAF_STOP; |
235 | 0 | } |
236 | | // Need at least 2 bytes for key_len |
237 | 0 | if (cur_keylen < 2) { |
238 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
239 | 0 | tsk_error_set_errstr("hfs_dir_open_meta: cur_keylen value out of bounds"); |
240 | 0 | return HFS_BTREE_CB_ERR; |
241 | 0 | } |
242 | 0 | rec_off2 = 2 + tsk_getu16(hfs->fs_info.endian, cur_key->key_len); |
243 | |
|
244 | 0 | if ((nodesize < 2) || (rec_off2 >= nodesize - 2)) { |
245 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
246 | 0 | tsk_error_set_errstr("hfs_dir_open_meta: nodesize value out of bounds"); |
247 | 0 | return HFS_BTREE_CB_ERR; |
248 | 0 | } |
249 | 0 | rec_type = tsk_getu16(hfs->fs_info.endian, &rec_buf[rec_off2]); |
250 | | |
251 | | // Catalog entry is for a file |
252 | 0 | if (rec_type == HFS_FILE_THREAD) { |
253 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
254 | 0 | tsk_error_set_errstr("hfs_dir_open_meta: Entry" |
255 | 0 | " is a file, not a folder"); |
256 | 0 | return HFS_BTREE_CB_ERR; |
257 | 0 | } |
258 | | |
259 | | /* This will link the folder to its parent, which is the ".." entry */ |
260 | 0 | else if (rec_type == HFS_FOLDER_THREAD) { |
261 | 0 | if ((nodesize < sizeof(hfs_thread)) || (rec_off2 > nodesize - sizeof(hfs_thread))) { |
262 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
263 | 0 | tsk_error_set_errstr("hfs_dir_open_meta: nodesize value out of bounds"); |
264 | 0 | return HFS_BTREE_CB_ERR; |
265 | 0 | } |
266 | 0 | hfs_thread *thread = (hfs_thread *) & rec_buf[rec_off2]; |
267 | 0 | strcpy(info->fs_name->name, ".."); |
268 | 0 | info->fs_name->meta_addr = |
269 | 0 | tsk_getu32(hfs->fs_info.endian, thread->parent_cnid); |
270 | 0 | info->fs_name->type = TSK_FS_NAME_TYPE_DIR; |
271 | 0 | info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
272 | 0 | } |
273 | | |
274 | | /* This is a folder in the folder */ |
275 | 0 | else if (rec_type == HFS_FOLDER_RECORD) { |
276 | 0 | if ((nodesize < sizeof(hfs_folder)) || (rec_off2 > nodesize - sizeof(hfs_folder))) { |
277 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
278 | 0 | tsk_error_set_errstr("hfs_dir_open_meta: nodesize value out of bounds"); |
279 | 0 | return HFS_BTREE_CB_ERR; |
280 | 0 | } |
281 | 0 | hfs_folder *folder = (hfs_folder *) & rec_buf[rec_off2]; |
282 | |
|
283 | 0 | info->fs_name->meta_addr = |
284 | 0 | tsk_getu32(hfs->fs_info.endian, folder->std.cnid); |
285 | 0 | info->fs_name->type = TSK_FS_NAME_TYPE_DIR; |
286 | 0 | info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
287 | | |
288 | | // Make sure there is enough space in cur_key for the name |
289 | | // (name is unicode so each characters is two bytes; 6 bytes |
290 | | // of non-name characters) |
291 | 0 | const int32_t nameLength = |
292 | 0 | tsk_getu16(hfs->fs_info.endian, cur_key->name.length); |
293 | |
|
294 | 0 | if (2*nameLength > tsk_getu16(hfs->fs_info.endian, cur_key->key_len) - 6) { |
295 | 0 | error_returned |
296 | 0 | ("hfs_dir_open_meta_cb: name length is too long"); |
297 | 0 | return HFS_BTREE_CB_ERR; |
298 | 0 | } |
299 | 0 | if (hfs_UTF16toUTF8(fs, (uint8_t *) cur_key->name.unicode, |
300 | 0 | nameLength, info->fs_name->name, HFS_MAXNAMLEN + 1, |
301 | 0 | HFS_U16U8_FLAG_REPLACE_SLASH)) { |
302 | 0 | return HFS_BTREE_CB_ERR; |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | /* This is a normal file in the folder */ |
307 | 0 | else if (rec_type == HFS_FILE_RECORD) { |
308 | 0 | if ((nodesize < sizeof(hfs_file)) || (rec_off2 > nodesize - sizeof(hfs_file))) { |
309 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
310 | 0 | tsk_error_set_errstr("hfs_dir_open_meta: nodesize value out of bounds"); |
311 | 0 | return HFS_BTREE_CB_ERR; |
312 | 0 | } |
313 | 0 | hfs_file *file = (hfs_file *) & rec_buf[rec_off2]; |
314 | | // This could be a hard link. We need to test this CNID, and follow it if necessary. |
315 | 0 | unsigned char is_err; |
316 | 0 | TSK_INUM_T file_cnid = |
317 | 0 | tsk_getu32(hfs->fs_info.endian, file->std.cnid); |
318 | 0 | TSK_INUM_T target_cnid = |
319 | 0 | hfs_follow_hard_link(hfs, file, &is_err); |
320 | 0 | if (is_err > 1) { |
321 | 0 | error_returned |
322 | 0 | ("hfs_dir_open_meta_cb: trying to follow a possible hard link in the directory"); |
323 | 0 | return HFS_BTREE_CB_ERR; |
324 | 0 | } |
325 | 0 | if (target_cnid != file_cnid) { |
326 | 0 | HFS_ENTRY entry; |
327 | 0 | uint8_t lkup; // lookup result |
328 | | |
329 | | // This is a hard link. We need to fill in the name->type and name->meta_addr from the target |
330 | 0 | info->fs_name->meta_addr = target_cnid; |
331 | | // get the Catalog entry for the target CNID |
332 | |
|
333 | 0 | lkup = hfs_cat_file_lookup(hfs, target_cnid, &entry, |
334 | 0 | FALSE); |
335 | 0 | if (lkup != 0) { |
336 | 0 | error_returned |
337 | 0 | ("hfs_dir_open_meta_cb: retrieving the catalog entry for the target of a hard link"); |
338 | 0 | return HFS_BTREE_CB_ERR; |
339 | 0 | } |
340 | 0 | info->fs_name->type = |
341 | 0 | hfsmode2tsknametype(tsk_getu16(hfs->fs_info.endian, |
342 | 0 | entry.cat.std.perm.mode)); |
343 | 0 | } |
344 | 0 | else { |
345 | | // This is NOT a hard link. |
346 | 0 | info->fs_name->meta_addr = |
347 | 0 | tsk_getu32(hfs->fs_info.endian, file->std.cnid); |
348 | 0 | info->fs_name->type = |
349 | 0 | hfsmode2tsknametype(tsk_getu16(hfs->fs_info.endian, |
350 | 0 | file->std.perm.mode)); |
351 | 0 | } |
352 | 0 | info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
353 | | |
354 | | // Make sure there is enough space in cur_key for the name |
355 | | // (name is unicode so each characters is two bytes; 6 bytes |
356 | | // of non-name characters) |
357 | 0 | const int32_t nameLength = |
358 | 0 | tsk_getu16(hfs->fs_info.endian, cur_key->name.length); |
359 | 0 | if (2*nameLength > tsk_getu16(hfs->fs_info.endian, cur_key->key_len) - 6) { |
360 | 0 | error_returned |
361 | 0 | ("hfs_dir_open_meta_cb: name length is too long"); |
362 | 0 | return HFS_BTREE_CB_ERR; |
363 | 0 | } |
364 | 0 | if (hfs_UTF16toUTF8(fs, (uint8_t *) cur_key->name.unicode, |
365 | 0 | nameLength, info->fs_name->name, HFS_MAXNAMLEN + 1, |
366 | 0 | HFS_U16U8_FLAG_REPLACE_SLASH)) { |
367 | 0 | return HFS_BTREE_CB_ERR; |
368 | 0 | } |
369 | 0 | } |
370 | 0 | else { |
371 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
372 | | // @@@ MAY NEED TO IMPROVE BELOW MESSAGE |
373 | 0 | tsk_error_set_errstr |
374 | 0 | ("hfs_dir_open_meta: Unknown record type %d in leaf node", |
375 | 0 | rec_type); |
376 | 0 | return HFS_BTREE_CB_ERR; |
377 | 0 | } |
378 | | |
379 | 0 | if (tsk_fs_dir_add(info->fs_dir, info->fs_name)) { |
380 | 0 | return HFS_BTREE_CB_ERR; |
381 | 0 | } |
382 | 0 | return HFS_BTREE_CB_LEAF_GO; |
383 | 0 | } |
384 | 0 | } |
385 | | |
386 | | /** \internal |
387 | | * Process a directory and load up FS_DIR with the entries. If a pointer to |
388 | | * an already allocated FS_DIR structure is given, it will be cleared. If no existing |
389 | | * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return |
390 | | * value is error or corruption, then the FS_DIR structure could |
391 | | * have entries (depending on when the error occurred). |
392 | | * |
393 | | * @param a_fs File system to analyze |
394 | | * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated |
395 | | * structure or a new structure. |
396 | | * @param a_addr Address of directory to process. |
397 | | * @param recursion_depth Recursion depth to limit the number of self-calls |
398 | | * @returns error, corruption, ok etc. |
399 | | */ |
400 | | TSK_RETVAL_ENUM |
401 | | hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, |
402 | | TSK_INUM_T a_addr, int recursion_depth) |
403 | 0 | { |
404 | 0 | HFS_INFO *hfs = (HFS_INFO *) fs; |
405 | 0 | uint32_t cnid; /* catalog node ID of the entry (= inum) */ |
406 | 0 | TSK_FS_DIR *fs_dir; |
407 | 0 | TSK_FS_NAME *fs_name; |
408 | 0 | HFS_DIR_OPEN_META_INFO info; |
409 | | |
410 | |
|
411 | 0 | tsk_error_reset(); |
412 | |
|
413 | 0 | cnid = (uint32_t) a_addr; |
414 | |
|
415 | 0 | if (tsk_verbose) |
416 | 0 | fprintf(stderr, |
417 | 0 | "hfs_dir_open_meta: called for directory %" PRIu32 "\n", cnid); |
418 | |
|
419 | 0 | if (a_addr < fs->first_inum || a_addr > fs->last_inum) { |
420 | 0 | tsk_error_reset(); |
421 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
422 | 0 | tsk_error_set_errstr("hfs_dir_open_meta: Invalid inode value: %" |
423 | 0 | PRIuINUM, a_addr); |
424 | 0 | return TSK_ERR; |
425 | 0 | } |
426 | 0 | else if (a_fs_dir == NULL) { |
427 | 0 | tsk_error_reset(); |
428 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
429 | 0 | tsk_error_set_errstr |
430 | 0 | ("hfs_dir_open_meta: NULL fs_dir argument given"); |
431 | 0 | return TSK_ERR; |
432 | 0 | } |
433 | | |
434 | 0 | if (tsk_verbose) |
435 | 0 | tsk_fprintf(stderr, |
436 | 0 | "hfs_dir_open_meta: Processing directory %" PRIuINUM "\n", |
437 | 0 | a_addr); |
438 | |
|
439 | 0 | fs_dir = *a_fs_dir; |
440 | 0 | if (fs_dir) { |
441 | 0 | tsk_fs_dir_reset(fs_dir); |
442 | 0 | fs_dir->addr = a_addr; |
443 | 0 | } |
444 | 0 | else if ((*a_fs_dir = fs_dir = |
445 | 0 | tsk_fs_dir_alloc(fs, a_addr, 128)) == NULL) { |
446 | 0 | return TSK_ERR; |
447 | 0 | } |
448 | | |
449 | 0 | if ((fs_name = tsk_fs_name_alloc(HFS_MAXNAMLEN + 1, 0)) == NULL) { |
450 | 0 | return TSK_ERR; |
451 | 0 | } |
452 | 0 | info.fs_dir = fs_dir; |
453 | 0 | info.fs_name = fs_name; |
454 | |
|
455 | 0 | if ((fs_dir->fs_file = |
456 | 0 | tsk_fs_file_open_meta(fs, NULL, a_addr)) == NULL) { |
457 | 0 | tsk_error_errstr2_concat(" - hfs_dir_open_meta"); |
458 | 0 | tsk_fs_name_free(fs_name); |
459 | 0 | return TSK_ERR; |
460 | 0 | } |
461 | | |
462 | | // if we are listing the root directory, add the Orphan directory and special HFS file entries |
463 | 0 | if (a_addr == fs->root_inum) { |
464 | 0 | int i; |
465 | 0 | for (i = 0; i < 6; i++) { |
466 | 0 | switch (i) { |
467 | 0 | case 0: |
468 | 0 | if (!hfs->has_extents_file) |
469 | 0 | continue; |
470 | 0 | strncpy(fs_name->name, HFS_EXTENTS_FILE_NAME, |
471 | 0 | fs_name->name_size); |
472 | 0 | fs_name->meta_addr = HFS_EXTENTS_FILE_ID; |
473 | 0 | break; |
474 | 0 | case 1: |
475 | 0 | strncpy(fs_name->name, HFS_CATALOG_FILE_NAME, |
476 | 0 | fs_name->name_size); |
477 | 0 | fs_name->meta_addr = HFS_CATALOG_FILE_ID; |
478 | 0 | break; |
479 | 0 | case 2: |
480 | | // Note: the Extents file and the BadBlocks file are really the same. |
481 | 0 | if (!hfs->has_extents_file) |
482 | 0 | continue; |
483 | 0 | strncpy(fs_name->name, HFS_BAD_BLOCK_FILE_NAME, |
484 | 0 | fs_name->name_size); |
485 | 0 | fs_name->meta_addr = HFS_BAD_BLOCK_FILE_ID; |
486 | 0 | break; |
487 | 0 | case 3: |
488 | 0 | strncpy(fs_name->name, HFS_ALLOCATION_FILE_NAME, |
489 | 0 | fs_name->name_size); |
490 | 0 | fs_name->meta_addr = HFS_ALLOCATION_FILE_ID; |
491 | 0 | break; |
492 | 0 | case 4: |
493 | 0 | if (!hfs->has_startup_file) |
494 | 0 | continue; |
495 | 0 | strncpy(fs_name->name, HFS_STARTUP_FILE_NAME, |
496 | 0 | fs_name->name_size); |
497 | 0 | fs_name->meta_addr = HFS_STARTUP_FILE_ID; |
498 | 0 | break; |
499 | 0 | case 5: |
500 | 0 | if (!hfs->has_attributes_file) |
501 | 0 | continue; |
502 | 0 | strncpy(fs_name->name, HFS_ATTRIBUTES_FILE_NAME, |
503 | 0 | fs_name->name_size); |
504 | 0 | fs_name->meta_addr = HFS_ATTRIBUTES_FILE_ID; |
505 | 0 | break; |
506 | | /* |
507 | | case 6: |
508 | | strncpy(fs_name->name, HFS_REPAIR_CATALOG_FILE_NAME, fs_name->name_size); |
509 | | fs_name->meta_addr = HFS_REPAIR_CATALOG_FILE_ID; |
510 | | break; |
511 | | case 7: |
512 | | strncpy(fs_name->name, HFS_BOGUS_EXTENT_FILE_NAME, fs_name->name_size); |
513 | | fs_name->meta_addr = HFS_BOGUS_EXTENT_FILE_ID; |
514 | | break; |
515 | | */ |
516 | 0 | } |
517 | 0 | fs_name->type = TSK_FS_NAME_TYPE_REG; |
518 | 0 | fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
519 | 0 | if (tsk_fs_dir_add(fs_dir, fs_name)) { |
520 | 0 | tsk_fs_name_free(fs_name); |
521 | 0 | return TSK_ERR; |
522 | 0 | } |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | 0 | info.cnid = cnid; |
527 | 0 | if (hfs_cat_traverse(hfs, hfs_dir_open_meta_cb, &info)) { |
528 | 0 | tsk_fs_name_free(fs_name); |
529 | 0 | return TSK_ERR; |
530 | 0 | } |
531 | | |
532 | 0 | tsk_fs_name_free(fs_name); |
533 | 0 | return TSK_OK; |
534 | 0 | } |
535 | | |
536 | | int |
537 | | hfs_name_cmp(TSK_FS_INFO * a_fs_info, const char *s1, const char *s2) |
538 | 0 | { |
539 | 0 | HFS_INFO *hfs = (HFS_INFO *) a_fs_info; |
540 | 0 | if (hfs->is_case_sensitive) |
541 | 0 | return strcmp(s1, s2); |
542 | 0 | else |
543 | 0 | return strcasecmp(s1, s2); |
544 | 0 | } |