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

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

diff -u --recursive --new-file v2.1.77/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
@@ -64,7 +64,7 @@
 static int nfs_rmdir(struct inode *, struct dentry *);
 static int nfs_unlink(struct inode *, struct dentry *);
 static int nfs_symlink(struct inode *, struct dentry *, const char *);
-static int nfs_link(struct inode *, struct inode *, struct dentry *);
+static int nfs_link(struct dentry *, struct inode *, struct dentry *);
 static int nfs_mknod(struct inode *, struct dentry *, int, int);
 static int nfs_rename(struct inode *, struct dentry *,
 		      struct inode *, struct dentry *);
@@ -108,8 +108,11 @@
 static int
 nfs_dir_open(struct inode *dir, struct file *file)
 {
-	dfprintk(VFS, "NFS: nfs_dir_open(%x/%ld)\n", dir->i_dev, dir->i_ino);
-	return nfs_revalidate_inode(NFS_SERVER(dir), dir);
+	struct dentry *dentry = file->f_dentry;
+
+	dfprintk(VFS, "NFS: nfs_dir_open(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
+	return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
 }
 
 static ssize_t
@@ -133,7 +136,8 @@
 
 static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	struct inode 		*inode = filp->f_dentry->d_inode;
+	struct dentry 		*dentry = filp->f_dentry;
+	struct inode 		*inode = dentry->d_inode;
 	static struct wait_queue *readdir_wait = NULL;
 	struct wait_queue	**waitp = NULL;
 	struct nfs_dirent	*cache, *free;
@@ -144,14 +148,17 @@
 	__u32			*entry;
 	char			*name, *start;
 
-	dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino);
+	dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
+	result = -EBADF;
 	if (!inode || !S_ISDIR(inode->i_mode)) {
 		printk("nfs_readdir: inode is NULL or not a directory\n");
-		return -EBADF;
+		goto out;
 	}
 
-	if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
-		return result;
+	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	if (result < 0)
+		goto out;
 
 	/*
 	 * Try to find the entry in the cache
@@ -250,7 +257,7 @@
 				goto done;
 		}
 
-		result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
+		result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
 					cookie, PAGE_SIZE, cache->entry);
 		if (result <= 0)
 			goto done;
@@ -300,6 +307,7 @@
 	wake_up(&cache->wait);
 	wake_up(&readdir_wait);
 
+out:
 	return result;
 }
 
@@ -449,11 +457,21 @@
 #endif
 }
 
-static struct dentry_operations nfs_dentry_operations = {
+/*
+ * Called when the dentry is being freed to release private memory.
+ */
+static void nfs_dentry_release(struct dentry *dentry)
+{
+	if (dentry->d_fsdata)
+		kfree(dentry->d_fsdata);
+}
+
+struct dentry_operations nfs_dentry_operations = {
 	nfs_lookup_revalidate,	/* d_validate(struct dentry *) */
-	0,			/* d_hash */
-	0,			/* d_compare */
-	nfs_dentry_delete	/* d_delete(struct dentry *) */
+	NULL,			/* d_hash */
+	NULL,			/* d_compare */
+	nfs_dentry_delete,	/* d_delete(struct dentry *) */
+	nfs_dentry_release	/* d_release(struct dentry *) */
 };
 
 /*
@@ -470,16 +488,20 @@
 	}
 }
 
+static void nfs_set_fh(struct dentry *dentry, struct nfs_fh *fhandle)
+{
+	*((struct nfs_fh *) dentry->d_fsdata) = *fhandle;
+}
+
 static int nfs_lookup(struct inode *dir, struct dentry * dentry)
 {
-	int len = dentry->d_name.len;
 	struct inode *inode;
 	int error;
 	struct nfs_fh fhandle;
 	struct nfs_fattr fattr;
 
-	dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n",
-			dir->i_dev, dir->i_ino, len, dentry->d_name.name);
+	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_lookup: inode is NULL or not a directory\n");
@@ -487,17 +509,26 @@
 	}
 
 	error = -ENAMETOOLONG;
-	if (len > NFS_MAXNAMLEN)
+	if (dentry->d_name.len > NFS_MAXNAMLEN)
 		goto out;
 
-	error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), 
+	error = -ENOMEM;
+	if (!dentry->d_fsdata) {
+		dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+		if (!dentry->d_fsdata)
+			goto out;
+	}
+	dentry->d_op = &nfs_dentry_operations;
+
+	error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dentry->d_parent), 
 				dentry->d_name.name, &fhandle, &fattr);
 	inode = NULL;
 	if (error == -ENOENT)
 		goto no_entry;
 	if (!error) {
 		error = -EACCES;
-		inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
+		nfs_set_fh(dentry, &fhandle);
+		inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr);
 		if (inode) {
 #ifdef NFS_PARANOIA
 if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink))
@@ -506,7 +537,6 @@
 inode->i_ino, inode->i_count, inode->i_nlink);
 #endif
 	    no_entry:
-			dentry->d_op = &nfs_dentry_operations;
 			d_add(dentry, inode);
 			nfs_renew_times(dentry);
 			error = 0;
@@ -530,6 +560,7 @@
 	struct inode *inode;
 	int error = -EACCES;
 
+	nfs_set_fh(dentry, fhandle);
 	inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
 	if (inode) {
 #ifdef NFS_PARANOIA
@@ -578,7 +609,7 @@
 	 * 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),
+	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 			dentry->d_name.name, &sattr, &fhandle, &fattr);
 	if (!error)
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
@@ -616,7 +647,7 @@
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
+	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
 	if (!error)
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
@@ -652,7 +683,7 @@
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
+	error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
 
 	/*
@@ -717,8 +748,8 @@
 		dentry->d_inode->i_nlink --;
 	d_delete(dentry);
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_rmdir(NFS_SERVER(dir),
-				NFS_FH(dir), dentry->d_name.name);
+	error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
+				dentry->d_name.name);
 	if (!error) {
 		if (rehash)
 			d_add(dentry, NULL);
@@ -839,8 +870,8 @@
 
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_rename(NFS_SERVER(dir),
-				NFS_FH(dir), dentry->d_name.name,
-				NFS_FH(dir), silly);
+				NFS_FH(dentry->d_parent), dentry->d_name.name,
+				NFS_FH(dentry->d_parent), silly);
 	if (!error) {
 		nfs_renew_times(dentry);
 		d_move(dentry, sdentry);
@@ -914,8 +945,8 @@
 		d_delete(dentry);
 	}
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_remove(NFS_SERVER(dir),
-					NFS_FH(dir), dentry->d_name.name);
+	error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
+				dentry->d_name.name);
 	/*
 	 * Rehash the negative dentry if the operation succeeded.
 	 */
