Commit b1379652 authored by Hirofumi Ogawa's avatar Hirofumi Ogawa Committed by Linus Torvalds

[PATCH] FAT: Allocate the cluster before adding the directory entry

With this change, ->mkdir() uses the correct updating order.
Signed-off-by: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 391517ec
......@@ -869,6 +869,118 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo)
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,int slots, struct buffer_head **bh,
}
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);
......@@ -3,6 +3,7 @@
* Released under GPL v2.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
......@@ -577,6 +578,8 @@ int fat_free_clusters(struct inode *inode, int cluster)
return err;
}
EXPORT_SYMBOL(fat_free_clusters);
int fat_count_free_clusters(struct super_block *sb)
{
struct msdos_sb_info *sbi = MSDOS_SB(sb);
......
......@@ -253,8 +253,8 @@ static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
/***** 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 *dir, const unsigned char *name,
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 *dir, struct dentry *dentry, int mode,
}
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 dentry *dentry, int mode)
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, struct dentry *dentry, int mode)
}
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 *old_dir, unsigned char *old_name,
}
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;
......
......@@ -658,7 +658,7 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name,
}
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 *dir, struct qstr *qname, int is_dir,
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, struct dentry *dentry, int mode,
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, struct dentry *dentry, int mode,
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 @@ static int vfat_unlink(struct inode *dir, struct dentry *dentry)
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_dir, struct dentry *old_dentry,
}
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);
......
......@@ -327,13 +327,13 @@ extern int fat_search_long(struct inode *inode, const unsigned char *name,
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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment