patch-2.1.106 linux/fs/nfsd/vfs.c

Next file: linux/fs/proc/array.c
Previous file: linux/fs/nfsd/nfsproc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.105/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
@@ -31,14 +31,13 @@
 
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/nfsfh.h>
 #include <linux/quotaops.h>
 
 #if LINUX_VERSION_CODE >= 0x020100
 #include <asm/uaccess.h>
 #endif
 
-extern void fh_update(struct svc_fh*);
-
 #define NFSDDBG_FACILITY		NFSDDBG_FILEOP
 
 /* Open mode for nfsd_open */
@@ -77,6 +76,41 @@
 static struct raparms *		raparm_cache = 0;
 
 /*
+ * Lock a parent directory following the VFS locking protocol.
+ */
+int
+fh_lock_parent(struct svc_fh *parent_fh, struct dentry *dchild)
+{
+	int	nfserr = 0;
+
+	fh_lock(parent_fh);
+	/*
+	 * Make sure the parent->child relationship still holds,
+	 * and that the child is still hashed.
+	 */
+	if (dchild->d_parent != parent_fh->fh_dentry) 
+		goto out_not_parent;
+	if (list_empty(&dchild->d_hash))
+		goto out_not_hashed; 
+out:
+	return nfserr;
+
+out_not_parent:
+	printk(KERN_WARNING
+		"fh_lock_parent: %s/%s parent changed\n",
+		dchild->d_parent->d_name.name, dchild->d_name.name);
+	goto out_unlock;
+out_not_hashed:
+	printk(KERN_WARNING
+		"fh_lock_parent: %s/%s unhashed\n",
+		dchild->d_parent->d_name.name, dchild->d_name.name);
+out_unlock:
+	nfserr = nfserr_noent;
+	fh_unlock(parent_fh);
+	goto out;
+}
+
+/*
  * Deny access to certain file systems
  */
 static inline int
@@ -546,8 +580,11 @@
 }
 
 /*
- * Create a file (regular, directory, device, fifo).
- * UNIX sockets not yet implemented.
+ * Create a file (regular, directory, device, fifo); UNIX sockets 
+ * not yet implemented.
+ * If the response fh has been verified, the parent directory should
+ * already be locked. Note that the parent directory is left locked.
+ *
  * N.B. Every call to nfsd_create needs an fh_put for _both_ fhp and resfhp
  */
 int
@@ -557,13 +594,12 @@
 {
 	struct dentry	*dentry, *dchild;
 	struct inode	*dirp;
+	nfsd_dirop_t	opfunc = NULL;
 	int		err;
 
 	err = nfserr_perm;
 	if (!flen)
 		goto out;
-	if (!(iap->ia_valid & ATTR_MODE))
-		iap->ia_mode = 0;
 	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
 	if (err)
 		goto out;
@@ -571,64 +607,74 @@
 	dentry = fhp->fh_dentry;
 	dirp = dentry->d_inode;
 
-	/* Get all the sanity checks out of the way before we lock the parent. */
 	err = nfserr_notdir;
 	if(!dirp->i_op || !dirp->i_op->lookup)
 		goto out;
-	err = nfserr_perm;
-	if (type == S_IFREG) {
-		if(!dirp->i_op->create)
-			goto out;
-	} else if(type == S_IFDIR) {
-		if(!dirp->i_op->mkdir)
-			goto out;
-	} else if((type == S_IFCHR) || (type == S_IFBLK) || (type == S_IFIFO)) {
-		if(!dirp->i_op->mknod)
-			goto out;
-	} else {
-		goto out;
-	}
-
 	/*
-	 * The response filehandle may have been setup already ...
+	 * Check whether the response filehandle has been verified yet.
+	 * If it has, the parent directory should already be locked.
 	 */
 	if (!resfhp->fh_dverified) {
 		dchild = lookup_dentry(fname, dget(dentry), 0);
 		err = PTR_ERR(dchild);
-		if(IS_ERR(dchild))
+		if (IS_ERR(dchild))
 			goto out_nfserr;
 		fh_compose(resfhp, fhp->fh_export, dchild);
-	} else
+		/* Lock the parent and check for errors ... */
+		err = fh_lock_parent(fhp, dchild);
+		if (err)
+			goto out;
+	} else {
 		dchild = resfhp->fh_dentry;
+		if (!fhp->fh_locked)
+			printk(KERN_ERR
+				"nfsd_create: parent %s/%s not locked!\n",
+				dentry->d_parent->d_name.name,
+				dentry->d_name.name);
+	}
 	/*
 	 * Make sure the child dentry is still negative ...
 	 */
+	err = nfserr_exist;
 	if (dchild->d_inode) {
-		printk("nfsd_create: dentry %s/%s not negative!\n",
-			dentry->d_parent->d_name.name, dentry->d_name.name);
+		printk(KERN_WARNING
+			"nfsd_create: dentry %s/%s not negative!\n",
+			dentry->d_name.name, dchild->d_name.name);
+		goto out; 
 	}
 
