X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Ffat%2Fdir.c;h=698b85bb1dd45855a709635eb6239a4c9bbef0d6;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=e5ae1b720dde8b42321235bbb2b3369e017e9d27;hpb=cee37fe97739d85991964371c1f3a745c00dd236;p=linux-2.6.git diff --git a/fs/fat/dir.c b/fs/fat/dir.c index e5ae1b720..698b85bb1 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -30,6 +30,29 @@ static inline loff_t fat_make_i_pos(struct super_block *sb, | (de - (struct msdos_dir_entry *)bh->b_data); } +static inline void fat_dir_readahead(struct inode *dir, sector_t iblock, + sector_t phys) +{ + struct super_block *sb = dir->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct buffer_head *bh; + int sec; + + /* This is not a first sector of cluster, or sec_per_clus == 1 */ + if ((iblock & (sbi->sec_per_clus - 1)) || sbi->sec_per_clus == 1) + return; + /* root dir of FAT12/FAT16 */ + if ((sbi->fat_bits != 32) && (dir->i_ino == MSDOS_ROOT_INO)) + return; + + bh = sb_find_get_block(sb, phys); + if (bh == NULL || !buffer_uptodate(bh)) { + for (sec = 0; sec < sbi->sec_per_clus; sec++) + sb_breadahead(sb, phys + sec); + } + brelse(bh); +} + /* Returns the inode number of the directory entry at offset pos. If bh is non-NULL, it is brelse'd before. Pos is incremented. The buffer header is returned in bh. @@ -45,8 +68,8 @@ static int fat__get_entry(struct inode *dir, loff_t *pos, { struct super_block *sb = dir->i_sb; sector_t phys, iblock; - int offset; - int err; + unsigned long mapped_blocks; + int err, offset; next: if (*bh) @@ -54,10 +77,12 @@ next: *bh = NULL; iblock = *pos >> sb->s_blocksize_bits; - err = fat_bmap(dir, iblock, &phys); + err = fat_bmap(dir, iblock, &phys, &mapped_blocks); if (err || !phys) return -1; /* beyond EOF or error */ + fat_dir_readahead(dir, iblock, phys); + *bh = sb_bread(sb, phys); if (*bh == NULL) { printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n", @@ -89,7 +114,7 @@ static inline int fat_get_entry(struct inode *dir, loff_t *pos, } /* - * Convert Unicode 16 to UTF8, translated Unicode, or ASCII. + * Convert Unicode 16 to UTF-8, translated Unicode, or ASCII. * If uni_xlate is enabled and we can't get a 1:1 conversion, use a * colon as an escape character since it is normally invalid on the vfat * filesystem. The following four characters are the hexadecimal digits @@ -197,6 +222,80 @@ fat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size, return len; } +enum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, }; + +/** + * fat_parse_long - Parse extended directory entry. + * + * This function returns zero on success, negative value on error, or one of + * the following: + * + * %PARSE_INVALID - Directory entry is invalid. + * %PARSE_NOT_LONGNAME - Directory entry does not contain longname. + * %PARSE_EOF - Directory has no more entries. + */ +static int fat_parse_long(struct inode *dir, loff_t *pos, + struct buffer_head **bh, struct msdos_dir_entry **de, + wchar_t **unicode, unsigned char *nr_slots) +{ + struct msdos_dir_slot *ds; + unsigned char id, slot, slots, alias_checksum; + + if (!*unicode) { + *unicode = (wchar_t *)__get_free_page(GFP_KERNEL); + if (!*unicode) { + brelse(*bh); + return -ENOMEM; + } + } +parse_long: + slots = 0; + ds = (struct msdos_dir_slot *)*de; + id = ds->id; + if (!(id & 0x40)) + return PARSE_INVALID; + slots = id & ~0x40; + if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ + return PARSE_INVALID; + *nr_slots = slots; + alias_checksum = ds->alias_checksum; + + slot = slots; + while (1) { + int offset; + + slot--; + offset = slot * 13; + fat16_towchar(*unicode + offset, ds->name0_4, 5); + fat16_towchar(*unicode + offset + 5, ds->name5_10, 6); + fat16_towchar(*unicode + offset + 11, ds->name11_12, 2); + + if (ds->id & 0x40) + (*unicode)[offset + 13] = 0; + if (fat_get_entry(dir, pos, bh, de) < 0) + return PARSE_EOF; + if (slot == 0) + break; + ds = (struct msdos_dir_slot *)*de; + if (ds->attr != ATTR_EXT) + return PARSE_NOT_LONGNAME; + if ((ds->id & ~0x40) != slot) + goto parse_long; + if (ds->alias_checksum != alias_checksum) + goto parse_long; + } + if ((*de)->name[0] == DELETED_FLAG) + return PARSE_INVALID; + if ((*de)->attr == ATTR_EXT) + goto parse_long; + if (IS_FREE((*de)->name) || ((*de)->attr & ATTR_VOLUME)) + return PARSE_INVALID; + if (fat_checksum((*de)->name) != alias_checksum) + *nr_slots = 0; + + return 0; +} + /* * Return values: negative -> error, 0 -> not found, positive -> found, * value is the total amount of slots, including the shortname entry. @@ -234,68 +333,16 @@ parse_record: if (de->attr != ATTR_EXT && IS_FREE(de->name)) continue; if (de->attr == ATTR_EXT) { - struct msdos_dir_slot *ds; - unsigned char id; - unsigned char slot; - unsigned char slots; - unsigned char sum; - unsigned char alias_checksum; - - if (!unicode) { - unicode = (wchar_t *) - __get_free_page(GFP_KERNEL); - if (!unicode) { - brelse(bh); - return -ENOMEM; - } - } -parse_long: - slots = 0; - ds = (struct msdos_dir_slot *) de; - id = ds->id; - if (!(id & 0x40)) - continue; - slots = id & ~0x40; - if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ + int status = fat_parse_long(inode, &cpos, &bh, &de, + &unicode, &nr_slots); + if (status < 0) + return status; + else if (status == PARSE_INVALID) continue; - nr_slots = slots; - alias_checksum = ds->alias_checksum; - - slot = slots; - while (1) { - int offset; - - slot--; - offset = slot * 13; - fat16_towchar(unicode + offset, ds->name0_4, 5); - fat16_towchar(unicode + offset + 5, ds->name5_10, 6); - fat16_towchar(unicode + offset + 11, ds->name11_12, 2); - - if (ds->id & 0x40) { - unicode[offset + 13] = 0; - } - if (fat_get_entry(inode, &cpos, &bh, &de) < 0) - goto EODir; - if (slot == 0) - break; - ds = (struct msdos_dir_slot *) de; - if (ds->attr != ATTR_EXT) - goto parse_record; - if ((ds->id & ~0x40) != slot) - goto parse_long; - if (ds->alias_checksum != alias_checksum) - goto parse_long; - } - if (de->name[0] == DELETED_FLAG) - continue; - if (de->attr == ATTR_EXT) - goto parse_long; - if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) - continue; - for (sum = 0, i = 0; i < 11; i++) - sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; - if (sum != alias_checksum) - nr_slots = 0; + else if (status == PARSE_NOT_LONGNAME) + goto parse_record; + else if (status == PARSE_EOF) + goto EODir; } memcpy(work, de->name, sizeof(de->name)); @@ -371,7 +418,7 @@ EODir: return err; } -EXPORT_SYMBOL(fat_search_long); +EXPORT_SYMBOL_GPL(fat_search_long); struct fat_ioctl_filldir_callback { struct dirent __user *dirent; @@ -383,8 +430,8 @@ struct fat_ioctl_filldir_callback { int short_len; }; -static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, - filldir_t filldir, int short_only, int both) +static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, + filldir_t filldir, int short_only, int both) { struct super_block *sb = inode->i_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); @@ -433,9 +480,10 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, bh = NULL; GetNew: - long_slots = 0; if (fat_get_entry(inode, &cpos, &bh, &de) == -1) goto EODir; +parse_record: + long_slots = 0; /* Check for long filename entry */ if (isvfat) { if (de->name[0] == DELETED_FLAG) @@ -450,69 +498,18 @@ GetNew: } if (isvfat && de->attr == ATTR_EXT) { - struct msdos_dir_slot *ds; - unsigned char id; - unsigned char slot; - unsigned char slots; - unsigned char sum; - unsigned char alias_checksum; - - if (!unicode) { - unicode = (wchar_t *)__get_free_page(GFP_KERNEL); - if (!unicode) { - filp->f_pos = cpos; - brelse(bh); - ret = -ENOMEM; - goto out; - } - } -ParseLong: - slots = 0; - ds = (struct msdos_dir_slot *) de; - id = ds->id; - if (!(id & 0x40)) - goto RecEnd; - slots = id & ~0x40; - if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ + int status = fat_parse_long(inode, &cpos, &bh, &de, + &unicode, &long_slots); + if (status < 0) { + filp->f_pos = cpos; + ret = status; + goto out; + } else if (status == PARSE_INVALID) goto RecEnd; - long_slots = slots; - alias_checksum = ds->alias_checksum; - - slot = slots; - while (1) { - int offset; - - slot--; - offset = slot * 13; - fat16_towchar(unicode + offset, ds->name0_4, 5); - fat16_towchar(unicode + offset + 5, ds->name5_10, 6); - fat16_towchar(unicode + offset + 11, ds->name11_12, 2); - - if (ds->id & 0x40) { - unicode[offset + 13] = 0; - } - if (fat_get_entry(inode, &cpos, &bh, &de) == -1) - goto EODir; - if (slot == 0) - break; - ds = (struct msdos_dir_slot *) de; - if (ds->attr != ATTR_EXT) - goto RecEnd; /* XXX */ - if ((ds->id & ~0x40) != slot) - goto ParseLong; - if (ds->alias_checksum != alias_checksum) - goto ParseLong; - } - if (de->name[0] == DELETED_FLAG) - goto RecEnd; - if (de->attr == ATTR_EXT) - goto ParseLong; - if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) - goto RecEnd; - for (sum = 0, i = 0; i < 11; i++) - sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; - if (sum != alias_checksum) - long_slots = 0; + else if (status == PARSE_NOT_LONGNAME) + goto parse_record; + else if (status == PARSE_EOF) + goto EODir; } if (sbi->options.dotsOK) { @@ -635,8 +632,7 @@ RecEnd: EODir: filp->f_pos = cpos; FillFailed: - if (bh) - brelse(bh); + brelse(bh); if (unicode) free_page((unsigned long)unicode); out: @@ -647,7 +643,7 @@ out: static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; - return fat_readdirx(inode, filp, dirent, filldir, 0, 0); + return __fat_readdir(inode, filp, dirent, filldir, 0, 0); } static int fat_ioctl_filldir(void *__buf, const char *name, int name_len, @@ -733,19 +729,19 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp, buf.dirent = d1; buf.result = 0; - down(&inode->i_sem); + mutex_lock(&inode->i_mutex); ret = -ENOENT; if (!IS_DEADDIR(inode)) { - ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir, - short_only, both); + ret = __fat_readdir(inode, filp, &buf, fat_ioctl_filldir, + short_only, both); } - up(&inode->i_sem); + mutex_unlock(&inode->i_mutex); if (ret >= 0) ret = buf.result; return ret; } -struct file_operations fat_dir_operations = { +const struct file_operations fat_dir_operations = { .read = generic_read_dir, .readdir = fat_readdir, .ioctl = fat_dir_ioctl, @@ -784,7 +780,7 @@ int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, return -ENOENT; } -EXPORT_SYMBOL(fat_get_dotdot_entry); +EXPORT_SYMBOL_GPL(fat_get_dotdot_entry); /* See if directory is empty */ int fat_dir_empty(struct inode *dir) @@ -807,7 +803,7 @@ int fat_dir_empty(struct inode *dir) return result; } -EXPORT_SYMBOL(fat_dir_empty); +EXPORT_SYMBOL_GPL(fat_dir_empty); /* * fat_subdirs counts the number of sub-directories of dir. It can be run @@ -853,7 +849,7 @@ int fat_scan(struct inode *dir, const unsigned char *name, return -ENOENT; } -EXPORT_SYMBOL(fat_scan); +EXPORT_SYMBOL_GPL(fat_scan); static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots) { @@ -940,7 +936,7 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) return 0; } -EXPORT_SYMBOL(fat_remove_entries); +EXPORT_SYMBOL_GPL(fat_remove_entries); static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used, struct buffer_head **bhs, int nr_bhs) @@ -1052,7 +1048,7 @@ error: return err; } -EXPORT_SYMBOL(fat_alloc_new_dir); +EXPORT_SYMBOL_GPL(fat_alloc_new_dir); static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, int *nr_cluster, struct msdos_dir_entry **de, @@ -1268,4 +1264,4 @@ error_remove: return err; } -EXPORT_SYMBOL(fat_add_entries); +EXPORT_SYMBOL_GPL(fat_add_entries);