From: OGAWA Hirofumi With this change, ->mkdir() uses the correct updating order. Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton --- 25-akpm/fs/fat/dir.c | 155 ++++++++++++++++++++++++++++----------- 25-akpm/fs/fat/fatent.c | 3 25-akpm/fs/msdos/namei.c | 54 ++++++------- 25-akpm/fs/vfat/namei.c | 59 +++++++------- 25-akpm/include/linux/msdos_fs.h | 2 5 files changed, 169 insertions(+), 104 deletions(-) diff -puN fs/fat/dir.c~fat-allocate-the-cluster-before-adding-the-directory fs/fat/dir.c --- 25/fs/fat/dir.c~fat-allocate-the-cluster-before-adding-the-directory Sun Mar 6 17:13:22 2005 +++ 25-akpm/fs/fat/dir.c Sun Mar 6 17:13:22 2005 @@ -869,6 +869,118 @@ int fat_remove_entries(struct inode *dir EXPORT_SYMBOL(fat_remove_entries); +static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used, + struct buffer_head **bhs, int nr_bhs) +{ + struct super_block *sb = dir->i_sb; + sector_t last_blknr = blknr + MSDOS_SB(sb)->sec_per_clus; + int err, i, n; + + /* Zeroing the unused blocks on this cluster */ + blknr += nr_used; + n = nr_used; + while (blknr < last_blknr) { + bhs[n] = sb_getblk(sb, blknr); + if (!bhs[n]) { + err = -ENOMEM; + goto error; + } + memset(bhs[n]->b_data, 0, sb->s_blocksize); + set_buffer_uptodate(bhs[n]); + mark_buffer_dirty(bhs[n]); + + n++; + blknr++; + if (n == nr_bhs) { + if (IS_DIRSYNC(dir)) { + err = fat_sync_bhs(bhs, n); + if (err) + goto error; + } + for (i = 0; i < n; i++) + brelse(bhs[i]); + n = 0; + } + } + if (IS_DIRSYNC(dir)) { + err = fat_sync_bhs(bhs, n); + if (err) + goto error; + } + for (i = 0; i < n; i++) + brelse(bhs[i]); + + return 0; + +error: + for (i = 0; i < n; i++) + bforget(bhs[i]); + return err; +} + +int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) +{ + struct super_block *sb = dir->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct buffer_head *bhs[MAX_BUF_PER_PAGE]; + struct msdos_dir_entry *de; + sector_t blknr; + __le16 date, time; + int err, cluster; + + err = fat_alloc_clusters(dir, &cluster, 1); + if (err) + goto error; + + blknr = fat_clus_to_blknr(sbi, cluster); + bhs[0] = sb_getblk(sb, blknr); + if (!bhs[0]) { + err = -ENOMEM; + goto error_free; + } + + fat_date_unix2dos(ts->tv_sec, &time, &date); + + de = (struct msdos_dir_entry *)bhs[0]->b_data; + /* filling the new directory slots ("." and ".." entries) */ + memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME); + memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME); + de->attr = de[1].attr = ATTR_DIR; + de[0].lcase = de[1].lcase = 0; + de[0].time = de[1].time = time; + de[0].date = de[1].date = date; + de[0].ctime_cs = de[1].ctime_cs = 0; + if (sbi->options.isvfat) { + /* extra timestamps */ + de[0].ctime = de[1].ctime = time; + de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date; + } else { + de[0].ctime = de[1].ctime = 0; + de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0; + } + de[0].start = cpu_to_le16(cluster); + de[0].starthi = cpu_to_le16(cluster >> 16); + de[1].start = cpu_to_le16(MSDOS_I(dir)->i_logstart); + de[1].starthi = cpu_to_le16(MSDOS_I(dir)->i_logstart >> 16); + de[0].size = de[1].size = 0; + memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de)); + set_buffer_uptodate(bhs[0]); + mark_buffer_dirty(bhs[0]); + + err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE); + if (err) + goto error_free; + + return cluster; + +error_free: + fat_free_clusters(dir, cluster); +error: + return err; +} + +EXPORT_SYMBOL(fat_alloc_new_dir); + static struct buffer_head *fat_extend_dir(struct inode *inode) { struct super_block *sb = inode->i_sb; @@ -960,46 +1072,3 @@ int fat_add_entries(struct inode *dir,in } EXPORT_SYMBOL(fat_add_entries); - -int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat) -{ - struct super_block *sb = dir->i_sb; - struct buffer_head *bh; - struct msdos_dir_entry *de; - __le16 date, time; - - bh = fat_extend_dir(dir); - if (IS_ERR(bh)) - return PTR_ERR(bh); - - /* zeroed out, so... */ - fat_date_unix2dos(dir->i_mtime.tv_sec, &time, &date); - de = (struct msdos_dir_entry *)bh->b_data; - memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME); - memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME); - de[0].attr = de[1].attr = ATTR_DIR; - de[0].time = de[1].time = time; - de[0].date = de[1].date = date; - if (is_vfat) { - /* extra timestamps */ - de[0].ctime = de[1].ctime = time; - de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date; - } - de[0].start = cpu_to_le16(MSDOS_I(dir)->i_logstart); - de[0].starthi = cpu_to_le16(MSDOS_I(dir)->i_logstart >> 16); - de[1].start = cpu_to_le16(MSDOS_I(parent)->i_logstart); - de[1].starthi = cpu_to_le16(MSDOS_I(parent)->i_logstart >> 16); - mark_buffer_dirty(bh); - if (sb->s_flags & MS_SYNCHRONOUS) - sync_dirty_buffer(bh); - brelse(bh); - - dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; - mark_inode_dirty(dir); - if (IS_SYNC(dir)) - fat_sync_inode(dir); - - return 0; -} - -EXPORT_SYMBOL(fat_new_dir); diff -puN fs/fat/fatent.c~fat-allocate-the-cluster-before-adding-the-directory fs/fat/fatent.c --- 25/fs/fat/fatent.c~fat-allocate-the-cluster-before-adding-the-directory Sun Mar 6 17:13:22 2005 +++ 25-akpm/fs/fat/fatent.c Sun Mar 6 17:13:22 2005 @@ -3,6 +3,7 @@ * Released under GPL v2. */ +#include #include #include @@ -577,6 +578,8 @@ error: return err; } +EXPORT_SYMBOL(fat_free_clusters); + int fat_count_free_clusters(struct super_block *sb) { struct msdos_sb_info *sbi = MSDOS_SB(sb); diff -puN fs/msdos/namei.c~fat-allocate-the-cluster-before-adding-the-directory fs/msdos/namei.c --- 25/fs/msdos/namei.c~fat-allocate-the-cluster-before-adding-the-directory Sun Mar 6 17:13:22 2005 +++ 25-akpm/fs/msdos/namei.c Sun Mar 6 17:13:22 2005 @@ -253,8 +253,8 @@ out: /***** Creates a directory entry (name is already formatted). */ static int msdos_add_entry(struct inode *dir, const unsigned char *name, - int is_dir, int is_hid, struct timespec *ts, - struct fat_slot_info *sinfo) + int is_dir, int is_hid, int cluster, + struct timespec *ts, struct fat_slot_info *sinfo) { struct msdos_dir_entry de; __le16 time, date; @@ -269,8 +269,8 @@ static int msdos_add_entry(struct inode de.time = de.ctime = time; de.date = de.cdate = de.adate = date; de.ctime_cs = 0; - de.start = 0; - de.starthi = 0; + de.start = cpu_to_le16(cluster); + de.starthi = cpu_to_le16(cluster >> 16); de.size = 0; offset = fat_add_entries(dir, 1, &sinfo->bh, &sinfo->de, &sinfo->i_pos); @@ -314,7 +314,7 @@ static int msdos_create(struct inode *di } ts = CURRENT_TIME_SEC; - err = msdos_add_entry(dir, msdos_name, 0, is_hid, &ts, &sinfo); + err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo); if (err) goto out; inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); @@ -375,7 +375,7 @@ static int msdos_mkdir(struct inode *dir struct inode *inode; unsigned char msdos_name[MSDOS_NAME]; struct timespec ts; - int err, is_hid; + int err, is_hid, cluster; lock_kernel(); @@ -392,43 +392,37 @@ static int msdos_mkdir(struct inode *dir } ts = CURRENT_TIME_SEC; - err = msdos_add_entry(dir, msdos_name, 1, is_hid, &ts, &sinfo); - if (err) + cluster = fat_alloc_new_dir(dir, &ts); + if (cluster < 0) { + err = cluster; goto out; + } + err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo); + if (err) + goto out_free; + dir->i_nlink++; + inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); + brelse(sinfo.bh); if (IS_ERR(inode)) { - brelse(sinfo.bh); err = PTR_ERR(inode); + /* the directory was completed, just return a error */ goto out; } + inode->i_nlink = 2; /* no need to mark them dirty */ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - dir->i_nlink++; - inode->i_nlink = 2; /* no need to mark them dirty */ + d_instantiate(dentry, inode); - err = fat_new_dir(inode, dir, 0); - if (err) - goto mkdir_error; - brelse(sinfo.bh); + unlock_kernel(); + return 0; - d_instantiate(dentry, inode); +out_free: + fat_free_clusters(dir, cluster); out: unlock_kernel(); return err; - -mkdir_error: - inode->i_nlink = 0; - inode->i_ctime = dir->i_ctime = dir->i_mtime = ts; - dir->i_nlink--; - mark_inode_dirty(inode); - mark_inode_dirty(dir); - sinfo.de->name[0] = DELETED_FLAG; - mark_buffer_dirty(sinfo.bh); - brelse(sinfo.bh); - fat_detach(inode); - iput(inode); - goto out; } /***** Unlink a file */ @@ -529,7 +523,7 @@ static int do_msdos_rename(struct inode } fat_detach(new_inode); } else { - err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, + err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0, &ts, &sinfo); if (err) goto out; diff -puN fs/vfat/namei.c~fat-allocate-the-cluster-before-adding-the-directory fs/vfat/namei.c --- 25/fs/vfat/namei.c~fat-allocate-the-cluster-before-adding-the-directory Sun Mar 6 17:13:22 2005 +++ 25-akpm/fs/vfat/namei.c Sun Mar 6 17:13:22 2005 @@ -658,7 +658,7 @@ out_free: } static int vfat_add_entry(struct inode *dir, struct qstr *qname, int is_dir, - struct timespec *ts, + int cluster, struct timespec *ts, struct fat_slot_info *sinfo) { struct super_block *sb = dir->i_sb; @@ -678,7 +678,7 @@ static int vfat_add_entry(struct inode * if (slots == NULL) return -ENOMEM; - err = vfat_build_slots(dir, qname->name, len, is_dir, 0, ts, + err = vfat_build_slots(dir, qname->name, len, is_dir, cluster, ts, slots, &nr_slots); if (err) goto cleanup; @@ -787,9 +787,11 @@ static int vfat_create(struct inode *dir lock_kernel(); ts = CURRENT_TIME_SEC; - err = vfat_add_entry(dir, &dentry->d_name, 0, &ts, &sinfo); + err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo); if (err) goto out; + dir->i_version++; + inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); brelse(sinfo.bh); if (IS_ERR(inode)) { @@ -800,7 +802,6 @@ static int vfat_create(struct inode *dir inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - dir->i_version++; dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry, inode); out: @@ -866,50 +867,48 @@ out: static int vfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct super_block *sb = dir->i_sb; - struct inode *inode = NULL; + struct inode *inode; struct fat_slot_info sinfo; struct timespec ts; - int err; + int err, cluster; lock_kernel(); ts = CURRENT_TIME_SEC; - err = vfat_add_entry(dir, &dentry->d_name, 1, &ts, &sinfo); - if (err) + cluster = fat_alloc_new_dir(dir, &ts); + if (cluster < 0) { + err = cluster; goto out; + } + err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &ts, &sinfo); + if (err) + goto out_free; + dir->i_version++; + dir->i_nlink++; + inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); + brelse(sinfo.bh); if (IS_ERR(inode)) { err = PTR_ERR(inode); - goto out_brelse; + /* the directory was completed, just return a error */ + goto out; } inode->i_version++; + inode->i_nlink = 2; /* no need to mark them dirty */ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - dir->i_version++; - dir->i_nlink++; - inode->i_nlink = 2; /* no need to mark them dirty */ - err = fat_new_dir(inode, dir, 1); - if (err) - goto mkdir_failed; - dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry, inode); -out_brelse: - brelse(sinfo.bh); + + unlock_kernel(); + return 0; + +out_free: + fat_free_clusters(dir, cluster); out: unlock_kernel(); return err; - -mkdir_failed: - inode->i_nlink = 0; - inode->i_mtime = inode->i_atime = ts; - fat_detach(inode); - mark_inode_dirty(inode); - fat_remove_entries(dir, &sinfo); /* and releases bh */ - iput(inode); - dir->i_nlink--; - goto out; } static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry, @@ -959,8 +958,8 @@ static int vfat_rename(struct inode *old } fat_detach(new_inode); } else { - err = vfat_add_entry(new_dir, &new_dentry->d_name, is_dir, &ts, - &sinfo); + err = vfat_add_entry(new_dir, &new_dentry->d_name, is_dir, 0, + &ts, &sinfo); if (err) goto out; brelse(sinfo.bh); diff -puN include/linux/msdos_fs.h~fat-allocate-the-cluster-before-adding-the-directory include/linux/msdos_fs.h --- 25/include/linux/msdos_fs.h~fat-allocate-the-cluster-before-adding-the-directory Sun Mar 6 17:13:22 2005 +++ 25-akpm/include/linux/msdos_fs.h Sun Mar 6 17:13:22 2005 @@ -327,13 +327,13 @@ extern int fat_search_long(struct inode 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_new_dir(struct inode *dir, struct inode *parent, int is_vfat); 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, struct fat_slot_info *sinfo); 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_remove_entries(struct inode *dir, struct fat_slot_info *sinfo); /* fat/fatent.c */ _