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

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

diff -u --recursive --new-file v2.1.62/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
@@ -21,9 +21,10 @@
 
 #include "../fat/msbuffer.h"
 
+#define MSDOS_PARANOIA 1	
+/* #define MSDOS_DEBUG 1 */	
 #define PRINTK(x)
 
-
 /* MS-DOS "device special files" */
 
 static const char *reserved_names[] = {
@@ -256,7 +257,7 @@
 
 	dentry->d_op = &msdos_dentry_operations;
 
-	if(!dir) {
+	if(!dir) { /* N.B. This test is bogus -- should never happen */
 		d_add(dentry, NULL);
 		return 0;
 	}
@@ -283,7 +284,7 @@
 	}
 	if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */
 		iput(inode);
-		d_add(dentry, NULL);
+		d_add(dentry, NULL); /* N.B. Do we really want a negative? */
 		return 0;
 	}
 	PRINTK (("msdos_lookup 6\n"));
@@ -292,8 +293,7 @@
 		iput(inode);
 		if (!(inode = iget(next->i_sb,next->i_ino))) {
 			fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
-			d_add(dentry, NULL);
-			return -ENOENT;
+			return -ENOENT; /* N.B. Maybe ENOMEM is better? */
 		}
 	}
 	PRINTK (("msdos_lookup 7\n"));
@@ -382,7 +382,8 @@
 	res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,
 				 &inode);
 	fat_unlock_creation();
-	d_instantiate(dentry, inode);
+	if (!res)
+		d_instantiate(dentry, inode);
 	return res;
 }
 
@@ -409,61 +410,77 @@
 /***** See if directory is empty */
 static int msdos_empty(struct inode *dir)
 {
-	struct super_block *sb = dir->i_sb;
 	loff_t pos;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
+	int result = 0;
 
-	if (dir->i_count > 1)
-		return -EBUSY;
 	if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
 		pos = 0;
 		bh = NULL;
-		while (fat_get_entry(dir,&pos,&bh,&de) > -1)
-			if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
-			    MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
-			    MSDOS_NAME)) {
-				fat_brelse(sb, bh);
-				return -ENOTEMPTY;
+		while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
+			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(sb, bh);
+			fat_brelse(dir->i_sb, bh);
 	}
-	return 0;
+	return result;
 }
 
 /***** Remove a directory */
-int msdos_rmdir(struct inode *dir,struct dentry *dentry)
+int msdos_rmdir(struct inode *dir, struct dentry *dentry)
 {
+	struct inode *inode = dentry->d_inode;
 	struct super_block *sb = dir->i_sb;
 	int res,ino;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
-	struct inode *inode;
 
 	bh = NULL;
-	inode = NULL;
-	res = -EPERM;
-	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 rmdir_done;
-	inode = dentry->d_inode;
 	res = -ENOTDIR;
-	if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
-	res = -EBUSY;
+	if (!S_ISDIR(inode->i_mode))
+		goto rmdir_done;
 	if (dir->i_dev != inode->i_dev || dir == inode)
-	  goto rmdir_done;
+		printk("msdos_rmdir: impossible condition\n");
+	/*
+	 * Prune any child dentries, then verify that
+	 * the directory is empty and not in use.
+	 */
+	shrink_dcache_parent(dentry);
 	res = msdos_empty(inode);
 	if (res)
 		goto rmdir_done;
+	res = -EBUSY;
+	if (dentry->d_count > 1) {
+#ifdef MSDOS_DEBUG
+printk("rename_diff_dir: %s/%s busy, d_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+#endif
+		goto rmdir_done;
+	}
+
 	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);
-	d_delete(dentry);
 	res = 0;
 rmdir_done:
 	fat_brelse(sb, bh);
@@ -489,18 +506,18 @@
 	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 ((res = msdos_create_entry(dir,msdos_name,1,is_hid,
-				      &inode)) < 0) {
-		fat_unlock_creation();
-		return res;
-	}
+	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 */
-	if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
+	if ((res = fat_add_cluster(inode)) < 0)
+		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 */
@@ -524,6 +541,7 @@
 mkdir_error:
 	if (msdos_rmdir(dir,dentry) < 0)
 		fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+out_unlock:
 	fat_unlock_creation();
 	return res;
 }
@@ -535,25 +553,20 @@
 	int nospc)	/* Flag special file ? */
 {
 	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;
-	struct inode *inode;
 
 	bh = NULL;
-	inode = NULL;
 	if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
 			      &bh,&de,&ino)) < 0)
 		goto unlink_done;
-	inode = dentry->d_inode;
-	if (!S_ISREG(inode->i_mode) && nospc){
-		res = -EPERM;
+	res = -EPERM;
+	if (!S_ISREG(inode->i_mode) && nospc)
 		goto unlink_done;
-	}
-	if (IS_IMMUTABLE(inode)){
-		res = -EPERM;
+	if (IS_IMMUTABLE(inode))
 		goto unlink_done;
-	}
 	inode->i_nlink = 0;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	MSDOS_I(inode)->i_busy = 1;
@@ -562,6 +575,7 @@
 	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);
 	return res;
@@ -707,7 +721,10 @@
 		: (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. */
+		/*
+		 * 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);
@@ -718,19 +735,53 @@
 		mark_inode_dirty(new_dir);
 	}
 	msdos_read_inode(free_inode);
+	/*
+	 * Check whether there's already a linked inode ...
+	 */
+	if (MSDOS_I(old_inode)->i_linked) {
+		struct inode *linked = MSDOS_I(old_inode)->i_linked;
+#ifdef MSDOS_PARANOIA
+printk("rename_diff_dir: inode %ld already has link %ld, freeing it\n",
+old_inode->i_ino, linked->i_ino);
+#endif
+		MSDOS_I(old_inode)->i_linked = NULL;
+		MSDOS_I(linked)->i_oldlink = NULL;
+		iput(linked);
+	}
 	MSDOS_I(old_inode)->i_busy = 1;
 	MSDOS_I(old_inode)->i_linked = free_inode;
 	MSDOS_I(free_inode)->i_oldlink = old_inode;
+#ifdef MSDOS_DEBUG
+printk("rename_diff_dir: inode %ld added as link of %ld\n",
+free_inode->i_ino, old_inode->i_ino);
+#endif
 	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);
 	if (exists) {
+		/*
+		 * Check whether there's already a depend inode ...
+		 */
+		if (MSDOS_I(new_inode)->i_depend) {
+			struct inode *depend = MSDOS_I(new_inode)->i_depend;
+#ifdef MSDOS_PARANOIA
+printk("rename_diff_dir: inode %ld already has depend %ld, freeing it\n",
+new_inode->i_ino, depend->i_ino);
+#endif
+			MSDOS_I(new_inode)->i_depend = NULL;
+			MSDOS_I(depend)->i_old = NULL;
+			iput(depend);
+		}
 		MSDOS_I(new_inode)->i_depend = free_inode;
 		MSDOS_I(free_inode)->i_old = new_inode;
 		/* Two references now exist to free_inode so increase count */
 		free_inode->i_count++;
+#ifdef MSDOS_DEBUG
+printk("rename_diff_dir: inode %ld added as depend of %ld\n",
+free_inode->i_ino, new_inode->i_ino);
+#endif
 		/* free_inode is put after putting new_inode and old_inode */
 		fat_brelse(sb, new_bh);
 	}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov