patch-2.3.99-pre9 linux/fs/nfs/dir.c

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

diff -u --recursive --new-file v2.3.99-pre8/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
@@ -35,8 +35,6 @@
 #define NFS_PARANOIA 1
 /* #define NFS_DEBUG_VERBOSE 1 */
 
-static int nfs_safe_remove(struct dentry *);
-
 static int nfs_readdir(struct file *, void *, filldir_t);
 static struct dentry *nfs_lookup(struct inode *, struct dentry *);
 static int nfs_create(struct inode *, struct dentry *, int);
@@ -71,6 +69,70 @@
 };
 
 typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
+typedef struct {
+	struct file	*file;
+	struct page	*page;
+	unsigned long	page_index;
+	unsigned	page_offset;
+	u64		target;
+	struct nfs_entry *entry;
+	decode_dirent_t	decode;
+	int		plus;
+	int		error;
+} nfs_readdir_descriptor_t;
+
+/* Now we cache directories properly, by stuffing the dirent
+ * data directly in the page cache.
+ *
+ * Inode invalidation due to refresh etc. takes care of
+ * _everything_, no sloppy entry flushing logic, no extraneous
+ * copying, network direct to page cache, the way it was meant
+ * to be.
+ *
+ * NOTE: Dirent information verification is done always by the
+ *	 page-in of the RPC reply, nowhere else, this simplies
+ *	 things substantially.
+ */
+static
+int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
+{
+	struct file	*file = desc->file;
+	struct inode	*inode = file->f_dentry->d_inode;
+	void		*buffer = (void *)kmap(page);
+	int		plus = NFS_USE_READDIRPLUS(inode);
+	int		error;
+
+	dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index);
+
+ again:
+	error = NFS_PROTO(inode)->readdir(file, desc->entry->cookie, buffer,
+					  NFS_SERVER(inode)->dtsize, plus);
+	/* We requested READDIRPLUS, but the server doesn't grok it */
+	if (desc->plus && error == -ENOTSUPP) {
+		NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
+		plus = 0;
+		goto again;
+	}
+	if (error < 0)
+		goto error;
+	SetPageUptodate(page);
+	kunmap(page);
+	/* Ensure consistent page alignment of the data.
+	 * Note: assumes we have exclusive access to this mapping either
+	 *	 throught inode->i_sem or some other mechanism.
+	 */
+	if (page->index == 0)
+		invalidate_inode_pages(inode);
+	UnlockPage(page);
+	return 0;
+ error:
+	SetPageError(page);
+	kunmap(page);
+	UnlockPage(page);
+	invalidate_inode_pages(inode);
+	desc->error = error;
+	return -EIO;
+}
 
 /*
  * Given a pointer to a buffer that has already been filled by a call
@@ -81,309 +143,217 @@
  * read.
  */
 static inline
-long find_dirent(struct page *page, loff_t offset,
-		 struct nfs_entry *entry,
-		 decode_dirent_t decode, int plus, int use_cookie)
-{
-	u8		*p = (u8 *)kmap(page),
-			*start = p;
-	unsigned long	base = page_offset(page),
-			pg_offset = 0;
-	int		loop_count = 0;
+int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page)
+{
+	struct nfs_entry *entry = desc->entry;
+	char		*start = (char *)kmap(page),
+			*p = start;
+	int		loop_count = 0,
+			status = 0;
 
-	if (!p)
-		return -EIO;
 	for(;;) {
-		p = (u8*)decode((__u32*)p, entry, plus);
-		if (IS_ERR(p))
+		p = (char *)desc->decode((u32*)p, entry, desc->plus);
+		if (IS_ERR(p)) {
+			status = PTR_ERR(p);
 			break;
-		pg_offset = p - start;
-		entry->prev = entry->offset;
-		entry->offset = base + pg_offset;
-		if ((use_cookie ? entry->cookie : entry->offset) > offset)
+		}
+		desc->page_offset = p - start;
+		dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie);
+		if (entry->prev_cookie == desc->target)
 			break;
 		if (loop_count++ > 200) {
 			loop_count = 0;
 			schedule();
 		}
 	}
-
 	kunmap(page);
