patch-2.3.2 linux/fs/msdos/namei.c

Next file: linux/fs/ncpfs/dir.c
Previous file: linux/fs/msdos/msdosfs_syms.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.1/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
@@ -3,6 +3,7 @@
  *
  *  Written 1992,1993 by Werner Almesberger
  *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
+ *  Rewritten for constant inumbers 1999 by Al Viro
  */
 
 
@@ -43,43 +44,24 @@
 static char bad_if_strict[] = "+=,; ";
 #endif
 
+/* Must die */
 void msdos_put_super(struct super_block *sb)
 {
 	fat_put_super(sb);
-	MOD_DEC_USE_COUNT;
 }
 
-struct super_operations msdos_sops = { 
-	msdos_read_inode,
-	fat_write_inode,
-	fat_put_inode,
-	fat_delete_inode,
-	fat_notify_change,
-	msdos_put_super,
-	NULL, /* added in 0.96c */
-	fat_statfs,
-	NULL
-};
-
 /***** Formats an MS-DOS file name. Rejects invalid names. */
 static int msdos_format_name(char conv,const char *name,int len,
-	char *res,int dot_dirs,char dotsOK)
+	char *res,char dotsOK)
 	/* conv is relaxed/normal/strict, name is proposed name,
 	 * len is the length of the proposed name, res is the result name,
-	 * dot_dirs is . and .. are OK, dotsOK is if hidden files get dots.
+	 * dotsOK is if hidden files get dots.
 	 */
 {
 	char *walk;
 	const char **reserved;
 	unsigned char c;
 	int space;
-
-	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
-		if (!dot_dirs) return -EEXIST;
-		memset(res+1,' ',10);
-		while (len--) *res++ = '.';
-		return 0;
-	}
 	if (name[0] == '.') {  /* dotfile because . and .. already done */
 		if (!dotsOK) return -EINVAL;
 		/* Get rid of dot - test for it elsewhere */
@@ -142,32 +124,31 @@
 	return 0;
 }
 
-
 /***** Locates a directory entry.  Uses unformatted name. */
 static int msdos_find(struct inode *dir,const char *name,int len,
     struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
 {
 	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,
-				name,len, msdos_name,1,dotsOK);
+				name,len, msdos_name,dotsOK);
 	if (res < 0)
 		return -ENOENT;
-	if((name[0]=='.') && dotsOK){
-	    switch(len){
-		case  0: panic("Empty name in msdos_find!");
-		case  1: scantype = SCAN_ANY;				break;
-		case  2: scantype = ((name[1]=='.')?SCAN_ANY:SCAN_HID); break;
-		default: scantype = SCAN_HID;
-	    }
-	} else {
-	    scantype = (dotsOK ? SCAN_NOTHID : SCAN_ANY);
+	res = fat_scan(dir,msdos_name,bh,de,ino);
+	if (!res && dotsOK) {
+		if (name[0]=='.') {
+			if (!((*de)->attr & ATTR_HIDDEN))
+				res = -ENOENT;
+		} else {
+			if ((*de)->attr & ATTR_HIDDEN)
+				res = -ENOENT;
+		}
 	}
-	return fat_scan(dir,msdos_name,bh,de,ino,scantype);
+	return res;
+
 }
 
 /*
@@ -183,7 +164,7 @@
 	char msdos_name[MSDOS_NAME];
 	
 	error = msdos_format_name(options->name_check, qstr->name, qstr->len,
-					msdos_name, 1, options->dotsOK);
+					msdos_name, options->dotsOK);
 	if (!error)
 		qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
 	return 0;
@@ -200,11 +181,11 @@
 	char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
 
 	error = msdos_format_name(options->name_check, a->name, a->len,
-					a_msdos_name, 1, options->dotsOK);
+					a_msdos_name, options->dotsOK);
 	if (error)
 		goto old_compare;
 	error = msdos_format_name(options->name_check, b->name, b->len,
-					b_msdos_name, 1, options->dotsOK);
+					b_msdos_name, options->dotsOK);
 	if (error)
 		goto old_compare;
 	error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
@@ -228,26 +209,9 @@
 	NULL
 };
 
-struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
-{
-	struct super_block *res;
-
-	MOD_INC_USE_COUNT;
-
-	MSDOS_SB(sb)->options.isvfat = 0;
-	sb->s_op = &msdos_sops;
-	res = fat_read_super(sb, data, silent);
-	if (res == NULL)
-		goto out_fail;
-	sb->s_root->d_op = &msdos_dentry_operations;
-	return res;
-
-out_fail:
-	sb->s_dev = 0;
-	MOD_DEC_USE_COUNT;
-	return NULL;
-}
-
+/*
+ * AV. Wrappers for FAT sb operations. Is it wise?
+ */
 
 /***** Get inode using directory and name */
 struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry)
@@ -255,7 +219,7 @@
 	struct super_block *sb = dir->i_sb;
 	struct inode *inode = NULL;
 	struct msdos_dir_entry *de;
-	struct buffer_head *bh;
+	struct buffer_head *bh = NULL;
 	int ino,res;
 	
 	PRINTK (("msdos_lookup\n"));
@@ -269,76 +233,52 @@
 		goto add;
 	if (res < 0)
 		goto out;
-	if (bh)
-		fat_brelse(sb, bh);
-
-	/* 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)) {
-		printk(KERN_WARNING "msdos_lookup: foreign inode??\n");
-	}
-	/* 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);
+	inode = fat_build_inode(sb, de, ino, &res);
+	if (res)
 		goto out;
-	}
 add:
 	d_add(dentry, inode);
 	res = 0;
 out:
+	if (bh)
+		fat_brelse(sb, bh);
 	return ERR_PTR(res);
 }
 
-
 /***** Creates a directory entry (name is already formatted). */
-static int msdos_create_entry(struct inode *dir, const char *name,
-    int is_dir, int is_hid, struct inode **result)
+static int msdos_add_entry(struct inode *dir, const char *name,
+			   struct buffer_head **bh,
+			   struct msdos_dir_entry **de,
+			   int *ino,
+			   int is_dir, int is_hid)
 {
 	struct super_block *sb = dir->i_sb;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *de;
-	int res,ino;
+	int res;
 
-	*result = NULL;
-	if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
-		if (res != -ENOENT) return res;
-		if ((dir->i_ino == MSDOS_ROOT_INO) &&
-		    (MSDOS_SB(sb)->fat_bits != 32))
-			return -ENOSPC;
-		if ((res = fat_add_cluster(dir)) < 0) return res;
-		if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
-	}
+	if ((res = fat_add_entries(dir, 1, bh, de, ino))<0)
+		return res;
 	/*
 	 * XXX all times should be set by caller upon successful completion.
 	 */
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(dir);
-	memcpy(de->name,name,MSDOS_NAME);
-	de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
-	de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
-	de->start = 0;
-	de->starthi = 0;
-	fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
-	de->size = 0;
-	fat_mark_buffer_dirty(sb, bh, 1);
-
-	if ((*result = iget(dir->i_sb,ino)) != NULL)
-		msdos_read_inode(*result);
-	fat_brelse(sb, bh);
-	if (!*result) return -EIO;
-	(*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
-	    CURRENT_TIME;
-	mark_inode_dirty(*result);
+	memcpy((*de)->name,name,MSDOS_NAME);
+	(*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+	if (is_hid)
+		(*de)->attr |= ATTR_HIDDEN;
+	(*de)->start = 0;
+	(*de)->starthi = 0;
+	fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
+	(*de)->size = 0;
+	fat_mark_buffer_dirty(sb, *bh, 1);
 	return 0;
 }
 
-/***** Create a file or directory */
+/*
+ * AV. Huh??? It's exported. Oughtta check usage.
+ */
+
+/***** Create a file */
 int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
 {
 	struct super_block *sb = dir->i_sb;
@@ -350,79 +290,27 @@
 
 	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);
+				msdos_name, MSDOS_SB(sb)->options.dotsOK);
 	if (res < 0)
 		return res;
 	is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
-	fat_lock_creation();
-	/* Scan for existing file twice, so that creating a file fails
-	 * with -EINVAL if the other (dotfile/nondotfile) exists.
-	 * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE?
-	 */
-	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
-		fat_unlock_creation();
-		fat_brelse(sb, bh);
-		return is_hid ? -EEXIST : -EINVAL;
- 	}
-	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
-		fat_unlock_creation();
+	/* Have to do it due to foo vs. .foo conflicts */
+	if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
 		fat_brelse(sb, bh);
-		return is_hid ? -EINVAL : -EEXIST;
+		return -EINVAL;
  	}
