aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fat/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fat/dir.c')
-rw-r--r--fs/fat/dir.c230
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
225enum { 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 */
237static 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 }
251parse_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 }
277parse_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
411static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, 433static 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;
460GetNew: 482GetNew:
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;
485parse_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 }
494ParseLong:
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:
671static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) 643static 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
677static int fat_ioctl_filldir(void *__buf, const char *name, int name_len, 649static 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)