diff options
Diffstat (limited to 'fs/msdos/namei.c')
-rw-r--r-- | fs/msdos/namei.c | 490 |
1 files changed, 316 insertions, 174 deletions
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 87c62e4c9..7d18237e7 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -5,17 +5,15 @@ * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu> */ -#include <linux/config.h> #define __NO_VERSION__ +#include <linux/config.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/msdos_fs.h> -#include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> -#include <linux/stat.h> #include <asm/uaccess.h> @@ -149,10 +147,10 @@ static int msdos_format_name(char conv,const char *name,int len, static int msdos_find(struct inode *dir,const char *name,int len, struct buffer_head **bh,struct msdos_dir_entry **de,int *ino) { - char msdos_name[MSDOS_NAME]; int res; char dotsOK; char scantype; + char msdos_name[MSDOS_NAME]; dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK; res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, @@ -225,7 +223,9 @@ static struct dentry_operations msdos_dentry_operations = { NULL, /* d_revalidate */ msdos_hash, msdos_cmp, - NULL /* d_delete */ + NULL, /* d_delete */ + NULL, + NULL }; struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent) @@ -253,49 +253,46 @@ out_fail: int msdos_lookup(struct inode *dir,struct dentry *dentry) { struct super_block *sb = dir->i_sb; - int ino,res; + struct inode *inode = NULL; struct msdos_dir_entry *de; struct buffer_head *bh; - struct inode *inode; + int ino,res; PRINTK (("msdos_lookup\n")); dentry->d_op = &msdos_dentry_operations; - if(!dir) { /* N.B. This test is bogus -- should never happen */ - d_add(dentry, NULL); - return 0; - } + res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh, + &de, &ino); - if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) { - if(res == -ENOENT) { - d_add(dentry, NULL); - res = 0; - } - return res; - } - PRINTK (("msdos_lookup 4\n")); + if (res == -ENOENT) + goto add; + if (res < 0) + goto out; if (bh) fat_brelse(sb, bh); - PRINTK (("msdos_lookup 4.5\n")); - if (!(inode = iget(dir->i_sb,ino))) - return -EACCES; - PRINTK (("msdos_lookup 5\n")); + + /* try to get the inode */ + res = -EACCES; + inode = iget(sb, ino); + if (!inode) + goto out; if (!inode->i_sb || - (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) { - /* crossed a mount point into a non-msdos fs */ - d_add(dentry, inode); - return 0; + (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) { + printk(KERN_WARNING "msdos_lookup: foreign inode??\n"); } - if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */ + /* mkdir in progress? */ + if (MSDOS_I(inode)->i_busy) { + printk(KERN_WARNING "msdos_lookup: %s/%s busy\n", + dentry->d_parent->d_name.name, dentry->d_name.name); iput(inode); - d_add(dentry, NULL); /* N.B. Do we really want a negative? */ - return 0; + goto out; } - PRINTK (("msdos_lookup 6\n")); +add: d_add(dentry, inode); - PRINTK (("msdos_lookup 7\n")); - return 0; + res = 0; +out: + return res; } @@ -308,9 +305,6 @@ static int msdos_create_entry(struct inode *dir, const char *name, struct msdos_dir_entry *de; int res,ino; - if(!dir) - return -ENOENT; - *result = NULL; if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) { if (res != -ENOENT) return res; @@ -350,14 +344,14 @@ int msdos_create(struct inode *dir,struct dentry *dentry,int mode) struct buffer_head *bh; struct msdos_dir_entry *de; struct inode *inode; - char msdos_name[MSDOS_NAME]; int ino,res,is_hid; + char msdos_name[MSDOS_NAME]; - if (!dir) return -ENOENT; - if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, - dentry->d_name.name,dentry->d_name.len, - msdos_name,0, - MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) + res = msdos_format_name(MSDOS_SB(sb)->options.name_check, + dentry->d_name.name,dentry->d_name.len, + msdos_name,0, + MSDOS_SB(sb)->options.dotsOK); + if (res < 0) return res; is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); fat_lock_creation(); @@ -434,8 +428,8 @@ static int msdos_empty(struct inode *dir) /***** Remove a directory */ int msdos_rmdir(struct inode *dir, struct dentry *dentry) { - struct inode *inode = dentry->d_inode; struct super_block *sb = dir->i_sb; + struct inode *inode = dentry->d_inode; int res,ino; struct buffer_head *bh; struct msdos_dir_entry *de; @@ -451,15 +445,14 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry) if (dir->i_dev != inode->i_dev || dir == inode) printk("msdos_rmdir: impossible condition\n"); /* - * Prune any child dentries, then verify that - * the directory is empty and not in use. + * Check whether the directory is empty, then prune + * any child dentries and make sure it's not in use. */ - shrink_dcache_parent(dentry); res = msdos_empty(inode); if (res) goto rmdir_done; res = -EBUSY; - if (dentry->d_count > 1) { + if (!list_empty(&dentry->d_hash)) { #ifdef MSDOS_DEBUG printk("msdos_rmdir: %s/%s busy, d_count=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); @@ -481,6 +474,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); res = 0; + rmdir_done: fat_brelse(sb, bh); return res; @@ -493,28 +487,32 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) struct buffer_head *bh; struct msdos_dir_entry *de; struct inode *inode,*dot; - char msdos_name[MSDOS_NAME]; int ino,res,is_hid; + char msdos_name[MSDOS_NAME]; - if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, - dentry->d_name.name,dentry->d_name.len, - msdos_name,0, - MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) + res = msdos_format_name(MSDOS_SB(sb)->options.name_check, + dentry->d_name.name,dentry->d_name.len, + msdos_name,0, + MSDOS_SB(sb)->options.dotsOK); + if (res < 0) return res; is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); fat_lock_creation(); - if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) { - fat_unlock_creation(); - /* N.B. does this need to be released on the other path? */ - fat_brelse(sb, bh); - return -EEXIST; - } + if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) + goto out_exist; + res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode); if (res < 0) goto out_unlock; + dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ MSDOS_I(inode)->i_busy = 1; /* prevent lookups */ + /* + * Instantiate the dentry now, in case we need to cleanup. + */ + d_instantiate(dentry, inode); + if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error; if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0) @@ -525,9 +523,9 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) dot->i_nlink = inode->i_nlink; mark_inode_dirty(dot); iput(dot); + if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0) goto mkdir_error; - fat_unlock_creation(); dot->i_size = dir->i_size; MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart; @@ -535,21 +533,26 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) mark_inode_dirty(dot); MSDOS_I(inode)->i_busy = 0; iput(dot); - d_instantiate(dentry, inode); - return 0; -mkdir_error: - if (msdos_rmdir(dir,dentry) < 0) - fat_fs_panic(dir->i_sb,"rmdir in mkdir failed"); + res = 0; + out_unlock: fat_unlock_creation(); return res; + +mkdir_error: + printk("msdos_mkdir: error=%d, attempting cleanup\n", res); + if (msdos_rmdir(dir,dentry) < 0) + fat_fs_panic(dir->i_sb,"rmdir in mkdir failed"); + goto out_unlock; + +out_exist: + fat_brelse(sb, bh); + res = -EEXIST; + goto out_unlock; } /***** Unlink a file */ -static int msdos_unlinkx( - struct inode *dir, - struct dentry *dentry, - int nospc) /* Flag special file ? */ +static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc) { struct super_block *sb = dir->i_sb; struct inode *inode = dentry->d_inode; @@ -558,22 +561,25 @@ static int msdos_unlinkx( struct msdos_dir_entry *de; bh = NULL; - if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len, - &bh,&de,&ino)) < 0) + res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, + &bh, &de, &ino); + if (res < 0) goto unlink_done; res = -EPERM; if (!S_ISREG(inode->i_mode) && nospc) goto unlink_done; if (IS_IMMUTABLE(inode)) goto unlink_done; + /* N.B. check for busy files? */ + inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; MSDOS_I(inode)->i_busy = 1; mark_inode_dirty(inode); mark_inode_dirty(dir); + d_delete(dentry); /* This also frees the inode */ de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); - d_delete(dentry); /* This also frees the inode */ res = 0; unlink_done: fat_brelse(sb, bh); @@ -592,28 +598,40 @@ int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry) return msdos_unlinkx (dir,dentry,0); } +#define MSDOS_CHECK_BUSY 1 + /***** Rename within a directory */ -static int rename_same_dir(struct inode *old_dir,char *old_name, +static int msdos_rename_same(struct inode *old_dir,char *old_name, struct dentry *old_dentry, struct inode *new_dir,char *new_name,struct dentry *new_dentry, struct buffer_head *old_bh, - struct msdos_dir_entry *old_de,int old_ino,int is_hid) + struct msdos_dir_entry *old_de, int old_ino, int is_hid) { struct super_block *sb = old_dir->i_sb; struct buffer_head *new_bh; struct msdos_dir_entry *new_de; struct inode *new_inode,*old_inode; - int new_ino,exists,error; + int new_ino, exists, error; + + if (!strncmp(old_name, new_name, MSDOS_NAME)) + goto set_hid; + error = -ENOENT; + if (*(unsigned char *) old_de->name == DELETED_FLAG) + goto out; - if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid; exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0; - if (*(unsigned char *) old_de->name == DELETED_FLAG) { - if (exists) - fat_brelse(sb, new_bh); - return -ENOENT; - } if (exists) { + error = -EIO; new_inode = new_dentry->d_inode; + /* Make sure it really exists ... */ + if (!new_inode) { + printk(KERN_ERR + "msdos_rename_same: %s/%s inode NULL, ino=%d\n", + new_dentry->d_parent->d_name.name, + new_dentry->d_name.name, new_ino); + d_drop(new_dentry); + goto out_error; + } error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ? msdos_empty(new_inode) @@ -621,23 +639,46 @@ static int rename_same_dir(struct inode *old_dir,char *old_name, : (old_de->attr & ATTR_DIR) ? -EPERM : 0; - if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM; - if (error) { - fat_brelse(sb, new_bh); - return error; - } + if (error) + goto out_error; + error = -EPERM; + if ((old_de->attr & ATTR_SYS)) + goto out_error; + if (S_ISDIR(new_inode->i_mode)) { + /* make sure it's empty */ + error = msdos_empty(new_inode); + if (error) + goto out_error; +#ifdef MSDOS_CHECK_BUSY + /* check for a busy dentry */ + error = -EBUSY; + shrink_dcache_parent(new_dentry); + if (new_dentry->d_count > 1) { +printk("msdos_rename_same: %s/%s busy, count=%d\n", +new_dentry->d_parent->d_name.name, new_dentry->d_name.name, +new_dentry->d_count); + goto out_error; + } +#endif new_dir->i_nlink--; mark_inode_dirty(new_dir); } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; mark_inode_dirty(new_inode); + /* + * Make it negative if it's not busy; + * otherwise let d_move() drop it. + */ + if (new_dentry->d_count == 1) + d_delete(new_dentry); + new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); fat_brelse(sb, new_bh); } - memcpy(old_de->name,new_name,MSDOS_NAME); + memcpy(old_de->name, new_name, MSDOS_NAME); /* Update the dcache */ d_move(old_dentry, new_dentry); set_hid: @@ -650,51 +691,63 @@ set_hid: MSDOS_I(old_inode)->i_attrs = is_hid ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN) : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN); - return 0; + error = 0; +out: + return error; + +out_error: + fat_brelse(sb, new_bh); + goto out; } /***** Rename across directories - a nonphysical move */ -static int rename_diff_dir(struct inode *old_dir,char *old_name, +static int msdos_rename_diff(struct inode *old_dir, char *old_name, struct dentry *old_dentry, - struct inode *new_dir,char *new_name,struct dentry *new_dentry, + struct inode *new_dir,char *new_name, struct dentry *new_dentry, struct buffer_head *old_bh, - struct msdos_dir_entry *old_de,int old_ino,int is_hid) + struct msdos_dir_entry *old_de, int old_ino, int is_hid) { struct super_block *sb = old_dir->i_sb; struct buffer_head *new_bh,*free_bh,*dotdot_bh; struct msdos_dir_entry *new_de,*free_de,*dotdot_de; struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode; - struct dentry *walk; int new_ino,free_ino,dotdot_ino; - int error,exists; + int error, exists; - if (old_dir->i_dev != new_dir->i_dev) return -EINVAL; - if (old_ino == new_dir->i_ino) return -EINVAL; - walk = new_dentry; + error = -EINVAL; + if (old_ino == new_dir->i_ino) + goto out; /* prevent moving directory below itself */ - for (;;) { - if (walk == old_dentry) return -EINVAL; - if (walk == walk->d_parent) break; - walk = walk->d_parent; - } + if (is_subdir(new_dentry, old_dentry)) + goto out; + + error = -ENOENT; + if (*(unsigned char *) old_de->name == DELETED_FLAG) + goto out; + /* find free spot */ - while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino, - SCAN_ANY)) < 0) { - if (error != -ENOENT) return error; + while ((error = fat_scan(new_dir, NULL, &free_bh, &free_de, &free_ino, + SCAN_ANY)) < 0) { + if (error != -ENOENT) + goto out; error = fat_add_cluster(new_dir); - if (error) return error; + if (error) + goto out; } + exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0; - old_inode = old_dentry->d_inode; - if (*(unsigned char *) old_de->name == DELETED_FLAG) { - fat_brelse(sb, free_bh); - if (exists) - fat_brelse(sb, new_bh); - return -ENOENT; - } - new_inode = NULL; /* to make GCC happy */ if (exists) { /* Trash the old file! */ + error = -EIO; new_inode = new_dentry->d_inode; + /* Make sure it really exists ... */ + if (!new_inode) { + printk(KERN_ERR + "msdos_rename_diff: %s/%s inode NULL, ino=%d\n", + new_dentry->d_parent->d_name.name, + new_dentry->d_name.name, new_ino); + d_drop(new_dentry); + goto out_new; + } error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ? msdos_empty(new_inode) @@ -702,40 +755,107 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name, : (old_de->attr & ATTR_DIR) ? -EPERM : 0; - if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM; - if (error) { - fat_brelse(sb, new_bh); - return error; + if (error) + goto out_new; + error = -EPERM; + if ((old_de->attr & ATTR_SYS)) + goto out_new; + +#ifdef MSDOS_CHECK_BUSY + /* check for a busy dentry */ + error = -EBUSY; + if (new_dentry->d_count > 1) { + shrink_dcache_parent(new_dentry); + if (new_dentry->d_count > 1) { +printk("msdos_rename_diff: target %s/%s busy, count=%d\n", +new_dentry->d_parent->d_name.name, new_dentry->d_name.name, +new_dentry->d_count); + goto out_new; + } + } +#endif + if (S_ISDIR(new_inode->i_mode)) { + /* make sure it's empty */ + error = msdos_empty(new_inode); + if (error) + goto out_new; + new_dir->i_nlink--; + mark_inode_dirty(new_dir); } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; mark_inode_dirty(new_inode); + /* + * Make it negative if it's not busy; + * otherwise let d_move() drop it. + */ + if (new_dentry->d_count == 1) + d_delete(new_dentry); new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); + fat_brelse(sb, new_bh); } - memcpy(free_de,old_de,sizeof(struct msdos_dir_entry)); - memcpy(free_de->name,new_name,MSDOS_NAME); + + old_inode = old_dentry->d_inode; + /* Get the dotdot inode if we'll need it ... */ + dotdot_bh = NULL; + dotdot_inode = NULL; + if (S_ISDIR(old_inode->i_mode)) { + error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh, + &dotdot_de, &dotdot_ino, SCAN_ANY); + if (error < 0) { + printk(KERN_WARNING + "MSDOS: %s/%s, get dotdot failed, ret=%d\n", + old_dentry->d_parent->d_name.name, + old_dentry->d_name.name, error); + goto rename_done; + } + error = -EIO; + dotdot_inode = iget(sb, dotdot_ino); + if (!dotdot_inode) + goto out_dotdot; + } + + /* get an inode for the new name */ + memcpy(free_de, old_de, sizeof(struct msdos_dir_entry)); + memcpy(free_de->name, new_name, MSDOS_NAME); free_de->attr = is_hid ? (free_de->attr|ATTR_HIDDEN) : (free_de->attr&~ATTR_HIDDEN); - if (!(free_inode = iget(new_dir->i_sb,free_ino))) { - free_de->name[0] = DELETED_FLAG; - /* - * Don't mark free_bh as dirty. Both states - * are supposed to be equivalent. - */ - fat_brelse(sb, free_bh); - if (exists) - fat_brelse(sb, new_bh); - return -EIO; - } - if (exists && S_ISDIR(new_inode->i_mode)) { - new_dir->i_nlink--; - mark_inode_dirty(new_dir); - } + + error = -EIO; + free_inode = iget(sb, free_ino); + if (!free_inode) + goto out_iput; + /* make sure it's not busy! */ + if (MSDOS_I(free_inode)->i_busy) + printk(KERN_ERR "msdos_rename_diff: new inode %ld busy!\n", + (ino_t) free_ino); + if (!list_empty(&free_inode->i_dentry)) + printk("msdos_rename_diff: free inode has aliases??\n"); msdos_read_inode(free_inode); + /* + * Make sure the old dentry isn't busy, + * as we need to change inodes ... + */ + error = -EBUSY; + if (old_dentry->d_count > 1) { + shrink_dcache_parent(old_dentry); + if (old_dentry->d_count > 1) { +printk("msdos_rename_diff: source %s/%s busy, count=%d\n", +old_dentry->d_parent->d_name.name, old_dentry->d_name.name, +old_dentry->d_count); + goto out_iput; + } + } + + /* keep the inode for a bit ... */ + old_inode->i_count++; + d_delete(old_dentry); + free_inode->i_mode = old_inode->i_mode; + free_inode->i_nlink = old_inode->i_nlink; free_inode->i_size = old_inode->i_size; free_inode->i_blocks = old_inode->i_blocks; free_inode->i_mtime = old_inode->i_mtime; @@ -747,49 +867,62 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name, MSDOS_I(free_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart; MSDOS_I(free_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs; - /* Detach d_alias from old inode and attach to new inode */ - list_del(&old_dentry->d_alias); + /* release the old inode's resources */ + MSDOS_I(old_inode)->i_start = 0; + MSDOS_I(old_inode)->i_logstart = 0; + old_inode->i_nlink = 0; + + /* + * Install the new inode ... + */ d_instantiate(old_dentry, free_inode); - iput(old_inode); + fat_mark_buffer_dirty(sb, free_bh, 1); fat_cache_inval_inode(old_inode); mark_inode_dirty(old_inode); old_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, old_bh, 1); - fat_mark_buffer_dirty(sb, free_bh, 1); + iput(old_inode); - if (exists) { - /* free_inode is put after putting new_inode and old_inode */ - fat_brelse(sb, new_bh); - } - if (S_ISDIR(old_inode->i_mode)) { - if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh, - &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done; - if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) { - fat_brelse(sb, dotdot_bh); - error = -EIO; - goto rename_done; - } + /* a directory? */ + if (dotdot_bh) { MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart; dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart); dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16); - mark_inode_dirty(dotdot_inode); - fat_mark_buffer_dirty(sb, dotdot_bh, 1); old_dir->i_nlink--; new_dir->i_nlink++; /* no need to mark them dirty */ dotdot_inode->i_nlink = new_dir->i_nlink; + mark_inode_dirty(dotdot_inode); iput(dotdot_inode); + fat_mark_buffer_dirty(sb, dotdot_bh, 1); fat_brelse(sb, dotdot_bh); } /* Update the dcache */ d_move(old_dentry, new_dentry); error = 0; + rename_done: fat_brelse(sb, free_bh); +out: return error; + +out_iput: + free_de->name[0] = DELETED_FLAG; + /* + * Don't mark free_bh as dirty. Both states + * are supposed to be equivalent. + */ + iput(free_inode); /* may be NULL */ + iput(dotdot_inode); +out_dotdot: + fat_brelse(sb, dotdot_bh); + goto rename_done; +out_new: + fat_brelse(sb, new_bh); + goto rename_done; } /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */ @@ -797,36 +930,45 @@ int msdos_rename(struct inode *old_dir,struct dentry *old_dentry, struct inode *new_dir,struct dentry *new_dentry) { struct super_block *sb = old_dir->i_sb; - char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME]; struct buffer_head *old_bh; struct msdos_dir_entry *old_de; - int old_ino,error; + int old_ino, error; int is_hid,old_hid; /* if new file and old file are hidden */ - - if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check, - old_dentry->d_name.name, - old_dentry->d_name.len,old_msdos_name,1, - MSDOS_SB(old_dir->i_sb)->options.dotsOK)) - < 0) goto rename_done; - if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check, - new_dentry->d_name.name, - new_dentry->d_name.len,new_msdos_name,0, - MSDOS_SB(new_dir->i_sb)->options.dotsOK)) - < 0) goto rename_done; - is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.'); + char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME]; + + error = -EINVAL; + if (sb != new_dir->i_sb) + goto rename_done; + error = msdos_format_name(MSDOS_SB(sb)->options.name_check, + old_dentry->d_name.name, old_dentry->d_name.len, + old_msdos_name, 1,MSDOS_SB(sb)->options.dotsOK); + if (error < 0) + goto rename_done; + error = msdos_format_name(MSDOS_SB(sb)->options.name_check, + new_dentry->d_name.name, new_dentry->d_name.len, + new_msdos_name, 0,MSDOS_SB(sb)->options.dotsOK); + if (error < 0) + goto rename_done; + + is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.'); old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.'); - if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de, - &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done; + error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, + &old_ino, old_hid?SCAN_HID:SCAN_NOTHID); + if (error < 0) + goto rename_done; + fat_lock_creation(); if (old_dir == new_dir) - error = rename_same_dir(old_dir,old_msdos_name,old_dentry, - new_dir,new_msdos_name,new_dentry, - old_bh,old_de,old_ino,is_hid); - else error = rename_diff_dir(old_dir,old_msdos_name,old_dentry, - new_dir,new_msdos_name,new_dentry, - old_bh,old_de,old_ino,is_hid); + error = msdos_rename_same(old_dir, old_msdos_name, old_dentry, + new_dir, new_msdos_name, new_dentry, + old_bh, old_de, (ino_t)old_ino, is_hid); + else + error = msdos_rename_diff(old_dir, old_msdos_name, old_dentry, + new_dir, new_msdos_name, new_dentry, + old_bh, old_de, (ino_t)old_ino, is_hid); fat_unlock_creation(); fat_brelse(sb, old_bh); + rename_done: return error; } @@ -848,7 +990,7 @@ struct inode_operations msdos_dir_inode_operations = { NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ - fat_bmap, /* bmap */ + NULL, /* bmap */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ |