File: | 3rdparty/vboot/futility/file_type_rwsig.c |
Warning: | line 322, column 3 Null pointer passed to 1st parameter expecting 'nonnull' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* Copyright 2015 The ChromiumOS Authors | |||
2 | * Use of this source code is governed by a BSD-style license that can be | |||
3 | * found in the LICENSE file. | |||
4 | * | |||
5 | * Some instances of the Chrome OS embedded controller firmware can't do a | |||
6 | * normal software sync handshake at boot, but will verify their own RW images | |||
7 | * instead. This is typically done by putting a struct vb2_packed_key in the RO | |||
8 | * image and a corresponding struct vb21_signature in the RW image. | |||
9 | * | |||
10 | * This file provides the basic implementation for that approach. | |||
11 | */ | |||
12 | ||||
13 | #include <stdint.h> | |||
14 | #include <stdio.h> | |||
15 | #include <unistd.h> | |||
16 | ||||
17 | #include "2common.h" | |||
18 | #include "2rsa.h" | |||
19 | #include "2sha.h" | |||
20 | #include "2sysincludes.h" | |||
21 | #include "file_type.h" | |||
22 | #include "fmap.h" | |||
23 | #include "futility.h" | |||
24 | #include "futility_options.h" | |||
25 | #include "host_common.h" | |||
26 | #include "host_common21.h" | |||
27 | #include "host_key21.h" | |||
28 | #include "host_misc.h" | |||
29 | #include "host_signature21.h" | |||
30 | #include "util_misc.h" | |||
31 | ||||
32 | #define SIGNATURE_RSVD_SIZE1024 1024 | |||
33 | ||||
34 | static void show_sig(const char *fname, const struct vb21_signature *sig) | |||
35 | { | |||
36 | printf("Signature: %s\n", fname); | |||
37 | printf(" Vboot API: 2.1\n"); | |||
38 | printf(" Desc: \"%s\"\n", vb21_common_desc(sig)); | |||
39 | printf(" Signature Algorithm: %d %s\n", sig->sig_alg, | |||
40 | vb2_get_sig_algorithm_name(sig->sig_alg)); | |||
41 | printf(" Hash Algorithm: %d %s\n", sig->hash_alg, | |||
42 | vb2_get_hash_algorithm_name(sig->hash_alg)); | |||
43 | printf(" Total size: %#x (%d)\n", sig->c.total_size, | |||
44 | sig->c.total_size); | |||
45 | printf(" ID: "); | |||
46 | print_bytes(&sig->id, sizeof(sig->id)); | |||
47 | printf("\n"); | |||
48 | printf(" Data size: %#x (%d)\n", sig->data_size, | |||
49 | sig->data_size); | |||
50 | } | |||
51 | ||||
52 | int ft_show_rwsig(const char *fname) | |||
53 | { | |||
54 | const struct vb21_packed_key *pkey = show_option.pkey; | |||
55 | struct vb2_public_key key; | |||
56 | uint8_t workbuf[VB2_VERIFY_DATA_WORKBUF_BYTES(64 + ({ typeof((3 * 1024)) __vb2_max_a = ((3 * 1024)); typeof (sizeof(struct vb2_digest_context)) __vb2_max_b = (sizeof(struct vb2_digest_context)); __vb2_max_a > __vb2_max_b ? __vb2_max_a : __vb2_max_b; }))] | |||
57 | __attribute__((aligned(VB2_WORKBUF_ALIGN8))); | |||
58 | uint32_t data_size, sig_size = SIGNATURE_RSVD_SIZE1024; | |||
59 | uint32_t total_data_size = 0; | |||
60 | uint8_t *data; | |||
61 | FmapHeader *fmap; | |||
62 | int i; | |||
63 | int fd = -1; | |||
64 | uint8_t *buf; | |||
65 | uint32_t len; | |||
66 | int rv = 1; | |||
67 | ||||
68 | if (futil_open_and_map_file(fname, &fd, FILE_RO, &buf, &len)) | |||
69 | return 1; | |||
70 | ||||
71 | VB2_DEBUG("name %s len 0x%08x (%d)\n", fname, len, len)vb2ex_printf(__func__, "name %s len 0x%08x (%d)\n", fname, len , len); | |||
72 | ||||
73 | /* Am I just looking at a signature file? */ | |||
74 | VB2_DEBUG("Looking for signature at 0x0\n")vb2ex_printf(__func__, "Looking for signature at 0x0\n"); | |||
75 | const struct vb21_signature *sig = (const struct vb21_signature *)buf; | |||
76 | if (VB2_SUCCESS == vb21_verify_signature(sig, len)) { | |||
77 | show_sig(fname, sig); | |||
78 | if (!show_option.fv) { | |||
79 | printf("No data available to verify\n"); | |||
80 | rv = show_option.strict; | |||
81 | goto done; | |||
82 | } | |||
83 | data = show_option.fv; | |||
84 | data_size = show_option.fv_size; | |||
85 | total_data_size = show_option.fv_size; | |||
86 | } else if ((fmap = fmap_find(buf, len))) { | |||
87 | /* This looks like a full image. */ | |||
88 | FmapAreaHeader *fmaparea; | |||
89 | ||||
90 | VB2_DEBUG("Found an FMAP!\n")vb2ex_printf(__func__, "Found an FMAP!\n"); | |||
91 | ||||
92 | /* If no public key is provided, use the one packed in RO | |||
93 | * image, and print that. */ | |||
94 | if (!pkey) { | |||
95 | pkey = (const struct vb21_packed_key *) | |||
96 | fmap_find_by_name(buf, len, fmap, "KEY_RO", 0); | |||
97 | ||||
98 | if (pkey) | |||
99 | show_vb21_pubkey_buf(fname, (uint8_t *)pkey, | |||
100 | pkey->c.total_size); | |||
101 | } | |||
102 | ||||
103 | sig = (const struct vb21_signature *) | |||
104 | fmap_find_by_name(buf, len, fmap, "SIG_RW", &fmaparea); | |||
105 | if (!sig) { | |||
106 | VB2_DEBUG("No SIG_RW in FMAP.\n")vb2ex_printf(__func__, "No SIG_RW in FMAP.\n"); | |||
107 | goto done; | |||
108 | } | |||
109 | ||||
110 | sig_size = fmaparea->area_size; | |||
111 | ||||
112 | VB2_DEBUG("Looking for signature at %#tx (%#x)\n",vb2ex_printf(__func__, "Looking for signature at %#tx (%#x)\n" , (uint8_t *)sig - buf, sig_size) | |||
113 | (uint8_t *)sig - buf, sig_size)vb2ex_printf(__func__, "Looking for signature at %#tx (%#x)\n" , (uint8_t *)sig - buf, sig_size); | |||
114 | ||||
115 | if (VB2_SUCCESS != vb21_verify_signature(sig, sig_size)) | |||
116 | goto done; | |||
117 | ||||
118 | show_sig(fname, sig); | |||
119 | data = fmap_find_by_name(buf, len, fmap, "EC_RW", &fmaparea); | |||
120 | data_size = sig->data_size; | |||
121 | /* | |||
122 | * TODO(crosbug.com/p/62231): EC_RW region should not include | |||
123 | * the signature. | |||
124 | */ | |||
125 | total_data_size = fmaparea->area_size - sig_size; | |||
126 | ||||
127 | if (!data) { | |||
128 | VB2_DEBUG("No EC_RW in FMAP.\n")vb2ex_printf(__func__, "No EC_RW in FMAP.\n"); | |||
129 | goto done; | |||
130 | } | |||
131 | } else { | |||
132 | /* Or maybe this is just the RW portion, that does not | |||
133 | * contain a FMAP. */ | |||
134 | if (show_option.sig_size) | |||
135 | sig_size = show_option.sig_size; | |||
136 | ||||
137 | VB2_DEBUG("Looking for signature at %#x\n", len - sig_size)vb2ex_printf(__func__, "Looking for signature at %#x\n", len - sig_size); | |||
138 | ||||
139 | if (len < sig_size) { | |||
140 | VB2_DEBUG("File is too small\n")vb2ex_printf(__func__, "File is too small\n"); | |||
141 | goto done; | |||
142 | } | |||
143 | ||||
144 | sig = (const struct vb21_signature *)(buf + len - sig_size); | |||
145 | if (VB2_SUCCESS == vb21_verify_signature(sig, sig_size)) { | |||
146 | show_sig(fname, sig); | |||
147 | data = buf; | |||
148 | data_size = sig->data_size; | |||
149 | total_data_size = len - sig_size; | |||
150 | } else { | |||
151 | goto done; | |||
152 | } | |||
153 | } | |||
154 | ||||
155 | if (!pkey) { | |||
156 | printf("No public key available to verify with\n"); | |||
157 | rv = show_option.strict; | |||
158 | goto done; | |||
159 | } | |||
160 | ||||
161 | /* We already did this once, so it should work again */ | |||
162 | if (vb21_unpack_key(&key, (const uint8_t *)pkey, pkey->c.total_size)) { | |||
163 | VB2_DEBUG("Can't unpack pubkey\n")vb2ex_printf(__func__, "Can't unpack pubkey\n"); | |||
164 | goto done; | |||
165 | } | |||
166 | ||||
167 | if (data_size > total_data_size) { | |||
168 | VB2_DEBUG("Invalid signature data_size: bigger than total area size.\n")vb2ex_printf(__func__, "Invalid signature data_size: bigger than total area size.\n" ); | |||
169 | goto done; | |||
170 | } | |||
171 | ||||
172 | /* The sig is destroyed by the verify operation, so make a copy */ | |||
173 | { | |||
174 | uint8_t sigbuf[sig->c.total_size]; | |||
175 | memcpy(sigbuf, sig, sizeof(sigbuf)); | |||
176 | struct vb2_workbuf wb; | |||
177 | ||||
178 | vb2_workbuf_init(&wb, workbuf, sizeof(workbuf)); | |||
179 | ||||
180 | if (vb21_verify_data(data, data_size, | |||
181 | (struct vb21_signature *)sigbuf, | |||
182 | (const struct vb2_public_key *)&key, | |||
183 | &wb)) { | |||
184 | ERROR("Signature verification failed\n")fprintf(stderr, "ERROR: %s: " "Signature verification failed\n" , __func__ ); | |||
185 | goto done; | |||
186 | } | |||
187 | } | |||
188 | ||||
189 | /* Check that the rest of region is padded with 0xff. */ | |||
190 | for (i = data_size; i < total_data_size; i++) { | |||
191 | if (data[i] != 0xff) { | |||
192 | ERROR("Padding verification failed\n")fprintf(stderr, "ERROR: %s: " "Padding verification failed\n" , __func__ ); | |||
193 | goto done; | |||
194 | } | |||
195 | } | |||
196 | ||||
197 | printf("Signature verification succeeded.\n"); | |||
198 | rv = 0; | |||
199 | done: | |||
200 | futil_unmap_and_close_file(fd, FILE_RO, buf, len); | |||
201 | return rv; | |||
202 | } | |||
203 | ||||
204 | int ft_sign_rwsig(const char *fname) | |||
205 | { | |||
206 | struct vb21_signature *tmp_sig = 0; | |||
207 | struct vb2_public_key *pubkey = 0; | |||
208 | struct vb21_packed_key *packedkey = 0; | |||
209 | uint8_t *keyb_data = 0; | |||
210 | uint32_t keyb_size; | |||
211 | uint8_t *data; /* data to be signed */ | |||
212 | uint32_t r, data_size, sig_size = SIGNATURE_RSVD_SIZE1024; | |||
213 | int retval = 1; | |||
214 | FmapHeader *fmap = NULL((void*)0); | |||
215 | FmapAreaHeader *fmaparea; | |||
216 | struct vb21_signature *old_sig = 0; | |||
| ||||
217 | uint8_t *buf = NULL((void*)0); | |||
218 | uint32_t len; | |||
219 | int fd = -1; | |||
220 | ||||
221 | if (futil_open_and_map_file(fname, &fd, FILE_MODE_SIGN(sign_option)(sign_option.create_new_outfile ? FILE_RO : FILE_RW), | |||
222 | &buf, &len)) | |||
223 | return 1; | |||
224 | ||||
225 | data = buf; | |||
226 | data_size = len; | |||
227 | ||||
228 | VB2_DEBUG("name %s len 0x%08x (%d)\n", fname, len, len)vb2ex_printf(__func__, "name %s len 0x%08x (%d)\n", fname, len , len); | |||
229 | ||||
230 | /* If we don't have a distinct OUTFILE, look for an existing sig */ | |||
231 | if (sign_option.inout_file_count < 2) { | |||
232 | fmap = fmap_find(data, len); | |||
233 | ||||
234 | if (fmap) { | |||
235 | /* This looks like a full image. */ | |||
236 | VB2_DEBUG("Found an FMAP!\n")vb2ex_printf(__func__, "Found an FMAP!\n"); | |||
237 | ||||
238 | old_sig = (struct vb21_signature *) | |||
239 | fmap_find_by_name(buf, len, fmap, "SIG_RW", | |||
240 | &fmaparea); | |||
241 | if (!old_sig) { | |||
242 | VB2_DEBUG("No SIG_RW in FMAP.\n")vb2ex_printf(__func__, "No SIG_RW in FMAP.\n"); | |||
243 | goto done; | |||
244 | } | |||
245 | ||||
246 | sig_size = fmaparea->area_size; | |||
247 | ||||
248 | VB2_DEBUG("Looking for signature at %#tx (%#x)\n",vb2ex_printf(__func__, "Looking for signature at %#tx (%#x)\n" , (uint8_t *)old_sig - buf, sig_size) | |||
249 | (uint8_t *)old_sig - buf, sig_size)vb2ex_printf(__func__, "Looking for signature at %#tx (%#x)\n" , (uint8_t *)old_sig - buf, sig_size); | |||
250 | ||||
251 | data = fmap_find_by_name(buf, len, fmap, "EC_RW", | |||
252 | &fmaparea); | |||
253 | if (!data) { | |||
254 | VB2_DEBUG("No EC_RW in FMAP.\n")vb2ex_printf(__func__, "No EC_RW in FMAP.\n"); | |||
255 | goto done; | |||
256 | } | |||
257 | } else { | |||
258 | /* Or maybe this is just the RW portion, that does not | |||
259 | * contain a FMAP. */ | |||
260 | if (sign_option.sig_size) | |||
261 | sig_size = sign_option.sig_size; | |||
262 | ||||
263 | VB2_DEBUG("Looking for old signature at %#x\n",vb2ex_printf(__func__, "Looking for old signature at %#x\n", len - sig_size) | |||
264 | len - sig_size)vb2ex_printf(__func__, "Looking for old signature at %#x\n", len - sig_size); | |||
265 | ||||
266 | if (len < sig_size) { | |||
267 | ERROR("File is too small\n")fprintf(stderr, "ERROR: %s: " "File is too small\n", __func__ ); | |||
268 | goto done; | |||
269 | } | |||
270 | ||||
271 | /* Take a look */ | |||
272 | old_sig = (struct vb21_signature *) | |||
273 | (buf + len - sig_size); | |||
274 | } | |||
275 | ||||
276 | if (vb21_verify_signature(old_sig, sig_size)) { | |||
277 | ERROR("Can't find a valid signature\n")fprintf(stderr, "ERROR: %s: " "Can't find a valid signature\n" , __func__ ); | |||
278 | goto done; | |||
279 | } | |||
280 | ||||
281 | /* Use the same extent again */ | |||
282 | data_size = old_sig->data_size; | |||
283 | ||||
284 | VB2_DEBUG("Found sig: data_size is %#x (%d)\n", data_size,vb2ex_printf(__func__, "Found sig: data_size is %#x (%d)\n", data_size , data_size) | |||
285 | data_size)vb2ex_printf(__func__, "Found sig: data_size is %#x (%d)\n", data_size , data_size); | |||
286 | } | |||
287 | ||||
288 | /* Unless overridden */ | |||
289 | if (sign_option.data_size) | |||
290 | data_size = sign_option.data_size; | |||
291 | ||||
292 | /* Sign the blob */ | |||
293 | if (sign_option.prikey) { | |||
294 | r = vb21_sign_data(&tmp_sig, | |||
295 | data, data_size, sign_option.prikey, 0); | |||
296 | if (r) { | |||
297 | ERROR("Unable to sign data (error 0x%08x)\n", r)fprintf(stderr, "ERROR: %s: " "Unable to sign data (error 0x%08x)\n" , __func__, r ); | |||
298 | goto done; | |||
299 | } | |||
300 | } else { | |||
301 | VB2_DEBUG("Private key not provided. Copying previous signature\n")vb2ex_printf(__func__, "Private key not provided. Copying previous signature\n" ); | |||
302 | if (!old_sig) { | |||
303 | /* This isn't necessary because no prikey mode runs only | |||
304 | * for fmap input or RW input */ | |||
305 | ERROR("Previous signature not found.\n")fprintf(stderr, "ERROR: %s: " "Previous signature not found.\n" , __func__ ); | |||
306 | goto done; | |||
307 | } | |||
308 | tmp_sig = calloc(1, old_sig->c.total_size); | |||
309 | if (!tmp_sig) | |||
310 | goto done; | |||
311 | memcpy(tmp_sig, old_sig, old_sig->c.total_size); | |||
312 | } | |||
313 | ||||
314 | if (sign_option.inout_file_count < 2) { | |||
315 | /* Overwrite the old signature */ | |||
316 | if (tmp_sig->c.total_size > sig_size) { | |||
317 | ERROR("New sig is too large (%d > %d)\n",fprintf(stderr, "ERROR: %s: " "New sig is too large (%d > %d)\n" , __func__, tmp_sig->c.total_size, sig_size ) | |||
318 | tmp_sig->c.total_size, sig_size)fprintf(stderr, "ERROR: %s: " "New sig is too large (%d > %d)\n" , __func__, tmp_sig->c.total_size, sig_size ); | |||
319 | goto done; | |||
320 | } | |||
321 | VB2_DEBUG("Replacing old signature with new one\n")vb2ex_printf(__func__, "Replacing old signature with new one\n" ); | |||
322 | memset(old_sig, 0xff, sig_size); | |||
| ||||
323 | memcpy(old_sig, tmp_sig, tmp_sig->c.total_size); | |||
324 | if (fmap && sign_option.ecrw_out) { | |||
325 | VB2_DEBUG("Writing %s (size=%d)\n",vb2ex_printf(__func__, "Writing %s (size=%d)\n", sign_option. ecrw_out, fmaparea->area_size) | |||
326 | sign_option.ecrw_out, fmaparea->area_size)vb2ex_printf(__func__, "Writing %s (size=%d)\n", sign_option. ecrw_out, fmaparea->area_size); | |||
327 | if (vb2_write_file(sign_option.ecrw_out, data, | |||
328 | fmaparea->area_size)) | |||
329 | goto done; | |||
330 | } | |||
331 | } else { | |||
332 | /* Write the signature to a new file */ | |||
333 | r = vb21_write_object(sign_option.outfile, tmp_sig); | |||
334 | if (r) { | |||
335 | ERROR("Unable to write sig (error 0x%08x)\n", r)fprintf(stderr, "ERROR: %s: " "Unable to write sig (error 0x%08x)\n" , __func__, r ); | |||
336 | goto done; | |||
337 | } | |||
338 | } | |||
339 | ||||
340 | /* For full images, let's replace the public key in RO. If prikey is | |||
341 | * not provided, skip it. */ | |||
342 | if (fmap && sign_option.prikey) { | |||
343 | uint8_t *new_pubkey; | |||
344 | uint8_t *pubkey_buf = 0; | |||
345 | ||||
346 | /* Create the public key */ | |||
347 | if (vb2_public_key_alloc(&pubkey, | |||
348 | sign_option.prikey->sig_alg)) { | |||
349 | ERROR("Unable to allocate the public key\n")fprintf(stderr, "ERROR: %s: " "Unable to allocate the public key\n" , __func__ ); | |||
350 | goto done; | |||
351 | } | |||
352 | ||||
353 | /* Extract the keyb blob */ | |||
354 | if (vb_keyb_from_private_key(sign_option.prikey, &keyb_data, &keyb_size)) { | |||
355 | ERROR("Couldn't extract the public key\n")fprintf(stderr, "ERROR: %s: " "Couldn't extract the public key\n" , __func__ ); | |||
356 | goto done; | |||
357 | } | |||
358 | ||||
359 | /* | |||
360 | * Copy the keyb blob to the public key's buffer, because that's | |||
361 | * where vb2_unpack_key_data() and vb2_public_key_pack() expect | |||
362 | * to find it. | |||
363 | */ | |||
364 | pubkey_buf = vb2_public_key_packed_data(pubkey); | |||
365 | memcpy(pubkey_buf, keyb_data, keyb_size); | |||
366 | ||||
367 | /* Fill in the internal struct pointers */ | |||
368 | if (vb2_unpack_key_data(pubkey, pubkey_buf, keyb_size)) { | |||
369 | ERROR("Unable to unpack the public key blob\n")fprintf(stderr, "ERROR: %s: " "Unable to unpack the public key blob\n" , __func__ ); | |||
370 | goto done; | |||
371 | } | |||
372 | ||||
373 | pubkey->hash_alg = sign_option.prikey->hash_alg; | |||
374 | pubkey->version = sign_option.version_specified ? | |||
375 | sign_option.version : 1; | |||
376 | vb2_public_key_set_desc(pubkey, sign_option.prikey->desc); | |||
377 | ||||
378 | memcpy((struct vb2_id *)pubkey->id, &sign_option.prikey->id, | |||
379 | sizeof(*(pubkey->id))); | |||
380 | ||||
381 | if (vb21_public_key_pack(&packedkey, pubkey)) { | |||
382 | goto done; | |||
383 | } | |||
384 | ||||
385 | new_pubkey = fmap_find_by_name(buf, len, fmap, "KEY_RO", | |||
386 | &fmaparea); | |||
387 | if (!new_pubkey) { | |||
388 | VB2_DEBUG("No KEY_RO in FMAP.\n")vb2ex_printf(__func__, "No KEY_RO in FMAP.\n"); | |||
389 | goto done; | |||
390 | } | |||
391 | /* Overwrite the old signature */ | |||
392 | if (packedkey->c.total_size > fmaparea->area_size) { | |||
393 | ERROR("New sig is too large (%d > %d)\n",fprintf(stderr, "ERROR: %s: " "New sig is too large (%d > %d)\n" , __func__, packedkey->c.total_size, sig_size ) | |||
394 | packedkey->c.total_size, sig_size)fprintf(stderr, "ERROR: %s: " "New sig is too large (%d > %d)\n" , __func__, packedkey->c.total_size, sig_size ); | |||
395 | goto done; | |||
396 | } | |||
397 | ||||
398 | memset(new_pubkey, 0xff, fmaparea->area_size); | |||
399 | memcpy(new_pubkey, packedkey, packedkey->c.total_size); | |||
400 | } | |||
401 | ||||
402 | /* Finally */ | |||
403 | retval = 0; | |||
404 | done: | |||
405 | futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option)(sign_option.create_new_outfile ? FILE_RO : FILE_RW), buf, len); | |||
406 | free(tmp_sig); | |||
407 | if (pubkey) | |||
408 | vb2_public_key_free(pubkey); | |||
409 | free(packedkey); | |||
410 | free(keyb_data); | |||
411 | ||||
412 | return retval; | |||
413 | } | |||
414 | ||||
415 | enum futil_file_type ft_recognize_rwsig(uint8_t *buf, uint32_t len) | |||
416 | { | |||
417 | const struct vb21_signature *sig = NULL((void*)0); | |||
418 | uint32_t sig_size; | |||
419 | ||||
420 | if (!vb21_verify_signature((const struct vb21_signature *)buf, len)) | |||
421 | return FILE_TYPE_RWSIG; | |||
422 | ||||
423 | FmapHeader *fmap = fmap_find(buf, len); | |||
424 | if (fmap) { | |||
425 | /* This looks like a full image. */ | |||
426 | FmapAreaHeader *fmaparea; | |||
427 | ||||
428 | sig = (const struct vb21_signature *) | |||
429 | fmap_find_by_name(buf, len, fmap, "SIG_RW", &fmaparea); | |||
430 | ||||
431 | if (!sig) | |||
432 | return FILE_TYPE_UNKNOWN; | |||
433 | ||||
434 | sig_size = fmaparea->area_size; | |||
435 | } else { | |||
436 | /* RW-only image */ | |||
437 | sig = (const struct vb21_signature *) | |||
438 | (buf + len - SIGNATURE_RSVD_SIZE1024); | |||
439 | sig_size = SIGNATURE_RSVD_SIZE1024; | |||
440 | } | |||
441 | ||||
442 | if (len >= sig_size && !vb21_verify_signature(sig, sig_size)) | |||
443 | return FILE_TYPE_RWSIG; | |||
444 | ||||
445 | return FILE_TYPE_UNKNOWN; | |||
446 | } |