patch-2.2.18 linux/fs/nfs/dir.c
Next file: linux/fs/nfs/file.c
Previous file: linux/fs/nfs/Makefile
Back to the patch index
Back to the overall index
- Lines: 1557
- Date:
Fri Sep 15 22:16:06 2000
- Orig file:
v2.2.17/fs/nfs/dir.c
- Orig date:
Fri Apr 21 12:46:44 2000
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/fs/nfs/dir.c linux/fs/nfs/dir.c
@@ -14,6 +14,9 @@
* Following Linus comments on my original hack, this version
* depends only on the dcache stuff and doesn't touch the inode
* layer (iput() and friends).
+ * 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM
+ * 7 Oct 1999 Rewrite of Dave's readdir stuff for NFSv3 support, and in order
+ * to simplify cookie handling. -Trond
*/
#include <linux/sched.h>
@@ -23,32 +26,22 @@
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
+#include <asm/pgtable.h>
#include <linux/mm.h>
#include <linux/sunrpc/types.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs3.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/clnt.h>
#include <asm/segment.h> /* for fs functions */
#define NFS_PARANOIA 1
/* #define NFS_DEBUG_VERBOSE 1 */
-/*
- * Head for a dircache entry. Currently still very simple; when
- * the cache grows larger, we will need a LRU list.
- */
-struct nfs_dirent {
- dev_t dev; /* device number */
- ino_t ino; /* inode number */
- u32 cookie; /* cookie of first entry */
- unsigned short valid : 1, /* data is valid */
- locked : 1; /* entry locked */
- unsigned int size; /* # of entries */
- unsigned long age; /* last used */
- unsigned long mtime; /* last attr stamp */
- struct wait_queue * wait;
- __u32 * entry; /* three __u32's per entry */
-};
-
static int nfs_safe_remove(struct dentry *);
static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *);
@@ -92,276 +85,382 @@
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
- NULL, /* writepage */
- NULL, /* bmap */
+ NULL, /* writepage */
+ NULL, /* bmap */
NULL, /* truncate */
- NULL, /* permission */
+ nfs_permission, /* permission */
NULL, /* smap */
NULL, /* updatepage */
nfs_revalidate, /* revalidate */
};
static ssize_t
-nfs_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+nfs_dir_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
return -EISDIR;
}
-static struct nfs_dirent dircache[NFS_MAX_DIRCACHE];
+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;
-/*
- * We need to do caching of directory entries to prevent an
- * incredible amount of RPC traffic. Only the most recent open
- * directory is cached. This seems sufficient for most purposes.
- * Technically, we ought to flush the cache on close but this is
- * not a problem in practice.
+/* 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.
*
- * XXX: Do proper directory caching by stuffing data into the
- * page cache (may require some fiddling for rsize < PAGE_SIZE).
+ * 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(struct file *filp, void *dirent, filldir_t filldir)
+static
+int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
{
- 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;
- unsigned long age, dead;
- u32 cookie;
- int ismydir, result;
- int i, j, index = 0;
- __u32 *entry;
- char *name, *start;
-
- dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
-
- result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
- if (result < 0)
- goto out;
-
- /*
- * Try to find the entry in the cache
+ struct file *file = desc->file;
+ struct dentry *dir = file->f_dentry;
+ struct inode *inode = dir->d_inode;
+ struct nfs_fattr dir_attr;
+ void *buffer = (void *)page_address(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->offset);
+
+ again:
+ error = NFS_CALL(readdir, inode, (dir, &dir_attr,
+ nfs_file_cred(file),
+ desc->entry->cookie, buffer,
+ NFS_SERVER(inode)->dtsize, plus));
+ nfs_refresh_inode(inode, &dir_attr);
+ /* 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;
+ flush_dcache_page(page_address(page)); /* Is this correct? */
+ set_bit(PG_uptodate, &page->flags);
+
+ /* Ensure consistent page alignment of the data.
+ * Note: assumes we have exclusive access to this inode either
+ * throught inode->i_sem or some other mechanism.
*/
-again:
- if (waitp) {
- interruptible_sleep_on(waitp);
- if (signal_pending(current))
- return -ERESTARTSYS;
- waitp = NULL;
- }
-
- cookie = filp->f_pos;
- entry = NULL;
- free = NULL;
- age = ~(unsigned long) 0;
- dead = jiffies - NFS_ATTRTIMEO(inode);
-
- for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
- /*
- dprintk("NFS: dircache[%d] valid %d locked %d\n",
- i, cache->valid, cache->locked);
- */
- ismydir = (cache->dev == inode->i_dev
- && cache->ino == inode->i_ino);
- if (cache->locked) {
- if (!ismydir || cache->cookie != cookie)
- continue;
- dfprintk(DIRCACHE, "NFS: waiting on dircache entry\n");
- waitp = &cache->wait;
- goto again;
- }
-
- if (ismydir && cache->mtime != inode->i_mtime)
- cache->valid = 0;
-
- if (!cache->valid || cache->age < dead) {
- free = cache;
- age = 0;
- } else if (cache->age < age) {
- free = cache;
- age = cache->age;
- }
-
- if (!ismydir || !cache->valid)
- continue;
-
- if (cache->cookie == cookie && cache->size > 0) {
- entry = cache->entry + (index = 0);
- cache->locked = 1;
- break;
- }
- for (j = 0; j < cache->size; j++) {
- __u32 *this_ent = cache->entry + j*3;
+ if (page->offset == 0)
+ invalidate_inode_pages(inode);
+ nfs_unlock_page(page);
+ return 0;
+ error:
+ set_bit(PG_error, &page->flags);
+ nfs_unlock_page(page);
+ desc->error = error;
+ return -EIO;
+}
- if (*(this_ent+1) != cookie)
- continue;
- if (j < cache->size - 1) {
- index = j + 1;
- entry = this_ent + 3;
- } else if (*(this_ent+2) & (1 << 15)) {
- /* eof */
- return 0;
- }
+/*
+ * Given a pointer to a buffer that has already been filled by a call
+ * to readdir, find the next entry.
+ *
+ * If the end of the buffer has been reached, return -EAGAIN, if not,
+ * return the offset within the buffer of the next entry to be
+ * read.
+ */
+static inline
+int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page)
+{
+ struct nfs_entry *entry = desc->entry;
+ char *start = (char *)page_address(page),
+ *p = start;
+ int loop_count = 0,
+ status = 0;
+
+ for(;;) {
+ p = (char *)desc->decode((u32*)p, entry, desc->plus);
+ if (IS_ERR(p)) {
+ status = PTR_ERR(p);
break;
}
- if (entry) {
- dfprintk(DIRCACHE, "NFS: found dircache entry %d\n",
- (int)(cache - dircache));
- cache->locked = 1;
+ 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();
}
}
+ dfprintk(VFS, "NFS: find_dirent() returns %d\n", status);
+ return status;
+}
- /*
- * Okay, entry not present in cache, or locked and inaccessible.
- * Set up the cache entry and attempt a READDIR call.
- */
- if (entry == NULL) {
- if ((cache = free) == NULL) {
- dfprintk(DIRCACHE, "NFS: dircache contention\n");
- waitp = &readdir_wait;
- goto again;
- }
- dfprintk(DIRCACHE, "NFS: using free dircache entry %d\n",
- (int)(free - dircache));
- cache->cookie = cookie;
- cache->locked = 1;
- cache->valid = 0;
- cache->dev = inode->i_dev;
- cache->ino = inode->i_ino;
- if (!cache->entry) {
- result = -ENOMEM;
- cache->entry = (__u32 *) get_free_page(GFP_KERNEL);
- if (!cache->entry)
- goto done;
- }
-
- result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
- cookie, PAGE_SIZE, cache->entry);
- if (result <= 0)
- goto done;
- cache->size = result;
- cache->valid = 1;
- entry = cache->entry + (index = 0);
- }
- cache->mtime = inode->i_mtime;
- cache->age = jiffies;
-
- /*
- * Yowza! We have a cache entry...
- */
- start = (char *) cache->entry;
- while (index < cache->size) {
- __u32 fileid = *entry++;
- __u32 nextpos = *entry++; /* cookie */
- __u32 length = *entry++;
+/*
+ * Find the given page, and call find_dirent() in order to try to
+ * return the next entry.
+ */
+static inline
+int find_dirent_page(nfs_readdir_descriptor_t *desc)
+{
+ struct inode *inode = desc->file->f_dentry->d_inode;
+ struct page *page;
+ unsigned long index = desc->page_index;
+ int status;
- /*
- * Unpack the eof flag, offset, and length
- */
- result = length & (1 << 15); /* eof flag */
- name = start + ((length >> 16) & 0xFFFF);
- length &= 0x7FFF;
- /*
- dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry,
- (int) length, name, length,
- (unsigned int) filp->f_pos,
- fileid, result);
- */
+ dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index);
- if (filldir(dirent, name, length, cookie, fileid) < 0)
- break;
- cookie = nextpos;
- index++;
+ if (desc->page) {
+ page_cache_release(desc->page);
+ desc->page = NULL;
}
- filp->f_pos = cookie;
- result = 0;
- /* XXX: May want to kick async readdir-ahead here. Not too hard
- * to do. */
-
-done:
- dfprintk(DIRCACHE, "NFS: nfs_readdir complete\n");
- cache->locked = 0;
- wake_up(&cache->wait);
- wake_up(&readdir_wait);
+ page = read_cache_page(inode, index,
+ (filler_t *)nfs_readdir_filler, desc);
+ if (IS_ERR(page)) {
+ status = PTR_ERR(page);
+ goto out;
+ }
-out:
- return result;
+ /* 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;
}
/*
- * Invalidate dircache entries for an inode.
+ * 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 'desc->target'.
*/
-void
-nfs_invalidate_dircache(struct inode *inode)
+static inline
+int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
{
- struct nfs_dirent *cache = dircache;
- dev_t dev = inode->i_dev;
- ino_t ino = inode->i_ino;
- int i;
-
- dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino);
- for (i = NFS_MAX_DIRCACHE; i--; cache++) {
- if (cache->ino != ino)
- continue;
- if (cache->dev != dev)
- continue;
- if (cache->locked) {
- printk("NFS: cache locked for %s/%ld\n",
- kdevname(dev), (long) ino);
- continue;
+ int res = 0;
+ int loop_count = 0;
+
+ dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target);
+ for (;;) {
+ res = find_dirent_page(desc);
+ if (res != -EAGAIN)
+ break;
+ /* Align to beginning of next page */
+ desc->page_offset = 0;
+ desc->page_index += PAGE_CACHE_SIZE;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
}
- cache->valid = 0; /* brute force */
}
+ dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res);
+ return res;
}
/*
- * Invalidate the dircache for a super block (or all caches),
- * and release the cache memory.
+ * Once we've found the start of the dirent within a page: fill 'er up...
*/
-void
-nfs_invalidate_dircache_sb(struct super_block *sb)
-{
- struct nfs_dirent *cache = dircache;
- int i;
-
- for (i = NFS_MAX_DIRCACHE; i--; cache++) {
- if (sb && sb->s_dev != cache->dev)
- continue;
- if (cache->locked) {
- printk("NFS: cache locked at umount %s\n",
- (cache->entry ? "(lost a page!)" : ""));
- continue;
+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 *)page_address(desc->page),
+ *p = start + desc->page_offset;
+ unsigned long fileid;
+ int loop_count = 0,
+ res = 0;
+
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target);
+
+ 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 += PAGE_CACHE_SIZE;
+ }
+ break;
}
- cache->valid = 0; /* brute force */
- if (cache->entry) {
- free_page((unsigned long) cache->entry);
- cache->entry = NULL;
+ desc->page_offset = p - start;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
}
}
+ page_cache_release(desc->page);
+ desc->page = NULL;
+
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res);
+ return res;
}
/*
- * Free directory cache memory
- * Called from cleanup_module
+ * 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.
*/
-void
-nfs_free_dircache(void)
+static inline
+int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
+ filldir_t filldir)
+{
+ struct file *file = desc->file;
+ struct dentry *dir = file->f_dentry;
+ struct inode *inode = dir->d_inode;
+ struct nfs_fattr dir_attr;
+ struct page *page = NULL;
+ unsigned long cache_page;
+ 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;
+ }
+
+ cache_page = page_cache_alloc();
+ if (!cache_page) {
+ status = -ENOMEM;
+ goto out;
+ }
+ page = page_cache_entry(cache_page);
+ p = (u32 *)page_address(page);
+ status = NFS_CALL(readdir, inode, (dir, &dir_attr,
+ nfs_file_cred(file),
+ desc->target, p,
+ NFS_SERVER(inode)->dtsize, 0));
+ nfs_refresh_inode(inode, &dir_attr);
+ if (status >= 0) {
+ p = desc->decode(p, desc->entry, 0);
+ if (IS_ERR(p))
+ status = PTR_ERR(p);
+ else
+ desc->entry->prev_cookie = desc->target;
+ }
+ 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
+ * page cache as is the case in most of the other filesystems.
+ */
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- dfprintk(DIRCACHE, "NFS: freeing dircache\n");
- nfs_invalidate_dircache_sb(NULL);
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ nfs_readdir_descriptor_t my_desc,
+ *desc = &my_desc;
+ struct nfs_entry my_entry;
+ long res;
+
+ res = nfs_revalidate(dentry);
+ if (res < 0)
+ return res;
+
+ /*
+ * filp->f_pos points to the file offset in the page cache.
+ * but if the cache has meanwhile been zapped, we need to
+ * read from the last dirent to revalidate f_pos
+ * itself.
+ */
+ memset(desc, 0, sizeof(*desc));
+ memset(&my_entry, 0, sizeof(my_entry));
+
+ desc->file = filp;
+ desc->target = filp->f_pos;
+ desc->entry = &my_entry;
+ desc->decode = NFS_PROTO(inode)->decode_dirent;
+
+ 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;
+
+ res = nfs_do_filldir(desc, dirent, filldir);
+ if (res < 0) {
+ res = 0;
+ break;
+ }
+ }
+ if (desc->page)
+ page_cache_release(desc->page);
+ if (desc->error < 0)
+ return desc->error;
+ if (res < 0)
+ return res;
+ return 0;
}
+
/*
* Whenever an NFS operation succeeds, we know that the dentry
* is valid, so we update the revalidation timestamp.
*/
-static inline void nfs_renew_times(struct dentry * dentry)
+static inline void
+nfs_renew_times(struct dentry * dentry)
{
- dentry->d_time = jiffies;
+ dentry->d_time = jiffies;
}
static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags)
@@ -384,7 +483,7 @@
if (diff < 15*60)
timeout = 0;
}
-
+
return time_after(jiffies,dentry->d_time + timeout);
}
@@ -398,8 +497,9 @@
#define NFS_REVALIDATE_NEGATIVE (1 * HZ)
static inline int nfs_neg_need_reval(struct dentry *dentry)
{
- unsigned long timeout = NFS_ATTRTIMEO(dentry->d_parent->d_inode);
- long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime;
+ struct inode *dir = dentry->d_parent->d_inode;
+ unsigned long timeout = NFS_ATTRTIMEO(dir);
+ long diff = CURRENT_TIME - dir->i_mtime;
if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE)
timeout = NFS_REVALIDATE_NEGATIVE;
@@ -421,11 +521,12 @@
*/
static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
{
- struct dentry * parent = dentry->d_parent;
- struct inode * inode = dentry->d_inode;
+ struct dentry *dir = dentry->d_parent;
+ struct inode *inode = dentry->d_inode,
+ *dir_i = dir->d_inode;
+ struct nfs_fh fhandle;
+ struct nfs_fattr fattr, dir_attr;
int error;
- struct nfs_fh fhandle;
- struct nfs_fattr fattr;
/*
* If we don't have an inode, let's look at the parent
@@ -440,39 +541,52 @@
if (is_bad_inode(inode)) {
dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
- parent->d_name.name, dentry->d_name.name);
+ dir->d_name.name, dentry->d_name.name);
goto out_bad;
}
- if (IS_ROOT(dentry))
- goto out_valid;
-
if (!nfs_dentry_force_reval(dentry, flags))
goto out_valid;
+ if (IS_ROOT(dentry)) {
+ __nfs_revalidate_inode(dentry);
+ goto out_valid_renew;
+ }
+
+ if (NFS_FLAGS(inode) & NFS_INO_STALE)
+ goto out_bad;
+
/*
* Do a new lookup and check the dentry attributes.
*/
- error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent),
- dentry->d_name.name, &fhandle, &fattr);
- if (error)
+ error = NFS_CALL(lookup, dir_i, (dir, &dir_attr,
+ &dentry->d_name, &fhandle, &fattr));
+ if (error < 0)
goto out_bad;
/* Inode number matches? */
- if (fattr.fileid != inode->i_ino)
+ if (!(fattr.valid & NFS_ATTR_FATTR) ||
+ NFS_FSID(inode) != fattr.fsid ||
+ NFS_FILEID(inode) != fattr.fileid)
goto out_bad;
/* Filehandle matches? */
- if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh)))
+ if (NFS_FH(dentry)->size == 0)
+ goto out_bad;
+
+ if (NFS_FH(dentry)->size != fhandle.size ||
+ memcmp(NFS_FH(dentry)->data, fhandle.data, fhandle.size))
goto out_bad;
/* Ok, remeber that we successfully checked it.. */
- nfs_renew_times(dentry);
nfs_refresh_inode(inode, &fattr);
+ nfs_refresh_inode(dir_i, &dir_attr);
-out_valid:
+ out_valid_renew:
+ nfs_renew_times(dentry);
+ out_valid:
return 1;
-out_bad:
+ out_bad:
if (!list_empty(&dentry->d_subdirs))
shrink_dcache_parent(dentry);
/* If we have submounts, don't unhash ! */
@@ -480,35 +594,37 @@
goto out_valid;
d_drop(dentry);
if (dentry->d_parent->d_inode)
- nfs_invalidate_dircache(dentry->d_parent->d_inode);
+ NFS_CACHEINV(dentry->d_parent->d_inode);
if (inode && S_ISDIR(inode->i_mode))
- nfs_invalidate_dircache(inode);
+ NFS_CACHEINV(inode);
return 0;
}
/*
* 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)
{
- 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 */
+ /* Unhash it, so that ->d_iput() would be called */
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);
}
+}
+
+__inline__ struct nfs_fh *nfs_fh_alloc(void)
+{
+ struct nfs_fh *p;
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return NULL;
+ memset(p, 0, sizeof(*p));
+ return p;
+}
+
+__inline__ void nfs_fh_free(struct nfs_fh *p)
+{
+ kfree(p);
}
/*
@@ -516,8 +632,21 @@
*/
static void nfs_dentry_release(struct dentry *dentry)
{
- if (dentry->d_fsdata)
- kfree(dentry->d_fsdata);
+ if (dentry->d_fsdata) {
+ nfs_fh_free(dentry->d_fsdata);
+ dentry->d_fsdata = NULL;
+ }
+}
+
+/*
+ * Called when the dentry loses inode.
+ * We use it to clean up silly-renamed files.
+ */
+static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
+{
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
+ nfs_complete_unlink(dentry);
+ iput(inode);
}
struct dentry_operations nfs_dentry_operations = {
@@ -526,36 +655,47 @@
NULL, /* d_compare */
nfs_dentry_delete, /* d_delete(struct dentry *) */
nfs_dentry_release, /* d_release(struct dentry *) */
- NULL /* d_iput */
+ nfs_dentry_iput /* d_iput */
};
-static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry)
+static struct dentry *nfs_lookup(struct inode *dir_i, struct dentry * dentry)
{
+ struct dentry *dir = dentry->d_parent;
struct inode *inode;
int error;
struct nfs_fh fhandle;
- struct nfs_fattr fattr;
+ struct nfs_fattr fattr, dir_attr;
dfprintk(VFS, "NFS: lookup(%s/%s)\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
error = -ENAMETOOLONG;
- if (dentry->d_name.len > NFS_MAXNAMLEN)
+ if (dentry->d_name.len > NFS_SERVER(dir_i)->namelen)
goto out;
- error = -ENOMEM;
+ dentry->d_op = &nfs_dentry_operations;
+
if (!dentry->d_fsdata) {
- dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
- if (!dentry->d_fsdata)
+ dentry->d_fsdata = nfs_fh_alloc();
+ if (!dentry->d_fsdata) {
+ error = -ENOMEM;
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);
+#if NFS_FIXME
+ inode = nfs_dircache_lookup(dir_i, dentry);
+ if (inode)
+ goto no_entry;
+#endif
+
+ error = NFS_CALL(lookup, dir_i, (dir, &dir_attr,
+ &dentry->d_name, &fhandle, &fattr));
+ nfs_refresh_inode(dir_i, &dir_attr);
inode = NULL;
if (error == -ENOENT)
goto no_entry;
+
if (!error) {
error = -EACCES;
inode = nfs_fhget(dentry, &fhandle, &fattr);
@@ -594,29 +734,46 @@
* 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)
+static int nfs_create(struct inode *dir_i, struct dentry *dentry, int mode)
{
- int error;
- struct nfs_sattr sattr;
- struct nfs_fattr fattr;
- struct nfs_fh fhandle;
+ struct dentry *dir = dentry->d_parent;
+ struct iattr attr;
+ struct nfs_fattr fattr, dir_attr;
+ struct nfs_fh fhandle;
+ int error;
dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
- sattr.mode = mode;
- sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
- sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+#ifdef NFSD_BROKEN_UID
+ /* We set uid/gid in the request because IBM's broken nfsd
+ * uses the root uid/gid otherwise. Argh!
+ * (Hopefully the server will override the gid when the directory
+ * has the sticky bit set. Irix may have a problem here...)
+ */
+ attr.ia_mode = mode;
+ attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+ attr.ia_uid = current->fsuid;
+ attr.ia_gid = current->fsgid;
+#else
+ attr.ia_mode = mode;
+ attr.ia_valid = ATTR_MODE;
+#endif
/*
* Invalidate the dir cache before the operation to avoid a race.
+ * The 0 argument passed into the create function should one day
+ * contain the O_EXCL flag if requested. This allows NFSv3 to
+ * select the appropriate create strategy. Currently open_namei
+ * does not pass the create flags.
*/
- nfs_invalidate_dircache(dir);
- error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
- dentry->d_name.name, &sattr, &fhandle, &fattr);
- if (!error)
+ nfs_zap_caches(dir_i);
+ error = NFS_CALL(create, dir_i, (dir, &dir_attr, &dentry->d_name,
+ &attr, 0, &fhandle, &fattr));
+ nfs_refresh_inode(dir_i, &dir_attr);
+ if (!error && fhandle.size != 0)
error = nfs_instantiate(dentry, &fhandle, &fattr);
- if (error)
+ if (error || fhandle.size == 0)
d_drop(dentry);
return error;
}
@@ -624,28 +781,35 @@
/*
* See comments for nfs_proc_create regarding failed operations.
*/
-static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+static int nfs_mknod(struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
{
- int error;
- struct nfs_sattr sattr;
- struct nfs_fattr fattr;
- struct nfs_fh fhandle;
+ struct dentry *dir = dentry->d_parent;
+ struct iattr attr;
+ struct nfs_fattr fattr, dir_attr;
+ struct nfs_fh fhandle;
+ int error;
dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
- sattr.mode = mode;
- 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(dentry->d_parent),
- dentry->d_name.name, &sattr, &fhandle, &fattr);
- if (!error)
+#ifdef NFSD_BROKEN_UID
+ attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+ attr.ia_mode = mode;
+ attr.ia_uid = current->fsuid;
+ attr.ia_gid = current->fsgid;
+#else
+ attr.ia_valid = ATTR_MODE;
+ attr.ia_mode = mode;
+#endif
+
+
+ nfs_zap_caches(dir_i);
+ error = NFS_CALL(mknod, dir_i, (dir, &dir_attr, &dentry->d_name,
+ &attr, rdev, &fhandle, &fattr));
+ nfs_refresh_inode(dir_i, &dir_attr);
+ if (!error && fhandle.size != 0)
error = nfs_instantiate(dentry, &fhandle, &fattr);
- if (error)
+ if (error || fhandle.size == 0)
d_drop(dentry);
return error;
}
@@ -653,52 +817,55 @@
/*
* See comments for nfs_proc_create regarding failed operations.
*/
-static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+static int nfs_mkdir(struct inode *dir_i, struct dentry *dentry, int mode)
{
- int error;
- struct nfs_sattr sattr;
- struct nfs_fattr fattr;
- struct nfs_fh fhandle;
-
- dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ struct dentry *dir = dentry->d_parent;
+ struct iattr attr;
+ struct nfs_fattr fattr, dir_attr;
+ struct nfs_fh fhandle;
+ int error;
+
+ dfprintk(VFS, "NFS: mkdir(%x/%ld, %s)\n",
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
+
+#ifdef NFSD_BROKEN_UID
+ attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+ attr.ia_mode = mode | S_IFDIR;
+ attr.ia_uid = current->fsuid;
+ attr.ia_gid = current->fsgid;
+#else
+ attr.ia_valid = ATTR_MODE;
+ attr.ia_mode = mode | S_IFDIR;
+#endif
- sattr.mode = mode | S_IFDIR;
- sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
- sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+ nfs_zap_caches(dir_i);
+ error = NFS_CALL(mkdir, dir_i, (dir, &dir_attr,
+ &dentry->d_name, &attr, &fhandle, &fattr));
+ nfs_refresh_inode(dir_i, &dir_attr);
+ if (!error && fhandle.size != 0)
+ error = nfs_instantiate(dentry, &fhandle, &fattr);
- /*
- * Always drop the dentry, we can't always depend on
- * the fattr returned by the server (AIX seems to be
- * broken). We're better off doing another lookup than
- * depending on potentially bogus information.
- */
- d_drop(dentry);
- nfs_invalidate_dircache(dir);
- error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
- dentry->d_name.name, &sattr, &fhandle, &fattr);
- if (!error)
- dir->i_nlink++;
+ if (error || fhandle.size == 0)
+ d_drop(dentry);
return error;
}
-static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
+static int nfs_rmdir(struct inode *dir_i, struct dentry *dentry)
{
- int error;
+ struct dentry *dir = dentry->d_parent;
+ struct nfs_fattr dir_attr;
+ int error;
dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name);
- nfs_invalidate_dircache(dir);
- error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
- dentry->d_name.name);
+ nfs_zap_caches(dir_i);
+ error = NFS_CALL(rmdir, dir_i, (dir, &dir_attr, &dentry->d_name));
+ nfs_refresh_inode(dir_i, &dir_attr);
- /* Update i_nlink and invalidate dentry. */
- if (!error) {
- d_drop(dentry);
- if (dir->i_nlink)
- dir->i_nlink--;
- }
+ /* Free the inode */
+ if (!error)
+ d_delete(dentry);
return error;
}
@@ -758,15 +925,18 @@
return sdentry;
}
-static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
+static int nfs_sillyrename(struct inode *dir_i, struct dentry *dentry)
{
+ struct dentry *dir = dentry->d_parent;
static unsigned int sillycounter = 0;
- const int i_inosize = sizeof(dir->i_ino)*2;
- const int countersize = sizeof(sillycounter)*2;
- const int slen = strlen(".nfs") + i_inosize + countersize;
- char silly[slen+1];
- struct dentry *sdentry;
- int error = -EIO;
+ struct nfs_fattr dir_attr;
+ const int i_inosize = sizeof(dir_i->i_ino)*2;
+ const int countersize = sizeof(sillycounter)*2;
+ const int slen = strlen(".nfs") + i_inosize + countersize;
+ struct qstr qsilly;
+ char silly[slen+1];
+ struct dentry * sdentry;
+ int error = -EIO;
dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -776,15 +946,13 @@
* Note that a silly-renamed file can be deleted once it's
* no longer in use -- it's just an ordinary file now.
*/
- if (dentry->d_count == 1) {
- dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
+ if (dentry->d_count == 1)
goto out; /* No need to silly rename. */
- }
#ifdef NFS_PARANOIA
-if (!dentry->d_inode)
-printk("NFS: silly-renaming %s/%s, negative dentry??\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+ if (!dentry->d_inode)
+ printk(KERN_ERR "NFS: silly-renaming %s/%s, negative dentry??\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
/*
* We don't allow a dentry to be silly-renamed twice.
@@ -800,7 +968,8 @@
do {
char *suffix = silly + slen - countersize;
- dput(sdentry);
+ if (sdentry)
+ dput(sdentry);
sillycounter++;
sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);
@@ -816,14 +985,16 @@
goto out;
} while(sdentry->d_inode != NULL); /* need negative lookup */
- nfs_invalidate_dircache(dir);
- error = nfs_proc_rename(NFS_SERVER(dir),
- NFS_FH(dentry->d_parent), dentry->d_name.name,
- NFS_FH(dentry->d_parent), silly);
+ nfs_zap_caches(dir_i);
+ qsilly.name = silly;
+ qsilly.len = strlen(silly);
+ error = NFS_CALL(rename, dir_i, (dir, &dir_attr, &dentry->d_name,
+ dir, &dir_attr, &qsilly));
+ nfs_refresh_inode(dir_i, &dir_attr);
if (!error) {
nfs_renew_times(dentry);
d_move(dentry, sdentry);
- dentry->d_flags |= DCACHE_NFSFS_RENAMED;
+ error = nfs_async_unlink(dentry);
/* If we return 0 we don't unlink */
}
dput(sdentry);
@@ -840,54 +1011,63 @@
*/
static int nfs_safe_remove(struct dentry *dentry)
{
- struct inode *dir = dentry->d_parent->d_inode;
- struct inode *inode = dentry->d_inode;
- int error, rehash = 0;
+ struct nfs_fattr dir_attr;
+ struct dentry *dir = dentry->d_parent;
+ struct inode *dir_i = dir->d_inode,
+ *inode = dentry->d_inode;
+ int error = 0, rehash = 0;
- dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name,
- inode->i_ino);
+ /*
+ * Unhash the dentry while we remove the file ...
+ */
+ if (!list_empty(&dentry->d_hash)) {
+ d_drop(dentry);
+ rehash = 1;
+ }
+
+ /* If the dentry was sillyrenamed, we simply call d_delete() */
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
+ goto out_delete;
+
+ dfprintk(VFS, "NFS: safe_remove(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
/* 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);
+ printk(KERN_ERR "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);
+ printk(KERN_INFO "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);
+ if (inode)
+ NFS_CACHEINV(inode);
+ error = NFS_CALL(remove, dir_i, (dir, &dir_attr, &dentry->d_name));
+ nfs_refresh_inode(dir_i, &dir_attr);
+ if (error < 0)
+ goto out;
+
+ out_delete:
/*
- * Unhash the dentry while we remove the file ...
- */
- if (!list_empty(&dentry->d_hash)) {
- d_drop(dentry);
- rehash = 1;
- }
- /*
- * Update i_nlink and free the inode before unlinking.
+ * Free the inode
*/
- if (inode) {
- if (inode->i_nlink)
- inode->i_nlink --;
- d_delete(dentry);
- }
- nfs_invalidate_dircache(dir);
- error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
- dentry->d_name.name);
+ d_delete(dentry);
+out:
/*
* Rehash the negative dentry if the operation succeeded.
*/
- if (!error && rehash)
- d_add(dentry, NULL);
-out:
+ if (rehash)
+ d_rehash(dentry);
return error;
}
@@ -903,6 +1083,8 @@
dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
dir->i_dev, dir->i_ino, dentry->d_name.name);
+ if (dentry->d_inode)
+ nfs_wb_all(dentry->d_inode);
error = nfs_sillyrename(dir, dentry);
if (error && error != -EBUSY) {
error = nfs_safe_remove(dentry);
@@ -914,45 +1096,60 @@
}
static int
-nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+nfs_symlink(struct inode *dir_i, struct dentry *dentry, const char *symname)
{
- struct nfs_sattr sattr;
- int error;
+ struct dentry *dir = dentry->d_parent;
+ struct nfs_fattr dir_attr, sym_attr;
+ struct nfs_fh sym_fh;
+ struct iattr attr;
+ struct qstr qsymname;
+ int error, mode, maxlen;
dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
+ dir_i->i_dev, dir_i->i_ino, dentry->d_name.name, symname);
error = -ENAMETOOLONG;
- if (strlen(symname) > NFS_MAXPATHLEN)
+ maxlen = (NFS_PROTO(dir_i)->version==2) ? NFS2_MAXPATHLEN : NFS3_MAXPATHLEN;
+ if (strlen(symname) > maxlen)
goto out;
#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);
+ if (dentry->d_inode)
+ printk(KERN_WARNING "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;
+#ifdef NFSD_BROKEN_UID
+ attr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID;
+ attr.ia_mode = mode = S_IFLNK | S_IRWXUGO;
+ attr.ia_uid = current->fsuid;
+ attr.ia_gid = current->fsgid;
+#else
+ attr.ia_valid = ATTR_MODE;
+ attr.ia_mode = mode = S_IFLNK | S_IRWXUGO;
+#endif
- /*
- * 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);
- nfs_invalidate_dircache(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);
- } else if (error == -EEXIST) {
- printk("nfs_proc_symlink: %s/%s already exists??\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
+
+ qsymname.name = symname;
+ qsymname.len = strlen(symname);
+
+ nfs_zap_caches(dir_i);
+ error = NFS_CALL(symlink, dir_i, (dir, &dir_attr,
+ &dentry->d_name, &qsymname, &attr,
+ &sym_fh, &sym_attr));
+ nfs_refresh_inode(dir_i, &dir_attr);
+ if (!error && sym_fh.size != 0 && (sym_attr.valid & NFS_ATTR_FATTR)) {
+ error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
+ } else {
+ if (error == -EEXIST)
+ printk(KERN_INFO "nfs_proc_symlink: %s/%s already exists??\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ d_drop(dentry);
}
out:
@@ -960,10 +1157,12 @@
}
static int
-nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+nfs_link(struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
{
- struct inode *inode = old_dentry->d_inode;
- int error;
+ struct dentry *dir = dentry->d_parent;
+ struct inode *inode = old_dentry->d_inode;
+ struct nfs_fattr old_attr, dir_attr;
+ int error;
dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
@@ -975,16 +1174,12 @@
* we can't use the existing dentry.
*/
d_drop(dentry);
- nfs_invalidate_dircache(dir);
- error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
- NFS_FH(dentry->d_parent), dentry->d_name.name);
- if (!error) {
- /*
- * Update the link count immediately, as some apps
- * (e.g. pine) test this after making a link.
- */
- inode->i_nlink++;
- }
+ nfs_zap_caches(dir_i);
+ NFS_CACHEINV(inode);
+ error = NFS_CALL(link, inode, (old_dentry, &old_attr,
+ dir, &dir_attr, &dentry->d_name));
+ nfs_refresh_inode(inode, &old_attr);
+ nfs_refresh_inode(dir_i, &dir_attr);
return error;
}
@@ -1011,14 +1206,28 @@
* no pending writes (if it's a file), and the use count must be 1.
* If these conditions are met, we can drop the dentries before doing
* the rename.
+ *
+ * FIXME: Sun seems to take this even one step further. The connectathon
+ * test suite has a file that renames open file A to open file B,
+ * and expects a silly rename to happen for B.
*/
static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_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 nfs_fattr old_attr, new_attr;
+ struct inode * old_inode = old_dentry->d_inode;
+ struct inode * new_inode = new_dentry->d_inode;
+ struct dentry * dentry = NULL, *rehash = NULL;
+ int error;
+
+ /*
+ * To prevent any new references to the target during the rename,
+ * we unhash the dentry and free the inode in advance.
+ */
+ if (!list_empty(&new_dentry->d_hash)) {
+ 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,
@@ -1036,6 +1245,8 @@
* With directories check is done in VFS.
*/
error = -EBUSY;
+ if (new_inode)
+ nfs_wb_all(new_inode);
if (new_dentry->d_count > 1 && new_inode) {
int err;
/* copy the target dentry's name */
@@ -1047,17 +1258,18 @@
/* 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);
+ d_instantiate(new_dentry, NULL);
}
/* dentry still busy? */
if (new_dentry->d_count > 1) {
#ifdef NFS_PARANOIA
-printk("nfs_rename: target %s/%s busy, d_count=%d\n",
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+ printk(KERN_INFO "nfs_rename: target %s/%s busy, d_count=%d\n",
+ new_dentry->d_parent->d_name.name,
+ new_dentry->d_name.name,new_dentry->d_count);
#endif
goto out;
}
@@ -1073,40 +1285,75 @@
if (new_dentry->d_count > 1 && new_inode) {
#ifdef NFS_PARANOIA
-printk("nfs_rename: new dentry %s/%s busy, d_count=%d\n",
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+ printk(KERN_INFO "nfs_rename: new dentry %s/%s busy, d_count=%d\n",
+ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
#endif
goto out;
}
- /*
- * To prevent any new references to the target during the rename,
- * we unhash the dentry and free the inode in advance.
- */
- if (!list_empty(&new_dentry->d_hash)) {
- d_drop(new_dentry);
- rehash = 1;
- }
if (new_inode)
d_delete(new_dentry);
- nfs_invalidate_dircache(new_dir);
- nfs_invalidate_dircache(old_dir);
- 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);
+ nfs_zap_caches(new_dir);
+ nfs_zap_caches(old_dir);
+ error = NFS_CALL(rename, old_dir,
+ (old_dentry->d_parent, &old_attr, &old_dentry->d_name,
+ new_dentry->d_parent, &new_attr, &new_dentry->d_name));
+ nfs_refresh_inode(old_dir, &old_attr);
+ nfs_refresh_inode(new_dir, &new_attr);
+out:
/* Update the dcache if needed */
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);
return error;
+}
+
+int
+nfs_permission(struct inode *i, int msk)
+{
+ struct nfs_fattr fattr;
+ struct dentry *de = NULL;
+ int err = vfs_permission(i, msk);
+ struct list_head *start, *tmp;
+
+ if (!NFS_PROTO(i)->access)
+ goto out;
+ /*
+ * Trust UNIX mode bits except:
+ *
+ * 1) When override capabilities may have been invoked
+ * 2) When root squashing may be involved
+ * 3) When ACLs may overturn a negative answer */
+ if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH)
+ && (current->fsuid != 0) && (current->fsgid != 0)
+ && err != -EACCES)
+ goto out;
+
+ tmp = start = &i->i_dentry;
+ while ((tmp = tmp->next) != start) {
+ de = list_entry(tmp, struct dentry, d_alias);
+ if (de->d_inode == i)
+ break;
+ }
+ if (!de || de->d_inode != i)
+ return 0;
+
+ err = NFS_CALL(access, i, (de, msk, &fattr, 0));
+
+ if (err == -EACCES && NFS_CLIENT(i)->cl_droppriv &&
+ current->uid != 0 && current->gid != 0 &&
+ (current->fsuid != current->uid || current->fsgid != current->gid))
+ err = NFS_CALL(access, i, (de, msk, &fattr, 1));
+
+ nfs_refresh_inode(i, &fattr);
+ out:
+ return err;
}
/*
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)