/src/sleuthkit/tsk/fs/fs_attr.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** fs_attr |
3 | | ** The Sleuth Kit |
4 | | ** |
5 | | ** Brian Carrier [carrier <at> sleuthkit [dot] org] |
6 | | ** Copyright (c) 2006-2011 Brian Carrier, Basis Technology. All Rights reserved |
7 | | ** Copyright (c) 2003-2005 Brian Carrier. All rights reserved |
8 | | ** |
9 | | ** TASK |
10 | | ** Copyright (c) 2002 Brian Carrier, @stake Inc. All rights reserved |
11 | | ** |
12 | | ** |
13 | | ** This software is distributed under the Common Public License 1.0 |
14 | | ** |
15 | | */ |
16 | | |
17 | | /** |
18 | | * \file fs_attr.c |
19 | | * Functions to allocate and add structures to maintain generic file |
20 | | * system attributes and run lists. |
21 | | */ |
22 | | |
23 | | |
24 | | /* |
25 | | * The TSK_FS_ATTR structure is motivated by NTFS. NTFS (and others) allow |
26 | | * one to have more than one data area per file. Furthermore, there is |
27 | | * more than one way to store the data (resident in the MFT entry or |
28 | | * in the Data Area runs). To handle this in |
29 | | * a generic format, the TSK_FS_ATTR structure was created. |
30 | | * |
31 | | * TSK_FS_ATTR structures have a type and id that describe it and then |
32 | | * a flag identifies it as a resident stream or a non-resident run |
33 | | * They form a linked list and are added to the TSK_FS_META structure |
34 | | */ |
35 | | #include "tsk_fs_i.h" |
36 | | #include "tsk_logical_fs.h" |
37 | | |
38 | | |
39 | | /** |
40 | | * \internal |
41 | | * Allocate a run list entry. |
42 | | * |
43 | | * @returns NULL on error |
44 | | */ |
45 | | TSK_FS_ATTR_RUN * |
46 | | tsk_fs_attr_run_alloc() |
47 | 3.81M | { |
48 | 3.81M | TSK_FS_ATTR_RUN *fs_attr_run = |
49 | 3.81M | (TSK_FS_ATTR_RUN *) tsk_malloc(sizeof(TSK_FS_ATTR_RUN)); |
50 | 3.81M | if (fs_attr_run == NULL) |
51 | 0 | return NULL; |
52 | | |
53 | 3.81M | return fs_attr_run; |
54 | 3.81M | } |
55 | | |
56 | | /** |
57 | | * \internal |
58 | | * Free a list of data_runs |
59 | | * |
60 | | * @param fs_attr_run Head of list to free |
61 | | */ |
62 | | void |
63 | | tsk_fs_attr_run_free(TSK_FS_ATTR_RUN * fs_attr_run) |
64 | 952k | { |
65 | 4.75M | while (fs_attr_run) { |
66 | 3.80M | TSK_FS_ATTR_RUN *fs_attr_run_prev = fs_attr_run; |
67 | 3.80M | fs_attr_run = fs_attr_run->next; |
68 | 3.80M | fs_attr_run_prev->next = NULL; |
69 | 3.80M | free(fs_attr_run_prev); |
70 | 3.80M | } |
71 | 952k | } |
72 | | |
73 | | |
74 | | |
75 | | |
76 | | /** |
77 | | * \internal |
78 | | * Allocates and initializes a new structure. |
79 | | * |
80 | | * @param type The type of attribute to create (Resident or Non-resident) |
81 | | * @returns NULL on error |
82 | | */ |
83 | | TSK_FS_ATTR * |
84 | | tsk_fs_attr_alloc(TSK_FS_ATTR_FLAG_ENUM type) |
85 | 2.25M | { |
86 | 2.25M | TSK_FS_ATTR *fs_attr = (TSK_FS_ATTR *) tsk_malloc(sizeof(TSK_FS_ATTR)); |
87 | 2.25M | if (fs_attr == NULL) { |
88 | 0 | return NULL; |
89 | 0 | } |
90 | | |
91 | 2.25M | fs_attr->name_size = 128; |
92 | 2.25M | if ((fs_attr->name = (char *) tsk_malloc(fs_attr->name_size)) == NULL) { |
93 | 0 | free(fs_attr); |
94 | 0 | return NULL; |
95 | 0 | } |
96 | | |
97 | 2.25M | if (type == TSK_FS_ATTR_NONRES) { |
98 | 548k | fs_attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (TSK_FS_ATTR_NONRES | TSK_FS_ATTR_INUSE); |
99 | 548k | } |
100 | 1.70M | else if (type == TSK_FS_ATTR_RES) { |
101 | 1.70M | fs_attr->rd.buf_size = 1024; |
102 | 1.70M | fs_attr->rd.buf = (uint8_t *) tsk_malloc(fs_attr->rd.buf_size); |
103 | 1.70M | if (fs_attr->rd.buf == NULL) { |
104 | 0 | free(fs_attr->name); |
105 | 0 | return NULL; |
106 | 0 | } |
107 | 1.70M | fs_attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (TSK_FS_ATTR_RES | TSK_FS_ATTR_INUSE); |
108 | 1.70M | } |
109 | 0 | else { |
110 | 0 | tsk_error_reset(); |
111 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
112 | 0 | tsk_error_set_errstr("tsk_fs_attr_alloc: Invalid Type: %d\n", |
113 | 0 | type); |
114 | 0 | return NULL; |
115 | 0 | } |
116 | | |
117 | 2.25M | return fs_attr; |
118 | 2.25M | } |
119 | | |
120 | | |
121 | | /** |
122 | | * \internal |
123 | | * Free a single TSK_FS_ATTR structure. This does not free the linked list. |
124 | | * |
125 | | * @param a_fs_attr Structure to free. |
126 | | */ |
127 | | void |
128 | | tsk_fs_attr_free(TSK_FS_ATTR * a_fs_attr) |
129 | 2.25M | { |
130 | 2.25M | if (a_fs_attr == NULL) |
131 | 0 | return; |
132 | | |
133 | 2.25M | if (a_fs_attr->nrd.run) |
134 | 924k | tsk_fs_attr_run_free(a_fs_attr->nrd.run); |
135 | 2.25M | a_fs_attr->nrd.run = NULL; |
136 | | |
137 | 2.25M | free(a_fs_attr->rd.buf); |
138 | 2.25M | a_fs_attr->rd.buf = NULL; |
139 | | |
140 | 2.25M | free(a_fs_attr->name); |
141 | 2.25M | a_fs_attr->name = NULL; |
142 | | |
143 | 2.25M | free(a_fs_attr); |
144 | 2.25M | } |
145 | | |
146 | | |
147 | | /** |
148 | | * \internal |
149 | | * Clear the run_lists fields of a single FS_DATA structure |
150 | | * |
151 | | * @param a_fs_attr Structure to clear for reuse |
152 | | */ |
153 | | void |
154 | | tsk_fs_attr_clear(TSK_FS_ATTR * a_fs_attr) |
155 | 443k | { |
156 | 443k | a_fs_attr->size = 0; |
157 | 443k | a_fs_attr->type = TSK_FS_ATTR_TYPE_NOT_FOUND; |
158 | 443k | a_fs_attr->id = 0; |
159 | 443k | a_fs_attr->flags = TSK_FS_ATTR_FLAG_NONE; |
160 | 443k | if (a_fs_attr->nrd.run) { |
161 | 15.5k | tsk_fs_attr_run_free(a_fs_attr->nrd.run); |
162 | 15.5k | a_fs_attr->nrd.run = NULL; |
163 | 15.5k | a_fs_attr->nrd.run_end = NULL; |
164 | 15.5k | a_fs_attr->nrd.allocsize = 0; |
165 | 15.5k | a_fs_attr->nrd.initsize = 0; |
166 | 15.5k | } |
167 | 443k | } |
168 | | |
169 | | |
170 | | |
171 | | |
172 | | /** |
173 | | * Add a name to an existing FS_DATA structure. Will reallocate |
174 | | * space for the name if needed. |
175 | | * |
176 | | * @param fs_attr Structure to add name to |
177 | | * @param name UTF-8 name to add |
178 | | * |
179 | | * @return 1 on error and 0 on success |
180 | | */ |
181 | | static uint8_t |
182 | | fs_attr_put_name(TSK_FS_ATTR * fs_attr, const char *name) |
183 | 2.38M | { |
184 | 2.38M | if ((name == NULL) || (strlen(name) == 0)) { |
185 | 1.74M | if (fs_attr->name_size > 0) { |
186 | 1.69M | free(fs_attr->name); |
187 | 1.69M | fs_attr->name_size = 0; |
188 | 1.69M | } |
189 | 1.74M | fs_attr->name = NULL; |
190 | 1.74M | return 0; |
191 | 1.74M | } |
192 | | |
193 | 637k | if (fs_attr->name_size < (strlen(name) + 1)) { |
194 | 17.5k | fs_attr->name = (char*) tsk_realloc(fs_attr->name, strlen(name) + 1); |
195 | 17.5k | if (fs_attr->name == NULL) |
196 | 0 | return 1; |
197 | 17.5k | fs_attr->name_size = strlen(name) + 1; |
198 | 17.5k | } |
199 | 637k | strncpy(fs_attr->name, name, fs_attr->name_size); |
200 | 637k | return 0; |
201 | 637k | } |
202 | | |
203 | | /** |
204 | | * \internal |
205 | | * Copy resident data to an attribute. |
206 | | * |
207 | | * @param a_fs_attr Attribute to add data to (cannot be NULL) |
208 | | * @param name Name of the attribute to set |
209 | | * @param type Type of the attribute to set |
210 | | * @param id Id of the attribute to set |
211 | | * @param res_data Pointer to where resident data is located (data will |
212 | | * be copied from here into FS_DATA) |
213 | | * @param len Length of resident data |
214 | | * @return 1 on error and 0 on success |
215 | | */ |
216 | | uint8_t |
217 | | tsk_fs_attr_set_str(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr, |
218 | | const char *name, TSK_FS_ATTR_TYPE_ENUM type, uint16_t id, |
219 | | void *res_data, size_t len) |
220 | 1.41M | { |
221 | 1.41M | if (a_fs_attr == NULL) { |
222 | 0 | tsk_error_reset(); |
223 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
224 | 0 | tsk_error_set_errstr("Null fs_attr in tsk_fs_attr_set_str"); |
225 | 0 | return 1; |
226 | 0 | } |
227 | | |
228 | 1.41M | a_fs_attr->fs_file = a_fs_file; |
229 | 1.41M | a_fs_attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (TSK_FS_ATTR_INUSE | TSK_FS_ATTR_RES); |
230 | 1.41M | a_fs_attr->type = type; |
231 | 1.41M | a_fs_attr->id = id; |
232 | 1.41M | a_fs_attr->nrd.compsize = 0; |
233 | | |
234 | 1.41M | if (fs_attr_put_name(a_fs_attr, name)) { |
235 | 0 | return 1; |
236 | 0 | } |
237 | | |
238 | 1.41M | if (a_fs_attr->rd.buf_size < len) { |
239 | 521 | a_fs_attr->rd.buf = |
240 | 521 | (uint8_t *) tsk_realloc((char *) a_fs_attr->rd.buf, len); |
241 | 521 | if (a_fs_attr->rd.buf == NULL) |
242 | 0 | return 1; |
243 | 521 | a_fs_attr->rd.buf_size = len; |
244 | 521 | } |
245 | | |
246 | 1.41M | memset(a_fs_attr->rd.buf, 0, a_fs_attr->rd.buf_size); |
247 | 1.41M | memcpy(a_fs_attr->rd.buf, res_data, len); |
248 | 1.41M | a_fs_attr->size = len; |
249 | | |
250 | 1.41M | return 0; |
251 | 1.41M | } |
252 | | |
253 | | |
254 | | /** |
255 | | * \internal |
256 | | * Set the needed fields along with an initial run list for a data attribute. To add more |
257 | | * runs, use ...._add_run(). |
258 | | * |
259 | | * @param a_fs_file File to add attribute to |
260 | | * @param a_fs_attr The data attribute to initialize and add the run to |
261 | | * @param a_data_run_new The set of runs to add (can be NULL). |
262 | | * @param name Name of the attribute (can be NULL) |
263 | | * @param type Type of attribute to add run to |
264 | | * @param id Id of attribute to add run to |
265 | | * @param size Total size of the attribute (can be larger than length of initial run being added) |
266 | | * @param init_size Number of bytes in attribute that have been initialized (less then or equal to size) |
267 | | * (note that this sets the initialized size for the attribute and it will not be updated as more runs are added). |
268 | | * @param alloc_size Allocated size of the attribute (>= size). Identifies the slack space. |
269 | | * (note that this sets the allocated size for the attribute and it will not be updated as more runs are added). |
270 | | * @param flags Flags about compression, sparse etc. of data |
271 | | * @param compsize Compression unit size (in case it needs to be created) |
272 | | * |
273 | | * @returns 1 on error and 0 on success |
274 | | */ |
275 | | uint8_t |
276 | | tsk_fs_attr_set_run(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr, |
277 | | TSK_FS_ATTR_RUN * a_data_run_new, const char *name, |
278 | | TSK_FS_ATTR_TYPE_ENUM type, uint16_t id, TSK_OFF_T size, |
279 | | TSK_OFF_T init_size, TSK_OFF_T alloc_size, |
280 | | TSK_FS_ATTR_FLAG_ENUM flags, uint32_t compsize) |
281 | 965k | { |
282 | 965k | if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) { |
283 | 0 | tsk_error_reset(); |
284 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
285 | 0 | tsk_error_set_errstr("Null fs_file in tsk_fs_attr_set_run"); |
286 | 0 | return 1; |
287 | 0 | } |
288 | 965k | if (a_fs_attr == NULL) { |
289 | 0 | tsk_error_reset(); |
290 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
291 | 0 | tsk_error_set_errstr("Null fs_attr in tsk_fs_attr_set_run"); |
292 | 0 | return 1; |
293 | 0 | } |
294 | | |
295 | 965k | if (alloc_size < size) { |
296 | 874 | tsk_error_reset(); |
297 | 874 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
298 | 874 | tsk_error_set_errstr("tsk_fs_attr_set_run: alloc_size (%" PRIdOFF |
299 | 874 | ") is less than size (%" PRIdOFF ")", alloc_size, size); |
300 | 874 | return 1; |
301 | 874 | } |
302 | | |
303 | 964k | a_fs_attr->fs_file = a_fs_file; |
304 | 964k | a_fs_attr->flags = (TSK_FS_ATTR_FLAG_ENUM) (TSK_FS_ATTR_INUSE | TSK_FS_ATTR_NONRES | flags); |
305 | 964k | a_fs_attr->type = type; |
306 | 964k | a_fs_attr->id = id; |
307 | 964k | a_fs_attr->size = size; |
308 | 964k | a_fs_attr->nrd.allocsize = alloc_size; |
309 | 964k | a_fs_attr->nrd.initsize = init_size; |
310 | 964k | a_fs_attr->nrd.compsize = compsize; |
311 | | |
312 | 964k | if (fs_attr_put_name(a_fs_attr, name)) { |
313 | 0 | return 1; |
314 | 0 | } |
315 | | |
316 | | /* Add the a_data_run_new to the attribute. */ |
317 | | |
318 | | /* We support the ODD case where the run is NULL. In this case, |
319 | | * we set the attribute size info, but set everything else to NULL. |
320 | | */ |
321 | 964k | if (a_data_run_new == NULL) { |
322 | 270k | a_fs_attr->nrd.run = NULL; |
323 | 270k | a_fs_attr->nrd.run_end = NULL; |
324 | 270k | return 0; |
325 | 270k | } |
326 | | |
327 | | /* |
328 | | * If this is not in the beginning, then we need to make a filler |
329 | | * to account for the cluster numbers we haven't seen yet |
330 | | * |
331 | | * This commonly happens when we process an MFT entry that |
332 | | * is not a base entry and it is referenced in an $ATTR_LIST |
333 | | * |
334 | | * The $DATA attribute in the non-base have a non-zero |
335 | | * a_data_run_new->offset. |
336 | | */ |
337 | 694k | if (a_data_run_new->offset != 0) { |
338 | 63.0k | TSK_FS_ATTR_RUN *fill_run = tsk_fs_attr_run_alloc(); |
339 | 63.0k | fill_run->flags = TSK_FS_ATTR_RUN_FLAG_FILLER; |
340 | 63.0k | fill_run->offset = 0; |
341 | 63.0k | fill_run->addr = 0; |
342 | 63.0k | fill_run->len = a_data_run_new->offset; |
343 | 63.0k | fill_run->next = a_data_run_new; |
344 | 63.0k | a_data_run_new = fill_run; |
345 | 63.0k | } |
346 | | |
347 | 694k | a_fs_attr->nrd.run = a_data_run_new; |
348 | | |
349 | | // update the pointer to the end of the list |
350 | 694k | a_fs_attr->nrd.run_end = a_data_run_new; |
351 | 947k | while (a_fs_attr->nrd.run_end->next) { |
352 | 253k | a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next; |
353 | 253k | } |
354 | | |
355 | 694k | return 0; |
356 | 964k | } |
357 | | |
358 | | static void |
359 | | dump_attr(TSK_FS_ATTR * a_fs_attr) |
360 | 0 | { |
361 | 0 | TSK_FS_ATTR_RUN *cur_run; |
362 | 0 | cur_run = a_fs_attr->nrd.run; |
363 | |
|
364 | 0 | fprintf(stderr, "Attribute Run Dump:\n"); |
365 | 0 | for (cur_run = a_fs_attr->nrd.run; cur_run; cur_run = cur_run->next) { |
366 | 0 | fprintf(stderr, " %" PRIuDADDR " to %" PRIuDADDR " %sFiller\n", |
367 | 0 | cur_run->offset, cur_run->offset + cur_run->len - 1, |
368 | 0 | (cur_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) ? "" : "Not"); |
369 | 0 | } |
370 | 0 | } |
371 | | |
372 | | /* |
373 | | * Prints the data runs for a non-resident attribute |
374 | | */ |
375 | | uint8_t |
376 | 0 | tsk_fs_attr_print(const TSK_FS_ATTR * a_fs_attr, FILE* hFile) { |
377 | 0 | TSK_FS_ATTR_RUN *fs_attr_run; |
378 | 0 | uint32_t skip_remain; |
379 | 0 | TSK_OFF_T tot_size; |
380 | 0 | TSK_FS_INFO *fs = a_fs_attr->fs_file->fs_info; |
381 | 0 | TSK_OFF_T off = 0; |
382 | 0 | uint8_t stop_loop = 0; |
383 | |
|
384 | 0 | if ( ! (a_fs_attr->flags & TSK_FS_ATTR_NONRES)) { |
385 | 0 | tsk_error_set_errstr("tsk_fs_attr_print called on non-resident attribute"); |
386 | 0 | return TSK_ERR; |
387 | 0 | } |
388 | | |
389 | 0 | tot_size = a_fs_attr->size; |
390 | 0 | skip_remain = a_fs_attr->nrd.skiplen; |
391 | |
|
392 | 0 | for (fs_attr_run = a_fs_attr->nrd.run; fs_attr_run; |
393 | 0 | fs_attr_run = fs_attr_run->next) { |
394 | 0 | TSK_DADDR_T addr, len_idx, run_len, run_start_addr; |
395 | |
|
396 | 0 | addr = fs_attr_run->addr; |
397 | 0 | run_len = 0; |
398 | 0 | run_start_addr = addr; |
399 | | |
400 | | /* cycle through each block in the run */ |
401 | 0 | for (len_idx = 0; len_idx < fs_attr_run->len; len_idx++) { |
402 | | |
403 | | |
404 | | /* If the address is too large then give an error */ |
405 | 0 | if (addr + len_idx > fs->last_block) { |
406 | 0 | if (a_fs_attr->fs_file-> |
407 | 0 | meta->flags & TSK_FS_META_FLAG_UNALLOC) |
408 | 0 | tsk_error_set_errno(TSK_ERR_FS_RECOVER); |
409 | 0 | else |
410 | 0 | tsk_error_set_errno(TSK_ERR_FS_BLK_NUM); |
411 | 0 | tsk_error_set_errstr |
412 | 0 | ("Invalid address in run (too large): %" PRIuDADDR "", |
413 | 0 | addr + len_idx); |
414 | 0 | return TSK_ERR; |
415 | 0 | } |
416 | | |
417 | | |
418 | | /* Need to account for the skip length, which is the number of bytes |
419 | | * in the start of the attribute that are skipped and that are not |
420 | | * included in the overall length. We will seek past those and not |
421 | | * return those in the action. We just read a block size so check |
422 | | * if there is data to be returned in this buffer. */ |
423 | | |
424 | 0 | if (skip_remain >= fs->block_size) { |
425 | 0 | skip_remain -= fs->block_size; |
426 | 0 | run_start_addr++; |
427 | 0 | } |
428 | 0 | else { |
429 | 0 | size_t ret_len; |
430 | | |
431 | | /* Do we want to return a full block, or just the end? */ |
432 | 0 | if ((TSK_OFF_T)fs->block_size - skip_remain < |
433 | 0 | tot_size - off) |
434 | 0 | ret_len = fs->block_size - skip_remain; |
435 | 0 | else |
436 | 0 | ret_len = (size_t)(tot_size - off); |
437 | |
|
438 | 0 | off += ret_len; |
439 | 0 | run_len++; |
440 | 0 | skip_remain = 0; |
441 | |
|
442 | 0 | if (off >= tot_size) { |
443 | 0 | stop_loop = 1; |
444 | 0 | break; |
445 | 0 | } |
446 | 0 | } |
447 | 0 | } |
448 | | |
449 | 0 | if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) { |
450 | 0 | tsk_fprintf(hFile, " Starting address: X, length: %lld Sparse", run_len); |
451 | 0 | } |
452 | 0 | else if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) { |
453 | 0 | tsk_fprintf(hFile, " Starting address: X, length: %lld Filler", run_len); |
454 | 0 | } |
455 | 0 | else { |
456 | 0 | tsk_fprintf(hFile, " Starting address: %lld, length: %lld %s", run_start_addr, run_len, |
457 | 0 | (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_ENCRYPTED) ? "Encrypted": ""); |
458 | 0 | } |
459 | 0 | tsk_fprintf(hFile, "\n"); |
460 | 0 | if (stop_loop) { |
461 | 0 | break; |
462 | 0 | } |
463 | 0 | } |
464 | 0 | return TSK_OK; |
465 | 0 | } |
466 | | |
467 | | /** |
468 | | * \internal |
469 | | * Add a set of consecutive runs to an attribute. This will add and remove FILLER entries |
470 | | * as needed and update internal variables. |
471 | | * |
472 | | * @param a_fs File system run is from |
473 | | * @param fs_attr Attribute to add run to |
474 | | * @param a_data_run_new The set of runs to add. |
475 | | * |
476 | | * @returns 1 on error and 0 on succes |
477 | | */ |
478 | | uint8_t |
479 | | tsk_fs_attr_add_run( |
480 | | [[maybe_unused]] TSK_FS_INFO * a_fs, |
481 | | TSK_FS_ATTR * a_fs_attr, |
482 | | TSK_FS_ATTR_RUN * a_data_run_new) |
483 | 52.4k | { |
484 | 52.4k | TSK_FS_ATTR_RUN *data_run_cur, *data_run_prev; |
485 | 52.4k | TSK_DADDR_T run_len; |
486 | | |
487 | 52.4k | tsk_error_reset(); |
488 | | |
489 | 52.4k | if (a_fs_attr == NULL) { |
490 | 0 | tsk_error_reset(); |
491 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
492 | 0 | tsk_error_set_errstr |
493 | 0 | ("tsk_fs_attr_add_run: Error, a_fs_attr is NULL"); |
494 | 0 | return 1; |
495 | 0 | } |
496 | | |
497 | | // we only support the case of a null run if it is the only run... |
498 | 52.4k | if (a_data_run_new == NULL) { |
499 | 396 | tsk_error_reset(); |
500 | 396 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
501 | 396 | tsk_error_set_errstr |
502 | 396 | ("tsk_fs_attr_add_run: Error, a_data_run_new is NULL (%" |
503 | 396 | PRIuINUM ")", a_fs_attr->fs_file->meta->addr); |
504 | 396 | return 1; |
505 | 396 | } |
506 | | |
507 | 52.0k | run_len = 0; |
508 | 52.0k | data_run_cur = a_data_run_new; |
509 | 135k | while (data_run_cur) { |
510 | 83.2k | run_len += data_run_cur->len; |
511 | 83.2k | data_run_cur = data_run_cur->next; |
512 | 83.2k | } |
513 | | |
514 | | /* First thing, is to check if we can just add it to the end */ |
515 | 52.0k | if ((a_fs_attr->nrd.run_end) |
516 | 52.0k | && (a_fs_attr->nrd.run_end->offset + a_fs_attr->nrd.run_end->len == |
517 | 40.1k | a_data_run_new->offset)) { |
518 | | |
519 | 2.77k | a_fs_attr->nrd.run_end->next = a_data_run_new; |
520 | | // update the pointer to the end of the list |
521 | 6.20k | while (a_fs_attr->nrd.run_end->next) |
522 | 3.43k | a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next; |
523 | | |
524 | | /* return head of a_fs_attr list */ |
525 | 2.77k | return 0; |
526 | 2.77k | } |
527 | | |
528 | | // cycle through existing runs and see if we can add this into a filler spot |
529 | 49.2k | data_run_cur = a_fs_attr->nrd.run; |
530 | 49.2k | data_run_prev = NULL; |
531 | 305k | while (data_run_cur) { |
532 | | |
533 | 278k | if (tsk_verbose) |
534 | 0 | tsk_fprintf(stderr, |
535 | 0 | "tsk_fs_attr_add: %" PRIuDADDR "@%" PRIuDADDR |
536 | 0 | " (Filler: %s)\n", data_run_cur->offset, data_run_cur->len, |
537 | 0 | (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) ? "Yes" |
538 | 0 | : "No"); |
539 | | |
540 | | /* Do we replace this filler spot? */ |
541 | 278k | if (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) { |
542 | | |
543 | | /* This should never happen because we always add |
544 | | * the filler to start from VCN 0 */ |
545 | 49.5k | if (data_run_cur->offset > a_data_run_new->offset) { |
546 | 935 | tsk_error_reset(); |
547 | 935 | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
548 | 935 | tsk_error_set_errstr |
549 | 935 | ("tsk_fs_attr_add_run: could not add data_run b.c. offset (%" |
550 | 935 | PRIuDADDR ") is larger than FILLER (%" PRIuDADDR ") (%" |
551 | 935 | PRIuINUM ")", a_data_run_new->offset, |
552 | 935 | data_run_cur->offset, a_fs_attr->fs_file->meta->addr); |
553 | 935 | if (tsk_verbose) |
554 | 0 | dump_attr(a_fs_attr); |
555 | 935 | return 1; |
556 | 935 | } |
557 | | |
558 | | /* Check if the new run starts inside of this filler. */ |
559 | 48.5k | if (data_run_cur->offset + data_run_cur->len > |
560 | 48.5k | a_data_run_new->offset) { |
561 | 21.0k | TSK_FS_ATTR_RUN *endrun; |
562 | | |
563 | | /* if the new starts at the same as the filler, |
564 | | * replace the pointer */ |
565 | 21.0k | if (data_run_cur->offset == a_data_run_new->offset) { |
566 | 12.5k | if (data_run_prev) |
567 | 6.28k | data_run_prev->next = a_data_run_new; |
568 | 6.26k | else |
569 | 6.26k | a_fs_attr->nrd.run = a_data_run_new; |
570 | 12.5k | } |
571 | | |
572 | | /* The new run does not start at the beginning of |
573 | | * the filler, so make a new start filler |
574 | | */ |
575 | 8.53k | else { |
576 | 8.53k | TSK_FS_ATTR_RUN *newfill = tsk_fs_attr_run_alloc(); |
577 | 8.53k | if (newfill == NULL) |
578 | 0 | return 1; |
579 | | |
580 | 8.53k | if (data_run_prev) |
581 | 6.21k | data_run_prev->next = newfill; |
582 | 2.32k | else |
583 | 2.32k | a_fs_attr->nrd.run = newfill; |
584 | | |
585 | 8.53k | newfill->next = a_data_run_new; |
586 | 8.53k | newfill->len = |
587 | 8.53k | a_data_run_new->offset - data_run_cur->offset; |
588 | 8.53k | newfill->offset = data_run_cur->offset; |
589 | 8.53k | newfill->flags = TSK_FS_ATTR_RUN_FLAG_FILLER; |
590 | | |
591 | 8.53k | data_run_cur->len -= newfill->len; |
592 | 8.53k | } |
593 | | |
594 | | /* get to the end of the run that we are trying to add */ |
595 | 21.0k | endrun = a_data_run_new; |
596 | 28.6k | while (endrun->next) |
597 | 7.59k | endrun = endrun->next; |
598 | | |
599 | | /* if the filler is the same size as the |
600 | | * new one, replace it |
601 | | */ |
602 | 21.0k | if (run_len == data_run_cur->len) { |
603 | 689 | endrun->next = data_run_cur->next; |
604 | | |
605 | | // update the pointer to the end of the list (if we are the end) |
606 | 689 | if (endrun->next == NULL) |
607 | 0 | a_fs_attr->nrd.run_end = endrun; |
608 | | |
609 | 689 | free(data_run_cur); |
610 | 689 | } |
611 | | /* else adjust the last filler entry */ |
612 | 20.3k | else { |
613 | 20.3k | endrun->next = data_run_cur; |
614 | 20.3k | data_run_cur->len -= run_len; |
615 | 20.3k | data_run_cur->offset = |
616 | 20.3k | a_data_run_new->offset + a_data_run_new->len; |
617 | 20.3k | } |
618 | | |
619 | 21.0k | return 0; |
620 | 21.0k | } |
621 | 48.5k | } |
622 | | |
623 | 256k | data_run_prev = data_run_cur; |
624 | 256k | data_run_cur = data_run_cur->next; |
625 | 256k | } |
626 | | |
627 | | |
628 | | /* |
629 | | * There is no filler holding the location of this run, so |
630 | | * we will add it to the end of the list |
631 | | * |
632 | | * we got here because it did not fit in the current list or |
633 | | * because the current list is NULL |
634 | | * |
635 | | * At this point data_run_prev is the end of the existing list or |
636 | | * 0 if there is no list |
637 | | */ |
638 | | /* This is an error condition. |
639 | | * It means that we cycled through the existing runs, |
640 | | * ended at a VCN that is larger than what we are adding, |
641 | | * and never found a filler entry to insert it into... |
642 | | */ |
643 | 27.2k | if ((data_run_prev) |
644 | 27.2k | && (data_run_prev->offset + data_run_prev->len > |
645 | 15.3k | a_data_run_new->offset)) { |
646 | | |
647 | | /* MAYBE this is because of a duplicate entry .. */ |
648 | 5.11k | if ((data_run_prev->addr == a_data_run_new->addr) && |
649 | 5.11k | (data_run_prev->len == a_data_run_new->len)) { |
650 | | // @@@ Sould be we freeing this....? What if the caller tries to write to it? |
651 | 2.73k | tsk_fs_attr_run_free(a_data_run_new); |
652 | 2.73k | return 0; |
653 | 2.73k | } |
654 | | |
655 | 2.38k | tsk_error_reset(); |
656 | 2.38k | tsk_error_set_errno(TSK_ERR_FS_GENFS); |
657 | 2.38k | tsk_error_set_errstr |
658 | 2.38k | ("fs_attr_add_run: error adding additional run (%" PRIuINUM |
659 | 2.38k | "): No filler entry for %" PRIuDADDR ". Final: %" PRIuDADDR, |
660 | 2.38k | a_fs_attr->fs_file->meta->addr, a_data_run_new->offset, |
661 | 2.38k | data_run_prev->offset + data_run_prev->len); |
662 | 2.38k | if (tsk_verbose) |
663 | 0 | dump_attr(a_fs_attr); |
664 | 2.38k | return 1; |
665 | 5.11k | } |
666 | | |
667 | | /* we should add it right here */ |
668 | 22.1k | else if (((data_run_prev) |
669 | 22.1k | && (data_run_prev->offset + data_run_prev->len == |
670 | 10.2k | a_data_run_new->offset)) |
671 | 22.1k | || (a_data_run_new->offset == 0)) { |
672 | 3.43k | if (data_run_prev) |
673 | 0 | data_run_prev->next = a_data_run_new; |
674 | 3.43k | else |
675 | 3.43k | a_fs_attr->nrd.run = a_data_run_new; |
676 | 3.43k | } |
677 | | /* we need to make a filler before it */ |
678 | 18.7k | else { |
679 | 18.7k | TSK_FS_ATTR_RUN *tmprun = tsk_fs_attr_run_alloc(); |
680 | 18.7k | if (tmprun == NULL) |
681 | 0 | return 1; |
682 | | |
683 | 18.7k | if (data_run_prev) { |
684 | 10.2k | data_run_prev->next = tmprun; |
685 | 10.2k | tmprun->offset = data_run_prev->offset + data_run_prev->len; |
686 | 10.2k | } |
687 | 8.44k | else { |
688 | 8.44k | a_fs_attr->nrd.run = tmprun; |
689 | 8.44k | } |
690 | | |
691 | 18.7k | tmprun->len = a_data_run_new->offset - tmprun->offset; |
692 | 18.7k | tmprun->flags = TSK_FS_ATTR_RUN_FLAG_FILLER; |
693 | 18.7k | tmprun->next = a_data_run_new; |
694 | 18.7k | } |
695 | | |
696 | | // update the pointer to the end of the list |
697 | 22.1k | a_fs_attr->nrd.run_end = a_data_run_new; |
698 | 28.7k | while (a_fs_attr->nrd.run_end->next) |
699 | 6.64k | a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next; |
700 | | |
701 | 22.1k | return 0; |
702 | 27.2k | } |
703 | | |
704 | | |
705 | | /** |
706 | | * Append a data run to the end of the attribute and update its offset |
707 | | * value. This ignores the offset in the data run and blindly appends. |
708 | | * |
709 | | * @param a_fs File system run is from |
710 | | * @param a_fs_attr Data attribute to append to |
711 | | * @param a_data_run Data run to append. |
712 | | */ |
713 | | void |
714 | | tsk_fs_attr_append_run( |
715 | | [[maybe_unused]] TSK_FS_INFO * a_fs, |
716 | | TSK_FS_ATTR * a_fs_attr, |
717 | | TSK_FS_ATTR_RUN * a_data_run) |
718 | 2.74M | { |
719 | 2.74M | TSK_FS_ATTR_RUN *data_run_cur; |
720 | | |
721 | 2.74M | if ((a_fs_attr == NULL) || (a_data_run == NULL)) { |
722 | 0 | return; |
723 | 0 | } |
724 | | |
725 | 2.74M | if (a_fs_attr->nrd.run == NULL) { |
726 | 233k | a_fs_attr->nrd.run = a_data_run; |
727 | 233k | a_data_run->offset = 0; |
728 | 233k | } |
729 | 2.51M | else { |
730 | | // just in case this was not updated |
731 | 2.51M | if ((a_fs_attr->nrd.run_end == NULL) |
732 | 2.51M | || (a_fs_attr->nrd.run_end->next != NULL)) { |
733 | | |
734 | 2.51M | int counter = 0; |
735 | | |
736 | 2.51M | data_run_cur = a_fs_attr->nrd.run; |
737 | 913M | while (data_run_cur->next) { |
738 | 911M | data_run_cur = data_run_cur->next; |
739 | | /* bugfix issue 2268 |
740 | | * "Infinite loop when processing a Linux disk image (tsk_fs_file_attr_getsize) #2268" |
741 | | * This establishes a counter that breaks out of the loop after a large number of iterations. |
742 | | */ |
743 | 911M | if (++counter > LOGICAL_MAX_ATTR_RUN) { |
744 | 0 | break; |
745 | 0 | } |
746 | 911M | } |
747 | 2.51M | a_fs_attr->nrd.run_end = data_run_cur; |
748 | 2.51M | } |
749 | 2.51M | a_fs_attr->nrd.run_end->next = a_data_run; |
750 | 2.51M | a_data_run->offset = |
751 | 2.51M | a_fs_attr->nrd.run_end->offset + a_fs_attr->nrd.run_end->len; |
752 | 2.51M | } |
753 | | |
754 | | // update the rest of the offsets in the run (if any exist) |
755 | 2.74M | data_run_cur = a_data_run; |
756 | 2.74M | while (data_run_cur->next) { |
757 | 0 | data_run_cur->next->offset = |
758 | 0 | data_run_cur->offset + data_run_cur->len; |
759 | 0 | a_fs_attr->nrd.run_end = data_run_cur->next; |
760 | 0 | data_run_cur = data_run_cur->next; |
761 | 0 | } |
762 | 2.74M | } |
763 | | |
764 | | /** \internal |
765 | | * Processes a resident TSK_FS_ATTR structure and calls the callback with the associated |
766 | | * data. The size of the buffer in the callback will be block_size at max. |
767 | | * |
768 | | * @param a_fs File system being analyzed |
769 | | * @param fs_attr Resident data structure to be walked |
770 | | * @param a_flags Flags for walking |
771 | | * @param a_action Callback action |
772 | | * @param a_ptr Pointer to data that is passed to callback |
773 | | * @returns 1 on error or 0 on success |
774 | | */ |
775 | | static uint8_t |
776 | | tsk_fs_attr_walk_res(const TSK_FS_ATTR * fs_attr, |
777 | | TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CB a_action, |
778 | | void *a_ptr) |
779 | 3.26k | { |
780 | 3.26k | char *buf = NULL; |
781 | 3.26k | int myflags; |
782 | 3.26k | int retval; |
783 | 3.26k | size_t buf_len = 0; |
784 | 3.26k | TSK_OFF_T off; |
785 | 3.26k | size_t read_len; |
786 | 3.26k | TSK_FS_INFO *fs; |
787 | | |
788 | 3.26k | fs = fs_attr->fs_file->fs_info; |
789 | | |
790 | 3.26k | if ((fs_attr->flags & TSK_FS_ATTR_RES) == 0) { |
791 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
792 | 0 | tsk_error_set_errstr |
793 | 0 | ("tsk_fs_file_walk_res: called with non-resident data"); |
794 | 0 | return 1; |
795 | 0 | } |
796 | | |
797 | | /* Allocate a buffer that is at most a block size in length */ |
798 | 3.26k | buf_len = (size_t) fs_attr->size; |
799 | 3.26k | if (buf_len > fs->block_size) |
800 | 158 | buf_len = fs->block_size; |
801 | | |
802 | 3.26k | if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) { |
803 | 3.26k | if ((buf = (char*) tsk_malloc(buf_len)) == NULL) { |
804 | 0 | return 1; |
805 | 0 | } |
806 | 3.26k | } |
807 | | |
808 | 3.26k | myflags = |
809 | 3.26k | TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC | |
810 | 3.26k | TSK_FS_BLOCK_FLAG_RES; |
811 | | |
812 | | // Call the callback in (at max) block-sized chunks. |
813 | 3.26k | retval = TSK_WALK_CONT; |
814 | 7.55k | for (off = 0; off < fs_attr->size; off += read_len) { |
815 | | |
816 | 7.45k | if ((uint64_t) (fs_attr->size - off) > buf_len) |
817 | 4.28k | read_len = buf_len; |
818 | 3.17k | else |
819 | 3.17k | read_len = (size_t) (fs_attr->size - off); |
820 | | |
821 | 7.45k | if (buf) { |
822 | | // wipe rest of buffer if we are not going to read into all of it |
823 | 7.45k | if (read_len != buf_len) |
824 | 122 | memset(&buf[read_len], 0, buf_len - read_len); |
825 | | |
826 | 7.45k | memcpy(buf, &fs_attr->rd.buf[off], read_len); |
827 | 7.45k | } |
828 | 7.45k | retval = |
829 | 7.45k | a_action(fs_attr->fs_file, off, 0, buf, read_len, |
830 | 7.45k | (TSK_FS_BLOCK_FLAG_ENUM) myflags, a_ptr); |
831 | | |
832 | 7.45k | if (retval != TSK_WALK_CONT) |
833 | 3.17k | break; |
834 | 7.45k | } |
835 | | |
836 | 3.26k | free(buf); |
837 | | |
838 | 3.26k | if (retval == TSK_WALK_ERROR) |
839 | 0 | return 1; |
840 | 3.26k | else |
841 | 3.26k | return 0; |
842 | 3.26k | } |
843 | | |
844 | | |
845 | | /** \internal |
846 | | * Processes a non-resident TSK_FS_ATTR structure and calls the callback with the associated |
847 | | * data. |
848 | | * |
849 | | * @param fs_attr Resident data structure to be walked |
850 | | * @param a_flags Flags for walking |
851 | | * @param a_action Callback action |
852 | | * @param a_ptr Pointer to data that is passed to callback |
853 | | * @returns 1 on error or 0 on success |
854 | | */ |
855 | | static uint8_t |
856 | | tsk_fs_attr_walk_nonres(const TSK_FS_ATTR * fs_attr, |
857 | | TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CB a_action, |
858 | | void *a_ptr) |
859 | 53.4k | { |
860 | 53.4k | char *buf = NULL; |
861 | 53.4k | TSK_OFF_T tot_size; |
862 | 53.4k | TSK_OFF_T off = 0; |
863 | 53.4k | TSK_FS_ATTR_RUN *fs_attr_run; |
864 | 53.4k | int retval; |
865 | 53.4k | uint32_t skip_remain; |
866 | 53.4k | TSK_FS_INFO *fs = fs_attr->fs_file->fs_info; |
867 | 53.4k | uint8_t stop_loop = 0; |
868 | | |
869 | 53.4k | if ((fs_attr->flags & TSK_FS_ATTR_NONRES) == 0) { |
870 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
871 | 0 | tsk_error_set_errstr |
872 | 0 | ("tsk_fs_file_walk_nonres: called with non-non-resident data"); |
873 | 0 | return 1; |
874 | 0 | } |
875 | | |
876 | | /* if we want the slack space too, then use the allocsize */ |
877 | 53.4k | if (a_flags & TSK_FS_FILE_WALK_FLAG_SLACK) |
878 | 26.4k | tot_size = fs_attr->nrd.allocsize; |
879 | 27.0k | else |
880 | 27.0k | tot_size = fs_attr->size; |
881 | | |
882 | 53.4k | skip_remain = fs_attr->nrd.skiplen; |
883 | | |
884 | 53.4k | if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) { |
885 | 47.3k | if ((buf = (char *) tsk_malloc(fs->block_size)) == NULL) { |
886 | 0 | return 1; |
887 | 0 | } |
888 | 47.3k | } |
889 | | |
890 | | /* cycle through the number of runs we have */ |
891 | 53.4k | retval = TSK_WALK_CONT; |
892 | 59.9k | for (fs_attr_run = fs_attr->nrd.run; fs_attr_run; |
893 | 53.4k | fs_attr_run = fs_attr_run->next) { |
894 | 53.3k | TSK_DADDR_T addr, len_idx; |
895 | | |
896 | 53.3k | addr = fs_attr_run->addr; |
897 | | |
898 | | /* cycle through each block in the run */ |
899 | 36.7M | for (len_idx = 0; len_idx < fs_attr_run->len; len_idx++) { |
900 | | |
901 | 36.7M | TSK_FS_BLOCK_FLAG_ENUM myflags; |
902 | | |
903 | | /* If the address is too large then give an error */ |
904 | 36.7M | if (addr + len_idx > fs->last_block) { |
905 | 449 | if (fs_attr->fs_file-> |
906 | 449 | meta->flags & TSK_FS_META_FLAG_UNALLOC) |
907 | 247 | tsk_error_set_errno(TSK_ERR_FS_RECOVER); |
908 | 202 | else |
909 | 202 | tsk_error_set_errno(TSK_ERR_FS_BLK_NUM); |
910 | 449 | tsk_error_set_errstr |
911 | 449 | ("Invalid address in run (too large): %" PRIuDADDR "", |
912 | 449 | addr + len_idx); |
913 | 449 | free(buf); |
914 | 449 | return 1; |
915 | 449 | } |
916 | | |
917 | | // load the buffer if they want more than just the address |
918 | 36.7M | if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) { |
919 | | |
920 | | /* sparse files just get 0s */ |
921 | 36.7M | if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) { |
922 | 1.29M | memset(buf, 0, fs->block_size); |
923 | 1.29M | } |
924 | | /* FILLER entries exist when the source file system can store run |
925 | | * info out of order and we did not get all of the run info. We |
926 | | * return 0s if data is read from this type of run. */ |
927 | 35.4M | else if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) { |
928 | 32.5M | memset(buf, 0, fs->block_size); |
929 | 32.5M | if (tsk_verbose) |
930 | 0 | fprintf(stderr, |
931 | 0 | "tsk_fs_attr_walk_nonres: File %" PRIuINUM |
932 | 0 | " has FILLER entry, using 0s\n", |
933 | 0 | fs_attr->fs_file->meta->addr); |
934 | 32.5M | } |
935 | | |
936 | | // we return 0s for reads past the initsize |
937 | 2.89M | else if (off >= fs_attr->nrd.initsize |
938 | 2.89M | && (a_flags & TSK_FS_FILE_WALK_FLAG_SLACK) == 0) { |
939 | 1.75M | memset(buf, 0, fs->block_size); |
940 | 1.75M | } |
941 | 1.14M | else { |
942 | 1.14M | ssize_t cnt; |
943 | 1.14M | if (fs->ftype == TSK_FS_TYPE_LOGICAL) { |
944 | | // We can't read logical files directly from the image. |
945 | 0 | cnt = logicalfs_read_block(fs, fs_attr->fs_file, addr + len_idx, buf); |
946 | 0 | } |
947 | 1.14M | else { |
948 | 1.14M | cnt = tsk_fs_read_block_decrypt |
949 | 1.14M | (fs, addr + len_idx, buf, fs->block_size, fs_attr_run->crypto_id + len_idx); |
950 | 1.14M | } |
951 | 1.14M | if (cnt != fs->block_size) { |
952 | 1.79k | if (cnt >= 0) { |
953 | 0 | tsk_error_reset(); |
954 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ); |
955 | 0 | } |
956 | 1.79k | tsk_error_set_errstr2 |
957 | 1.79k | ("tsk_fs_file_walk: Error reading block at %" |
958 | 1.79k | PRIuDADDR, addr + len_idx); |
959 | 1.79k | free(buf); |
960 | 1.79k | return 1; |
961 | 1.79k | } |
962 | 1.13M | if (off + fs->block_size > fs_attr->nrd.initsize |
963 | 1.13M | && (a_flags & TSK_FS_FILE_WALK_FLAG_SLACK) == 0) { |
964 | 24.7k | memset(&buf[fs_attr->nrd.initsize - off], 0, |
965 | 24.7k | fs->block_size - |
966 | 24.7k | (size_t) (fs_attr->nrd.initsize - off)); |
967 | 24.7k | } |
968 | 1.13M | } |
969 | 36.7M | } |
970 | | |
971 | | /* Need to account for the skip length, which is the number of bytes |
972 | | * in the start of the attribute that are skipped and that are not |
973 | | * included in the overall length. We will seek past those and not |
974 | | * return those in the action. We just read a block size so check |
975 | | * if there is data to be returned in this buffer. */ |
976 | 36.7M | if (skip_remain >= fs->block_size) { |
977 | 0 | skip_remain -= fs->block_size; |
978 | 0 | } |
979 | 36.7M | else { |
980 | 36.7M | size_t ret_len; |
981 | | |
982 | | /* Do we want to return a full block, or just the end? */ |
983 | 36.7M | if ((TSK_OFF_T) fs->block_size - skip_remain < |
984 | 36.7M | tot_size - off) |
985 | 36.7M | ret_len = fs->block_size - skip_remain; |
986 | 44.6k | else |
987 | 44.6k | ret_len = (size_t) (tot_size - off); |
988 | | |
989 | | /* Only do sparse or FILLER clusters if NOSPARSE is not set */ |
990 | 36.7M | if ((fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) || |
991 | 36.7M | (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) || |
992 | 36.7M | (off > fs_attr->nrd.initsize)) { |
993 | 36.6M | myflags = fs->block_getflags(fs, 0); |
994 | 36.6M | myflags = (TSK_FS_BLOCK_FLAG_ENUM) (myflags | TSK_FS_BLOCK_FLAG_SPARSE); |
995 | 36.6M | if ((a_flags & TSK_FS_FILE_WALK_FLAG_NOSPARSE) == 0) { |
996 | 36.6M | retval = |
997 | 36.6M | a_action(fs_attr->fs_file, off, 0, |
998 | 36.6M | &buf[skip_remain], ret_len, myflags, a_ptr); |
999 | 36.6M | } |
1000 | 36.6M | } |
1001 | 155k | else { |
1002 | 155k | myflags = fs->block_getflags(fs, addr + len_idx); |
1003 | 155k | myflags = (TSK_FS_BLOCK_FLAG_ENUM) (myflags | TSK_FS_BLOCK_FLAG_RAW); |
1004 | | |
1005 | 155k | retval = |
1006 | 155k | a_action(fs_attr->fs_file, off, addr + len_idx, |
1007 | 155k | &buf[skip_remain], ret_len, myflags, a_ptr); |
1008 | 155k | } |
1009 | 36.7M | off += ret_len; |
1010 | 36.7M | skip_remain = 0; |
1011 | | |
1012 | 36.7M | if (retval != TSK_WALK_CONT) { |
1013 | 44.4k | stop_loop = 1; |
1014 | 44.4k | break; |
1015 | 44.4k | } |
1016 | | |
1017 | 36.7M | if (off >= tot_size) { |
1018 | 151 | stop_loop = 1; |
1019 | 151 | break; |
1020 | 151 | } |
1021 | 36.7M | } |
1022 | 36.7M | } |
1023 | 51.0k | if (stop_loop) |
1024 | 44.6k | break; |
1025 | 51.0k | } |
1026 | | |
1027 | 51.2k | free(buf); |
1028 | | |
1029 | 51.2k | if (retval == TSK_WALK_ERROR) |
1030 | 1 | return 1; |
1031 | 51.2k | else |
1032 | 51.2k | return 0; |
1033 | 51.2k | } |
1034 | | |
1035 | | |
1036 | | /** |
1037 | | * \ingroup fslib |
1038 | | * Process an attribute and call a callback function with its contents. The callback will be |
1039 | | * called with chunks of data that are fs->block_size or less. The address given in the callback |
1040 | | * will be correct only for raw files (when the raw file contents were stored in the block). For |
1041 | | * compressed and sparse attributes, the address may be zero. |
1042 | | * |
1043 | | * @param a_fs_attr Attribute to process |
1044 | | * @param a_flags Flags to use while processing attribute |
1045 | | * @param a_action Callback action to call with content |
1046 | | * @param a_ptr Pointer that will passed to callback |
1047 | | * @returns 1 on error and 0 on success. |
1048 | | */ |
1049 | | uint8_t |
1050 | | tsk_fs_attr_walk(const TSK_FS_ATTR * a_fs_attr, |
1051 | | TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CB a_action, |
1052 | | void *a_ptr) |
1053 | 58.0k | { |
1054 | 58.0k | TSK_FS_INFO *fs; |
1055 | | |
1056 | | // clean up any error messages that are lying around |
1057 | 58.0k | tsk_error_reset(); |
1058 | | |
1059 | | // check the FS_INFO, FS_FILE structures |
1060 | 58.0k | if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL) |
1061 | 58.0k | || (a_fs_attr->fs_file->meta == NULL) |
1062 | 58.0k | || (a_fs_attr->fs_file->fs_info == NULL)) { |
1063 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1064 | 0 | tsk_error_set_errstr |
1065 | 0 | ("tsk_fs_attr_walk: called with NULL pointers"); |
1066 | 0 | return 1; |
1067 | 0 | } |
1068 | 58.0k | fs = a_fs_attr->fs_file->fs_info; |
1069 | | |
1070 | 58.0k | if (fs->tag != TSK_FS_INFO_TAG) { |
1071 | | // || (a_fs_attr->id != TSK_FS_ATTR_ID)) { |
1072 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1073 | 0 | tsk_error_set_errstr |
1074 | 0 | ("tsk_fs_attr_walk: called with unallocated structures"); |
1075 | 0 | return 1; |
1076 | 0 | } |
1077 | 58.0k | if (a_fs_attr->flags & TSK_FS_ATTR_COMP) { |
1078 | 1.31k | if (a_fs_attr->w == NULL) { |
1079 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1080 | 0 | tsk_error_set_errstr |
1081 | 0 | ("tsk_fs_attr_walk: compressed attribute found, but special function not defined"); |
1082 | 0 | return 1; |
1083 | 0 | } |
1084 | 1.31k | return a_fs_attr->w(a_fs_attr, a_flags, a_action, a_ptr); |
1085 | 1.31k | } |
1086 | | // resident data |
1087 | 56.7k | if (a_fs_attr->flags & TSK_FS_ATTR_RES) { |
1088 | 3.26k | fflush(stderr); |
1089 | 3.26k | return tsk_fs_attr_walk_res(a_fs_attr, a_flags, a_action, a_ptr); |
1090 | 3.26k | } |
1091 | | // non-resident data |
1092 | 53.4k | else if (a_fs_attr->flags & TSK_FS_ATTR_NONRES) { |
1093 | 53.4k | return tsk_fs_attr_walk_nonres(a_fs_attr, a_flags, a_action, |
1094 | 53.4k | a_ptr); |
1095 | 53.4k | } |
1096 | | |
1097 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1098 | 0 | tsk_error_set_errstr |
1099 | 0 | ("tsk_fs_attr_walk: called with unknown attribute type: %x", |
1100 | 0 | a_fs_attr->flags); |
1101 | 0 | return 1; |
1102 | 56.7k | } |
1103 | | |
1104 | | |
1105 | | |
1106 | | /** |
1107 | | * \ingroup fslib |
1108 | | * Read the contents of a given attribute using a typical read() type interface. |
1109 | | * 0s are returned for missing runs. |
1110 | | * |
1111 | | * @param a_fs_attr The attribute to read. |
1112 | | * @param a_offset The byte offset to start reading from. |
1113 | | * @param a_buf The buffer to read the data into. |
1114 | | * @param a_len The number of bytes to read from the file. |
1115 | | * @param a_flags Flags to use while reading |
1116 | | * @returns The number of bytes read or -1 on error (incl if offset is past end of file). |
1117 | | */ |
1118 | | ssize_t |
1119 | | tsk_fs_attr_read(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset, |
1120 | | char *a_buf, size_t a_len, TSK_FS_FILE_READ_FLAG_ENUM a_flags) |
1121 | 8.57M | { |
1122 | 8.57M | TSK_FS_INFO *fs; |
1123 | | |
1124 | 8.57M | if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL) |
1125 | 8.57M | || (a_fs_attr->fs_file->fs_info == NULL) || (a_buf == NULL)) { |
1126 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1127 | 0 | tsk_error_set_errstr |
1128 | 0 | ("tsk_fs_attr_read: Attribute has null pointers."); |
1129 | 0 | return -1; |
1130 | 0 | } |
1131 | 8.57M | fs = a_fs_attr->fs_file->fs_info; |
1132 | | |
1133 | | // Handle logical directories separately |
1134 | 8.57M | if (fs->ftype == TSK_FS_TYPE_LOGICAL) { |
1135 | 0 | return logicalfs_read(fs, a_fs_attr->fs_file, a_offset, a_len, a_buf); |
1136 | 0 | } |
1137 | | |
1138 | | /* for compressed data, call the specialized function */ |
1139 | 8.57M | if (a_fs_attr->flags & TSK_FS_ATTR_COMP) { |
1140 | 0 | if (a_fs_attr->r == NULL) { |
1141 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1142 | 0 | tsk_error_set_errstr |
1143 | 0 | ("tsk_fs_attr_read: Attribute has compressed type set, but no compressed read function defined"); |
1144 | 0 | return -1; |
1145 | 0 | } |
1146 | 0 | return a_fs_attr->r(a_fs_attr, a_offset, a_buf, a_len); |
1147 | 0 | } |
1148 | | |
1149 | | /* For resident data, copy data from the local buffer */ |
1150 | 8.57M | else if (a_fs_attr->flags & TSK_FS_ATTR_RES) { |
1151 | 22.5k | size_t len_toread; |
1152 | | |
1153 | 22.5k | if (a_offset >= a_fs_attr->size) { |
1154 | 1 | tsk_error_reset(); |
1155 | 1 | tsk_error_set_errno(TSK_ERR_FS_READ_OFF); |
1156 | 1 | tsk_error_set_errstr("tsk_fs_attr_read - %" PRIdOFF, a_offset); |
1157 | 1 | return -1; |
1158 | 1 | } |
1159 | | |
1160 | 22.5k | len_toread = a_len; |
1161 | 22.5k | if (a_offset + (TSK_OFF_T)a_len > a_fs_attr->size) { |
1162 | 2 | len_toread = (size_t) (a_fs_attr->size - a_offset); |
1163 | 2 | memset(&a_buf[len_toread], 0, a_len - len_toread); |
1164 | 2 | } |
1165 | | |
1166 | 22.5k | memcpy(a_buf, &a_fs_attr->rd.buf[a_offset], len_toread); |
1167 | | |
1168 | 22.5k | return (ssize_t) len_toread; |
1169 | 22.5k | } |
1170 | | |
1171 | | /* For non-resident data, load the needed block and copy the data */ |
1172 | 8.55M | else if (a_fs_attr->flags & TSK_FS_ATTR_NONRES) { |
1173 | 8.55M | TSK_FS_ATTR_RUN *data_run_cur; |
1174 | 8.55M | TSK_DADDR_T blkoffset_toread; // block offset of where we want to start reading from |
1175 | 8.55M | size_t byteoffset_toread; // byte offset in blkoffset_toread of where we want to start reading from |
1176 | 8.55M | ssize_t len_remain; // length remaining to copy |
1177 | 8.55M | size_t len_toread; // length total to copy |
1178 | | |
1179 | 8.55M | if (((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) |
1180 | 8.55M | && (a_offset >= a_fs_attr->nrd.allocsize)) |
1181 | 8.55M | || (!(a_flags & TSK_FS_FILE_READ_FLAG_SLACK) |
1182 | 8.55M | && (a_offset >= a_fs_attr->size))) { |
1183 | 5.36k | tsk_error_reset(); |
1184 | 5.36k | tsk_error_set_errno(TSK_ERR_FS_READ_OFF); |
1185 | 5.36k | tsk_error_set_errstr("tsk_fs_attr_read - %" PRIdOFF, a_offset); |
1186 | 5.36k | return -1; |
1187 | 5.36k | } |
1188 | | |
1189 | 8.54M | blkoffset_toread = a_offset / fs->block_size; |
1190 | 8.54M | byteoffset_toread = (size_t) (a_offset % fs->block_size); |
1191 | | |
1192 | | // determine how many bytes we can copy |
1193 | 8.54M | len_toread = a_len; |
1194 | 8.54M | if (a_flags & TSK_FS_FILE_READ_FLAG_SLACK) { |
1195 | 0 | if (a_offset + (TSK_OFF_T)a_len > a_fs_attr->nrd.allocsize) |
1196 | 0 | len_toread = |
1197 | 0 | (size_t) (a_fs_attr->nrd.allocsize - a_offset); |
1198 | 0 | } |
1199 | 8.54M | else { |
1200 | 8.54M | if (a_offset + (TSK_OFF_T)a_len > a_fs_attr->size) |
1201 | 4.67k | len_toread = (size_t) (a_fs_attr->size - a_offset); |
1202 | 8.54M | } |
1203 | | |
1204 | | |
1205 | | // wipe the buffer we won't read into |
1206 | 8.54M | if (len_toread < a_len) |
1207 | 4.67k | memset(&a_buf[len_toread], 0, a_len - len_toread); |
1208 | | |
1209 | 8.54M | len_remain = len_toread; |
1210 | | |
1211 | | // cycle through the runs until we find the one where our offset starts |
1212 | 127M | for (data_run_cur = a_fs_attr->nrd.run; data_run_cur && len_remain > 0; |
1213 | 119M | data_run_cur = data_run_cur->next) { |
1214 | | |
1215 | 119M | TSK_DADDR_T blkoffset_inrun; |
1216 | 119M | size_t len_inrun; |
1217 | | |
1218 | | // See if this run contains the starting offset they requested |
1219 | 119M | if (data_run_cur->offset + data_run_cur->len <= |
1220 | 119M | blkoffset_toread) |
1221 | 110M | continue; |
1222 | | |
1223 | | // We want this run, so find out the offset that we want |
1224 | | // we'll start at 0 if we already read data in the last run. |
1225 | 8.54M | if (data_run_cur->offset < blkoffset_toread) |
1226 | 7.53M | blkoffset_inrun = blkoffset_toread - data_run_cur->offset; |
1227 | 1.00M | else |
1228 | 1.00M | blkoffset_inrun = 0; |
1229 | | |
1230 | | // see if we need to read the rest of this run and into the next or if it is all here |
1231 | 8.54M | len_inrun = len_remain; |
1232 | 8.54M | if ((data_run_cur->len - blkoffset_inrun) * fs->block_size - |
1233 | 8.54M | byteoffset_toread < (size_t)len_remain) { |
1234 | 3.27k | len_inrun = |
1235 | 3.27k | (size_t) ((data_run_cur->len - |
1236 | 3.27k | blkoffset_inrun) * fs->block_size - |
1237 | 3.27k | byteoffset_toread); |
1238 | 3.27k | } |
1239 | | |
1240 | | /* sparse files/runs just get 0s */ |
1241 | 8.54M | if (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) { |
1242 | 5.40M | memset(&a_buf[len_toread - len_remain], 0, len_inrun); |
1243 | 5.40M | } |
1244 | | |
1245 | | /* FILLER entries exist when the source file system can store run |
1246 | | * info out of order and we did not get all of the run info. We |
1247 | | * return 0s if data is read from this type of run. */ |
1248 | 3.14M | else if (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) { |
1249 | 2.17M | if (a_buf == NULL) { |
1250 | 0 | tsk_error_reset(); |
1251 | 0 | tsk_error_set_errno(TSK_ERR_FS_READ_OFF); |
1252 | 0 | tsk_error_set_errstr("tsk_fs_attr_read - missing a_buf"); |
1253 | 0 | return -1; |
1254 | 0 | } |
1255 | 2.17M | memset(&a_buf[len_toread - len_remain], 0, len_inrun); |
1256 | 2.17M | if (tsk_verbose) |
1257 | 0 | fprintf(stderr, |
1258 | 0 | "tsk_fs_attr_read_type: File %" PRIuINUM |
1259 | 0 | " has FILLER entry, using 0s\n", |
1260 | 0 | (a_fs_attr->fs_file->meta) ? a_fs_attr-> |
1261 | 0 | fs_file->meta->addr : 0); |
1262 | 2.17M | } |
1263 | | |
1264 | | // we return 0s for reads past the initsize (unless they want slack space) |
1265 | 973k | else if (((TSK_OFF_T) ((data_run_cur->offset + |
1266 | 973k | blkoffset_inrun) * fs->block_size + |
1267 | 973k | byteoffset_toread) >= a_fs_attr->nrd.initsize) |
1268 | 973k | && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) { |
1269 | 214 | memset(&a_buf[len_toread - len_remain], 0, len_inrun); |
1270 | 214 | if (tsk_verbose) |
1271 | 0 | fprintf(stderr, |
1272 | 0 | "tsk_fs_attr_read: Returning 0s for read past end of initsize (%" |
1273 | 0 | PRIuINUM ")\n", ((a_fs_attr->fs_file) |
1274 | 0 | && (a_fs_attr->fs_file-> |
1275 | 0 | meta)) ? a_fs_attr->fs_file->meta-> |
1276 | 0 | addr : 0); |
1277 | 214 | } |
1278 | | |
1279 | | // we are going to read some data |
1280 | 973k | else { |
1281 | 973k | TSK_OFF_T fs_offset_b; |
1282 | 973k | ssize_t cnt; |
1283 | | |
1284 | | // calculate the byte offset in the file system that we want to read from |
1285 | 973k | fs_offset_b = |
1286 | 973k | (data_run_cur->addr + |
1287 | 973k | blkoffset_inrun) * fs->block_size; |
1288 | | |
1289 | | // add the byte offset in the block |
1290 | 973k | fs_offset_b += byteoffset_toread; |
1291 | 973k | cnt = |
1292 | 973k | tsk_fs_read_decrypt(fs, fs_offset_b, |
1293 | 973k | &a_buf[len_toread - len_remain], len_inrun, |
1294 | 973k | data_run_cur->crypto_id + blkoffset_inrun); |
1295 | | |
1296 | 973k | if (cnt != (ssize_t)len_inrun) { |
1297 | 80.9k | if (cnt >= 0) { |
1298 | 59.9k | tsk_error_reset(); |
1299 | 59.9k | tsk_error_set_errno(TSK_ERR_FS_READ); |
1300 | 59.9k | } |
1301 | 80.9k | tsk_error_set_errstr2 |
1302 | 80.9k | ("tsk_fs_attr_read_type: offset: %" PRIdOFF |
1303 | 80.9k | " Len: %" PRIuSIZE "", fs_offset_b, len_inrun); |
1304 | 80.9k | return cnt; |
1305 | 80.9k | } |
1306 | | |
1307 | | // see if part of the data is in the non-initialized space |
1308 | 892k | if (((TSK_OFF_T) ((data_run_cur->offset + |
1309 | 892k | blkoffset_inrun) * fs->block_size + |
1310 | 892k | byteoffset_toread + len_inrun) > |
1311 | 892k | a_fs_attr->nrd.initsize) |
1312 | 892k | && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) { |
1313 | | |
1314 | 56 | size_t uninit_off = (size_t) (a_fs_attr->nrd.initsize - |
1315 | 56 | ((data_run_cur->offset + |
1316 | 56 | blkoffset_inrun) * fs->block_size + |
1317 | 56 | byteoffset_toread)); |
1318 | | |
1319 | 56 | memset(&a_buf[len_toread - len_remain + uninit_off], 0, |
1320 | 56 | len_inrun - uninit_off); |
1321 | 56 | } |
1322 | | |
1323 | 892k | } |
1324 | | |
1325 | 8.46M | len_remain -= len_inrun; |
1326 | | |
1327 | | // reset this in case we need to also read from the next run |
1328 | 8.46M | byteoffset_toread = 0; |
1329 | 8.46M | } |
1330 | 8.46M | return (ssize_t) (len_toread - len_remain); |
1331 | 8.54M | } |
1332 | | |
1333 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
1334 | 0 | tsk_error_set_errstr("tsk_fs_attr_read: Unknown attribute type: %x", |
1335 | 0 | a_fs_attr->flags); |
1336 | 0 | return -1; |
1337 | 8.57M | } |