/src/sleuthkit/tsk/fs/hfs_dent.cpp
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 | 28.0k | { |
103 | 28.0k | UTF8 *ptr8; |
104 | 28.0k | uint8_t *uniclean; |
105 | 28.0k | UTF16 *ptr16; |
106 | 28.0k | int i; |
107 | 28.0k | TSKConversionResult r; |
108 | | |
109 | | // remove nulls from the Unicode string |
110 | | // convert / to : |
111 | 28.0k | uniclean = (uint8_t *) tsk_malloc(ulen * 2); |
112 | 28.0k | if (!uniclean) |
113 | 0 | return 1; |
114 | | |
115 | 28.0k | memcpy(uniclean, uni, ulen * 2); |
116 | | |
117 | 646k | for (i = 0; i < ulen; ++i) { |
118 | 618k | uint16_t uc = tsk_getu16(fs->endian, uniclean + i * 2); |
119 | | |
120 | | |
121 | 618k | int changed = 0; |
122 | 618k | if (uc == UTF16_NULL) { |
123 | 114k | uc = UTF16_NULL_REPLACE; |
124 | 114k | changed = 1; |
125 | 114k | } |
126 | 503k | else if ((flags & HFS_U16U8_FLAG_REPLACE_SLASH) |
127 | 503k | && uc == UTF16_SLASH) { |
128 | 690 | uc = UTF16_COLON; |
129 | 690 | changed = 1; |
130 | 690 | } |
131 | | |
132 | 502k | else if ((flags & HFS_U16U8_FLAG_REPLACE_CONTROL) |
133 | 502k | && uc < UTF16_LEAST_PRINTABLE) { |
134 | 0 | uc = (uint16_t) UTF16_NULL_REPLACE; |
135 | 0 | changed = 1; |
136 | 0 | } |
137 | | |
138 | 618k | if (changed) |
139 | 115k | *((uint16_t *) (uniclean + i * 2)) = |
140 | 115k | tsk_getu16(fs->endian, (uint8_t *) & uc); |
141 | 618k | } |
142 | | |
143 | | // convert to UTF-8 |
144 | 28.0k | memset(asc, 0, alen); |
145 | | |
146 | 28.0k | ptr8 = (UTF8 *) asc; |
147 | 28.0k | ptr16 = (UTF16 *) uniclean; |
148 | 28.0k | r = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &ptr16, |
149 | 28.0k | (const UTF16 *) (&uniclean[ulen * 2]), &ptr8, |
150 | 28.0k | (UTF8 *) & asc[alen], TSKstrictConversion); |
151 | | |
152 | 28.0k | free(uniclean); |
153 | 28.0k | if (r != TSKconversionOK) { |
154 | 218 | tsk_error_set_errno(TSK_ERR_FS_UNICODE); |
155 | 218 | tsk_error_set_errstr |
156 | 218 | ("hfs_UTF16toUTF8: unicode conversion failed (%d)", (int) r); |
157 | 218 | return 1; |
158 | 218 | } |
159 | | |
160 | 27.7k | return 0; |
161 | 28.0k | } |
162 | | |
163 | | static TSK_FS_NAME_TYPE_ENUM |
164 | | hfsmode2tsknametype(uint16_t a_mode) |
165 | 9.26k | { |
166 | 9.26k | switch (a_mode & HFS_IN_IFMT) { |
167 | 19 | case HFS_IN_IFIFO: |
168 | 19 | return TSK_FS_NAME_TYPE_FIFO; |
169 | 54 | case HFS_IN_IFCHR: |
170 | 54 | return TSK_FS_NAME_TYPE_CHR; |
171 | 157 | case HFS_IN_IFDIR: |
172 | 157 | return TSK_FS_NAME_TYPE_DIR; |
173 | 41 | case HFS_IN_IFBLK: |
174 | 41 | return TSK_FS_NAME_TYPE_BLK; |
175 | 1.87k | case HFS_IN_IFREG: |
176 | 1.87k | return TSK_FS_NAME_TYPE_REG; |
177 | 1.38k | case HFS_IN_IFLNK: |
178 | 1.38k | return TSK_FS_NAME_TYPE_LNK; |
179 | 42 | case HFS_IN_IFSOCK: |
180 | 42 | return TSK_FS_NAME_TYPE_SOCK; |
181 | 81 | case HFS_IFWHT: |
182 | 81 | return TSK_FS_NAME_TYPE_WHT; |
183 | 0 | case HFS_IFXATTR: |
184 | 0 | return TSK_FS_NAME_TYPE_UNDEF; |
185 | 5.60k | default: |
186 | | /* error */ |
187 | 5.60k | return TSK_FS_NAME_TYPE_UNDEF; |
188 | 9.26k | } |
189 | 9.26k | } |
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( |
201 | | HFS_INFO * hfs, |
202 | | int8_t level_type, |
203 | | const hfs_btree_key_cat * cur_key, |
204 | | int cur_keylen, |
205 | | size_t nodesize, |
206 | | [[maybe_unused]] TSK_OFF_T key_off, |
207 | | void *ptr) |
208 | 99.8k | { |
209 | 99.8k | HFS_DIR_OPEN_META_INFO *info = (HFS_DIR_OPEN_META_INFO *) ptr; |
210 | 99.8k | TSK_FS_INFO *fs = &hfs->fs_info; |
211 | | |
212 | 99.8k | if (tsk_verbose) |
213 | 0 | fprintf(stderr, |
214 | 0 | "hfs_dir_open_meta_cb: want %" PRIu32 " vs got %" PRIu32 |
215 | 0 | " (%s node)\n", info->cnid, tsk_getu32(hfs->fs_info.endian, |
216 | 0 | cur_key->parent_cnid), |
217 | 0 | (level_type == HFS_BT_NODE_TYPE_IDX) ? "Index" : "Leaf"); |
218 | | |
219 | 99.8k | if (level_type == HFS_BT_NODE_TYPE_IDX) { |
220 | 9.84k | if (tsk_getu32(hfs->fs_info.endian, |
221 | 9.84k | cur_key->parent_cnid) < info->cnid) { |
222 | 8.06k | return HFS_BTREE_CB_IDX_LT; |
223 | 8.06k | } |
224 | 1.78k | else { |
225 | 1.78k | return HFS_BTREE_CB_IDX_EQGT; |
226 | 1.78k | } |
227 | 9.84k | } |
228 | 89.9k | else { |
229 | 89.9k | uint8_t *rec_buf = (uint8_t *) cur_key; |
230 | 89.9k | uint16_t rec_type; |
231 | 89.9k | size_t rec_off2; |
232 | | |
233 | 89.9k | if (tsk_getu32(hfs->fs_info.endian, |
234 | 89.9k | cur_key->parent_cnid) < info->cnid) { |
235 | 54.2k | return HFS_BTREE_CB_LEAF_GO; |
236 | 54.2k | } |
237 | 35.6k | else if (tsk_getu32(hfs->fs_info.endian, |
238 | 35.6k | cur_key->parent_cnid) > info->cnid) { |
239 | 5.63k | return HFS_BTREE_CB_LEAF_STOP; |
240 | 5.63k | } |
241 | | // Need at least 2 bytes for key_len |
242 | 30.0k | if (cur_keylen < 2) { |
243 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
244 | 0 | tsk_error_set_errstr("hfs_dir_open_meta: cur_keylen value out of bounds"); |
245 | 0 | return HFS_BTREE_CB_ERR; |
246 | 0 | } |
247 | 30.0k | rec_off2 = 2 + tsk_getu16(hfs->fs_info.endian, cur_key->key_len); |
248 | | |
249 | 30.0k | if ((nodesize < 2) || (rec_off2 >= nodesize - 2)) { |
250 | 12 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
251 | 12 | tsk_error_set_errstr("hfs_dir_open_meta: nodesize value out of bounds"); |
252 | 12 | return HFS_BTREE_CB_ERR; |
253 | 12 | } |
254 | 30.0k | rec_type = tsk_getu16(hfs->fs_info.endian, &rec_buf[rec_off2]); |
255 | | |
256 | | // Catalog entry is for a file |
257 | 30.0k | if (rec_type == HFS_FILE_THREAD) { |
258 | 912 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
259 | 912 | tsk_error_set_errstr("hfs_dir_open_meta: Entry" |
260 | 912 | " is a file, not a folder"); |
261 | 912 | return HFS_BTREE_CB_ERR; |
262 | 912 | } |
263 | | |
264 | | /* This will link the folder to its parent, which is the ".." entry */ |
265 | 29.1k | else if (rec_type == HFS_FOLDER_THREAD) { |
266 | 7.24k | hfs_thread *thread = (hfs_thread *) & rec_buf[rec_off2]; |
267 | | |
268 | | // hfs_thread is of variable size on disk. The minimum size is |
269 | | // 10 bytes (8 for the non-name fields, 2 for the length of an |
270 | | // empty name). |
271 | 7.24k | const size_t min_hfs_thread_size = sizeof(hfs_thread) - sizeof(hfs_uni_str) + 2; |
272 | | |
273 | | // First, check that we can read as far as the name length; then |
274 | | // get the name length and check that the whole record fits into |
275 | | // the buffer. |
276 | 7.24k | if (rec_off2 > nodesize - min_hfs_thread_size || |
277 | 7.24k | rec_off2 > nodesize - (min_hfs_thread_size + tsk_getu16(hfs->fs_info.endian, thread->name.length))) { |
278 | 12 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
279 | 12 | tsk_error_set_errstr("hfs_dir_open_meta: nodesize value out of bounds"); |
280 | 12 | return HFS_BTREE_CB_ERR; |
281 | 12 | } |
282 | | |
283 | 7.23k | strcpy(info->fs_name->name, ".."); |
284 | 7.23k | info->fs_name->meta_addr = |
285 | 7.23k | tsk_getu32(hfs->fs_info.endian, thread->parent_cnid); |
286 | 7.23k | info->fs_name->type = TSK_FS_NAME_TYPE_DIR; |
287 | 7.23k | info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
288 | 7.23k | } |
289 | | |
290 | | /* This is a folder in the folder */ |
291 | 21.8k | else if (rec_type == HFS_FOLDER_RECORD) { |
292 | 12.4k | if ((nodesize < sizeof(hfs_folder)) || (rec_off2 > nodesize - sizeof(hfs_folder))) { |
293 | 21 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
294 | 21 | tsk_error_set_errstr("hfs_dir_open_meta: nodesize value out of bounds"); |
295 | 21 | return HFS_BTREE_CB_ERR; |
296 | 21 | } |
297 | 12.3k | hfs_folder *folder = (hfs_folder *) & rec_buf[rec_off2]; |
298 | | |
299 | 12.3k | info->fs_name->meta_addr = |
300 | 12.3k | tsk_getu32(hfs->fs_info.endian, folder->std.cnid); |
301 | 12.3k | info->fs_name->type = TSK_FS_NAME_TYPE_DIR; |
302 | 12.3k | info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
303 | | |
304 | | // Make sure there is enough space in cur_key for the name |
305 | | // (name is unicode so each characters is two bytes; 6 bytes |
306 | | // of non-name characters) |
307 | 12.3k | const int32_t nameLength = |
308 | 12.3k | tsk_getu16(hfs->fs_info.endian, cur_key->name.length); |
309 | | |
310 | 12.3k | if (2*nameLength > tsk_getu16(hfs->fs_info.endian, cur_key->key_len) - 6) { |
311 | 119 | error_returned |
312 | 119 | ("hfs_dir_open_meta_cb: name length is too long"); |
313 | 119 | return HFS_BTREE_CB_ERR; |
314 | 119 | } |
315 | 12.2k | if (hfs_UTF16toUTF8(fs, (uint8_t *) cur_key->name.unicode, |
316 | 12.2k | nameLength, info->fs_name->name, HFS_MAXNAMLEN + 1, |
317 | 12.2k | HFS_U16U8_FLAG_REPLACE_SLASH)) { |
318 | 59 | return HFS_BTREE_CB_ERR; |
319 | 59 | } |
320 | 12.2k | } |
321 | | |
322 | | /* This is a normal file in the folder */ |
323 | 9.49k | else if (rec_type == HFS_FILE_RECORD) { |
324 | 9.28k | if ((nodesize < sizeof(hfs_file)) || (rec_off2 > nodesize - sizeof(hfs_file))) { |
325 | 21 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
326 | 21 | tsk_error_set_errstr("hfs_dir_open_meta: nodesize value out of bounds"); |
327 | 21 | return HFS_BTREE_CB_ERR; |
328 | 21 | } |
329 | 9.26k | hfs_file *file = (hfs_file *) & rec_buf[rec_off2]; |
330 | | // This could be a hard link. We need to test this CNID, and follow it if necessary. |
331 | 9.26k | unsigned char is_err; |
332 | 9.26k | TSK_INUM_T file_cnid = |
333 | 9.26k | tsk_getu32(hfs->fs_info.endian, file->std.cnid); |
334 | 9.26k | TSK_INUM_T target_cnid = |
335 | 9.26k | hfs_follow_hard_link(hfs, file, &is_err); |
336 | 9.26k | if (is_err > 1) { |
337 | 0 | error_returned |
338 | 0 | ("hfs_dir_open_meta_cb: trying to follow a possible hard link in the directory"); |
339 | 0 | return HFS_BTREE_CB_ERR; |
340 | 0 | } |
341 | 9.26k | if (target_cnid != file_cnid) { |
342 | 0 | HFS_ENTRY entry; |
343 | 0 | uint8_t lkup; // lookup result |
344 | | |
345 | | // This is a hard link. We need to fill in the name->type and name->meta_addr from the target |
346 | 0 | info->fs_name->meta_addr = target_cnid; |
347 | | // get the Catalog entry for the target CNID |
348 | |
|
349 | 0 | lkup = hfs_cat_file_lookup(hfs, target_cnid, &entry, |
350 | 0 | FALSE); |
351 | 0 | if (lkup != 0) { |
352 | 0 | error_returned |
353 | 0 | ("hfs_dir_open_meta_cb: retrieving the catalog entry for the target of a hard link"); |
354 | 0 | return HFS_BTREE_CB_ERR; |
355 | 0 | } |
356 | 0 | info->fs_name->type = |
357 | 0 | hfsmode2tsknametype(tsk_getu16(hfs->fs_info.endian, |
358 | 0 | entry.cat.std.perm.mode)); |
359 | 0 | } |
360 | 9.26k | else { |
361 | | // This is NOT a hard link. |
362 | 9.26k | info->fs_name->meta_addr = |
363 | 9.26k | tsk_getu32(hfs->fs_info.endian, file->std.cnid); |
364 | 9.26k | info->fs_name->type = |
365 | 9.26k | hfsmode2tsknametype(tsk_getu16(hfs->fs_info.endian, |
366 | 9.26k | file->std.perm.mode)); |
367 | 9.26k | } |
368 | 9.26k | info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
369 | | |
370 | | // Make sure there is enough space in cur_key for the name |
371 | | // (name is unicode so each characters is two bytes; 6 bytes |
372 | | // of non-name characters) |
373 | 9.26k | const int32_t nameLength = |
374 | 9.26k | tsk_getu16(hfs->fs_info.endian, cur_key->name.length); |
375 | 9.26k | if (2*nameLength > tsk_getu16(hfs->fs_info.endian, cur_key->key_len) - 6) { |
376 | 90 | error_returned |
377 | 90 | ("hfs_dir_open_meta_cb: name length is too long"); |
378 | 90 | return HFS_BTREE_CB_ERR; |
379 | 90 | } |
380 | 9.17k | if (hfs_UTF16toUTF8(fs, (uint8_t *) cur_key->name.unicode, |
381 | 9.17k | nameLength, info->fs_name->name, HFS_MAXNAMLEN + 1, |
382 | 9.17k | HFS_U16U8_FLAG_REPLACE_SLASH)) { |
383 | 65 | return HFS_BTREE_CB_ERR; |
384 | 65 | } |
385 | 9.17k | } |
386 | 210 | else { |
387 | 210 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
388 | | // @@@ MAY NEED TO IMPROVE BELOW MESSAGE |
389 | 210 | tsk_error_set_errstr |
390 | 210 | ("hfs_dir_open_meta: Unknown record type %d in leaf node", |
391 | 210 | rec_type); |
392 | 210 | return HFS_BTREE_CB_ERR; |
393 | 210 | } |
394 | | |
395 | 28.5k | if (tsk_fs_dir_add(info->fs_dir, info->fs_name)) { |
396 | 0 | return HFS_BTREE_CB_ERR; |
397 | 0 | } |
398 | 28.5k | return HFS_BTREE_CB_LEAF_GO; |
399 | 28.5k | } |
400 | 99.8k | } |
401 | | |
402 | | /** \internal |
403 | | * Process a directory and load up FS_DIR with the entries. If a pointer to |
404 | | * an already allocated FS_DIR structure is given, it will be cleared. If no existing |
405 | | * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return |
406 | | * value is error or corruption, then the FS_DIR structure could |
407 | | * have entries (depending on when the error occurred). |
408 | | * |
409 | | * @param a_fs File system to analyze |
410 | | * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated |
411 | | * structure or a new structure. |
412 | | * @param a_addr Address of directory to process. |
413 | | * @param recursion_depth Recursion depth to limit the number of self-calls |
414 | | * @returns error, corruption, ok etc. |
415 | | */ |
416 | | TSK_RETVAL_ENUM |
417 | | hfs_dir_open_meta( |
418 | | TSK_FS_INFO * fs, |
419 | | TSK_FS_DIR ** a_fs_dir, |
420 | | TSK_INUM_T a_addr, |
421 | | [[maybe_unused]] int recursion_depth) |
422 | 12.3k | { |
423 | 12.3k | HFS_INFO *hfs = (HFS_INFO *) fs; |
424 | 12.3k | uint32_t cnid; /* catalog node ID of the entry (= inum) */ |
425 | 12.3k | TSK_FS_DIR *fs_dir; |
426 | 12.3k | TSK_FS_NAME *fs_name; |
427 | 12.3k | HFS_DIR_OPEN_META_INFO info; |
428 | | |
429 | | |
430 | 12.3k | tsk_error_reset(); |
431 | | |
432 | 12.3k | cnid = (uint32_t) a_addr; |
433 | | |
434 | 12.3k | if (tsk_verbose) |
435 | 0 | fprintf(stderr, |
436 | 0 | "hfs_dir_open_meta: called for directory %" PRIu32 "\n", cnid); |
437 | | |
438 | 12.3k | if (a_addr < fs->first_inum || a_addr > fs->last_inum) { |
439 | 951 | tsk_error_reset(); |
440 | 951 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
441 | 951 | tsk_error_set_errstr("hfs_dir_open_meta: Invalid inode value: %" |
442 | 951 | PRIuINUM, a_addr); |
443 | 951 | return TSK_ERR; |
444 | 951 | } |
445 | 11.4k | else if (a_fs_dir == NULL) { |
446 | 0 | tsk_error_reset(); |
447 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
448 | 0 | tsk_error_set_errstr |
449 | 0 | ("hfs_dir_open_meta: NULL fs_dir argument given"); |
450 | 0 | return TSK_ERR; |
451 | 0 | } |
452 | | |
453 | 11.4k | if (tsk_verbose) |
454 | 0 | tsk_fprintf(stderr, |
455 | 0 | "hfs_dir_open_meta: Processing directory %" PRIuINUM "\n", |
456 | 0 | a_addr); |
457 | | |
458 | 11.4k | fs_dir = *a_fs_dir; |
459 | 11.4k | if (fs_dir) { |
460 | 0 | tsk_fs_dir_reset(fs_dir); |
461 | 0 | fs_dir->addr = a_addr; |
462 | 0 | } |
463 | 11.4k | else if ((*a_fs_dir = fs_dir = |
464 | 11.4k | tsk_fs_dir_alloc(fs, a_addr, 128)) == NULL) { |
465 | 0 | return TSK_ERR; |
466 | 0 | } |
467 | | |
468 | 11.4k | if ((fs_name = tsk_fs_name_alloc(HFS_MAXNAMLEN + 1, 0)) == NULL) { |
469 | 0 | return TSK_ERR; |
470 | 0 | } |
471 | 11.4k | info.fs_dir = fs_dir; |
472 | 11.4k | info.fs_name = fs_name; |
473 | | |
474 | 11.4k | if ((fs_dir->fs_file = |
475 | 11.4k | tsk_fs_file_open_meta(fs, NULL, a_addr)) == NULL) { |
476 | 3.21k | tsk_error_errstr2_concat(" - hfs_dir_open_meta"); |
477 | 3.21k | tsk_fs_name_free(fs_name); |
478 | 3.21k | return TSK_ERR; |
479 | 3.21k | } |
480 | | |
481 | | // if we are listing the root directory, add the Orphan directory and special HFS file entries |
482 | 8.20k | if (a_addr == fs->root_inum) { |
483 | 5.03k | int i; |
484 | 35.2k | for (i = 0; i < 6; i++) { |
485 | 30.2k | switch (i) { |
486 | 5.03k | case 0: |
487 | 5.03k | if (!hfs->has_extents_file) |
488 | 1.08k | continue; |
489 | 3.95k | strncpy(fs_name->name, HFS_EXTENTS_FILE_NAME, |
490 | 3.95k | fs_name->name_size); |
491 | 3.95k | fs_name->meta_addr = HFS_EXTENTS_FILE_ID; |
492 | 3.95k | break; |
493 | 5.03k | case 1: |
494 | 5.03k | strncpy(fs_name->name, HFS_CATALOG_FILE_NAME, |
495 | 5.03k | fs_name->name_size); |
496 | 5.03k | fs_name->meta_addr = HFS_CATALOG_FILE_ID; |
497 | 5.03k | break; |
498 | 5.03k | case 2: |
499 | | // Note: the Extents file and the BadBlocks file are really the same. |
500 | 5.03k | if (!hfs->has_extents_file) |
501 | 1.08k | continue; |
502 | 3.95k | strncpy(fs_name->name, HFS_BAD_BLOCK_FILE_NAME, |
503 | 3.95k | fs_name->name_size); |
504 | 3.95k | fs_name->meta_addr = HFS_BAD_BLOCK_FILE_ID; |
505 | 3.95k | break; |
506 | 5.03k | case 3: |
507 | 5.03k | strncpy(fs_name->name, HFS_ALLOCATION_FILE_NAME, |
508 | 5.03k | fs_name->name_size); |
509 | 5.03k | fs_name->meta_addr = HFS_ALLOCATION_FILE_ID; |
510 | 5.03k | break; |
511 | 5.03k | case 4: |
512 | 5.03k | if (!hfs->has_startup_file) |
513 | 1.43k | continue; |
514 | 3.60k | strncpy(fs_name->name, HFS_STARTUP_FILE_NAME, |
515 | 3.60k | fs_name->name_size); |
516 | 3.60k | fs_name->meta_addr = HFS_STARTUP_FILE_ID; |
517 | 3.60k | break; |
518 | 5.03k | case 5: |
519 | 5.03k | if (!hfs->has_attributes_file) |
520 | 881 | continue; |
521 | 4.15k | strncpy(fs_name->name, HFS_ATTRIBUTES_FILE_NAME, |
522 | 4.15k | fs_name->name_size); |
523 | 4.15k | fs_name->meta_addr = HFS_ATTRIBUTES_FILE_ID; |
524 | 4.15k | break; |
525 | | /* |
526 | | case 6: |
527 | | strncpy(fs_name->name, HFS_REPAIR_CATALOG_FILE_NAME, fs_name->name_size); |
528 | | fs_name->meta_addr = HFS_REPAIR_CATALOG_FILE_ID; |
529 | | break; |
530 | | case 7: |
531 | | strncpy(fs_name->name, HFS_BOGUS_EXTENT_FILE_NAME, fs_name->name_size); |
532 | | fs_name->meta_addr = HFS_BOGUS_EXTENT_FILE_ID; |
533 | | break; |
534 | | */ |
535 | 30.2k | } |
536 | 25.7k | fs_name->type = TSK_FS_NAME_TYPE_REG; |
537 | 25.7k | fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
538 | 25.7k | if (tsk_fs_dir_add(fs_dir, fs_name)) { |
539 | 0 | tsk_fs_name_free(fs_name); |
540 | 0 | return TSK_ERR; |
541 | 0 | } |
542 | 25.7k | } |
543 | 5.03k | } |
544 | | |
545 | 8.20k | info.cnid = cnid; |
546 | 8.20k | if (hfs_cat_traverse(hfs, hfs_dir_open_meta_cb, &info)) { |
547 | 2.12k | tsk_fs_name_free(fs_name); |
548 | 2.12k | return TSK_ERR; |
549 | 2.12k | } |
550 | | |
551 | 6.08k | tsk_fs_name_free(fs_name); |
552 | 6.08k | return TSK_OK; |
553 | 8.20k | } |
554 | | |
555 | | int |
556 | | hfs_name_cmp(TSK_FS_INFO * a_fs_info, const char *s1, const char *s2) |
557 | 11.8k | { |
558 | 11.8k | HFS_INFO *hfs = (HFS_INFO *) a_fs_info; |
559 | 11.8k | if (hfs->is_case_sensitive) |
560 | 152 | return strcmp(s1, s2); |
561 | 11.6k | else |
562 | 11.6k | return strcasecmp(s1, s2); |
563 | 11.8k | } |