diff options
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/dir.c | 224 |
1 files changed, 101 insertions, 123 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 204d8614406f..ba824964b9bb 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c | |||
@@ -222,6 +222,80 @@ fat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size, | |||
222 | return len; | 222 | return len; |
223 | } | 223 | } |
224 | 224 | ||
225 | enum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, }; | ||
226 | |||
227 | /** | ||
228 | * fat_parse_long - Parse extended directory entry. | ||
229 | * | ||
230 | * This function returns zero on success, negative value on error, or one of | ||
231 | * the following: | ||
232 | * | ||
233 | * %PARSE_INVALID - Directory entry is invalid. | ||
234 | * %PARSE_NOT_LONGNAME - Directory entry does not contain longname. | ||
235 | * %PARSE_EOF - Directory has no more entries. | ||
236 | */ | ||
237 | static int fat_parse_long(struct inode *dir, loff_t *pos, | ||
238 | struct buffer_head **bh, struct msdos_dir_entry **de, | ||
239 | wchar_t **unicode, unsigned char *nr_slots) | ||
240 | { | ||
241 | struct msdos_dir_slot *ds; | ||
242 | unsigned char id, slot, slots, alias_checksum; | ||
243 | |||
244 | if (!*unicode) { | ||
245 | *unicode = (wchar_t *)__get_free_page(GFP_KERNEL); | ||
246 | if (!*unicode) { | ||
247 | brelse(*bh); | ||
248 | return -ENOMEM; | ||
249 | } | ||
250 | } | ||
251 | parse_long: | ||
252 | slots = 0; | ||
253 | ds = (struct msdos_dir_slot *)*de; | ||
254 | id = ds->id; | ||
255 | if (!(id & 0x40)) | ||
256 | return PARSE_INVALID; | ||
257 | slots = id & ~0x40; | ||
258 | if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ | ||
259 | return PARSE_INVALID; | ||
260 | *nr_slots = slots; | ||
261 | alias_checksum = ds->alias_checksum; | ||
262 | |||
263 | slot = slots; | ||
264 | while (1) { | ||
265 | int offset; | ||
266 | |||
267 | slot--; | ||
268 | offset = slot * 13; | ||
269 | fat16_towchar(*unicode + offset, ds->name0_4, 5); | ||
270 | fat16_towchar(*unicode + offset + 5, ds->name5_10, 6); | ||
271 | fat16_towchar(*unicode + offset + 11, ds->name11_12, 2); | ||
272 | |||
273 | if (ds->id & 0x40) | ||
274 | (*unicode)[offset + 13] = 0; | ||
275 | if (fat_get_entry(dir, pos, bh, de) < 0) | ||
276 | return PARSE_EOF; | ||
277 | if (slot == 0) | ||
278 | break; | ||
279 | ds = (struct msdos_dir_slot *)*de; | ||
280 | if (ds->attr != ATTR_EXT) | ||
281 | return PARSE_NOT_LONGNAME; | ||
282 | if ((ds->id & ~0x40) != slot) | ||
283 | goto parse_long; | ||
284 | if (ds->alias_checksum != alias_checksum) | ||
285 | goto parse_long; | ||
286 | } | ||
287 | if ((*de)->name[0] == DELETED_FLAG) | ||
288 | return PARSE_INVALID; | ||
289 | if ((*de)->attr == ATTR_EXT) | ||
290 | goto parse_long; | ||
291 | if (IS_FREE((*de)->name) || ((*de)->attr & ATTR_VOLUME)) | ||
292 | return PARSE_INVALID; | ||
293 | if (fat_checksum((*de)->name) != alias_checksum) | ||
294 | *nr_slots = 0; | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
225 | /* | 299 | /* |
226 | * Return values: negative -> error, 0 -> not found, positive -> found, | 300 | * Return values: negative -> error, 0 -> not found, positive -> found, |
227 | * value is the total amount of slots, including the shortname entry. | 301 | * value is the total amount of slots, including the shortname entry. |
@@ -259,65 +333,16 @@ parse_record: | |||
259 | if (de->attr != ATTR_EXT && IS_FREE(de->name)) | 333 | if (de->attr != ATTR_EXT && IS_FREE(de->name)) |
260 | continue; | 334 | continue; |
261 | if (de->attr == ATTR_EXT) { | 335 | if (de->attr == ATTR_EXT) { |
262 | struct msdos_dir_slot *ds; | 336 | int status = fat_parse_long(inode, &cpos, &bh, &de, |
263 | unsigned char id; | 337 | &unicode, &nr_slots); |
264 | unsigned char slot; | 338 | if (status < 0) |
265 | unsigned char slots; | 339 | return status; |
266 | unsigned char alias_checksum; | 340 | else if (status == PARSE_INVALID) |
267 | |||
268 | if (!unicode) { | ||
269 | unicode = (wchar_t *) | ||
270 | __get_free_page(GFP_KERNEL); | ||
271 | if (!unicode) { | ||
272 | brelse(bh); | ||
273 | return -ENOMEM; | ||
274 | } | ||
275 | } | ||
276 | parse_long: | ||
277 | slots = 0; | ||
278 | ds = (struct msdos_dir_slot *) de; | ||
279 | id = ds->id; | ||
280 | if (!(id & 0x40)) | ||
281 | continue; | ||
282 | slots = id & ~0x40; | ||
283 | if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ | ||
284 | continue; | ||
285 | nr_slots = slots; | ||
286 | alias_checksum = ds->alias_checksum; | ||
287 | |||
288 | slot = slots; | ||
289 | while (1) { | ||
290 | int offset; | ||
291 | |||
292 | slot--; | ||
293 | offset = slot * 13; | ||
294 | fat16_towchar(unicode + offset, ds->name0_4, 5); | ||
295 | fat16_towchar(unicode + offset + 5, ds->name5_10, 6); | ||
296 | fat16_towchar(unicode + offset + 11, ds->name11_12, 2); | ||
297 | |||
298 | if (ds->id & 0x40) { | ||
299 | unicode[offset + 13] = 0; | ||
300 | } | ||
301 | if (fat_get_entry(inode, &cpos, &bh, &de) < 0) | ||
302 | goto EODir; | ||
303 | if (slot == 0) | ||
304 | break; | ||
305 | ds = (struct msdos_dir_slot *) de; | ||
306 | if (ds->attr != ATTR_EXT) | ||
307 | goto parse_record; | ||
308 | if ((ds->id & ~0x40) != slot) | ||
309 | goto parse_long; | ||
310 | if (ds->alias_checksum != alias_checksum) | ||
311 | goto parse_long; | ||
312 | } | ||
313 | if (de->name[0] == DELETED_FLAG) | ||
314 | continue; | ||
315 | if (de->attr == ATTR_EXT) | ||
316 | goto parse_long; | ||
317 | if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) | ||
318 | continue; | 341 | continue; |
319 | if (fat_checksum(de->name) != alias_checksum) | 342 | else if (status == PARSE_NOT_LONGNAME) |
320 | nr_slots = 0; | 343 | goto parse_record; |
344 | else if (status == PARSE_EOF) | ||
345 | goto EODir; | ||
321 | } | 346 | } |
322 | 347 | ||
323 | memcpy(work, de->name, sizeof(de->name)); | 348 | memcpy(work, de->name, sizeof(de->name)); |
@@ -405,8 +430,8 @@ struct fat_ioctl_filldir_callback { | |||
405 | int short_len; | 430 | int short_len; |
406 | }; | 431 | }; |
407 | 432 | ||
408 | static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, | 433 | static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, |
409 | filldir_t filldir, int short_only, int both) | 434 | filldir_t filldir, int short_only, int both) |
410 | { | 435 | { |
411 | struct super_block *sb = inode->i_sb; | 436 | struct super_block *sb = inode->i_sb; |
412 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | 437 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
@@ -455,9 +480,10 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, | |||
455 | 480 | ||
456 | bh = NULL; | 481 | bh = NULL; |
457 | GetNew: | 482 | GetNew: |
458 | long_slots = 0; | ||
459 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) | 483 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) |
460 | goto EODir; | 484 | goto EODir; |
485 | parse_record: | ||
486 | long_slots = 0; | ||
461 | /* Check for long filename entry */ | 487 | /* Check for long filename entry */ |
462 | if (isvfat) { | 488 | if (isvfat) { |
463 | if (de->name[0] == DELETED_FLAG) | 489 | if (de->name[0] == DELETED_FLAG) |
@@ -472,66 +498,18 @@ GetNew: | |||
472 | } | 498 | } |
473 | 499 | ||
474 | if (isvfat && de->attr == ATTR_EXT) { | 500 | if (isvfat && de->attr == ATTR_EXT) { |
475 | struct msdos_dir_slot *ds; | 501 | int status = fat_parse_long(inode, &cpos, &bh, &de, |
476 | unsigned char id; | 502 | &unicode, &long_slots); |
477 | unsigned char slot; | 503 | if (status < 0) { |
478 | unsigned char slots; | 504 | filp->f_pos = cpos; |
479 | unsigned char alias_checksum; | 505 | ret = status; |
480 | 506 | goto out; | |
481 | if (!unicode) { | 507 | } else if (status == PARSE_INVALID) |
482 | unicode = (wchar_t *)__get_free_page(GFP_KERNEL); | ||
483 | if (!unicode) { | ||
484 | filp->f_pos = cpos; | ||
485 | brelse(bh); | ||
486 | ret = -ENOMEM; | ||
487 | goto out; | ||
488 | } | ||
489 | } | ||
490 | ParseLong: | ||
491 | slots = 0; | ||
492 | ds = (struct msdos_dir_slot *) de; | ||
493 | id = ds->id; | ||
494 | if (!(id & 0x40)) | ||
495 | goto RecEnd; | ||
496 | slots = id & ~0x40; | ||
497 | if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ | ||
498 | goto RecEnd; | 508 | goto RecEnd; |
499 | long_slots = slots; | 509 | else if (status == PARSE_NOT_LONGNAME) |
500 | alias_checksum = ds->alias_checksum; | 510 | goto parse_record; |
501 | 511 | else if (status == PARSE_EOF) | |
502 | slot = slots; | 512 | goto EODir; |
503 | while (1) { | ||
504 | int offset; | ||
505 | |||
506 | slot--; | ||
507 | offset = slot * 13; | ||
508 | fat16_towchar(unicode + offset, ds->name0_4, 5); | ||
509 | fat16_towchar(unicode + offset + 5, ds->name5_10, 6); | ||
510 | fat16_towchar(unicode + offset + 11, ds->name11_12, 2); | ||
511 | |||
512 | if (ds->id & 0x40) { | ||
513 | unicode[offset + 13] = 0; | ||
514 | } | ||
515 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) | ||
516 | goto EODir; | ||
517 | if (slot == 0) | ||
518 | break; | ||
519 | ds = (struct msdos_dir_slot *) de; | ||
520 | if (ds->attr != ATTR_EXT) | ||
521 | goto RecEnd; /* XXX */ | ||
522 | if ((ds->id & ~0x40) != slot) | ||
523 | goto ParseLong; | ||
524 | if (ds->alias_checksum != alias_checksum) | ||
525 | goto ParseLong; | ||
526 | } | ||
527 | if (de->name[0] == DELETED_FLAG) | ||
528 | goto RecEnd; | ||
529 | if (de->attr == ATTR_EXT) | ||
530 | goto ParseLong; | ||
531 | if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) | ||
532 | goto RecEnd; | ||
533 | if (fat_checksum(de->name) != alias_checksum) | ||
534 | long_slots = 0; | ||
535 | } | 513 | } |
536 | 514 | ||
537 | if (sbi->options.dotsOK) { | 515 | if (sbi->options.dotsOK) { |
@@ -665,7 +643,7 @@ out: | |||
665 | static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) | 643 | static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) |
666 | { | 644 | { |
667 | struct inode *inode = filp->f_dentry->d_inode; | 645 | struct inode *inode = filp->f_dentry->d_inode; |
668 | return fat_readdirx(inode, filp, dirent, filldir, 0, 0); | 646 | return __fat_readdir(inode, filp, dirent, filldir, 0, 0); |
669 | } | 647 | } |
670 | 648 | ||
671 | static int fat_ioctl_filldir(void *__buf, const char *name, int name_len, | 649 | static int fat_ioctl_filldir(void *__buf, const char *name, int name_len, |
@@ -754,8 +732,8 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp, | |||
754 | down(&inode->i_sem); | 732 | down(&inode->i_sem); |
755 | ret = -ENOENT; | 733 | ret = -ENOENT; |
756 | if (!IS_DEADDIR(inode)) { | 734 | if (!IS_DEADDIR(inode)) { |
757 | ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir, | 735 | ret = __fat_readdir(inode, filp, &buf, fat_ioctl_filldir, |
758 | short_only, both); | 736 | short_only, both); |
759 | } | 737 | } |
760 | up(&inode->i_sem); | 738 | up(&inode->i_sem); |
761 | if (ret >= 0) | 739 | if (ret >= 0) |