-	res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,
-				 &inode);
-	fat_unlock_creation();
-	if (!res)
-		d_instantiate(dentry, inode);
-	return res;
-}
-
-
-#ifdef DEBUG
-
-static void dump_fat(struct super_block *sb,int start)
-{
-	printk("[");
-	while (start) {
-		printk("%d ",start);
-        	start = fat_access(sb,start,-1);
-		if (!start) {
-			printk("ERROR");
-			break;
-		}
-		if (start == -1) break;
-	}
-	printk("]\n");
-}
-
-#endif
-
-/***** See if directory is empty */
-static int msdos_empty(struct inode *dir)
-{
-	loff_t pos;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *de;
-	int result = 0;
-
-	pos = 0;
-	bh = NULL;
-	while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
-		/* Ignore vfat longname entries */
-		if (de->attr == ATTR_EXT)
-			continue;
-		if (!IS_FREE(de->name) && 
-		    strncmp(de->name,MSDOS_DOT   , MSDOS_NAME) &&
-		    strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
-			result = -ENOTEMPTY;
-			break;
-		}
-	}
-	if (bh)
-		fat_brelse(dir->i_sb, bh);
-
-	return result;
+	inode = NULL;
+	res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 0, is_hid);
+	if (res)
+		return res;
+	inode = fat_build_inode(dir->i_sb, de, ino, &res);
+	fat_brelse(sb, bh);
+	if (!inode)
+		return res;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	d_instantiate(dentry, inode);
+	return 0;
 }
 
 /***** Remove a directory */
@@ -446,23 +334,19 @@
 	res = -EBUSY;
 	if (!list_empty(&dentry->d_hash))
 		goto rmdir_done;
-	res = msdos_empty(inode);
+	res = fat_dir_empty(inode);
 	if (res)
 		goto rmdir_done;
 
+	de->name[0] = DELETED_FLAG;
+	fat_mark_buffer_dirty(sb, bh, 1);
+	fat_detach(inode);
 	inode->i_nlink = 0;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_nlink--;
 	mark_inode_dirty(inode);
 	mark_inode_dirty(dir);
-	/*
-	 * Do the d_delete before any blocking operations.
-	 * We must make a negative dentry, as the FAT code
-	 * apparently relies on the inode being iput().
-	 */
 	d_delete(dentry);
-	de->name[0] = DELETED_FLAG;
-	fat_mark_buffer_dirty(sb, bh, 1);
 	res = 0;
 
 rmdir_done:
@@ -476,77 +360,94 @@
 	struct super_block *sb = dir->i_sb;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
-	struct inode *inode,*dot;
-	int ino,res,is_hid;
+	struct inode *inode;
+	int res,is_hid;
 	char msdos_name[MSDOS_NAME];
+	struct buffer_head *bh1;
+	struct msdos_dir_entry *de1;
+	int ino;
 
 	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);
+				msdos_name,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)
+	/* foo vs .foo situation */
+	if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0)
 		goto out_exist;
 
-	res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode);
-	if (res < 0)
+	res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 1, is_hid);
+	if (res)
 		goto out_unlock;
+	inode = fat_build_inode(dir->i_sb, de, ino, &res);
+	if (!inode) {
+		fat_brelse(sb, bh);
+		goto out_unlock;
+	}
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	res = 0;
 
 	dir->i_nlink++;
 	inode->i_nlink = 2; /* no need to mark them dirty */
 
-	if ((res = fat_add_cluster(inode)) < 0)
+	if (!(bh1 = fat_add_cluster1(inode))) {
+		res = -ENOSPC;
 		goto mkdir_error;
-	if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
-		goto mkdir_error;
-	dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
-	MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
-	MSDOS_I(dot)->i_logstart = MSDOS_I(inode)->i_logstart;
-	dot->i_nlink = inode->i_nlink;
-	mark_inode_dirty(dot);
-	iput(dot);
+	}
+	fat_brelse(sb, bh);
+	de1 = (struct msdos_dir_entry *)bh1->b_data;
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+	mark_inode_dirty(inode);
 
-	if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
-		goto mkdir_error;
-	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;
-	dot->i_nlink = dir->i_nlink;
-	mark_inode_dirty(dot);
-	iput(dot);
+	memcpy(de1->name,MSDOS_DOT,MSDOS_NAME);
+	de1->attr = ATTR_DIR;
+	de1->start = CT_LE_W(MSDOS_I(inode)->i_logstart);
+	de1->starthi = CT_LE_W(MSDOS_I(inode)->i_logstart >> 16);
+	fat_date_unix2dos(inode->i_mtime,&de1->time,&de1->date);
+	de1->size = 0;
+	de1->time = CT_LE_W(de1->time);
+	de1->date = CT_LE_W(de1->date);
+	de1++;
+	memcpy(de1->name,MSDOS_DOTDOT,MSDOS_NAME);
+	de1->attr = ATTR_DIR;
+	de1->start = CT_LE_W(MSDOS_I(dir)->i_logstart);
+	de1->starthi = CT_LE_W(MSDOS_I(dir)->i_logstart >> 16);
+	fat_date_unix2dos(dir->i_mtime,&de1->time,&de1->date);
+	de1->size = 0;
+	de1->time = CT_LE_W(de1->time);
+	de1->date = CT_LE_W(de1->date);
+	fat_mark_buffer_dirty(sb, bh1, 1);
+	fat_brelse(sb, bh1);
 	d_instantiate(dentry, inode);
 	res = 0;
 
 out_unlock:
-	fat_unlock_creation();
 	return res;
 
 mkdir_error:
 	printk("msdos_mkdir: error=%d, attempting cleanup\n", res);
-	bh = NULL;
-	fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY);
 	inode->i_nlink = 0;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_nlink--;
 	mark_inode_dirty(inode);
 	mark_inode_dirty(dir);
-	iput(inode);
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
 	fat_brelse(sb, bh);
+	fat_detach(inode);
+	iput(inode);
 	goto out_unlock;
 
 out_exist:
 	fat_brelse(sb, bh);
-	res = -EEXIST;
+	res = -EINVAL;
 	goto out_unlock;
 }
 
 /***** Unlink a file */
-static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc)
+int msdos_unlink( struct inode *dir, struct dentry *dentry)
 {
 	struct super_block *sb = dir->i_sb;
 	struct inode *inode = dentry->d_inode;
@@ -559,39 +460,21 @@
 			&bh, &de, &ino);
 	if (res < 0)
 		goto unlink_done;
-	res = -EPERM;
-	if (!S_ISREG(inode->i_mode) && nospc)
-		goto unlink_done;
-	/* N.B. check for busy files? */
 
+	de->name[0] = DELETED_FLAG;
+	fat_mark_buffer_dirty(sb, bh, 1);
+	fat_detach(inode);
+	fat_brelse(sb, bh);
 	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);
 	res = 0;
 unlink_done:
-	fat_brelse(sb, bh);
 	return res;
 }
 
-/***** Unlink, as called for msdosfs */
-int msdos_unlink(struct inode *dir,struct dentry *dentry)
-{
-	return msdos_unlinkx (dir,dentry,1);
-}
-
-/***** Unlink, as called for umsdosfs */
-int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry)
-{
-	return msdos_unlinkx (dir,dentry,0);
-}
-
-/* Now we could merge it with msdos_rename_same. Later */
-/***** Rename across directories - a nonphysical move */
 static int do_msdos_rename(struct inode *old_dir, char *old_name,
     struct dentry *old_dentry,
     struct inode *new_dir,char *new_name, struct dentry *new_dentry,
@@ -599,157 +482,97 @@
     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,*dotdot_inode;
-	int new_ino,free_ino,dotdot_ino;
-	int error, exists;
+	struct buffer_head *new_bh=NULL,*dotdot_bh=NULL;
+	struct msdos_dir_entry *new_de,*dotdot_de;
+	struct inode *old_inode,*new_inode;
+	int new_ino,dotdot_ino;
+	int error;
+	int is_dir;
 
 	old_inode = old_dentry->d_inode;
-	if (old_dir==new_dir && !strncmp(old_name, new_name, MSDOS_NAME))
-		goto set_hid;
-	error = -ENOENT;
-	if (*(unsigned char *) old_de->name == DELETED_FLAG)
-		goto out;
+	new_inode = new_dentry->d_inode;
+	is_dir = S_ISDIR(old_inode->i_mode);
 
-	/* find free spot */
-	if (new_dir!=old_dir)
-		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 (fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino)>=0 &&!new_inode)
+		goto degenerate_case;
+	if (is_dir) {
+		if (new_inode) {
+			error = fat_dir_empty(new_inode);
 			if (error)
 				goto out;
 		}
-
-	exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
-	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: %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 = -EPERM;
-		if ((old_de->attr & ATTR_SYS))
-			goto out_new;
-
-		if (S_ISDIR(new_inode->i_mode)) {
-			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);
-
-		new_de->name[0] = DELETED_FLAG;
-		fat_mark_buffer_dirty(sb, new_bh, 1);
-		fat_brelse(sb, new_bh);
-	}
-
-	if (old_dir==new_dir) {
-		memcpy(old_de->name, new_name, MSDOS_NAME);
-set_hid:
-		old_de->attr = is_hid
-			? (old_de->attr | ATTR_HIDDEN)
-			: (old_de->attr &~ ATTR_HIDDEN);
-		fat_mark_buffer_dirty(sb, old_bh, 1);
-		MSDOS_I(old_inode)->i_attrs = is_hid
-			? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
-			: (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
-		error = 0;
-		goto out;
-	}
-
-	/* 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);
+				&dotdot_de, &dotdot_ino);
 		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;
+			goto out;
 		}
-		error = -EIO;
-		dotdot_inode = iget(sb, dotdot_ino);
-		if (!dotdot_inode)
-			goto out_dotdot;
 	}
+	if (!new_bh) {
+		error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de,
+					&new_ino, is_dir, is_hid);
+		if (error)
+			goto out;
+	}
+	new_dir->i_version = ++event;
 
-	/*
-	 * Potential race here. It will go away when we'll switch to
-	 * sane inumbers (along with a frigging lot of other races).
-	 */
-
-	/* set new entry */
-	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);
-
-	/*
-	 * Now the tricky part. We need to change i_ino. icache ignores
-	 * i_ino for unhashed inodes, so we'll remove inode from hash,
-	 * change what we want to change and reinsert it back. NB: we
-	 * don't have to invalidate FAT cache here - all we need is to
-	 * flip i_ino in relevant cache entries. Later.
-	 */
-	remove_inode_hash(old_inode);
+	/* There we go */
 