-	return (IS_ERR(p)) ?  PTR_ERR(p) : (long)pg_offset;
+	dfprintk(VFS, "NFS: find_dirent() returns %d\n", status);
+	return status;
 }
 
 /*
  * Find the given page, and call find_dirent() in order to try to
  * return the next entry.
- *
- * Returns -EIO if the page is not available, or up to date.
  */
 static inline
-long find_dirent_page(struct inode *inode, loff_t offset,
-		      struct nfs_entry *entry)
+int find_dirent_page(nfs_readdir_descriptor_t *desc)
 {
-	decode_dirent_t	decode = NFS_PROTO(inode)->decode_dirent;
+	struct inode	*inode = desc->file->f_dentry->d_inode;
 	struct page	*page;
-	unsigned long	index = entry->offset >> PAGE_CACHE_SHIFT;
-	long		status = -EIO;
-	int		plus = NFS_USE_READDIRPLUS(inode),
-			use_cookie = NFS_MONOTONE_COOKIES(inode);
-
-	dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", entry->offset & PAGE_CACHE_MASK);
-
-	if (entry->page)
-		page_cache_release(entry->page);
-
-	page = find_get_page(&inode->i_data, index);
-
-	if (page && Page_Uptodate(page))
-		status = find_dirent(page, offset, entry, decode, plus, use_cookie);
-
-	/* NB: on successful return we will be holding the page */
-	if (status < 0) {
-		entry->page = NULL;
-		if (page)
-			page_cache_release(page);
-	} else
-		entry->page = page;
+	unsigned long	index = desc->page_index;
+	int		status;
+
+	dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index);
 
-	dfprintk(VFS, "NFS: find_dirent_page() returns %ld\n", status);
+	if (desc->page) {
+		page_cache_release(desc->page);
+		desc->page = NULL;
+	}
+
+	page = read_cache_page(&inode->i_data, index,
+			       (filler_t *)nfs_readdir_filler, desc);
+	if (IS_ERR(page)) {
+		status = PTR_ERR(page);
+		goto out;
+	}
+	if (!Page_Uptodate(page))
+		goto read_error;
+
+	/* NOTE: Someone else may have changed the READDIRPLUS flag */
+	desc->plus = NFS_USE_READDIRPLUS(inode);
+	status = find_dirent(desc, page);
+	if (status >= 0)
+		desc->page = page;
+	else
+		page_cache_release(page);
+ out:
+	dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status);
 	return status;
+ read_error:
+	page_cache_release(page);
+	return -EIO;
 }
 
-
 /*
  * Recurse through the page cache pages, and return a
  * filled nfs_entry structure of the next directory entry if possible.
  *
- * The target for the search is position 'offset'.
- * The latter may either be an offset into the page cache, or (better)
- * a cookie depending on whether we're interested in strictly following
- * the RFC wrt. not assuming monotonicity of cookies or not.
- *
- * For most systems, the latter is more reliable since it naturally
- * copes with holes in the directory.
+ * The target for the search is 'desc->target'.
  */
 static inline
-long search_cached_dirent_pages(struct inode *inode, loff_t offset,
-				struct nfs_entry *entry)
+int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
 {
-	long		res = 0;
+	int		res = 0;
 	int		loop_count = 0;
 
-	dfprintk(VFS, "NFS: search_cached_dirent_pages() searching for cookie %Ld\n", (long long)offset);
+	dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target);
 	for (;;) {
-		res = find_dirent_page(inode, offset, entry);
-		if (res == -EAGAIN) {
-			/* Align to beginning of next page */
-			entry->offset &= PAGE_CACHE_MASK;
-			entry->offset += PAGE_CACHE_SIZE;
-		}
+		res = find_dirent_page(desc);
 		if (res != -EAGAIN)
 			break;
+		/* Align to beginning of next page */
+		desc->page_offset = 0;
+		desc->page_index ++;
 		if (loop_count++ > 200) {
 			loop_count = 0;
 			schedule();
 		}
 	}
