/src/sleuthkit/tsk/fs/fatfs_utils.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** The Sleuth Kit |
3 | | ** |
4 | | ** Copyright (c) 2013 Basis Technology Corp. All rights reserved |
5 | | ** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org] |
6 | | ** |
7 | | ** This software is distributed under the Common Public License 1.0 |
8 | | ** |
9 | | */ |
10 | | |
11 | | /** |
12 | | * \file fatfs_utils.c |
13 | | * Contains utility functions for processing FAT file systems. |
14 | | */ |
15 | | |
16 | | #include "tsk_fs_i.h" |
17 | | #include "tsk_fatfs.h" |
18 | | #include <assert.h> |
19 | | |
20 | | /** |
21 | | * \internal |
22 | | * Tests whether a pointer argument is set to NULL. If the pointer is NULL, |
23 | | * sets a TSK_ERR_FS_ARG error with an error message that includes a parameter |
24 | | * name and a function name supplied by the caller. |
25 | | * |
26 | | * @param a_ptr The pointer to test for NULL. |
27 | | * @param a_param_name The name of the parameter for which the pointer was |
28 | | * passed as an argument. |
29 | | * @param a_func_name The name of the function for which a_param is a |
30 | | * parameter. |
31 | | * @return Returns 1 if the pointer is NULL, 0 otherwise. |
32 | | */ |
33 | | uint8_t |
34 | | fatfs_ptr_arg_is_null(void *a_ptr, const char *a_param_name, const char *a_func_name) |
35 | 11.0M | { |
36 | 11.0M | const char *func_name = "fatfs_ptr_arg_is_null"; |
37 | | |
38 | 11.0M | assert(a_param_name != NULL); |
39 | 11.0M | assert(a_func_name != NULL); |
40 | | |
41 | 11.0M | if (a_ptr == NULL) { |
42 | 0 | tsk_error_reset(); |
43 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
44 | 0 | if ((a_param_name != NULL) && (a_func_name != NULL)) { |
45 | 0 | tsk_error_set_errstr("%s: %s is NULL", a_param_name, a_func_name); |
46 | 0 | } |
47 | 0 | else { |
48 | 0 | tsk_error_set_errstr("%s: NULL pointer", func_name); |
49 | 0 | } |
50 | |
|
51 | 0 | return 1; |
52 | 0 | } |
53 | | |
54 | 11.0M | return 0; |
55 | 11.0M | } |
56 | | |
57 | | /** |
58 | | * \internal |
59 | | * Tests whether an inode address is in the range of valid inode addresses for |
60 | | * a given file system. |
61 | | * |
62 | | * @param a_fatfs Generic FAT file system info structure. |
63 | | * @param a_inum An inode address. |
64 | | * @return Returns 1 if the address is in range, 0 otherwise. |
65 | | */ |
66 | | uint8_t |
67 | | fatfs_inum_is_in_range(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum) |
68 | 2.77M | { |
69 | 2.77M | const char *func_name = "fatfs_inum_is_in_range"; |
70 | 2.77M | TSK_FS_INFO *fs = (TSK_FS_INFO*)a_fatfs; |
71 | | |
72 | 2.77M | assert(a_fatfs != NULL); |
73 | | |
74 | 2.77M | if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) { |
75 | 0 | return 0; |
76 | 0 | } |
77 | | |
78 | 2.77M | if ((a_inum < fs->first_inum) || (a_inum > fs->last_inum)) { |
79 | 0 | return 0; |
80 | 0 | } |
81 | | |
82 | 2.77M | return 1; |
83 | 2.77M | } |
84 | | |
85 | | /** |
86 | | * \internal |
87 | | * Tests whether an inode address argument is in the range of valid inode |
88 | | * addresses for a given file system. If the address is out of range, |
89 | | * sets a TSK_ERR_FS_ARG error with an error message that includes the inode |
90 | | * address and a function name supplied by the caller. |
91 | | * |
92 | | * @param a_fatfs Generic FAT file system info structure. |
93 | | * @param a_inum An inode address. |
94 | | * @param a_func_name The name of the function that received the inode address |
95 | | * as an argument. |
96 | | * @return Returns 1 if the address is in range, 0 otherwise. |
97 | | */ |
98 | | uint8_t |
99 | | fatfs_inum_arg_is_in_range(FATFS_INFO *a_fatfs, TSK_INUM_T a_inum, const char *a_func_name) |
100 | 1.98M | { |
101 | 1.98M | const char *func_name = "fatfs_inum_arg_is_in_range"; |
102 | | |
103 | 1.98M | assert(a_fatfs != NULL); |
104 | 1.98M | assert(a_func_name != NULL); |
105 | | |
106 | 1.98M | if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) { |
107 | 0 | return 0; |
108 | 0 | } |
109 | | |
110 | 1.98M | if (!fatfs_inum_is_in_range(a_fatfs, a_inum)) { |
111 | 0 | tsk_error_reset(); |
112 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
113 | 0 | if (a_func_name != NULL) { |
114 | 0 | tsk_error_set_errstr("%s: inode address: %" PRIuINUM " out of range", a_func_name, a_inum); |
115 | 0 | } |
116 | 0 | else { |
117 | 0 | tsk_error_set_errstr("%s: inode address: %" PRIuINUM " out of range", func_name, a_inum); |
118 | 0 | } |
119 | |
|
120 | 0 | return 0; |
121 | 0 | } |
122 | 1.98M | return 1; |
123 | 1.98M | } |
124 | | |
125 | | /** |
126 | | * \internal |
127 | | * Convert a DOS time stamp into a UNIX time stamp. A DOS time stamp consists |
128 | | * of a date with the year specified as an offset from 1980. A UNIX time stamp |
129 | | * is seconds since January 1, 1970 in UTC. |
130 | | * |
131 | | * @param date Date part of a DOS time stamp. |
132 | | * @param time Time part of a DOS time stamp. |
133 | | * @param timetens Tenths of seconds part of a DOS time stamp, range is 0-199. |
134 | | * @return A UNIX time stamp. |
135 | | */ |
136 | | time_t |
137 | | fatfs_dos_2_unix_time(uint16_t date, uint16_t time, uint8_t timetens) |
138 | 837k | { |
139 | 837k | struct tm tm1; |
140 | 837k | time_t ret; |
141 | | |
142 | 837k | if (date == 0) |
143 | 0 | return 0; |
144 | | |
145 | 837k | memset(&tm1, 0, sizeof(struct tm)); |
146 | | |
147 | 837k | tm1.tm_sec = ((time & FATFS_SEC_MASK) >> FATFS_SEC_SHIFT) * 2; |
148 | 837k | if ((tm1.tm_sec < 0) || (tm1.tm_sec > 60)) |
149 | 2.46k | tm1.tm_sec = 0; |
150 | | |
151 | | /* The ctimetens value has a range of 0 to 199 */ |
152 | 837k | if (timetens >= 100) |
153 | 5.33k | tm1.tm_sec++; |
154 | | |
155 | 837k | tm1.tm_min = ((time & FATFS_MIN_MASK) >> FATFS_MIN_SHIFT); |
156 | 837k | if ((tm1.tm_min < 0) || (tm1.tm_min > 59)) |
157 | 1.09k | tm1.tm_min = 0; |
158 | | |
159 | 837k | tm1.tm_hour = ((time & FATFS_HOUR_MASK) >> FATFS_HOUR_SHIFT); |
160 | 837k | if ((tm1.tm_hour < 0) || (tm1.tm_hour > 23)) |
161 | 2.71k | tm1.tm_hour = 0; |
162 | | |
163 | 837k | tm1.tm_mday = ((date & FATFS_DAY_MASK) >> FATFS_DAY_SHIFT); |
164 | 837k | if ((tm1.tm_mday < 1) || (tm1.tm_mday > 31)) |
165 | 0 | tm1.tm_mday = 0; |
166 | | |
167 | 837k | tm1.tm_mon = ((date & FATFS_MON_MASK) >> FATFS_MON_SHIFT) - 1; |
168 | 837k | if ((tm1.tm_mon < 0) || (tm1.tm_mon > 11)) |
169 | 0 | tm1.tm_mon = 0; |
170 | | |
171 | | /* There is a limit to the year because the UNIX time value is |
172 | | * a 32-bit value |
173 | | * the maximum UNIX time is Tue Jan 19 03:14:07 2038 */ |
174 | 837k | tm1.tm_year = ((date & FATFS_YEAR_MASK) >> FATFS_YEAR_SHIFT) + 80; |
175 | 837k | if ((tm1.tm_year < 0) || (tm1.tm_year > 137)) |
176 | 7.05k | tm1.tm_year = 0; |
177 | | |
178 | | /* set the daylight savings variable to -1 so that mktime() figures |
179 | | * it out */ |
180 | 837k | tm1.tm_isdst = -1; |
181 | | |
182 | 837k | ret = mktime(&tm1); |
183 | | |
184 | 837k | if (ret < 0) { |
185 | 7.05k | if (tsk_verbose) |
186 | 0 | tsk_fprintf(stderr, |
187 | 0 | "fatfs_dos_2_unix_time: Error running mktime() on: %d:%d:%d %d/%d/%d\n", |
188 | 0 | ((time & FATFS_HOUR_MASK) >> FATFS_HOUR_SHIFT), |
189 | 0 | ((time & FATFS_MIN_MASK) >> FATFS_MIN_SHIFT), |
190 | 0 | ((time & FATFS_SEC_MASK) >> FATFS_SEC_SHIFT) * 2, |
191 | 0 | ((date & FATFS_MON_MASK) >> FATFS_MON_SHIFT) - 1, |
192 | 0 | ((date & FATFS_DAY_MASK) >> FATFS_DAY_SHIFT), |
193 | 0 | ((date & FATFS_YEAR_MASK) >> FATFS_YEAR_SHIFT) + 80); |
194 | 7.05k | return 0; |
195 | 7.05k | } |
196 | | |
197 | 830k | return ret; |
198 | 837k | } |
199 | | |
200 | | /** |
201 | | * \internal |
202 | | * Converts the tenths of seconds part a DOS time stamp into nanoseconds. |
203 | | * of a date with the year specified as an offset from 1980. A UNIX time stamp |
204 | | * is seconds since January 1, 1970 in UTC. |
205 | | * |
206 | | * @param timetens Tenths of seconds part of a DOS time stamp, range is 0-199. |
207 | | * @return A duration in nanoseconds. |
208 | | */ |
209 | | uint32_t |
210 | | fatfs_dos_2_nanosec(uint8_t timetens) |
211 | 423k | { |
212 | 423k | timetens %= 100; |
213 | 423k | return timetens * 10000000; |
214 | 423k | } |
215 | | |
216 | | /** |
217 | | * \internal |
218 | | * Cleans up a string so that it contains only ASCII characters. Useful when |
219 | | * |
220 | | * @param name The string |
221 | | */ |
222 | | void |
223 | | fatfs_cleanup_ascii(char *str) |
224 | 976k | { |
225 | 976k | const char *func_name = "fatfs_cleanup_ascii"; |
226 | | |
227 | 976k | assert(str != NULL); |
228 | | |
229 | 976k | if (!fatfs_ptr_arg_is_null(str, "str", func_name)) { |
230 | 976k | int i; |
231 | 2.90M | for (i = 0; str[i] != '\0'; i++) { |
232 | 1.93M | if ((unsigned char) (str[i]) > 0x7e) { |
233 | 81.1k | str[i] = '^'; |
234 | 81.1k | } |
235 | 1.93M | } |
236 | 976k | } |
237 | 976k | } |
238 | | |
239 | | /** |
240 | | * \internal |
241 | | * Converts a UTF-16 string from an inode into a null-terminated UTF-8 string. If the |
242 | | * conversion fails, sets a TSK_ERR_FS_UNICODE error with an error message |
243 | | * that includes the inode address and a description of the UTF-16 string |
244 | | * supplied by the caller. |
245 | | * |
246 | | * Unlike tsk_UTF16toUTF8, a_src and a_dest will not be updated to point |
247 | | * to where the conversion stopped reading/writing. |
248 | | * |
249 | | * @param a_fatfs Generic FAT file system info structure. |
250 | | * @param a_src The UTF-16 string to convert. |
251 | | * @param a_src_len The number of UTF16 items in a_src. |
252 | | * @param a_dest The buffer for the UTF-8 string. |
253 | | * @param a_dest_len The number of bytes in a_dest. |
254 | | * @param a_inum The address of the source inode, used if an error message is |
255 | | * generated. |
256 | | * @param a_desc A description of the source string, used if an error message |
257 | | * is generated. |
258 | | * @return TSKConversionResult. |
259 | | */ |
260 | | TSKConversionResult |
261 | | fatfs_utf16_inode_str_2_utf8(FATFS_INFO *a_fatfs, UTF16 *a_src, size_t a_src_len, UTF8 *a_dest, size_t a_dest_len, TSK_INUM_T a_inum, const char *a_desc) |
262 | 122 | { |
263 | 122 | const char *func_name = "fatfs_copy_utf16_str"; |
264 | 122 | TSK_FS_INFO *fs = &(a_fatfs->fs_info); |
265 | 122 | TSKConversionResult conv_result = TSKconversionOK; |
266 | 122 | UTF8* dest_start; |
267 | 122 | UTF8* dest_end; |
268 | | |
269 | 122 | assert(a_fatfs != NULL); |
270 | 122 | assert(a_src != NULL); |
271 | 122 | assert(a_src_len > 0); |
272 | 122 | assert(a_dest != NULL); |
273 | 122 | assert(a_dest_len > 0); |
274 | 122 | assert(a_desc != NULL); |
275 | | |
276 | 122 | if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name)) { |
277 | 0 | return TSKsourceIllegal; |
278 | 0 | } |
279 | | |
280 | 122 | if (fatfs_ptr_arg_is_null(a_fatfs, "a_src", func_name)) { |
281 | 0 | return TSKsourceExhausted; |
282 | 0 | } |
283 | | |
284 | 122 | if (a_src_len <= 0) { |
285 | 0 | return TSKsourceExhausted; |
286 | 0 | } |
287 | | |
288 | 122 | if (fatfs_ptr_arg_is_null(a_fatfs, "a_dest", func_name)) { |
289 | 0 | return TSKtargetExhausted; |
290 | 0 | } |
291 | | |
292 | 122 | if (a_dest_len <= 0) { |
293 | 0 | return TSKtargetExhausted; |
294 | 0 | } |
295 | | |
296 | 122 | if (fatfs_ptr_arg_is_null(a_fatfs, "a_desc", func_name)) { |
297 | 0 | return TSKsourceIllegal; |
298 | 0 | } |
299 | | |
300 | | /* Do the conversion. Note that a_dest and a_src will point to where the conversion |
301 | | * stopped reading/writing. */ |
302 | 122 | dest_start = a_dest; |
303 | 122 | dest_end = (UTF8*)&a_dest[a_dest_len]; |
304 | 122 | conv_result = tsk_UTF16toUTF8(fs->endian, (const UTF16**)&a_src, (UTF16*)&a_src[a_src_len], &a_dest, dest_end, TSKlenientConversion); |
305 | | |
306 | 122 | if (conv_result != TSKconversionOK) { |
307 | 0 | tsk_error_reset(); |
308 | 0 | tsk_error_set_errno(TSK_ERR_FS_UNICODE); |
309 | 0 | tsk_error_set_errstr("%s: Error converting %s for inum %" PRIuINUM " from UTF16 to UTF8: %d", func_name, a_desc, a_inum, conv_result); |
310 | 0 | *a_dest = '\0'; |
311 | 0 | return conv_result; |
312 | 0 | } |
313 | | |
314 | | /* Make sure the result is NULL-terminated. */ |
315 | 122 | if((uintptr_t)a_dest >= (uintptr_t)dest_end) |
316 | 0 | dest_start[a_dest_len - 1] = '\0'; |
317 | 122 | else |
318 | 122 | *a_dest = '\0'; |
319 | | |
320 | 122 | return conv_result; |
321 | 122 | } |