/src/sleuthkit/tsk/img/raw.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Brian Carrier [carrier <at> sleuthkit [dot] org] |
3 | | * Copyright (c) 2006-2011 Brian Carrier, Basis Technology. All rights reserved |
4 | | * Copyright (c) 2005 Brian Carrier. All rights reserved |
5 | | * |
6 | | * raw |
7 | | * |
8 | | * This software is distributed under the Common Public License 1.0 |
9 | | * |
10 | | */ |
11 | | |
12 | | |
13 | | /** |
14 | | * \file raw.c |
15 | | * Internal code to open and read single or split raw disk images |
16 | | */ |
17 | | |
18 | | #include "tsk_img_i.h" |
19 | | #include "raw.h" |
20 | | #include "tsk/util/file_system_utils.h" |
21 | | |
22 | | #include <memory> |
23 | | |
24 | | #ifdef __APPLE__ |
25 | | #include <sys/disk.h> |
26 | | #endif |
27 | | |
28 | | #ifdef TSK_WIN32 |
29 | | #include <winioctl.h> |
30 | | #else |
31 | | #include <sys/types.h> |
32 | | #include <sys/stat.h> |
33 | | #include <unistd.h> |
34 | | #include <fcntl.h> |
35 | | #endif |
36 | | |
37 | | #ifndef S_IFMT |
38 | | #define S_IFMT __S_IFMT |
39 | | #endif |
40 | | |
41 | | #ifndef S_IFDIR |
42 | | #define S_IFDIR __S_IFDIR |
43 | | #endif |
44 | | |
45 | | /** |
46 | | * \internal |
47 | | * Read from one of the multiple files in a split set of disk images. |
48 | | * |
49 | | * @param split_info Disk image info to read from |
50 | | * @param idx Index of the disk image in the set to read from |
51 | | * @param buf [out] Buffer to write data to |
52 | | * @param len Number of bytes to read |
53 | | * @param rel_offset Byte offset in the disk image to read from (not the offset in the full disk image set) |
54 | | * |
55 | | * @return -1 on error or number of bytes read |
56 | | */ |
57 | | static ssize_t |
58 | | raw_read_segment(IMG_RAW_INFO * raw_info, int idx, char *buf, |
59 | | size_t len, TSK_OFF_T rel_offset) |
60 | 0 | { |
61 | 0 | TSK_IMG_INFO* img_info = &raw_info->img_info.img_info; |
62 | |
|
63 | 0 | IMG_SPLIT_CACHE *cimg; |
64 | 0 | ssize_t cnt; |
65 | | |
66 | | /* Is the image already open? */ |
67 | 0 | if (raw_info->cptr[idx] == -1) { |
68 | 0 | if (tsk_verbose) { |
69 | 0 | tsk_fprintf(stderr, |
70 | 0 | "raw_read_segment: opening file into slot %d: %" PRIttocTSK |
71 | 0 | "\n", raw_info->next_slot, img_info->images[idx]); |
72 | 0 | } |
73 | | |
74 | | /* Grab the next cache slot */ |
75 | 0 | cimg = &raw_info->cache[raw_info->next_slot]; |
76 | | |
77 | | /* Free it if being used */ |
78 | 0 | if (cimg->fd != 0) { |
79 | 0 | if (tsk_verbose) { |
80 | 0 | tsk_fprintf(stderr, |
81 | 0 | "raw_read_segment: closing file %" PRIttocTSK "\n", |
82 | 0 | img_info->images[cimg->image]); |
83 | 0 | } |
84 | | #ifdef TSK_WIN32 |
85 | | CloseHandle(cimg->fd); |
86 | | #else |
87 | 0 | close(cimg->fd); |
88 | 0 | #endif |
89 | 0 | raw_info->cptr[cimg->image] = -1; |
90 | 0 | } |
91 | |
|
92 | | #ifdef TSK_WIN32 |
93 | | cimg->fd = CreateFile(img_info->images[idx], FILE_READ_DATA, |
94 | | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, |
95 | | NULL); |
96 | | if ( cimg->fd == INVALID_HANDLE_VALUE ) { |
97 | | int lastError = (int)GetLastError(); |
98 | | cimg->fd = 0; /* so we don't close it next time */ |
99 | | tsk_error_reset(); |
100 | | tsk_error_set_errno(TSK_ERR_IMG_OPEN); |
101 | | tsk_error_set_errstr("raw_read: file \"%" PRIttocTSK |
102 | | "\" - %d", img_info->images[idx], lastError); |
103 | | return -1; |
104 | | } |
105 | | |
106 | | #else |
107 | 0 | if ((cimg->fd = |
108 | 0 | open(img_info->images[idx], O_RDONLY | O_BINARY)) < 0) { |
109 | 0 | cimg->fd = 0; /* so we don't close it next time */ |
110 | 0 | tsk_error_reset(); |
111 | 0 | tsk_error_set_errno(TSK_ERR_IMG_OPEN); |
112 | 0 | tsk_error_set_errstr("raw_read: file \"%" PRIttocTSK |
113 | 0 | "\" - %s", img_info->images[idx], strerror(errno)); |
114 | 0 | return -1; |
115 | 0 | } |
116 | 0 | #endif |
117 | 0 | cimg->image = idx; |
118 | 0 | cimg->seek_pos = 0; |
119 | 0 | raw_info->cptr[idx] = raw_info->next_slot; |
120 | 0 | if (++raw_info->next_slot == SPLIT_CACHE) { |
121 | 0 | raw_info->next_slot = 0; |
122 | 0 | } |
123 | 0 | } |
124 | 0 | else { |
125 | | /* image already open */ |
126 | 0 | cimg = &raw_info->cache[raw_info->cptr[idx]]; |
127 | 0 | } |
128 | | |
129 | | #ifdef TSK_WIN32 |
130 | | { |
131 | | // Default to the values that were passed in |
132 | | TSK_OFF_T offset_to_read = rel_offset; |
133 | | size_t len_to_read = len; |
134 | | char ** buf_pointer = &buf; |
135 | | char * sector_aligned_buf = NULL; |
136 | | |
137 | | // If the offset to seek to isn't sector-aligned and this is a device, we need to start at the previous sector boundary and |
138 | | // read some extra data. |
139 | | if ((offset_to_read % img_info->sector_size != 0) |
140 | | && is_windows_device_path(img_info->images[idx])) { |
141 | | offset_to_read = (offset_to_read / img_info->sector_size) * img_info->sector_size; |
142 | | len_to_read += img_info->sector_size; // this length will already be a multiple of sector size |
143 | | sector_aligned_buf = (char *)tsk_malloc(len_to_read); |
144 | | if (sector_aligned_buf == NULL) { |
145 | | tsk_error_reset(); |
146 | | tsk_error_set_errno(TSK_ERR_IMG_READ); |
147 | | tsk_error_set_errstr("raw_read: error allocating memory to read file \"%" PRIttocTSK |
148 | | "\" offset: %" PRIdOFF " read len: %" PRIuSIZE, |
149 | | img_info->images[idx], offset_to_read, len_to_read); |
150 | | return -1; |
151 | | } |
152 | | buf_pointer = §or_aligned_buf; |
153 | | } |
154 | | |
155 | | DWORD nread; |
156 | | if (cimg->seek_pos != offset_to_read) { |
157 | | LARGE_INTEGER li; |
158 | | li.QuadPart = offset_to_read; |
159 | | |
160 | | li.LowPart = SetFilePointer(cimg->fd, li.LowPart, |
161 | | &li.HighPart, FILE_BEGIN); |
162 | | |
163 | | if ((li.LowPart == INVALID_SET_FILE_POINTER) && |
164 | | (GetLastError() != NO_ERROR)) { |
165 | | if (sector_aligned_buf != NULL) { |
166 | | free(sector_aligned_buf); |
167 | | } |
168 | | int lastError = (int)GetLastError(); |
169 | | tsk_error_reset(); |
170 | | tsk_error_set_errno(TSK_ERR_IMG_SEEK); |
171 | | tsk_error_set_errstr("raw_read: file \"%" PRIttocTSK |
172 | | "\" offset %" PRIdOFF " seek - %d", |
173 | | img_info->images[idx], offset_to_read, |
174 | | lastError); |
175 | | return -1; |
176 | | } |
177 | | cimg->seek_pos = offset_to_read; |
178 | | } |
179 | | |
180 | | //For physical drive when the buffer is larger than remaining data, |
181 | | // WinAPI ReadFile call returns -1 |
182 | | //in this case buffer of exact length must be passed to ReadFile |
183 | | if ((raw_info->is_winobj) && (offset_to_read + (TSK_OFF_T)len_to_read > img_info->size )) |
184 | | len_to_read = (size_t)(img_info->size - offset_to_read); |
185 | | |
186 | | if (FALSE == ReadFile(cimg->fd, *buf_pointer, (DWORD)len_to_read, &nread, NULL)) { |
187 | | if (sector_aligned_buf != NULL) { |
188 | | free(sector_aligned_buf); |
189 | | } |
190 | | int lastError = GetLastError(); |
191 | | tsk_error_reset(); |
192 | | tsk_error_set_errno(TSK_ERR_IMG_READ); |
193 | | tsk_error_set_errstr("raw_read: file \"%" PRIttocTSK |
194 | | "\" offset: %" PRIdOFF " read len: %" PRIuSIZE " - %d", |
195 | | img_info->images[idx], offset_to_read, len_to_read, |
196 | | lastError); |
197 | | return -1; |
198 | | } |
199 | | // When the read operation reaches the end of a file, |
200 | | // ReadFile returns TRUE and sets nread to zero. |
201 | | // We need to check if we've reached the end of a file and set nread to |
202 | | // the number of bytes read. |
203 | | if (raw_info->is_winobj && nread == 0 && offset_to_read + len_to_read == (size_t) img_info->size) { |
204 | | nread = (DWORD)len_to_read; |
205 | | } |
206 | | cnt = (ssize_t) nread; |
207 | | |
208 | | if (raw_info->img_writer != NULL) { |
209 | | /* img_writer is not used with split images, so rel_offset is just the normal offset*/ |
210 | | raw_info->img_writer->add(raw_info->img_writer, offset_to_read, *buf_pointer, cnt); |
211 | | // If WriteFile returns error in the addNewBlock, hadErrorExtending is 1 |
212 | | if (raw_info->img_writer->inFinalizeImageWriter && raw_info->img_writer->hadErrorExtending) { |
213 | | if (sector_aligned_buf != NULL) { |
214 | | free(sector_aligned_buf); |
215 | | } |
216 | | tsk_error_reset(); |
217 | | tsk_error_set_errno(TSK_ERR_IMG_WRITE); |
218 | | tsk_error_set_errstr("raw_read: file \"%" PRIttocTSK |
219 | | "\" offset: %" PRIdOFF " tsk_img_writer_add cnt: %" PRIuSIZE, |
220 | | img_info->images[idx], offset_to_read, cnt |
221 | | ); |
222 | | return -1; |
223 | | } |
224 | | } |
225 | | |
226 | | // Update this with the actual bytes read |
227 | | cimg->seek_pos += cnt; |
228 | | |
229 | | // If we had to do the sector alignment, copy the result into the original buffer and fix |
230 | | // the number of bytes read |
231 | | if (sector_aligned_buf != NULL) { |
232 | | memcpy(buf, sector_aligned_buf + rel_offset % img_info->sector_size, len); |
233 | | cnt = cnt - rel_offset % img_info->sector_size; |
234 | | if (cnt < 0) { |
235 | | cnt = -1; |
236 | | } |
237 | | free(sector_aligned_buf); |
238 | | } |
239 | | } |
240 | | #else |
241 | 0 | if (cimg->seek_pos != rel_offset) { |
242 | 0 | if (lseek(cimg->fd, rel_offset, SEEK_SET) != rel_offset) { |
243 | 0 | tsk_error_reset(); |
244 | 0 | tsk_error_set_errno(TSK_ERR_IMG_SEEK); |
245 | 0 | tsk_error_set_errstr("raw_read: file \"%" PRIttocTSK |
246 | 0 | "\" offset %" PRIdOFF " seek - %s", img_info->images[idx], |
247 | 0 | rel_offset, strerror(errno)); |
248 | 0 | return -1; |
249 | 0 | } |
250 | 0 | cimg->seek_pos = rel_offset; |
251 | 0 | } |
252 | | |
253 | 0 | cnt = read(cimg->fd, buf, len); |
254 | 0 | if (cnt < 0) { |
255 | 0 | tsk_error_reset(); |
256 | 0 | tsk_error_set_errno(TSK_ERR_IMG_READ); |
257 | 0 | tsk_error_set_errstr("raw_read: file \"%" PRIttocTSK "\" offset: %" |
258 | 0 | PRIdOFF " read len: %" PRIuSIZE " - %s", img_info->images[idx], |
259 | 0 | rel_offset, len, strerror(errno)); |
260 | 0 | return -1; |
261 | 0 | } |
262 | 0 | cimg->seek_pos += cnt; |
263 | 0 | #endif |
264 | |
|
265 | 0 | return cnt; |
266 | 0 | } |
267 | | |
268 | | |
269 | | /** |
270 | | * \internal |
271 | | * Read data from a (potentially split) raw disk image. The offset to |
272 | | * start reading from is equal to the volume offset plus the read offset. |
273 | | * |
274 | | * Note: The routine -assumes- we are under a lock on &(img_info->cache_lock)) |
275 | | * |
276 | | * @param img_info Disk image to read from |
277 | | * @param offset Byte offset in image to start reading from |
278 | | * @param buf [out] Buffer to write data to |
279 | | * @param len Number of bytes to read |
280 | | * |
281 | | * @return number of bytes read or -1 on error |
282 | | */ |
283 | | static ssize_t |
284 | | raw_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len) |
285 | 0 | { |
286 | 0 | IMG_RAW_INFO *raw_info = (IMG_RAW_INFO *) img_info; |
287 | 0 | int i; |
288 | |
|
289 | 0 | if (tsk_verbose) { |
290 | 0 | tsk_fprintf(stderr, |
291 | 0 | "raw_read: byte offset: %" PRIdOFF " len: %" PRIuSIZE "\n", |
292 | 0 | offset, len); |
293 | 0 | } |
294 | |
|
295 | 0 | if (offset > img_info->size) { |
296 | 0 | tsk_error_reset(); |
297 | 0 | tsk_error_set_errno(TSK_ERR_IMG_READ_OFF); |
298 | 0 | tsk_error_set_errstr("raw_read: offset %" PRIdOFF " too large", |
299 | 0 | offset); |
300 | 0 | return -1; |
301 | 0 | } |
302 | | |
303 | 0 | tsk_take_lock(&raw_info->read_lock); |
304 | 0 | std::unique_ptr<tsk_lock_t, void(*)(tsk_lock_t*)> lock_guard( |
305 | 0 | &raw_info->read_lock, tsk_release_lock |
306 | 0 | ); |
307 | | |
308 | | // Find the location of the offset |
309 | 0 | for (i = 0; i < img_info->num_img; i++) { |
310 | | |
311 | | /* Does the data start in this image? */ |
312 | 0 | if (offset < raw_info->max_off[i]) { |
313 | 0 | TSK_OFF_T rel_offset; |
314 | 0 | size_t read_len; |
315 | 0 | ssize_t cnt; |
316 | | |
317 | | /* Get the offset relative to this image segment */ |
318 | 0 | if (i > 0) { |
319 | 0 | rel_offset = offset - raw_info->max_off[i - 1]; |
320 | 0 | } |
321 | 0 | else { |
322 | 0 | rel_offset = offset; |
323 | 0 | } |
324 | | |
325 | | /* Get the length to read */ |
326 | | // NOTE: max_off - offset can be a very large number. Do not cast to size_t |
327 | 0 | if (raw_info->max_off[i] - offset >= (TSK_OFF_T)len) |
328 | 0 | read_len = len; |
329 | 0 | else |
330 | 0 | read_len = (size_t) (raw_info->max_off[i] - offset); |
331 | | |
332 | |
|
333 | 0 | if (tsk_verbose) { |
334 | 0 | tsk_fprintf(stderr, |
335 | 0 | "raw_read: found in image %d relative offset: %" |
336 | 0 | PRIdOFF " len: %" PRIdOFF "\n", i, rel_offset, |
337 | 0 | (TSK_OFF_T) read_len); |
338 | 0 | } |
339 | |
|
340 | 0 | cnt = raw_read_segment(raw_info, i, buf, read_len, rel_offset); |
341 | 0 | if (cnt < 0) { |
342 | 0 | return -1; |
343 | 0 | } |
344 | 0 | if ((size_t) cnt != read_len) { |
345 | 0 | return cnt; |
346 | 0 | } |
347 | | |
348 | | /* read from the next image segment(s) if needed */ |
349 | 0 | if (((size_t) cnt == read_len) && (read_len != len)) { |
350 | |
|
351 | 0 | len -= read_len; |
352 | | |
353 | | /* go to the next image segment */ |
354 | 0 | while ((len > 0) && (i+1 < img_info->num_img)) { |
355 | 0 | ssize_t cnt2; |
356 | |
|
357 | 0 | i++; |
358 | |
|
359 | 0 | if ((raw_info->max_off[i] - raw_info->max_off[i - 1]) >= (TSK_OFF_T)len) |
360 | 0 | read_len = len; |
361 | 0 | else |
362 | 0 | read_len = (size_t) (raw_info->max_off[i] - raw_info->max_off[i - 1]); |
363 | |
|
364 | 0 | if (tsk_verbose) { |
365 | 0 | tsk_fprintf(stderr, |
366 | 0 | "raw_read: additional image reads: image %d len: %" |
367 | 0 | PRIuSIZE "\n", i, read_len); |
368 | 0 | } |
369 | |
|
370 | 0 | cnt2 = raw_read_segment(raw_info, i, &buf[cnt], |
371 | 0 | read_len, 0); |
372 | 0 | if (cnt2 < 0) { |
373 | 0 | return -1; |
374 | 0 | } |
375 | 0 | cnt += cnt2; |
376 | |
|
377 | 0 | if ((size_t) cnt2 != read_len) { |
378 | 0 | return cnt; |
379 | 0 | } |
380 | | |
381 | 0 | len -= cnt2; |
382 | 0 | } |
383 | 0 | } |
384 | 0 | return cnt; |
385 | 0 | } |
386 | 0 | } |
387 | | |
388 | 0 | tsk_error_reset(); |
389 | 0 | tsk_error_set_errno(TSK_ERR_IMG_READ_OFF); |
390 | 0 | tsk_error_set_errstr("raw_read: offset %" PRIdOFF |
391 | 0 | " not found in any segments", offset); |
392 | |
|
393 | 0 | return -1; |
394 | 0 | } |
395 | | |
396 | | |
397 | | /** |
398 | | * \internal |
399 | | * Display information about the disk image set. |
400 | | * |
401 | | * @param img_info Disk image to analyze |
402 | | * @param hFile Handle to print information to |
403 | | */ |
404 | | static void |
405 | | raw_imgstat(TSK_IMG_INFO * img_info, FILE * hFile) |
406 | 0 | { |
407 | 0 | IMG_RAW_INFO *raw_info = (IMG_RAW_INFO *) img_info; |
408 | |
|
409 | 0 | tsk_fprintf(hFile, "IMAGE FILE INFORMATION\n"); |
410 | 0 | tsk_fprintf(hFile, "--------------------------------------------\n"); |
411 | 0 | tsk_fprintf(hFile, "Image Type: raw\n"); |
412 | 0 | tsk_fprintf(hFile, "\nSize in bytes: %" PRIdOFF "\n", img_info->size); |
413 | 0 | tsk_fprintf(hFile, "Sector size:\t%d\n", img_info->sector_size); |
414 | |
|
415 | 0 | if (img_info->num_img > 1) { |
416 | 0 | int i; |
417 | |
|
418 | 0 | tsk_fprintf(hFile, |
419 | 0 | "\n--------------------------------------------\n"); |
420 | 0 | tsk_fprintf(hFile, "Split Information:\n"); |
421 | |
|
422 | 0 | for (i = 0; i < img_info->num_img; i++) { |
423 | 0 | tsk_fprintf(hFile, |
424 | 0 | "%" PRIttocTSK " (%" PRIdOFF " to %" PRIdOFF ")\n", |
425 | 0 | img_info->images[i], |
426 | 0 | (TSK_OFF_T) (i == 0) ? 0 : raw_info->max_off[i - 1], |
427 | 0 | (TSK_OFF_T) (raw_info->max_off[i] - 1)); |
428 | 0 | } |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | | |
433 | | /** |
434 | | * \internal |
435 | | * Free the memory and close the file handles for the disk image |
436 | | * |
437 | | * @param img_info Disk image to close |
438 | | */ |
439 | | static void |
440 | | raw_close(TSK_IMG_INFO * img_info) |
441 | 0 | { |
442 | 0 | IMG_RAW_INFO *raw_info = (IMG_RAW_INFO *) img_info; |
443 | 0 | int i; |
444 | |
|
445 | | #ifdef TSK_WIN32 |
446 | | if (raw_info->img_writer != NULL) { |
447 | | raw_info->img_writer->close(raw_info->img_writer); |
448 | | free(raw_info->img_writer); |
449 | | raw_info->img_writer = NULL; |
450 | | } |
451 | | #endif |
452 | |
|
453 | 0 | for (i = 0; i < SPLIT_CACHE; i++) { |
454 | 0 | if (raw_info->cache[i].fd != 0) |
455 | | #ifdef TSK_WIN32 |
456 | | CloseHandle(raw_info->cache[i].fd); |
457 | | #else |
458 | 0 | close(raw_info->cache[i].fd); |
459 | 0 | #endif |
460 | 0 | } |
461 | |
|
462 | 0 | free(raw_info->max_off); |
463 | 0 | free(raw_info->cptr); |
464 | |
|
465 | 0 | tsk_deinit_lock(&(raw_info->read_lock)); |
466 | 0 | tsk_img_free(raw_info); |
467 | 0 | } |
468 | | |
469 | | #ifdef TSK_WIN32 |
470 | | /** |
471 | | * \internal |
472 | | * Test seeking to the given offset and then reading a sector. |
473 | | * @param file_handle The open file handle to the image |
474 | | * @param offset The offset to seek to (in bytes) |
475 | | * @param len The length to read (in bytes). Should be a multiple of the sector size. |
476 | | * @param buf An allocated buffer large enough to hold len bytes |
477 | | * |
478 | | * @return 1 if the seek/read is successful, 0 if not |
479 | | */ |
480 | | static int |
481 | | test_sector_read(HANDLE file_handle, TSK_OFF_T offset, DWORD len, char * buf) { |
482 | | LARGE_INTEGER li; |
483 | | li.QuadPart = offset; |
484 | | |
485 | | // Seek to the given offset |
486 | | li.LowPart = SetFilePointer(file_handle, li.LowPart, |
487 | | &li.HighPart, FILE_BEGIN); |
488 | | if ((li.LowPart == INVALID_SET_FILE_POINTER) && |
489 | | (GetLastError() != NO_ERROR)) { |
490 | | return 0; |
491 | | } |
492 | | |
493 | | // Read a byte at the given offset |
494 | | DWORD nread; |
495 | | if (FALSE == ReadFile(file_handle, buf, len, &nread, NULL)) { |
496 | | return 0; |
497 | | } |
498 | | if (nread != len) { |
499 | | return 0; |
500 | | } |
501 | | |
502 | | // Success |
503 | | return 1; |
504 | | } |
505 | | |
506 | | /** |
507 | | * Attempts to calculate the actual sector size needed for reading the image. |
508 | | * If successful, the calculated sector size will be stored in raw_info. If it |
509 | | * fails the sector_size field will not be updated. |
510 | | * @param raw_info The incomplete IMG_RAW_INFO object. The sector_size field may be updated by this method. |
511 | | * @param image_name Image file name |
512 | | * @param image_size Image size |
513 | | */ |
514 | | static void |
515 | | set_device_sector_size(IMG_RAW_INFO * raw_info, const TSK_TCHAR * image_name, TSK_OFF_T image_size) { |
516 | | unsigned int min_sector_size = 512; |
517 | | unsigned int max_sector_size = 4096; |
518 | | |
519 | | HANDLE file_handle = CreateFile(image_name, FILE_READ_DATA, |
520 | | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, |
521 | | NULL); |
522 | | if (file_handle == INVALID_HANDLE_VALUE) { |
523 | | if (tsk_verbose) { |
524 | | tsk_fprintf(stderr, |
525 | | "find_sector_size: failed to open image \"%" PRIttocTSK "\"\n", image_name); |
526 | | } |
527 | | return; |
528 | | } |
529 | | |
530 | | TSK_IMG_INFO* img_info = &raw_info->img_info.img_info; |
531 | | |
532 | | // First test whether we need to align on sector boundaries |
533 | | char* buf = (char*) malloc(max_sector_size); |
534 | | int needs_sector_alignment = 0; |
535 | | if (image_size > img_info->sector_size) { |
536 | | if (test_sector_read(file_handle, 1, img_info->sector_size, buf)) { |
537 | | needs_sector_alignment = 0; |
538 | | } |
539 | | else { |
540 | | needs_sector_alignment = 1; |
541 | | } |
542 | | } |
543 | | |
544 | | // If reading a sector starting at offset 1 failed, the assumption is that we have a device |
545 | | // that requires reads to be sector-aligned. |
546 | | if (needs_sector_alignment) { |
547 | | // Start at the minimum (512) and double up to max_sector_size (4096) |
548 | | unsigned int sector_size = min_sector_size; |
549 | | |
550 | | while (sector_size <= max_sector_size) { |
551 | | // If we don't have enough data to do the test just stop |
552 | | if (image_size < sector_size * 2) { |
553 | | break; |
554 | | } |
555 | | |
556 | | if (test_sector_read(file_handle, sector_size, sector_size, buf)) { |
557 | | // Found a valid sector size |
558 | | if (tsk_verbose) { |
559 | | tsk_fprintf(stderr, |
560 | | "find_sector_size: using sector size %d\n", sector_size); |
561 | | } |
562 | | img_info->sector_size = sector_size; |
563 | | |
564 | | if (file_handle != 0) { |
565 | | CloseHandle(file_handle); |
566 | | } |
567 | | free(buf); |
568 | | return; |
569 | | } |
570 | | sector_size *= 2; |
571 | | } |
572 | | if (tsk_verbose) { |
573 | | tsk_fprintf(stderr, |
574 | | "find_sector_size: failed to determine correct sector size. Reverting to default %d\n", img_info->sector_size); |
575 | | } |
576 | | free(buf); |
577 | | } |
578 | | |
579 | | if (file_handle != 0) { |
580 | | CloseHandle(file_handle); |
581 | | } |
582 | | } |
583 | | #endif |
584 | | |
585 | | /** |
586 | | * \internal |
587 | | * Open the set of disk images as a set of split raw images |
588 | | * |
589 | | * @param a_num_img Number of images in set |
590 | | * @param a_images List of disk image paths (in sorted order) |
591 | | * @param a_ssize Size of device sector in bytes (or 0 for default) |
592 | | * |
593 | | * @return NULL on error |
594 | | */ |
595 | | TSK_IMG_INFO * |
596 | | raw_open(int a_num_img, const TSK_TCHAR * const a_images[], |
597 | | unsigned int a_ssize) |
598 | 0 | { |
599 | 0 | TSK_IMG_INFO *img_info; |
600 | 0 | int i; |
601 | 0 | TSK_OFF_T first_seg_size; |
602 | |
|
603 | 0 | const auto deleter = [](IMG_RAW_INFO* raw_info) { |
604 | 0 | if (raw_info) { |
605 | 0 | free(raw_info->cptr); |
606 | 0 | free(raw_info->max_off); |
607 | 0 | } |
608 | 0 | tsk_img_free(raw_info); |
609 | 0 | }; |
610 | |
|
611 | 0 | std::unique_ptr<IMG_RAW_INFO, decltype(deleter)> raw_info{ |
612 | 0 | (IMG_RAW_INFO *) tsk_img_malloc(sizeof(IMG_RAW_INFO)), |
613 | 0 | deleter |
614 | 0 | }; |
615 | 0 | if (!raw_info) { |
616 | 0 | return nullptr; |
617 | 0 | } |
618 | | |
619 | 0 | raw_info->cptr = nullptr; |
620 | 0 | raw_info->max_off = nullptr; |
621 | 0 | img_info = (TSK_IMG_INFO *) raw_info.get(); |
622 | |
|
623 | 0 | img_info->itype = TSK_IMG_TYPE_RAW; |
624 | |
|
625 | 0 | raw_info->img_info.read = raw_read; |
626 | 0 | raw_info->img_info.close = raw_close; |
627 | 0 | raw_info->img_info.imgstat = raw_imgstat; |
628 | |
|
629 | 0 | raw_info->is_winobj = 0; |
630 | |
|
631 | | #if defined(TSK_WIN32) || defined(__CYGWIN__) |
632 | | /* determine if this is the path to a Windows device object */ |
633 | | if ((a_images[0][0] == _TSK_T('\\')) |
634 | | && (a_images[0][1] == _TSK_T('\\')) |
635 | | && ((a_images[0][2] == _TSK_T('.')) || (a_images[0][2] == _TSK_T('?'))) |
636 | | && (a_images[0][3] == _TSK_T('\\'))) { |
637 | | raw_info->is_winobj = 1; |
638 | | } |
639 | | #endif |
640 | | |
641 | | /* Check that the first image file exists and is not a directory */ |
642 | 0 | first_seg_size = get_size_of_file_on_disk(a_images[0], raw_info->is_winobj); |
643 | 0 | if (first_seg_size < -1) { |
644 | 0 | return nullptr; |
645 | 0 | } |
646 | | |
647 | | /* Set the sector size */ |
648 | 0 | img_info->sector_size = 512; |
649 | 0 | if (a_ssize) { |
650 | 0 | img_info->sector_size = a_ssize; |
651 | 0 | } |
652 | | #ifdef TSK_WIN32 |
653 | | else if (is_windows_device_path(a_images[0])) { |
654 | | /* On Windows, figure out the actual sector size if one was not given and this is a device. |
655 | | * This is to prevent problems reading later. */ |
656 | | set_device_sector_size(raw_info.get(), a_images[0], first_seg_size); |
657 | | } |
658 | | #endif |
659 | | |
660 | | /* see if there are more of them... */ |
661 | 0 | if (a_num_img == 1 && raw_info->is_winobj == 0) { |
662 | 0 | if ((img_info->images = |
663 | 0 | tsk_img_findFiles(a_images[0], |
664 | 0 | &img_info->num_img)) == nullptr) { |
665 | 0 | tsk_error_reset(); |
666 | 0 | tsk_error_set_errno(TSK_ERR_IMG_STAT); |
667 | 0 | tsk_error_set_errstr |
668 | 0 | ("raw_open: could not find segment files starting at \"%" |
669 | 0 | PRIttocTSK "\"", a_images[0]); |
670 | 0 | return nullptr; |
671 | 0 | } |
672 | 0 | } |
673 | 0 | else { |
674 | 0 | if (!tsk_img_copy_image_names(img_info, a_images, a_num_img)) { |
675 | 0 | return nullptr; |
676 | 0 | } |
677 | 0 | } |
678 | | |
679 | | /* sanity check: when we have multiple segments, the size of |
680 | | * each must be known */ |
681 | 0 | if (img_info->num_img > 1 && first_seg_size < 0) { |
682 | 0 | if (tsk_verbose) { |
683 | 0 | tsk_fprintf(stderr, |
684 | 0 | "raw_open: file size is unknown in a segmented raw image\n"); |
685 | 0 | } |
686 | 0 | return nullptr; |
687 | 0 | } |
688 | | |
689 | | /* initialize the split cache */ |
690 | 0 | raw_info->cptr = (int *) tsk_malloc(img_info->num_img * sizeof(int)); |
691 | 0 | if (!raw_info->cptr) { |
692 | 0 | return nullptr; |
693 | 0 | } |
694 | 0 | memset((void *) &raw_info->cache, 0, |
695 | 0 | SPLIT_CACHE * sizeof(IMG_SPLIT_CACHE)); |
696 | 0 | raw_info->next_slot = 0; |
697 | | |
698 | | /* initialize the offset table and re-use the first segment |
699 | | * size gathered above */ |
700 | 0 | raw_info->max_off = |
701 | 0 | (TSK_OFF_T *) tsk_malloc(img_info->num_img * sizeof(TSK_OFF_T)); |
702 | 0 | if (!raw_info->max_off) { |
703 | 0 | return nullptr; |
704 | 0 | } |
705 | 0 | img_info->size = first_seg_size; |
706 | 0 | raw_info->max_off[0] = img_info->size; |
707 | 0 | raw_info->cptr[0] = -1; |
708 | 0 | if (tsk_verbose) { |
709 | 0 | tsk_fprintf(stderr, |
710 | 0 | "raw_open: segment: 0 size: %" PRIdOFF " max offset: %" |
711 | 0 | PRIdOFF " path: %" PRIttocTSK "\n", first_seg_size, |
712 | 0 | raw_info->max_off[0], img_info->images[0]); |
713 | 0 | } |
714 | | |
715 | | /* get size info for each file - we do not open each one because that |
716 | | * could cause us to run out of file descriptors when we only need a few. |
717 | | * The descriptors are opened as needed */ |
718 | 0 | for (i = 1; i < img_info->num_img; i++) { |
719 | 0 | TSK_OFF_T size; |
720 | 0 | raw_info->cptr[i] = -1; |
721 | 0 | size = get_size_of_file_on_disk(img_info->images[i], raw_info->is_winobj); |
722 | 0 | if (size < 0) { |
723 | 0 | if (size == -1) { |
724 | 0 | if (tsk_verbose) { |
725 | 0 | tsk_fprintf(stderr, |
726 | 0 | "raw_open: file size is unknown in a segmented raw image\n"); |
727 | 0 | } |
728 | 0 | } |
729 | 0 | return nullptr; |
730 | 0 | } |
731 | | |
732 | | /* add the size of this image to the total and save the current max */ |
733 | 0 | img_info->size += size; |
734 | 0 | raw_info->max_off[i] = img_info->size; |
735 | |
|
736 | 0 | if (tsk_verbose) { |
737 | 0 | tsk_fprintf(stderr, |
738 | 0 | "raw_open: segment: %d size: %" PRIdOFF " max offset: %" |
739 | 0 | PRIdOFF " path: %" PRIttocTSK "\n", i, size, |
740 | 0 | raw_info->max_off[i], img_info->images[i]); |
741 | 0 | } |
742 | 0 | } |
743 | | |
744 | | // initialize the read lock |
745 | 0 | tsk_init_lock(&raw_info->read_lock); |
746 | |
|
747 | 0 | return (TSK_IMG_INFO*) raw_info.release(); |
748 | 0 | } |