-	if (res < 0 && entry->page) {
-		page_cache_release(entry->page);
-		entry->page = NULL;
-	}
-	dfprintk(VFS, "NFS: search_cached_dirent_pages() returned %ld\n", res);
-	return res;
-}
-
-
-/* Now we cache directories properly, by stuffing the dirent
- * data directly in the page cache.
- *
- * Inode invalidation due to refresh etc. takes care of
- * _everything_, no sloppy entry flushing logic, no extraneous
- * copying, network direct to page cache, the way it was meant
- * to be.
- *
- * NOTE: Dirent information verification is done always by the
- *	 page-in of the RPC reply, nowhere else, this simplies
- *	 things substantially.
- */
-static inline
-long try_to_get_dirent_page(struct file *file, struct inode *inode,
-			    struct nfs_entry *entry)
-{
-	struct dentry	*dir = file->f_dentry;
-	struct page	*page;
-	__u32		*p;
-	unsigned long	index = entry->offset >> PAGE_CACHE_SHIFT;
-	long		res = 0;
-	unsigned int	dtsize = NFS_SERVER(inode)->dtsize;
-	int		plus = NFS_USE_READDIRPLUS(inode);
-
-	dfprintk(VFS, "NFS: try_to_get_dirent_page() reading directory page @ index %ld\n", index);
-
-	page = grab_cache_page(&inode->i_data, index);
-
-	if (!page) {
-		res = -ENOMEM;
-		goto out;
-	}
-
-	if (Page_Uptodate(page)) {
-		dfprintk(VFS, "NFS: try_to_get_dirent_page(): page already up to date.\n");
-		goto unlock_out;
-	}
-
-	p = (__u32 *)kmap(page);
-
-	if (dtsize > PAGE_CACHE_SIZE)
-		dtsize = PAGE_CACHE_SIZE;
-	res = NFS_PROTO(inode)->readdir(dir, entry->cookie, p, dtsize, plus);
-
-	kunmap(page);
-
-	if (res < 0)
-		goto error;
-	if (PageError(page))
-		ClearPageError(page);
-	SetPageUptodate(page);
-
- unlock_out:
-	UnlockPage(page);
-	page_cache_release(page);
- out:
-	dfprintk(VFS, "NFS: try_to_get_dirent_page() returns %ld\n", res);
+	dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res);
 	return res;
- error:
-	SetPageError(page);
-	goto unlock_out;
 }
 
-/* Recover from a revalidation flush.  The case here is that
- * the inode for the directory got invalidated somehow, and
- * all of our cached information is lost.  In order to get
- * a correct cookie for the current readdir request from the
- * user, we must (re-)fetch all the older readdir page cache
- * entries.
- *
- * Returns < 0 if some error occurs.
+/*
+ * Once we've found the start of the dirent within a page: fill 'er up...
  */