-	/* Looks good, lock the directory. */
-	fh_lock(fhp);
-	DQUOT_INIT(dirp);
+	/*
+	 * Get the dir op function pointer.
+	 */
+	err = nfserr_perm;
 	switch (type) {
 	case S_IFREG:
-		err = dirp->i_op->create(dirp, dchild, iap->ia_mode);
+		opfunc = (nfsd_dirop_t) dirp->i_op->create;
 		break;
 	case S_IFDIR:
-		err = dirp->i_op->mkdir(dirp, dchild, iap->ia_mode);
+		opfunc = (nfsd_dirop_t) dirp->i_op->mkdir;
 		break;
 	case S_IFCHR:
 	case S_IFBLK:
 	case S_IFIFO:
-		err = dirp->i_op->mknod(dirp, dchild, iap->ia_mode, rdev);
+		opfunc = dirp->i_op->mknod;
 		break;
 	}
-	DQUOT_DROP(dirp);
-	fh_unlock(fhp);
+	if (!opfunc)
+		goto out;
 
+	if (!(iap->ia_valid & ATTR_MODE))
+		iap->ia_mode = 0;
+
+	/*
+	 * Call the dir op function to create the object.
+	 */
+	DQUOT_INIT(dirp);
+	err = opfunc(dirp, dchild, iap->ia_mode, rdev);
+	DQUOT_DROP(dirp);
 	if (err < 0)
 		goto out_nfserr;
+
 	if (EX_ISSYNC(fhp->fh_export))
 		write_inode_now(dirp);
 
@@ -778,21 +824,29 @@
 	if (IS_ERR(dnew))
 		goto out_nfserr;
 
-	err = -EEXIST;
+	/*
+	 * Lock the parent before checking for existence
+	 */
+	err = fh_lock_parent(fhp, dnew);
+	if (err)
+		goto out_compose;
+
+	err = nfserr_exist;
 	if (!dnew->d_inode) {
-		fh_lock(fhp);
 		DQUOT_INIT(dirp);
 		err = dirp->i_op->symlink(dirp, dnew, path);
 		DQUOT_DROP(dirp);
-		fh_unlock(fhp);
 		if (!err) {
 			if (EX_ISSYNC(fhp->fh_export))
 				write_inode_now(dirp);
-		}
+		} else
+			err = nfserrno(-err);
 	}
+	fh_unlock(fhp);
+
+	/* Compose the fh so the dentry will be freed ... */
+out_compose:
 	fh_compose(resfhp, fhp->fh_export, dnew);
-	if (err)
-		goto out_nfserr;
 out:
 	return err;
 
@@ -820,6 +874,10 @@
 	if (err)
 		goto out;
 
+	err = nfserr_perm;
+	if (!len)
+		goto out;
+
 	ddir = ffhp->fh_dentry;
 	dirp = ddir->d_inode;
 
@@ -827,44 +885,48 @@
 	err = PTR_ERR(dnew);
 	if (IS_ERR(dnew))
 		goto out_nfserr;
+	/*
+	 * Lock the parent before checking for existence
+	 */
+	err = fh_lock_parent(ffhp, dnew);
+	if (err)
+		goto out_dput;
 
-	err = -EEXIST;
+	err = nfserr_exist;
 	if (dnew->d_inode)
-		goto dput_and_out;
-
-	err = -EPERM;
-	if (!len)
-		goto dput_and_out;
+		goto out_unlock;
 
 	dold = tfhp->fh_dentry;
 	dest = dold->d_inode;
 
-	err = -EACCES;
+	err = nfserr_acces;
 	if (nfsd_iscovered(ddir, ffhp->fh_export))
-		goto dput_and_out;
+		goto out_unlock;
+	/* FIXME: nxdev for NFSv3 */
 	if (dirp->i_dev != dest->i_dev)
-		goto dput_and_out;	/* FIXME: nxdev for NFSv3 */
+		goto out_unlock;
 
-	err = -EPERM;
+	err = nfserr_perm;
 	if (IS_IMMUTABLE(dest) /* || IS_APPEND(dest) */ )
-		goto dput_and_out;
+		goto out_unlock;
 	if (!dirp->i_op || !dirp->i_op->link)
-		goto dput_and_out;
+		goto out_unlock;
 
-	fh_lock(ffhp);
 	DQUOT_INIT(dirp);
 	err = dirp->i_op->link(dold, dirp, dnew);
 	DQUOT_DROP(dirp);
-	fh_unlock(ffhp);
+	if (!err) {
+		if (EX_ISSYNC(ffhp->fh_export)) {
+			write_inode_now(dirp);
+			write_inode_now(dest);
+		}
+	} else
+		err = nfserrno(-err);
 
-	if (!err && EX_ISSYNC(ffhp->fh_export)) {
-		write_inode_now(dirp);
-		write_inode_now(dest);
-	}
-dput_and_out:
+out_unlock:
+	fh_unlock(ffhp);
+out_dput:
 	dput(dnew);
-	if (err)
-		goto out_nfserr;
 out:
 	return err;
 
@@ -1005,7 +1067,10 @@
 	if (IS_ERR(rdentry))
 		goto out_nfserr;
 
-	fh_lock(fhp);
+	err = fh_lock_parent(fhp, rdentry);
+	if (err)
+		goto out;
+
 	DQUOT_INIT(dirp);
 	if (type == S_IFDIR) {
 		err = -ENOTDIR;

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