@@ -998,7 +1029,7 @@
 	 */
 	d_drop(dentry);
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
+	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, symname, &sattr);
 	if (!error) {
 		nfs_renew_times(dentry->d_parent);
@@ -1012,13 +1043,14 @@
 }
 
 static int 
-nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
+nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 {
+	struct inode *inode = old_dentry->d_inode;
 	int error;
 
-	dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n",
-				inode->i_dev, inode->i_ino,
-				dir->i_dev, dir->i_ino, dentry->d_name.name);
+	dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
+		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_link: dir is NULL or not a directory\n");
@@ -1029,13 +1061,21 @@
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
 		goto out;
 
+	/*
+	 * Drop the dentry in advance to force a new lookup.
+	 * Since nfs_proc_link doesn't return a filehandle,
+	 * we can't use the existing dentry.
+	 */
+	d_drop(dentry);
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), NFS_FH(dir),
-				dentry->d_name.name);
+	error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
+				NFS_FH(dentry->d_parent), dentry->d_name.name);
 	if (!error) {
-		inode->i_count ++;
-		inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */
-		d_instantiate(dentry, inode);
+ 		/*
+		 * Update the link count immediately, as some apps
+		 * (e.g. pine) test this after making a link.
+		 */
+		inode->i_nlink++;
 	}
 out:
 	return error;
@@ -1181,9 +1221,9 @@
 
 	nfs_invalidate_dircache(new_dir);
 	nfs_invalidate_dircache(old_dir);
-	error = nfs_proc_rename(NFS_SERVER(old_dir),
-				NFS_FH(old_dir), old_dentry->d_name.name,
-				NFS_FH(new_dir), new_dentry->d_name.name);
+	error = nfs_proc_rename(NFS_DSERVER(old_dentry),
+			NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
+			NFS_FH(new_dentry->d_parent), new_dentry->d_name.name);
 	if (!error) {
 		/* Update the dcache if needed */
 		if (rehash)

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