-static inline
-long refetch_to_readdir(struct file *file, struct inode *inode,
-			loff_t off, struct nfs_entry *entry)
-{
-	struct nfs_entry	my_dirent,
-				*dirent = &my_dirent;
-	long			res;
-	int			plus = NFS_USE_READDIRPLUS(inode),
-				use_cookie = NFS_MONOTONE_COOKIES(inode),
-				loop_count = 0;
-
-	dfprintk(VFS, "NFS: refetch_to_readdir() searching for cookie %Ld\n", (long long)off);
-	*dirent = *entry;
-	entry->page = NULL;
-
-	for (res = 0;res >= 0;) {
-		if (loop_count++ > 200) {
-			loop_count = 0;
-			schedule();
-		}
+static 
+int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
+		   filldir_t filldir)
+{
+	struct file	*file = desc->file;
+	struct nfs_entry *entry = desc->entry;
+	char		*start = (char *)kmap(desc->page),
+			*p = start + desc->page_offset;
+	unsigned long	fileid;
+	int		loop_count = 0,
+			res = 0;
 
-		/* Search for last cookie in page cache */
-		res = search_cached_dirent_pages(inode, off, dirent);
+	dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target);
 
-		if (res >= 0) {
-			/* Cookie was found */
-			if ((use_cookie?dirent->cookie:dirent->offset) > off) {
-				*entry = *dirent;
-				dirent->page = NULL;
-				break;
+	for(;;) {
+		/* Note: entry->prev_cookie contains the cookie for
+		 *	 retrieving the current dirent on the server */
+		fileid = nfs_fileid_to_ino_t(entry->ino);
+		res = filldir(dirent, entry->name, entry->len, 
+			      entry->prev_cookie, fileid);
+		if (res < 0)
+			break;
+		file->f_pos = desc->target = entry->cookie;
+		p = (char *)desc->decode((u32 *)p, entry, desc->plus);
+		if (IS_ERR(p)) {
+			if (PTR_ERR(p) == -EAGAIN) {
+				desc->page_offset = 0;
+				desc->page_index ++;
 			}
-			continue;
-		}
-
-		if (dirent->page)
-			page_cache_release(dirent->page);
-		dirent->page = NULL;
-
-		if (res != -EIO) {
-			*entry = *dirent;
 			break;
 		}
-
-		/* Read in a new page */
-		res = try_to_get_dirent_page(file, inode, dirent);
-		if (res == -EBADCOOKIE) {
-			memset(dirent, 0, sizeof(*dirent));
-			nfs_zap_caches(inode);
-			res = 0;
-		}
-		/* We requested READDIRPLUS, but the server doesn't grok it */
-		if (plus && res == -ENOTSUPP) {
-			NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
-			memset(dirent, 0, sizeof(*dirent));
-			nfs_zap_caches(inode);
-			plus = 0;
-			res = 0;
+		desc->page_offset = p - start;
+		if (loop_count++ > 200) {
+			loop_count = 0;
+			schedule();
 		}
 	}
-	if (dirent->page)
-		page_cache_release(dirent->page);
+	kunmap(desc->page);
+	page_cache_release(desc->page);
+	desc->page = NULL;
 
-	dfprintk(VFS, "NFS: refetch_to_readdir() returns %ld\n", res);
+	dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res);
 	return res;
 }
 
 /*
- * Once we've found the start of the dirent within a page: fill 'er up...
+ * If we cannot find a cookie in our cache, we suspect that this is
+ * because it points to a deleted file, so we ask the server to return
+ * whatever it thinks is the next entry. We then feed this to filldir.
+ * If all goes well, we should then be able to find our way round the
+ * cache on the next call to readdir_search_pagecache();
+ *
+ * NOTE: we cannot add the anonymous page to the pagecache because
+ *	 the data it contains might not be page aligned. Besides,
+ *	 we should already have a complete representation of the
+ *	 directory in the page cache by the time we get here.
  */
-static
-int nfs_do_filldir(struct file *file, struct inode *inode,
-		   struct nfs_entry *entry, void *dirent, filldir_t filldir)
+static inline
+int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
+		     filldir_t filldir)
 {
-	decode_dirent_t	decode = NFS_PROTO(inode)->decode_dirent;
-	struct page	*page = entry->page;
-	__u8		*p,
-			*start;
-	unsigned long	base = page_offset(page),
-			offset = entry->offset,
-			pg_offset,
-			fileid;
-	int		plus = NFS_USE_READDIRPLUS(inode),
-			use_cookie = NFS_MONOTONE_COOKIES(inode),
-			loop_count = 0,
-			res = 0;
-
-	dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ offset %ld\n", entry->offset);
-	pg_offset = offset & ~PAGE_CACHE_MASK;
-	start = (u8*)kmap(page);
-	p = start + pg_offset;
+	struct file	*file = desc->file;
+	struct inode	*inode = file->f_dentry->d_inode;
+	struct page	*page = NULL;
+	u32		*p;
+	int		status = -EIO;
+
+	dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target);
+	if (desc->page) {
+		page_cache_release(desc->page);
+		desc->page = NULL;
+	}
 
-	for(;;) {
-		/* Note: entry->prev contains the offset of the start of the
-		 *       current dirent */
-		fileid = nfs_fileid_to_ino_t(entry->ino);
-		if (use_cookie)
-			res = filldir(dirent, entry->name, entry->len, entry->prev_cookie, fileid);
+	page = page_cache_alloc();
+	if (!page) {
+		status = -ENOMEM;
+		goto out;
+	}
+	p = (u32 *)kmap(page);
+	status = NFS_PROTO(inode)->readdir(file, desc->target, p,
+					   NFS_SERVER(inode)->dtsize, 0);
+	if (status >= 0) {
+		p = desc->decode(p, desc->entry, 0);
+		if (IS_ERR(p))
+			status = PTR_ERR(p);
 		else
-			res = filldir(dirent, entry->name, entry->len, entry->prev, fileid);
-		if (res < 0)
-			break;
-		file->f_pos = (use_cookie) ? entry->cookie : entry->offset;
-		p = (u8*)decode((__u32*)p, entry, plus);
-		if (!p || IS_ERR(p))
-			break;
-		pg_offset = p - start;
-		entry->prev = entry->offset;
-		entry->offset = base + pg_offset;
-		if (loop_count++ > 200) {
-			loop_count = 0;
-			schedule();
-		}
+			desc->entry->prev_cookie = desc->target;
 	}
 	kunmap(page);
+	if (status < 0)
+		goto out_release;
 
-	dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ offset %ld; returning = %d\n", entry->offset, res);
-	return res;
+	desc->page_index = 0;
+	desc->page_offset = 0;
+	desc->page = page;
+	status = nfs_do_filldir(desc, dirent, filldir);
+
+	/* Reset read descriptor so it searches the page cache from
+	 * the start upon the next call to readdir_search_pagecache() */
+	desc->page_index = 0;
+	desc->page_offset = 0;
+	memset(desc->entry, 0, sizeof(*desc->entry));
+ out:
+	dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status);
+	return status;
+ out_release:
+	page_cache_release(page);
+	goto out;
 }
 
 /* The file offset position is now represented as a true offset into the
@@ -393,10 +363,9 @@
 {
 	struct dentry	*dentry = filp->f_dentry;
 	struct inode	*inode = dentry->d_inode;
-	struct page	*page;
-	struct nfs_entry my_entry,
-			*entry = &my_entry;
-	loff_t		offset;
+	nfs_readdir_descriptor_t my_desc,
+			*desc = &my_desc;
+	struct nfs_entry my_entry;
 	long		res;
 
 	res = nfs_revalidate(dentry);
@@ -409,36 +378,41 @@
 	 * read from the last dirent to revalidate f_pos
 	 * itself.
 	 */
