/src/sleuthkit/tsk/fs/ntfs.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** ntfs |
3 | | ** The Sleuth Kit |
4 | | ** |
5 | | ** Content and meta data layer support for the NTFS file system |
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-2005 Brian Carrier. All rights reserved |
10 | | ** |
11 | | ** TASK |
12 | | ** Copyright (c) 2002 Brian Carrier, @stake Inc. All rights reserved |
13 | | ** |
14 | | ** This software is distributed under the Common Public License 1.0 |
15 | | ** |
16 | | ** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05) |
17 | | ** |
18 | | */ |
19 | | #include "tsk_fs_i.h" |
20 | | #include "tsk_ntfs.h" |
21 | | |
22 | | #include <ctype.h> |
23 | | |
24 | | #include <memory> |
25 | | |
26 | | #include "encryptionHelper.h" |
27 | | |
28 | | /** |
29 | | * \file ntfs.c |
30 | | * Contains the TSK internal general NTFS processing code |
31 | | */ |
32 | | /* |
33 | | * NOTES TO SELF: |
34 | | * |
35 | | * - multiple ".." entries may exist |
36 | | */ |
37 | | |
38 | | /* |
39 | | * How are we to handle the META flag? Is the MFT $Data Attribute META? |
40 | | */ |
41 | | |
42 | | |
43 | | /* Macro to pass in both the epoch time value and the nano time value */ |
44 | 0 | #define WITHNANO(x) x, (unsigned int)x##_nano |
45 | | |
46 | | |
47 | | /* mini-design note: |
48 | | * The MFT has entries for every file and dir in the fs. |
49 | | * The first entry ($MFT) is for the MFT itself and it is used to find |
50 | | * the location of the entire table because it can become fragmented. |
51 | | * Therefore, the $Data attribute of $MFT is saved in the NTFS_INFO |
52 | | * structure for easy access. We also use the size of the MFT as |
53 | | * a way to calculate the maximum MFT entry number (last_inum). |
54 | | * |
55 | | * Ok, that is simple, but getting the full $Data attribute can be tough |
56 | | * because $MFT may not fit into one MFT entry (i.e. an attribute list). |
57 | | * We need to process the attribute list attribute to find out which |
58 | | * other entries to process. But, the attribute list attribute comes |
59 | | * before any $Data attribute (so it could refer to an MFT that has not |
60 | | * yet been 'defined'). Although, the $Data attribute seems to always |
61 | | * exist and define at least the run for the entry in the attribute list. |
62 | | * |
63 | | * So, the way this is solved is that generic mft_lookup is used to get |
64 | | * any MFT entry, even $MFT. If $MFT is not cached then we calculate |
65 | | * the address of where to read based on multiplication and guessing. |
66 | | * When we are loading the $MFT, we set 'loading_the_MFT' to 1 so |
67 | | * that we can update things as we go along. When we read $MFT we |
68 | | * read all the attributes and save info about the $Data one. If |
69 | | * there is an attribute list, we will have the location of the |
70 | | * additional MFT in the cached $Data location, which will be |
71 | | * updated as we process the attribute list. After each MFT |
72 | | * entry that we process while loading the MFT, the 'final_inum' |
73 | | * value is updated to reflect what we can currently load so |
74 | | * that the sanity checks still work. |
75 | | */ |
76 | | |
77 | | |
78 | | /********************************************************************** |
79 | | * |
80 | | * MISC FUNCS |
81 | | * |
82 | | **********************************************************************/ |
83 | | |
84 | | /* convert the NT Time (UTC hundred nanoseconds from 1/1/1601) |
85 | | * to UNIX (UTC seconds from 1/1/1970) |
86 | | * |
87 | | * The basic calculation is to remove the nanoseconds and then |
88 | | * subtract the number of seconds between 1601 and 1970 |
89 | | * i.e. TIME - DELTA |
90 | | * |
91 | | * Returns 0 if NT date is outside of Unix range |
92 | | * |
93 | | */ |
94 | | uint32_t |
95 | | nt2unixtime(uint64_t ntdate) |
96 | 0 | { |
97 | | // (369*365 + 89) * 24 * 3600 * 10000000 |
98 | 0 | #define NSEC_BTWN_1601_1970 (uint64_t)(116444736000000000ULL) |
99 | | |
100 | | // return 0 if before 1970 |
101 | 0 | if (ntdate < NSEC_BTWN_1601_1970) |
102 | 0 | return 0; |
103 | | |
104 | 0 | ntdate -= (uint64_t) NSEC_BTWN_1601_1970; |
105 | 0 | ntdate /= (uint64_t) 10000000; |
106 | | |
107 | | // return if beyond 32-bit epoch range |
108 | 0 | if (ntdate > 0xffffffffULL) |
109 | 0 | return 0; |
110 | | |
111 | 0 | return (uint32_t) ntdate; |
112 | 0 | } |
113 | | |
114 | | /* convert the NT Time (UTC hundred nanoseconds from 1/1/1601) |
115 | | * to only the nanoseconds |
116 | | * |
117 | | */ |
118 | | uint32_t |
119 | | nt2nano(uint64_t ntdate) |
120 | 0 | { |
121 | 0 | return (uint32_t) (ntdate % 10000000)*100; |
122 | 0 | } |
123 | | |
124 | | |
125 | | /********************************************************************** |
126 | | * |
127 | | * Lookup Functions |
128 | | * |
129 | | **********************************************************************/ |
130 | | |
131 | | |
132 | | |
133 | | |
134 | | /** |
135 | | * Read an MFT entry and save it in raw form in the given buffer. |
136 | | * NOTE: This will remove the update sequence integrity checks in the |
137 | | * structure. |
138 | | * |
139 | | * @param a_ntfs File system to read from |
140 | | * @param a_buf Buffer to save raw data to. Must be of size NTFS_INFO.mft_rsize_b |
141 | | * @param a_mftnum Address of MFT entry to read |
142 | | * @param mft_start_addr Within-file-system byte address of start of MFT entry |
143 | | * |
144 | | * @returns Error value |
145 | | */ |
146 | | TSK_RETVAL_ENUM |
147 | | ntfs_dinode_lookup(NTFS_INFO * a_ntfs, char *a_buf, TSK_INUM_T a_mftnum, TSK_OFF_T * mft_start_addr) |
148 | 0 | { |
149 | 0 | TSK_OFF_T mftaddr_b, mftaddr2_b, offset; |
150 | 0 | size_t mftaddr_len = 0; |
151 | 0 | int i; |
152 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_ntfs->fs_info; |
153 | 0 | TSK_FS_ATTR_RUN *data_run; |
154 | 0 | ntfs_upd *upd; |
155 | 0 | uint16_t sig_seq; |
156 | 0 | ntfs_mft *mft; |
157 | | |
158 | | |
159 | | /* sanity checks */ |
160 | 0 | if (!a_buf) { |
161 | 0 | tsk_error_reset(); |
162 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
163 | 0 | tsk_error_set_errstr("mft_lookup: null mft buffer"); |
164 | 0 | return TSK_ERR; |
165 | 0 | } |
166 | | |
167 | 0 | if (a_mftnum < fs->first_inum) { |
168 | 0 | tsk_error_reset(); |
169 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
170 | 0 | tsk_error_set_errstr("mft_lookup: inode number is too small (%" |
171 | 0 | PRIuINUM ")", a_mftnum); |
172 | 0 | return TSK_ERR; |
173 | 0 | } |
174 | | |
175 | | /* Because this code reads teh actual MFT, we need to make sure we |
176 | | * decrement the last_inum because the last value is a special value |
177 | | * for the ORPHANS directory */ |
178 | 0 | if (a_mftnum > fs->last_inum - 1) { |
179 | 0 | tsk_error_reset(); |
180 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
181 | 0 | tsk_error_set_errstr("mft_lookup: inode number is too large (%" |
182 | 0 | PRIuINUM ")", a_mftnum); |
183 | 0 | return TSK_ERR; |
184 | 0 | } |
185 | | |
186 | | |
187 | 0 | if (tsk_verbose) |
188 | 0 | tsk_fprintf(stderr, |
189 | 0 | "ntfs_dinode_lookup: Processing MFT %" PRIuINUM "\n", |
190 | 0 | a_mftnum); |
191 | | |
192 | | /* If mft_data (the cached $Data attribute of $MFT) is not there yet, |
193 | | * then we have not started to load $MFT yet. In that case, we will |
194 | | * 'cheat' and calculate where it goes. This should only be for |
195 | | * $MFT itself, in which case the calculation is easy |
196 | | */ |
197 | 0 | if (!a_ntfs->mft_data) { |
198 | | |
199 | | /* This is just a random check with the assumption being that |
200 | | * we don't want to just do a guess calculation for a very large |
201 | | * MFT entry |
202 | | */ |
203 | 0 | if (a_mftnum > NTFS_LAST_DEFAULT_INO) { |
204 | 0 | tsk_error_reset(); |
205 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
206 | 0 | tsk_error_set_errstr |
207 | 0 | ("Error trying to load a high MFT entry when the MFT itself has not been loaded (%" |
208 | 0 | PRIuINUM ")", a_mftnum); |
209 | 0 | return TSK_ERR; |
210 | 0 | } |
211 | | |
212 | 0 | mftaddr_b = a_ntfs->root_mft_addr + a_mftnum * a_ntfs->mft_rsize_b; |
213 | 0 | mftaddr2_b = 0; |
214 | 0 | } |
215 | 0 | else { |
216 | | /* The MFT may not be in consecutive clusters, so we need to use its |
217 | | * data attribute run list to find out what address to read |
218 | | * |
219 | | * This is why we cached it |
220 | | */ |
221 | | |
222 | | // will be set to the address of the MFT entry |
223 | 0 | mftaddr_b = mftaddr2_b = 0; |
224 | | |
225 | | /* The byte offset within the $Data stream */ |
226 | 0 | offset = a_mftnum * a_ntfs->mft_rsize_b; |
227 | | |
228 | | /* NOTE: data_run values are in clusters |
229 | | * |
230 | | * cycle through the runs in $Data and identify which |
231 | | * has the MFT entry that we want |
232 | | */ |
233 | 0 | for (data_run = a_ntfs->mft_data->nrd.run; |
234 | 0 | data_run != NULL; data_run = data_run->next) { |
235 | | |
236 | | /* Test for possible overflows / error conditions */ |
237 | 0 | if ((offset < 0) || (data_run->len >= (TSK_DADDR_T)(LLONG_MAX / a_ntfs->csize_b))){ |
238 | 0 | tsk_error_reset(); |
239 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
240 | 0 | tsk_error_set_errstr |
241 | 0 | ("ntfs_dinode_lookup: Overflow when calculating run length"); |
242 | 0 | return TSK_COR; |
243 | 0 | } |
244 | | |
245 | | /* The length of this specific run */ |
246 | 0 | TSK_OFF_T run_len = data_run->len * a_ntfs->csize_b; |
247 | | |
248 | | /* Is our MFT entry is in this run somewhere ? */ |
249 | 0 | if (offset < run_len) { |
250 | |
|
251 | 0 | if (tsk_verbose) |
252 | 0 | tsk_fprintf(stderr, |
253 | 0 | "ntfs_dinode_lookup: Found in offset: %" |
254 | 0 | PRIuDADDR " size: %" PRIuDADDR " at offset: %" |
255 | 0 | PRIdOFF "\n", data_run->addr, data_run->len, |
256 | 0 | offset); |
257 | | |
258 | | /* special case where the MFT entry crosses |
259 | | * a run (only happens when cluster size is 512-bytes |
260 | | * and there are an odd number of clusters in the run) |
261 | | */ |
262 | 0 | if (run_len < offset + a_ntfs->mft_rsize_b) { |
263 | |
|
264 | 0 | if (tsk_verbose) |
265 | 0 | tsk_fprintf(stderr, |
266 | 0 | "ntfs_dinode_lookup: Entry crosses run border\n"); |
267 | |
|
268 | 0 | if (data_run->next == NULL) { |
269 | 0 | tsk_error_reset(); |
270 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
271 | 0 | tsk_error_set_errstr |
272 | 0 | ("mft_lookup: MFT entry crosses a cluster and there are no more clusters!"); |
273 | 0 | return TSK_COR; |
274 | 0 | } |
275 | | |
276 | | /* Assign address where the remainder of the entry is */ |
277 | 0 | mftaddr2_b = data_run->next->addr * a_ntfs->csize_b; |
278 | | /* this should always be 512, but just in case */ |
279 | 0 | mftaddr_len = (size_t) (run_len - offset); |
280 | 0 | } |
281 | | |
282 | | /* Assign address of where the MFT entry starts */ |
283 | 0 | mftaddr_b = data_run->addr * a_ntfs->csize_b + offset; |
284 | 0 | if (tsk_verbose) |
285 | 0 | tsk_fprintf(stderr, |
286 | 0 | "ntfs_dinode_lookup: Entry address at: %" |
287 | 0 | PRIdOFF "\n", mftaddr_b); |
288 | 0 | break; |
289 | 0 | } |
290 | | |
291 | | /* decrement the offset we are looking for */ |
292 | 0 | offset -= run_len; |
293 | 0 | } |
294 | | |
295 | | /* Did we find it? */ |
296 | 0 | if (!mftaddr_b) { |
297 | 0 | tsk_error_reset(); |
298 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); |
299 | 0 | tsk_error_set_errstr("mft_lookup: Error finding MFT entry %" |
300 | 0 | PRIuINUM " in $MFT", a_mftnum); |
301 | 0 | return TSK_ERR; |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | | |
306 | | /* can we do just one read or do we need multiple? */ |
307 | 0 | if (mftaddr2_b) { |
308 | 0 | ssize_t cnt; |
309 | | /* read the first part into mft */ |
310 | 0 | cnt = tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, a_buf, mftaddr_len); |
311 | 0 | if (cnt != (ssize_t)mftaddr_len) { |
312 | 0 | if (cnt >= 0) { |
313 | 0 | tsk_error_reset(); |
314 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
315 | 0 | } |
316 | 0 | tsk_error_set_errstr2 |
317 | 0 | ("ntfs_dinode_lookup: Error reading MFT Entry (part 1) at %" |
318 | 0 | PRIdOFF, mftaddr_b); |
319 | 0 | return TSK_ERR; |
320 | 0 | } |
321 | | |
322 | | /* read the second part into mft */ |
323 | 0 | cnt = tsk_fs_read |
324 | 0 | (&a_ntfs->fs_info, mftaddr2_b, |
325 | 0 | (char *) ((uintptr_t) a_buf + (uintptr_t) mftaddr_len), |
326 | 0 | a_ntfs->mft_rsize_b - mftaddr_len); |
327 | 0 | if (cnt != (ssize_t)(a_ntfs->mft_rsize_b - mftaddr_len)) { |
328 | 0 | if (cnt >= 0) { |
329 | 0 | tsk_error_reset(); |
330 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
331 | 0 | } |
332 | 0 | tsk_error_set_errstr2 |
333 | 0 | ("ntfs_dinode_lookup: Error reading MFT Entry (part 2) at %" |
334 | 0 | PRIdOFF, mftaddr2_b); |
335 | 0 | return TSK_ERR; |
336 | 0 | } |
337 | 0 | } |
338 | 0 | else { |
339 | 0 | ssize_t cnt; |
340 | | /* read the raw entry into mft */ |
341 | 0 | cnt = |
342 | 0 | tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, a_buf, |
343 | 0 | a_ntfs->mft_rsize_b); |
344 | 0 | if (cnt != a_ntfs->mft_rsize_b) { |
345 | 0 | if (cnt >= 0) { |
346 | 0 | tsk_error_reset(); |
347 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
348 | 0 | } |
349 | 0 | tsk_error_set_errstr2 |
350 | 0 | ("ntfs_dinode_lookup: Error reading MFT Entry at %" |
351 | 0 | PRIdOFF, mftaddr_b); |
352 | 0 | return TSK_ERR; |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | | /* A nonzero address means that mftaddr_b has been requested for export */ |
357 | 0 | if (mft_start_addr) { |
358 | 0 | *mft_start_addr = mftaddr_b; |
359 | 0 | } |
360 | | |
361 | | /* Sanity Check */ |
362 | | #if 0 |
363 | | /* This is no longer applied because it caused too many problems |
364 | | * with images that had 0 and 1 etc. as values. Testing shows that |
365 | | * even Windows XP doesn't care if entries have an invalid entry, so |
366 | | * this is no longer checked. The update sequence check should find |
367 | | * corrupt entries |
368 | | * */ |
369 | | if ((tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC) |
370 | | && (tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC_BAAD) |
371 | | && (tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC_ZERO)) { |
372 | | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
373 | | tsk_error_set_errstr("entry %d has an invalid MFT magic: %x", |
374 | | mftnum, tsk_getu32(fs->endian, mft->magic)); |
375 | | return 1; |
376 | | } |
377 | | #endif |
378 | | /* The MFT entries have error and integrity checks in them |
379 | | * called update sequences. They must be checked and removed |
380 | | * so that later functions can process the data as normal. |
381 | | * They are located in the last 2 bytes of each 512-bytes of data. |
382 | | * |
383 | | * We first verify that the the 2-byte value is a give value and |
384 | | * then replace it with what should be there |
385 | | */ |
386 | | /* sanity check so we don't run over in the next loop */ |
387 | 0 | mft = (ntfs_mft *) a_buf; |
388 | 0 | if ((tsk_getu16(fs->endian, mft->upd_cnt) > 0) && |
389 | 0 | (((uint32_t) (tsk_getu16(fs->endian, |
390 | 0 | mft->upd_cnt) - 1) * NTFS_UPDATE_SEQ_STRIDE) > |
391 | 0 | a_ntfs->mft_rsize_b)) { |
392 | 0 | tsk_error_reset(); |
393 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
394 | 0 | tsk_error_set_errstr |
395 | 0 | ("dinode_lookup: More Update Sequence Entries than MFT size"); |
396 | 0 | return TSK_COR; |
397 | 0 | } |
398 | 0 | uint16_t upd_cnt = tsk_getu16(fs->endian, mft->upd_cnt); |
399 | 0 | uint16_t upd_off = tsk_getu16(fs->endian, mft->upd_off); |
400 | | |
401 | | // Make sure upd_cnt > 0 to prevent an integer wrap around. |
402 | | // NOTE: There is a bug here because upd_cnt can be for unused entries. |
403 | | // They are now skipped (as of July 2021). We shoudl refactor this code |
404 | | // to allow upd_cnt = 0. |
405 | 0 | if ((upd_cnt == 0) || (upd_cnt > (((a_ntfs->mft_rsize_b) / 2) + 1))) { |
406 | 0 | tsk_error_reset(); |
407 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
408 | 0 | tsk_error_set_errstr |
409 | 0 | ("dinode_lookup: Invalid update count value out of bounds"); |
410 | 0 | return TSK_COR; |
411 | 0 | } |
412 | 0 | size_t mft_rsize_b = ((size_t) upd_cnt - 1) * 2; |
413 | |
|
414 | 0 | if ((size_t) upd_off + sizeof(ntfs_upd) > (a_ntfs->mft_rsize_b - mft_rsize_b)) { |
415 | 0 | tsk_error_reset(); |
416 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
417 | 0 | tsk_error_set_errstr |
418 | 0 | ("dinode_lookup: Update sequence would read past MFT size"); |
419 | 0 | return TSK_COR; |
420 | 0 | } |
421 | | |
422 | | /* Apply the update sequence structure template */ |
423 | | |
424 | 0 | upd = (ntfs_upd *) ((uintptr_t) a_buf + upd_off); |
425 | | /* Get the sequence value that each 16-bit value should be */ |
426 | 0 | sig_seq = tsk_getu16(fs->endian, upd->upd_val); |
427 | | /* cycle through each sector */ |
428 | 0 | for (i = 1; i < tsk_getu16(fs->endian, mft->upd_cnt); i++) { |
429 | 0 | uint8_t *new_val, *old_val; |
430 | | /* The offset into the buffer of the value to analyze */ |
431 | 0 | size_t offset = i * NTFS_UPDATE_SEQ_STRIDE - 2; |
432 | | |
433 | | /* Check that there is room in the buffer to read the current sequence value */ |
434 | 0 | if (offset + 2 > a_ntfs->mft_rsize_b) { |
435 | 0 | tsk_error_reset(); |
436 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
437 | 0 | tsk_error_set_errstr |
438 | 0 | ("dinode_lookup: Ran out of data while parsing update sequence values"); |
439 | 0 | return TSK_COR; |
440 | 0 | } |
441 | | |
442 | | /* get the current sequence value */ |
443 | 0 | uint16_t cur_seq = |
444 | 0 | tsk_getu16(fs->endian, (uintptr_t) a_buf + offset); |
445 | 0 | if (cur_seq != sig_seq) { |
446 | | /* get the replacement value */ |
447 | 0 | uint16_t cur_repl = |
448 | 0 | tsk_getu16(fs->endian, &upd->upd_seq + (i - 1) * 2); |
449 | 0 | tsk_error_reset(); |
450 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
451 | |
|
452 | 0 | tsk_error_set_errstr |
453 | 0 | ("Incorrect update sequence value in MFT entry\nSignature Value: 0x%" |
454 | 0 | PRIx16 " Actual Value: 0x%" PRIx16 |
455 | 0 | " Replacement Value: 0x%" PRIx16 |
456 | 0 | "\nThis is typically because of a corrupted entry", |
457 | 0 | sig_seq, cur_seq, cur_repl); |
458 | 0 | return TSK_COR; |
459 | 0 | } |
460 | | |
461 | 0 | new_val = &upd->upd_seq + (i - 1) * 2; |
462 | 0 | old_val = (uint8_t *) ((uintptr_t) a_buf + offset); |
463 | | /* |
464 | | if (tsk_verbose) |
465 | | tsk_fprintf(stderr, |
466 | | "ntfs_dinode_lookup: upd_seq %i Replacing: %.4" |
467 | | PRIx16 " With: %.4" PRIx16 "\n", i, |
468 | | tsk_getu16(fs->endian, old_val), tsk_getu16(fs->endian, |
469 | | new_val)); |
470 | | */ |
471 | 0 | *old_val++ = *new_val++; |
472 | 0 | *old_val = *new_val; |
473 | 0 | } |
474 | | |
475 | 0 | return TSK_OK; |
476 | 0 | } |
477 | | |
478 | | |
479 | | |
480 | | /* |
481 | | * given a cluster, return the allocation status or |
482 | | * -1 if an error occurs |
483 | | */ |
484 | | static int |
485 | | is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr) |
486 | 0 | { |
487 | 0 | int bits_p_clust, b; |
488 | 0 | TSK_DADDR_T base; |
489 | 0 | int8_t ret; |
490 | 0 | bits_p_clust = 8 * ntfs->fs_info.block_size; |
491 | | |
492 | | /* While we are loading the MFT, assume that everything |
493 | | * is allocated. This should only be needed when we are |
494 | | * dealing with an attribute list ... |
495 | | */ |
496 | 0 | if (ntfs->loading_the_MFT == 1) { |
497 | 0 | return 1; |
498 | 0 | } |
499 | 0 | else if (ntfs->bmap == NULL) { |
500 | 0 | tsk_error_reset(); |
501 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
502 | |
|
503 | 0 | tsk_error_set_errstr("is_clustalloc: Bitmap pointer is null: %" |
504 | 0 | PRIuDADDR "\n", addr); |
505 | 0 | return -1; |
506 | 0 | } |
507 | | |
508 | | /* Is the cluster too big? */ |
509 | 0 | if (addr > ntfs->fs_info.last_block) { |
510 | 0 | tsk_error_reset(); |
511 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
512 | 0 | tsk_error_set_errstr("is_clustalloc: cluster too large"); |
513 | 0 | return -1; |
514 | 0 | } |
515 | | |
516 | | /* identify the base cluster in the bitmap file */ |
517 | 0 | base = addr / bits_p_clust; |
518 | 0 | b = (int) (addr % bits_p_clust); |
519 | |
|
520 | 0 | tsk_take_lock(&ntfs->lock); |
521 | | |
522 | | /* is this the same as in the cached buffer? */ |
523 | 0 | if (base != ntfs->bmap_buf_off) { |
524 | 0 | TSK_DADDR_T c = base; |
525 | 0 | TSK_FS_ATTR_RUN *run; |
526 | 0 | TSK_DADDR_T fsaddr = 0; |
527 | 0 | ssize_t cnt; |
528 | | |
529 | | /* get the file system address of the bitmap cluster */ |
530 | 0 | for (run = ntfs->bmap; run; run = run->next) { |
531 | 0 | if (run->len <= c) { |
532 | 0 | c -= run->len; |
533 | 0 | } |
534 | 0 | else { |
535 | 0 | fsaddr = run->addr + c; |
536 | 0 | break; |
537 | 0 | } |
538 | 0 | } |
539 | |
|
540 | 0 | if (fsaddr == 0) { |
541 | 0 | tsk_release_lock(&ntfs->lock); |
542 | 0 | tsk_error_reset(); |
543 | 0 | tsk_error_set_errno(TSK_ERR_FS_BLK_NUM); |
544 | 0 | tsk_error_set_errstr |
545 | 0 | ("is_clustalloc: cluster not found in bitmap: %" PRIuDADDR |
546 | 0 | "", c); |
547 | 0 | return -1; |
548 | 0 | } |
549 | 0 | if (fsaddr > ntfs->fs_info.last_block) { |
550 | 0 | tsk_release_lock(&ntfs->lock); |
551 | 0 | tsk_error_reset(); |
552 | 0 | tsk_error_set_errno(TSK_ERR_FS_BLK_NUM); |
553 | 0 | tsk_error_set_errstr |
554 | 0 | ("is_clustalloc: Cluster in bitmap too large for image: %" |
555 | 0 | PRIuDADDR, fsaddr); |
556 | 0 | return -1; |
557 | 0 | } |
558 | 0 | ntfs->bmap_buf_off = base; |
559 | 0 | cnt = tsk_fs_read_block |
560 | 0 | (&ntfs->fs_info, fsaddr, ntfs->bmap_buf, |
561 | 0 | ntfs->fs_info.block_size); |
562 | 0 | if (cnt != ntfs->fs_info.block_size) { |
563 | 0 | tsk_release_lock(&ntfs->lock); |
564 | 0 | if (cnt >= 0) { |
565 | 0 | tsk_error_reset(); |
566 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
567 | 0 | } |
568 | 0 | tsk_error_set_errstr2 |
569 | 0 | ("is_clustalloc: Error reading bitmap at %" PRIuDADDR, |
570 | 0 | fsaddr); |
571 | 0 | return -1; |
572 | 0 | } |
573 | 0 | } |
574 | | |
575 | | /* identify if the cluster is allocated or not */ |
576 | 0 | ret = (isset(ntfs->bmap_buf, b)) ? 1 : 0; |
577 | |
|
578 | 0 | tsk_release_lock(&ntfs->lock); |
579 | 0 | return ret; |
580 | 0 | } |
581 | | |
582 | | |
583 | | |
584 | | /********************************************************************** |
585 | | * |
586 | | * TSK_FS_ATTR functions |
587 | | * |
588 | | **********************************************************************/ |
589 | | |
590 | | |
591 | | /** |
592 | | * Process a non-resident runlist and convert its contents into the generic fs_attr_run |
593 | | * structure. |
594 | | * @param ntfs File system that attribute is located in. |
595 | | * @param start_vcn The starting VCN for this run. |
596 | | * @param runlist The raw runlist data from the MFT entry. |
597 | | * @param runlist_size The size of the raw runlist data from the MFT entry. |
598 | | * @param a_data_run_head [out] Pointer to pointer of run that is created. (NULL on error and for $BadClust - special case because it is a sparse file for the entire FS). |
599 | | * @param totlen [out] Pointer to location where total length of run (in bytes) can be returned (or NULL) |
600 | | * @param mnum MFT entry address |
601 | | * |
602 | | * @returns Return status of error, corrupt, or OK (note a_data_run can be NULL even when OK is returned if $BadClust is encountered) |
603 | | */ |
604 | | static TSK_RETVAL_ENUM |
605 | | ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn, |
606 | | ntfs_runlist * runlist_head, uint32_t runlist_size, TSK_FS_ATTR_RUN ** a_data_run_head, |
607 | | TSK_OFF_T * totlen, TSK_INUM_T mnum) |
608 | 0 | { |
609 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) ntfs; |
610 | 0 | ntfs_runlist *run; |
611 | 0 | TSK_FS_ATTR_RUN *data_run, *data_run_prev = NULL; |
612 | 0 | unsigned int i, idx; |
613 | 0 | TSK_DADDR_T prev_addr = 0; |
614 | 0 | TSK_OFF_T file_offset = start_vcn; |
615 | 0 | uint32_t runlist_offset = 0; |
616 | |
|
617 | 0 | run = runlist_head; |
618 | 0 | *a_data_run_head = NULL; |
619 | | |
620 | | /* initialize if non-NULL */ |
621 | 0 | if (totlen) |
622 | 0 | *totlen = 0; |
623 | |
|
624 | 0 | if (runlist_size < 1) { |
625 | 0 | return TSK_ERR; |
626 | 0 | } |
627 | | |
628 | | /* Cycle through each run in the runlist |
629 | | * We go until we find an entry with no length |
630 | | * An entry with offset of 0 is for a sparse run |
631 | | */ |
632 | 0 | while ((runlist_offset < runlist_size) && NTFS_RUNL_LENSZ(run) != 0) { |
633 | 0 | int64_t addr_offset = 0; |
634 | | |
635 | | /* allocate a new tsk_fs_attr_run */ |
636 | 0 | data_run = tsk_fs_attr_run_alloc(); |
637 | 0 | if (data_run == NULL) { |
638 | 0 | tsk_fs_attr_run_free(*a_data_run_head); |
639 | 0 | *a_data_run_head = NULL; |
640 | 0 | return TSK_ERR; |
641 | 0 | } |
642 | | |
643 | | /* make the list, unless its the first pass & then we set the head */ |
644 | 0 | if (data_run_prev) |
645 | 0 | data_run_prev->next = data_run; |
646 | 0 | else |
647 | 0 | *a_data_run_head = data_run; |
648 | 0 | data_run_prev = data_run; |
649 | | |
650 | | /* These fields are a variable number of bytes long |
651 | | * these for loops are the equivalent of the getuX macros |
652 | | */ |
653 | 0 | idx = 0; |
654 | | |
655 | | /* Get the length of this run. |
656 | | * A length of more than eight bytes will not fit in the |
657 | | * 64-bit length field (and is likely corrupt) |
658 | | */ |
659 | 0 | if (NTFS_RUNL_LENSZ(run) > 8 || NTFS_RUNL_LENSZ(run) > runlist_size - runlist_offset - 1) { |
660 | 0 | tsk_error_reset(); |
661 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
662 | 0 | tsk_error_set_errstr |
663 | 0 | ("ntfs_make_run: Run length is too large to process"); |
664 | 0 | tsk_fs_attr_run_free(*a_data_run_head); |
665 | 0 | *a_data_run_head = NULL; |
666 | 0 | return TSK_COR; |
667 | 0 | } |
668 | 0 | for (i = 0, data_run->len = 0; i < NTFS_RUNL_LENSZ(run); i++) { |
669 | 0 | data_run->len |= ((uint64_t)(run->buf[idx++]) << (i * 8)); |
670 | 0 | if (tsk_verbose) |
671 | 0 | tsk_fprintf(stderr, |
672 | 0 | "ntfs_make_data_run: Len idx: %i cur: %" |
673 | 0 | PRIu8 " (%" PRIx8 ") tot: %" PRIuDADDR |
674 | 0 | " (%" PRIxDADDR ")\n", i, |
675 | 0 | run->buf[idx - 1], run->buf[idx - 1], |
676 | 0 | data_run->len, data_run->len); |
677 | 0 | } |
678 | | |
679 | | /* Sanity check on length */ |
680 | 0 | if (data_run->len > fs->block_count) { |
681 | 0 | tsk_error_reset(); |
682 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
683 | 0 | tsk_error_set_errstr |
684 | 0 | ("ntfs_make_run: Run length is larger than file system"); |
685 | 0 | tsk_fs_attr_run_free(*a_data_run_head); |
686 | 0 | *a_data_run_head = NULL; |
687 | 0 | return TSK_COR; |
688 | 0 | } |
689 | | |
690 | 0 | data_run->offset = file_offset; |
691 | 0 | file_offset += data_run->len; |
692 | | |
693 | | /* Update the length if we were passed a value */ |
694 | 0 | if (totlen) |
695 | 0 | *totlen += (data_run->len * ntfs->csize_b); |
696 | | |
697 | | /* Get the address offset of this run. |
698 | | * An address offset of more than eight bytes will not fit in the |
699 | | * 64-bit addr_offset field (and is likely corrupt) |
700 | | */ |
701 | 0 | if (NTFS_RUNL_OFFSZ(run) > 8) { |
702 | 0 | tsk_error_reset(); |
703 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
704 | 0 | tsk_error_set_errstr |
705 | 0 | ("ntfs_make_run: Run address offset is too large to process"); |
706 | 0 | tsk_fs_attr_run_free(*a_data_run_head); |
707 | 0 | *a_data_run_head = NULL; |
708 | 0 | return TSK_COR; |
709 | 0 | } |
710 | 0 | for (i = 0, data_run->addr = 0; i < NTFS_RUNL_OFFSZ(run); i++) { |
711 | | //data_run->addr |= (run->buf[idx++] << (i * 8)); |
712 | 0 | addr_offset |= ((int64_t)(run->buf[idx++]) << (i * 8)); |
713 | 0 | if (tsk_verbose) |
714 | 0 | tsk_fprintf(stderr, |
715 | 0 | "ntfs_make_data_run: Off idx: %i cur: %" |
716 | 0 | PRIu8 " (%" PRIx8 ") tot: %" PRIuDADDR |
717 | 0 | " (%" PRIxDADDR ")\n", i, |
718 | 0 | run->buf[idx - 1], run->buf[idx - 1], addr_offset, |
719 | 0 | addr_offset); |
720 | 0 | } |
721 | | |
722 | | /* addr_offset value is signed so extend it to 64-bits */ |
723 | 0 | if ((int8_t) run->buf[idx - 1] < 0) { |
724 | 0 | for (; i < sizeof(addr_offset); i++) |
725 | 0 | addr_offset |= (int64_t) ((int64_t) 0xff << (i * 8)); |
726 | 0 | } |
727 | |
|
728 | 0 | if (tsk_verbose) |
729 | 0 | tsk_fprintf(stderr, |
730 | 0 | "ntfs_make_data_run: Signed addr_offset: %" |
731 | 0 | PRId64 " Previous address: %" |
732 | 0 | PRIuDADDR "\n", addr_offset, prev_addr); |
733 | | |
734 | | /* The NT 4.0 version of NTFS uses an offset of -1 to represent |
735 | | * a hole, so add the sparse flag and make it look like the 2K |
736 | | * version with a offset of 0 |
737 | | * |
738 | | * A user reported an issue where the $Bad file started with |
739 | | * its offset as -1 and it was not NT (maybe a conversion) |
740 | | * Change the check now to not limit to NT, but make sure |
741 | | * that it is the first run |
742 | | */ |
743 | 0 | if (((addr_offset == -1) && (prev_addr == 0)) |
744 | 0 | || ((addr_offset == -1) |
745 | 0 | && (ntfs->ver == NTFS_VINFO_NT))) { |
746 | 0 | data_run->flags = (TSK_FS_ATTR_RUN_FLAG_ENUM) (data_run->flags | TSK_FS_ATTR_RUN_FLAG_SPARSE); |
747 | 0 | data_run->addr = 0; |
748 | 0 | if (tsk_verbose) |
749 | 0 | tsk_fprintf(stderr, "ntfs_make_data_run: Sparse Run\n"); |
750 | 0 | } |
751 | | |
752 | | /* A Sparse file has a run with an offset of 0 |
753 | | * there is a special case though of the BOOT MFT entry which |
754 | | * is the super block and has a legit offset of 0. |
755 | | * |
756 | | * The value given is a delta of the previous offset, so add |
757 | | * them for non-sparse files |
758 | | * |
759 | | * For sparse files the next run will have its offset relative |
760 | | * to the current "prev_addr" so skip that code |
761 | | */ |
762 | | // @@@ BC: we'll need to pass in an inode value for this check |
763 | 0 | else if ((addr_offset) || (mnum == NTFS_MFT_BOOT)) { |
764 | |
|
765 | 0 | data_run->addr = prev_addr + addr_offset; |
766 | 0 | prev_addr = data_run->addr; |
767 | | |
768 | | /* Sanity check on length and offset */ |
769 | 0 | if (data_run->addr + data_run->len > fs->block_count) { |
770 | 0 | tsk_error_reset(); |
771 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
772 | 0 | tsk_error_set_errstr |
773 | 0 | ("ntfs_make_run: Run offset and length is larger than file system"); |
774 | 0 | tsk_fs_attr_run_free(*a_data_run_head); |
775 | 0 | *a_data_run_head = NULL; |
776 | 0 | return TSK_COR; |
777 | 0 | } |
778 | |
|
779 | 0 | } |
780 | 0 | else { |
781 | 0 | data_run->flags = (TSK_FS_ATTR_RUN_FLAG_ENUM) (data_run->flags | TSK_FS_ATTR_RUN_FLAG_SPARSE); |
782 | 0 | if (tsk_verbose) |
783 | 0 | tsk_fprintf(stderr, "ntfs_make_data_run: Sparse Run\n"); |
784 | 0 | } |
785 | | |
786 | | /* Advance run */ |
787 | 0 | uint32_t run_size = 1 + NTFS_RUNL_LENSZ(run) + NTFS_RUNL_OFFSZ(run); |
788 | 0 | run = (ntfs_runlist *) ((uintptr_t) run + run_size); |
789 | | |
790 | | // Abritrary limit runlist_offset at INT32_MAX ((1 << 31) - 1) |
791 | 0 | if (run_size > (((uint32_t) 1UL << 31 ) -1) - runlist_offset) { |
792 | 0 | return TSK_ERR; |
793 | 0 | } |
794 | 0 | runlist_offset += run_size; |
795 | 0 | } |
796 | | |
797 | | /* special case for $BADCLUST, which is a sparse file whose size is |
798 | | * the entire file system. |
799 | | * |
800 | | * If there is only one run entry and it is sparse, then there are no |
801 | | * bad blocks, so get rid of it. |
802 | | */ |
803 | 0 | if ((*a_data_run_head != NULL) |
804 | 0 | && ((*a_data_run_head)->next == NULL) |
805 | 0 | && ((*a_data_run_head)->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) |
806 | 0 | && ((*a_data_run_head)->len == fs->last_block + 1)) { |
807 | 0 | tsk_fs_attr_run_free(*a_data_run_head); |
808 | 0 | *a_data_run_head = NULL; |
809 | 0 | } |
810 | |
|
811 | 0 | return TSK_OK; |
812 | 0 | } |
813 | | |
814 | | |
815 | | |
816 | | /*********** UNCOMPRESSION CODE *************/ |
817 | | |
818 | | |
819 | | /* |
820 | | * NTFS Breaks compressed data into compression units, which are |
821 | | * typically 16 clusters in size. If the data in the comp unit |
822 | | * compresses to something smaller than 16 clusters then the |
823 | | * compressed data is stored and the rest of the compression unit |
824 | | * is filled with sparse clusters. The entire compression unit |
825 | | * can also be sparse. |
826 | | * |
827 | | * The uncompressed content in the compression unit is further broken |
828 | | * into 4k (pre-compression) blocks. When stored, each 4k block has |
829 | | * a 2-byte header that identifies the compressed size (and if there |
830 | | * was compression). |
831 | | * |
832 | | * The compressed data is a series of token groups. Each token group |
833 | | * contains a 1-byte header and 8 tokens. The 8-bits in the token |
834 | | * group header identify the type of each token in the group. |
835 | | * |
836 | | * There are two types of tokens. |
837 | | * Symbol tokens are 1 byte in length and the 1-byte value is the value |
838 | | * for that position in the file and it should be direcly copied into the |
839 | | * uncompressed data. Phrase tokens identify a previous run of data |
840 | | * in the same compression unit that should be |
841 | | * copied to the current location. These contain offset and length info. |
842 | | * |
843 | | * The attribute will have enough cluster addresses to store all of |
844 | | * the content, but the addresses will be 0 in the compression unit |
845 | | * if it is all sparse and the ending clusters will be 0 in the |
846 | | * compression unit if they are not needed. |
847 | | * |
848 | | */ |
849 | | |
850 | | /* Variables used for ntfs_uncompress() method */ |
851 | | typedef struct { |
852 | | char *uncomp_buf; // Buffer for uncompressed data |
853 | | char *comp_buf; // buffer for compressed data |
854 | | size_t comp_len; // number of bytes used in compressed data buffer |
855 | | size_t uncomp_idx; // Index into buffer for next byte |
856 | | size_t buf_size_b; // size of both buffers in bytes (1 compression unit) |
857 | | } NTFS_COMP_INFO; |
858 | | |
859 | | |
860 | | /** |
861 | | * Reset the values in the NTFS_COMP_INFO structure. We need to |
862 | | * do this in between every compression unit that we process in the file. |
863 | | * |
864 | | * @param comp Structure to reset |
865 | | */ |
866 | | static void |
867 | | ntfs_uncompress_reset(NTFS_COMP_INFO * comp) |
868 | 0 | { |
869 | 0 | memset(comp->uncomp_buf, 0, comp->buf_size_b); |
870 | 0 | comp->uncomp_idx = 0; |
871 | 0 | memset(comp->comp_buf, 0, comp->buf_size_b); |
872 | 0 | comp->comp_len = 0; |
873 | 0 | } |
874 | | |
875 | | /** |
876 | | * Setup the NTFS_COMP_INFO structure with a buffer and |
877 | | * initialize the basic settings. |
878 | | * |
879 | | * @param fs File system state information |
880 | | * @param comp Compression state information to initialize |
881 | | * @param compunit_size_c The size (in clusters) of a compression |
882 | | * unit |
883 | | * @return 1 on error and 0 on success |
884 | | */ |
885 | | static int |
886 | | ntfs_uncompress_setup(TSK_FS_INFO * fs, NTFS_COMP_INFO * comp, |
887 | | uint32_t compunit_size_c) |
888 | 0 | { |
889 | 0 | if (fs->block_size == 0 || compunit_size_c == 0) { |
890 | 0 | return 1; |
891 | 0 | } |
892 | 0 | comp->buf_size_b = fs->block_size * compunit_size_c; |
893 | | |
894 | | // Detect an integer overflow e.g. 65536 * 65536 |
895 | 0 | if (comp->buf_size_b < fs->block_size) { |
896 | 0 | return 1; |
897 | 0 | } |
898 | | |
899 | 0 | if ((comp->uncomp_buf = (char*) tsk_malloc(comp->buf_size_b)) == NULL) { |
900 | 0 | comp->buf_size_b = 0; |
901 | 0 | return 1; |
902 | 0 | } |
903 | 0 | if ((comp->comp_buf = (char*) tsk_malloc(comp->buf_size_b)) == NULL) { |
904 | 0 | free(comp->uncomp_buf); |
905 | 0 | comp->uncomp_buf = NULL; |
906 | 0 | comp->buf_size_b = 0; |
907 | 0 | return 1; |
908 | 0 | } |
909 | | |
910 | 0 | ntfs_uncompress_reset(comp); |
911 | |
|
912 | 0 | return 0; |
913 | 0 | } |
914 | | |
915 | | static void |
916 | | ntfs_uncompress_done(NTFS_COMP_INFO * comp) |
917 | 0 | { |
918 | 0 | free(comp->uncomp_buf); |
919 | 0 | comp->uncomp_buf = NULL; |
920 | 0 | free(comp->comp_buf); |
921 | 0 | comp->comp_buf = NULL; |
922 | 0 | comp->buf_size_b = 0; |
923 | 0 | } |
924 | | |
925 | | |
926 | | /** |
927 | | * Uncompress the block of data in comp->comp_buf. |
928 | | * Store the result in the comp->uncomp_buf. |
929 | | * |
930 | | * @param comp Compression unit structure |
931 | | * |
932 | | * @returns 1 on error and 0 on success |
933 | | */ |
934 | | static uint8_t |
935 | | ntfs_uncompress_compunit(NTFS_COMP_INFO * comp) |
936 | 0 | { |
937 | 0 | size_t cl_index; |
938 | 0 | uint8_t recover_data = 0; |
939 | |
|
940 | 0 | tsk_error_reset(); |
941 | |
|
942 | 0 | comp->uncomp_idx = 0; |
943 | | |
944 | | /* Cycle through the compressed data |
945 | | * We maintain state using different levels of loops. |
946 | | * We use +1 here because the size value at start of block is 2 bytes. |
947 | | */ |
948 | 0 | for (cl_index = 0; cl_index + 1 < comp->comp_len;) { |
949 | 0 | size_t blk_end; // index into the buffer to where block ends |
950 | 0 | size_t blk_size; // size of the current block |
951 | 0 | uint8_t iscomp; // set to 1 if block is compressed |
952 | 0 | size_t blk_st_uncomp; // index into uncompressed buffer where block started |
953 | 0 | uint16_t sb_header; // subblock header |
954 | |
|
955 | 0 | sb_header = tsk_getu16(TSK_LIT_ENDIAN, comp->comp_buf + cl_index); |
956 | | |
957 | | // If the sb_header isn't set, we just fill the rest of the buffer with zeros. |
958 | | // This seems to be what several different NTFS implementations do. |
959 | 0 | if (sb_header == 0) { |
960 | 0 | memset(comp->uncomp_buf + comp->uncomp_idx, 0, comp->buf_size_b - comp->uncomp_idx); |
961 | 0 | comp->uncomp_idx = comp->buf_size_b; |
962 | 0 | break; |
963 | 0 | } |
964 | | |
965 | 0 | blk_size = (sb_header & 0x0FFF) + 3; |
966 | |
|
967 | 0 | if (tsk_verbose) |
968 | 0 | tsk_fprintf(stderr, |
969 | 0 | "ntfs_uncompress_compunit: Start compression block (length=%" PRIuSIZE " index=%" PRIuSIZE |
970 | 0 | " compressed buffer size=%" PRIuSIZE ")\n", |
971 | 0 | blk_size, cl_index, comp->comp_len); |
972 | | |
973 | | // this seems to indicate end of block |
974 | 0 | if (blk_size == 3) |
975 | 0 | break; |
976 | | |
977 | 0 | blk_end = cl_index + blk_size; |
978 | 0 | if (blk_end > comp->comp_len) { |
979 | 0 | blk_end = comp->comp_len - 1; |
980 | 0 | if (tsk_verbose) |
981 | 0 | tsk_fprintf(stderr, |
982 | 0 | "WARNING: ntfs_uncompress_compunit: Compression block length longer than buffer length. Attempting to continue.\n"); |
983 | 0 | recover_data = 1; |
984 | | // return 0; // zero out the entire block |
985 | | // if we don't return 0, let the function continue to display as much decompressed data as possible |
986 | 0 | } |
987 | | |
988 | | /* The MSB identifies if the block is compressed */ |
989 | 0 | iscomp = ((sb_header & 0x8000) != 0); |
990 | | |
991 | | // keep track of where this block started in the buffer |
992 | 0 | blk_st_uncomp = comp->uncomp_idx; |
993 | 0 | cl_index += 2; |
994 | | |
995 | | // the 4096 size seems to occur at the same times as no compression |
996 | 0 | if ((iscomp) && (blk_size - 2 != 4096)) { |
997 | 0 | if (tsk_verbose) |
998 | 0 | tsk_fprintf(stderr, "ntfs_uncompress_compunit: Compression block is compressed\n"); |
999 | | |
1000 | | // cycle through the token groups in the block |
1001 | 0 | while (cl_index < blk_end) { |
1002 | 0 | int a; |
1003 | | |
1004 | | // get the header header |
1005 | 0 | unsigned char header = comp->comp_buf[cl_index]; |
1006 | 0 | cl_index++; |
1007 | |
|
1008 | 0 | if (tsk_verbose) |
1009 | 0 | tsk_fprintf(stderr, |
1010 | 0 | "ntfs_uncompress_compunit: Token Group Header: %x\n", header); |
1011 | |
|
1012 | 0 | for (a = 0; a < 8 && cl_index < blk_end; a++) { |
1013 | | |
1014 | | /* Determine token type and parse appropriately. * |
1015 | | * Symbol tokens are the symbol themselves, so copy it |
1016 | | * into the uncompressed buffer |
1017 | | */ |
1018 | 0 | if ((header & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) { |
1019 | 0 | if (tsk_verbose) |
1020 | 0 | tsk_fprintf(stderr, |
1021 | 0 | "ntfs_uncompress_compunit: Symbol Token: (offset %" |
1022 | 0 | PRIuSIZE ")\n", cl_index); |
1023 | |
|
1024 | 0 | if (comp->uncomp_idx >= comp->buf_size_b) { |
1025 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1026 | 0 | tsk_error_set_errstr |
1027 | 0 | ("ntfs_uncompress_compunit: Trying to write past end of uncompression buffer: %" |
1028 | 0 | PRIuSIZE "", comp->uncomp_idx); |
1029 | 0 | return 1; |
1030 | 0 | } |
1031 | 0 | comp->uncomp_buf[comp->uncomp_idx++] = |
1032 | 0 | comp->comp_buf[cl_index]; |
1033 | |
|
1034 | 0 | cl_index++; |
1035 | 0 | } |
1036 | | |
1037 | | /* Otherwise, it is a phrase token, which points back |
1038 | | * to a previous sequence of bytes. |
1039 | | */ |
1040 | 0 | else { |
1041 | 0 | size_t i; |
1042 | 0 | int shift; |
1043 | 0 | size_t start_position_index = 0; |
1044 | 0 | size_t end_position_index = 0; |
1045 | 0 | unsigned int offset = 0; |
1046 | 0 | unsigned int length = 0; |
1047 | 0 | uint16_t pheader; |
1048 | |
|
1049 | 0 | if (cl_index + 1 >= blk_end) { |
1050 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1051 | 0 | tsk_error_set_errstr |
1052 | 0 | ("ntfs_uncompress_compunit: Phrase token index is past end of block: %d", |
1053 | 0 | a); |
1054 | 0 | return 1; |
1055 | 0 | } |
1056 | | |
1057 | 0 | pheader = |
1058 | 0 | ((((comp->comp_buf[cl_index + |
1059 | 0 | 1]) << 8) & 0xFF00) | |
1060 | 0 | (comp->comp_buf[cl_index] & 0xFF)); |
1061 | 0 | cl_index += 2; |
1062 | | |
1063 | | |
1064 | | /* The number of bits for the start and length |
1065 | | * in the 2-byte header change depending on the |
1066 | | * location in the compression unit. This identifies |
1067 | | * how many bits each has */ |
1068 | 0 | shift = 0; |
1069 | 0 | for (i = |
1070 | 0 | comp->uncomp_idx - |
1071 | 0 | blk_st_uncomp - 1; i >= 0x10; i >>= 1) { |
1072 | 0 | shift++; |
1073 | 0 | } |
1074 | 0 | if (shift > 12) { |
1075 | 0 | tsk_error_reset(); |
1076 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1077 | 0 | tsk_error_set_errstr |
1078 | 0 | ("ntfs_uncompress_compunit: Shift is too large: %d", shift); |
1079 | 0 | return 1; |
1080 | 0 | } |
1081 | | |
1082 | | //tsk_fprintf(stderr, "Start: %X Shift: %d UnComp_IDX %d BlkStart: %lu BlkIdx: %d BlkSize: %d\n", (int)(comp->uncomp_idx - comp->blk_st - 1), shift, comp->uncomp_idx, comp->blk_st, comp->blk_idx, comp->blk_size); |
1083 | | |
1084 | 0 | offset = (pheader >> (12 - shift)) + 1; |
1085 | 0 | length = (pheader & (0xFFF >> shift)) + 2; |
1086 | |
|
1087 | 0 | start_position_index = comp->uncomp_idx - offset; |
1088 | 0 | end_position_index = start_position_index + length; |
1089 | |
|
1090 | 0 | if (tsk_verbose) |
1091 | 0 | tsk_fprintf(stderr, |
1092 | 0 | "ntfs_uncompress_compunit: Phrase Token: (offset %" |
1093 | 0 | PRIuSIZE ")\tLen: %d\tPrevOffset: %d\tHeader=%x\n", cl_index-2, |
1094 | 0 | length, offset, pheader); |
1095 | | |
1096 | | /* Sanity checks on values */ |
1097 | 0 | if (offset > comp->uncomp_idx) { |
1098 | 0 | tsk_error_reset(); |
1099 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1100 | 0 | tsk_error_set_errstr |
1101 | 0 | ("ntfs_uncompress_compunit: Phrase token offset is too large: %d (max: %" |
1102 | 0 | PRIuSIZE ")", offset, comp->uncomp_idx); |
1103 | 0 | return 1; |
1104 | 0 | } |
1105 | 0 | else if (length + start_position_index > |
1106 | 0 | comp->buf_size_b) { |
1107 | 0 | tsk_error_reset(); |
1108 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1109 | 0 | tsk_error_set_errstr |
1110 | 0 | ("ntfs_uncompress_compunit: Phrase token length is too large: %d (max: %" PRIuSIZE")", |
1111 | 0 | length, |
1112 | 0 | comp->buf_size_b - start_position_index); |
1113 | 0 | return 1; |
1114 | 0 | } |
1115 | 0 | else if (end_position_index - |
1116 | 0 | start_position_index + 1 > |
1117 | 0 | comp->buf_size_b - comp->uncomp_idx) { |
1118 | 0 | tsk_error_reset(); |
1119 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1120 | 0 | tsk_error_set_errstr |
1121 | 0 | ("ntfs_uncompress_compunit: Phrase token length is too large for rest of uncomp buf: %" PRIuSIZE" (max: %" |
1122 | 0 | PRIuSIZE ")", |
1123 | 0 | end_position_index - start_position_index + |
1124 | 0 | 1, comp->buf_size_b - comp->uncomp_idx); |
1125 | 0 | return 1; |
1126 | 0 | } |
1127 | | |
1128 | 0 | for (; |
1129 | 0 | start_position_index <= end_position_index |
1130 | 0 | && comp->uncomp_idx < comp->buf_size_b; |
1131 | 0 | start_position_index++) { |
1132 | | |
1133 | | // Copy the previous data to the current position |
1134 | 0 | comp->uncomp_buf[comp->uncomp_idx++] |
1135 | 0 | = comp->uncomp_buf[start_position_index]; |
1136 | 0 | } |
1137 | 0 | } |
1138 | 0 | header >>= 1; |
1139 | 0 | } // end of loop inside of token group |
1140 | |
|
1141 | 0 | } // end of loop inside of block |
1142 | 0 | } |
1143 | | |
1144 | | // this block contains uncompressed data |
1145 | 0 | else { |
1146 | 0 | if (tsk_verbose) |
1147 | 0 | tsk_fprintf(stderr, "ntfs_uncompress_compunit: Block size is not compressed\n"); |
1148 | |
|
1149 | 0 | while (cl_index < blk_end && cl_index < comp->comp_len) { |
1150 | | /* This seems to happen only with corrupt data -- such as |
1151 | | * when an unallocated file is being processed... */ |
1152 | 0 | if (comp->uncomp_idx >= comp->buf_size_b) { |
1153 | 0 | tsk_error_reset(); |
1154 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1155 | 0 | tsk_error_set_errstr |
1156 | 0 | ("ntfs_uncompress_compunit: Trying to write past end of uncompression buffer (1) -- corrupt data?)"); |
1157 | 0 | return 1; |
1158 | 0 | } |
1159 | | |
1160 | | // Place data in uncompression_buffer |
1161 | 0 | comp->uncomp_buf[comp->uncomp_idx++] = |
1162 | 0 | comp->comp_buf[cl_index++]; |
1163 | 0 | } |
1164 | 0 | } |
1165 | 0 | } // end of loop inside of compression unit |
1166 | | // if we are attempting to recover, we may not have decompressed an entire CU. Set uncomp_idx to the expected size. |
1167 | 0 | if (recover_data) { |
1168 | 0 | comp->uncomp_idx = comp->buf_size_b; |
1169 | 0 | } |
1170 | 0 | return 0; |
1171 | 0 | } |
1172 | | |
1173 | | |
1174 | | |
1175 | | /** |
1176 | | * Process a compression unit and return the decompressed data in a buffer in comp. |
1177 | | * |
1178 | | * @param ntfs File system |
1179 | | * @param comp Compression state info (output will be stored in here) |
1180 | | * @param comp_unit List of addresses that store compressed data |
1181 | | * @param comp_unit_size Number of addresses in comp_unit |
1182 | | * @returns 1 on error and 0 on success |
1183 | | */ |
1184 | | static uint8_t |
1185 | | ntfs_proc_compunit(NTFS_INFO * ntfs, NTFS_COMP_INFO * comp, |
1186 | | TSK_DADDR_T * comp_unit, uint32_t comp_unit_size) |
1187 | 0 | { |
1188 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) ntfs; |
1189 | 0 | int sparse; |
1190 | 0 | uint64_t a; |
1191 | | |
1192 | | /* With compressed attributes, there are three scenarios. |
1193 | | * 1: The compression unit is not compressed, |
1194 | | * 2: The compression unit is sparse |
1195 | | * 3: The compression unit is compressed |
1196 | | */ |
1197 | | |
1198 | | /* Check if the entire compression unit is sparse */ |
1199 | 0 | sparse = 1; |
1200 | 0 | for (a = 0; a < comp_unit_size && sparse == 1; a++) { |
1201 | 0 | if (comp_unit[a]) { |
1202 | 0 | sparse = 0; |
1203 | 0 | break; |
1204 | 0 | } |
1205 | 0 | } |
1206 | | |
1207 | | /* Entire comp unit is sparse... */ |
1208 | 0 | if (sparse) { |
1209 | 0 | if (tsk_verbose) |
1210 | 0 | tsk_fprintf(stderr, |
1211 | 0 | "ntfs_proc_compunit: Unit is fully sparse\n"); |
1212 | |
|
1213 | 0 | memset(comp->uncomp_buf, 0, comp->buf_size_b); |
1214 | 0 | comp->uncomp_idx = comp->buf_size_b; |
1215 | 0 | } |
1216 | | |
1217 | | /* Check if the end of the unit is sparse, which means the |
1218 | | * unit is compressed */ |
1219 | 0 | else if (comp_unit[comp_unit_size - 1] == 0) { |
1220 | |
|
1221 | 0 | if (tsk_verbose) |
1222 | 0 | tsk_fprintf(stderr, |
1223 | 0 | "ntfs_proc_compunit: Unit is compressed\n"); |
1224 | | |
1225 | | // load up the compressed buffer so we can decompress it |
1226 | 0 | ntfs_uncompress_reset(comp); |
1227 | 0 | for (a = 0; a < comp_unit_size; a++) { |
1228 | 0 | ssize_t cnt; |
1229 | |
|
1230 | 0 | if (comp_unit[a] == 0) |
1231 | 0 | break; |
1232 | | |
1233 | | /* To get the uncompressed size, we must uncompress the |
1234 | | * data -- even if addresses are only needed */ |
1235 | 0 | cnt = |
1236 | 0 | tsk_fs_read_block(fs, comp_unit[a], |
1237 | 0 | &comp->comp_buf[comp->comp_len], fs->block_size); |
1238 | 0 | if (cnt != fs->block_size) { |
1239 | 0 | if (cnt >= 0) { |
1240 | 0 | tsk_error_reset(); |
1241 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1242 | 0 | } |
1243 | 0 | tsk_error_set_errstr2 |
1244 | 0 | ("ntfs_proc_compunit: Error reading block at %" |
1245 | 0 | PRIuDADDR, comp_unit[a]); |
1246 | 0 | return 1; |
1247 | 0 | } |
1248 | 0 | comp->comp_len += fs->block_size; |
1249 | 0 | } |
1250 | | |
1251 | 0 | if (ntfs_uncompress_compunit(comp)) { |
1252 | 0 | return 1; |
1253 | 0 | } |
1254 | 0 | } |
1255 | | |
1256 | | /* Uncompressed data */ |
1257 | 0 | else { |
1258 | 0 | if (tsk_verbose) |
1259 | 0 | tsk_fprintf(stderr, |
1260 | 0 | "ntfs_proc_compunit: Unit is not compressed\n"); |
1261 | |
|
1262 | 0 | comp->uncomp_idx = 0; |
1263 | 0 | for (a = 0; a < comp_unit_size; a++) { |
1264 | 0 | ssize_t cnt; |
1265 | | |
1266 | | // Prevent an OOB write of comp->uncomp_buf |
1267 | 0 | if ((comp->uncomp_idx >= comp->buf_size_b) || (fs->block_size > comp->buf_size_b - comp->uncomp_idx)) { |
1268 | 0 | tsk_error_reset(); |
1269 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1270 | 0 | tsk_error_set_errstr("ntfs_proc_compunit: Buffer not big enough for uncompressed data (Index: %" PRIuSIZE ")", comp->uncomp_idx); |
1271 | 0 | return 1; |
1272 | 0 | } |
1273 | | |
1274 | 0 | cnt = |
1275 | 0 | tsk_fs_read_block(fs, comp_unit[a], |
1276 | 0 | &comp->uncomp_buf[comp->uncomp_idx], fs->block_size); |
1277 | 0 | if (cnt != fs->block_size) { |
1278 | 0 | if (cnt >= 0) { |
1279 | 0 | tsk_error_reset(); |
1280 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
1281 | 0 | } |
1282 | 0 | tsk_error_set_errstr2 |
1283 | 0 | ("ntfs_proc_compunit: Error reading block at %" |
1284 | 0 | PRIuDADDR, comp_unit[a]); |
1285 | 0 | return 1; |
1286 | 0 | } |
1287 | 0 | comp->uncomp_idx += fs->block_size; |
1288 | 0 | } |
1289 | 0 | } |
1290 | 0 | return 0; |
1291 | 0 | } |
1292 | | |
1293 | | |
1294 | | |
1295 | | /** |
1296 | | * Currently ignores the SPARSE flag |
1297 | | */ |
1298 | | static uint8_t |
1299 | | ntfs_attr_walk_special( |
1300 | | const TSK_FS_ATTR * fs_attr, |
1301 | | [[maybe_unused]] int flags, |
1302 | | TSK_FS_FILE_WALK_CB a_action, |
1303 | | void *ptr) |
1304 | 0 | { |
1305 | 0 | TSK_FS_INFO *fs; |
1306 | 0 | NTFS_INFO *ntfs; |
1307 | | |
1308 | | // clean up any error messages that are lying around |
1309 | 0 | tsk_error_reset(); |
1310 | 0 | if ((fs_attr == NULL) || (fs_attr->fs_file == NULL) |
1311 | 0 | || (fs_attr->fs_file->meta == NULL) |
1312 | 0 | || (fs_attr->fs_file->fs_info == NULL)) { |
1313 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1314 | 0 | tsk_error_set_errstr |
1315 | 0 | ("ntfs_attr_walk_special: Null arguments given\n"); |
1316 | 0 | return 1; |
1317 | 0 | } |
1318 | | |
1319 | 0 | fs = fs_attr->fs_file->fs_info; |
1320 | 0 | ntfs = (NTFS_INFO *) fs; |
1321 | | |
1322 | | /* Process the compressed buffer |
1323 | | * |
1324 | | * The compsize value equal to 0 can occur if we are processing an |
1325 | | * isolated entry that is part of an attribute list. The first |
1326 | | * sequence of the attribute has the compsize and the latter ones |
1327 | | * do not. So, if one of the non-base MFT entries is processed by |
1328 | | * itself, we have that case. I tried to assume it was 16, but it |
1329 | | * caused decompression problems -- likely because this sequence |
1330 | | * did not start on a compression unit boundary. So, now we just |
1331 | | * dump the compressed data instead of giving an error. |
1332 | | */ |
1333 | 0 | if (fs_attr->flags & TSK_FS_ATTR_COMP) { |
1334 | 0 | TSK_DADDR_T addr; |
1335 | 0 | TSK_FS_ATTR_RUN *fs_attr_run; |
1336 | 0 | TSK_DADDR_T *comp_unit; |
1337 | 0 | uint32_t comp_unit_idx = 0; |
1338 | 0 | NTFS_COMP_INFO comp; |
1339 | 0 | TSK_OFF_T off = 0; |
1340 | 0 | int retval; |
1341 | 0 | uint8_t stop_loop = 0; |
1342 | 0 | uint8_t init_size_reached = 0; |
1343 | 0 | uint8_t has_init_size = 0; |
1344 | |
|
1345 | 0 | if (fs_attr->nrd.compsize <= 0) { |
1346 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1347 | 0 | tsk_error_set_errstr |
1348 | 0 | ("ntfs_attrwalk_special: Compressed attribute has compsize of 0 (%" |
1349 | 0 | PRIuINUM ")", fs_attr->fs_file->meta->addr); |
1350 | 0 | return 1; |
1351 | 0 | } |
1352 | | |
1353 | | /* Allocate the buffers and state structure */ |
1354 | 0 | if (ntfs_uncompress_setup(fs, &comp, fs_attr->nrd.compsize)) { |
1355 | 0 | return 1; |
1356 | 0 | } |
1357 | | |
1358 | 0 | comp_unit = |
1359 | 0 | (TSK_DADDR_T *) tsk_malloc(fs_attr->nrd.compsize * |
1360 | 0 | sizeof(TSK_DADDR_T)); |
1361 | 0 | if (comp_unit == NULL) { |
1362 | 0 | ntfs_uncompress_done(&comp); |
1363 | 0 | return 1; |
1364 | 0 | } |
1365 | 0 | retval = TSK_WALK_CONT; |
1366 | |
|
1367 | 0 | if (fs_attr->nrd.initsize != fs_attr->fs_file->meta->size) |
1368 | 0 | has_init_size = 1; |
1369 | | |
1370 | | /* cycle through the number of runs we have */ |
1371 | 0 | for (fs_attr_run = fs_attr->nrd.run; fs_attr_run; |
1372 | 0 | fs_attr_run = fs_attr_run->next) { |
1373 | 0 | size_t len_idx; |
1374 | | |
1375 | | /* We may get a FILLER entry at the beginning of the run |
1376 | | * if we are processing a non-base file record since |
1377 | | * this $DATA attribute could not be the first sequence in the |
1378 | | * attribute. Therefore, do not error if it starts at 0 */ |
1379 | 0 | if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) { |
1380 | 0 | if (fs_attr_run->addr != 0) { |
1381 | 0 | tsk_error_reset(); |
1382 | |
|
1383 | 0 | if (fs_attr->fs_file->meta-> |
1384 | 0 | flags & TSK_FS_META_FLAG_UNALLOC) |
1385 | 0 | tsk_error_set_errno(TSK_ERR_FS_RECOVER); |
1386 | 0 | else |
1387 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
1388 | 0 | tsk_error_set_errstr |
1389 | 0 | ("ntfs_attr_walk_special: Filler Entry exists in fs_attr_run %" |
1390 | 0 | PRIuDADDR "@%" PRIuDADDR " - type: %" PRIu32 |
1391 | 0 | " id: %d Meta: %" PRIuINUM " Status: %s", |
1392 | 0 | fs_attr_run->len, fs_attr_run->addr, fs_attr->type, |
1393 | 0 | fs_attr->id, fs_attr->fs_file->meta->addr, |
1394 | 0 | (fs_attr->fs_file->meta-> |
1395 | 0 | flags & TSK_FS_META_FLAG_ALLOC) ? "Allocated" : |
1396 | 0 | "Deleted"); |
1397 | 0 | free(comp_unit); |
1398 | 0 | ntfs_uncompress_done(&comp); |
1399 | 0 | return 1; |
1400 | 0 | } |
1401 | 0 | else { |
1402 | 0 | if ((fs_attr_run->len > LLONG_MAX) |
1403 | 0 | || (LLONG_MAX / fs_attr_run->len < fs->block_size)) { |
1404 | 0 | if (fs_attr->fs_file->meta-> |
1405 | 0 | flags & TSK_FS_META_FLAG_UNALLOC) |
1406 | 0 | tsk_error_set_errno(TSK_ERR_FS_RECOVER); |
1407 | 0 | else |
1408 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
1409 | 0 | tsk_error_set_errstr |
1410 | 0 | ("ntfs_attr_walk_special: Attribute run length is too large %" |
1411 | 0 | PRIuDADDR "@%" PRIuDADDR " - type: %" PRIu32 |
1412 | 0 | " id: %d Meta: %" PRIuINUM " Status: %s", |
1413 | 0 | fs_attr_run->len, fs_attr_run->addr, fs_attr->type, |
1414 | 0 | fs_attr->id, fs_attr->fs_file->meta->addr, |
1415 | 0 | (fs_attr->fs_file->meta-> |
1416 | 0 | flags & TSK_FS_META_FLAG_ALLOC) ? "Allocated" : |
1417 | 0 | "Deleted"); |
1418 | 0 | free(comp_unit); |
1419 | 0 | ntfs_uncompress_done(&comp); |
1420 | 0 | return 1; |
1421 | 0 | } |
1422 | 0 | off += (fs_attr_run->len * fs->block_size); |
1423 | 0 | continue; |
1424 | 0 | } |
1425 | 0 | } |
1426 | 0 | addr = fs_attr_run->addr; |
1427 | | |
1428 | | /* cycle through each cluster in the run */ |
1429 | 0 | for (len_idx = 0; len_idx < fs_attr_run->len; len_idx++) { |
1430 | |
|
1431 | 0 | if (addr > fs->last_block) { |
1432 | 0 | tsk_error_reset(); |
1433 | |
|
1434 | 0 | if (fs_attr->fs_file->meta-> |
1435 | 0 | flags & TSK_FS_META_FLAG_UNALLOC) |
1436 | 0 | tsk_error_set_errno(TSK_ERR_FS_RECOVER); |
1437 | 0 | else |
1438 | 0 | tsk_error_set_errno(TSK_ERR_FS_BLK_NUM); |
1439 | 0 | tsk_error_set_errstr |
1440 | 0 | ("ntfs_attr_walk_special: Invalid address in run (too large): %" |
1441 | 0 | PRIuDADDR " Meta: %" PRIuINUM " Status: %s", addr, |
1442 | 0 | fs_attr->fs_file->meta->addr, |
1443 | 0 | (fs_attr->fs_file->meta-> |
1444 | 0 | flags & TSK_FS_META_FLAG_ALLOC) ? "Allocated" : |
1445 | 0 | "Deleted"); |
1446 | |
|
1447 | 0 | free(comp_unit); |
1448 | 0 | ntfs_uncompress_done(&comp); |
1449 | 0 | return 1; |
1450 | 0 | } |
1451 | | |
1452 | | // queue up the addresses until we get a full unit |
1453 | 0 | comp_unit[comp_unit_idx++] = addr; |
1454 | | |
1455 | | // time to decompress (if queue is full or this is the last block) |
1456 | 0 | if ((comp_unit_idx == fs_attr->nrd.compsize) |
1457 | 0 | || ((len_idx == fs_attr_run->len - 1) |
1458 | 0 | && (fs_attr_run->next == NULL))) { |
1459 | 0 | size_t i; |
1460 | | |
1461 | |
|
1462 | 0 | if (tsk_verbose) |
1463 | 0 | tsk_fprintf(stderr, |
1464 | 0 | "ntfs_proc_compunit: Decompressing at file offset %" PRIdOFF "\n", off); |
1465 | | |
1466 | | // decompress the unit if we have not passed initsize yet. |
1467 | 0 | if (!init_size_reached) { |
1468 | 0 | if (ntfs_proc_compunit(ntfs, &comp, comp_unit, |
1469 | 0 | comp_unit_idx)) { |
1470 | 0 | tsk_error_set_errstr2("%" PRIuINUM " - type: %" |
1471 | 0 | PRIu32 " id: %d Status: %s", |
1472 | 0 | fs_attr->fs_file->meta->addr, fs_attr->type, |
1473 | 0 | fs_attr->id, |
1474 | 0 | (fs_attr->fs_file->meta-> |
1475 | 0 | flags & TSK_FS_META_FLAG_ALLOC) ? |
1476 | 0 | "Allocated" : "Deleted"); |
1477 | 0 | free(comp_unit); |
1478 | 0 | ntfs_uncompress_done(&comp); |
1479 | 0 | return 1; |
1480 | 0 | } |
1481 | | |
1482 | | /* if we've passed the initialized size while reading this block, |
1483 | | * zero out the buffer beyond the initialized size. */ |
1484 | 0 | if (has_init_size && (off < fs_attr->nrd.initsize)) { |
1485 | 0 | const int64_t prev_remanining_init_size = fs_attr->nrd.initsize - off; |
1486 | 0 | if (prev_remanining_init_size < (int64_t)comp.buf_size_b) { |
1487 | 0 | memset(&comp.uncomp_buf[prev_remanining_init_size], 0, comp.buf_size_b - prev_remanining_init_size); |
1488 | 0 | init_size_reached = 1; |
1489 | 0 | } |
1490 | 0 | } |
1491 | 0 | } |
1492 | | // set the buffers to 0s if we are past initsize |
1493 | 0 | else { |
1494 | 0 | ntfs_uncompress_reset(&comp); |
1495 | 0 | comp.uncomp_idx = comp.buf_size_b; |
1496 | 0 | } |
1497 | | |
1498 | | // now call the callback with the uncompressed data |
1499 | 0 | for (i = 0; i < comp_unit_idx; i++) { |
1500 | 0 | int myflags; |
1501 | 0 | size_t read_len; |
1502 | |
|
1503 | 0 | myflags = |
1504 | 0 | TSK_FS_BLOCK_FLAG_CONT | |
1505 | 0 | TSK_FS_BLOCK_FLAG_COMP; |
1506 | 0 | retval = is_clustalloc(ntfs, comp_unit[i]); |
1507 | 0 | if (retval == -1) { |
1508 | 0 | if (fs_attr->fs_file->meta-> |
1509 | 0 | flags & TSK_FS_META_FLAG_UNALLOC) |
1510 | 0 | tsk_error_set_errno(TSK_ERR_FS_RECOVER); |
1511 | 0 | free(comp_unit); |
1512 | 0 | ntfs_uncompress_done(&comp); |
1513 | 0 | return 1; |
1514 | 0 | } |
1515 | 0 | else if (retval == 1) { |
1516 | 0 | myflags |= TSK_FS_BLOCK_FLAG_ALLOC; |
1517 | 0 | } |
1518 | 0 | else if (retval == 0) { |
1519 | 0 | myflags |= TSK_FS_BLOCK_FLAG_UNALLOC; |
1520 | 0 | } |
1521 | | |
1522 | | // Unclear what the behavior should be here |
1523 | | // assuming POSIX like behavior is likely the required approach |
1524 | 0 | if (off >= fs_attr->size) |
1525 | 0 | read_len = 0; |
1526 | 0 | else if (fs_attr->size - off > fs->block_size) |
1527 | 0 | read_len = fs->block_size; |
1528 | 0 | else |
1529 | 0 | read_len = (size_t) (fs_attr->size - off); |
1530 | |
|
1531 | 0 | if (i * fs->block_size + read_len > |
1532 | 0 | comp.uncomp_idx) { |
1533 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1534 | 0 | tsk_error_set_errstr |
1535 | 0 | ("ntfs_attrwalk_special: Trying to read past end of uncompressed buffer: %" |
1536 | 0 | PRIuSIZE " %" PRIuSIZE " Meta: %" PRIuINUM |
1537 | 0 | " Status: %s", |
1538 | 0 | i * fs->block_size + read_len, |
1539 | 0 | comp.uncomp_idx, |
1540 | 0 | fs_attr->fs_file->meta->addr, |
1541 | 0 | (fs_attr->fs_file->meta-> |
1542 | 0 | flags & TSK_FS_META_FLAG_ALLOC) ? |
1543 | 0 | "Allocated" : "Deleted"); |
1544 | 0 | free(comp_unit); |
1545 | 0 | ntfs_uncompress_done(&comp); |
1546 | 0 | return 1; |
1547 | 0 | } |
1548 | | |
1549 | | // call the callback |
1550 | 0 | retval = |
1551 | 0 | a_action(fs_attr->fs_file, off, comp_unit[i], |
1552 | 0 | &comp.uncomp_buf[i * fs->block_size], read_len, |
1553 | 0 | (TSK_FS_BLOCK_FLAG_ENUM) myflags, ptr); |
1554 | |
|
1555 | 0 | off += read_len; |
1556 | |
|
1557 | 0 | if (off >= fs_attr->size) { |
1558 | 0 | stop_loop = 1; |
1559 | 0 | break; |
1560 | 0 | } |
1561 | 0 | if (retval != TSK_WALK_CONT) { |
1562 | 0 | stop_loop = 1; |
1563 | 0 | break; |
1564 | 0 | } |
1565 | 0 | } |
1566 | 0 | comp_unit_idx = 0; |
1567 | 0 | } |
1568 | | |
1569 | 0 | if (stop_loop) |
1570 | 0 | break; |
1571 | | |
1572 | | /* If it is a sparse run, don't increment the addr so that |
1573 | | * it remains 0 */ |
1574 | 0 | if (((fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) == |
1575 | 0 | 0) |
1576 | 0 | && ((fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) |
1577 | 0 | == 0)) |
1578 | 0 | addr++; |
1579 | 0 | } |
1580 | | |
1581 | 0 | if (stop_loop) |
1582 | 0 | break; |
1583 | 0 | } |
1584 | | |
1585 | 0 | ntfs_uncompress_done(&comp); |
1586 | 0 | free(comp_unit); |
1587 | |
|
1588 | 0 | if (retval == TSK_WALK_ERROR) |
1589 | 0 | return 1; |
1590 | 0 | else |
1591 | 0 | return 0; |
1592 | 0 | } |
1593 | 0 | else { |
1594 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1595 | 0 | tsk_error_set_errstr |
1596 | 0 | ("ntfs_attrwalk_special: called with non-special attribute: %x", |
1597 | 0 | fs_attr->flags); |
1598 | 0 | return 1; |
1599 | 0 | } |
1600 | 0 | } |
1601 | | |
1602 | | |
1603 | | /** \internal |
1604 | | * |
1605 | | * @returns number of bytes read or -1 on error (incl if offset is past EOF) |
1606 | | */ |
1607 | | static ssize_t |
1608 | | ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr, |
1609 | | TSK_OFF_T a_offset, char *a_buf, size_t a_len) |
1610 | 0 | { |
1611 | 0 | TSK_FS_INFO *fs = NULL; |
1612 | 0 | NTFS_INFO *ntfs = NULL; |
1613 | |
|
1614 | 0 | if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL) |
1615 | 0 | || (a_fs_attr->fs_file->meta == NULL) |
1616 | 0 | || (a_fs_attr->fs_file->fs_info == NULL)) { |
1617 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1618 | 0 | tsk_error_set_errstr |
1619 | 0 | ("ntfs_file_read_special: NULL parameters passed"); |
1620 | 0 | return -1; |
1621 | 0 | } |
1622 | | |
1623 | 0 | fs = a_fs_attr->fs_file->fs_info; |
1624 | 0 | ntfs = (NTFS_INFO *) fs; |
1625 | |
|
1626 | 0 | if (a_fs_attr->flags & TSK_FS_ATTR_COMP) { |
1627 | 0 | TSK_FS_ATTR_RUN *data_run_cur; |
1628 | 0 | TSK_OFF_T cu_blkoffset; // block offset of starting compression unit to start reading from |
1629 | 0 | size_t byteoffset; // byte offset in compression unit of where we want to start reading from |
1630 | 0 | TSK_DADDR_T *comp_unit; |
1631 | 0 | uint32_t comp_unit_idx = 0; |
1632 | 0 | NTFS_COMP_INFO comp; |
1633 | 0 | size_t buf_idx = 0; |
1634 | 0 | uint8_t init_size_reached = 0; |
1635 | 0 | uint8_t has_init_size = 0; |
1636 | |
|
1637 | 0 | if (a_fs_attr->nrd.compsize <= 0) { |
1638 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
1639 | 0 | tsk_error_set_errstr |
1640 | 0 | ("ntfs_file_read_special: Compressed attribute has compsize of 0"); |
1641 | 0 | return -1; |
1642 | 0 | } |
1643 | | |
1644 | 0 | if (a_offset >= a_fs_attr->size) { |
1645 | 0 | tsk_error_reset(); |
1646 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ_OFF); |
1647 | 0 | tsk_error_set_errstr("ntfs_file_read_special - %" PRIdOFF |
1648 | 0 | " Meta: %" PRIuINUM, a_offset, |
1649 | 0 | a_fs_attr->fs_file->meta->addr); |
1650 | 0 | return -1; |
1651 | 0 | } |
1652 | | |
1653 | | // we return 0s for reads past the initsize |
1654 | 0 | if (a_offset >= a_fs_attr->nrd.initsize) { |
1655 | 0 | ssize_t len; |
1656 | |
|
1657 | 0 | if (tsk_verbose) |
1658 | 0 | fprintf(stderr, |
1659 | 0 | "ntfs_file_read_special: Returning 0s for read past end of initsize (%" |
1660 | 0 | PRIuINUM ")\n", a_fs_attr->fs_file->meta->addr); |
1661 | |
|
1662 | 0 | if (a_offset + (TSK_OFF_T)a_len > a_fs_attr->nrd.allocsize) |
1663 | 0 | len = (ssize_t) (a_fs_attr->nrd.allocsize - a_offset); |
1664 | 0 | else |
1665 | 0 | len = (ssize_t) a_len; |
1666 | 0 | memset(a_buf, 0, a_len); |
1667 | 0 | return len; |
1668 | 0 | } |
1669 | | |
1670 | 0 | if (a_fs_attr->nrd.initsize != a_fs_attr->fs_file->meta->size) |
1671 | 0 | has_init_size = 1; |
1672 | | |
1673 | | /* Allocate the buffers and state structure */ |
1674 | 0 | if (ntfs_uncompress_setup(fs, &comp, a_fs_attr->nrd.compsize)) { |
1675 | 0 | return -1; |
1676 | 0 | } |
1677 | | |
1678 | 0 | comp_unit = |
1679 | 0 | (TSK_DADDR_T *) tsk_malloc(a_fs_attr->nrd.compsize * |
1680 | 0 | sizeof(TSK_DADDR_T)); |
1681 | 0 | if (comp_unit == NULL) { |
1682 | 0 | ntfs_uncompress_done(&comp); |
1683 | 0 | return -1; |
1684 | 0 | } |
1685 | | |
1686 | | // figure out the needed offsets |
1687 | 0 | cu_blkoffset = a_offset / fs->block_size; |
1688 | 0 | if (cu_blkoffset) { |
1689 | 0 | cu_blkoffset /= a_fs_attr->nrd.compsize; |
1690 | 0 | cu_blkoffset *= a_fs_attr->nrd.compsize; |
1691 | 0 | } |
1692 | |
|
1693 | 0 | byteoffset = (size_t) (a_offset - cu_blkoffset * fs->block_size); |
1694 | | |
1695 | | // cycle through the run until we find where we can start to process the clusters |
1696 | 0 | for (data_run_cur = a_fs_attr->nrd.run; |
1697 | 0 | (data_run_cur) && (buf_idx < a_len); |
1698 | 0 | data_run_cur = data_run_cur->next) { |
1699 | |
|
1700 | 0 | TSK_DADDR_T addr; |
1701 | 0 | size_t a; |
1702 | | |
1703 | | // See if this run contains the starting offset they requested |
1704 | 0 | if (data_run_cur->offset + data_run_cur->len < |
1705 | 0 | (TSK_DADDR_T) cu_blkoffset) |
1706 | 0 | continue; |
1707 | | |
1708 | | |
1709 | | // seek to the start of where we want to read (we may need to read several runs) |
1710 | 0 | if (data_run_cur->offset > (TSK_DADDR_T) cu_blkoffset) |
1711 | 0 | a = 0; |
1712 | 0 | else |
1713 | 0 | a = (size_t) (cu_blkoffset - data_run_cur->offset); |
1714 | |
|
1715 | 0 | addr = data_run_cur->addr; |
1716 | | // don't increment addr if it is 0 -- sparse |
1717 | 0 | if (addr) |
1718 | 0 | addr += a; |
1719 | | |
1720 | | /* cycle through the relevant in the run */ |
1721 | 0 | for (; a < data_run_cur->len && buf_idx < a_len; a++) { |
1722 | | |
1723 | | // queue up the addresses until we get a full unit |
1724 | 0 | comp_unit[comp_unit_idx++] = addr; |
1725 | | |
1726 | | // time to decompress (if queue is full or this is the last block) |
1727 | 0 | if ((comp_unit_idx == a_fs_attr->nrd.compsize) |
1728 | 0 | || ((a == data_run_cur->len - 1) |
1729 | 0 | && (data_run_cur->next == NULL))) { |
1730 | 0 | size_t cpylen; |
1731 | | |
1732 | | // decompress the unit if we are still in initsize |
1733 | 0 | if (!init_size_reached) { |
1734 | 0 | if (ntfs_proc_compunit(ntfs, &comp, comp_unit, |
1735 | 0 | comp_unit_idx)) { |
1736 | 0 | tsk_error_set_errstr2("%" PRIuINUM " - type: %" |
1737 | 0 | PRIu32 " id: %d Status: %s", |
1738 | 0 | a_fs_attr->fs_file->meta->addr, |
1739 | 0 | a_fs_attr->type, a_fs_attr->id, |
1740 | 0 | (a_fs_attr->fs_file->meta-> |
1741 | 0 | flags & TSK_FS_META_FLAG_ALLOC) ? |
1742 | 0 | "Allocated" : "Deleted"); |
1743 | 0 | free(comp_unit); |
1744 | 0 | ntfs_uncompress_done(&comp); |
1745 | 0 | return -1; |
1746 | 0 | } |
1747 | | |
1748 | | /* if we've passed the initialized size while reading this block, |
1749 | | * zero out the buffer beyond the initialized size |
1750 | | */ |
1751 | 0 | if (has_init_size) { |
1752 | 0 | const int64_t remanining_init_size = a_fs_attr->nrd.initsize - buf_idx - a_offset; |
1753 | 0 | if (remanining_init_size < (int64_t)comp.buf_size_b) { |
1754 | 0 | memset(comp.uncomp_buf + remanining_init_size, 0, comp.buf_size_b - remanining_init_size); |
1755 | 0 | init_size_reached = 1; |
1756 | 0 | } |
1757 | 0 | } |
1758 | 0 | } |
1759 | 0 | else { |
1760 | 0 | ntfs_uncompress_reset(&comp); |
1761 | 0 | comp.uncomp_idx = comp.buf_size_b; |
1762 | 0 | } |
1763 | | |
1764 | | // copy uncompressed data to the output buffer |
1765 | 0 | if (comp.uncomp_idx < byteoffset) { |
1766 | | |
1767 | | // @@ ERROR |
1768 | 0 | free(comp_unit); |
1769 | 0 | ntfs_uncompress_done(&comp); |
1770 | 0 | return -1; |
1771 | 0 | } |
1772 | 0 | else if (comp.uncomp_idx - byteoffset < |
1773 | 0 | a_len - buf_idx) { |
1774 | 0 | cpylen = comp.uncomp_idx - byteoffset; |
1775 | 0 | } |
1776 | 0 | else { |
1777 | 0 | cpylen = a_len - buf_idx; |
1778 | 0 | } |
1779 | | // Make sure not to return more bytes than are in the file |
1780 | 0 | if (cpylen > (a_fs_attr->size - (a_offset + buf_idx))) |
1781 | 0 | cpylen = |
1782 | 0 | (size_t) (a_fs_attr->size - (a_offset + |
1783 | 0 | buf_idx)); |
1784 | |
|
1785 | 0 | memcpy(&a_buf[buf_idx], &comp.uncomp_buf[byteoffset], |
1786 | 0 | cpylen); |
1787 | | |
1788 | | // reset this in case we need to also read from the next run |
1789 | 0 | byteoffset = 0; |
1790 | 0 | buf_idx += cpylen; |
1791 | 0 | comp_unit_idx = 0; |
1792 | |
|
1793 | 0 | } |
1794 | | /* If it is a sparse run, don't increment the addr so that |
1795 | | * it remains 0 */ |
1796 | 0 | if (((data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) == |
1797 | 0 | 0) |
1798 | 0 | && ((data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) |
1799 | 0 | == 0)) |
1800 | 0 | addr++; |
1801 | 0 | } |
1802 | 0 | } |
1803 | | |
1804 | 0 | free(comp_unit); |
1805 | 0 | ntfs_uncompress_done(&comp); |
1806 | 0 | return (ssize_t) buf_idx; |
1807 | 0 | } |
1808 | 0 | else { |
1809 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1810 | 0 | tsk_error_set_errstr |
1811 | 0 | ("ntfs_file_read_special: called with non-special attribute: %x", |
1812 | 0 | a_fs_attr->flags); |
1813 | 0 | return -1; |
1814 | 0 | } |
1815 | 0 | } |
1816 | | |
1817 | | |
1818 | | /* needs to be predefined for proc_attrseq */ |
1819 | | static TSK_RETVAL_ENUM ntfs_proc_attrlist(NTFS_INFO *, TSK_FS_FILE *, |
1820 | | const TSK_FS_ATTR *, TSK_STACK *); |
1821 | | |
1822 | | |
1823 | | /* This structure is used when processing attrlist attributes. |
1824 | | * The Id part of the MFTNUM-TYPE-ID triple is unique only to a given |
1825 | | * MFTNUM. With the case of attribute lists, a file may use multiple |
1826 | | * MFT entires and therefore have multiple attributes with the same |
1827 | | * type and id pair (if they are in different MFT entries). This map |
1828 | | * is created by proc_attrlist when it assigns unique IDs to the |
1829 | | * other entries. proc_attrseq uses this when it adds the attributes. |
1830 | | */ |
1831 | | typedef struct { |
1832 | | int num_used; |
1833 | | TSK_INUM_T extMft[256]; |
1834 | | uint32_t type[256]; |
1835 | | uint32_t extId[256]; |
1836 | | uint8_t name[256][512]; |
1837 | | uint32_t newId[256]; |
1838 | | } NTFS_ATTRLIST_MAP; |
1839 | | |
1840 | | /* |
1841 | | * Process an NTFS attribute sequence and load the data into data |
1842 | | * structures. |
1843 | | * An attribute sequence is a linked list of the attributes in an MFT entry. |
1844 | | * This is called by copy_inode and proc_attrlist. |
1845 | | * |
1846 | | * @param ntfs File system to analyze |
1847 | | * @param fs_file Generic metadata structure to add the attribute info to |
1848 | | * @param attrseq Start of the attribute sequence to analyze |
1849 | | * @param len Length of the attribute sequence buffer |
1850 | | * @param a_attrinum MFT entry address that the attribute sequence came from (diff from fs_file for attribute lists) |
1851 | | * @param a_attr_map List that maps to new IDs that were assigned by processing |
1852 | | * the attribute list attribute (if it exists) or NULL if there is no attrlist. |
1853 | | * @param a_seen_inum_list List of inums that have been previously processed based on attribute lists. |
1854 | | * Can be NULL when this is called for the first time. Should be non-NULL when this is called recursively by proc_attrlist. |
1855 | | * @returns Error code |
1856 | | */ |
1857 | | static TSK_RETVAL_ENUM |
1858 | | ntfs_proc_attrseq(NTFS_INFO * ntfs, |
1859 | | TSK_FS_FILE * fs_file, const ntfs_attr * a_attrseq, size_t len, |
1860 | | TSK_INUM_T a_attrinum, const NTFS_ATTRLIST_MAP * a_attr_map, TSK_STACK * a_seen_inum_list) |
1861 | 0 | { |
1862 | 0 | const ntfs_attr *attr; |
1863 | 0 | const TSK_FS_ATTR *fs_attr_attrl = NULL; |
1864 | 0 | char name[NTFS_MAXNAMLEN_UTF8 + 1]; |
1865 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info; |
1866 | |
|
1867 | 0 | if (tsk_verbose) |
1868 | 0 | tsk_fprintf(stderr, |
1869 | 0 | "ntfs_proc_attrseq: Processing extended entry for primary entry %" |
1870 | 0 | PRIuINUM "\n", fs_file->meta->addr); |
1871 | |
|
1872 | 0 | if (fs_file->meta->attr == NULL) { |
1873 | 0 | tsk_error_reset(); |
1874 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1875 | 0 | tsk_error_set_errstr("Null attribute list in ntfs_proc_attrseq"); |
1876 | 0 | return TSK_ERR; |
1877 | 0 | } |
1878 | | |
1879 | 0 | if (len > ntfs->mft_rsize_b) { |
1880 | 0 | tsk_error_reset(); |
1881 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1882 | 0 | tsk_error_set_errstr("invalid length in ntfs_proc_attrseq"); |
1883 | 0 | return TSK_ERR; |
1884 | 0 | } |
1885 | | |
1886 | | |
1887 | | /* Cycle through the list of attributes |
1888 | | * There are 16 bytes in the non-union part of |
1889 | | * an ntfs_attr, so make sure there is at least room for that */ |
1890 | 0 | for (attr = a_attrseq; ((uintptr_t) attr >= (uintptr_t) a_attrseq) |
1891 | 0 | && ((uintptr_t) attr + 16 <= ((uintptr_t) a_attrseq + len)) |
1892 | 0 | && (tsk_getu32(fs->endian, attr->len) > 0 |
1893 | 0 | && (tsk_getu32(fs->endian, attr->type) != |
1894 | 0 | 0xffffffff)); |
1895 | 0 | attr = |
1896 | 0 | (ntfs_attr *) ((uintptr_t) attr + tsk_getu32(fs->endian, |
1897 | 0 | attr->len))) { |
1898 | |
|
1899 | 0 | int retVal, i; |
1900 | 0 | uint32_t type; |
1901 | 0 | uint16_t id, id_new; |
1902 | | |
1903 | | // sanity check on bounds of attribute. Prevents other |
1904 | | // issues later on that use attr->len for bounds checks. |
1905 | 0 | if (((uintptr_t) attr + tsk_getu32(fs->endian, |
1906 | 0 | attr->len)) > (uintptr_t)a_attrseq + len) { |
1907 | 0 | break; |
1908 | 0 | } |
1909 | | |
1910 | | // Ensure that the name offset doesn't refer to a location beyond |
1911 | | // the attribute. |
1912 | 0 | if (((uintptr_t)attr + tsk_getu16(fs->endian, attr->name_off)) > |
1913 | 0 | ((uintptr_t)attr + tsk_getu32(fs->endian, attr->len))) { |
1914 | 0 | break; |
1915 | 0 | } |
1916 | | |
1917 | | /* Get the type of this attribute */ |
1918 | 0 | type = tsk_getu32(fs->endian, attr->type); |
1919 | 0 | id = tsk_getu16(fs->endian, attr->id); |
1920 | 0 | id_new = id; |
1921 | | |
1922 | | /* If the map was supplied, search through it to see if this |
1923 | | * entry is in there. Use that ID instead so that we always have |
1924 | | * unique IDs for each attribute -- even if it spans multiple MFT entries. */ |
1925 | 0 | if (a_attr_map) { |
1926 | 0 | for (i = 0; i < a_attr_map->num_used; i++) { |
1927 | 0 | if ((a_attr_map->type[i] == type) && |
1928 | 0 | (memcmp(a_attr_map->name[i], |
1929 | 0 | (void *) ((uintptr_t) attr + |
1930 | 0 | tsk_getu16(fs->endian, attr->name_off)), |
1931 | 0 | attr->nlen * 2) == 0)) { |
1932 | 0 | id_new = a_attr_map->newId[i]; |
1933 | 0 | break; |
1934 | 0 | } |
1935 | 0 | } |
1936 | 0 | } |
1937 | | |
1938 | | /* Copy the name and convert it to UTF8 */ |
1939 | 0 | const uint16_t nameoff = tsk_getu16(fs->endian, attr->name_off); |
1940 | 0 | if (attr->nlen && nameoff + (uint32_t) attr->nlen * 2 < tsk_getu32(fs->endian, attr->len)) { |
1941 | 0 | int i; |
1942 | 0 | UTF8 *name8; |
1943 | 0 | UTF16 *name16; |
1944 | |
|
1945 | 0 | name8 = (UTF8 *) name; |
1946 | 0 | name16 = (UTF16 *) ((uintptr_t) attr + nameoff); |
1947 | |
|
1948 | 0 | retVal = |
1949 | 0 | tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, |
1950 | 0 | (UTF16 *) ((uintptr_t) name16 + |
1951 | 0 | attr->nlen * 2), |
1952 | 0 | &name8, |
1953 | 0 | (UTF8 *) ((uintptr_t) name8 + |
1954 | 0 | sizeof(name)), TSKlenientConversion); |
1955 | |
|
1956 | 0 | if (retVal != TSKconversionOK) { |
1957 | 0 | if (tsk_verbose) |
1958 | 0 | tsk_fprintf(stderr, |
1959 | 0 | "ntfs_proc_attrseq: Error converting NTFS attribute name to UTF8: %d %" |
1960 | 0 | PRIuINUM, retVal, fs_file->meta->addr); |
1961 | 0 | *name = '\0'; |
1962 | 0 | } |
1963 | | |
1964 | | /* Make sure it is NULL Terminated */ |
1965 | 0 | else if ((uintptr_t) name8 >= (uintptr_t) name + sizeof(name)) |
1966 | 0 | name[sizeof(name) - 1] = '\0'; |
1967 | 0 | else |
1968 | 0 | *name8 = '\0'; |
1969 | | |
1970 | | /* Clean up name */ |
1971 | 0 | i = 0; |
1972 | 0 | while (name[i] != '\0') { |
1973 | 0 | if (TSK_IS_CNTRL(name[i])) |
1974 | 0 | name[i] = '^'; |
1975 | 0 | i++; |
1976 | 0 | } |
1977 | 0 | } |
1978 | 0 | else { |
1979 | 0 | name[0] = '\0'; |
1980 | 0 | } |
1981 | | |
1982 | | /* For resident attributes, we will copy the buffer into |
1983 | | * a TSK_FS_ATTR buffer, which is stored in the TSK_FS_META |
1984 | | * structure |
1985 | | */ |
1986 | 0 | if (attr->res == NTFS_MFT_RES) { |
1987 | 0 | TSK_FS_ATTR *fs_attr; |
1988 | |
|
1989 | 0 | if (tsk_verbose) |
1990 | 0 | tsk_fprintf(stderr, |
1991 | 0 | "ntfs_proc_attrseq: Resident Attribute in Type: %" |
1992 | 0 | PRIu32 " Id: %" PRIu16 " IdNew: %" PRIu16 |
1993 | 0 | " Name: %s\n", type, id, id_new, name); |
1994 | | |
1995 | | /* Check that there is room for the data. |
1996 | | * Resident data needs 24 bytes total */ |
1997 | 0 | if (((uintptr_t)attr + 24) > ((uintptr_t)a_attrseq + len)) { |
1998 | 0 | tsk_error_reset(); |
1999 | 0 | tsk_error_set_errno(TSK_ERR_FS_CORRUPT); |
2000 | 0 | tsk_error_set_errstr("ntfs_attr_walk: Resident attribute %" |
2001 | 0 | PRIuINUM "-%" PRIu32 |
2002 | 0 | " starting offset and length too large", |
2003 | 0 | fs_file->meta->addr, type); |
2004 | 0 | return TSK_COR; |
2005 | 0 | } |
2006 | | |
2007 | | /* Validate the offset lengths */ |
2008 | 0 | if (((tsk_getu16(fs->endian, |
2009 | 0 | attr->c.r.soff) + (uintptr_t) attr) > |
2010 | 0 | ((uintptr_t) a_attrseq + len)) |
2011 | 0 | || (((size_t)tsk_getu16(fs->endian, |
2012 | 0 | attr->c.r.soff) + tsk_getu32(fs->endian, |
2013 | 0 | attr->c.r.ssize) + (uintptr_t) attr) > |
2014 | 0 | ((uintptr_t) a_attrseq + len))) { |
2015 | 0 | tsk_error_reset(); |
2016 | 0 | tsk_error_set_errno(TSK_ERR_FS_CORRUPT); |
2017 | 0 | tsk_error_set_errstr("ntfs_attr_walk: Resident attribute %" |
2018 | 0 | PRIuINUM "-%" PRIu32 |
2019 | 0 | " starting offset and length too large", |
2020 | 0 | fs_file->meta->addr, type); |
2021 | 0 | return TSK_COR; |
2022 | 0 | } |
2023 | | |
2024 | | // Get a free fs_attr structure |
2025 | 0 | if ((fs_attr = |
2026 | 0 | tsk_fs_attrlist_getnew(fs_file->meta->attr, |
2027 | 0 | TSK_FS_ATTR_RES)) == NULL) { |
2028 | 0 | tsk_error_errstr2_concat(" - proc_attrseq"); |
2029 | 0 | return TSK_ERR; |
2030 | 0 | } |
2031 | | |
2032 | | // set the details in the fs_attr structure |
2033 | 0 | if (tsk_fs_attr_set_str(fs_file, fs_attr, name, |
2034 | 0 | (TSK_FS_ATTR_TYPE_ENUM) type, |
2035 | 0 | id_new, (void *) ((uintptr_t) attr + |
2036 | 0 | tsk_getu16(fs->endian, |
2037 | 0 | attr->c.r.soff)), tsk_getu32(fs->endian, |
2038 | 0 | attr->c.r.ssize))) { |
2039 | 0 | tsk_error_errstr2_concat("- proc_attrseq"); |
2040 | 0 | return TSK_ERR; |
2041 | 0 | } |
2042 | | |
2043 | | // set the meta size if we find the relevant attribute |
2044 | 0 | if (TSK_FS_IS_DIR_META(fs_file->meta->type) |
2045 | 0 | && (type == NTFS_ATYPE_IDXROOT)) { |
2046 | 0 | fs_file->meta->size = |
2047 | 0 | tsk_getu32(fs->endian, attr->c.r.ssize); |
2048 | 0 | } |
2049 | 0 | else if ((fs_file->meta->type == TSK_FS_META_TYPE_REG) |
2050 | 0 | && (type == NTFS_ATYPE_DATA) && (name[0] == '\0')) { |
2051 | 0 | fs_file->meta->size = |
2052 | 0 | tsk_getu32(fs->endian, attr->c.r.ssize); |
2053 | 0 | } |
2054 | 0 | } |
2055 | | |
2056 | | /* For non-resident attributes, we will copy the runlist |
2057 | | * to the generic form and then save it in the TSK_FS_META->attr |
2058 | | * list |
2059 | | */ |
2060 | 0 | else { |
2061 | 0 | TSK_FS_ATTR *fs_attr = NULL; |
2062 | 0 | TSK_FS_ATTR_RUN *fs_attr_run = NULL; |
2063 | 0 | uint8_t data_flag = 0; |
2064 | 0 | uint32_t compsize = 0; |
2065 | 0 | TSK_RETVAL_ENUM retval; |
2066 | |
|
2067 | 0 | if (tsk_verbose) |
2068 | 0 | tsk_fprintf(stderr, |
2069 | 0 | "ntfs_proc_attrseq: Non-Resident Attribute Type: %" |
2070 | 0 | PRIu32 " Id: %" PRIu16 " IdNew: %" PRIu16 |
2071 | 0 | " Name: %s Start VCN: %" PRIu64 "\n", type, id, |
2072 | 0 | id_new, name, tsk_getu64(fs->endian, |
2073 | 0 | attr->c.nr.start_vcn)); |
2074 | | |
2075 | | /* Check that there is room for the data. |
2076 | | * Non-resident data needs 64 bytes total */ |
2077 | 0 | if (((uintptr_t)attr + 64) > ((uintptr_t)a_attrseq + len)) { |
2078 | 0 | tsk_error_reset(); |
2079 | 0 | tsk_error_set_errno(TSK_ERR_FS_CORRUPT); |
2080 | 0 | tsk_error_set_errstr("ntfs_attr_walk: Non-Resident attribute %" |
2081 | 0 | PRIuINUM "-%" PRIu32 |
2082 | 0 | " starting offset and length too large", |
2083 | 0 | fs_file->meta->addr, type); |
2084 | 0 | return TSK_COR; |
2085 | 0 | } |
2086 | | |
2087 | 0 | uint32_t attr_len = tsk_getu32(fs->endian, attr->len); |
2088 | 0 | uint64_t run_start_vcn = tsk_getu64(fs->endian, attr->c.nr.start_vcn); |
2089 | 0 | uint16_t run_off = tsk_getu16(fs->endian, attr->c.nr.run_off); |
2090 | | |
2091 | | // sanity check |
2092 | 0 | if ((run_off < 48) || (run_off >= attr_len)) { |
2093 | 0 | if (tsk_verbose) |
2094 | 0 | tsk_fprintf(stderr, "ntfs_proc_attrseq: run offset out of bounds\n"); |
2095 | 0 | break; |
2096 | 0 | } |
2097 | | |
2098 | | /* convert the run to generic form */ |
2099 | 0 | retval = ntfs_make_data_run(ntfs, |
2100 | 0 | run_start_vcn, |
2101 | 0 | (ntfs_runlist *) ((uintptr_t) attr + run_off), |
2102 | 0 | attr_len - run_off, |
2103 | 0 | &fs_attr_run, NULL, |
2104 | 0 | a_attrinum); |
2105 | 0 | if (retval != TSK_OK) { |
2106 | 0 | tsk_error_errstr2_concat(" - proc_attrseq"); |
2107 | 0 | return retval; |
2108 | 0 | } |
2109 | | |
2110 | | /* Determine the flags based on compression and stuff */ |
2111 | 0 | data_flag = 0; |
2112 | 0 | if (tsk_getu16(fs->endian, attr->flags) & NTFS_ATTR_FLAG_COMP) { |
2113 | 0 | data_flag |= TSK_FS_ATTR_COMP; |
2114 | 0 | fs_file->meta->flags = (TSK_FS_META_FLAG_ENUM) (fs_file->meta->flags | TSK_FS_META_FLAG_COMP); |
2115 | 0 | } |
2116 | |
|
2117 | 0 | if (tsk_getu16(fs->endian, attr->flags) & NTFS_ATTR_FLAG_ENC) |
2118 | 0 | data_flag |= TSK_FS_ATTR_ENC; |
2119 | |
|
2120 | 0 | if (tsk_getu16(fs->endian, attr->flags) & NTFS_ATTR_FLAG_SPAR) |
2121 | 0 | data_flag |= TSK_FS_ATTR_SPARSE; |
2122 | | |
2123 | | /* SPECIAL CASE |
2124 | | * We are in non-res section, so we know this |
2125 | | * isn't $STD_INFO and $FNAME |
2126 | | * |
2127 | | * When we are processing a non-base entry, we may |
2128 | | * find an attribute with an id of 0 and it is an |
2129 | | * extension of a previous run (i.e. non-zero start VCN) |
2130 | | * |
2131 | | * We will lookup if we already have such an attribute |
2132 | | * and get its ID |
2133 | | * |
2134 | | * We could also check for a start_vcn if this does |
2135 | | * not fix the problem. |
2136 | | * |
2137 | | * NOTE: This should not be needed now that TSK assigns |
2138 | | * unique ID values to the extended attributes. |
2139 | | */ |
2140 | 0 | if (id_new == 0) { |
2141 | 0 | int cnt, i; |
2142 | | |
2143 | | // cycle through the attributes |
2144 | 0 | cnt = tsk_fs_file_attr_getsize(fs_file); |
2145 | 0 | for (i = 0; i < cnt; i++) { |
2146 | |
|
2147 | 0 | const TSK_FS_ATTR *fs_attr2 = |
2148 | 0 | tsk_fs_file_attr_get_idx(fs_file, i); |
2149 | 0 | if (!fs_attr2) |
2150 | 0 | continue; |
2151 | | |
2152 | | /* We found an attribute with the same name and type */ |
2153 | 0 | if (fs_attr2->type == type) { |
2154 | 0 | if (((name[0] == '\0') && (fs_attr2->name == NULL)) |
2155 | 0 | || ((fs_attr2->name) |
2156 | 0 | && (strcmp(fs_attr2->name, name) == 0))) { |
2157 | 0 | id_new = fs_attr2->id; |
2158 | 0 | if (tsk_verbose) |
2159 | 0 | tsk_fprintf(stderr, |
2160 | 0 | "ntfs_proc_attrseq: Updating id from 0 to %" |
2161 | 0 | PRIu16 "\n", id_new); |
2162 | 0 | break; |
2163 | 0 | } |
2164 | 0 | } |
2165 | 0 | } |
2166 | 0 | } |
2167 | | |
2168 | | /* the compression unit size is stored in the header |
2169 | | * it is stored as the power of 2 (if it is not 0) |
2170 | | */ |
2171 | 0 | if (tsk_getu16(fs->endian, attr->c.nr.compusize) > 16) { |
2172 | | /* 64k is the maximum compression unit size */ |
2173 | 0 | tsk_error_reset(); |
2174 | 0 | tsk_error_set_errno(TSK_ERR_FS_CORRUPT); |
2175 | 0 | tsk_error_set_errstr("ntfs_proc_attrseq: Compression unit size 2^%d too large", |
2176 | 0 | tsk_getu16(fs->endian, attr->c.nr.compusize)); |
2177 | 0 | if (fs_attr_run) { |
2178 | 0 | tsk_fs_attr_run_free(fs_attr_run); |
2179 | 0 | fs_attr_run = NULL; |
2180 | 0 | } |
2181 | 0 | return TSK_COR; |
2182 | 0 | } |
2183 | | |
2184 | 0 | if (tsk_getu16(fs->endian, attr->c.nr.compusize) > 0) { |
2185 | 0 | compsize = |
2186 | 0 | 1 << (tsk_getu16(fs->endian, attr->c.nr.compusize)); |
2187 | 0 | } |
2188 | 0 | else { |
2189 | 0 | compsize = 0; |
2190 | | /* if this is 0, be sure to cancel out the COMP flag. |
2191 | | * This occurs when we process an extended attribute |
2192 | | * that has compressed data -- the attributes in the |
2193 | | * latter MFT entries do not have compsize set. |
2194 | | */ |
2195 | 0 | if (data_flag & TSK_FS_ATTR_COMP) { |
2196 | 0 | if (tsk_verbose) |
2197 | 0 | fprintf(stderr, |
2198 | 0 | "ntfs_proc_attrseq: Clearing compression setting for attribute %" |
2199 | 0 | PRIuINUM "-%d because compsize is 0\n", |
2200 | 0 | fs_file->meta->addr, type); |
2201 | 0 | data_flag &= ~TSK_FS_ATTR_COMP; |
2202 | 0 | } |
2203 | 0 | } |
2204 | | |
2205 | | /* Add the run to the list */ |
2206 | | // see if this attribute has already been partially defined |
2207 | | // @@@ This is bad design, we are casting away the const... |
2208 | 0 | fs_attr = |
2209 | 0 | (TSK_FS_ATTR *) tsk_fs_attrlist_get_id(fs_file->meta->attr, |
2210 | 0 | (TSK_FS_ATTR_TYPE_ENUM) type, id_new); |
2211 | 0 | if (fs_attr == NULL) { |
2212 | 0 | uint64_t ssize; // size |
2213 | 0 | uint64_t alen; // allocated length |
2214 | |
|
2215 | 0 | if ((fs_attr = |
2216 | 0 | tsk_fs_attrlist_getnew(fs_file->meta->attr, |
2217 | 0 | TSK_FS_ATTR_RES)) == NULL) { |
2218 | 0 | tsk_error_errstr2_concat(" - proc_attrseq: getnew"); |
2219 | | // JRB: Coverity found leak. |
2220 | 0 | if (fs_attr_run) { |
2221 | 0 | tsk_fs_attr_run_free(fs_attr_run); |
2222 | 0 | fs_attr_run = NULL; |
2223 | 0 | } |
2224 | 0 | return TSK_ERR; |
2225 | 0 | } |
2226 | | |
2227 | 0 | ssize = tsk_getu64(fs->endian, attr->c.nr.ssize); |
2228 | | /* This can happen with extended attributes, so |
2229 | | * we set it based on what we currently have. |
2230 | | * fs_attr_run can be NULL for $BadClust file. */ |
2231 | 0 | if ((ssize == 0) && (fs_attr_run)) { |
2232 | 0 | TSK_FS_ATTR_RUN *fs_attr_run_tmp; |
2233 | |
|
2234 | 0 | ssize = fs_attr_run->offset * fs->block_size; |
2235 | 0 | fs_attr_run_tmp = fs_attr_run; |
2236 | 0 | while (fs_attr_run_tmp) { |
2237 | 0 | ssize += (fs_attr_run_tmp->len * fs->block_size); |
2238 | 0 | fs_attr_run_tmp = fs_attr_run_tmp->next; |
2239 | 0 | } |
2240 | 0 | } |
2241 | | |
2242 | | // update the meta->size value if this is the default $Data attribute |
2243 | 0 | if ((fs_file->meta->type == TSK_FS_META_TYPE_REG) |
2244 | 0 | && (type == NTFS_ATYPE_DATA) && (name[0] == '\0')) { |
2245 | 0 | fs_file->meta->size = ssize; |
2246 | 0 | } |
2247 | |
|
2248 | 0 | alen = tsk_getu64(fs->endian, attr->c.nr.alen); |
2249 | | /* This can also happen with extended attributes. |
2250 | | * set it to what we know about */ |
2251 | 0 | if (alen == 0) { |
2252 | 0 | alen = ssize; |
2253 | 0 | } |
2254 | |
|
2255 | 0 | if (tsk_fs_attr_set_run(fs_file, fs_attr, |
2256 | 0 | fs_attr_run, name, |
2257 | 0 | (TSK_FS_ATTR_TYPE_ENUM) type, id_new, ssize, |
2258 | 0 | tsk_getu64(fs->endian, attr->c.nr.initsize), |
2259 | 0 | alen, (TSK_FS_ATTR_FLAG_ENUM) data_flag, compsize)) { |
2260 | 0 | tsk_error_errstr2_concat("- proc_attrseq: set run"); |
2261 | | |
2262 | | // If the run wasn't saved to the attribute, free it now |
2263 | 0 | if (fs_attr_run && (fs_attr->nrd.run == NULL)) { |
2264 | 0 | tsk_fs_attr_run_free(fs_attr_run); |
2265 | 0 | fs_attr_run = NULL; |
2266 | 0 | } |
2267 | 0 | return TSK_COR; |
2268 | 0 | } |
2269 | | // fs_file has taken over management of fs_attr_run |
2270 | 0 | fs_attr_run = NULL; |
2271 | | |
2272 | | // set the special functions |
2273 | 0 | if (fs_file->meta->flags & TSK_FS_META_FLAG_COMP) { |
2274 | 0 | fs_attr->w = ntfs_attr_walk_special; |
2275 | 0 | fs_attr->r = ntfs_file_read_special; |
2276 | 0 | } |
2277 | |
|
2278 | 0 | } |
2279 | 0 | else { |
2280 | 0 | if (tsk_fs_attr_add_run(fs, fs_attr, fs_attr_run)) { |
2281 | 0 | tsk_error_errstr2_concat(" - proc_attrseq: put run"); |
2282 | 0 | if (fs_attr_run) { |
2283 | 0 | tsk_fs_attr_run_free(fs_attr_run); |
2284 | 0 | fs_attr_run = NULL; |
2285 | 0 | } |
2286 | 0 | return TSK_COR; |
2287 | 0 | } |
2288 | 0 | } |
2289 | 0 | } |
2290 | | |
2291 | | /* |
2292 | | * Special Cases, where we grab additional information |
2293 | | * regardless if they are resident or not |
2294 | | */ |
2295 | | |
2296 | | /* Standard Information (is always resident) */ |
2297 | 0 | if (type == NTFS_ATYPE_SI) { |
2298 | 0 | uint32_t attr_len = tsk_getu32(fs->endian, attr->len); |
2299 | 0 | uint16_t attr_off = tsk_getu16(fs->endian, attr->c.r.soff); |
2300 | |
|
2301 | 0 | if (attr->res != NTFS_MFT_RES) { |
2302 | 0 | tsk_error_reset(); |
2303 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
2304 | 0 | tsk_error_set_errstr |
2305 | 0 | ("proc_attrseq: Standard Information Attribute is not resident!"); |
2306 | 0 | return TSK_COR; |
2307 | 0 | } |
2308 | 0 | if ((attr_off < 16) || (attr_off >= attr_len)) { |
2309 | 0 | tsk_error_reset(); |
2310 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
2311 | 0 | tsk_error_set_errstr |
2312 | 0 | ("proc_attrseq: resident data offset of Standard Information Attribute is out of bounds!"); |
2313 | 0 | return TSK_COR; |
2314 | 0 | } |
2315 | | // A Standard Information Attribute can be 48 or 72 bytes in size (ntfs_attr_si is 72) |
2316 | 0 | if ((attr_len < 48) || (attr_off > attr_len - 48)) { |
2317 | 0 | tsk_error_reset(); |
2318 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
2319 | 0 | tsk_error_set_errstr |
2320 | 0 | ("proc_attrseq: resident data of Standard Information Attribute is too small!"); |
2321 | 0 | return TSK_COR; |
2322 | 0 | } |
2323 | 0 | ntfs_attr_si *si = (ntfs_attr_si *) ((uintptr_t) attr + attr_off); |
2324 | |
|
2325 | 0 | fs_file->meta->mtime = |
2326 | 0 | nt2unixtime(tsk_getu64(fs->endian, si->mtime)); |
2327 | 0 | fs_file->meta->mtime_nano = |
2328 | 0 | nt2nano(tsk_getu64(fs->endian, si->mtime)); |
2329 | |
|
2330 | 0 | fs_file->meta->atime = |
2331 | 0 | nt2unixtime(tsk_getu64(fs->endian, si->atime)); |
2332 | 0 | fs_file->meta->atime_nano = |
2333 | 0 | nt2nano(tsk_getu64(fs->endian, si->atime)); |
2334 | |
|
2335 | 0 | fs_file->meta->ctime = |
2336 | 0 | nt2unixtime(tsk_getu64(fs->endian, si->ctime)); |
2337 | 0 | fs_file->meta->ctime_nano = |
2338 | 0 | nt2nano(tsk_getu64(fs->endian, si->ctime)); |
2339 | |
|
2340 | 0 | fs_file->meta->crtime = |
2341 | 0 | nt2unixtime(tsk_getu64(fs->endian, si->crtime)); |
2342 | 0 | fs_file->meta->crtime_nano = |
2343 | 0 | nt2nano(tsk_getu64(fs->endian, si->crtime)); |
2344 | |
|
2345 | 0 | fs_file->meta->uid = tsk_getu32(fs->endian, si->own_id); |
2346 | 0 | fs_file->meta->mode = (TSK_FS_META_MODE_ENUM) |
2347 | 0 | (fs_file->meta->mode | TSK_FS_META_MODE_IXUSR | |
2348 | 0 | TSK_FS_META_MODE_IXGRP | TSK_FS_META_MODE_IXOTH); |
2349 | 0 | if ((tsk_getu32(fs->endian, si->dos) & NTFS_SI_RO) == 0) |
2350 | 0 | fs_file->meta->mode = (TSK_FS_META_MODE_ENUM) |
2351 | 0 | (fs_file->meta->mode | TSK_FS_META_MODE_IRUSR | |
2352 | 0 | TSK_FS_META_MODE_IRGRP | TSK_FS_META_MODE_IROTH); |
2353 | 0 | if ((tsk_getu32(fs->endian, si->dos) & NTFS_SI_HID) == 0) |
2354 | 0 | fs_file->meta->mode = (TSK_FS_META_MODE_ENUM) |
2355 | 0 | (fs_file->meta->mode | TSK_FS_META_MODE_IWUSR | |
2356 | 0 | TSK_FS_META_MODE_IWGRP | TSK_FS_META_MODE_IWOTH); |
2357 | 0 | } |
2358 | | |
2359 | | /* File Name (always resident) */ |
2360 | 0 | else if (type == NTFS_ATYPE_FNAME) { |
2361 | 0 | uint32_t attr_len = tsk_getu32(fs->endian, attr->len); |
2362 | 0 | uint16_t attr_off = tsk_getu16(fs->endian, attr->c.r.soff); |
2363 | |
|
2364 | 0 | if (attr->res != NTFS_MFT_RES) { |
2365 | 0 | tsk_error_reset(); |
2366 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
2367 | 0 | tsk_error_set_errstr |
2368 | 0 | ("proc_attr_seq: File Name Attribute is not resident!"); |
2369 | 0 | return TSK_COR; |
2370 | 0 | } |
2371 | 0 | if ((attr_off < 16) || (attr_off >= attr_len)) { |
2372 | 0 | tsk_error_reset(); |
2373 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
2374 | 0 | tsk_error_set_errstr |
2375 | 0 | ("proc_attrseq: resident data offset of File Name Attribute is out of bounds!"); |
2376 | 0 | return TSK_COR; |
2377 | 0 | } |
2378 | | // A File Name Attribute should be at least 66 bytes in size |
2379 | 0 | if ((attr_len < 66) || (attr_off > attr_len - 66)) { |
2380 | 0 | tsk_error_reset(); |
2381 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
2382 | 0 | tsk_error_set_errstr |
2383 | 0 | ("proc_attrseq: resident data of File Name Attribute is too small!"); |
2384 | 0 | return TSK_COR; |
2385 | 0 | } |
2386 | 0 | ntfs_attr_fname *fname = (ntfs_attr_fname *) ((uintptr_t) attr + attr_off); |
2387 | 0 | if (fname->nspace == NTFS_FNAME_DOS) { |
2388 | 0 | continue; |
2389 | 0 | } |
2390 | | |
2391 | 0 | fs_file->meta->time2.ntfs.fn_mtime = |
2392 | 0 | nt2unixtime(tsk_getu64(fs->endian, fname->mtime)); |
2393 | 0 | fs_file->meta->time2.ntfs.fn_mtime_nano = |
2394 | 0 | nt2nano(tsk_getu64(fs->endian, fname->mtime)); |
2395 | |
|
2396 | 0 | fs_file->meta->time2.ntfs.fn_atime = |
2397 | 0 | nt2unixtime(tsk_getu64(fs->endian, fname->atime)); |
2398 | 0 | fs_file->meta->time2.ntfs.fn_atime_nano = |
2399 | 0 | nt2nano(tsk_getu64(fs->endian, fname->atime)); |
2400 | |
|
2401 | 0 | fs_file->meta->time2.ntfs.fn_ctime = |
2402 | 0 | nt2unixtime(tsk_getu64(fs->endian, fname->ctime)); |
2403 | 0 | fs_file->meta->time2.ntfs.fn_ctime_nano = |
2404 | 0 | nt2nano(tsk_getu64(fs->endian, fname->ctime)); |
2405 | |
|
2406 | 0 | fs_file->meta->time2.ntfs.fn_crtime = |
2407 | 0 | nt2unixtime(tsk_getu64(fs->endian, fname->crtime)); |
2408 | 0 | fs_file->meta->time2.ntfs.fn_crtime_nano = |
2409 | 0 | nt2nano(tsk_getu64(fs->endian, fname->crtime)); |
2410 | |
|
2411 | 0 | fs_file->meta->time2.ntfs.fn_id = id; |
2412 | |
|
2413 | 0 | TSK_FS_META_NAME_LIST *fs_name; |
2414 | | |
2415 | | /* Seek to the end of the fs_name structures in TSK_FS_META */ |
2416 | 0 | if (fs_file->meta->name2) { |
2417 | 0 | for (fs_name = fs_file->meta->name2; |
2418 | 0 | (fs_name) && (fs_name->next != NULL); |
2419 | 0 | fs_name = fs_name->next) { |
2420 | 0 | } |
2421 | | |
2422 | | /* add to the end of the existing list */ |
2423 | 0 | fs_name->next = (TSK_FS_META_NAME_LIST *) |
2424 | 0 | tsk_malloc(sizeof(TSK_FS_META_NAME_LIST)); |
2425 | 0 | if (fs_name->next == NULL) { |
2426 | 0 | return TSK_ERR; |
2427 | 0 | } |
2428 | 0 | fs_name = fs_name->next; |
2429 | 0 | fs_name->next = NULL; |
2430 | 0 | } |
2431 | 0 | else { |
2432 | | /* First name, so we start a list */ |
2433 | 0 | fs_file->meta->name2 = fs_name = (TSK_FS_META_NAME_LIST *) |
2434 | 0 | tsk_malloc(sizeof(TSK_FS_META_NAME_LIST)); |
2435 | 0 | if (fs_name == NULL) { |
2436 | 0 | return TSK_ERR; |
2437 | 0 | } |
2438 | 0 | fs_name->next = NULL; |
2439 | 0 | } |
2440 | 0 | if (fname->nlen > attr_len - 66) { |
2441 | 0 | tsk_error_reset(); |
2442 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
2443 | 0 | tsk_error_set_errstr |
2444 | 0 | ("proc_attrseq: invalid name value size out of bounds!"); |
2445 | 0 | return TSK_COR; |
2446 | 0 | } |
2447 | 0 | UTF16 *name16 = (UTF16 *) & fname->name; |
2448 | 0 | UTF8 *name8 = (UTF8 *) fs_name->name; |
2449 | |
|
2450 | 0 | retVal = |
2451 | 0 | tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, |
2452 | 0 | (UTF16 *) ((uintptr_t) name16 + |
2453 | 0 | fname->nlen * 2), |
2454 | 0 | &name8, |
2455 | 0 | (UTF8 *) ((uintptr_t) name8 + |
2456 | 0 | sizeof(fs_name->name)), TSKlenientConversion); |
2457 | 0 | if (retVal != TSKconversionOK) { |
2458 | 0 | if (tsk_verbose) |
2459 | 0 | tsk_fprintf(stderr, |
2460 | 0 | "proc_attr_seq: Error converting NTFS name in $FNAME to UTF8: %d", |
2461 | 0 | retVal); |
2462 | 0 | *name8 = '\0'; |
2463 | 0 | } |
2464 | | /* Make sure it is NULL Terminated */ |
2465 | 0 | else if ((uintptr_t) name8 >= |
2466 | 0 | (uintptr_t) fs_name->name + sizeof(fs_name->name)) |
2467 | 0 | fs_name->name[sizeof(fs_name->name) - 1] = '\0'; |
2468 | 0 | else |
2469 | 0 | *name8 = '\0'; |
2470 | |
|
2471 | 0 | fs_name->par_inode = tsk_getu48(fs->endian, fname->par_ref); |
2472 | 0 | fs_name->par_seq = tsk_getu16(fs->endian, fname->par_seq); |
2473 | 0 | } |
2474 | | |
2475 | | /* If this is an attribute list than we need to process |
2476 | | * it to get the list of other entries to read. But, because |
2477 | | * of the wierd scenario of the $MFT having an attribute list |
2478 | | * and not knowing where the other MFT entires are yet, we wait |
2479 | | * until the end of the attrseq to processes the list and then |
2480 | | * we should have the $Data attribute loaded |
2481 | | */ |
2482 | 0 | else if (type == NTFS_ATYPE_ATTRLIST) { |
2483 | 0 | if (fs_attr_attrl) { |
2484 | 0 | tsk_error_reset(); |
2485 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
2486 | 0 | tsk_error_set_errstr |
2487 | 0 | ("Multiple instances of attribute lists in the same MFT\n" |
2488 | 0 | "I didn't realize that could happen, contact the developers"); |
2489 | 0 | return TSK_ERR; |
2490 | 0 | } |
2491 | 0 | fs_attr_attrl = tsk_fs_attrlist_get_id(fs_file->meta->attr, |
2492 | 0 | (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_ATTRLIST, id_new); |
2493 | 0 | if (fs_attr_attrl == NULL) { |
2494 | 0 | tsk_error_errstr2_concat |
2495 | 0 | ("- proc_attrseq: getting attribute list"); |
2496 | 0 | return TSK_ERR; |
2497 | 0 | } |
2498 | 0 | } |
2499 | 0 | } |
2500 | | |
2501 | | |
2502 | | /* Are we currently in the process of loading $MFT? */ |
2503 | 0 | if (ntfs->loading_the_MFT == 1) { |
2504 | | |
2505 | | /* If we don't even have a mini cached version, get it now |
2506 | | * Even if we are not done because of attribute lists, then we |
2507 | | * should at least have the head of the list |
2508 | | */ |
2509 | 0 | if (!ntfs->mft_data) { |
2510 | 0 | int cnt, i; |
2511 | | |
2512 | | // cycle through the attributes |
2513 | 0 | cnt = tsk_fs_file_attr_getsize(fs_file); |
2514 | 0 | for (i = 0; i < cnt; i++) { |
2515 | 0 | const TSK_FS_ATTR *fs_attr = |
2516 | 0 | tsk_fs_file_attr_get_idx(fs_file, i); |
2517 | 0 | if (!fs_attr) |
2518 | 0 | continue; |
2519 | | |
2520 | | // get the default attribute |
2521 | 0 | if ((fs_attr->type == NTFS_ATYPE_DATA) && |
2522 | 0 | (fs_attr->name == NULL)) { |
2523 | 0 | ntfs->mft_data = fs_attr; |
2524 | 0 | break; |
2525 | 0 | } |
2526 | 0 | } |
2527 | | |
2528 | | // @@@ Is this needed here -- maybe it should be only in _open |
2529 | 0 | if (!ntfs->mft_data) { |
2530 | 0 | tsk_error_reset(); |
2531 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
2532 | 0 | tsk_error_set_errstr |
2533 | 0 | ("$Data not found while loading the MFT"); |
2534 | 0 | return TSK_ERR; |
2535 | 0 | } |
2536 | 0 | } |
2537 | | |
2538 | | /* Update the inode count based on the current size |
2539 | | * IF $MFT has an attribute list, this value will increase each |
2540 | | * time |
2541 | | */ |
2542 | 0 | fs->inum_count = ntfs->mft_data->size / ntfs->mft_rsize_b; |
2543 | 0 | fs->last_inum = fs->inum_count - 1; |
2544 | 0 | } |
2545 | | |
2546 | | /* If there was an attribute list, process it now, we wait because |
2547 | | * the list can contain MFT entries that are described in $Data |
2548 | | * of this MFT entry. For example, part of the $DATA attribute |
2549 | | * could follow the ATTRLIST entry, so we read it first and then |
2550 | | * process the attribute list |
2551 | | */ |
2552 | 0 | if (fs_attr_attrl) { |
2553 | 0 | TSK_RETVAL_ENUM retval; |
2554 | 0 | if (a_seen_inum_list != NULL) { |
2555 | 0 | tsk_stack_push(a_seen_inum_list, a_attrinum); |
2556 | 0 | } |
2557 | 0 | if ((retval = ntfs_proc_attrlist(ntfs, fs_file, fs_attr_attrl, a_seen_inum_list)) != TSK_OK) { |
2558 | 0 | return retval; |
2559 | 0 | } |
2560 | 0 | } |
2561 | | |
2562 | 0 | fs_file->meta->attr_state = TSK_FS_META_ATTR_STUDIED; |
2563 | 0 | return TSK_OK; |
2564 | 0 | } |
2565 | | |
2566 | | |
2567 | | |
2568 | | /******** Attribute List Action and Function ***********/ |
2569 | | |
2570 | | |
2571 | | |
2572 | | /* |
2573 | | * Attribute lists are used when all of the attribute headers can not |
2574 | | * fit into one MFT entry. This contains an entry for every attribute |
2575 | | * and where they are located. We process this to get the locations |
2576 | | * and then call proc_attrseq on each of those, which adds the data |
2577 | | * to the fs_file structure. |
2578 | | * |
2579 | | * @param ntfs File system being analyzed |
2580 | | * @param fs_file Main file that will have attributes added to it. |
2581 | | * @param fs_attr_attrlist Attrlist attribute that needs to be parsed. |
2582 | | * @param a_seen_inum_list List of MFT entries (inums) previously |
2583 | | * processed for this file or NULL. |
2584 | | * |
2585 | | * @returns status of error, corrupt, or OK |
2586 | | */ |
2587 | | static TSK_RETVAL_ENUM |
2588 | | ntfs_proc_attrlist(NTFS_INFO * ntfs, |
2589 | | TSK_FS_FILE * fs_file, const TSK_FS_ATTR * fs_attr_attrlist, TSK_STACK * processed_inum_list) |
2590 | 0 | { |
2591 | 0 | ntfs_attrlist *list; |
2592 | 0 | char *buf; |
2593 | 0 | uintptr_t endaddr; |
2594 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info; |
2595 | 0 | ntfs_mft *mft; |
2596 | 0 | TSK_FS_LOAD_FILE load_file; |
2597 | 0 | TSK_INUM_T mftToDo[256]; |
2598 | 0 | uint16_t mftToDoCnt = 0; |
2599 | 0 | NTFS_ATTRLIST_MAP *map; |
2600 | 0 | uint16_t nextid = 0; |
2601 | 0 | TSK_STACK * mftSeenList = NULL; |
2602 | 0 | int a; |
2603 | |
|
2604 | 0 | if (tsk_verbose) |
2605 | 0 | tsk_fprintf(stderr, |
2606 | 0 | "ntfs_proc_attrlist: Processing entry %" |
2607 | 0 | PRIuINUM "\n", fs_file->meta->addr); |
2608 | |
|
2609 | 0 | if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) { |
2610 | 0 | return TSK_ERR; |
2611 | 0 | } |
2612 | | |
2613 | 0 | if ((map = |
2614 | 0 | (NTFS_ATTRLIST_MAP *) tsk_malloc(sizeof(NTFS_ATTRLIST_MAP))) == |
2615 | 0 | NULL) { |
2616 | 0 | free(mft); |
2617 | 0 | return TSK_ERR; |
2618 | 0 | } |
2619 | | |
2620 | | /* Clear the contents of the todo buffer */ |
2621 | 0 | memset(mftToDo, 0, sizeof(mftToDo)); |
2622 | | |
2623 | | /* Get a copy of the attribute list stream using the above action */ |
2624 | 0 | load_file.left = load_file.total = (size_t) fs_attr_attrlist->size; |
2625 | 0 | load_file.base = load_file.cur = buf = |
2626 | 0 | (char*) tsk_malloc((size_t) fs_attr_attrlist->size); |
2627 | 0 | if (buf == NULL) { |
2628 | 0 | free(mft); |
2629 | 0 | free(map); |
2630 | 0 | return TSK_ERR; |
2631 | 0 | } |
2632 | 0 | endaddr = (uintptr_t) buf + (uintptr_t) fs_attr_attrlist->size; |
2633 | 0 | if (tsk_fs_attr_walk(fs_attr_attrlist, TSK_FS_FILE_WALK_FLAG_NONE, tsk_fs_load_file_action, |
2634 | 0 | (void *) &load_file)) { |
2635 | 0 | tsk_error_errstr2_concat("- processing attrlist"); |
2636 | 0 | free(mft); |
2637 | 0 | free(buf); |
2638 | 0 | free(map); |
2639 | 0 | return TSK_ERR; |
2640 | 0 | } |
2641 | | |
2642 | | /* this value should be zero, if not then we didn't read all of the |
2643 | | * buffer |
2644 | | */ |
2645 | 0 | if (load_file.left > 0) { |
2646 | 0 | tsk_error_reset(); |
2647 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
2648 | 0 | tsk_error_set_errstr2("processing attrlist of entry %" PRIuINUM, |
2649 | 0 | fs_file->meta->addr); |
2650 | 0 | free(mft); |
2651 | 0 | free(buf); |
2652 | 0 | free(map); |
2653 | 0 | return TSK_ERR; |
2654 | 0 | } |
2655 | | |
2656 | | /* The TSK design requires that each attribute have its own ID. |
2657 | | * Therefore, we need to identify all of the unique attributes |
2658 | | * so that we can assign a unique ID to them. |
2659 | | * In this process, we will also identify the unique MFT entries to |
2660 | | * process. */ |
2661 | 0 | nextid = fs_attr_attrlist->id; // we won't see this entry in the list |
2662 | 0 | for (list = (ntfs_attrlist *) buf; |
2663 | 0 | (list) |
2664 | | // ntfs_attrlist contains the first byte of the name, which might actually be 0-length |
2665 | 0 | && (uintptr_t) list + sizeof(ntfs_attrlist) - 1 <= endaddr |
2666 | 0 | && tsk_getu16(fs->endian, list->len) > 0 |
2667 | 0 | && (uintptr_t) list + tsk_getu16(fs->endian, list->len) <= endaddr |
2668 | 0 | && (uintptr_t) list + sizeof(ntfs_attrlist) - 1 + 2 * list->nlen <= endaddr; |
2669 | 0 | list = |
2670 | 0 | (ntfs_attrlist *) ((uintptr_t) list + tsk_getu16(fs->endian, |
2671 | 0 | list->len))) { |
2672 | 0 | uint8_t found; |
2673 | 0 | int i; |
2674 | |
|
2675 | 0 | TSK_INUM_T mftnum = tsk_getu48(fs->endian, list->file_ref); |
2676 | 0 | uint32_t type = tsk_getu32(fs->endian, list->type); |
2677 | 0 | uint16_t id = tsk_getu16(fs->endian, list->id); |
2678 | |
|
2679 | 0 | if (tsk_verbose) |
2680 | 0 | tsk_fprintf(stderr, |
2681 | 0 | "ntfs_proc_attrlist: mft: %" PRIuINUM |
2682 | 0 | " type %" PRIu32 " id %" PRIu16 |
2683 | 0 | " VCN: %" PRIu64 "\n", mftnum, type, |
2684 | 0 | id, tsk_getu64(fs->endian, list->start_vcn)); |
2685 | | |
2686 | | |
2687 | | // keep track of the biggest ID that we saw. |
2688 | 0 | if (id > nextid) |
2689 | 0 | nextid = id; |
2690 | | |
2691 | | /* First identify the unique attributes. |
2692 | | * we can have duplicate entries at different VCNs. Ignore those. */ |
2693 | 0 | found = 0; |
2694 | 0 | for (i = 0; i < map->num_used; i++) { |
2695 | 0 | if ((map->type[i] == type) |
2696 | 0 | && (memcmp(map->name[i], &list->name, |
2697 | 0 | list->nlen * 2) == 0)) { |
2698 | 0 | found = 1; |
2699 | 0 | break; |
2700 | 0 | } |
2701 | 0 | } |
2702 | | |
2703 | | // add it to the list |
2704 | 0 | if (found == 0) { |
2705 | 0 | map->extMft[map->num_used] = mftnum; |
2706 | 0 | map->type[map->num_used] = type; |
2707 | 0 | map->extId[map->num_used] = id; |
2708 | 0 | memcpy(map->name[map->num_used], &list->name, list->nlen * 2); |
2709 | 0 | if (map->num_used < 255) |
2710 | 0 | map->num_used++; |
2711 | 0 | } |
2712 | | |
2713 | | /* also check the todo list -- skip the base entry |
2714 | | * the goal here is to get a unique list of MFT entries |
2715 | | * to later process. */ |
2716 | 0 | if (mftnum != fs_file->meta->addr) { |
2717 | 0 | found = 0; |
2718 | 0 | for (i = 0; i < mftToDoCnt; i++) { |
2719 | 0 | if (mftToDo[i] == mftnum) { |
2720 | 0 | found = 1; |
2721 | 0 | break; |
2722 | 0 | } |
2723 | 0 | } |
2724 | 0 | if ((found == 0) && (mftToDoCnt < 256)) { |
2725 | 0 | mftToDo[mftToDoCnt++] = mftnum; |
2726 | 0 | } |
2727 | 0 | } |
2728 | 0 | } |
2729 | | |
2730 | | // update the map and assign unique IDs |
2731 | 0 | for (a = 0; a < map->num_used; a++) { |
2732 | | // skip the base entry attributes -- they have unique attribute IDs |
2733 | 0 | if (map->extMft[a] == fs_file->meta->addr) |
2734 | 0 | continue; |
2735 | 0 | map->newId[a] = ++nextid; |
2736 | 0 | } |
2737 | | |
2738 | | |
2739 | | /* Process the ToDo list & and call ntfs_proc_attr */ |
2740 | 0 | for (a = 0; a < mftToDoCnt; a++) { |
2741 | 0 | TSK_RETVAL_ENUM retval; |
2742 | | |
2743 | | /* Sanity check. */ |
2744 | 0 | if (mftToDo[a] < ntfs->fs_info.first_inum || |
2745 | | // decrement the last_inum because the last value is a special value for the ORPHANS directory |
2746 | 0 | mftToDo[a] > ntfs->fs_info.last_inum - 1 || |
2747 | | // MFT 0 is for $MFT. We had one system that we got a reference to it from parsing an allocated attribute list |
2748 | 0 | mftToDo[a] == 0) { |
2749 | |
|
2750 | 0 | if (tsk_verbose) { |
2751 | | /* this case can easily occur if the attribute list was non-resident and the cluster has been reallocated */ |
2752 | |
|
2753 | 0 | tsk_fprintf(stderr, |
2754 | 0 | "Invalid MFT file reference (%" |
2755 | 0 | PRIuINUM |
2756 | 0 | ") in the unallocated attribute list of MFT %" |
2757 | 0 | PRIuINUM "", mftToDo[a], fs_file->meta->addr); |
2758 | 0 | } |
2759 | 0 | continue; |
2760 | 0 | } |
2761 | | |
2762 | 0 | if ((retval = |
2763 | 0 | ntfs_dinode_lookup(ntfs, (char *) mft, |
2764 | 0 | mftToDo[a], 0)) != TSK_OK) { |
2765 | | // if the entry is corrupt, then continue |
2766 | 0 | if (retval == TSK_COR) { |
2767 | 0 | if (tsk_verbose) |
2768 | 0 | tsk_error_print(stderr); |
2769 | 0 | tsk_error_reset(); |
2770 | 0 | continue; |
2771 | 0 | } |
2772 | | |
2773 | 0 | free(mft); |
2774 | 0 | free(map); |
2775 | 0 | free(buf); |
2776 | 0 | if (mftSeenList != NULL) |
2777 | 0 | tsk_stack_free(mftSeenList); |
2778 | 0 | tsk_error_errstr2_concat(" - proc_attrlist"); |
2779 | 0 | return TSK_ERR; |
2780 | 0 | } |
2781 | | |
2782 | | /* verify that this entry refers to the original one */ |
2783 | 0 | if (tsk_getu48(fs->endian, mft->base_ref) != fs_file->meta->addr) { |
2784 | | |
2785 | | /* Before we raise alarms, check if the original was |
2786 | | * unallocated. If so, then the list entry could |
2787 | | * have been reallocated, so we will just ignore it |
2788 | | */ |
2789 | 0 | if (((tsk_getu16(fs->endian, |
2790 | 0 | mft->flags) & NTFS_MFT_INUSE) == 0) |
2791 | 0 | || (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) { |
2792 | 0 | continue; |
2793 | 0 | } |
2794 | 0 | else { |
2795 | 0 | tsk_error_reset(); |
2796 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
2797 | 0 | tsk_error_set_errstr("ntfs_proc_attrlist: MFT %" PRIuINUM |
2798 | 0 | " is not an attribute list for %" |
2799 | 0 | PRIuINUM |
2800 | 0 | " (base file ref = %" PRIuINUM ")", |
2801 | 0 | mftToDo[a], |
2802 | 0 | fs_file->meta->addr, |
2803 | 0 | tsk_getu48(fs->endian, mft->base_ref)); |
2804 | 0 | free(mft); |
2805 | 0 | free(map); |
2806 | 0 | free(buf); |
2807 | 0 | if (mftSeenList != NULL) |
2808 | 0 | tsk_stack_free(mftSeenList); |
2809 | 0 | return TSK_COR; |
2810 | 0 | } |
2811 | 0 | } |
2812 | | |
2813 | | // bounds check |
2814 | 0 | if (tsk_getu16(fs->endian, mft->attr_off) > ntfs->mft_rsize_b) { |
2815 | 0 | if (tsk_verbose) |
2816 | 0 | tsk_fprintf(stderr, "ntfs_proc_attrlist: corrupt MFT entry attribute offsets\n"); |
2817 | 0 | continue; |
2818 | 0 | } |
2819 | | |
2820 | | /* Process the attribute seq for this MFT entry and add them |
2821 | | * to the TSK_FS_META structure |
2822 | | */ |
2823 | 0 | if (processed_inum_list != NULL && tsk_stack_find(processed_inum_list, mftToDo[a])) { |
2824 | 0 | tsk_error_reset(); |
2825 | 0 | tsk_error_set_errno(TSK_ERR_FS_CORRUPT); |
2826 | 0 | tsk_error_set_errstr("ntfs_proc_attrlist: MFT %" PRIuINUM |
2827 | 0 | " seen in more than one attribute list for %" |
2828 | 0 | PRIuINUM |
2829 | 0 | " (base file ref = %" PRIuINUM ")", |
2830 | 0 | mftToDo[a], |
2831 | 0 | fs_file->meta->addr, |
2832 | 0 | tsk_getu48(fs->endian, mft->base_ref)); |
2833 | 0 | free(mft); |
2834 | 0 | free(map); |
2835 | 0 | free(buf); |
2836 | 0 | if (mftSeenList != NULL) |
2837 | 0 | tsk_stack_free(mftSeenList); |
2838 | 0 | return TSK_COR; |
2839 | 0 | } |
2840 | | |
2841 | 0 | if (processed_inum_list == NULL) { |
2842 | | /* |
2843 | | * Create a stack to keep track of inums already seen. |
2844 | | * The local mftSeenList variable is used to keep track |
2845 | | * of which iteration created the stack so that it can |
2846 | | * be correctly freed later. |
2847 | | */ |
2848 | 0 | processed_inum_list = mftSeenList = tsk_stack_create(); |
2849 | 0 | } |
2850 | |
|
2851 | 0 | if ((retval = |
2852 | 0 | ntfs_proc_attrseq(ntfs, fs_file, (ntfs_attr *) ((uintptr_t) |
2853 | 0 | mft + tsk_getu16(fs->endian, mft->attr_off)), |
2854 | 0 | ntfs->mft_rsize_b - tsk_getu16(fs->endian, |
2855 | 0 | mft->attr_off), mftToDo[a], map, processed_inum_list)) != TSK_OK) { |
2856 | |
|
2857 | 0 | if (retval == TSK_COR) { |
2858 | 0 | if (tsk_verbose) |
2859 | 0 | tsk_error_print(stderr); |
2860 | 0 | tsk_error_reset(); |
2861 | 0 | continue; |
2862 | 0 | } |
2863 | 0 | tsk_error_errstr2_concat("- proc_attrlist"); |
2864 | 0 | free(mft); |
2865 | 0 | free(map); |
2866 | 0 | free(buf); |
2867 | 0 | if (mftSeenList != NULL) |
2868 | 0 | tsk_stack_free(mftSeenList); |
2869 | 0 | return TSK_ERR; |
2870 | 0 | } |
2871 | 0 | } |
2872 | | |
2873 | 0 | free(mft); |
2874 | 0 | free(map); |
2875 | 0 | free(buf); |
2876 | 0 | if (mftSeenList != NULL) |
2877 | 0 | tsk_stack_free(mftSeenList); |
2878 | 0 | return TSK_OK; |
2879 | 0 | } |
2880 | | |
2881 | | |
2882 | | |
2883 | | /** |
2884 | | * Copy the MFT entry saved in a_buf to the generic structure. |
2885 | | * |
2886 | | * @param ntfs File system structure that contains entry to copy |
2887 | | * @param fs_file Structure to copy processed data to. |
2888 | | * @param a_buf MFT structure to copy from. Must be of size NTFS_INFO.mft_rsize_b |
2889 | | * @param a_mnum MFT entry address |
2890 | | * |
2891 | | * @returns error code |
2892 | | */ |
2893 | | static TSK_RETVAL_ENUM |
2894 | | ntfs_dinode_copy(NTFS_INFO * ntfs, TSK_FS_FILE * a_fs_file, char *a_buf, |
2895 | | TSK_INUM_T a_mnum) |
2896 | 0 | { |
2897 | 0 | ntfs_attr *attr; |
2898 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info; |
2899 | 0 | TSK_RETVAL_ENUM retval; |
2900 | 0 | ntfs_mft *mft = (ntfs_mft *) a_buf; |
2901 | |
|
2902 | 0 | if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) { |
2903 | 0 | tsk_error_reset(); |
2904 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
2905 | 0 | tsk_error_set_errstr("ntfs_dinode_copy: NULL fs_file given"); |
2906 | 0 | return TSK_ERR; |
2907 | 0 | } |
2908 | | |
2909 | | /* if the attributes list has been used previously, then make sure the |
2910 | | * flags are cleared |
2911 | | */ |
2912 | 0 | if (a_fs_file->meta->attr) { |
2913 | 0 | tsk_fs_attrlist_markunused(a_fs_file->meta->attr); |
2914 | 0 | } |
2915 | 0 | else { |
2916 | 0 | a_fs_file->meta->attr = tsk_fs_attrlist_alloc(); |
2917 | 0 | if (a_fs_file->meta->attr == NULL) |
2918 | 0 | return TSK_ERR; |
2919 | 0 | } |
2920 | 0 | a_fs_file->meta->attr_state = TSK_FS_META_ATTR_EMPTY; |
2921 | | |
2922 | | /* If there are any name structures allocated, then free 'em */ |
2923 | 0 | if (a_fs_file->meta->name2) { |
2924 | 0 | TSK_FS_META_NAME_LIST *fs_name1, *fs_name2; |
2925 | 0 | fs_name1 = a_fs_file->meta->name2; |
2926 | |
|
2927 | 0 | while (fs_name1) { |
2928 | 0 | fs_name2 = fs_name1->next; |
2929 | 0 | free(fs_name1); |
2930 | 0 | fs_name1 = fs_name2; |
2931 | 0 | } |
2932 | 0 | a_fs_file->meta->name2 = NULL; |
2933 | 0 | } |
2934 | | |
2935 | | /* Set the a_fs_file->meta values from mft */ |
2936 | 0 | a_fs_file->meta->nlink = tsk_getu16(fs->endian, mft->link); |
2937 | 0 | a_fs_file->meta->seq = tsk_getu16(fs->endian, mft->seq); |
2938 | 0 | a_fs_file->meta->addr = a_mnum; |
2939 | | |
2940 | | /* Set the mode for file or directory */ |
2941 | 0 | if (tsk_getu16(fs->endian, mft->flags) & NTFS_MFT_DIR) |
2942 | 0 | a_fs_file->meta->type = TSK_FS_META_TYPE_DIR; |
2943 | 0 | else |
2944 | 0 | a_fs_file->meta->type = TSK_FS_META_TYPE_REG; |
2945 | 0 | a_fs_file->meta->mode = TSK_FS_META_MODE_UNSPECIFIED; // will be set by proc_attrseq |
2946 | | |
2947 | | /* the following will be changed once we find the correct attribute, |
2948 | | * but initialize them now just in case |
2949 | | */ |
2950 | 0 | a_fs_file->meta->uid = 0; |
2951 | 0 | a_fs_file->meta->gid = 0; |
2952 | 0 | a_fs_file->meta->size = 0; |
2953 | 0 | a_fs_file->meta->mtime = 0; |
2954 | 0 | a_fs_file->meta->mtime_nano = 0; |
2955 | 0 | a_fs_file->meta->atime = 0; |
2956 | 0 | a_fs_file->meta->atime_nano = 0; |
2957 | 0 | a_fs_file->meta->ctime = 0; |
2958 | 0 | a_fs_file->meta->ctime_nano = 0; |
2959 | 0 | a_fs_file->meta->crtime = 0; |
2960 | 0 | a_fs_file->meta->crtime_nano = 0; |
2961 | 0 | a_fs_file->meta->time2.ntfs.fn_mtime = 0; |
2962 | 0 | a_fs_file->meta->time2.ntfs.fn_mtime_nano = 0; |
2963 | 0 | a_fs_file->meta->time2.ntfs.fn_atime = 0; |
2964 | 0 | a_fs_file->meta->time2.ntfs.fn_atime_nano = 0; |
2965 | 0 | a_fs_file->meta->time2.ntfs.fn_ctime = 0; |
2966 | 0 | a_fs_file->meta->time2.ntfs.fn_ctime_nano = 0; |
2967 | 0 | a_fs_file->meta->time2.ntfs.fn_crtime = 0; |
2968 | 0 | a_fs_file->meta->time2.ntfs.fn_crtime_nano = 0; |
2969 | 0 | a_fs_file->meta->time2.ntfs.fn_id = 0; |
2970 | | |
2971 | | /* add the flags */ |
2972 | 0 | a_fs_file->meta->flags = |
2973 | 0 | ((tsk_getu16(fs->endian, mft->flags) & |
2974 | 0 | NTFS_MFT_INUSE) ? TSK_FS_META_FLAG_ALLOC : |
2975 | 0 | TSK_FS_META_FLAG_UNALLOC); |
2976 | | |
2977 | | |
2978 | | /* Process the attribute sequence to fill in the fs_meta->attr |
2979 | | * list and the other info such as size and times |
2980 | | */ |
2981 | 0 | if (tsk_getu16(fs->endian, mft->attr_off) > ntfs->mft_rsize_b) { |
2982 | 0 | tsk_error_reset(); |
2983 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
2984 | 0 | tsk_error_set_errstr("ntfs_dinode_copy: corrupt MFT entry attribute offsets"); |
2985 | 0 | return TSK_ERR; |
2986 | 0 | } |
2987 | | |
2988 | 0 | attr = |
2989 | 0 | (ntfs_attr *) ((uintptr_t) mft + tsk_getu16(fs->endian, |
2990 | 0 | mft->attr_off)); |
2991 | 0 | if ((retval = ntfs_proc_attrseq(ntfs, a_fs_file, attr, |
2992 | 0 | ntfs->mft_rsize_b - tsk_getu16(fs->endian, |
2993 | 0 | mft->attr_off), a_fs_file->meta->addr, |
2994 | 0 | NULL, NULL)) != TSK_OK) { |
2995 | 0 | return retval; |
2996 | 0 | } |
2997 | | |
2998 | | /* The entry has been 'used' if it has attributes */ |
2999 | | |
3000 | 0 | if ((a_fs_file->meta->attr == NULL) |
3001 | 0 | || (a_fs_file->meta->attr->head == NULL) |
3002 | 0 | || ((a_fs_file->meta->attr->head->flags & TSK_FS_ATTR_INUSE) == 0)) |
3003 | 0 | a_fs_file->meta->flags = (TSK_FS_META_FLAG_ENUM) (a_fs_file->meta->flags | TSK_FS_META_FLAG_UNUSED); |
3004 | 0 | else |
3005 | 0 | a_fs_file->meta->flags = (TSK_FS_META_FLAG_ENUM) (a_fs_file->meta->flags | TSK_FS_META_FLAG_USED); |
3006 | |
|
3007 | 0 | return TSK_OK; |
3008 | 0 | } |
3009 | | |
3010 | | |
3011 | | |
3012 | | /** \internal |
3013 | | * Load the attributes. In NTFS, the attributes are already loaded |
3014 | | * so return error values based on current state. |
3015 | | * @param a_fs_file File to load attributes for. |
3016 | | * @returns 1 on error |
3017 | | */ |
3018 | | static uint8_t |
3019 | | ntfs_load_attrs(TSK_FS_FILE * a_fs_file) |
3020 | 0 | { |
3021 | 0 | if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) { |
3022 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
3023 | 0 | tsk_error_set_errstr("ntfs_load_attrs: called with NULL pointers"); |
3024 | 0 | return 1; |
3025 | 0 | } |
3026 | | |
3027 | | /* Verify the file has attributes */ |
3028 | 0 | if (a_fs_file->meta->attr == NULL) { |
3029 | 0 | if (a_fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) |
3030 | 0 | tsk_error_set_errno(TSK_ERR_FS_RECOVER); |
3031 | 0 | else |
3032 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
3033 | 0 | tsk_error_set_errstr("ntfs_load_attrs: attributes are NULL"); |
3034 | 0 | return 1; |
3035 | 0 | } |
3036 | 0 | return 0; |
3037 | 0 | } |
3038 | | |
3039 | | /** |
3040 | | * Read an MFT entry and save it in the generic TSK_FS_META format. |
3041 | | * |
3042 | | * @param fs File system to read from. |
3043 | | * @param mftnum Address of mft entry to read |
3044 | | * @returns 1 on error |
3045 | | */ |
3046 | | static uint8_t |
3047 | | ntfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file, |
3048 | | TSK_INUM_T mftnum) |
3049 | 0 | { |
3050 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) fs; |
3051 | 0 | char *mft; |
3052 | 0 | uint8_t allocedMeta = 0; |
3053 | | |
3054 | | // clean up any error messages that are lying around |
3055 | 0 | tsk_error_reset(); |
3056 | |
|
3057 | 0 | if (a_fs_file == NULL) { |
3058 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
3059 | 0 | tsk_error_set_errstr("ntfs_inode_lookup: fs_file is NULL"); |
3060 | 0 | return 1; |
3061 | 0 | } |
3062 | | |
3063 | 0 | if (a_fs_file->meta == NULL) { |
3064 | 0 | a_fs_file->meta = tsk_fs_meta_alloc(NTFS_FILE_CONTENT_LEN); |
3065 | 0 | if (a_fs_file->meta == NULL) |
3066 | 0 | return 1; |
3067 | 0 | allocedMeta = 1; |
3068 | 0 | } |
3069 | 0 | else { |
3070 | 0 | tsk_fs_meta_reset(a_fs_file->meta); |
3071 | 0 | } |
3072 | | |
3073 | | // see if they are looking for the special "orphans" directory |
3074 | 0 | if (mftnum == TSK_FS_ORPHANDIR_INUM(fs)) { |
3075 | 0 | if (tsk_fs_dir_make_orphan_dir_meta(fs, a_fs_file->meta)) |
3076 | 0 | return 1; |
3077 | 0 | else |
3078 | 0 | return 0; |
3079 | 0 | } |
3080 | | |
3081 | 0 | if ((mft = (char *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) { |
3082 | 0 | return 1; |
3083 | 0 | } |
3084 | | |
3085 | | /* Lookup inode and store it in the ntfs structure */ |
3086 | 0 | if (ntfs_dinode_lookup(ntfs, mft, mftnum, & (a_fs_file->meta->start_of_inode)) != TSK_OK) { |
3087 | 0 | free(mft); |
3088 | 0 | return 1; |
3089 | 0 | } |
3090 | | |
3091 | | /* Copy the structure in ntfs to generic a_fs_file->meta */ |
3092 | 0 | if (ntfs_dinode_copy(ntfs, a_fs_file, mft, mftnum) != TSK_OK) { |
3093 | 0 | free(mft); |
3094 | 0 | return 1; |
3095 | 0 | } |
3096 | | |
3097 | | /* Check if the metadata is the same sequence as the name - if it was already set. |
3098 | | * Note that this is not as efficient and elegant as desired, but works for now. |
3099 | | * Better design would be to pass sequence into dinode_lookup and have a more |
3100 | | * obvious way to pass the desired sequence in. fs_dir_walk_lcl sets the name |
3101 | | * before calling this, which motivated this quick fix. */ |
3102 | 0 | if ((a_fs_file->name != NULL) && (a_fs_file->name->meta_addr == mftnum)) { |
3103 | | |
3104 | | /* NTFS Updates the sequence when an entry is deleted and not when |
3105 | | * it is allocated. So, if we have a deleted MFT entry, then use |
3106 | | * its previous sequence number to compare with the name so that we |
3107 | | * still match them up (until the entry is allocated again). */ |
3108 | 0 | uint16_t seqToCmp = a_fs_file->meta->seq; |
3109 | 0 | if (a_fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) { |
3110 | 0 | if (a_fs_file->meta->seq > 0) |
3111 | 0 | seqToCmp--; |
3112 | 0 | } |
3113 | |
|
3114 | 0 | if (a_fs_file->name->meta_seq != seqToCmp) { |
3115 | 0 | if (allocedMeta) { |
3116 | 0 | tsk_fs_meta_close(a_fs_file->meta); |
3117 | 0 | a_fs_file->meta = NULL; |
3118 | 0 | } |
3119 | 0 | else { |
3120 | 0 | tsk_fs_meta_reset(a_fs_file->meta); |
3121 | 0 | } |
3122 | 0 | } |
3123 | 0 | } |
3124 | |
|
3125 | 0 | free(mft); |
3126 | 0 | return 0; |
3127 | 0 | } |
3128 | | |
3129 | | |
3130 | | |
3131 | | |
3132 | | /********************************************************************** |
3133 | | * |
3134 | | * Load special MFT structures into the NTFS_INFO structure |
3135 | | * |
3136 | | **********************************************************************/ |
3137 | | |
3138 | | /* The attrdef structure defines the types of attributes and gives a |
3139 | | * name value to the type number. |
3140 | | * |
3141 | | * We currently do not use this during the analysis (Because it has not |
3142 | | * historically changed, but we do display it in fsstat |
3143 | | * |
3144 | | * Return 1 on error and 0 on success |
3145 | | */ |
3146 | | static uint8_t |
3147 | | ntfs_load_attrdef(NTFS_INFO * ntfs) |
3148 | 0 | { |
3149 | 0 | const TSK_FS_ATTR *fs_attr; |
3150 | 0 | TSK_FS_INFO *fs = &ntfs->fs_info; |
3151 | 0 | TSK_FS_LOAD_FILE load_file; |
3152 | | |
3153 | | /* if already loaded, return now */ |
3154 | 0 | if (ntfs->attrdef) |
3155 | 0 | return 1; |
3156 | | |
3157 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
3158 | 0 | tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_ATTR), |
3159 | 0 | tsk_fs_file_close |
3160 | 0 | }; |
3161 | |
|
3162 | 0 | if (!fs_file) |
3163 | 0 | return 1; |
3164 | | |
3165 | 0 | fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM |
3166 | 0 | ) NTFS_ATYPE_DATA); |
3167 | 0 | if (!fs_attr) { |
3168 | | //("Data attribute not found in $Attr"); |
3169 | 0 | return 1; |
3170 | 0 | } |
3171 | | |
3172 | | // @@@ We need to do a sanity check on the size of fs_attr->size |
3173 | | |
3174 | | /* Get a copy of the attribute list stream using the above action */ |
3175 | 0 | load_file.left = load_file.total = (size_t) fs_attr->size; |
3176 | 0 | load_file.base = load_file.cur = (char*) tsk_malloc((size_t) fs_attr->size); |
3177 | 0 | if (load_file.cur == NULL) { |
3178 | 0 | return 1; |
3179 | 0 | } |
3180 | 0 | ntfs->attrdef = (ntfs_attrdef *) load_file.base; |
3181 | |
|
3182 | 0 | if (tsk_fs_attr_walk(fs_attr, |
3183 | 0 | TSK_FS_FILE_WALK_FLAG_NONE, tsk_fs_load_file_action, (void *) &load_file)) { |
3184 | 0 | tsk_error_errstr2_concat(" - load_attrdef"); |
3185 | 0 | free(ntfs->attrdef); |
3186 | 0 | ntfs->attrdef = NULL; |
3187 | 0 | return 1; |
3188 | 0 | } |
3189 | 0 | else if (load_file.left > 0) { |
3190 | 0 | tsk_error_reset(); |
3191 | 0 | tsk_error_set_errno(TSK_ERR_FS_FWALK); |
3192 | 0 | tsk_error_set_errstr |
3193 | 0 | ("load_attrdef: space still left after walking $Attr data"); |
3194 | 0 | free(ntfs->attrdef); |
3195 | 0 | ntfs->attrdef = NULL; |
3196 | 0 | return 1; |
3197 | 0 | } |
3198 | | |
3199 | 0 | ntfs->attrdef_len = (size_t) fs_attr->size; |
3200 | 0 | return 0; |
3201 | 0 | } |
3202 | | |
3203 | | |
3204 | | /* |
3205 | | * return the name of the attribute type. If the attribute has not |
3206 | | * been loaded yet, it will be. |
3207 | | * |
3208 | | * Return 1 on error and 0 on success |
3209 | | */ |
3210 | | uint8_t |
3211 | | ntfs_attrname_lookup(TSK_FS_INFO * fs, uint16_t type, char *name, int len) |
3212 | 0 | { |
3213 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) fs; |
3214 | 0 | ntfs_attrdef *attrdef; |
3215 | 0 | if (!ntfs->attrdef) { |
3216 | 0 | if (ntfs_load_attrdef(ntfs)) |
3217 | 0 | return 1; |
3218 | 0 | } |
3219 | | |
3220 | 0 | attrdef = ntfs->attrdef; |
3221 | 0 | while ( |
3222 | 0 | (((uintptr_t) attrdef - (uintptr_t) ntfs->attrdef + |
3223 | 0 | sizeof(ntfs_attrdef)) < ntfs->attrdef_len) && |
3224 | 0 | (tsk_getu32(fs->endian, attrdef->type))) { |
3225 | 0 | if (tsk_getu32(fs->endian, attrdef->type) == type) { |
3226 | |
|
3227 | 0 | UTF16 *name16 = (UTF16 *) attrdef->label; |
3228 | 0 | UTF8 *name8 = (UTF8 *) name; |
3229 | 0 | int retVal; |
3230 | 0 | retVal = |
3231 | 0 | tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, |
3232 | 0 | (UTF16 *) ((uintptr_t) name16 + |
3233 | 0 | sizeof(attrdef->label)), |
3234 | 0 | &name8, |
3235 | 0 | (UTF8 *) ((uintptr_t) name8 + len), TSKlenientConversion); |
3236 | 0 | if (retVal != TSKconversionOK) { |
3237 | 0 | if (tsk_verbose) |
3238 | 0 | tsk_fprintf(stderr, |
3239 | 0 | "attrname_lookup: Error converting NTFS attribute def label to UTF8: %d", |
3240 | 0 | retVal); |
3241 | 0 | break; |
3242 | 0 | } |
3243 | | |
3244 | | /* Make sure it is NULL Terminated */ |
3245 | 0 | else if ((uintptr_t) name8 >= (uintptr_t) name + len) |
3246 | 0 | name[len - 1] = '\0'; |
3247 | 0 | else |
3248 | 0 | *name8 = '\0'; |
3249 | 0 | return 0; |
3250 | 0 | } |
3251 | 0 | attrdef++; |
3252 | 0 | } |
3253 | | /* If we didn't find it, then call it '?' */ |
3254 | 0 | snprintf(name, len, "?"); |
3255 | 0 | return 0; |
3256 | 0 | } |
3257 | | |
3258 | | |
3259 | | /* Load the block bitmap $Data run and allocate a buffer for a cache |
3260 | | * |
3261 | | * return 1 on error and 0 on success |
3262 | | * */ |
3263 | | static uint8_t |
3264 | | ntfs_load_bmap(NTFS_INFO * ntfs) |
3265 | 0 | { |
3266 | 0 | ssize_t cnt = 0; |
3267 | 0 | ntfs_attr *attr = NULL; |
3268 | 0 | ntfs_attr *data_attr = NULL; |
3269 | 0 | TSK_FS_INFO *fs = NULL; |
3270 | 0 | ntfs_mft *mft = NULL; |
3271 | |
|
3272 | 0 | uint32_t attr_len = 0; |
3273 | 0 | uint32_t attr_type = 0; |
3274 | |
|
3275 | 0 | uint64_t run_start_vcn; |
3276 | 0 | uint16_t run_off; |
3277 | |
|
3278 | 0 | if (ntfs == NULL) { |
3279 | 0 | goto on_error; |
3280 | 0 | } |
3281 | 0 | fs = &ntfs->fs_info; |
3282 | |
|
3283 | 0 | if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) { |
3284 | 0 | goto on_error; |
3285 | 0 | } |
3286 | | |
3287 | | /* Get data on the bitmap */ |
3288 | 0 | if (ntfs_dinode_lookup(ntfs, (char *) mft, NTFS_MFT_BMAP, 0) != TSK_OK) { |
3289 | 0 | goto on_error; |
3290 | 0 | } |
3291 | | |
3292 | 0 | attr = (ntfs_attr *) ((uintptr_t) mft + |
3293 | 0 | tsk_getu16(fs->endian, mft->attr_off)); |
3294 | 0 | data_attr = NULL; |
3295 | | |
3296 | | /* cycle through them */ |
3297 | 0 | while ((uintptr_t) attr + sizeof (ntfs_attr) <= |
3298 | 0 | ((uintptr_t) mft + (uintptr_t) ntfs->mft_rsize_b)) { |
3299 | |
|
3300 | 0 | attr_len = tsk_getu32(fs->endian, attr->len); |
3301 | 0 | attr_type = tsk_getu32(fs->endian, attr->type); |
3302 | |
|
3303 | 0 | if ((attr_len == 0) || (attr_type == 0xffffffff)) { |
3304 | 0 | break; |
3305 | 0 | } |
3306 | | |
3307 | 0 | if (attr_type == NTFS_ATYPE_DATA) { |
3308 | 0 | data_attr = attr; |
3309 | 0 | break; |
3310 | 0 | } |
3311 | | |
3312 | 0 | attr = (ntfs_attr *) ((uintptr_t) attr + attr_len); |
3313 | 0 | } |
3314 | | |
3315 | | /* did we get it? */ |
3316 | 0 | if (data_attr == NULL) { |
3317 | 0 | tsk_error_reset(); |
3318 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
3319 | 0 | tsk_error_set_errstr("Error Finding Bitmap Data Attribute"); |
3320 | 0 | goto on_error; |
3321 | 0 | } |
3322 | 0 | attr_len = tsk_getu32(fs->endian, data_attr->len); |
3323 | 0 | if (attr_len > ntfs->mft_rsize_b) { |
3324 | 0 | goto on_error; |
3325 | 0 | } |
3326 | | |
3327 | 0 | run_start_vcn = tsk_getu64(fs->endian, data_attr->c.nr.start_vcn); |
3328 | 0 | run_off = tsk_getu16(fs->endian, data_attr->c.nr.run_off); |
3329 | |
|
3330 | 0 | if ((run_off < 48) || |
3331 | 0 | (run_off >= attr_len) || |
3332 | 0 | ((uintptr_t) data_attr + run_off) > ((uintptr_t) mft + (uintptr_t) ntfs->mft_rsize_b)) { |
3333 | 0 | tsk_error_reset(); |
3334 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
3335 | 0 | tsk_error_set_errstr("Invalid run_off of Bitmap Data Attribute - value out of bounds"); |
3336 | 0 | goto on_error; |
3337 | 0 | } |
3338 | | /* convert data run to generic form */ |
3339 | 0 | if ((ntfs_make_data_run(ntfs, |
3340 | 0 | run_start_vcn, |
3341 | 0 | (ntfs_runlist *) ((uintptr_t) data_attr + run_off), |
3342 | 0 | attr_len - run_off, |
3343 | 0 | &(ntfs->bmap), NULL, NTFS_MFT_BMAP)) != TSK_OK) { |
3344 | 0 | goto on_error; |
3345 | 0 | } |
3346 | 0 | ntfs->bmap_buf = (char *) tsk_malloc(fs->block_size); |
3347 | 0 | if (ntfs->bmap_buf == NULL) { |
3348 | 0 | goto on_error; |
3349 | 0 | } |
3350 | | |
3351 | | /* Load the first cluster so that we have something there */ |
3352 | 0 | ntfs->bmap_buf_off = 0; |
3353 | | |
3354 | | // Check ntfs->bmap before it is accessed. |
3355 | 0 | if (ntfs->bmap == NULL) { |
3356 | 0 | goto on_error; |
3357 | 0 | } |
3358 | 0 | if (ntfs->bmap->addr > fs->last_block) { |
3359 | 0 | tsk_error_reset(); |
3360 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3361 | 0 | tsk_error_set_errstr |
3362 | 0 | ("ntfs_load_bmap: Bitmap too large for image size: %" PRIuDADDR |
3363 | 0 | "", ntfs->bmap->addr); |
3364 | 0 | goto on_error; |
3365 | 0 | } |
3366 | 0 | cnt = |
3367 | 0 | tsk_fs_read_block(fs, |
3368 | 0 | ntfs->bmap->addr, ntfs->bmap_buf, fs->block_size); |
3369 | 0 | if (cnt != fs->block_size) { |
3370 | 0 | if (cnt >= 0) { |
3371 | 0 | tsk_error_reset(); |
3372 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
3373 | 0 | } |
3374 | 0 | tsk_error_set_errstr2("ntfs_load_bmap: Error reading block at %" |
3375 | 0 | PRIuDADDR, ntfs->bmap->addr); |
3376 | 0 | goto on_error; |
3377 | 0 | } |
3378 | | |
3379 | 0 | free (mft); |
3380 | 0 | return 0; |
3381 | | |
3382 | 0 | on_error: |
3383 | 0 | if (mft != NULL) { |
3384 | 0 | free (mft); |
3385 | 0 | } |
3386 | 0 | return 1; |
3387 | 0 | } |
3388 | | |
3389 | | |
3390 | | /* |
3391 | | * Load the VOLUME MFT entry and the VINFO attribute so that we |
3392 | | * can identify the volume version of this. |
3393 | | * |
3394 | | * Return 1 on error and 0 on success |
3395 | | */ |
3396 | | static uint8_t |
3397 | | ntfs_load_ver(NTFS_INFO * ntfs) |
3398 | 0 | { |
3399 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info; |
3400 | 0 | const TSK_FS_ATTR *fs_attr; |
3401 | |
|
3402 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
3403 | 0 | tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_VOL), |
3404 | 0 | tsk_fs_file_close |
3405 | 0 | }; |
3406 | |
|
3407 | 0 | if (!fs_file) { |
3408 | 0 | return 1; |
3409 | 0 | } |
3410 | | |
3411 | | /* cache the data attribute */ |
3412 | 0 | fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_VINFO); |
3413 | 0 | if (!fs_attr) { |
3414 | 0 | tsk_error_reset(); |
3415 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
3416 | 0 | tsk_error_set_errstr("Volume Info attribute not found in $Volume"); |
3417 | 0 | return 1; |
3418 | 0 | } |
3419 | | |
3420 | 0 | if ((fs_attr->flags & TSK_FS_ATTR_RES) |
3421 | 0 | && (fs_attr->size)) { |
3422 | 0 | ntfs_attr_vinfo *vinfo = (ntfs_attr_vinfo *) fs_attr->rd.buf; |
3423 | |
|
3424 | 0 | if ((vinfo->maj_ver == 1) |
3425 | 0 | && (vinfo->min_ver == 2)) { |
3426 | 0 | ntfs->ver = NTFS_VINFO_NT; |
3427 | 0 | } |
3428 | 0 | else if ((vinfo->maj_ver == 3) |
3429 | 0 | && (vinfo->min_ver == 0)) { |
3430 | 0 | ntfs->ver = NTFS_VINFO_2K; |
3431 | 0 | } |
3432 | 0 | else if ((vinfo->maj_ver == 3) |
3433 | 0 | && (vinfo->min_ver == 1)) { |
3434 | 0 | ntfs->ver = NTFS_VINFO_XP; |
3435 | 0 | } |
3436 | 0 | else { |
3437 | 0 | tsk_error_reset(); |
3438 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3439 | 0 | tsk_error_set_errstr("unknown version: %d.%d\n", |
3440 | 0 | vinfo->maj_ver, vinfo->min_ver); |
3441 | 0 | return 1; |
3442 | 0 | } |
3443 | 0 | } |
3444 | 0 | else { |
3445 | 0 | tsk_error_reset(); |
3446 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3447 | 0 | tsk_error_set_errstr |
3448 | 0 | ("load_version: VINFO is a non-resident attribute"); |
3449 | 0 | return 1; |
3450 | 0 | } |
3451 | | |
3452 | 0 | return 0; |
3453 | 0 | } |
3454 | | |
3455 | | |
3456 | | #if TSK_USE_SID |
3457 | | /** \internal |
3458 | | * Prints the value of sds into the a_sidstr string in ASCII form. This will allocate a new buffer for the |
3459 | | * string, so a_sidstr should not point to a buffer. Output is in format of: |
3460 | | * S-R-I-S-S... with 'R' being revision, 'I' being the identifier authority, and 'S' being subauthority values. |
3461 | | * |
3462 | | * @param a_fs File system |
3463 | | * @param a_sds SDS |
3464 | | * @param a_sidstr [out] Pointer that will be assigned to the buffer allocated by this function to store the string. |
3465 | | * @returns 1 on error, 0 on success |
3466 | | */ |
3467 | | static uint8_t |
3468 | | ntfs_sds_to_str(TSK_FS_INFO * a_fs, const ntfs_attr_sds * a_sds, |
3469 | | char **a_sidstr) |
3470 | 0 | { |
3471 | 0 | ntfs_sid *sid = NULL; |
3472 | |
|
3473 | 0 | uint32_t owner_offset; |
3474 | 0 | *a_sidstr = NULL; |
3475 | |
|
3476 | 0 | if ((a_fs == NULL) || (a_sds == NULL) || (a_sidstr == NULL)) { |
3477 | 0 | tsk_error_reset(); |
3478 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
3479 | 0 | tsk_error_set_errstr("Invalid argument"); |
3480 | 0 | return 1; |
3481 | 0 | } |
3482 | | |
3483 | 0 | owner_offset = |
3484 | 0 | tsk_getu32(a_fs->endian, a_sds->self_rel_sec_desc.owner); |
3485 | |
|
3486 | 0 | if (((uintptr_t) & a_sds->self_rel_sec_desc + owner_offset) > |
3487 | 0 | ((uintptr_t) a_sds + tsk_getu32(a_fs->endian, a_sds->ent_size))) { |
3488 | 0 | tsk_error_reset(); |
3489 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
3490 | 0 | tsk_error_set_errstr |
3491 | 0 | ("ntfs_sds_to_str: owner offset larger than a_sds length"); |
3492 | 0 | return 1; |
3493 | 0 | } |
3494 | | |
3495 | 0 | sid = |
3496 | 0 | (ntfs_sid *) ((uint8_t *) & a_sds->self_rel_sec_desc + |
3497 | 0 | owner_offset); |
3498 | | |
3499 | | //tsk_fprintf(stderr, "Revision: %i\n", sid->revision); |
3500 | | |
3501 | | // This check helps not process invalid data, which was noticed while testing |
3502 | | // a failing harddrive |
3503 | 0 | if (sid->revision == 1) { |
3504 | 0 | uint64_t authority = 0; |
3505 | 0 | int i, len; |
3506 | 0 | char *sid_str_offset = NULL; |
3507 | 0 | char *sid_str = NULL; |
3508 | | |
3509 | | //tsk_fprintf(stderr, "Sub-Authority Count: %i\n", sid->sub_auth_count); |
3510 | 0 | authority = 0; |
3511 | 0 | for (i = 0; i < 6; i++) |
3512 | 0 | authority += (uint64_t) sid->ident_auth[i] << ((5 - i) * 8); |
3513 | | |
3514 | | //tsk_fprintf(stderr, "NT Authority: %" PRIu64 "\n", authority); |
3515 | | |
3516 | | // "S-1-AUTH-SUBAUTH-SUBAUTH..." |
3517 | 0 | const size_t sid_str_len = 4 + 13 + (1 + 10) * sid->sub_auth_count; |
3518 | | |
3519 | | // Allocate the buffer for the string representation of the SID. |
3520 | 0 | if ((sid_str = (char *) tsk_malloc(sid_str_len + 1)) == NULL) { |
3521 | 0 | return 1; |
3522 | 0 | } |
3523 | | |
3524 | 0 | len = snprintf(sid_str, sid_str_len + 1, "S-1-%" PRIu64, authority); |
3525 | 0 | sid_str_offset = sid_str + len; |
3526 | |
|
3527 | 0 | for (i = 0; i < sid->sub_auth_count; i++) { |
3528 | 0 | len = snprintf(sid_str_offset, sid_str_len + 1 - len, "-%" PRIu32, sid->sub_auth[i]); |
3529 | 0 | sid_str_offset += len; |
3530 | 0 | } |
3531 | 0 | *a_sidstr = sid_str; |
3532 | | //tsk_fprintf(stderr, "SID: %s\n", sid_str); |
3533 | 0 | } |
3534 | 0 | else { |
3535 | 0 | tsk_error_reset(); |
3536 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3537 | 0 | tsk_error_set_errstr("ntfs_sds_to_str: Invalid SID revision (%d)", |
3538 | 0 | sid->revision); |
3539 | 0 | return 1; // Invalid revision number in the SID. |
3540 | 0 | } |
3541 | | |
3542 | 0 | return 0; |
3543 | 0 | } |
3544 | | |
3545 | | |
3546 | | |
3547 | | |
3548 | | /** \internal |
3549 | | * Maps a security id value from a file to its SDS structure |
3550 | | * |
3551 | | * Note: This routine assumes &ntfs->sid_lock is locked by the caller. |
3552 | | * |
3553 | | * @param fs File system |
3554 | | * @param secid Security Id to find SDS for. |
3555 | | * @returns NULL on error |
3556 | | */ |
3557 | | static const ntfs_attr_sds * |
3558 | | ntfs_get_sds(TSK_FS_INFO * fs, uint32_t secid) |
3559 | 0 | { |
3560 | 0 | uint32_t i = 0; |
3561 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) fs; |
3562 | 0 | ntfs_attr_sii *sii = NULL; |
3563 | 0 | ntfs_attr_sds *sds = NULL; |
3564 | 0 | uint32_t sii_secid = 0; |
3565 | 0 | uint32_t sds_secid = 0; |
3566 | 0 | uint32_t sii_sechash = 0; |
3567 | 0 | uint32_t sds_sechash = 0; |
3568 | 0 | uint64_t sds_file_off = 0; |
3569 | | //uint32_t sds_ent_size = 0; |
3570 | 0 | uint64_t sii_sds_file_off = 0; |
3571 | 0 | uint32_t sii_sds_ent_size = 0; |
3572 | |
|
3573 | 0 | if ((fs == NULL) || (secid == 0)) { |
3574 | 0 | tsk_error_reset(); |
3575 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
3576 | 0 | tsk_error_set_errstr("Invalid argument"); |
3577 | 0 | return NULL; |
3578 | 0 | } |
3579 | | |
3580 | | // Loop through all the SII entries looking for the security id matching that found in the file. |
3581 | | // This lookup is obviously O(n^2) for all n files. However, since so many files have the exact |
3582 | | // same security identifier, it is not really that bad. In reality, 100,000 files may only map to |
3583 | | // 10,000 security identifiers. Since SII entries are 0x28 bytes each and security identifiers |
3584 | | // increase incrementally, we could go directly to the entry in question ((secid * 0x28) + 256). |
3585 | | // SII entries started at 256 on Vista; however, I did not look at the starting secid for other |
3586 | | // versions of NTFS. |
3587 | | // |
3588 | | // It appears that the file format may have changed since this was first written. There now appear to |
3589 | | // be multiple entries for each security ID. Some may no longer be valid, so we loop over all of them |
3590 | | // until we find one that looks valid. |
3591 | 0 | for (i = 0; i < ntfs->sii_data.used; i++) { |
3592 | 0 | if (! (tsk_getu32(fs->endian, |
3593 | 0 | ((ntfs_attr_sii *)(ntfs->sii_data.buffer))[i].key_sec_id) == secid)) { |
3594 | 0 | continue; |
3595 | 0 | } |
3596 | | |
3597 | | // We found a potentially good SII entry |
3598 | 0 | sii = &((ntfs_attr_sii *)(ntfs->sii_data.buffer))[i]; |
3599 | 0 | sii_secid = tsk_getu32(fs->endian, sii->key_sec_id); |
3600 | 0 | sii_sechash = tsk_getu32(fs->endian, sii->data_hash_sec_desc); |
3601 | 0 | sii_sds_file_off = tsk_getu64(fs->endian, sii->sec_desc_off); |
3602 | 0 | sii_sds_ent_size = tsk_getu32(fs->endian, sii->sec_desc_size); |
3603 | | |
3604 | | // Check that we do not go out of bounds. |
3605 | 0 | if (sii_sds_file_off > ntfs->sds_data.size) { |
3606 | 0 | tsk_error_reset(); |
3607 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3608 | 0 | tsk_error_set_errstr("ntfs_get_sds: SII offset too large (%" PRIu64 |
3609 | 0 | ")", sii_sds_file_off); |
3610 | 0 | continue; |
3611 | 0 | } |
3612 | 0 | else if (!sii_sds_ent_size) { |
3613 | 0 | tsk_error_reset(); |
3614 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3615 | 0 | tsk_error_set_errstr("ntfs_get_sds: SII entry size is invalid (%" |
3616 | 0 | PRIu32 ")", sii_sds_ent_size); |
3617 | 0 | continue; |
3618 | 0 | } |
3619 | | |
3620 | 0 | sds = |
3621 | 0 | (ntfs_attr_sds *)((uint8_t *)ntfs->sds_data.buffer + |
3622 | 0 | sii_sds_file_off); |
3623 | 0 | sds_secid = tsk_getu32(fs->endian, sds->sec_id); |
3624 | 0 | sds_sechash = tsk_getu32(fs->endian, sds->hash_sec_desc); |
3625 | 0 | sds_file_off = tsk_getu64(fs->endian, sds->file_off); |
3626 | | |
3627 | | // Sanity check to make sure the $SII entry points to |
3628 | | // the correct $SDS entry. |
3629 | 0 | if ((sds_secid == sii_secid) && |
3630 | 0 | (sds_sechash == sii_sechash) && (sds_file_off == sii_sds_file_off) |
3631 | | //&& (sds_ent_size == sii_sds_ent_size) |
3632 | 0 | ) { |
3633 | | // Clear any previous errors |
3634 | 0 | tsk_error_reset(); |
3635 | 0 | return sds; |
3636 | 0 | } |
3637 | 0 | tsk_error_reset(); |
3638 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3639 | 0 | tsk_error_set_errstr("ntfs_get_sds: SII entry %" PRIu32 " not found", sii_secid); |
3640 | 0 | } |
3641 | | |
3642 | | // If we never even found an SII entry that matched our secid, update the error state. |
3643 | | // Otherwise leave it as the last error recorded. |
3644 | 0 | if (sii == NULL) { |
3645 | 0 | tsk_error_reset(); |
3646 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3647 | 0 | tsk_error_set_errstr("ntfs_get_sds: Got to end w/out data"); |
3648 | 0 | } |
3649 | 0 | return NULL; |
3650 | 0 | } |
3651 | | #endif |
3652 | | |
3653 | | /** \internal |
3654 | | * NTFS-specific function (pointed to in FS_INFO) that maps a security ID |
3655 | | * to an ASCII printable string. |
3656 | | * Read the contents of the STANDARD_INFORMATION attribute of a file |
3657 | | * to get the security id. Once we have the security id, we will |
3658 | | * search $Secure:$SII to find a matching security id. That $SII entry |
3659 | | * will contain the offset within the $SDS stream for the $SDS entry, |
3660 | | * which contains the owner SID |
3661 | | * |
3662 | | * @param a_fs_file File to get security info on |
3663 | | * @param sid_str [out] location where string representation of security info will be stored. |
3664 | | Caller must free the string. |
3665 | | * @returns 1 on error |
3666 | | */ |
3667 | | static uint8_t |
3668 | | ntfs_file_get_sidstr(TSK_FS_FILE * a_fs_file, char **sid_str) |
3669 | 0 | { |
3670 | 0 | #if TSK_USE_SID |
3671 | 0 | const TSK_FS_ATTR *fs_data; |
3672 | 0 | ntfs_attr_si *si; |
3673 | 0 | const ntfs_attr_sds *sds; |
3674 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) a_fs_file->fs_info; |
3675 | |
|
3676 | 0 | *sid_str = NULL; |
3677 | |
|
3678 | 0 | if (!a_fs_file->meta->attr) { |
3679 | 0 | tsk_error_reset(); |
3680 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3681 | 0 | tsk_error_set_errstr |
3682 | 0 | ("ntfs_file_get_sidstr: file argument has no meta data"); |
3683 | 0 | return 1; |
3684 | 0 | } |
3685 | | |
3686 | | // Read STANDARD_INFORMATION attribute for the security id of the file. |
3687 | 0 | fs_data = tsk_fs_attrlist_get(a_fs_file->meta->attr, |
3688 | 0 | TSK_FS_ATTR_TYPE_NTFS_SI); |
3689 | 0 | if (!fs_data) { |
3690 | 0 | tsk_error_set_errstr2("- ntfs_file_get_sidstr:SI attribute"); |
3691 | 0 | return 1; |
3692 | 0 | } |
3693 | | |
3694 | 0 | si = (ntfs_attr_si *) fs_data->rd.buf; |
3695 | 0 | if (!si) { |
3696 | 0 | tsk_error_reset(); |
3697 | 0 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
3698 | 0 | tsk_error_set_errstr("ntfs_file_get_sidstr: SI buf is NULL"); |
3699 | 0 | return 1; |
3700 | 0 | } |
3701 | | |
3702 | 0 | tsk_take_lock(&ntfs->sid_lock); |
3703 | | // sds points inside ntfs->sds_data, which we've just locked |
3704 | 0 | sds = |
3705 | 0 | ntfs_get_sds(a_fs_file->fs_info, |
3706 | 0 | tsk_getu32(a_fs_file->fs_info->endian, si->sec_id)); |
3707 | 0 | if (!sds) { |
3708 | 0 | tsk_release_lock(&ntfs->sid_lock); |
3709 | 0 | tsk_error_set_errstr2("- ntfs_file_get_sidstr:SI attribute"); |
3710 | 0 | return 1; |
3711 | 0 | } |
3712 | 0 | if (ntfs_sds_to_str(a_fs_file->fs_info, sds, sid_str)) { |
3713 | 0 | tsk_release_lock(&ntfs->sid_lock); |
3714 | 0 | tsk_error_set_errstr2("- ntfs_file_get_sidstr:SI attribute"); |
3715 | 0 | return 1; |
3716 | 0 | } |
3717 | 0 | tsk_release_lock(&ntfs->sid_lock); |
3718 | 0 | return 0; |
3719 | | #else |
3720 | | *sid_str = NULL; |
3721 | | tsk_error_reset(); |
3722 | | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
3723 | | tsk_error_set_errstr("Unsupported function"); |
3724 | | return 1; |
3725 | | #endif |
3726 | 0 | } |
3727 | | |
3728 | | |
3729 | | #if TSK_USE_SID |
3730 | | /** \internal |
3731 | | * Process all the $SII entries into a single array by removing all the Attribute Headers. |
3732 | | * Note: This routine assumes &ntfs->sid_lock is locked by the caller. |
3733 | | * @param fs File system structure to store results into |
3734 | | * @param sii_buffer Buffer of raw $SII entries to parse |
3735 | | */ |
3736 | | static void |
3737 | | ntfs_proc_sii(TSK_FS_INFO * fs, NTFS_SXX_BUFFER * sii_buffer) |
3738 | 0 | { |
3739 | 0 | unsigned int sii_buffer_offset = 0; |
3740 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) fs; |
3741 | 0 | ntfs_attr_sii *sii; |
3742 | |
|
3743 | 0 | if ((fs == NULL) || (sii_buffer == NULL) |
3744 | 0 | || (ntfs->sii_data.buffer == NULL)) |
3745 | 0 | return; |
3746 | | |
3747 | | /* Loop by cluster size */ |
3748 | 0 | for (sii_buffer_offset = 0; sii_buffer_offset < sii_buffer->size; |
3749 | 0 | sii_buffer_offset += ntfs->idx_rsize_b) { |
3750 | |
|
3751 | 0 | uint8_t* idx_buffer_end = 0; |
3752 | |
|
3753 | 0 | ntfs_idxrec *idxrec = |
3754 | 0 | (ntfs_idxrec *) & sii_buffer->buffer[sii_buffer_offset]; |
3755 | | |
3756 | | // stop processing if we hit corrupt data |
3757 | 0 | if (tsk_getu32(fs->endian, idxrec->list.begin_off) > ntfs->idx_rsize_b) { |
3758 | 0 | if (tsk_verbose) |
3759 | 0 | tsk_fprintf(stderr, "ntfs_proc_sii: corrupt offset\n"); |
3760 | 0 | break; |
3761 | 0 | } |
3762 | 0 | else if (tsk_getu32(fs->endian, idxrec->list.bufend_off) > ntfs->idx_rsize_b) { |
3763 | 0 | if (tsk_verbose) |
3764 | 0 | tsk_fprintf(stderr, "ntfs_proc_sii: corrupt offset\n"); |
3765 | 0 | break; |
3766 | 0 | } |
3767 | 0 | else if (tsk_getu32(fs->endian, idxrec->list.begin_off) > tsk_getu32(fs->endian, idxrec->list.bufend_off)) { |
3768 | 0 | if (tsk_verbose) |
3769 | 0 | tsk_fprintf(stderr, "ntfs_proc_sii: corrupt offset\n"); |
3770 | 0 | break; |
3771 | 0 | } |
3772 | | |
3773 | | // get pointer to first record |
3774 | 0 | uint8_t* sii_data_ptr = ((uint8_t*)& idxrec->list + |
3775 | 0 | tsk_getu32(fs->endian, idxrec->list.begin_off)); |
3776 | | |
3777 | | // where last record ends |
3778 | 0 | idx_buffer_end = (uint8_t*) & idxrec->list + |
3779 | 0 | tsk_getu32(fs->endian, idxrec->list.bufend_off); |
3780 | | |
3781 | | |
3782 | | // copy records into NTFS_INFO |
3783 | 0 | while (sii_data_ptr + sizeof(ntfs_attr_sii) <= idx_buffer_end) { |
3784 | | /* make sure we don't go over bounds of ntfs->sii_data.buffer */ |
3785 | 0 | if ((ntfs->sii_data.used + 1) * sizeof(ntfs_attr_sii) > ntfs->sii_data.size) { |
3786 | 0 | if (tsk_verbose) |
3787 | 0 | tsk_fprintf(stderr, "ntfs_proc_sii: data buffer too small\n"); |
3788 | 0 | return; // reached end of ntfs->sii_data.buffer |
3789 | 0 | } |
3790 | | |
3791 | | // It appears that perhaps older versions of NTFS always had entries of length 0x28. Now it appears we also can |
3792 | | // have entries of length 0x30. And there are also some entries that take up 0x28 bytes but have their length set to 0x10. |
3793 | | |
3794 | | // 1400140000000000280004000000000002110000f233505302110000a026320000000000ec000000 // Normal entry of length 0x28 |
3795 | | // 0000000000000000100000000200000003110000a65c02000311000090273200000000005c010000 // Possibly deleted? entry of length 0x28 but reporting length 0x10 |
3796 | | // 140014000000000030000400010000001d150000abb032671d150000805a3a0000000000e80000006800000000000000 // Entry of length 0x30. Unclear what the eight final bytes are |
3797 | | // 00000000000000001800000003001b00540000000000000067110000a0823200000000003c0100005400000000000000 // I think this is the possibly deleted form of a long entry |
3798 | | // |
3799 | | // I haven't been able to find any documentation of what's going on - it's all old and says the entry length will be 0x28. The flags |
3800 | | // are also different across these three types but I also can't find any documentation on what they mean. So this is a best guess on |
3801 | | // how we should handle things: |
3802 | | // - If the length field is 0x30 or the first two fields are null and the length is 0x18, save the entry and advance 0x30 bytes. |
3803 | | // The last eight bytes on the long entries will be ignored. |
3804 | | // - Otherwise save the entry and advance by 0x28 bytes. |
3805 | | // |
3806 | 0 | sii = (ntfs_attr_sii*)sii_data_ptr; |
3807 | 0 | int data_off = tsk_getu16(fs->endian, sii->data_off); |
3808 | 0 | int data_size = tsk_getu16(fs->endian, sii->size); |
3809 | 0 | int ent_size = tsk_getu16(fs->endian, sii->ent_size); |
3810 | | |
3811 | | // Copy the entry. It seems like we could have a check here that the first two fields are 0x14 |
3812 | | // but we don't know for sure that not having those indicates an invalid entry. |
3813 | 0 | memcpy(ntfs->sii_data.buffer + |
3814 | 0 | (ntfs->sii_data.used * sizeof(ntfs_attr_sii)), sii_data_ptr, |
3815 | 0 | sizeof(ntfs_attr_sii)); |
3816 | 0 | ntfs->sii_data.used++; |
3817 | | |
3818 | | // Advance the pointer |
3819 | 0 | if (ent_size == 0x30 || (data_off == 0 && data_size == 0 && ent_size == 0x18)) { |
3820 | 0 | sii_data_ptr += 0x30; |
3821 | 0 | } |
3822 | 0 | else { |
3823 | 0 | sii_data_ptr += 0x28; |
3824 | 0 | } |
3825 | | |
3826 | | /* |
3827 | | printf("Security id %d is at offset 0x%I64x for 0x%x bytes\n", tsk_getu32(fs->endian,sii->key_sec_id), |
3828 | | tsk_getu64(fs->endian,sii->sec_desc_off), |
3829 | | tsk_getu32(fs->endian,sii->sec_desc_size)); |
3830 | | } |
3831 | | else |
3832 | | { |
3833 | | printf("\n\tOffset to data %x Size of data %x Size of Index entry %x\n", tsk_getu16(fs->endian,sii->data_off), |
3834 | | tsk_getu16(fs->endian,sii->size), |
3835 | | tsk_getu16(fs->endian,sii->ent_size)); |
3836 | | printf("\tSecurity id %d is at offset 0x%I64x for 0x%x bytes\n\n", tsk_getu32(fs->endian,sii->key_sec_id), |
3837 | | tsk_getu64(fs->endian,sii->sec_desc_off), |
3838 | | tsk_getu32(fs->endian,sii->sec_desc_size)); |
3839 | | } |
3840 | | */ |
3841 | 0 | } |
3842 | 0 | } |
3843 | 0 | } |
3844 | | |
3845 | | |
3846 | | /* |
3847 | | * Load the $Secure attributes so that we can identify the user. |
3848 | | * |
3849 | | * Note: This routine is called only from ntfs_open and therefore does |
3850 | | * not need to lock ntfs->sid_lock. |
3851 | | * |
3852 | | * @returns 1 on error (which occurs only if malloc or other system error). |
3853 | | */ |
3854 | | static uint8_t |
3855 | | ntfs_load_secure(NTFS_INFO * ntfs) |
3856 | 0 | { |
3857 | 0 | TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info; |
3858 | 0 | TSK_FS_META *fs_meta = NULL; |
3859 | 0 | const TSK_FS_ATTR *fs_attr_sds = NULL; |
3860 | 0 | const TSK_FS_ATTR *fs_attr_sii = NULL; |
3861 | 0 | NTFS_SXX_BUFFER sii_buffer; |
3862 | 0 | ssize_t cnt; |
3863 | |
|
3864 | 0 | ntfs->sii_data.buffer = NULL; |
3865 | 0 | ntfs->sii_data.size = 0; |
3866 | 0 | ntfs->sii_data.used = 0; |
3867 | 0 | ntfs->sds_data.buffer = NULL; |
3868 | 0 | ntfs->sds_data.size = 0; |
3869 | 0 | ntfs->sds_data.used = 0; |
3870 | | |
3871 | | |
3872 | | // Open $Secure. The $SDS stream contains all the security descriptors |
3873 | | // and is indexed by $SII and $SDH. |
3874 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> secure{ |
3875 | 0 | tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_SECURE), |
3876 | 0 | tsk_fs_file_close |
3877 | 0 | }; |
3878 | |
|
3879 | 0 | if (!secure) { |
3880 | 0 | if (tsk_verbose) |
3881 | 0 | tsk_fprintf(stderr, |
3882 | 0 | "ntfs_load_secure: error opening $Secure file: %s\n", |
3883 | 0 | tsk_error_get_errstr()); |
3884 | 0 | tsk_error_reset(); |
3885 | 0 | return 0; |
3886 | 0 | } |
3887 | | |
3888 | | // Make sure the TSK_FS_META is not NULL. We need it to get the |
3889 | | // $SII and $SDH attributes. |
3890 | 0 | fs_meta = secure->meta; |
3891 | 0 | if (!fs_meta) { |
3892 | 0 | if (tsk_verbose) |
3893 | 0 | tsk_fprintf(stderr, |
3894 | 0 | "ntfs_load_secure: $Secure file has no attributes\n"); |
3895 | 0 | tsk_error_reset(); |
3896 | 0 | return 0; |
3897 | 0 | } |
3898 | | |
3899 | | // Get the $SII attribute. |
3900 | 0 | fs_attr_sii = |
3901 | 0 | tsk_fs_attrlist_get_name_type(fs_meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_IDXALLOC, |
3902 | 0 | "$SII\0"); |
3903 | 0 | if (!fs_attr_sii) { |
3904 | 0 | if (tsk_verbose) |
3905 | 0 | tsk_fprintf(stderr, |
3906 | 0 | "ntfs_load_secure: error getting $Secure:$SII IDX_ALLOC attribute\n"); |
3907 | 0 | tsk_error_reset(); |
3908 | 0 | return 0; |
3909 | |
|
3910 | 0 | } |
3911 | | |
3912 | | // Get the $SDS attribute. |
3913 | 0 | fs_attr_sds = tsk_fs_attrlist_get(fs_meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_DATA); |
3914 | 0 | if (!fs_attr_sds) { |
3915 | 0 | if (tsk_verbose) |
3916 | 0 | tsk_fprintf(stderr, |
3917 | 0 | "ntfs_load_secure: error getting $Secure:$SDS $Data attribute\n"); |
3918 | 0 | tsk_error_reset(); |
3919 | 0 | return 0; |
3920 | 0 | } |
3921 | | |
3922 | | /* First we read in $SII to a local buffer adn then process it into NTFS_INFO */ |
3923 | | |
3924 | | // Allocate local space for the entire $SII stream. |
3925 | 0 | sii_buffer.size = (size_t) roundup(fs_attr_sii->size, fs->block_size); |
3926 | 0 | sii_buffer.used = 0; |
3927 | | |
3928 | | // arbitrary check because we had problems before with alloc too much memory |
3929 | 0 | if (sii_buffer.size > 64000000) { |
3930 | 0 | if (tsk_verbose) |
3931 | 0 | tsk_fprintf(stderr, |
3932 | 0 | "ntfs_load_secure: sii_buffer.size is too large: %z\n", |
3933 | 0 | sii_buffer.size); |
3934 | 0 | return 0; |
3935 | 0 | } |
3936 | 0 | if ((sii_buffer.buffer = (char*) tsk_malloc(sii_buffer.size)) == NULL) { |
3937 | 0 | return 1; |
3938 | 0 | } |
3939 | | |
3940 | | // Read in the raw $SII stream. |
3941 | 0 | cnt = |
3942 | 0 | tsk_fs_attr_read(fs_attr_sii, 0, sii_buffer.buffer, |
3943 | 0 | sii_buffer.size, TSK_FS_FILE_READ_FLAG_NONE); |
3944 | 0 | if (cnt != (ssize_t)sii_buffer.size) { |
3945 | 0 | if (tsk_verbose) |
3946 | 0 | tsk_fprintf(stderr, |
3947 | 0 | "ntfs_load_secure: error reading $Secure:$SII attribute: %s\n", |
3948 | 0 | tsk_error_get_errstr()); |
3949 | 0 | tsk_error_reset(); |
3950 | |
|
3951 | 0 | free(sii_buffer.buffer); |
3952 | 0 | return 0; |
3953 | 0 | } |
3954 | | |
3955 | | // allocate the structure for the processed version of the data |
3956 | 0 | ntfs->sii_data.used = 0; // use this to count the number of $SII entries |
3957 | 0 | if ((ntfs->sii_data.buffer = |
3958 | 0 | (char *) tsk_malloc(sii_buffer.size)) == NULL) { |
3959 | 0 | free(sii_buffer.buffer); |
3960 | 0 | return 1; |
3961 | 0 | } |
3962 | 0 | ntfs->sii_data.size = sii_buffer.size; |
3963 | | |
3964 | | // parse sii_buffer into ntfs->sii_data. |
3965 | 0 | ntfs_proc_sii(fs, &sii_buffer); |
3966 | 0 | free(sii_buffer.buffer); |
3967 | | |
3968 | | /* Now we copy $SDS into NTFS_INFO. We do not do any processing in this step. */ |
3969 | | |
3970 | | // Allocate space for the entire $SDS stream with all the security |
3971 | | // descriptors. We should be able to use the $SII offset to index |
3972 | | // into the $SDS stream. |
3973 | 0 | ntfs->sds_data.size = (size_t) fs_attr_sds->size; |
3974 | | // arbitrary check because we had problems before with alloc too much memory |
3975 | 0 | if (ntfs->sds_data.size > 64000000) { |
3976 | 0 | if (tsk_verbose) |
3977 | 0 | tsk_fprintf(stderr, |
3978 | 0 | "ntfs_load_secure: ntfs->sds_data.size is too large: %z\n", |
3979 | 0 | ntfs->sds_data.size); |
3980 | 0 | free(ntfs->sii_data.buffer); |
3981 | 0 | ntfs->sii_data.buffer = NULL; |
3982 | 0 | ntfs->sii_data.used = 0; |
3983 | 0 | ntfs->sii_data.size = 0; |
3984 | 0 | return 0; |
3985 | 0 | } |
3986 | 0 | ntfs->sds_data.used = 0; |
3987 | 0 | if ((ntfs->sds_data.buffer = |
3988 | 0 | (char *) tsk_malloc(ntfs->sds_data.size)) == NULL) { |
3989 | 0 | free(ntfs->sii_data.buffer); |
3990 | 0 | ntfs->sii_data.buffer = NULL; |
3991 | 0 | ntfs->sii_data.used = 0; |
3992 | 0 | ntfs->sii_data.size = 0; |
3993 | 0 | return 1; |
3994 | 0 | } |
3995 | | |
3996 | | // Read in the raw $SDS ($DATA) stream. |
3997 | 0 | cnt = |
3998 | 0 | tsk_fs_attr_read(fs_attr_sds, 0, |
3999 | 0 | ntfs->sds_data.buffer, ntfs->sds_data.size, |
4000 | 0 | TSK_FS_FILE_READ_FLAG_NONE); |
4001 | 0 | if (cnt != (ssize_t)ntfs->sds_data.size) { |
4002 | 0 | if (tsk_verbose) |
4003 | 0 | tsk_fprintf(stderr, |
4004 | 0 | "ntfs_load_secure: error reading $Secure:$SDS attribute: %s\n", |
4005 | 0 | tsk_error_get_errstr()); |
4006 | 0 | tsk_error_reset(); |
4007 | |
|
4008 | 0 | free(ntfs->sii_data.buffer); |
4009 | 0 | ntfs->sii_data.buffer = NULL; |
4010 | 0 | ntfs->sii_data.used = 0; |
4011 | 0 | ntfs->sii_data.size = 0; |
4012 | 0 | free(ntfs->sds_data.buffer); |
4013 | 0 | ntfs->sds_data.buffer = NULL; |
4014 | 0 | ntfs->sds_data.used = 0; |
4015 | 0 | ntfs->sds_data.size = 0; |
4016 | 0 | return 0; |
4017 | 0 | } |
4018 | | |
4019 | 0 | return 0; |
4020 | 0 | } |
4021 | | |
4022 | | #endif |
4023 | | |
4024 | | /********************************************************************** |
4025 | | * |
4026 | | * Exported Walk Functions |
4027 | | * |
4028 | | **********************************************************************/ |
4029 | | |
4030 | | |
4031 | | static TSK_FS_BLOCK_FLAG_ENUM |
4032 | | ntfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr) |
4033 | 0 | { |
4034 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) a_fs; |
4035 | 0 | int retval; |
4036 | 0 | int flags = 0; |
4037 | | |
4038 | | /* identify if the cluster is allocated or not */ |
4039 | 0 | retval = is_clustalloc(ntfs, a_addr); |
4040 | 0 | if (retval == 1) |
4041 | 0 | flags = TSK_FS_BLOCK_FLAG_ALLOC; |
4042 | 0 | else if (retval == 0) |
4043 | 0 | flags = TSK_FS_BLOCK_FLAG_UNALLOC; |
4044 | |
|
4045 | 0 | return (TSK_FS_BLOCK_FLAG_ENUM) flags; |
4046 | 0 | } |
4047 | | |
4048 | | |
4049 | | |
4050 | | /* |
4051 | | * flags: TSK_FS_BLOCK_FLAG_ALLOC and FS_FLAG_UNALLOC |
4052 | | * |
4053 | | * @@@ We should probably consider some data META, but it is tough with |
4054 | | * the NTFS design ... |
4055 | | */ |
4056 | | static uint8_t |
4057 | | ntfs_block_walk(TSK_FS_INFO * fs, |
4058 | | TSK_DADDR_T a_start_blk, TSK_DADDR_T a_end_blk, |
4059 | | TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, TSK_FS_BLOCK_WALK_CB a_action, |
4060 | | void *a_ptr) |
4061 | 0 | { |
4062 | 0 | const char *myname = "ntfs_block_walk"; |
4063 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) fs; |
4064 | 0 | TSK_DADDR_T addr; |
4065 | 0 | TSK_FS_BLOCK *fs_block; |
4066 | | |
4067 | | // clean up any error messages that are lying around |
4068 | 0 | tsk_error_reset(); |
4069 | | |
4070 | | /* |
4071 | | * Sanity checks. |
4072 | | */ |
4073 | 0 | if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) { |
4074 | 0 | tsk_error_reset(); |
4075 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
4076 | 0 | tsk_error_set_errstr("%s: start block: %" PRIuDADDR "", myname, |
4077 | 0 | a_start_blk); |
4078 | 0 | return 1; |
4079 | 0 | } |
4080 | 0 | else if (a_end_blk < fs->first_block || a_end_blk > fs->last_block) { |
4081 | 0 | tsk_error_reset(); |
4082 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
4083 | 0 | tsk_error_set_errstr("%s: last block: %" PRIuDADDR "", myname, |
4084 | 0 | a_end_blk); |
4085 | 0 | return 1; |
4086 | 0 | } |
4087 | | |
4088 | | /* Sanity check on a_flags -- make sure at least one ALLOC is set */ |
4089 | 0 | if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) && |
4090 | 0 | ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) { |
4091 | 0 | a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM) |
4092 | 0 | (a_flags | TSK_FS_BLOCK_WALK_FLAG_ALLOC | |
4093 | 0 | TSK_FS_BLOCK_WALK_FLAG_UNALLOC); |
4094 | 0 | } |
4095 | 0 | if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_META) == 0) && |
4096 | 0 | ((a_flags & TSK_FS_BLOCK_WALK_FLAG_CONT) == 0)) { |
4097 | 0 | a_flags = (TSK_FS_BLOCK_WALK_FLAG_ENUM) |
4098 | 0 | (a_flags | TSK_FS_BLOCK_WALK_FLAG_CONT | TSK_FS_BLOCK_WALK_FLAG_META); |
4099 | 0 | } |
4100 | | |
4101 | |
|
4102 | 0 | if ((fs_block = tsk_fs_block_alloc(fs)) == NULL) { |
4103 | 0 | return 1; |
4104 | 0 | } |
4105 | | |
4106 | | /* Cycle through the blocks */ |
4107 | 0 | for (addr = a_start_blk; addr <= a_end_blk; addr++) { |
4108 | 0 | int retval; |
4109 | 0 | int myflags; |
4110 | | |
4111 | | /* identify if the cluster is allocated or not */ |
4112 | 0 | retval = is_clustalloc(ntfs, addr); |
4113 | 0 | if (retval == -1) { |
4114 | 0 | tsk_fs_block_free(fs_block); |
4115 | 0 | return 1; |
4116 | 0 | } |
4117 | | |
4118 | 0 | else if (retval == 1) { |
4119 | 0 | myflags = TSK_FS_BLOCK_FLAG_ALLOC; |
4120 | 0 | } |
4121 | 0 | else { |
4122 | 0 | myflags = TSK_FS_BLOCK_FLAG_UNALLOC; |
4123 | 0 | } |
4124 | | |
4125 | | // test if we should call the callback with this one |
4126 | 0 | if ((myflags & TSK_FS_BLOCK_FLAG_ALLOC) |
4127 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC))) |
4128 | 0 | continue; |
4129 | 0 | else if ((myflags & TSK_FS_BLOCK_FLAG_UNALLOC) |
4130 | 0 | && (!(a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC))) |
4131 | 0 | continue; |
4132 | | |
4133 | 0 | if (a_flags & TSK_FS_BLOCK_WALK_FLAG_AONLY) |
4134 | 0 | myflags |= TSK_FS_BLOCK_FLAG_AONLY; |
4135 | |
|
4136 | 0 | if (tsk_fs_block_get_flag(fs, fs_block, addr, |
4137 | 0 | (TSK_FS_BLOCK_FLAG_ENUM) myflags) == NULL) { |
4138 | 0 | tsk_error_set_errstr2 |
4139 | 0 | ("ntfs_block_walk: Error reading block at %" PRIuDADDR, |
4140 | 0 | addr); |
4141 | 0 | tsk_fs_block_free(fs_block); |
4142 | 0 | return 1; |
4143 | 0 | } |
4144 | | |
4145 | 0 | retval = a_action(fs_block, a_ptr); |
4146 | 0 | if (retval == TSK_WALK_STOP) { |
4147 | 0 | break; |
4148 | 0 | } |
4149 | 0 | else if (retval == TSK_WALK_ERROR) { |
4150 | 0 | tsk_fs_block_free(fs_block); |
4151 | 0 | return 1; |
4152 | 0 | } |
4153 | 0 | } |
4154 | | |
4155 | 0 | tsk_fs_block_free(fs_block); |
4156 | 0 | return 0; |
4157 | 0 | } |
4158 | | |
4159 | | |
4160 | | |
4161 | | /* |
4162 | | * inode_walk |
4163 | | * |
4164 | | * Flags: TSK_FS_META_FLAG_ALLOC, TSK_FS_META_FLAG_UNALLOC, |
4165 | | * TSK_FS_META_FLAG_USED, TSK_FS_META_FLAG_UNUSED, TSK_FS_META_FLAG_ORPHAN |
4166 | | * |
4167 | | * Note that with ORPHAN, entries will be found that can also be |
4168 | | * found by searching based on parent directories (if parent directory is |
4169 | | * known) |
4170 | | */ |
4171 | | static uint8_t |
4172 | | ntfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, |
4173 | | TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM flags, |
4174 | | TSK_FS_META_WALK_CB a_action, void *ptr) |
4175 | 0 | { |
4176 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) fs; |
4177 | 0 | unsigned int myflags; |
4178 | 0 | TSK_INUM_T mftnum; |
4179 | 0 | TSK_INUM_T end_inum_tmp; |
4180 | 0 | ntfs_mft *mft; |
4181 | | /* |
4182 | | * Sanity checks. |
4183 | | */ |
4184 | 0 | if (start_inum < fs->first_inum) { |
4185 | 0 | tsk_error_reset(); |
4186 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
4187 | 0 | tsk_error_set_errstr |
4188 | 0 | ("inode_walk: Starting inode number is too small (%" PRIuINUM |
4189 | 0 | ")", start_inum); |
4190 | 0 | return 1; |
4191 | 0 | } |
4192 | 0 | if (start_inum > fs->last_inum) { |
4193 | 0 | tsk_error_reset(); |
4194 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
4195 | 0 | tsk_error_set_errstr |
4196 | 0 | ("inode_walk: Starting inode number is too large (%" PRIuINUM |
4197 | 0 | ")", start_inum); |
4198 | 0 | return 1; |
4199 | 0 | } |
4200 | 0 | if (end_inum < fs->first_inum) { |
4201 | 0 | tsk_error_reset(); |
4202 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
4203 | 0 | tsk_error_set_errstr |
4204 | 0 | ("inode_walk: Ending inode number is too small (%" PRIuINUM |
4205 | 0 | ")", end_inum); |
4206 | 0 | return 1; |
4207 | 0 | } |
4208 | 0 | if (end_inum > fs->last_inum) { |
4209 | 0 | tsk_error_reset(); |
4210 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
4211 | 0 | tsk_error_set_errstr("Ending inode number is too large (%" PRIuINUM |
4212 | 0 | ")", end_inum); |
4213 | 0 | return 1; |
4214 | 0 | } |
4215 | | |
4216 | | |
4217 | | /* If ORPHAN is wanted, then make sure that the flags are correct */ |
4218 | 0 | if (flags & TSK_FS_META_FLAG_ORPHAN) { |
4219 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_UNALLOC); |
4220 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_ALLOC); |
4221 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED); |
4222 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags & ~TSK_FS_META_FLAG_UNUSED); |
4223 | 0 | } |
4224 | | |
4225 | 0 | else { |
4226 | 0 | if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) && |
4227 | 0 | ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) { |
4228 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC); |
4229 | 0 | } |
4230 | | |
4231 | | /* If neither of the USED or UNUSED flags are set, then set them |
4232 | | * both |
4233 | | */ |
4234 | 0 | if (((flags & TSK_FS_META_FLAG_USED) == 0) && |
4235 | 0 | ((flags & TSK_FS_META_FLAG_UNUSED) == 0)) { |
4236 | 0 | flags = (TSK_FS_META_FLAG_ENUM) (flags | TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED); |
4237 | 0 | } |
4238 | 0 | } |
4239 | | |
4240 | | |
4241 | | /* If we are looking for orphan files and have not yet filled |
4242 | | * in the list of unalloc inodes that are pointed to, then fill |
4243 | | * in the list |
4244 | | * */ |
4245 | 0 | if ((flags & TSK_FS_META_FLAG_ORPHAN)) { |
4246 | 0 | if (tsk_fs_dir_load_inum_named(fs) != TSK_OK) { |
4247 | 0 | tsk_error_errstr2_concat |
4248 | 0 | ("- ntfs_inode_walk: identifying inodes allocated by file names"); |
4249 | 0 | return 1; |
4250 | 0 | } |
4251 | 0 | } |
4252 | | |
4253 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
4254 | 0 | tsk_fs_file_alloc(fs), |
4255 | 0 | tsk_fs_file_close |
4256 | 0 | }; |
4257 | |
|
4258 | 0 | if (!fs_file) |
4259 | 0 | return 1; |
4260 | | |
4261 | 0 | if ((fs_file->meta = tsk_fs_meta_alloc(NTFS_FILE_CONTENT_LEN)) == NULL) { |
4262 | | // JRB: Coverity CID: 348 |
4263 | 0 | return 1; |
4264 | 0 | } |
4265 | | |
4266 | 0 | if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) { |
4267 | 0 | return 1; |
4268 | 0 | } |
4269 | | // we need to handle fs->last_inum specially because it is for the |
4270 | | // virtual ORPHANS directory. Handle it outside of the loop. |
4271 | 0 | if (end_inum == TSK_FS_ORPHANDIR_INUM(fs)) |
4272 | 0 | end_inum_tmp = end_inum - 1; |
4273 | 0 | else |
4274 | 0 | end_inum_tmp = end_inum; |
4275 | | |
4276 | |
|
4277 | 0 | for (mftnum = start_inum; mftnum <= end_inum_tmp; mftnum++) { |
4278 | 0 | int retval; |
4279 | 0 | TSK_RETVAL_ENUM retval2; |
4280 | | |
4281 | | /* read MFT entry in to NTFS_INFO */ |
4282 | 0 | if ((retval2 = |
4283 | 0 | ntfs_dinode_lookup(ntfs, (char *) mft, |
4284 | 0 | mftnum, & (fs_file->meta->start_of_inode))) != TSK_OK) { |
4285 | | // if the entry is corrupt, then skip to the next one |
4286 | 0 | if (retval2 == TSK_COR) { |
4287 | 0 | if (tsk_verbose) |
4288 | 0 | tsk_error_print(stderr); |
4289 | 0 | tsk_error_reset(); |
4290 | 0 | continue; |
4291 | 0 | } |
4292 | 0 | free(mft); |
4293 | 0 | return 1; |
4294 | 0 | } |
4295 | | |
4296 | | /* we only want to look at base file records |
4297 | | * (extended are because the base could not fit into one) |
4298 | | */ |
4299 | 0 | if (tsk_getu48(fs->endian, mft->base_ref) != NTFS_MFT_BASE) |
4300 | 0 | continue; |
4301 | | |
4302 | | /* NOTE: We could add a sanity check here with the MFT bitmap |
4303 | | * to validate of the INUSE flag and bitmap are in agreement |
4304 | | */ |
4305 | | /* check flags */ |
4306 | 0 | myflags = |
4307 | 0 | ((tsk_getu16(fs->endian, mft->flags) & |
4308 | 0 | NTFS_MFT_INUSE) ? TSK_FS_META_FLAG_ALLOC : |
4309 | 0 | TSK_FS_META_FLAG_UNALLOC); |
4310 | | |
4311 | | /* If we want only orphans, then check if this |
4312 | | * inode is in the seen list |
4313 | | * */ |
4314 | 0 | if ((myflags & TSK_FS_META_FLAG_UNALLOC) && |
4315 | 0 | (flags & TSK_FS_META_FLAG_ORPHAN) && |
4316 | 0 | (tsk_fs_dir_find_inum_named(fs, mftnum))) { |
4317 | 0 | continue; |
4318 | 0 | } |
4319 | | |
4320 | | /* copy into generic format */ |
4321 | 0 | if ((retval = |
4322 | 0 | ntfs_dinode_copy(ntfs, fs_file.get(), (char *) mft, |
4323 | 0 | mftnum)) != TSK_OK) { |
4324 | | // continue on if there were only corruption problems |
4325 | 0 | if (retval == TSK_COR) { |
4326 | 0 | if (tsk_verbose) |
4327 | 0 | tsk_error_print(stderr); |
4328 | 0 | tsk_error_reset(); |
4329 | 0 | continue; |
4330 | 0 | } |
4331 | 0 | free(mft); |
4332 | 0 | return 1; |
4333 | 0 | } |
4334 | | |
4335 | 0 | myflags |= |
4336 | 0 | (fs_file->meta->flags & (TSK_FS_META_FLAG_USED | |
4337 | 0 | TSK_FS_META_FLAG_UNUSED)); |
4338 | 0 | if ((flags & myflags) != myflags) |
4339 | 0 | continue; |
4340 | | |
4341 | | /* call action */ |
4342 | 0 | retval = a_action(fs_file.get(), ptr); |
4343 | 0 | if (retval == TSK_WALK_STOP) { |
4344 | 0 | free(mft); |
4345 | 0 | return 0; |
4346 | 0 | } |
4347 | 0 | else if (retval == TSK_WALK_ERROR) { |
4348 | 0 | free(mft); |
4349 | 0 | return 1; |
4350 | 0 | } |
4351 | 0 | } |
4352 | | |
4353 | | // handle the virtual orphans folder if they asked for it |
4354 | 0 | if ((end_inum == TSK_FS_ORPHANDIR_INUM(fs)) |
4355 | 0 | && (flags & TSK_FS_META_FLAG_ALLOC) |
4356 | 0 | && (flags & TSK_FS_META_FLAG_USED)) { |
4357 | 0 | int retval; |
4358 | |
|
4359 | 0 | if (tsk_fs_dir_make_orphan_dir_meta(fs, fs_file->meta)) { |
4360 | 0 | free(mft); |
4361 | 0 | return 1; |
4362 | 0 | } |
4363 | | /* call action */ |
4364 | 0 | retval = a_action(fs_file.get(), ptr); |
4365 | 0 | if (retval == TSK_WALK_STOP) { |
4366 | 0 | free(mft); |
4367 | 0 | return 0; |
4368 | 0 | } |
4369 | 0 | else if (retval == TSK_WALK_ERROR) { |
4370 | 0 | free(mft); |
4371 | 0 | return 1; |
4372 | 0 | } |
4373 | 0 | } |
4374 | | |
4375 | 0 | free(mft); |
4376 | 0 | return 0; |
4377 | 0 | } |
4378 | | |
4379 | | |
4380 | | |
4381 | | static uint8_t |
4382 | | ntfs_fscheck( |
4383 | | [[maybe_unused]] TSK_FS_INFO * fs, |
4384 | | [[maybe_unused]] FILE * hFile) |
4385 | 0 | { |
4386 | 0 | tsk_error_reset(); |
4387 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
4388 | 0 | tsk_error_set_errstr("fscheck not implemented for NTFS yet"); |
4389 | 0 | return 1; |
4390 | 0 | } |
4391 | | |
4392 | | |
4393 | | /** |
4394 | | * Print details about the file system to a file handle. |
4395 | | * |
4396 | | * @param fs File system to print details on |
4397 | | * @param hFile File handle to print text to |
4398 | | * |
4399 | | * @returns 1 on error and 0 on success |
4400 | | */ |
4401 | | static uint8_t |
4402 | | ntfs_fsstat(TSK_FS_INFO * fs, FILE * hFile) |
4403 | 0 | { |
4404 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) fs; |
4405 | 0 | const TSK_FS_ATTR *fs_attr; |
4406 | 0 | char asc[512]; |
4407 | 0 | ntfs_attrdef *attrdeftmp; |
4408 | |
|
4409 | 0 | tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n"); |
4410 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
4411 | 0 | tsk_fprintf(hFile, "File System Type: NTFS\n"); |
4412 | 0 | tsk_fprintf(hFile, |
4413 | 0 | "Volume Serial Number: %.16" PRIX64 |
4414 | 0 | "\n", tsk_getu64(fs->endian, ntfs->fs->serial)); |
4415 | 0 | tsk_fprintf(hFile, "OEM Name: %c%c%c%c%c%c%c%c\n", |
4416 | 0 | ntfs->fs->oemname[0], |
4417 | 0 | ntfs->fs->oemname[1], |
4418 | 0 | ntfs->fs->oemname[2], |
4419 | 0 | ntfs->fs->oemname[3], |
4420 | 0 | ntfs->fs->oemname[4], |
4421 | 0 | ntfs->fs->oemname[5], ntfs->fs->oemname[6], ntfs->fs->oemname[7]); |
4422 | | /* |
4423 | | * Volume |
4424 | | */ |
4425 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
4426 | 0 | tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_VOL), |
4427 | 0 | tsk_fs_file_close |
4428 | 0 | }; |
4429 | |
|
4430 | 0 | if (!fs_file) { |
4431 | 0 | tsk_error_reset(); |
4432 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); |
4433 | 0 | tsk_error_errstr2_concat |
4434 | 0 | (" - fsstat: Error finding Volume MFT Entry"); |
4435 | 0 | return 1; |
4436 | 0 | } |
4437 | | |
4438 | 0 | fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_VNAME); |
4439 | 0 | if (!fs_attr) { |
4440 | 0 | tsk_error_reset(); |
4441 | 0 | tsk_error_set_errno(TSK_ERR_FS_INODE_COR); |
4442 | 0 | tsk_error_set_errstr("Volume Name attribute not found in $Volume"); |
4443 | 0 | return 1; |
4444 | 0 | } |
4445 | | |
4446 | 0 | if ((fs_attr->flags & TSK_FS_ATTR_RES) |
4447 | 0 | && (fs_attr->size)) { |
4448 | |
|
4449 | 0 | UTF16 *name16 = (UTF16 *) fs_attr->rd.buf; |
4450 | 0 | UTF8 *name8 = (UTF8 *) asc; |
4451 | 0 | int retVal; |
4452 | 0 | retVal = |
4453 | 0 | tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, |
4454 | 0 | (UTF16 *) ((uintptr_t) name16 + |
4455 | 0 | (int) fs_attr->size), &name8, |
4456 | 0 | (UTF8 *) ((uintptr_t) name8 + sizeof(asc)), |
4457 | 0 | TSKlenientConversion); |
4458 | 0 | if (retVal != TSKconversionOK) { |
4459 | 0 | if (tsk_verbose) |
4460 | 0 | tsk_fprintf(stderr, |
4461 | 0 | "fsstat: Error converting NTFS Volume label to UTF8: %d", |
4462 | 0 | retVal); |
4463 | 0 | *name8 = '\0'; |
4464 | 0 | } |
4465 | | |
4466 | | /* Make sure it is NULL Terminated */ |
4467 | 0 | else if ((uintptr_t) name8 >= (uintptr_t) asc + sizeof(asc)) |
4468 | 0 | asc[sizeof(asc) - 1] = '\0'; |
4469 | 0 | else |
4470 | 0 | *name8 = '\0'; |
4471 | 0 | tsk_fprintf(hFile, "Volume Name: %s\n", asc); |
4472 | 0 | } |
4473 | |
|
4474 | 0 | fs_attr = NULL; |
4475 | 0 | if (ntfs->ver == NTFS_VINFO_NT) |
4476 | 0 | tsk_fprintf(hFile, "Version: Windows NT\n"); |
4477 | 0 | else if (ntfs->ver == NTFS_VINFO_2K) |
4478 | 0 | tsk_fprintf(hFile, "Version: Windows 2000\n"); |
4479 | 0 | else if (ntfs->ver == NTFS_VINFO_XP) |
4480 | 0 | tsk_fprintf(hFile, "Version: Windows XP\n"); |
4481 | 0 | tsk_fprintf(hFile, "\nMETADATA INFORMATION\n"); |
4482 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
4483 | 0 | tsk_fprintf(hFile, |
4484 | 0 | "First Cluster of MFT: %" PRIu64 "\n", |
4485 | 0 | tsk_getu64(fs->endian, ntfs->fs->mft_clust)); |
4486 | 0 | tsk_fprintf(hFile, |
4487 | 0 | "First Cluster of MFT Mirror: %" |
4488 | 0 | PRIu64 "\n", tsk_getu64(fs->endian, ntfs->fs->mftm_clust)); |
4489 | 0 | tsk_fprintf(hFile, |
4490 | 0 | "Size of MFT Entries: %" PRIu16 " bytes\n", ntfs->mft_rsize_b); |
4491 | 0 | tsk_fprintf(hFile, |
4492 | 0 | "Size of Index Records: %" PRIu16 " bytes\n", ntfs->idx_rsize_b); |
4493 | 0 | tsk_fprintf(hFile, |
4494 | 0 | "Range: %" PRIuINUM " - %" PRIuINUM |
4495 | 0 | "\n", fs->first_inum, fs->last_inum); |
4496 | 0 | tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum); |
4497 | 0 | tsk_fprintf(hFile, "\nCONTENT INFORMATION\n"); |
4498 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
4499 | 0 | tsk_fprintf(hFile, "Sector Size: %" PRIu16 "\n", ntfs->ssize_b); |
4500 | 0 | tsk_fprintf(hFile, "Cluster Size: %" PRIu16 "\n", ntfs->csize_b); |
4501 | 0 | tsk_fprintf(hFile, |
4502 | 0 | "Total Cluster Range: %" PRIuDADDR |
4503 | 0 | " - %" PRIuDADDR "\n", fs->first_block, fs->last_block); |
4504 | |
|
4505 | 0 | if (fs->last_block != fs->last_block_act) |
4506 | 0 | tsk_fprintf(hFile, |
4507 | 0 | "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n", |
4508 | 0 | fs->first_block, fs->last_block_act); |
4509 | |
|
4510 | 0 | tsk_fprintf(hFile, |
4511 | 0 | "Total Sector Range: 0 - %" PRIu64 |
4512 | 0 | "\n", tsk_getu64(fs->endian, ntfs->fs->vol_size_s) - 1); |
4513 | | /* |
4514 | | * Attrdef Info |
4515 | | */ |
4516 | 0 | tsk_fprintf(hFile, "\n$AttrDef Attribute Values:\n"); |
4517 | 0 | if (!ntfs->attrdef) { |
4518 | 0 | if (ntfs_load_attrdef(ntfs)) { |
4519 | 0 | tsk_fprintf(hFile, "Error loading attribute definitions\n"); |
4520 | 0 | goto attrdef_egress; |
4521 | 0 | } |
4522 | 0 | } |
4523 | | |
4524 | 0 | attrdeftmp = ntfs->attrdef; |
4525 | 0 | while ((((uintptr_t) attrdeftmp - (uintptr_t) ntfs->attrdef + |
4526 | 0 | sizeof(ntfs_attrdef)) < ntfs->attrdef_len) && |
4527 | 0 | (tsk_getu32(fs->endian, attrdeftmp->type))) { |
4528 | 0 | UTF16 *name16 = (UTF16 *) attrdeftmp->label; |
4529 | 0 | UTF8 *name8 = (UTF8 *) asc; |
4530 | 0 | int retVal; |
4531 | 0 | retVal = |
4532 | 0 | tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, |
4533 | 0 | (UTF16 *) ((uintptr_t) name16 + |
4534 | 0 | sizeof(attrdeftmp->label)), |
4535 | 0 | &name8, |
4536 | 0 | (UTF8 *) ((uintptr_t) name8 + sizeof(asc)), |
4537 | 0 | TSKlenientConversion); |
4538 | 0 | if (retVal != TSKconversionOK) { |
4539 | 0 | if (tsk_verbose) |
4540 | 0 | tsk_fprintf(stderr, |
4541 | 0 | "fsstat: Error converting NTFS attribute def label to UTF8: %d", |
4542 | 0 | retVal); |
4543 | 0 | *name8 = '\0'; |
4544 | 0 | } |
4545 | | |
4546 | | /* Make sure it is NULL Terminated */ |
4547 | 0 | else if ((uintptr_t) name8 >= (uintptr_t) asc + sizeof(asc)) |
4548 | 0 | asc[sizeof(asc) - 1] = '\0'; |
4549 | 0 | else |
4550 | 0 | *name8 = '\0'; |
4551 | 0 | tsk_fprintf(hFile, "%s (%" PRIu32 ") ", |
4552 | 0 | asc, tsk_getu32(fs->endian, attrdeftmp->type)); |
4553 | 0 | if ((tsk_getu64(fs->endian, attrdeftmp->minsize) == 0) && |
4554 | 0 | (tsk_getu64(fs->endian, |
4555 | 0 | attrdeftmp->maxsize) == 0xffffffffffffffffULL)) { |
4556 | |
|
4557 | 0 | tsk_fprintf(hFile, "Size: No Limit"); |
4558 | 0 | } |
4559 | 0 | else { |
4560 | 0 | tsk_fprintf(hFile, "Size: %" PRIu64 "-%" PRIu64, |
4561 | 0 | tsk_getu64(fs->endian, attrdeftmp->minsize), |
4562 | 0 | tsk_getu64(fs->endian, attrdeftmp->maxsize)); |
4563 | 0 | } |
4564 | |
|
4565 | 0 | tsk_fprintf(hFile, " Flags: %s%s%s\n", |
4566 | 0 | (tsk_getu32(fs->endian, attrdeftmp->flags) & |
4567 | 0 | NTFS_ATTRDEF_FLAGS_RES ? "Resident" : |
4568 | 0 | ""), (tsk_getu32(fs->endian, |
4569 | 0 | attrdeftmp->flags) & |
4570 | 0 | NTFS_ATTRDEF_FLAGS_NONRES ? |
4571 | 0 | "Non-resident" : ""), |
4572 | 0 | (tsk_getu32(fs->endian, attrdeftmp->flags) & |
4573 | 0 | NTFS_ATTRDEF_FLAGS_IDX ? ",Index" : "")); |
4574 | 0 | attrdeftmp++; |
4575 | 0 | } |
4576 | |
|
4577 | 0 | attrdef_egress: |
4578 | |
|
4579 | 0 | return 0; |
4580 | 0 | } |
4581 | | |
4582 | | |
4583 | | /************************* istat *******************************/ |
4584 | | |
4585 | 0 | #define NTFS_PRINT_WIDTH 8 |
4586 | | typedef struct { |
4587 | | FILE *hFile; |
4588 | | int idx; |
4589 | | } NTFS_PRINT_ADDR; |
4590 | | static TSK_WALK_RET_ENUM |
4591 | | print_addr_act( |
4592 | | [[maybe_unused]] TSK_FS_FILE * fs_file, |
4593 | | [[maybe_unused]] TSK_OFF_T a_off, |
4594 | | TSK_DADDR_T addr, |
4595 | | [[maybe_unused]] char *buf, |
4596 | | [[maybe_unused]] size_t size, |
4597 | | [[maybe_unused]] TSK_FS_BLOCK_FLAG_ENUM flags, |
4598 | | void *ptr) |
4599 | 0 | { |
4600 | 0 | NTFS_PRINT_ADDR *print = (NTFS_PRINT_ADDR *) ptr; |
4601 | 0 | tsk_fprintf(print->hFile, "%" PRIuDADDR " ", addr); |
4602 | 0 | if (++(print->idx) == NTFS_PRINT_WIDTH) { |
4603 | 0 | tsk_fprintf(print->hFile, "\n"); |
4604 | 0 | print->idx = 0; |
4605 | 0 | } |
4606 | |
|
4607 | 0 | return TSK_WALK_CONT; |
4608 | 0 | } |
4609 | | |
4610 | | /** |
4611 | | * Print details on a specific file to a file handle. |
4612 | | * |
4613 | | * @param fs File system file is located in |
4614 | | * @param hFile File name to print text to |
4615 | | * @param inum Address of file in file system |
4616 | | * @param numblock The number of blocks in file to force print (can go beyond file size) |
4617 | | * @param sec_skew Clock skew in seconds to also print times in |
4618 | | * |
4619 | | * @returns 1 on error and 0 on success |
4620 | | */ |
4621 | | static uint8_t |
4622 | | ntfs_istat( |
4623 | | TSK_FS_INFO * fs, |
4624 | | TSK_FS_ISTAT_FLAG_ENUM istat_flags, |
4625 | | FILE * hFile, |
4626 | | TSK_INUM_T inum, |
4627 | | [[maybe_unused]] TSK_DADDR_T numblock, |
4628 | | int32_t sec_skew) |
4629 | 0 | { |
4630 | 0 | const TSK_FS_ATTR *fs_attr; |
4631 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) fs; |
4632 | 0 | ntfs_mft *mft; |
4633 | 0 | char timeBuf[128]; |
4634 | 0 | int idx; |
4635 | | |
4636 | | // clean up any error messages that are lying around |
4637 | 0 | tsk_error_reset(); |
4638 | |
|
4639 | 0 | if ((mft = (ntfs_mft *) tsk_malloc(ntfs->mft_rsize_b)) == NULL) { |
4640 | 0 | return 1; |
4641 | 0 | } |
4642 | | |
4643 | 0 | if (ntfs_dinode_lookup(ntfs, (char *) mft, inum, 0)) { |
4644 | 0 | free(mft); |
4645 | 0 | return 1; |
4646 | 0 | } |
4647 | | |
4648 | 0 | std::unique_ptr<TSK_FS_FILE, decltype(&tsk_fs_file_close)> fs_file{ |
4649 | 0 | tsk_fs_file_open_meta(fs, NULL, inum), |
4650 | 0 | tsk_fs_file_close |
4651 | 0 | }; |
4652 | |
|
4653 | 0 | if (!fs_file) { |
4654 | 0 | tsk_error_errstr2_concat(" - istat"); |
4655 | 0 | free(mft); |
4656 | 0 | return 1; |
4657 | 0 | } |
4658 | | |
4659 | 0 | tsk_fprintf(hFile, "MFT Entry Header Values:\n"); |
4660 | 0 | tsk_fprintf(hFile, |
4661 | 0 | "Entry: %" PRIuINUM |
4662 | 0 | " Sequence: %" PRIu32 "\n", inum, fs_file->meta->seq); |
4663 | 0 | if (tsk_getu48(fs->endian, mft->base_ref) != 0) { |
4664 | 0 | tsk_fprintf(hFile, |
4665 | 0 | "Base File Record: %" PRIu64 "\n", |
4666 | 0 | (uint64_t) tsk_getu48(fs->endian, mft->base_ref)); |
4667 | 0 | } |
4668 | |
|
4669 | 0 | tsk_fprintf(hFile, |
4670 | 0 | "$LogFile Sequence Number: %" PRIu64 |
4671 | 0 | "\n", tsk_getu64(fs->endian, mft->lsn)); |
4672 | 0 | tsk_fprintf(hFile, "%sAllocated %s\n", |
4673 | 0 | (fs_file->meta->flags & TSK_FS_META_FLAG_ALLOC) ? "" : |
4674 | 0 | "Not ", |
4675 | 0 | TSK_FS_IS_DIR_META(fs_file->meta->type) ? "Directory" : "File"); |
4676 | 0 | tsk_fprintf(hFile, "Links: %u\n", fs_file->meta->nlink); |
4677 | | |
4678 | | /* STANDARD_INFORMATION info */ |
4679 | 0 | fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_SI); |
4680 | 0 | if (fs_attr) { |
4681 | 0 | ntfs_attr_si *si = (ntfs_attr_si *) fs_attr->rd.buf; |
4682 | 0 | char *sid_str; |
4683 | |
|
4684 | 0 | int a = 0; |
4685 | 0 | tsk_fprintf(hFile, "\n$STANDARD_INFORMATION Attribute Values:\n"); |
4686 | 0 | tsk_fprintf(hFile, "Flags: "); |
4687 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_RO) |
4688 | 0 | tsk_fprintf(hFile, "%sRead Only", a++ == 0 ? "" : ", "); |
4689 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_HID) |
4690 | 0 | tsk_fprintf(hFile, "%sHidden", a++ == 0 ? "" : ", "); |
4691 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_SYS) |
4692 | 0 | tsk_fprintf(hFile, "%sSystem", a++ == 0 ? "" : ", "); |
4693 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_ARCH) |
4694 | 0 | tsk_fprintf(hFile, "%sArchive", a++ == 0 ? "" : ", "); |
4695 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_DEV) |
4696 | 0 | tsk_fprintf(hFile, "%sDevice", a++ == 0 ? "" : ", "); |
4697 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_NORM) |
4698 | 0 | tsk_fprintf(hFile, "%sNormal", a++ == 0 ? "" : ", "); |
4699 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_TEMP) |
4700 | 0 | tsk_fprintf(hFile, "%sTemporary", a++ == 0 ? "" : ", "); |
4701 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_SPAR) |
4702 | 0 | tsk_fprintf(hFile, "%sSparse", a++ == 0 ? "" : ", "); |
4703 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_REP) |
4704 | 0 | tsk_fprintf(hFile, "%sReparse Point", a++ == 0 ? "" : ", "); |
4705 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_COMP) |
4706 | 0 | tsk_fprintf(hFile, "%sCompressed", a++ == 0 ? "" : ", "); |
4707 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_OFF) |
4708 | 0 | tsk_fprintf(hFile, "%sOffline", a++ == 0 ? "" : ", "); |
4709 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_NOIDX) |
4710 | 0 | tsk_fprintf(hFile, "%sNot Content Indexed", |
4711 | 0 | a++ == 0 ? "" : ", "); |
4712 | 0 | if (tsk_getu32(fs->endian, si->dos) & NTFS_SI_ENC) |
4713 | 0 | tsk_fprintf(hFile, "%sEncrypted", a++ == 0 ? "" : ", "); |
4714 | 0 | tsk_fprintf(hFile, "\n"); |
4715 | 0 | tsk_fprintf(hFile, "Owner ID: %" PRIu32 "\n", |
4716 | 0 | tsk_getu32(fs->endian, si->own_id)); |
4717 | |
|
4718 | 0 | #if TSK_USE_SID |
4719 | 0 | ntfs_file_get_sidstr(fs_file.get(), &sid_str); |
4720 | |
|
4721 | 0 | tsk_fprintf(hFile, "Security ID: %" PRIu32 " (%s)\n", |
4722 | 0 | tsk_getu32(fs->endian, si->sec_id), sid_str ? sid_str : ""); |
4723 | 0 | free(sid_str); |
4724 | 0 | sid_str = NULL; |
4725 | 0 | #endif |
4726 | | |
4727 | |
|
4728 | 0 | if (tsk_getu32(fs->endian, si->maxver) != 0) { |
4729 | 0 | tsk_fprintf(hFile, |
4730 | 0 | "Version %" PRIu32 " of %" PRIu32 |
4731 | 0 | "\n", tsk_getu32(fs->endian, si->ver), |
4732 | 0 | tsk_getu32(fs->endian, si->maxver)); |
4733 | 0 | } |
4734 | |
|
4735 | 0 | if (tsk_getu64(fs->endian, si->quota) != 0) { |
4736 | 0 | tsk_fprintf(hFile, "Quota Charged: %" PRIu64 "\n", |
4737 | 0 | tsk_getu64(fs->endian, si->quota)); |
4738 | 0 | } |
4739 | |
|
4740 | 0 | if (tsk_getu64(fs->endian, si->usn) != 0) { |
4741 | 0 | tsk_fprintf(hFile, |
4742 | 0 | "Last User Journal Update Sequence Number: %" |
4743 | 0 | PRIu64 "\n", tsk_getu64(fs->endian, si->usn)); |
4744 | 0 | } |
4745 | | |
4746 | | |
4747 | | /* Times - take it from fs_file->meta instead of redoing the work */ |
4748 | |
|
4749 | 0 | if (sec_skew != 0) { |
4750 | 0 | tsk_fprintf(hFile, "\nAdjusted times:\n"); |
4751 | 0 | if (fs_file->meta->mtime) |
4752 | 0 | fs_file->meta->mtime -= sec_skew; |
4753 | 0 | if (fs_file->meta->atime) |
4754 | 0 | fs_file->meta->atime -= sec_skew; |
4755 | 0 | if (fs_file->meta->ctime) |
4756 | 0 | fs_file->meta->ctime -= sec_skew; |
4757 | 0 | if (fs_file->meta->crtime) |
4758 | 0 | fs_file->meta->crtime -= sec_skew; |
4759 | |
|
4760 | 0 | tsk_fprintf(hFile, "Created:\t%s\n", |
4761 | 0 | tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->crtime), timeBuf)); |
4762 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
4763 | 0 | tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->mtime), timeBuf)); |
4764 | 0 | tsk_fprintf(hFile, "MFT Modified:\t%s\n", |
4765 | 0 | tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->ctime), timeBuf)); |
4766 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
4767 | 0 | tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->atime), timeBuf)); |
4768 | |
|
4769 | 0 | if (fs_file->meta->mtime) |
4770 | 0 | fs_file->meta->mtime += sec_skew; |
4771 | 0 | if (fs_file->meta->atime) |
4772 | 0 | fs_file->meta->atime += sec_skew; |
4773 | 0 | if (fs_file->meta->ctime) |
4774 | 0 | fs_file->meta->ctime += sec_skew; |
4775 | 0 | if (fs_file->meta->crtime) |
4776 | 0 | fs_file->meta->crtime += sec_skew; |
4777 | |
|
4778 | 0 | tsk_fprintf(hFile, "\nOriginal times:\n"); |
4779 | 0 | } |
4780 | |
|
4781 | 0 | tsk_fprintf(hFile, "Created:\t%s\n", |
4782 | 0 | tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->crtime), timeBuf)); |
4783 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
4784 | 0 | tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->mtime), timeBuf)); |
4785 | 0 | tsk_fprintf(hFile, "MFT Modified:\t%s\n", |
4786 | 0 | tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->ctime), timeBuf)); |
4787 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
4788 | 0 | tsk_fs_time_to_str_subsecs(WITHNANO(fs_file->meta->atime), timeBuf)); |
4789 | 0 | } |
4790 | | |
4791 | | /* $FILE_NAME Information */ |
4792 | 0 | for (idx = 0; idx < tsk_fs_attrlist_get_len(fs_file->meta->attr); idx++) { |
4793 | 0 | ntfs_attr_fname *fname; |
4794 | 0 | uint64_t flags; |
4795 | 0 | int a = 0; |
4796 | 0 | UTF16 *name16; |
4797 | 0 | UTF8 *name8; |
4798 | 0 | char name8buf[NTFS_MAXNAMLEN_UTF8 + 1]; |
4799 | 0 | int retVal; |
4800 | |
|
4801 | 0 | fs_attr = tsk_fs_attrlist_get_idx(fs_file->meta->attr, idx); |
4802 | 0 | if (fs_attr->type != NTFS_ATYPE_FNAME) { |
4803 | 0 | continue; |
4804 | 0 | } |
4805 | 0 | fname = (ntfs_attr_fname *) fs_attr->rd.buf; |
4806 | |
|
4807 | 0 | tsk_fprintf(hFile, "\n$FILE_NAME Attribute Values:\n"); |
4808 | 0 | flags = tsk_getu64(fs->endian, fname->flags); |
4809 | 0 | tsk_fprintf(hFile, "Flags: "); |
4810 | 0 | if (flags & NTFS_FNAME_FLAGS_DIR) |
4811 | 0 | tsk_fprintf(hFile, "%sDirectory", a++ == 0 ? "" : ", "); |
4812 | 0 | if (flags & NTFS_FNAME_FLAGS_DEV) |
4813 | 0 | tsk_fprintf(hFile, "%sDevice", a++ == 0 ? "" : ", "); |
4814 | 0 | if (flags & NTFS_FNAME_FLAGS_NORM) |
4815 | 0 | tsk_fprintf(hFile, "%sNormal", a++ == 0 ? "" : ", "); |
4816 | 0 | if (flags & NTFS_FNAME_FLAGS_RO) |
4817 | 0 | tsk_fprintf(hFile, "%sRead Only", a++ == 0 ? "" : ", "); |
4818 | 0 | if (flags & NTFS_FNAME_FLAGS_HID) |
4819 | 0 | tsk_fprintf(hFile, "%sHidden", a++ == 0 ? "" : ", "); |
4820 | 0 | if (flags & NTFS_FNAME_FLAGS_SYS) |
4821 | 0 | tsk_fprintf(hFile, "%sSystem", a++ == 0 ? "" : ", "); |
4822 | 0 | if (flags & NTFS_FNAME_FLAGS_ARCH) |
4823 | 0 | tsk_fprintf(hFile, "%sArchive", a++ == 0 ? "" : ", "); |
4824 | 0 | if (flags & NTFS_FNAME_FLAGS_TEMP) |
4825 | 0 | tsk_fprintf(hFile, "%sTemp", a++ == 0 ? "" : ", "); |
4826 | 0 | if (flags & NTFS_FNAME_FLAGS_SPAR) |
4827 | 0 | tsk_fprintf(hFile, "%sSparse", a++ == 0 ? "" : ", "); |
4828 | 0 | if (flags & NTFS_FNAME_FLAGS_REP) |
4829 | 0 | tsk_fprintf(hFile, "%sReparse Point", a++ == 0 ? "" : ", "); |
4830 | 0 | if (flags & NTFS_FNAME_FLAGS_COMP) |
4831 | 0 | tsk_fprintf(hFile, "%sCompressed", a++ == 0 ? "" : ", "); |
4832 | 0 | if (flags & NTFS_FNAME_FLAGS_ENC) |
4833 | 0 | tsk_fprintf(hFile, "%sEncrypted", a++ == 0 ? "" : ", "); |
4834 | 0 | if (flags & NTFS_FNAME_FLAGS_OFF) |
4835 | 0 | tsk_fprintf(hFile, "%sOffline", a++ == 0 ? "" : ", "); |
4836 | 0 | if (flags & NTFS_FNAME_FLAGS_NOIDX) |
4837 | 0 | tsk_fprintf(hFile, "%sNot Content Indexed", |
4838 | 0 | a++ == 0 ? "" : ", "); |
4839 | 0 | if (flags & NTFS_FNAME_FLAGS_IDXVIEW) |
4840 | 0 | tsk_fprintf(hFile, "%sIndex View", a++ == 0 ? "" : ", "); |
4841 | 0 | tsk_fprintf(hFile, "\n"); |
4842 | | |
4843 | |
|
4844 | 0 | name16 = (UTF16 *) & fname->name; |
4845 | 0 | name8 = (UTF8 *) name8buf; |
4846 | |
|
4847 | 0 | retVal = |
4848 | 0 | tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, |
4849 | 0 | (UTF16 *) ((uintptr_t) name16 + |
4850 | 0 | fname->nlen * 2), |
4851 | 0 | &name8, |
4852 | 0 | (UTF8 *) ((uintptr_t) name8 + NTFS_MAXNAMLEN_UTF8), |
4853 | 0 | TSKlenientConversion); |
4854 | 0 | if (retVal != TSKconversionOK) { |
4855 | 0 | if (tsk_verbose) |
4856 | 0 | tsk_fprintf(stderr, |
4857 | 0 | "ntfs_istat: Error converting NTFS name in $FNAME to UTF8: %d", |
4858 | 0 | retVal); |
4859 | 0 | *name8 = '\0'; |
4860 | 0 | } |
4861 | | /* Make sure it is NULL Terminated */ |
4862 | 0 | else if ((uintptr_t) name8 >= |
4863 | 0 | (uintptr_t) name8buf + NTFS_MAXNAMLEN_UTF8) |
4864 | 0 | name8buf[NTFS_MAXNAMLEN_UTF8] = '\0'; |
4865 | 0 | else |
4866 | 0 | *name8 = '\0'; |
4867 | | |
4868 | |
|
4869 | 0 | tsk_fprintf(hFile, "Name: %s\n", name8buf); |
4870 | |
|
4871 | 0 | tsk_fprintf(hFile, |
4872 | 0 | "Parent MFT Entry: %" PRIu64 |
4873 | 0 | " \tSequence: %" PRIu16 "\n", |
4874 | 0 | (uint64_t) tsk_getu48(fs->endian, fname->par_ref), |
4875 | 0 | tsk_getu16(fs->endian, fname->par_seq)); |
4876 | 0 | tsk_fprintf(hFile, |
4877 | 0 | "Allocated Size: %" PRIu64 |
4878 | 0 | " \tActual Size: %" PRIu64 "\n", |
4879 | 0 | tsk_getu64(fs->endian, fname->alloc_fsize), |
4880 | 0 | tsk_getu64(fs->endian, fname->real_fsize)); |
4881 | | /* |
4882 | | * Times |
4883 | | */ |
4884 | | |
4885 | | /* Times - take it from fs_file->meta instead of redoing the work */ |
4886 | |
|
4887 | 0 | if (sec_skew != 0) { |
4888 | 0 | tsk_fprintf(hFile, "\nAdjusted times:\n"); |
4889 | |
|
4890 | 0 | tsk_fprintf(hFile, "Created:\t%s\n", |
4891 | 0 | tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->crtime)) - sec_skew, nt2nano(tsk_getu64(fs->endian, fname->crtime)), timeBuf)); |
4892 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
4893 | 0 | tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->mtime)) - sec_skew, nt2nano(tsk_getu64(fs->endian, fname->mtime)), timeBuf)); |
4894 | 0 | tsk_fprintf(hFile, "MFT Modified:\t%s\n", |
4895 | 0 | tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->ctime)) - sec_skew, nt2nano(tsk_getu64(fs->endian, fname->ctime)), timeBuf)); |
4896 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
4897 | 0 | tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->atime)) - sec_skew, nt2nano(tsk_getu64(fs->endian, fname->atime)), timeBuf)); |
4898 | |
|
4899 | 0 | tsk_fprintf(hFile, "\nOriginal times:\n"); |
4900 | 0 | } |
4901 | |
|
4902 | 0 | tsk_fprintf(hFile, "Created:\t%s\n", |
4903 | 0 | tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->crtime)), nt2nano(tsk_getu64(fs->endian, fname->crtime)), timeBuf)); |
4904 | 0 | tsk_fprintf(hFile, "File Modified:\t%s\n", |
4905 | 0 | tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->mtime)), nt2nano(tsk_getu64(fs->endian, fname->mtime)), timeBuf)); |
4906 | 0 | tsk_fprintf(hFile, "MFT Modified:\t%s\n", |
4907 | 0 | tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->ctime)), nt2nano(tsk_getu64(fs->endian, fname->ctime)), timeBuf)); |
4908 | 0 | tsk_fprintf(hFile, "Accessed:\t%s\n", |
4909 | 0 | tsk_fs_time_to_str_subsecs(nt2unixtime(tsk_getu64(fs->endian, fname->atime)), nt2nano(tsk_getu64(fs->endian, fname->atime)), timeBuf)); |
4910 | 0 | } |
4911 | | |
4912 | | |
4913 | | /* $OBJECT_ID Information */ |
4914 | 0 | fs_attr = tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_OBJID); |
4915 | 0 | if (fs_attr) { |
4916 | 0 | ntfs_attr_objid *objid = (ntfs_attr_objid *) fs_attr->rd.buf; |
4917 | 0 | uint64_t id1, id2; |
4918 | 0 | tsk_fprintf(hFile, "\n$OBJECT_ID Attribute Values:\n"); |
4919 | 0 | id1 = tsk_getu64(fs->endian, objid->objid1); |
4920 | 0 | id2 = tsk_getu64(fs->endian, objid->objid2); |
4921 | 0 | tsk_fprintf(hFile, |
4922 | 0 | "Object Id: %.8" PRIx32 "-%.4" PRIx16 |
4923 | 0 | "-%.4" PRIx16 "-%.4" PRIx16 "-%.4" |
4924 | 0 | PRIx16 "%.8" PRIx32 "\n", |
4925 | 0 | tsk_getu32(fs->endian, objid->objid1), |
4926 | 0 | tsk_getu16(fs->endian, objid->objid2), |
4927 | 0 | tsk_getu16(fs->endian, objid->objid3), |
4928 | 0 | tsk_getu16(TSK_BIG_ENDIAN, objid->objid4), |
4929 | 0 | tsk_getu16(TSK_BIG_ENDIAN, objid->objid5), |
4930 | 0 | tsk_getu32(TSK_BIG_ENDIAN, objid->objid6)); |
4931 | | |
4932 | | /* The rest of the fields do not always exist. Check the attr size */ |
4933 | 0 | if (fs_attr->size > 16) { |
4934 | 0 | id1 = tsk_getu64(fs->endian, objid->orig_volid1); |
4935 | 0 | id2 = tsk_getu64(fs->endian, objid->orig_volid2); |
4936 | 0 | tsk_fprintf(hFile, |
4937 | 0 | "Birth Volume Id: %.8" PRIx32 "-%.4" |
4938 | 0 | PRIx16 "-%.4" PRIx16 "-%.4" PRIx16 |
4939 | 0 | "-%.12" PRIx64 "\n", |
4940 | 0 | (uint32_t) (id2 >> 32) & 0xffffffff, |
4941 | 0 | (uint16_t) (id2 >> 16) & 0xffff, |
4942 | 0 | (uint16_t) (id2 & 0xffff), |
4943 | 0 | (uint16_t) (id1 >> 48) & 0xffff, |
4944 | 0 | (uint64_t) (id1 & (uint64_t) |
4945 | 0 | 0x0000ffffffffffffULL)); |
4946 | 0 | } |
4947 | |
|
4948 | 0 | if (fs_attr->size > 32) { |
4949 | 0 | id1 = tsk_getu64(fs->endian, objid->orig_objid1); |
4950 | 0 | id2 = tsk_getu64(fs->endian, objid->orig_objid2); |
4951 | 0 | tsk_fprintf(hFile, |
4952 | 0 | "Birth Object Id: %.8" PRIx32 "-%.4" |
4953 | 0 | PRIx16 "-%.4" PRIx16 "-%.4" PRIx16 |
4954 | 0 | "-%.12" PRIx64 "\n", |
4955 | 0 | (uint32_t) (id2 >> 32) & 0xffffffff, |
4956 | 0 | (uint16_t) (id2 >> 16) & 0xffff, |
4957 | 0 | (uint16_t) (id2 & 0xffff), |
4958 | 0 | (uint16_t) (id1 >> 48) & 0xffff, |
4959 | 0 | (uint64_t) (id1 & (uint64_t) |
4960 | 0 | 0x0000ffffffffffffULL)); |
4961 | 0 | } |
4962 | |
|
4963 | 0 | if (fs_attr->size > 48) { |
4964 | 0 | id1 = tsk_getu64(fs->endian, objid->orig_domid1); |
4965 | 0 | id2 = tsk_getu64(fs->endian, objid->orig_domid2); |
4966 | 0 | tsk_fprintf(hFile, |
4967 | 0 | "Birth Domain Id: %.8" PRIx32 "-%.4" |
4968 | 0 | PRIx16 "-%.4" PRIx16 "-%.4" PRIx16 |
4969 | 0 | "-%.12" PRIx64 "\n", |
4970 | 0 | (uint32_t) (id2 >> 32) & 0xffffffff, |
4971 | 0 | (uint16_t) (id2 >> 16) & 0xffff, |
4972 | 0 | (uint16_t) (id2 & 0xffff), |
4973 | 0 | (uint16_t) (id1 >> 48) & 0xffff, |
4974 | 0 | (uint64_t) (id1 & (uint64_t) |
4975 | 0 | 0x0000ffffffffffffULL)); |
4976 | 0 | } |
4977 | 0 | } |
4978 | | |
4979 | | /* Attribute List Information */ |
4980 | 0 | fs_attr = |
4981 | 0 | tsk_fs_attrlist_get(fs_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_ATTRLIST); |
4982 | 0 | if (fs_attr) { |
4983 | 0 | char *buf; |
4984 | 0 | ntfs_attrlist *list; |
4985 | 0 | uintptr_t endaddr; |
4986 | 0 | TSK_FS_LOAD_FILE load_file; |
4987 | |
|
4988 | 0 | tsk_fprintf(hFile, "\n$ATTRIBUTE_LIST Attribute Values:\n"); |
4989 | | |
4990 | | /* Get a copy of the attribute list stream */ |
4991 | 0 | load_file.total = load_file.left = (size_t) fs_attr->size; |
4992 | 0 | load_file.cur = load_file.base = buf = |
4993 | 0 | (char*) tsk_malloc((size_t) fs_attr->size); |
4994 | 0 | if (buf == NULL) { |
4995 | 0 | free(mft); |
4996 | 0 | return 1; |
4997 | 0 | } |
4998 | | |
4999 | 0 | endaddr = (uintptr_t) buf + (uintptr_t) fs_attr->size; |
5000 | 0 | if (tsk_fs_attr_walk(fs_attr, |
5001 | 0 | TSK_FS_FILE_WALK_FLAG_NONE, tsk_fs_load_file_action, (void *) &load_file)) { |
5002 | 0 | tsk_fprintf(hFile, "error reading attribute list buffer\n"); |
5003 | 0 | tsk_error_reset(); |
5004 | 0 | goto egress; |
5005 | 0 | } |
5006 | | |
5007 | | /* this value should be zero, if not then we didn't read all of the |
5008 | | * buffer |
5009 | | */ |
5010 | 0 | if (load_file.left > 0) { |
5011 | 0 | tsk_fprintf(hFile, "error reading attribute list buffer\n"); |
5012 | 0 | goto egress; |
5013 | 0 | } |
5014 | | |
5015 | | /* Process the list & print the details */ |
5016 | 0 | for (list = (ntfs_attrlist *) buf; |
5017 | 0 | (list) && ((uintptr_t) list < endaddr) |
5018 | 0 | && (tsk_getu16(fs->endian, list->len) > 0); |
5019 | 0 | list = |
5020 | 0 | (ntfs_attrlist *) ((uintptr_t) list + tsk_getu16(fs->endian, |
5021 | 0 | list->len))) { |
5022 | 0 | tsk_fprintf(hFile, |
5023 | 0 | "Type: %" PRIu32 "-%" PRIu16 " \tMFT Entry: %" PRIu64 |
5024 | 0 | " \tVCN: %" PRIu64 "\n", tsk_getu32(fs->endian, |
5025 | 0 | list->type), tsk_getu16(fs->endian, list->id), |
5026 | 0 | (uint64_t) tsk_getu48(fs->endian, list->file_ref), |
5027 | 0 | tsk_getu64(fs->endian, list->start_vcn)); |
5028 | 0 | } |
5029 | 0 | egress: |
5030 | 0 | free(buf); |
5031 | 0 | } |
5032 | | |
5033 | | /* Print all of the attributes */ |
5034 | 0 | tsk_fprintf(hFile, "\nAttributes: \n"); |
5035 | 0 | if (fs_file->meta->attr) { |
5036 | 0 | int cnt, i; |
5037 | | |
5038 | | // cycle through the attributes |
5039 | 0 | cnt = tsk_fs_file_attr_getsize(fs_file.get()); |
5040 | 0 | for (i = 0; i < cnt; i++) { |
5041 | 0 | char type[512]; |
5042 | |
|
5043 | 0 | const TSK_FS_ATTR *fs_attr = |
5044 | 0 | tsk_fs_file_attr_get_idx(fs_file.get(), i); |
5045 | 0 | if (!fs_attr) |
5046 | 0 | continue; |
5047 | | |
5048 | 0 | if (ntfs_attrname_lookup(fs, fs_attr->type, type, 512)) { |
5049 | 0 | tsk_fprintf(hFile, "error looking attribute name\n"); |
5050 | 0 | break; |
5051 | 0 | } |
5052 | | |
5053 | | /* print the layout if it is non-resident and not "special" */ |
5054 | 0 | if (fs_attr->flags & TSK_FS_ATTR_NONRES) { |
5055 | 0 | NTFS_PRINT_ADDR print_addr; |
5056 | |
|
5057 | 0 | tsk_fprintf(hFile, |
5058 | 0 | "Type: %s (%" PRIu32 "-%" PRIu16 |
5059 | 0 | ") Name: %s Non-Resident%s%s%s size: %" |
5060 | 0 | PRIdOFF " init_size: %" PRIdOFF "\n", type, |
5061 | 0 | fs_attr->type, fs_attr->id, |
5062 | 0 | (fs_attr->name) ? fs_attr->name : "N/A", |
5063 | 0 | (fs_attr->flags & TSK_FS_ATTR_ENC) ? ", Encrypted" : |
5064 | 0 | "", |
5065 | 0 | (fs_attr->flags & TSK_FS_ATTR_COMP) ? ", Compressed" : |
5066 | 0 | "", |
5067 | 0 | (fs_attr->flags & TSK_FS_ATTR_SPARSE) ? ", Sparse" : |
5068 | 0 | "", fs_attr->size, fs_attr->nrd.initsize); |
5069 | 0 | if (istat_flags & TSK_FS_ISTAT_RUNLIST) { |
5070 | 0 | if (tsk_fs_attr_print(fs_attr, hFile)) { |
5071 | 0 | tsk_fprintf(hFile, "\nError creating run lists\n"); |
5072 | 0 | tsk_error_print(hFile); |
5073 | 0 | tsk_error_reset(); |
5074 | 0 | } |
5075 | 0 | } |
5076 | 0 | else { |
5077 | 0 | print_addr.idx = 0; |
5078 | 0 | print_addr.hFile = hFile; |
5079 | 0 | if (tsk_fs_file_walk_type(fs_file.get(), fs_attr->type, |
5080 | 0 | fs_attr->id, |
5081 | 0 | (TSK_FS_FILE_WALK_FLAG_ENUM) |
5082 | 0 | (TSK_FS_FILE_WALK_FLAG_AONLY | |
5083 | 0 | TSK_FS_FILE_WALK_FLAG_SLACK), |
5084 | 0 | print_addr_act, (void *)&print_addr)) { |
5085 | 0 | tsk_fprintf(hFile, "\nError walking file\n"); |
5086 | 0 | tsk_error_print(hFile); |
5087 | 0 | tsk_error_reset(); |
5088 | 0 | } |
5089 | 0 | if (print_addr.idx != 0) |
5090 | 0 | tsk_fprintf(hFile, "\n"); |
5091 | 0 | } |
5092 | |
|
5093 | 0 | } |
5094 | 0 | else { |
5095 | 0 | tsk_fprintf(hFile, |
5096 | 0 | "Type: %s (%" PRIu32 "-%" PRIu16 |
5097 | 0 | ") Name: %s Resident%s%s%s size: %" |
5098 | 0 | PRIdOFF "\n", type, fs_attr->type, |
5099 | 0 | fs_attr->id, |
5100 | 0 | (fs_attr->name) ? fs_attr->name : "N/A", |
5101 | 0 | (fs_attr->flags & TSK_FS_ATTR_ENC) ? ", Encrypted" |
5102 | 0 | : "", |
5103 | 0 | (fs_attr->flags & TSK_FS_ATTR_COMP) ? |
5104 | 0 | ", Compressed" : "", |
5105 | 0 | (fs_attr->flags & TSK_FS_ATTR_SPARSE) ? ", Sparse" : |
5106 | 0 | "", fs_attr->size); |
5107 | |
|
5108 | 0 | } |
5109 | 0 | } |
5110 | 0 | } |
5111 | |
|
5112 | 0 | free(mft); |
5113 | 0 | return 0; |
5114 | 0 | } |
5115 | | |
5116 | | |
5117 | | |
5118 | | /* JOURNAL CODE - MOVE TO NEW FILE AT SOME POINT */ |
5119 | | |
5120 | | static uint8_t |
5121 | | ntfs_jopen( |
5122 | | [[maybe_unused]] TSK_FS_INFO * fs, |
5123 | | [[maybe_unused]] TSK_INUM_T inum) |
5124 | 0 | { |
5125 | 0 | tsk_error_reset(); |
5126 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
5127 | 0 | tsk_error_set_errstr("NTFS Journal is not yet supported\n"); |
5128 | 0 | return 1; |
5129 | 0 | } |
5130 | | |
5131 | | static uint8_t |
5132 | | ntfs_jentry_walk( |
5133 | | [[maybe_unused]] TSK_FS_INFO * fs, |
5134 | | [[maybe_unused]] int flags, |
5135 | | [[maybe_unused]] TSK_FS_JENTRY_WALK_CB a_action, |
5136 | | [[maybe_unused]] void *ptr) |
5137 | 0 | { |
5138 | 0 | tsk_error_reset(); |
5139 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
5140 | 0 | tsk_error_set_errstr("NTFS Journal is not yet supported\n"); |
5141 | 0 | return 1; |
5142 | 0 | } |
5143 | | |
5144 | | static uint8_t |
5145 | | ntfs_jblk_walk( |
5146 | | [[maybe_unused]] TSK_FS_INFO * fs, |
5147 | | [[maybe_unused]] TSK_DADDR_T start, |
5148 | | [[maybe_unused]] TSK_DADDR_T end, |
5149 | | [[maybe_unused]] int flags, |
5150 | | [[maybe_unused]] TSK_FS_JBLK_WALK_CB a_action, |
5151 | | [[maybe_unused]] void *ptr) |
5152 | 0 | { |
5153 | 0 | tsk_error_reset(); |
5154 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC); |
5155 | 0 | tsk_error_set_errstr("NTFS Journal is not yet supported\n"); |
5156 | 0 | return 1; |
5157 | 0 | } |
5158 | | |
5159 | | |
5160 | | static TSK_FS_ATTR_TYPE_ENUM |
5161 | | ntfs_get_default_attr_type(const TSK_FS_FILE * a_file) |
5162 | 0 | { |
5163 | 0 | if ((a_file == NULL) || (a_file->meta == NULL)) |
5164 | 0 | return TSK_FS_ATTR_TYPE_DEFAULT; |
5165 | | |
5166 | | /* Use DATA for files and IDXROOT for dirs */ |
5167 | 0 | if (TSK_FS_IS_DIR_META(a_file->meta->type)) |
5168 | 0 | return TSK_FS_ATTR_TYPE_NTFS_IDXROOT; |
5169 | 0 | else |
5170 | 0 | return TSK_FS_ATTR_TYPE_NTFS_DATA; |
5171 | |
|
5172 | 0 | } |
5173 | | |
5174 | | |
5175 | | static void |
5176 | | ntfs_close(TSK_FS_INFO * fs) |
5177 | 0 | { |
5178 | 0 | NTFS_INFO *ntfs = (NTFS_INFO *) fs; |
5179 | |
|
5180 | 0 | if (fs == NULL) |
5181 | 0 | return; |
5182 | | |
5183 | 0 | #if TSK_USE_SID |
5184 | 0 | free(ntfs->sii_data.buffer); |
5185 | 0 | ntfs->sii_data.buffer = NULL; |
5186 | |
|
5187 | 0 | free(ntfs->sds_data.buffer); |
5188 | 0 | ntfs->sds_data.buffer = NULL; |
5189 | |
|
5190 | 0 | #endif |
5191 | |
|
5192 | 0 | fs->tag = 0; |
5193 | 0 | free(ntfs->fs); |
5194 | 0 | tsk_fs_attr_run_free(ntfs->bmap); |
5195 | 0 | free(ntfs->bmap_buf); |
5196 | 0 | tsk_fs_file_close(ntfs->mft_file); |
5197 | |
|
5198 | 0 | if (ntfs->orphan_map) |
5199 | 0 | ntfs_orphan_map_free(ntfs); |
5200 | |
|
5201 | 0 | tsk_deinit_lock(&ntfs->lock); |
5202 | 0 | tsk_deinit_lock(&ntfs->orphan_map_lock); |
5203 | 0 | #if TSK_USE_SID |
5204 | 0 | tsk_deinit_lock(&ntfs->sid_lock); |
5205 | 0 | #endif |
5206 | |
|
5207 | 0 | tsk_fs_free(fs); |
5208 | 0 | } |
5209 | | |
5210 | | /** |
5211 | | * Check if the boot format matches that produced in KAPE VHDs |
5212 | | * that are missing the 0x55AA marker. |
5213 | | * Will also set the endianness. |
5214 | | * |
5215 | | * @param ntfs_info File system info |
5216 | | * @returns 0 if format appeares valid, 1 otherwise |
5217 | | */ |
5218 | | static int |
5219 | 0 | process_kape_boot_format(NTFS_INFO* ntfs_info) { |
5220 | | |
5221 | | // Check that we have a VHD |
5222 | 0 | if (ntfs_info->fs_info.img_info->itype != TSK_IMG_TYPE_VHD_VHD) { |
5223 | 0 | return 1; |
5224 | 0 | } |
5225 | | |
5226 | | // Check that expected name is present |
5227 | 0 | if (strncmp(ntfs_info->fs->oemname, "NTFS ", 8) != 0) { |
5228 | 0 | return 1; |
5229 | 0 | } |
5230 | | |
5231 | | // Check endianness using the sector size |
5232 | 0 | uint16_t ssize = tsk_getu16(TSK_LIT_ENDIAN, ntfs_info->fs->ssize); |
5233 | 0 | if ((ssize != 0) && (ssize % 512 == 0)) { |
5234 | 0 | ntfs_info->fs_info.endian = TSK_LIT_ENDIAN; |
5235 | 0 | return 0; |
5236 | 0 | } |
5237 | 0 | ssize = tsk_getu16(TSK_BIG_ENDIAN, ntfs_info->fs->ssize); |
5238 | 0 | if ((ssize != 0) && (ssize % 512 == 0)) { |
5239 | 0 | ntfs_info->fs_info.endian = TSK_BIG_ENDIAN; |
5240 | 0 | return 0; |
5241 | 0 | } |
5242 | | |
5243 | 0 | return 1; |
5244 | 0 | } |
5245 | | |
5246 | | /** |
5247 | | * Open part of a disk image as an NTFS file system. |
5248 | | * |
5249 | | * @param img_info Disk image to analyze |
5250 | | * @param offset Byte offset where NTFS file system starts |
5251 | | * @param ftype Specific type of NTFS file system |
5252 | | * @param a_pass (Optional) bitlocker password |
5253 | | * @param test NOT USED |
5254 | | * @returns NULL on error or if data is not an NTFS file system |
5255 | | */ |
5256 | | TSK_FS_INFO * |
5257 | | ntfs_open( |
5258 | | TSK_IMG_INFO * img_info, |
5259 | | TSK_OFF_T offset, |
5260 | | TSK_FS_TYPE_ENUM ftype, |
5261 | | const char* a_pass, |
5262 | | [[maybe_unused]] uint8_t test) |
5263 | 0 | { |
5264 | 0 | const char *myname = "ntfs_open"; |
5265 | 0 | NTFS_INFO *ntfs = NULL; |
5266 | 0 | TSK_FS_INFO *fs = NULL; |
5267 | 0 | unsigned int len = 0; |
5268 | 0 | ssize_t cnt = 0; |
5269 | | |
5270 | | // clean up any error messages that are lying around |
5271 | 0 | tsk_error_reset(); |
5272 | |
|
5273 | 0 | if (TSK_FS_TYPE_ISNTFS(ftype) == 0) { |
5274 | 0 | tsk_error_reset(); |
5275 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
5276 | 0 | tsk_error_set_errstr("Invalid FS type in ntfs_open"); |
5277 | 0 | return NULL; |
5278 | 0 | } |
5279 | | |
5280 | 0 | if (img_info->sector_size == 0) { |
5281 | 0 | tsk_error_reset(); |
5282 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
5283 | 0 | tsk_error_set_errstr("ntfs_open: sector size is 0"); |
5284 | 0 | return NULL; |
5285 | 0 | } |
5286 | | |
5287 | 0 | uint32_t csize; |
5288 | |
|
5289 | 0 | if ((ntfs = (NTFS_INFO *) tsk_fs_malloc(sizeof(*ntfs))) == NULL) { |
5290 | 0 | goto on_error; |
5291 | 0 | } |
5292 | 0 | fs = &(ntfs->fs_info); |
5293 | |
|
5294 | 0 | fs->ftype = TSK_FS_TYPE_NTFS; |
5295 | 0 | fs->duname = "Cluster"; |
5296 | 0 | fs->flags = TSK_FS_INFO_FLAG_HAVE_SEQ; |
5297 | 0 | fs->tag = TSK_FS_INFO_TAG; |
5298 | |
|
5299 | 0 | fs->img_info = img_info; |
5300 | 0 | fs->offset = offset; |
5301 | |
|
5302 | 0 | ntfs->loading_the_MFT = 0; |
5303 | 0 | ntfs->bmap = NULL; |
5304 | 0 | ntfs->bmap_buf = NULL; |
5305 | | |
5306 | | // Check for any volume encryption and initialize if found. |
5307 | | // A non-zero value will only be returned if we are very confident encryption was found |
5308 | | // but encountered an error and should not continue trying to open the volume. |
5309 | | // In this case we should also have a specific error to get back to the user, such as reporting an incorrect password. |
5310 | 0 | if (0 != handleVolumeEncryption((TSK_FS_INFO*)ntfs, a_pass)) { |
5311 | 0 | goto on_error; |
5312 | 0 | } |
5313 | | |
5314 | | /* Read the boot sector */ |
5315 | 0 | len = roundup(sizeof(ntfs_sb), img_info->sector_size); |
5316 | 0 | ntfs->fs = (ntfs_sb *) tsk_malloc(len); |
5317 | 0 | if (ntfs->fs == NULL) { |
5318 | 0 | goto on_error; |
5319 | 0 | } |
5320 | | |
5321 | 0 | cnt = tsk_fs_read(fs, (TSK_OFF_T) 0, (char *) ntfs->fs, len); |
5322 | 0 | if (cnt != len) { |
5323 | 0 | if (cnt >= 0) { |
5324 | 0 | tsk_error_reset(); |
5325 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
5326 | 0 | } |
5327 | 0 | tsk_error_set_errstr2("%s: Error reading boot sector.", myname); |
5328 | 0 | goto on_error; |
5329 | 0 | } |
5330 | | |
5331 | | /* Check the magic value */ |
5332 | 0 | if (tsk_fs_guessu16(fs, ntfs->fs->magic, NTFS_FS_MAGIC)) { |
5333 | 0 | if (process_kape_boot_format(ntfs)) { |
5334 | 0 | tsk_error_reset(); |
5335 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
5336 | 0 | tsk_error_set_errstr("Not a NTFS file system (magic)"); |
5337 | 0 | if (tsk_verbose) |
5338 | 0 | fprintf(stderr, "ntfs_open: Incorrect NTFS magic\n"); |
5339 | 0 | goto on_error; |
5340 | 0 | } |
5341 | 0 | } |
5342 | | |
5343 | | |
5344 | | /* |
5345 | | * block calculations : although there are no blocks in ntfs, |
5346 | | * we are using a cluster as a "block" |
5347 | | */ |
5348 | | |
5349 | 0 | ntfs->ssize_b = tsk_getu16(fs->endian, ntfs->fs->ssize); |
5350 | 0 | if ((ntfs->ssize_b == 0) || (ntfs->ssize_b % 512)) { |
5351 | 0 | tsk_error_reset(); |
5352 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
5353 | 0 | tsk_error_set_errstr |
5354 | 0 | ("Not a NTFS file system (invalid sector size %d))", |
5355 | 0 | ntfs->ssize_b); |
5356 | 0 | if (tsk_verbose) |
5357 | 0 | fprintf(stderr, "ntfs_open: invalid sector size: %d\n", |
5358 | 0 | ntfs->ssize_b); |
5359 | 0 | goto on_error; |
5360 | 0 | } |
5361 | | |
5362 | 0 | csize = ntfs->fs->csize; |
5363 | 0 | if (ntfs->fs->csize > 0x80) { |
5364 | 0 | csize = 1 << -(int8_t)ntfs->fs->csize; |
5365 | 0 | } |
5366 | |
|
5367 | 0 | if ((csize != 0x01) && |
5368 | 0 | (csize != 0x02) && |
5369 | 0 | (csize != 0x04) && |
5370 | 0 | (csize != 0x08) && |
5371 | 0 | (csize != 0x10) && |
5372 | 0 | (csize != 0x20) && |
5373 | 0 | (csize != 0x40) && |
5374 | 0 | (csize != 0x80) && |
5375 | 0 | (csize != 0x100) && |
5376 | 0 | (csize != 0x200) && |
5377 | 0 | (csize != 0x400) && |
5378 | 0 | (csize != 0x800) && |
5379 | 0 | (csize != 0x1000) |
5380 | 0 | ) { |
5381 | 0 | tsk_error_reset(); |
5382 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
5383 | 0 | tsk_error_set_errstr |
5384 | 0 | ("Not a NTFS file system (invalid cluster size %d)", |
5385 | 0 | ntfs->fs->csize); |
5386 | 0 | if (tsk_verbose) |
5387 | 0 | fprintf(stderr, "ntfs_open: invalid cluster size: %d\n", |
5388 | 0 | ntfs->fs->csize); |
5389 | 0 | goto on_error; |
5390 | 0 | } |
5391 | | |
5392 | 0 | ntfs->csize_b = csize * ntfs->ssize_b; |
5393 | 0 | fs->first_block = 0; |
5394 | | /* This field is defined as 64-bits but according to the |
5395 | | * NTFS drivers in Linux, old Windows versions used only 32-bits |
5396 | | */ |
5397 | 0 | fs->block_count = |
5398 | 0 | (TSK_DADDR_T) tsk_getu64(fs->endian, |
5399 | 0 | ntfs->fs->vol_size_s) / csize; |
5400 | 0 | if (fs->block_count == 0) { |
5401 | 0 | tsk_error_reset(); |
5402 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
5403 | 0 | tsk_error_set_errstr("Not a NTFS file system (volume size is 0)"); |
5404 | 0 | if (tsk_verbose) |
5405 | 0 | fprintf(stderr, "ntfs_open: invalid volume size: 0\n"); |
5406 | 0 | goto on_error; |
5407 | 0 | } |
5408 | | |
5409 | 0 | fs->last_block = fs->last_block_act = fs->block_count - 1; |
5410 | 0 | fs->block_size = ntfs->csize_b; |
5411 | 0 | fs->dev_bsize = img_info->sector_size; |
5412 | | |
5413 | | // determine the last block we have in this image |
5414 | 0 | if ((TSK_DADDR_T) ((img_info->size - offset) / fs->block_size) < |
5415 | 0 | fs->block_count) |
5416 | 0 | fs->last_block_act = |
5417 | 0 | (img_info->size - offset) / fs->block_size - 1; |
5418 | |
|
5419 | 0 | ntfs->mft_rsize_b = 0; |
5420 | 0 | if (ntfs->fs->mft_rsize_c > 0) { |
5421 | 0 | ntfs->mft_rsize_b = ntfs->fs->mft_rsize_c * ntfs->csize_b; |
5422 | 0 | } |
5423 | 0 | else if (ntfs->fs->mft_rsize_c > -32) { |
5424 | | /* if the mft_rsize_c is not > 0, then it is -log2(rsize_b) */ |
5425 | 0 | ntfs->mft_rsize_b = 1 << -ntfs->fs->mft_rsize_c; |
5426 | 0 | } |
5427 | |
|
5428 | 0 | if ((ntfs->mft_rsize_b == 0) || (ntfs->mft_rsize_b % 512)) { |
5429 | 0 | tsk_error_reset(); |
5430 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
5431 | 0 | tsk_error_set_errstr |
5432 | 0 | ("Not a NTFS file system (invalid MFT entry size)"); |
5433 | 0 | if (tsk_verbose) |
5434 | 0 | fprintf(stderr, "ntfs_open: invalid MFT entry size\n"); |
5435 | 0 | goto on_error; |
5436 | 0 | } |
5437 | | |
5438 | 0 | ntfs->idx_rsize_b = 0; |
5439 | 0 | if (ntfs->fs->idx_rsize_c > 0) { |
5440 | 0 | ntfs->idx_rsize_b = ntfs->fs->idx_rsize_c * ntfs->csize_b; |
5441 | 0 | } |
5442 | 0 | else if (ntfs->fs->idx_rsize_c > -32) { |
5443 | | /* if the idx_rsize_c is not > 0, then it is -log2(rsize_b) */ |
5444 | 0 | ntfs->idx_rsize_b = 1 << -ntfs->fs->idx_rsize_c; |
5445 | 0 | } |
5446 | |
|
5447 | 0 | if ((ntfs->idx_rsize_b == 0) || (ntfs->idx_rsize_b % 512)) { |
5448 | 0 | tsk_error_reset(); |
5449 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
5450 | 0 | tsk_error_set_errstr |
5451 | 0 | ("Not a NTFS file system (invalid idx record size %d)", |
5452 | 0 | ntfs->idx_rsize_b); |
5453 | 0 | if (tsk_verbose) |
5454 | 0 | fprintf(stderr, "ntfs_open: invalid idx record size %d\n", |
5455 | 0 | ntfs->idx_rsize_b); |
5456 | 0 | goto on_error; |
5457 | 0 | } |
5458 | | |
5459 | 0 | ntfs->root_mft_addr = |
5460 | 0 | tsk_getu64(fs->endian, ntfs->fs->mft_clust) * ntfs->csize_b; |
5461 | 0 | if (tsk_getu64(fs->endian, ntfs->fs->mft_clust) > fs->last_block) { |
5462 | 0 | tsk_error_reset(); |
5463 | 0 | tsk_error_set_errno(TSK_ERR_FS_MAGIC); |
5464 | 0 | tsk_error_set_errstr |
5465 | 0 | ("Not a NTFS file system (invalid starting MFT clust)"); |
5466 | 0 | if (tsk_verbose) |
5467 | 0 | fprintf(stderr, "ntfs_open: invalid starting MFT cluster\n"); |
5468 | 0 | goto on_error; |
5469 | 0 | } |
5470 | | |
5471 | | /* |
5472 | | * Set the function pointers (before we start calling internal functions) |
5473 | | */ |
5474 | 0 | fs->inode_walk = ntfs_inode_walk; |
5475 | 0 | fs->block_walk = ntfs_block_walk; |
5476 | 0 | fs->block_getflags = ntfs_block_getflags; |
5477 | |
|
5478 | 0 | fs->get_default_attr_type = ntfs_get_default_attr_type; |
5479 | 0 | fs->load_attrs = ntfs_load_attrs; |
5480 | |
|
5481 | 0 | fs->file_add_meta = ntfs_inode_lookup; |
5482 | 0 | fs->dir_open_meta = ntfs_dir_open_meta; |
5483 | 0 | fs->fsstat = ntfs_fsstat; |
5484 | 0 | fs->fscheck = ntfs_fscheck; |
5485 | 0 | fs->istat = ntfs_istat; |
5486 | 0 | fs->close = ntfs_close; |
5487 | 0 | fs->name_cmp = ntfs_name_cmp; |
5488 | |
|
5489 | 0 | fs->fread_owner_sid = ntfs_file_get_sidstr; |
5490 | 0 | fs->jblk_walk = ntfs_jblk_walk; |
5491 | 0 | fs->jentry_walk = ntfs_jentry_walk; |
5492 | 0 | fs->jopen = ntfs_jopen; |
5493 | 0 | fs->journ_inum = 0; |
5494 | | |
5495 | | |
5496 | | |
5497 | | // set up locks |
5498 | 0 | tsk_init_lock(&ntfs->lock); |
5499 | 0 | tsk_init_lock(&ntfs->orphan_map_lock); |
5500 | 0 | #if TSK_USE_SID |
5501 | 0 | tsk_init_lock(&ntfs->sid_lock); |
5502 | 0 | #endif |
5503 | | |
5504 | | /* |
5505 | | * inode |
5506 | | */ |
5507 | |
|
5508 | 0 | fs->root_inum = NTFS_ROOTINO; |
5509 | 0 | fs->first_inum = NTFS_FIRSTINO; |
5510 | 0 | fs->last_inum = NTFS_LAST_DEFAULT_INO; |
5511 | 0 | ntfs->mft_data = NULL; |
5512 | | |
5513 | | /* load the data run for the MFT table into ntfs->mft */ |
5514 | 0 | ntfs->loading_the_MFT = 1; |
5515 | 0 | if ((ntfs->mft_file = |
5516 | 0 | tsk_fs_file_open_meta(fs, NULL, NTFS_MFT_MFT)) == NULL) { |
5517 | 0 | if (tsk_verbose) |
5518 | 0 | fprintf(stderr, |
5519 | 0 | "ntfs_open: Error opening $MFT (%s)\n", tsk_error_get()); |
5520 | 0 | goto on_error; |
5521 | 0 | } |
5522 | | |
5523 | | /* cache the data attribute |
5524 | | * |
5525 | | * This will likely be done already by proc_attrseq, but this |
5526 | | * should be quick |
5527 | | */ |
5528 | 0 | ntfs->mft_data = |
5529 | 0 | tsk_fs_attrlist_get(ntfs->mft_file->meta->attr, (TSK_FS_ATTR_TYPE_ENUM) NTFS_ATYPE_DATA); |
5530 | 0 | if (!ntfs->mft_data) { |
5531 | 0 | tsk_error_errstr2_concat(" - Data Attribute not found in $MFT"); |
5532 | 0 | if (tsk_verbose) |
5533 | 0 | fprintf(stderr, |
5534 | 0 | "ntfs_open: Data attribute not found in $MFT (%s)\n", |
5535 | 0 | tsk_error_get()); |
5536 | 0 | goto on_error; |
5537 | 0 | } |
5538 | | |
5539 | | /* Get the inode count based on the table size */ |
5540 | 0 | fs->inum_count = ntfs->mft_data->size / ntfs->mft_rsize_b + 1; // we are adding 1 in this calc to account for Orphans directory |
5541 | 0 | fs->last_inum = fs->inum_count - 1; |
5542 | | |
5543 | | /* reset the flag that we are no longer loading $MFT */ |
5544 | 0 | ntfs->loading_the_MFT = 0; |
5545 | | |
5546 | | /* Volume ID */ |
5547 | 0 | for (fs->fs_id_used = 0; fs->fs_id_used < 8; fs->fs_id_used++) { |
5548 | 0 | fs->fs_id[fs->fs_id_used] = ntfs->fs->serial[fs->fs_id_used]; |
5549 | 0 | } |
5550 | | |
5551 | | /* load the version of the file system */ |
5552 | 0 | if (ntfs_load_ver(ntfs)) { |
5553 | 0 | if (tsk_verbose) |
5554 | 0 | fprintf(stderr, |
5555 | 0 | "ntfs_open: Error loading file system version ((%s)\n", |
5556 | 0 | tsk_error_get()); |
5557 | 0 | goto on_error; |
5558 | 0 | } |
5559 | | |
5560 | | /* load the data block bitmap data run into ntfs_info */ |
5561 | 0 | if (ntfs_load_bmap(ntfs)) { |
5562 | 0 | if (tsk_verbose) |
5563 | 0 | fprintf(stderr, "ntfs_open: Error loading block bitmap (%s)\n", |
5564 | 0 | tsk_error_get()); |
5565 | 0 | goto on_error; |
5566 | 0 | } |
5567 | | |
5568 | | /* load the SID data into ntfs_info ($Secure - $SDS, $SDH, $SII */ |
5569 | | |
5570 | | |
5571 | 0 | #if TSK_USE_SID |
5572 | 0 | if (ntfs_load_secure(ntfs)) { |
5573 | 0 | if (tsk_verbose) |
5574 | 0 | fprintf(stderr, "ntfs_open: Error loading Secure Info (%s)\n", |
5575 | 0 | tsk_error_get()); |
5576 | 0 | goto on_error; |
5577 | 0 | } |
5578 | 0 | #endif |
5579 | | |
5580 | | // initialize the caches |
5581 | 0 | ntfs->attrdef = NULL; |
5582 | 0 | ntfs->orphan_map = NULL; |
5583 | | |
5584 | | // initialize the number of allocated files |
5585 | 0 | ntfs->alloc_file_count = 0; |
5586 | |
|
5587 | 0 | if (tsk_verbose) { |
5588 | 0 | tsk_fprintf(stderr, |
5589 | 0 | "ssize: %" PRIu16 |
5590 | 0 | " csize: %d serial: %" PRIx64 "\n", |
5591 | 0 | tsk_getu16(fs->endian, ntfs->fs->ssize), |
5592 | 0 | csize, tsk_getu64(fs->endian, ntfs->fs->serial)); |
5593 | 0 | tsk_fprintf(stderr, |
5594 | 0 | "mft_rsize: %d idx_rsize: %d vol: %d mft: %" |
5595 | 0 | PRIu64 " mft_mir: %" PRIu64 "\n", |
5596 | 0 | ntfs->mft_rsize_b, ntfs->idx_rsize_b, |
5597 | 0 | (int) fs->block_count, tsk_getu64(fs->endian, |
5598 | 0 | ntfs->fs->mft_clust), tsk_getu64(fs->endian, |
5599 | 0 | ntfs->fs->mftm_clust)); |
5600 | 0 | } |
5601 | 0 | return fs; |
5602 | | |
5603 | 0 | on_error: |
5604 | 0 | ntfs_close(fs); |
5605 | 0 | return NULL; |
5606 | 0 | } |