summaryrefslogtreecommitdiffstats
path: root/fs/nfs/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r--fs/nfs/dir.c656
1 files changed, 292 insertions, 364 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 2d2ee4a02..f35ef3bdb 100644
--- a/fs/nfs/dir.c
+++ b/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 @@ struct inode_operations nfs_dir_inode_operations = {
};
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 @@ typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
* 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)
+int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page)
{
- u8 *p = (u8 *)kmap(page),
- *start = p;
- unsigned long base = page_offset(page),
- pg_offset = 0;
- int loop_count = 0;
+ 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);
+ unsigned long index = desc->page_index;
+ int status;
- if (page && Page_Uptodate(page))
- status = find_dirent(page, offset, entry, decode, plus, use_cookie);
+ dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index);
- /* 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;
+ if (desc->page) {
+ page_cache_release(desc->page);
+ desc->page = NULL;
+ }
- dfprintk(VFS, "NFS: find_dirent_page() returns %ld\n", status);
+ 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)
+static
+int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
+ filldir_t filldir)
{
- 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();
- }
+ 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);
-
- dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ offset %ld; returning = %d\n", entry->offset, res);
- return res;
+ if (status < 0)
+ goto out_release;
+
+ 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 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
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 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
* 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 @@ out_bad:
/*
* 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 @@ static void nfs_dentry_release(struct dentry *dentry)
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 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
nfs_renew_times(dentry);
error = 0;
}
- NFS_CACHEINV(dentry->d_parent->d_inode);
return error;
}
@@ -809,7 +768,6 @@ static int nfs_mkdir(struct inode *dir_i, struct dentry *dentry, int mode)
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 @@ static int nfs_rmdir(struct inode *dir_i, struct dentry *dentry)
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 @@ out:
* 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 @@ static int nfs_safe_remove(struct dentry *dentry)
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 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
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
+ * Free the inode
*/
- if (inode) {
- if (inode->i_nlink)
- inode->i_nlink --;
- d_delete(dentry);
- }
- /*
- * Rehash the negative dentry if the operation succeeded.
- */
- if (rehash)
- d_add(dentry, NULL);
+ d_delete(dentry);
out:
+ if (rehash)
+ d_rehash(dentry);
return error;
}
@@ -1067,14 +1004,8 @@ nfs_link(struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
*/
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 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
{
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 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
*/
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 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
/* 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 @@ go_ahead:
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 @@ go_ahead:
&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);