-	memset(entry, 0, sizeof(*entry));
+	memset(desc, 0, sizeof(*desc));
+	memset(&my_entry, 0, sizeof(my_entry));
 
-	offset = filp->f_pos;
+	desc->file = filp;
+	desc->target = filp->f_pos;
+	desc->entry = &my_entry;
+	desc->decode = NFS_PROTO(inode)->decode_dirent;
 
-	while(!entry->eof) {
-		res = search_cached_dirent_pages(inode, offset, entry);
-
-		if (res < 0) {
-			if (entry->eof)
-				break;
-			res = refetch_to_readdir(filp, inode, offset, entry);
-			if (res < 0)
+	while(!desc->entry->eof) {
+		res = readdir_search_pagecache(desc);
+		if (res == -EBADCOOKIE) {
+			/* This means either end of directory */
+			if (desc->entry->cookie == desc->target) {
+				res = 0;
 				break;
+			}
+			/* Or that the server has 'lost' a cookie */
+			res = uncached_readdir(desc, dirent, filldir);
+			if (res >= 0)
+				continue;
 		}
+		if (res < 0)
+			break;
 
-		page = entry->page;
-		if (!page)
-			printk(KERN_ERR "NFS: Missing page...\n");
-		res = nfs_do_filldir(filp, inode, entry, dirent, filldir);
-		page_cache_release(page);
-		entry->page = NULL;
+		res = nfs_do_filldir(desc, dirent, filldir);
 		if (res < 0) {
 			res = 0;
 			break;
 		}
-		offset = filp->f_pos;
 	}
-	if (entry->page)
-		page_cache_release(entry->page);
-	if (res < 0 && res != -EBADCOOKIE)
+	if (desc->page)
+		page_cache_release(desc->page);
+	if (desc->error < 0)
+		return desc->error;
+	if (res < 0)
 		return res;
 	return 0;
 }
@@ -583,26 +557,18 @@
 
 /*
  * This is called from dput() when d_count is going to 0.
- * We use it to clean up silly-renamed files.
  */
-static void nfs_dentry_delete(struct dentry *dentry)
+static int nfs_dentry_delete(struct dentry *dentry)
 {
 	dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
 		dentry->d_flags);
 
 	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
-		int error;
-		
-		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
-		/* Unhash it first */
-		d_drop(dentry);
-		error = nfs_safe_remove(dentry);
-		if (error)
-			printk("NFS: can't silly-delete %s/%s, error=%d\n",
-				dentry->d_parent->d_name.name,
-				dentry->d_name.name, error);
+		/* Unhash it, so that ->d_iput() would be called */
+		return 1;
 	}
