diff options
Diffstat (limited to 'fs/fat/dir.c')
-rw-r--r-- | fs/fat/dir.c | 230 |
1 files changed, 101 insertions, 129 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 895049b2ac9c..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,68 +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 sum; | 340 | else if (status == PARSE_INVALID) |
267 | unsigned char alias_checksum; | ||
268 | |||
269 | if (!unicode) { | ||
270 | unicode = (wchar_t *) | ||
271 | __get_free_page(GFP_KERNEL); | ||
272 | if (!unicode) { | ||
273 | brelse(bh); | ||
274 | return -ENOMEM; | ||
275 | } | ||
276 | } | ||
277 | parse_long: | ||
278 | slots = 0; | ||
279 | ds = (struct msdos_dir_slot *) de; | ||
280 | id = ds->id; | ||
281 | if (!(id & 0x40)) | ||
282 | continue; | ||
283 | slots = id & ~0x40; | ||
284 | if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ | ||
285 | continue; | ||
286 | nr_slots = slots; | ||
287 | alias_checksum = ds->alias_checksum; | ||
288 | |||
289 | slot = slots; | ||
290 | while (1) { | ||
291 | int offset; | ||
292 | |||
293 | slot--; | ||
294 | offset = slot * 13; | ||
295 | fat16_towchar(unicode + offset, ds->name0_4, 5); | ||
296 | fat16_towchar(unicode + offset + 5, ds->name5_10, 6); | ||
297 | fat16_towchar(unicode + offset + 11, ds->name11_12, 2); | ||
298 | |||
299 | if (ds->id & 0x40) { | ||
300 | unicode[offset + 13] = 0; | ||
301 | } | ||
302 | if (fat_get_entry(inode, &cpos, &bh, &de) < 0) | ||
303 | goto EODir; | ||
304 | if (slot == 0) | ||
305 | break; | ||
306 | ds = (struct msdos_dir_slot *) de; | ||
307 | if (ds->attr != ATTR_EXT) | ||
308 | goto parse_record; | ||
309 | if ((ds->id & ~0x40) != slot) | ||
310 | goto parse_long; | ||
311 | if (ds->alias_checksum != alias_checksum) | ||
312 | goto parse_long; | ||
313 | } | ||
314 | if (de->name[0] == DELETED_FLAG) | ||
315 | continue; | ||
316 | if (de->attr == ATTR_EXT) | ||
317 | goto parse_long; | ||
318 | if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) | ||
319 | continue; | 341 | continue; |
320 | for (sum = 0, i = 0; i < 11; i++) | 342 | else if (status == PARSE_NOT_LONGNAME) |
321 | sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; | 343 | goto parse_record; |
322 | if (sum != alias_checksum) | 344 | else if (status == PARSE_EOF) |
323 | nr_slots = 0; | 345 | goto EODir; |
324 | } | 346 | } |
325 | 347 | ||
326 | memcpy(work, de->name, sizeof(de->name)); | 348 | memcpy(work, de->name, sizeof(de->name)); |
@@ -408,8 +430,8 @@ struct fat_ioctl_filldir_callback { | |||
408 | int short_len; | 430 | int short_len; |
409 | }; | 431 | }; |
410 | 432 | ||
411 | 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, |
412 | filldir_t filldir, int short_only, int both) | 434 | filldir_t filldir, int short_only, int both) |
413 | { | 435 | { |
414 | struct super_block *sb = inode->i_sb; | 436 | struct super_block *sb = inode->i_sb; |
415 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | 437 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
@@ -458,9 +480,10 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, | |||
458 | 480 | ||
459 | bh = NULL; | 481 | bh = NULL; |
460 | GetNew: | 482 | GetNew: |
461 | long_slots = 0; | ||
462 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) | 483 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) |
463 | goto EODir; | 484 | goto EODir; |
485 | parse_record: | ||
486 | long_slots = 0; | ||
464 | /* Check for long filename entry */ | 487 | /* Check for long filename entry */ |
465 | if (isvfat) { | 488 | if (isvfat) { |
466 | if (de->name[0] == DELETED_FLAG) | 489 | if (de->name[0] == DELETED_FLAG) |
@@ -475,69 +498,18 @@ GetNew: | |||
475 | } | 498 | } |
476 | 499 | ||
477 | if (isvfat && de->attr == ATTR_EXT) { | 500 | if (isvfat && de->attr == ATTR_EXT) { |
478 | struct msdos_dir_slot *ds; | 501 | int status = fat_parse_long(inode, &cpos, &bh, &de, |
479 | unsigned char id; | 502 | &unicode, &long_slots); |
480 | unsigned char slot; | 503 | if (status < 0) { |
481 | unsigned char slots; | 504 | filp->f_pos = cpos; |
482 | unsigned char sum; | 505 | ret = status; |
483 | unsigned char alias_checksum; | 506 | goto out; |
484 | 507 | } else if (status == PARSE_INVALID) | |
485 | if (!unicode) { | ||
486 | unicode = (wchar_t *)__get_free_page(GFP_KERNEL); | ||
487 | if (!unicode) { | ||
488 | filp->f_pos = cpos; | ||
489 | brelse(bh); | ||
490 | ret = -ENOMEM; | ||
491 | goto out; | ||
492 | } | ||
493 | } | ||
494 | ParseLong: | ||
495 | slots = 0; | ||
496 | ds = (struct msdos_dir_slot *) de; | ||
497 | id = ds->id; | ||
498 | if (!(id & 0x40)) | ||
499 | goto RecEnd; | ||
500 | slots = id & ~0x40; | ||
501 | if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ | ||
502 | goto RecEnd; | 508 | goto RecEnd; |
503 | long_slots = slots; | 509 | else if (status == PARSE_NOT_LONGNAME) |
504 | alias_checksum = ds->alias_checksum; | 510 | goto parse_record; |
505 | 511 | else if (status == PARSE_EOF) | |
506 | slot = slots; | 512 | goto EODir; |
507 | while (1) { | ||
508 | int offset; | ||
509 | |||
510 | slot--; | ||
511 | offset = slot * 13; | ||
512 | fat16_towchar(unicode + offset, ds->name0_4, 5); | ||
513 | fat16_towchar(unicode + offset + 5, ds->name5_10, 6); | ||
514 | fat16_towchar(unicode + offset + 11, ds->name11_12, 2); | ||
515 | |||
516 | if (ds->id & 0x40) { | ||
517 | unicode[offset + 13] = 0; | ||
518 | } | ||
519 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) | ||
520 | goto EODir; | ||
521 | if (slot == 0) | ||
522 | break; | ||
523 | ds = (struct msdos_dir_slot *) de; | ||
524 | if (ds->attr != ATTR_EXT) | ||
525 | goto RecEnd; /* XXX */ | ||
526 | if ((ds->id & ~0x40) != slot) | ||
527 | goto ParseLong; | ||
528 | if (ds->alias_checksum != alias_checksum) | ||
529 | goto ParseLong; | ||
530 | } | ||
531 | if (de->name[0] == DELETED_FLAG) | ||
532 | goto RecEnd; | ||
533 | if (de->attr == ATTR_EXT) | ||
534 | goto ParseLong; | ||
535 | if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) | ||
536 | goto RecEnd; | ||
537 | for (sum = 0, i = 0; i < 11; i++) | ||
538 | sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; | ||
539 | if (sum != alias_checksum) | ||
540 | long_slots = 0; | ||
541 | } | 513 | } |
542 | 514 | ||
543 | if (sbi->options.dotsOK) { | 515 | if (sbi->options.dotsOK) { |
@@ -671,7 +643,7 @@ out: | |||
671 | 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) |
672 | { | 644 | { |
673 | struct inode *inode = filp->f_dentry->d_inode; | 645 | struct inode *inode = filp->f_dentry->d_inode; |
674 | return fat_readdirx(inode, filp, dirent, filldir, 0, 0); | 646 | return __fat_readdir(inode, filp, dirent, filldir, 0, 0); |
675 | } | 647 | } |
676 | 648 | ||
677 | 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, |
@@ -760,8 +732,8 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp, | |||
760 | down(&inode->i_sem); | 732 | down(&inode->i_sem); |
761 | ret = -ENOENT; | 733 | ret = -ENOENT; |
762 | if (!IS_DEADDIR(inode)) { | 734 | if (!IS_DEADDIR(inode)) { |
763 | ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir, | 735 | ret = __fat_readdir(inode, filp, &buf, fat_ioctl_filldir, |
764 | short_only, both); | 736 | short_only, both); |
765 | } | 737 | } |
766 | up(&inode->i_sem); | 738 | up(&inode->i_sem); |
767 | if (ret >= 0) | 739 | if (ret >= 0) |