/src/sleuthkit/tsk/fs/fs_attrlist.c
Line | Count | Source |
1 | | /* |
2 | | ** fs_attrlist |
3 | | ** The Sleuth Kit |
4 | | ** |
5 | | ** Brian Carrier [carrier <at> sleuthkit [dot] org] |
6 | | ** Copyright (c) 2008-2011 Brian Carrier. All Rights reserved |
7 | | ** |
8 | | ** This software is distributed under the Common Public License 1.0 |
9 | | */ |
10 | | |
11 | | /** |
12 | | * \file fs_attrlist.c |
13 | | * File that contains functions to process TSK_FS_ATTRLIST structures, which |
14 | | * hold a linked list of TSK_FS_ATTR attribute structures. |
15 | | */ |
16 | | |
17 | | #include "tsk_fs_i.h" |
18 | | |
19 | | /** \internal |
20 | | * Allocate a new data list structure |
21 | | * |
22 | | * @returns Pointer to new list structure or NULL on error |
23 | | */ |
24 | | TSK_FS_ATTRLIST * |
25 | | tsk_fs_attrlist_alloc() |
26 | 967k | { |
27 | 967k | TSK_FS_ATTRLIST *fs_attrlist; |
28 | | |
29 | 967k | if ((fs_attrlist = |
30 | 967k | (TSK_FS_ATTRLIST *) tsk_malloc(sizeof(TSK_FS_ATTRLIST))) == |
31 | 967k | NULL) |
32 | 0 | return NULL; |
33 | 967k | return fs_attrlist; |
34 | 967k | } |
35 | | |
36 | | /** \internal |
37 | | * Free a list and the attributes inside of it |
38 | | */ |
39 | | void |
40 | | tsk_fs_attrlist_free(TSK_FS_ATTRLIST * a_fs_attrlist) |
41 | 967k | { |
42 | 967k | TSK_FS_ATTR *fs_attr_cur, *fs_attr_tmp; |
43 | 967k | if (a_fs_attrlist == NULL) |
44 | 0 | return; |
45 | | |
46 | 967k | fs_attr_cur = a_fs_attrlist->head; |
47 | 3.54M | while (fs_attr_cur) { |
48 | 2.57M | fs_attr_tmp = fs_attr_cur->next; |
49 | 2.57M | tsk_fs_attr_free(fs_attr_cur); |
50 | 2.57M | fs_attr_cur = fs_attr_tmp; |
51 | 2.57M | } |
52 | 967k | free(a_fs_attrlist); |
53 | 967k | } |
54 | | |
55 | | /** \internal |
56 | | * Add a new attribute to the list. |
57 | | * |
58 | | * @param a_fs_attrlist List structure to add to |
59 | | * @param a_fs_attr Data attribute to add |
60 | | * @returns 1 on error and 0 on success. Caller must free memory on error. |
61 | | */ |
62 | | uint8_t |
63 | | tsk_fs_attrlist_add(TSK_FS_ATTRLIST * a_fs_attrlist, |
64 | | TSK_FS_ATTR * a_fs_attr) |
65 | 2.58M | { |
66 | 2.58M | if (a_fs_attrlist == NULL) { |
67 | 0 | tsk_error_reset(); |
68 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
69 | 0 | tsk_error_set_errstr("Null list in tsk_fs_attrlist_add"); |
70 | 0 | return 1; |
71 | 0 | } |
72 | | |
73 | | // verify INUSE is set |
74 | 2.58M | a_fs_attr->flags |= TSK_FS_ATTR_INUSE; |
75 | | |
76 | 2.58M | if (a_fs_attrlist->head == NULL) { |
77 | 932k | a_fs_attrlist->head = a_fs_attr; |
78 | 932k | } |
79 | 1.64M | else { |
80 | 1.64M | TSK_FS_ATTR *fs_attr_cur; |
81 | 1.64M | fs_attr_cur = a_fs_attrlist->head; |
82 | 5.00M | while (fs_attr_cur) { |
83 | | // check if it already exists |
84 | 5.00M | if ((fs_attr_cur->type == a_fs_attr->type) |
85 | 87.6k | && (fs_attr_cur->id == a_fs_attr->id)) { |
86 | 3.26k | tsk_error_reset(); |
87 | 3.26k | tsk_error_set_errno(TSK_ERR_FS_ARG); |
88 | 3.26k | tsk_error_set_errstr |
89 | 3.26k | ("datalist_add: Type %d and Id %d already in list", |
90 | 3.26k | a_fs_attr->type, a_fs_attr->id); |
91 | 3.26k | return 1; |
92 | 3.26k | } |
93 | 5.00M | if (fs_attr_cur->next == NULL) { |
94 | 1.64M | fs_attr_cur->next = a_fs_attr; |
95 | 1.64M | break; |
96 | 1.64M | } |
97 | 3.35M | fs_attr_cur = fs_attr_cur->next; |
98 | 3.35M | } |
99 | 1.64M | } |
100 | 2.57M | return 0; |
101 | 2.58M | } |
102 | | |
103 | | |
104 | | |
105 | | /** |
106 | | * \internal |
107 | | * Return either an empty element in the list or create a new one at the end |
108 | | * |
109 | | * Preference is given to finding one of the same type to prevent |
110 | | * excessive malloc's, but if one is not found then a different |
111 | | * type is used: type = [TSK_FS_ATTR_NONRES | TSK_FS_ATTR_RES] |
112 | | * |
113 | | * @param a_fs_attrlist Attribute list to search |
114 | | * @param a_atype Preference for attribute type to reuse |
115 | | * @return NULL on error or attribute in list to use |
116 | | */ |
117 | | TSK_FS_ATTR * |
118 | | tsk_fs_attrlist_getnew(TSK_FS_ATTRLIST * a_fs_attrlist, |
119 | | TSK_FS_ATTR_FLAG_ENUM a_atype) |
120 | 2.72M | { |
121 | 2.72M | TSK_FS_ATTR *fs_attr_cur; |
122 | 2.72M | TSK_FS_ATTR *fs_attr_ok = NULL; |
123 | | |
124 | 2.72M | if (a_fs_attrlist == NULL) { |
125 | 0 | tsk_error_reset(); |
126 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
127 | 0 | tsk_error_set_errstr("Null list in tsk_fs_attrlist_getnew()"); |
128 | 0 | return NULL; |
129 | 0 | } |
130 | | |
131 | 2.72M | if ((a_atype != TSK_FS_ATTR_NONRES) && (a_atype != TSK_FS_ATTR_RES)) { |
132 | 0 | tsk_error_reset(); |
133 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
134 | 0 | tsk_error_set_errstr("Invalid Type in tsk_fs_attrlist_getnew()"); |
135 | 0 | return NULL; |
136 | 0 | } |
137 | | |
138 | 7.87M | for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; |
139 | 5.29M | fs_attr_cur = fs_attr_cur->next) { |
140 | 5.29M | if (fs_attr_cur->flags == 0) { |
141 | 148k | if (a_atype == TSK_FS_ATTR_NONRES) { |
142 | 0 | if (fs_attr_cur->nrd.run) |
143 | 0 | break; |
144 | 0 | else if (!fs_attr_ok) |
145 | 0 | fs_attr_ok = fs_attr_cur; |
146 | 0 | } |
147 | | /* we want one with an allocated buf */ |
148 | 148k | else { |
149 | 148k | if (fs_attr_cur->rd.buf_size) |
150 | 148k | break; |
151 | 0 | else if (!fs_attr_ok) |
152 | 0 | fs_attr_ok = fs_attr_cur; |
153 | 148k | } |
154 | 148k | } |
155 | 5.29M | } |
156 | | |
157 | | /* if we fell out then check fs_attr_tmp */ |
158 | 2.72M | if (!fs_attr_cur) { |
159 | 2.58M | if (fs_attr_ok) |
160 | 0 | fs_attr_cur = fs_attr_ok; |
161 | 2.58M | else { |
162 | | /* make a new one */ |
163 | 2.58M | if ((fs_attr_cur = tsk_fs_attr_alloc(a_atype)) == NULL) |
164 | 0 | return NULL; |
165 | | |
166 | | // add it to the list |
167 | 2.58M | if (tsk_fs_attrlist_add(a_fs_attrlist, fs_attr_cur)) { |
168 | 3.26k | tsk_fs_attr_free(fs_attr_cur); |
169 | 3.26k | return NULL; |
170 | 3.26k | } |
171 | 2.58M | } |
172 | 2.58M | } |
173 | | |
174 | 2.72M | fs_attr_cur->flags = (TSK_FS_ATTR_INUSE | a_atype); |
175 | 2.72M | return fs_attr_cur; |
176 | 2.72M | } |
177 | | |
178 | | |
179 | | /** \internal |
180 | | * Cycle through the attributes and mark them as unused. Does not free anything. |
181 | | * @param a_fs_attrlist Attribute list too mark. |
182 | | */ |
183 | | void |
184 | | tsk_fs_attrlist_markunused(TSK_FS_ATTRLIST * a_fs_attrlist) |
185 | 614k | { |
186 | 614k | TSK_FS_ATTR *fs_attr_cur; |
187 | 614k | if (a_fs_attrlist == NULL) |
188 | 0 | return; |
189 | | |
190 | 614k | fs_attr_cur = a_fs_attrlist->head; |
191 | 1.35M | while (fs_attr_cur) { |
192 | 744k | tsk_fs_attr_clear(fs_attr_cur); |
193 | 744k | fs_attr_cur = fs_attr_cur->next; |
194 | 744k | } |
195 | 614k | } |
196 | | |
197 | | /** |
198 | | * \internal |
199 | | * Search the attribute list of TSK_FS_ATTR structures for an entry with a given |
200 | | * type (no ID). If more than one entry with the same type exists, the one with |
201 | | * the lowest ID will be returned. |
202 | | * |
203 | | * @param a_fs_attrlist Data list structure to search in |
204 | | * @param a_type Type of attribute to find |
205 | | * |
206 | | * @return NULL is returned on error and if an entry could not be found. |
207 | | * tsk_errno will be set to TSK_ERR_FS_ATTR_NOTFOUND if entry could not be found. |
208 | | */ |
209 | | const TSK_FS_ATTR * |
210 | | tsk_fs_attrlist_get(const TSK_FS_ATTRLIST * a_fs_attrlist, |
211 | | TSK_FS_ATTR_TYPE_ENUM a_type) |
212 | 9.62M | { |
213 | 9.62M | TSK_FS_ATTR *fs_attr_cur; |
214 | 9.62M | TSK_FS_ATTR *fs_attr_ok = NULL; |
215 | | |
216 | 9.62M | if (!a_fs_attrlist) { |
217 | 1.09k | tsk_error_reset(); |
218 | 1.09k | tsk_error_set_errno(TSK_ERR_FS_ARG); |
219 | 1.09k | tsk_error_set_errstr("tsk_fs_attrlist_get: Null list pointer"); |
220 | 1.09k | return NULL; |
221 | 1.09k | } |
222 | | |
223 | 23.5M | for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; |
224 | 13.8M | fs_attr_cur = fs_attr_cur->next) { |
225 | 13.8M | if ((fs_attr_cur->flags & TSK_FS_ATTR_INUSE) |
226 | 13.8M | && (fs_attr_cur->type == a_type)) { |
227 | | |
228 | | /* If we are looking for NTFS $Data, |
229 | | * then return default when we see it */ |
230 | 9.61M | if ((fs_attr_cur->type == TSK_FS_ATTR_TYPE_NTFS_DATA) && |
231 | 2.20k | (fs_attr_cur->name == NULL)) { |
232 | 2.20k | return fs_attr_cur; |
233 | 2.20k | } |
234 | | |
235 | | // make sure we return the lowest if multiple exist |
236 | 9.61M | if ((fs_attr_ok == NULL) || (fs_attr_ok->id > fs_attr_cur->id)) |
237 | 9.61M | fs_attr_ok = fs_attr_cur; |
238 | 9.61M | } |
239 | 13.8M | } |
240 | | |
241 | 9.62M | if (!fs_attr_ok) { |
242 | 12.6k | tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND); |
243 | 12.6k | tsk_error_set_errstr("tsk_fs_attrlist_get: Attribute %d not found", |
244 | 12.6k | a_type); |
245 | 12.6k | return NULL; |
246 | 12.6k | } |
247 | 9.61M | else { |
248 | 9.61M | return fs_attr_ok; |
249 | 9.61M | } |
250 | 9.62M | } |
251 | | |
252 | | /** |
253 | | * \internal |
254 | | * Search the attribute list of TSK_FS_ATTR structures for an entry with a given |
255 | | * type and id. |
256 | | * |
257 | | * @param a_fs_attrlist Data list structure to search in |
258 | | * @param a_type Type of attribute to find |
259 | | * @param a_id Id of attribute to find. |
260 | | * |
261 | | * @return NULL is returned on error and if an entry could not be found. |
262 | | * tsk_errno will be set to TSK_ERR_FS_ATTR_NOTFOUND if entry could not be found. |
263 | | */ |
264 | | const TSK_FS_ATTR * |
265 | | tsk_fs_attrlist_get_id(const TSK_FS_ATTRLIST * a_fs_attrlist, |
266 | | TSK_FS_ATTR_TYPE_ENUM a_type, uint16_t a_id) |
267 | 606k | { |
268 | 606k | TSK_FS_ATTR *fs_attr_cur; |
269 | | |
270 | 606k | if (!a_fs_attrlist) { |
271 | 0 | tsk_error_reset(); |
272 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
273 | 0 | tsk_error_set_errstr("tsk_fs_attrlist_get_id: Null list pointer"); |
274 | 0 | return NULL; |
275 | 0 | } |
276 | | |
277 | 2.38M | for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; |
278 | 1.81M | fs_attr_cur = fs_attr_cur->next) { |
279 | 1.81M | if ((fs_attr_cur->flags & TSK_FS_ATTR_INUSE) |
280 | 1.75M | && (fs_attr_cur->type == a_type) && (fs_attr_cur->id == a_id)) |
281 | 35.2k | return fs_attr_cur; |
282 | 1.81M | } |
283 | | |
284 | 571k | tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND); |
285 | 571k | tsk_error_set_errstr |
286 | 571k | ("tsk_fs_attrlist_get_id: Attribute %d-%d not found", a_type, |
287 | 571k | a_id); |
288 | 571k | return NULL; |
289 | 606k | } |
290 | | |
291 | | |
292 | | /** |
293 | | * \internal |
294 | | * Search the attribute list of TSK_FS_ATTR structures for an entry with a |
295 | | given |
296 | | * type (no ID) and a given name. If more than one entry with the same |
297 | | type exists, |
298 | | * the one with the lowest ID will be returned. |
299 | | * |
300 | | * @param a_fs_attrlist Data list structure to search in |
301 | | * @param a_type Type of attribute to find |
302 | | * @param name Name of the attribute to find (NULL for an entry with no name) |
303 | | * |
304 | | * @return NULL is returned on error and if an entry could not be found. |
305 | | * tsk_errno will be set to TSK_ERR_FS_ATTR_NOTFOUND if entry could not be |
306 | | found. |
307 | | */ |
308 | | const TSK_FS_ATTR * |
309 | | tsk_fs_attrlist_get_name_type(const TSK_FS_ATTRLIST * a_fs_attrlist, |
310 | | TSK_FS_ATTR_TYPE_ENUM a_type, const char *name) |
311 | 179 | { |
312 | 179 | TSK_FS_ATTR *fs_attr_cur; |
313 | 179 | TSK_FS_ATTR *fs_attr_ok = NULL; |
314 | | |
315 | 179 | if (!a_fs_attrlist) { |
316 | 6 | tsk_error_reset(); |
317 | 6 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
318 | 6 | tsk_error_set_errstr |
319 | 6 | ("tsk_fs_attrlist_get_name_type: Null list pointer"); |
320 | 6 | return NULL; |
321 | 6 | } |
322 | | |
323 | 764 | for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; fs_attr_cur = |
324 | 591 | fs_attr_cur->next) { |
325 | 591 | if ((fs_attr_cur->flags & TSK_FS_ATTR_INUSE) && |
326 | 591 | (fs_attr_cur->type == a_type)) { |
327 | | |
328 | 45 | if (((name == NULL) && (fs_attr_cur->name == NULL)) || |
329 | 45 | ((name) && (fs_attr_cur->name) |
330 | 32 | && (!strcmp(fs_attr_cur->name, name)))) { |
331 | | |
332 | | /* If we are looking for NTFS $Data, |
333 | | * then return default when we see it */ |
334 | 4 | if ((fs_attr_cur->type == TSK_FS_ATTR_TYPE_NTFS_DATA) && |
335 | 0 | (fs_attr_cur->name == NULL)) { |
336 | 0 | return fs_attr_cur; |
337 | 0 | } |
338 | | |
339 | | // make sure we return the lowest if multiple exist |
340 | 4 | if ((fs_attr_ok == NULL) |
341 | 0 | || (fs_attr_ok->id > fs_attr_cur->id)) |
342 | 4 | fs_attr_ok = fs_attr_cur; |
343 | 4 | } |
344 | | |
345 | 41 | else if ((name) && (fs_attr_cur->name) && fs_attr_cur->type == TSK_FS_ATTR_TYPE_NTFS_DATA && !strcasecmp(fs_attr_cur->name, name)) { |
346 | | // NTFS data streams should do case insensitive compare |
347 | | // make sure we return the lowest if multiple exist |
348 | 0 | if ((fs_attr_ok == NULL) || (fs_attr_ok->id > fs_attr_cur->id)) |
349 | 0 | fs_attr_ok = fs_attr_cur; |
350 | 0 | } |
351 | 45 | } |
352 | 591 | } |
353 | | |
354 | 173 | if (!fs_attr_ok) { |
355 | 169 | tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND); |
356 | 169 | tsk_error_set_errstr("tsk_fs_attrlist_get: Attribute %d not found", |
357 | 169 | a_type); |
358 | 169 | return NULL; |
359 | 169 | } |
360 | 4 | else { |
361 | 4 | return fs_attr_ok; |
362 | 4 | } |
363 | 173 | } |
364 | | |
365 | | |
366 | | /** |
367 | | * \internal |
368 | | * Return the a_idx'th attribute in the attribute list. |
369 | | * |
370 | | * @param a_fs_attrlist Data list structure to search in |
371 | | * @param a_idx 0-based index of attribute to return |
372 | | * |
373 | | * @return NULL is returned on error and if an entry could not be found. |
374 | | * tsk_errno will be set to TSK_ERR_FS_ATTR_NOTFOUND if entry could not be found. |
375 | | */ |
376 | | const TSK_FS_ATTR * |
377 | | tsk_fs_attrlist_get_idx(const TSK_FS_ATTRLIST * a_fs_attrlist, int a_idx) |
378 | 94.3k | { |
379 | 94.3k | TSK_FS_ATTR *fs_attr_cur; |
380 | 94.3k | int i = 0; |
381 | | |
382 | 94.3k | if (!a_fs_attrlist) { |
383 | 0 | tsk_error_reset(); |
384 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
385 | 0 | tsk_error_set_errstr("tsk_fs_attrlist_get_idx: Null list pointer"); |
386 | 0 | return NULL; |
387 | 0 | } |
388 | | |
389 | 185k | for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; |
390 | 185k | fs_attr_cur = fs_attr_cur->next) { |
391 | 185k | if (fs_attr_cur->flags & TSK_FS_ATTR_INUSE) { |
392 | 185k | if (i == a_idx) { |
393 | 94.3k | return fs_attr_cur; |
394 | 94.3k | } |
395 | 91.2k | i++; |
396 | 91.2k | } |
397 | 185k | } |
398 | | |
399 | 0 | tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND); |
400 | 0 | tsk_error_set_errstr |
401 | 0 | ("tsk_fs_attrlist_get_idx: Attribute index %d not found", a_idx); |
402 | 0 | return NULL; |
403 | 94.3k | } |
404 | | |
405 | | |
406 | | /** |
407 | | * \internal |
408 | | * Return the number of attributes in the attribute list |
409 | | * |
410 | | * @param a_fs_attrlist Data list structure to analyze |
411 | | * |
412 | | * @return the number of attributes and 0 if error (if argument is NULL) |
413 | | */ |
414 | | int |
415 | | tsk_fs_attrlist_get_len(const TSK_FS_ATTRLIST * a_fs_attrlist) |
416 | 36.9k | { |
417 | 36.9k | TSK_FS_ATTR *fs_attr_cur; |
418 | 36.9k | int len = 0; |
419 | | |
420 | 36.9k | if (!a_fs_attrlist) { |
421 | 0 | tsk_error_reset(); |
422 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
423 | 0 | tsk_error_set_errstr("tsk_fs_attrlist_get_len: Null list pointer"); |
424 | 0 | return 0; |
425 | 0 | } |
426 | | |
427 | 139k | for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; |
428 | 102k | fs_attr_cur = fs_attr_cur->next) { |
429 | 102k | if (fs_attr_cur->flags & TSK_FS_ATTR_INUSE) |
430 | 98.5k | len++; |
431 | 102k | } |
432 | 36.9k | return len; |
433 | 36.9k | } |