diff options
| -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) |
