/src/sleuthkit/tsk/vs/mm_part.c
Line | Count | Source |
1 | | /* |
2 | | * The Sleuth Kit |
3 | | * |
4 | | * Brian Carrier [carrier <at> sleuthkit [dot] org] |
5 | | * Copyright (c) 2003-2011 Brian Carrier. All rights reserved |
6 | | * |
7 | | * This software is distributed under the Common Public License 1.0 |
8 | | */ |
9 | | |
10 | | /** \file mm_part.c |
11 | | * Contains the functions need to create, maintain, and access the linked list of |
12 | | * partitions in a volume. |
13 | | */ |
14 | | #include "tsk_vs_i.h" |
15 | | |
16 | | |
17 | | /** |
18 | | * Add a partition to a sorted list |
19 | | * @param a_vs Volume system that partition belongs to |
20 | | * @param a_start Starting sector address of volume (relative to start of volume) |
21 | | * @param len Length of volume in sectors |
22 | | * @param type Type of volume |
23 | | * @param desc Text description of partition. Note that this is not copied |
24 | | * and must not be freed until the volume system has been closed. |
25 | | * @param table The table ID that the volume was located in or -1 for volumes not in a partition table. |
26 | | * @param slot The slot number in the partition table that the volume was located in or -1 for volumes not in a partition table. |
27 | | * @returns Pointer to structure that was created for the partition or NULL on error. |
28 | | */ |
29 | | TSK_VS_PART_INFO * |
30 | | tsk_vs_part_add(TSK_VS_INFO * a_vs, TSK_DADDR_T a_start, TSK_DADDR_T len, |
31 | | TSK_VS_PART_FLAG_ENUM type, char *desc, int8_t table, int16_t slot) |
32 | 1.44M | { |
33 | 1.44M | TSK_VS_PART_INFO *part; |
34 | 1.44M | TSK_VS_PART_INFO *cur_part; |
35 | | |
36 | 1.44M | if ((part = |
37 | 1.44M | (TSK_VS_PART_INFO *) tsk_malloc(sizeof(TSK_VS_PART_INFO))) == |
38 | 1.44M | NULL) { |
39 | 0 | return NULL; |
40 | 0 | } |
41 | | |
42 | | /* set the values */ |
43 | 1.44M | part->next = NULL; |
44 | 1.44M | part->prev = NULL; |
45 | 1.44M | part->start = a_start; |
46 | 1.44M | part->len = len; |
47 | 1.44M | part->desc = desc; |
48 | 1.44M | part->table_num = table; |
49 | 1.44M | part->slot_num = slot; |
50 | 1.44M | part->flags = type; |
51 | 1.44M | part->vs = a_vs; |
52 | 1.44M | part->addr = 0; |
53 | 1.44M | part->tag = TSK_VS_PART_INFO_TAG; |
54 | | |
55 | | /* is this the first entry in the list */ |
56 | 1.44M | if (a_vs->part_list == NULL) { |
57 | 28.2k | a_vs->part_list = part; |
58 | 28.2k | a_vs->part_count = 1; |
59 | 28.2k | return part; |
60 | 28.2k | } |
61 | | |
62 | | /* Cycle through to find the correct place to put it into */ |
63 | 710M | for (cur_part = a_vs->part_list; cur_part != NULL; |
64 | 710M | cur_part = cur_part->next) { |
65 | | /* The one to add starts before this partition */ |
66 | 710M | if (cur_part->start > part->start) { |
67 | 3.02k | part->next = cur_part; |
68 | 3.02k | part->prev = cur_part->prev; |
69 | 3.02k | if (part->prev) |
70 | 0 | part->prev->next = part; |
71 | 3.02k | cur_part->prev = part; |
72 | | |
73 | | /* If we are now the head update a_vs */ |
74 | 3.02k | if (part->prev == NULL) |
75 | 3.02k | a_vs->part_list = part; |
76 | | |
77 | | /* update the count and address numbers */ |
78 | 3.02k | a_vs->part_count++; |
79 | 3.02k | part->addr = cur_part->addr; |
80 | 19.7k | for (; cur_part != NULL; cur_part = cur_part->next) |
81 | 16.7k | cur_part->addr++; |
82 | | |
83 | 3.02k | return part; |
84 | 3.02k | } |
85 | | |
86 | | /* the one to add is bigger then current and the list is done */ |
87 | 710M | else if (cur_part->next == NULL) { |
88 | 144k | cur_part->next = part; |
89 | 144k | part->prev = cur_part; |
90 | | |
91 | | /* Update partition counts and addresses */ |
92 | 144k | a_vs->part_count++; |
93 | 144k | part->addr = cur_part->addr + 1; |
94 | 144k | return part; |
95 | 144k | } |
96 | | |
97 | | /* The one to add fits in between this and the next */ |
98 | 710M | else if (cur_part->next->start > part->start) { |
99 | 1.26M | part->prev = cur_part; |
100 | 1.26M | part->next = cur_part->next; |
101 | 1.26M | cur_part->next->prev = part; |
102 | 1.26M | cur_part->next = part; |
103 | | |
104 | | /* Update partition counts and addresses */ |
105 | 1.26M | a_vs->part_count++; |
106 | 1.26M | part->addr = cur_part->addr + 1; |
107 | 680M | for (cur_part = part->next; cur_part != NULL; |
108 | 679M | cur_part = cur_part->next) |
109 | 679M | cur_part->addr++; |
110 | 1.26M | return part; |
111 | 1.26M | } |
112 | 710M | } |
113 | 0 | return NULL; |
114 | 1.41M | } |
115 | | |
116 | | /** |
117 | | * Identify regions in the partition list where there are unused sectors |
118 | | * and create new entries for them. |
119 | | * |
120 | | * @param a_vs Pointer to open volume system |
121 | | * @returns 1 on error and 0 on success |
122 | | */ |
123 | | uint8_t |
124 | | tsk_vs_part_unused(TSK_VS_INFO * a_vs) |
125 | 2.84k | { |
126 | 2.84k | TSK_VS_PART_INFO *part = a_vs->part_list; |
127 | 2.84k | TSK_DADDR_T prev_end = 0; |
128 | | |
129 | | /* prev_ent is set to where the previous entry stopped plus 1 */ |
130 | 78.5k | for (part = a_vs->part_list; part != NULL; part = part->next) { |
131 | | |
132 | | // ignore the META volume |
133 | 75.6k | if (part->flags & TSK_VS_PART_FLAG_META) |
134 | 12.9k | continue; |
135 | | |
136 | | // there is space before current and previous volume |
137 | 62.7k | if (part->start > prev_end) { |
138 | 25.3k | char *str; |
139 | 25.3k | if ((str = tsk_malloc(12)) == NULL) |
140 | 0 | return 1; |
141 | | |
142 | 25.3k | snprintf(str, 12, "Unallocated"); |
143 | 25.3k | if (NULL == tsk_vs_part_add(a_vs, prev_end, |
144 | 25.3k | part->start - prev_end, TSK_VS_PART_FLAG_UNALLOC, str, |
145 | 25.3k | -1, -1)) { |
146 | 0 | free(str); |
147 | 0 | return 1; |
148 | 0 | } |
149 | 25.3k | } |
150 | | |
151 | 62.7k | prev_end = part->start + part->len; |
152 | 62.7k | } |
153 | | |
154 | | /* Is there unallocated space at the end? */ |
155 | 2.84k | if (prev_end < (TSK_DADDR_T) (a_vs->img_info->size / a_vs->block_size)) { |
156 | 217 | char *str; |
157 | 217 | if ((str = tsk_malloc(12)) == NULL) |
158 | 0 | return 1; |
159 | | |
160 | 217 | snprintf(str, 12, "Unallocated"); |
161 | 217 | if (NULL == tsk_vs_part_add(a_vs, prev_end, |
162 | 217 | a_vs->img_info->size / a_vs->block_size - prev_end, |
163 | 217 | TSK_VS_PART_FLAG_UNALLOC, str, -1, -1)) { |
164 | 0 | free(str); |
165 | 0 | return 1; |
166 | 0 | } |
167 | 217 | } |
168 | | |
169 | 2.84k | return 0; |
170 | 2.84k | } |
171 | | |
172 | | /* |
173 | | * free the buffer with the description |
174 | | */ |
175 | | void |
176 | | tsk_vs_part_free(TSK_VS_INFO * a_vs) |
177 | 142k | { |
178 | 142k | TSK_VS_PART_INFO *part = a_vs->part_list; |
179 | 142k | TSK_VS_PART_INFO *part2; |
180 | | |
181 | 1.58M | while (part) { |
182 | 1.44M | free(part->desc); |
183 | 1.44M | part->tag = 0; |
184 | 1.44M | part2 = part->next; |
185 | 1.44M | free(part); |
186 | 1.44M | part = part2; |
187 | 1.44M | } |
188 | 142k | a_vs->part_list = NULL; |
189 | 142k | } |
190 | | |
191 | | /** |
192 | | * \ingroup vslib |
193 | | * Return handle to a volume in the volume system. |
194 | | * |
195 | | * @param a_vs Open volume system |
196 | | * @param a_idx Index for volume to return (0-based) |
197 | | * @returns Handle to volume or NULL on error |
198 | | */ |
199 | | const TSK_VS_PART_INFO * |
200 | | tsk_vs_part_get(const TSK_VS_INFO * a_vs, TSK_PNUM_T a_idx) |
201 | 0 | { |
202 | 0 | TSK_VS_PART_INFO *part; |
203 | |
|
204 | 0 | if ((a_vs == NULL) || (a_vs->tag != TSK_VS_INFO_TAG)) { |
205 | 0 | tsk_error_reset(); |
206 | 0 | tsk_error_set_errno(TSK_ERR_VS_ARG); |
207 | 0 | tsk_error_set_errstr |
208 | 0 | ("tsk_vs_part_get: pointer is NULL or has unallocated structures"); |
209 | 0 | return NULL; |
210 | 0 | } |
211 | | |
212 | 0 | if (a_idx >= a_vs->part_count) { |
213 | 0 | tsk_error_reset(); |
214 | 0 | tsk_error_set_errno(TSK_ERR_VS_ARG); |
215 | 0 | tsk_error_set_errstr("tsk_vs_part_get: Volume address is too big"); |
216 | 0 | return NULL; |
217 | 0 | } |
218 | | |
219 | 0 | for (part = a_vs->part_list; part != NULL; part = part->next) { |
220 | 0 | if (part->addr == a_idx) |
221 | 0 | return part; |
222 | 0 | } |
223 | | |
224 | 0 | return NULL; |
225 | 0 | } |
226 | | |
227 | | |
228 | | /** |
229 | | * \ingroup vslib |
230 | | * Walk a range of partitions and pass the data to a callback function. |
231 | | * |
232 | | * @param a_vs Pointer to open volume system |
233 | | * @param a_start Address of first partition to walk from. |
234 | | * @param a_last Address of last partition to walk to. |
235 | | * @param a_flags Flags that are used to identify which of the partitions in the range should be returned (if 0, all partitions will be returned). |
236 | | * @param a_action Callback action to call for each partition. |
237 | | * @param a_ptr Pointer to data that will be passed to callback. |
238 | | * @returns 1 on error and 0 on success |
239 | | */ |
240 | | uint8_t |
241 | | tsk_vs_part_walk(TSK_VS_INFO * a_vs, TSK_PNUM_T a_start, TSK_PNUM_T a_last, |
242 | | TSK_VS_PART_FLAG_ENUM a_flags, TSK_VS_PART_WALK_CB a_action, |
243 | | void *a_ptr) |
244 | 2.84k | { |
245 | 2.84k | TSK_VS_PART_INFO *part; |
246 | | |
247 | 2.84k | if (a_start >= a_vs->part_count) { |
248 | 0 | tsk_error_reset(); |
249 | 0 | tsk_error_set_errno(TSK_ERR_VS_WALK_RNG); |
250 | 0 | tsk_error_set_errstr |
251 | 0 | ("tsk_vs_part_walk: Start partition too large: %" PRIuPNUM "", |
252 | 0 | a_start); |
253 | 0 | return 1; |
254 | 0 | } |
255 | | |
256 | 2.84k | if (a_last >= a_vs->part_count) { |
257 | 0 | tsk_error_reset(); |
258 | 0 | tsk_error_set_errno(TSK_ERR_VS_WALK_RNG); |
259 | 0 | tsk_error_set_errstr("tsk_vs_part_walk: End partition too large: %" |
260 | 0 | PRIuPNUM "", a_last); |
261 | 0 | return 1; |
262 | 0 | } |
263 | | |
264 | 2.84k | if (a_flags == 0) { |
265 | 0 | a_flags |= |
266 | 0 | (TSK_VS_PART_FLAG_ALLOC | TSK_VS_PART_FLAG_UNALLOC | |
267 | 0 | TSK_VS_PART_FLAG_META); |
268 | 0 | } |
269 | | |
270 | 101k | for (part = a_vs->part_list; part != NULL; part = part->next) { |
271 | 101k | if ((part->addr >= a_start) && ((part->flags & a_flags) != 0)) { |
272 | 101k | switch (a_action(a_vs, part, a_ptr)) { |
273 | 101k | case TSK_WALK_CONT: break; |
274 | 0 | case TSK_WALK_STOP: return 0; |
275 | 0 | case TSK_WALK_ERROR: return 1; |
276 | 101k | } |
277 | 101k | } |
278 | | |
279 | 101k | if (part->addr >= a_last) |
280 | 2.84k | break; |
281 | 101k | } |
282 | 2.84k | return 0; |
283 | 2.84k | } |