-	fat_cache_inval_inode(old_inode);
-	old_inode->i_version = ++event;
-	MSDOS_I(old_inode)->i_binary =
-		fat_is_binary(MSDOS_SB(sb)->options.conversion, free_de->ext);
-	old_inode->i_ino = free_ino;
-	fat_mark_buffer_dirty(sb, free_bh, 1);
+	if (new_inode)
+		fat_detach(new_inode);
 	old_de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, old_bh, 1);
-
-	insert_inode_hash(old_inode);
-
-	/* a directory? */
+	fat_detach(old_inode);
+	fat_attach(old_inode, new_ino);
+	if (is_hid)
+		MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+	else
+		MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+	mark_inode_dirty(old_inode);
+	old_dir->i_version = ++event;
+	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+	mark_inode_dirty(old_dir);
+	if (new_inode) {
+		new_inode->i_nlink--;
+		new_inode->i_ctime = CURRENT_TIME;
+		mark_inode_dirty(new_inode);
+	}
 	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);
-		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);
+		old_dir->i_nlink--;
+		mark_inode_dirty(old_dir);
+		if (new_inode) {
+			new_inode->i_nlink--;
+			mark_inode_dirty(new_inode);
+		} else {
+			new_dir->i_nlink++;
+			mark_inode_dirty(new_dir);
+		}
 	}
-
 	error = 0;
-
-rename_done:
-	fat_brelse(sb, free_bh);
 out:
+	fat_brelse(sb, new_bh);
+	fat_brelse(sb, dotdot_bh);
 	return error;
 
-out_dotdot:
-	fat_brelse(sb, dotdot_bh);
-	goto rename_done;
-out_new:
-	fat_brelse(sb, new_bh);
-	goto rename_done;
+degenerate_case:
+	error = -EINVAL;
+	if (new_de!=old_de)
+		goto out;
+	if (is_hid)
+		MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+	else
+		MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+	mark_inode_dirty(old_inode);
+	old_dir->i_version = ++event;
+	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+	mark_inode_dirty(old_dir);
+	return 0;
 }
 
 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
@@ -765,27 +588,24 @@
 
 	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);
+				old_msdos_name,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);
+				new_msdos_name,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]!='.');
-	error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de,
-			&old_ino, old_hid?SCAN_HID:SCAN_NOTHID);
+	error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, &old_ino);
 	if (error < 0)
 		goto rename_done;
 
-	fat_lock_creation();
 	error = do_msdos_rename(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:
@@ -817,10 +637,29 @@
 	NULL,                   /* revalidate */
 };
 
+static void msdos_put_super_callback(struct super_block *sb)
+{
+	MOD_DEC_USE_COUNT;
+}
 
-void msdos_read_inode(struct inode *inode)
+struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
 {
-	fat_read_inode(inode, &msdos_dir_inode_operations);
+	struct super_block *res;
+
+	MOD_INC_USE_COUNT;
+
+	MSDOS_SB(sb)->options.isvfat = 0;
+	res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations);
+	if (res == NULL)
+		goto out_fail;
+	MSDOS_SB(sb)->put_super_callback=msdos_put_super_callback;
+	sb->s_root->d_op = &msdos_dentry_operations;
+	return res;
+
+out_fail:
+	sb->s_dev = 0;
+	MOD_DEC_USE_COUNT;
+	return NULL;
 }
 
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)