patch-2.1.67 linux/fs/nfs/dir.c

Next file: linux/fs/nfsd/export.c
Previous file: linux/fs/ext2/namei.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.66/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
@@ -478,15 +478,16 @@
 	int error;
 
 	dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n",
-				dir->i_dev, dir->i_ino, len, dentry->d_name.name);
+			dir->i_dev, dir->i_ino, len, dentry->d_name.name);
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_lookup: inode is NULL or not a directory\n");
 		return -ENOENT;
 	}
 
+	error = -ENAMETOOLONG;
 	if (len > NFS_MAXNAMLEN)
-		return -ENAMETOOLONG;
+		goto out;
 
 	error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), 
 				dentry->d_name.name, &fhandle, &fattr);
@@ -504,6 +505,7 @@
 			error = 0;
 		}
 	}
+out:
 	return error;
 }
 
@@ -516,7 +518,6 @@
 	struct inode *inode;
 	int error = -EACCES;
 
-	nfs_invalidate_dircache(dir);
 	inode = nfs_fhget(dir->i_sb, fhandle, fattr);
 	if (inode) {
 		d_instantiate(dentry, inode);
@@ -526,6 +527,12 @@
 	return error;
 }
 
+/*
+ * Following a failed create operation, we drop the dentry rather
+ * than retain a negative dentry. This avoids a problem in the event
+ * that the operation succeeded on the server, but an error in the
+ * reply path made it appear to have failed.
+ */
 static int nfs_create(struct inode *dir, struct dentry * dentry, int mode)
 {
 	struct nfs_sattr sattr;
@@ -541,19 +548,36 @@
 		return -ENOENT;
 	}
 
+	error = -ENAMETOOLONG;
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
-		return -ENAMETOOLONG;
+		goto out;
 
 	sattr.mode = mode;
 	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+
+	/*
+	 * Invalidate the dir cache before the operation to avoid a race.
+	 */
+	nfs_invalidate_dircache(dir);
 	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
 			dentry->d_name.name, &sattr, &fhandle, &fattr);
 	if (!error)
 		error = nfs_instantiate(dir, dentry, &fattr, &fhandle);
+	else {
+#ifdef NFS_PARANOIA
+printk("nfs_create: %s/%s failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
+#endif
+		d_drop(dentry);
+	}
+out:
 	return error;
 }
 
+/*
+ * See comments for nfs_proc_create regarding failed operations.
+ */
 static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
 {
 	struct nfs_sattr sattr;
@@ -576,15 +600,26 @@
 	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
 	if (S_ISCHR(mode) || S_ISBLK(mode))
 		sattr.size = rdev; /* get out your barf bag */
-
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+
+	nfs_invalidate_dircache(dir);
 	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
 	if (!error)
 		error = nfs_instantiate(dir, dentry, &fattr, &fhandle);
+	else {
+#ifdef NFS_PARANOIA
+printk("nfs_mknod: %s/%s failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
+#endif
+		d_drop(dentry);
+	}
 	return error;
 }
 
+/*
+ * See comments for nfs_proc_create regarding failed operations.
+ */
 static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
 	struct nfs_sattr sattr;
@@ -607,10 +642,18 @@
 	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
+	nfs_invalidate_dircache(dir);
 	error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
 	if (!error)
 		error = nfs_instantiate(dir, dentry, &fattr, &fhandle);
+	else {
+#ifdef NFS_PARANOIA
+printk("nfs_mkdir: %s/%s failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
+#endif
+		d_drop(dentry);
+	}
 	return error;
 }
 
@@ -897,7 +940,7 @@
 	int error;
 
 	dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
-				dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
+			dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_symlink: inode is NULL or not a directory\n");
@@ -911,25 +954,35 @@
 	if (strlen(symname) > NFS_MAXPATHLEN)
 		goto out;
 
-	sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */
+#ifdef NFS_PARANOIA
+if (dentry->d_inode)
+printk("nfs_proc_symlink: %s/%s not negative!\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+	/*
+	 * Fill in the sattr for the call.
+ 	 * Note: SunOS 4.1.2 crashes if the mode isn't initialized!
+	 */
+	sattr.mode = S_IFLNK | S_IRWXUGO;
 	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
+	/*
+	 * Drop the dentry in advance to force a new lookup.
+	 * Since nfs_proc_symlink doesn't return a fattr, we
+	 * can't instantiate the new inode.
+	 */
+	d_drop(dentry);
 	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
 				dentry->d_name.name, symname, &sattr);
 	if (!error) {
 		nfs_invalidate_dircache(dir);
 		nfs_renew_times(dentry->d_parent);
-		/*  this looks _funny_ doesn't it? But: nfs_proc_symlink()
-		 *  only fills in sattr, not fattr. Thus nfs_fhget() cannot be
-		 *  called, it would be pointless, without a valid fattr
-		 *  argument. Other possibility: call nfs_proc_lookup()
-		 *  HERE. But why? If somebody wants to reference this
-		 *  symlink, the cached_lookup() will fail, and
-		 *  nfs_proc_symlink() will be called anyway.
-		 */
-		d_drop(dentry);
+	} else if (error == -EEXIST) {
+		printk("nfs_proc_symlink: %s/%s already exists??\n",
+			dentry->d_parent->d_name.name, dentry->d_name.name);
 	}
+
 out:
 	return error;
 }

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