From: OGAWA Hirofumi In order not to write out the same block repeatedly, rewrite the fat_add_entries(). Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton --- 25-akpm/fs/fat/dir.c | 261 ++++++++++++++++++++++++++++----------- 25-akpm/fs/msdos/namei.c | 13 - 25-akpm/fs/vfat/namei.c | 35 ----- 25-akpm/include/linux/msdos_fs.h | 5 4 files changed, 198 insertions(+), 116 deletions(-) diff -puN fs/fat/dir.c~fat-rewrite-fat_add_entries fs/fat/dir.c --- 25/fs/fat/dir.c~fat-rewrite-fat_add_entries Sun Mar 6 17:13:23 2005 +++ 25-akpm/fs/fat/dir.c Sun Mar 6 17:13:23 2005 @@ -981,94 +981,211 @@ error: EXPORT_SYMBOL(fat_alloc_new_dir); -static struct buffer_head *fat_extend_dir(struct inode *inode) +static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, + int *nr_cluster, struct msdos_dir_entry **de, + struct buffer_head **bh, loff_t *i_pos) { - struct super_block *sb = inode->i_sb; - struct buffer_head *bh, *res = NULL; - int err, cluster, sec_per_clus = MSDOS_SB(sb)->sec_per_clus; - sector_t sector, last_sector; - - if (MSDOS_SB(sb)->fat_bits != 32) { - if (inode->i_ino == MSDOS_ROOT_INO) - return ERR_PTR(-ENOSPC); - } + struct super_block *sb = dir->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct buffer_head *bhs[MAX_BUF_PER_PAGE]; + sector_t blknr, start_blknr, last_blknr; + unsigned long size, copy; + int err, i, n, offset, cluster[2]; + + /* + * The minimum cluster size is 512bytes, and maximum entry + * size is 32*slots (672bytes). So, iff the cluster size is + * 512bytes, we may need two clusters. + */ + size = nr_slots * sizeof(struct msdos_dir_entry); + *nr_cluster = (size + (sbi->cluster_size - 1)) >> sbi->cluster_bits; + BUG_ON(*nr_cluster > 2); - err = fat_alloc_clusters(inode, &cluster, 1); + err = fat_alloc_clusters(dir, cluster, *nr_cluster); if (err) - return ERR_PTR(err); - err = fat_chain_add(inode, cluster, 1); - if (err) { - fat_free_clusters(inode, cluster); - return ERR_PTR(err); - } + goto error; - sector = fat_clus_to_blknr(MSDOS_SB(sb), cluster); - last_sector = sector + sec_per_clus; - for ( ; sector < last_sector; sector++) { - if ((bh = sb_getblk(sb, sector))) { - memset(bh->b_data, 0, sb->s_blocksize); - set_buffer_uptodate(bh); - mark_buffer_dirty(bh); - if (sb->s_flags & MS_SYNCHRONOUS) - sync_dirty_buffer(bh); - if (!res) - res = bh; - else - brelse(bh); + /* + * First stage: Fill the directory entry. NOTE: This cluster + * is not referenced from any inode yet, so updates order is + * not important. + */ + i = n = copy = 0; + do { + start_blknr = blknr = fat_clus_to_blknr(sbi, cluster[i]); + last_blknr = start_blknr + sbi->sec_per_clus; + while (blknr < last_blknr) { + bhs[n] = sb_getblk(sb, blknr); + if (!bhs[n]) { + err = -ENOMEM; + goto error_nomem; + } + + /* fill the directory entry */ + copy = min(size, sb->s_blocksize); + memcpy(bhs[n]->b_data, slots, copy); + slots += copy; + size -= copy; + set_buffer_uptodate(bhs[n]); + mark_buffer_dirty(bhs[n]); + if (!size) + break; + n++; + blknr++; } - } - if (res == NULL) - res = ERR_PTR(-EIO); - if (inode->i_size & (sb->s_blocksize - 1)) { - fat_fs_panic(sb, "Odd directory size"); - inode->i_size = (inode->i_size + sb->s_blocksize) - & ~((loff_t)sb->s_blocksize - 1); - } - inode->i_size += MSDOS_SB(sb)->cluster_size; - MSDOS_I(inode)->mmu_private += MSDOS_SB(sb)->cluster_size; + } while (++i < *nr_cluster); - return res; -} + memset(bhs[n]->b_data + copy, 0, sb->s_blocksize - copy); + offset = copy - sizeof(struct msdos_dir_entry); + get_bh(bhs[n]); + *bh = bhs[n]; + *de = (struct msdos_dir_entry *)((*bh)->b_data + offset); + *i_pos = ((loff_t)blknr << sbi->dir_per_block_bits) + (offset >> MSDOS_DIR_BITS); -/* This assumes that size of cluster is above the 32*slots */ + /* Second stage: clear the rest of cluster, and write outs */ + err = fat_zeroed_cluster(dir, start_blknr, ++n, bhs, MAX_BUF_PER_PAGE); + if (err) + goto error_free; -int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh, - struct msdos_dir_entry **de, loff_t *i_pos) -{ - struct super_block *sb = dir->i_sb; - loff_t offset, curr; - int row; - struct buffer_head *new_bh; + return cluster[0]; - offset = curr = 0; +error_free: + brelse(*bh); *bh = NULL; - row = 0; - while (fat_get_entry(dir, &curr, bh, de, i_pos) > -1) { + n = 0; +error_nomem: + for (i = 0; i < n; i++) + bforget(bhs[i]); + fat_free_clusters(dir, cluster[0]); +error: + return err; +} + +int fat_add_entries(struct inode *dir, void *slots, int nr_slots, + struct fat_slot_info *sinfo) +{ + struct super_block *sb = dir->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct buffer_head *bh, *prev, *bhs[3]; /* 32*slots (672bytes) */ + struct msdos_dir_entry *de; + int err, free_slots, i, nr_bhs; + loff_t pos, i_pos; + + sinfo->nr_slots = nr_slots; + + /* First stage: search free direcotry entries */ + free_slots = nr_bhs = 0; + bh = prev = NULL; + pos = 0; + err = -ENOSPC; + while (fat_get_entry(dir, &pos, &bh, &de, &i_pos) > -1) { /* check the maximum size of directory */ - if (curr >= FAT_MAX_DIR_SIZE) { - brelse(*bh); - return -ENOSPC; - } + if (pos >= FAT_MAX_DIR_SIZE) + goto error; - if (IS_FREE((*de)->name)) { - if (++row == slots) - return offset; + if (IS_FREE(de->name)) { + if (prev != bh) { + get_bh(bh); + bhs[nr_bhs] = prev = bh; + nr_bhs++; + } + free_slots++; + if (free_slots == nr_slots) + goto found; } else { - row = 0; - offset = curr; + for (i = 0; i < nr_bhs; i++) + brelse(bhs[i]); + prev = NULL; + free_slots = nr_bhs = 0; } } - if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) - return -ENOSPC; - new_bh = fat_extend_dir(dir); - if (IS_ERR(new_bh)) - return PTR_ERR(new_bh); - brelse(new_bh); - do { - fat_get_entry(dir, &curr, bh, de, i_pos); - } while (++row < slots); + if ((dir->i_ino == MSDOS_ROOT_INO) && (sbi->fat_bits != 32)) + goto error; + +found: + err = 0; + pos -= free_slots * sizeof(*de); + nr_slots -= free_slots; + if (free_slots) { + /* + * Second stage: filling the free entries with new entries. + * NOTE: If this slots has shortname, first, we write + * the long name slots, then write the short name. + */ + int size = free_slots * sizeof(*de); + int offset = pos & (sb->s_blocksize - 1); + int long_bhs = nr_bhs - (nr_slots == 0); + + /* Fill the long name slots. */ + for (i = 0; i < long_bhs; i++) { + int copy = min_t(int, sb->s_blocksize - offset, size); + memcpy(bhs[i]->b_data + offset, slots, copy); + mark_buffer_dirty(bhs[i]); + offset = 0; + slots += copy; + size -= copy; + } + if (long_bhs && IS_DIRSYNC(dir)) + err = fat_sync_bhs(bhs, long_bhs); + if (!err && i < nr_bhs) { + /* Fill the short name slot. */ + int copy = min_t(int, sb->s_blocksize - offset, size); + memcpy(bhs[i]->b_data + offset, slots, copy); + mark_buffer_dirty(bhs[i]); + if (IS_DIRSYNC(dir)) + err = sync_dirty_buffer(bhs[i]); + } + for (i = 0; i < nr_bhs; i++) + brelse(bhs[i]); + if (err) + goto error_remove; + } + + if (nr_slots) { + int cluster, nr_cluster; - return offset; + /* + * Third stage: allocate the cluster for new entries. + * And initialize the cluster with new entries, then + * add the cluster to dir. + */ + cluster = fat_add_new_entries(dir, slots, nr_slots, &nr_cluster, + &de, &bh, &i_pos); + if (cluster < 0) { + err = cluster; + goto error_remove; + } + err = fat_chain_add(dir, cluster, nr_cluster); + if (err) { + fat_free_clusters(dir, cluster); + goto error_remove; + } + if (dir->i_size & (sbi->cluster_size - 1)) { + fat_fs_panic(sb, "Odd directory size"); + dir->i_size = (dir->i_size + sbi->cluster_size - 1) + & ~((loff_t)sbi->cluster_size - 1); + } + dir->i_size += nr_cluster << sbi->cluster_bits; + MSDOS_I(dir)->mmu_private += nr_cluster << sbi->cluster_bits; + } + sinfo->slot_off = pos; + sinfo->i_pos = i_pos; + sinfo->de = de; + sinfo->bh = bh; + + return 0; + +error: + brelse(bh); + for (i = 0; i < nr_bhs; i++) + brelse(bhs[i]); + return err; + +error_remove: + brelse(bh); + if (free_slots) + __fat_remove_entries(dir, pos, free_slots); + return err; } EXPORT_SYMBOL(fat_add_entries); diff -puN fs/msdos/namei.c~fat-rewrite-fat_add_entries fs/msdos/namei.c --- 25/fs/msdos/namei.c~fat-rewrite-fat_add_entries Sun Mar 6 17:13:23 2005 +++ 25-akpm/fs/msdos/namei.c Sun Mar 6 17:13:23 2005 @@ -258,7 +258,7 @@ static int msdos_add_entry(struct inode { struct msdos_dir_entry de; __le16 time, date; - int offset; + int err; memcpy(de.name, name, MSDOS_NAME); de.attr = is_dir ? ATTR_DIR : ATTR_ARCH; @@ -273,14 +273,9 @@ static int msdos_add_entry(struct inode de.starthi = cpu_to_le16(cluster >> 16); de.size = 0; - offset = fat_add_entries(dir, 1, &sinfo->bh, &sinfo->de, &sinfo->i_pos); - if (offset < 0) - return offset; - sinfo->slot_off = offset; - sinfo->nr_slots = 1; - - memcpy(sinfo->de, &de, sizeof(de)); - mark_buffer_dirty(sinfo->bh); + err = fat_add_entries(dir, &de, 1, sinfo); + if (err) + return err; dir->i_ctime = dir->i_mtime = *ts; mark_inode_dirty(dir); diff -puN fs/vfat/namei.c~fat-rewrite-fat_add_entries fs/vfat/namei.c --- 25/fs/vfat/namei.c~fat-rewrite-fat_add_entries Sun Mar 6 17:13:23 2005 +++ 25-akpm/fs/vfat/namei.c Sun Mar 6 17:13:23 2005 @@ -661,14 +661,9 @@ static int vfat_add_entry(struct inode * int cluster, struct timespec *ts, struct fat_slot_info *sinfo) { - struct super_block *sb = dir->i_sb; struct msdos_dir_slot *slots; unsigned int len; - int err, i, nr_slots; - loff_t offset; - struct msdos_dir_entry *de, *dummy_de; - struct buffer_head *bh, *dummy_bh; - loff_t dummy_i_pos; + int err, nr_slots; len = vfat_striptail_len(qname); if (len == 0) @@ -683,37 +678,13 @@ static int vfat_add_entry(struct inode * if (err) goto cleanup; - /* build the empty directory entry of number of slots */ - offset = - fat_add_entries(dir, nr_slots, &dummy_bh, &dummy_de, &dummy_i_pos); - if (offset < 0) { - err = offset; + err = fat_add_entries(dir, slots, nr_slots, sinfo); + if (err) goto cleanup; - } - brelse(dummy_bh); - - /* Now create the new entry */ - bh = NULL; - for (i = 0; i < nr_slots; i++) { - if (fat_get_entry(dir, &offset, &bh, &de, &sinfo->i_pos) < 0) { - err = -EIO; - goto cleanup; - } - memcpy(de, slots + i, sizeof(struct msdos_dir_slot)); - mark_buffer_dirty(bh); - if (sb->s_flags & MS_SYNCHRONOUS) - sync_dirty_buffer(bh); - } /* update timestamp */ dir->i_ctime = dir->i_mtime = dir->i_atime = *ts; mark_inode_dirty(dir); - - /* slots can't be less than 1 */ - sinfo->slot_off = offset - sizeof(struct msdos_dir_slot) * nr_slots; - sinfo->nr_slots = nr_slots; - sinfo->de = de; - sinfo->bh = bh; cleanup: kfree(slots); return err; diff -puN include/linux/msdos_fs.h~fat-rewrite-fat_add_entries include/linux/msdos_fs.h --- 25/include/linux/msdos_fs.h~fat-rewrite-fat_add_entries Sun Mar 6 17:13:23 2005 +++ 25-akpm/include/linux/msdos_fs.h Sun Mar 6 17:13:23 2005 @@ -324,9 +324,6 @@ extern int fat_bmap(struct inode *inode, extern struct file_operations fat_dir_operations; extern int fat_search_long(struct inode *inode, const unsigned char *name, int name_len, struct fat_slot_info *sinfo); -extern int fat_add_entries(struct inode *dir, int slots, - struct buffer_head **bh, - struct msdos_dir_entry **de, loff_t *i_pos); extern int fat_dir_empty(struct inode *dir); extern int fat_subdirs(struct inode *dir); extern int fat_scan(struct inode *dir, const unsigned char *name, @@ -334,6 +331,8 @@ extern int fat_scan(struct inode *dir, c extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, struct msdos_dir_entry **de, loff_t *i_pos); extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts); +extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots, + struct fat_slot_info *sinfo); extern int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo); /* fat/fatent.c */ _