+	return 0;
 
 }
 
@@ -627,36 +593,30 @@
 		nfs_fh_free(dentry->d_fsdata);
 }
 
-struct dentry_operations nfs_dentry_operations = {
-	d_revalidate:	nfs_lookup_revalidate,
-	d_delete:	nfs_dentry_delete,
-	d_release:	nfs_dentry_release,
-};
-
-#if 0 /* dead code */
-#ifdef NFS_PARANOIA
 /*
- * Display all dentries holding the specified inode.
+ * Called when the dentry loses inode.
+ * We use it to clean up silly-renamed files.
  */
-static void show_dentry(struct list_head * dlist)
+static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
 {
-	struct list_head *tmp = dlist;
-
-	while ((tmp = tmp->next) != dlist) {
-		struct dentry * dentry = list_entry(tmp, struct dentry, d_alias);
-		const char * unhashed = "";
-
-		if (d_unhashed(dentry))
-			unhashed = "(unhashed)";
-
-		printk("show_dentry: %s/%s, d_count=%d%s\n",
-			dentry->d_parent->d_name.name,
-			dentry->d_name.name, dentry->d_count,
-			unhashed);
+	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+		struct dentry *dir = dentry->d_parent;
+		struct inode *dir_i = dir->d_inode;
+		int error;
+		
+		nfs_zap_caches(dir_i);
+		NFS_CACHEINV(inode);
+		error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name);
 	}
+	iput(inode);
 }
-#endif /* NFS_PARANOIA */
-#endif /* 0 */
+
+struct dentry_operations nfs_dentry_operations = {
+	d_revalidate:	nfs_lookup_revalidate,
+	d_delete:	nfs_dentry_delete,
+	d_release:	nfs_dentry_release,
+	d_iput:		nfs_dentry_iput,
+};
 
 static struct dentry *nfs_lookup(struct inode *dir_i, struct dentry * dentry)
 {
@@ -715,7 +675,6 @@
 		nfs_renew_times(dentry);
 		error = 0;
 	}
-	NFS_CACHEINV(dentry->d_parent->d_inode);
 	return error;
 }
 
@@ -809,7 +768,6 @@
 	d_drop(dentry);
 #endif
 	nfs_zap_caches(dir_i);
-	dir_i->i_nlink++;
 	error = NFS_PROTO(dir_i)->mkdir(dir, &dentry->d_name, &attr, &fhandle,
 					&fattr);
 	if (!error && fhandle.size != 0)
@@ -830,13 +788,6 @@
 	nfs_zap_caches(dir_i);
 	error = NFS_PROTO(dir_i)->rmdir(dir, &dentry->d_name);
 
-	/* Update i_nlink and invalidate dentry. */
-	if (!error) {
-		d_drop(dentry);
-		if (dir_i->i_nlink)
-			dir_i->i_nlink--;
-	}
-
 	return error;
 }
 
@@ -919,7 +870,7 @@
  * Remove a file after making sure there are no pending writes,
  * and after checking that the file has only one user. 
  *
- * We update inode->i_nlink and free the inode prior to the operation
+ * We invalidate the attribute cache and free the inode prior to the operation
  * to avoid possible races if the server reuses the inode.
  */
 static int nfs_safe_remove(struct dentry *dentry)
@@ -927,28 +878,12 @@
 	struct dentry *dir = dentry->d_parent;
 	struct inode *dir_i = dir->d_inode;
 	struct inode *inode = dentry->d_inode;
-	int error, rehash = 0;
+	int error = -EBUSY, rehash = 0;
 		
 	dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
 		inode->i_ino);
 
-	/* N.B. not needed now that d_delete is done in advance? */
-	error = -EBUSY;
-	if (!inode) {
-#ifdef NFS_PARANOIA
-printk("nfs_safe_remove: %s/%s already negative??\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
-	}
-
-	if (dentry->d_count > 1) {
-#ifdef NFS_PARANOIA
-printk("nfs_safe_remove: %s/%s busy, d_count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
-#endif
-		goto out;
-	}
 	/*
 	 * Unhash the dentry while we remove the file ...
 	 */
@@ -956,24 +891,26 @@
 		d_drop(dentry);
 		rehash = 1;
 	}
+	if (dentry->d_count > 1) {
+#ifdef NFS_PARANOIA
+		printk("nfs_safe_remove: %s/%s busy, d_count=%d\n",
+			dentry->d_parent->d_name.name, dentry->d_name.name,
+			dentry->d_count);
+#endif
+		goto out;
+	}
 	nfs_zap_caches(dir_i);
+	NFS_CACHEINV(inode);
 	error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name);
 	if (error < 0)
 		goto out;
 	/*
-	 * Update i_nlink and free the inode
-	 */
-	if (inode) {
-		if (inode->i_nlink)
-			inode->i_nlink --;
-		d_delete(dentry);
-	}
-	/*
-	 * Rehash the negative dentry if the operation succeeded.
+	 * Free the inode
 	 */
-	if (rehash)
-		d_add(dentry, NULL);
+	d_delete(dentry);
 out:
+	if (rehash)
+		d_rehash(dentry);
 	return error;
 }
 
@@ -1067,14 +1004,8 @@
 	 */
 	d_drop(dentry);
 	nfs_zap_caches(dir_i);
+	NFS_CACHEINV(inode);
 	error = NFS_PROTO(dir_i)->link(old_dentry, dir, &dentry->d_name);
-	if (!error) {
- 		/*
-		 * Update the link count immediately, as some apps
-		 * (e.g. pine) test this after making a link.
-		 */
-		inode->i_nlink++;
-	}
 	return error;
 }
 
@@ -1107,8 +1038,17 @@
 {
 	struct inode *old_inode = old_dentry->d_inode;
 	struct inode *new_inode = new_dentry->d_inode;
-	struct dentry *dentry = NULL;
-	int error, rehash = 0;
+	struct dentry *dentry = NULL, *rehash = NULL;
+	int error = -EBUSY;
+
+	/*
+	 * To prevent any new references to the target during the rename,
+	 * we unhash the dentry and free the inode in advance.
+	 */
+	if (!d_unhashed(new_dentry)) {
+		d_drop(new_dentry);
+		rehash = new_dentry;
+	}
 
 	dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
 		 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
@@ -1125,7 +1065,6 @@
 	 */
 	if (!new_inode)
 		goto go_ahead;
-	error = -EBUSY;
 	if (S_ISDIR(new_inode->i_mode))
 		goto out;
 	else if (new_dentry->d_count > 1) {
@@ -1139,10 +1078,10 @@
 		/* silly-rename the existing target ... */
 		err = nfs_sillyrename(new_dir, new_dentry);
 		if (!err) {
-			new_dentry = dentry;
+			new_dentry = rehash = dentry;
 			new_inode = NULL;
-			/* hash the replacement target */
-			d_add(new_dentry, NULL);
+			/* instantiate the replacement target */
+			d_instantiate(new_dentry, NULL);
 		}
 
 		/* dentry still busy? */
@@ -1166,14 +1105,6 @@
 		shrink_dcache_parent(old_dentry);
 	}
 
-	/*
-	 * To prevent any new references to the target during the rename,
-	 * we unhash the dentry and free the inode in advance.
-	 */
-	if (!d_unhashed(new_dentry)) {
-		d_drop(new_dentry);
-		rehash = 1;
-	}
 	if (new_inode)
 		d_delete(new_dentry);
 
@@ -1183,15 +1114,12 @@
 					   &old_dentry->d_name,
 					   new_dentry->d_parent,
 					   &new_dentry->d_name);
-	NFS_CACHEINV(old_dir);
-	NFS_CACHEINV(new_dir);
-	/* Update the dcache if needed */
+out:
 	if (rehash)
-		d_add(new_dentry, NULL);
+		d_rehash(rehash);
 	if (!error && !S_ISDIR(old_inode->i_mode))
 		d_move(old_dentry, new_dentry);
 
-out:
 	/* new dentry created? */
 	if (dentry)
 		dput(dentry);

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