summaryrefslogtreecommitdiffstats
path: root/fs/nfs/dir.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /fs/nfs/dir.c
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r--fs/nfs/dir.c500
1 files changed, 352 insertions, 148 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 8f16dc799..a11b9fb6a 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -6,19 +6,38 @@
* nfs directory handling functions
*
* 10 Apr 1996 Added silly rename for unlink --okir
+ * 28 Sep 1996 Improved directory cache --okir
*/
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/stat.h>
-#include <linux/nfs_fs.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/mm.h>
+#include <linux/sunrpc/types.h>
+#include <linux/nfs_fs.h>
-#include <asm/uaccess.h> /* for fs functions */
+#include <asm/segment.h> /* for fs functions */
+
+/*
+ * 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; /* cooke 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;
+ struct nfs_entry * entry;
+};
static int nfs_dir_open(struct inode * inode, struct file * file);
static long nfs_dir_read(struct inode *, struct file *, char *, unsigned long);
@@ -64,42 +83,26 @@ struct inode_operations nfs_dir_inode_operations = {
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ nfs_revalidate, /* revalidate */
};
-static inline void revalidate_dir(struct nfs_server * server, struct inode * dir)
+static int
+nfs_dir_open(struct inode *dir, struct file *file)
{
- struct nfs_fattr fattr;
-
- if (jiffies - NFS_READTIME(dir) < NFS_ATTRTIMEO(dir))
- return;
-
- NFS_READTIME(dir) = jiffies;
- if (nfs_proc_getattr(server, NFS_FH(dir), &fattr) == 0) {
- nfs_refresh_inode(dir, &fattr);
- if (fattr.mtime.seconds == NFS_OLDMTIME(dir)) {
- if ((NFS_ATTRTIMEO(dir) <<= 1) > server->acdirmax)
- NFS_ATTRTIMEO(dir) = server->acdirmax;
- return;
- }
- NFS_OLDMTIME(dir) = fattr.mtime.seconds;
- }
- /* invalidate directory cache here when we _really_ start caching */
+ dfprintk(VFS, "NFS: nfs_dir_open(%x/%ld)\n", dir->i_dev, dir->i_ino);
+ return nfs_revalidate_inode(NFS_SERVER(dir), dir);
}
-static int nfs_dir_open(struct inode * dir, struct file * file)
-{
- revalidate_dir(NFS_SERVER(dir), dir);
- return 0;
-}
-
-static long nfs_dir_read(struct inode *inode, struct file *filp,
- char *buf, unsigned long count)
+static long
+nfs_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count)
{
return -EISDIR;
}
-static struct nfs_entry *c_entry = NULL;
+static struct nfs_dirent dircache[NFS_MAX_DIRCACHE];
/*
* We need to do caching of directory entries to prevent an
@@ -107,119 +110,222 @@ static struct nfs_entry *c_entry = NULL;
* 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.
+ *
+ * XXX: Do proper directory caching by stuffing data into the
+ * page cache (may require some fiddling for rsize < PAGE_SIZE).
*/
-static int nfs_readdir(struct inode *inode, struct file *filp,
- void *dirent, filldir_t filldir)
+static int
+nfs_readdir(struct inode *inode, struct file *filp, void *dirent,
+ filldir_t filldir)
{
- static kdev_t c_dev = 0;
- static int c_ino;
- static int c_size;
-
- int result;
- int i, index = 0;
- struct nfs_entry *entry;
-
+ static struct wait_queue *readdir_wait = NULL;
+ struct wait_queue **waitp = NULL;
+ struct nfs_dirent *cache, *free;
+ struct nfs_entry *entry;
+ unsigned long age, dead;
+ u32 cookie;
+ int ismydir, result;
+ int i, j, index = 0;
+
+ dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino);
if (!inode || !S_ISDIR(inode->i_mode)) {
printk("nfs_readdir: inode is NULL or not a directory\n");
return -EBADF;
}
- revalidate_dir(NFS_SERVER(inode), inode);
+ if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
+ return result;
+
+ /*
+ * Try to find the entry in the cache
+ */
+again:
+ if (waitp) {
+ interruptible_sleep_on(waitp);
+ if (current->signal & ~current->blocked)
+ 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;
+ }
- /* initialize cache memory if it hasn't been used before */
+ if (ismydir && cache->mtime != NFS_OLDMTIME(inode))
+ cache->valid = 0;
- if (c_entry == NULL) {
- i = sizeof (struct nfs_entry)*NFS_READDIR_CACHE_SIZE;
- c_entry = (struct nfs_entry *) kmalloc(i, GFP_KERNEL);
- if (c_entry == NULL) {
- printk("nfs_readdir: no MEMORY for cache\n");
- return -ENOMEM;
+ if (!cache->valid || cache->age < dead) {
+ free = cache;
+ age = 0;
+ } else if (cache->age < age) {
+ free = cache;
+ age = cache->age;
}
- for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++) {
- c_entry[i].name = (char *) kmalloc(NFS_MAXNAMLEN + 1,
- GFP_KERNEL);
- if (c_entry[i].name == NULL) {
- printk("nfs_readdir: no MEMORY for cache\n");
- while (--i>=0)
- kfree(c_entry[i].name);
- kfree(c_entry);
- c_entry = NULL;
- return -ENOMEM;
- }
- }
- }
- entry = NULL;
- /* try to find it in the cache */
+ if (!ismydir || !cache->valid)
+ continue;
- if (inode->i_dev == c_dev && inode->i_ino == c_ino) {
- for (i = 0; i < c_size; i++) {
- if (filp->f_pos == c_entry[i].cookie) {
- if (i == c_size - 1) {
- if (c_entry[i].eof)
- return 0;
- }
- else
- entry = c_entry + (index = i + 1);
- break;
+ if (cache->cookie == cookie && cache->size > 0) {
+ entry = cache->entry + (index = 0);
+ cache->locked = 1;
+ break;
+ }
+ for (j = 0; j < cache->size; j++) {
+ /*
+ dprintk("NFS: examing entry %.*s @%d\n",
+ (int) cache->entry[j].length,
+ cache->entry[j].name,
+ cache->entry[j].cookie);
+ */
+ if (cache->entry[j].cookie != cookie)
+ continue;
+ if (j < cache->size - 1) {
+ entry = cache->entry + (index = j + 1);
+ } else if (cache->entry[j].eof) {
+ return 0;
}
+ break;
+ }
+ if (entry) {
+ dfprintk(DIRCACHE, "NFS: found dircache entry %d\n",
+ (int)(cache - dircache));
+ cache->locked = 1;
+ break;
}
}
- /* if we didn't find it in the cache, revert to an nfs call */
-
- if (!entry) {
- result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
- filp->f_pos, NFS_READDIR_CACHE_SIZE, c_entry);
- if (result < 0) {
- c_dev = 0;
- return result;
+ /*
+ * 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;
}
- if (result > 0) {
- c_dev = inode->i_dev;
- c_ino = inode->i_ino;
- c_size = result;
- entry = c_entry + (index = 0);
+ 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) {
+ cache->entry = (struct nfs_entry *)
+ get_free_page(GFP_KERNEL);
+ if (!cache->entry) {
+ result = -ENOMEM;
+ goto done;
+ }
}
- }
- /* if we found it in the cache or from an nfs call, return results */
- if (!entry)
- return 0;
- while (index < c_size) {
- int nextpos = entry->cookie;
- if (filldir(dirent, entry->name, strlen(entry->name), filp->f_pos, entry->fileid) < 0)
- break;
- filp->f_pos = nextpos;
- /* revalidate the cache if we slept in filldir() */
- if (inode->i_dev != c_dev)
- break;
- if (inode->i_ino != c_ino)
+ result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
+ cookie, PAGE_SIZE, cache->entry);
+ if (result <= 0)
+ goto done;
+ cache->size = result;
+ cache->valid = 1;
+ entry = cache->entry + (index = 0);
+ }
+ cache->mtime = NFS_OLDMTIME(inode);
+ cache->age = jiffies;
+
+ /*
+ * Yowza! We have a cache entry...
+ */
+ while (index < cache->size) {
+ int nextpos = entry->cookie;
+
+ /*
+ dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry,
+ (int) entry->length, entry->name, entry->length,
+ (unsigned int) filp->f_pos,
+ entry->fileid, entry->eof);
+ */
+
+ if (filldir(dirent, entry->name, entry->length, cookie, entry->fileid) < 0)
break;
- if (nextpos != entry->cookie)
+ cookie = nextpos;
+ if (nextpos != entry->cookie) {
+ printk("nfs_readdir: shouldn't happen!\n");
break;
+ }
index++;
entry++;
}
- return 0;
+ 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);
+
+ return result;
}
/*
- * free cache memory
- * called from cleanup_module
+ * Invalidate dircache entries for inode
*/
-
-void nfs_kfree_cache(void)
+void
+nfs_invalidate_dircache(struct inode *inode)
{
- int i;
+ struct nfs_dirent *cache;
+ dev_t dev = inode->i_dev;
+ ino_t ino = inode->i_ino;
+ int i;
- if (c_entry == NULL)
- return;
- for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++)
- kfree(c_entry[i].name);
- kfree(c_entry);
- c_entry = NULL;
+ dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino);
+ for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
+ if (!cache->locked && cache->dev == dev && cache->ino == ino)
+ cache->valid = 0; /* brute force */
+ }
+}
+
+/*
+ * Free directory cache memory
+ * Called from cleanup_module
+ */
+void
+nfs_free_dircache(void)
+{
+ struct nfs_dirent *cache;
+ int i;
+
+ dfprintk(DIRCACHE, "NFS: freeing dircache\n");
+ for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
+ cache->valid = 0;
+ if (cache->locked) {
+ printk("nfs_kfree_cache: locked entry in dircache!\n");
+ continue;
+ }
+ if (cache->entry)
+ free_page((unsigned long) cache->entry);
+ cache->entry = NULL;
+ }
}
@@ -234,15 +340,18 @@ void nfs_kfree_cache(void)
* Since the cache is not hashed yet, it is a good idea not to make it too
* large because every lookup looks through the entire cache even
* though most of them will fail.
+ *
+ * FIXME: The lookup cache should also cache failed lookups. This can
+ * be a considerable win on diskless clients.
*/
static struct nfs_lookup_cache_entry {
- kdev_t dev;
- int inode;
- char filename[NFS_MAXNAMLEN + 1];
- struct nfs_fh fhandle;
+ kdev_t dev;
+ ino_t inode;
+ char filename[NFS_MAXNAMLEN + 1];
+ struct nfs_fh fhandle;
struct nfs_fattr fattr;
- int expiration_date;
+ int expiration_date;
} nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE];
static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir,
@@ -269,6 +378,8 @@ static int nfs_lookup_cache_lookup(struct inode *dir, const char *filename,
struct nfs_lookup_cache_entry *entry;
+ dfprintk(LOOKUPCACHE, "NFS: lookup_cache_lookup(%x/%ld, %s)\n",
+ dir->i_dev, dir->i_ino, filename);
if (!nfs_lookup_cache_in_use) {
memset(nfs_lookup_cache, 0, sizeof(nfs_lookup_cache));
nfs_lookup_cache_in_use = 1;
@@ -292,6 +403,9 @@ static void nfs_lookup_cache_add(struct inode *dir, const char *filename,
static int nfs_lookup_cache_pos = 0;
struct nfs_lookup_cache_entry *entry;
+ dfprintk(LOOKUPCACHE, "NFS: lookup_cache_add(%x/%ld, %s\n",
+ dir->i_dev, dir->i_ino, filename);
+
/* compensate for bug in SGI NFS server */
if (fattr->size == -1 || fattr->uid == -1 || fattr->gid == -1
|| fattr->atime.seconds == -1 || fattr->mtime.seconds == -1)
@@ -301,6 +415,7 @@ static void nfs_lookup_cache_add(struct inode *dir, const char *filename,
if (nfs_lookup_cache_pos == NFS_LOOKUP_CACHE_SIZE)
nfs_lookup_cache_pos = 0;
}
+
entry->dev = dir->i_dev;
entry->inode = dir->i_ino;
strcpy(entry->filename, filename);
@@ -314,9 +429,9 @@ static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode,
const char *filename)
{
struct nfs_lookup_cache_entry *entry;
- kdev_t dev;
- int fileid;
- int i;
+ kdev_t dev;
+ ino_t fileid;
+ int i;
if (inode) {
dev = inode->i_dev;
@@ -328,6 +443,10 @@ static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode,
}
else
return;
+
+ dfprintk(LOOKUPCACHE, "NFS: lookup_cache_remove(%x/%ld)\n",
+ dev, (long)fileid);
+
for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
entry = nfs_lookup_cache + i;
if (entry->dev == dev && entry->fattr.fileid == fileid)
@@ -358,6 +477,9 @@ static int nfs_lookup(struct inode *dir, const char *__name, int len,
char name[len > NFS_MAXNAMLEN? 1 : len+1];
int error;
+ dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n",
+ dir->i_dev, dir->i_ino, len, __name);
+
*result = NULL;
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_lookup: inode is NULL or not a directory\n");
@@ -399,6 +521,9 @@ static int nfs_create(struct inode *dir, const char *name, int len, int mode,
struct nfs_fh fhandle;
int error;
+ dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
+ dir->i_dev, dir->i_ino, name);
+
*result = NULL;
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_create: inode is NULL or not a directory\n");
@@ -422,6 +547,7 @@ static int nfs_create(struct inode *dir, const char *name, int len, int mode,
return -EACCES;
}
nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
+ nfs_invalidate_dircache(dir);
iput(dir);
return 0;
}
@@ -434,6 +560,9 @@ static int nfs_mknod(struct inode *dir, const char *name, int len,
struct nfs_fh fhandle;
int error;
+ dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
+ dir->i_dev, dir->i_ino, name);
+
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_mknod: inode is NULL or not a directory\n");
iput(dir);
@@ -453,12 +582,8 @@ static int nfs_mknod(struct inode *dir, const char *name, int len,
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
name, &sattr, &fhandle, &fattr);
if (!error)
- {
nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
- /* The parent dir inode count may have changed ! */
- nfs_lookup_cache_remove( NULL, dir, NULL);
- }
-
+ nfs_invalidate_dircache(dir);
iput(dir);
return error;
}
@@ -470,6 +595,9 @@ static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode)
struct nfs_fh fhandle;
int error;
+ dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
+ dir->i_dev, dir->i_ino, name);
+
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_mkdir: inode is NULL or not a directory\n");
iput(dir);
@@ -484,12 +612,9 @@ static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode)
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
name, &sattr, &fhandle, &fattr);
- if (!error) {
- if (fattr.fileid == dir->i_ino)
- printk("Sony NewsOS 4.1R buggy nfs server?\n");
- else
- nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
- }
+ if (!error)
+ nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
+ nfs_invalidate_dircache(dir);
iput(dir);
return error;
}
@@ -498,6 +623,9 @@ static int nfs_rmdir(struct inode *dir, const char *name, int len)
{
int error;
+ dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
+ dir->i_dev, dir->i_ino, name);
+
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_rmdir: inode is NULL or not a directory\n");
iput(dir);
@@ -508,7 +636,9 @@ static int nfs_rmdir(struct inode *dir, const char *name, int len)
return -ENAMETOOLONG;
}
error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name);
- nfs_lookup_cache_remove(dir, NULL, name);
+ if (!error)
+ nfs_lookup_cache_remove(dir, NULL, name);
+ nfs_invalidate_dircache(dir);
iput(dir);
return error;
}
@@ -522,46 +652,78 @@ static int nfs_sillyrename(struct inode *dir, const char *name, int len)
dir->i_count++;
if (nfs_lookup(dir, name, len, &inode) < 0)
return -EIO; /* arbitrary */
- if (inode->i_count == 1 || NFS_RENAMED_DIR(inode)) {
+
+ if (inode->i_count == 1) {
+ iput(inode);
+ return -EIO;
+ }
+ if (NFS_RENAMED_DIR(inode)) {
+ iput(NFS_RENAMED_DIR(inode));
+ NFS_RENAMED_DIR(inode) = NULL;
iput(inode);
return -EIO;
}
- slen = sprintf(silly, ".nfs%ld", inode->i_ino);
+ slen = sprintf(silly, ".nfs%ld", inode->i_ino);
if (len == slen && !strncmp(name, silly, len)) {
iput(inode);
return -EIO; /* DWIM */
}
+
+ dfprintk(VFS, "NFS: sillyrename(%x/%ld, %s)\n",
+ dir->i_dev, dir->i_ino, name);
+
ret = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), name,
- NFS_FH(dir), silly, 0);
+ NFS_FH(dir), silly);
if (ret >= 0) {
nfs_lookup_cache_remove(dir, NULL, name);
nfs_lookup_cache_remove(dir, NULL, silly);
NFS_RENAMED_DIR(inode) = dir;
dir->i_count++;
}
+ nfs_invalidate_dircache(dir);
iput(inode);
return ret;
}
+/*
+ * When releasing the inode, finally remove any unlinked but open files.
+ * Note that we have to clear the set of pending signals temporarily;
+ * otherwise the RPC call will fail.
+ */
void nfs_sillyrename_cleanup(struct inode *inode)
{
+ unsigned long oldsig;
struct inode *dir = NFS_RENAMED_DIR(inode);
char silly[14];
int error, slen;
+ dfprintk(VFS, "NFS: sillyrename cleanup(%x/%ld)\n",
+ inode->i_dev, inode->i_ino);
+
+ oldsig = current->signal;
+ current->signal = 0;
+
slen = sprintf(silly, ".nfs%ld", inode->i_ino);
- if ((error = nfs_unlink(dir, silly, slen)) < 0) {
- printk("NFS silly_rename cleanup failed (err = %d)\n",
- -error);
- }
+ error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly);
+ if (error < 0)
+ printk("NFS: silly_rename cleanup failed (err %d)\n", -error);
+
+ nfs_lookup_cache_remove(dir, NULL, silly);
+ nfs_invalidate_dircache(dir);
NFS_RENAMED_DIR(inode) = NULL;
+ iput(dir);
+
+ current->signal |= oldsig;
}
static int nfs_unlink(struct inode *dir, const char *name, int len)
{
int error;
+ dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
+ dir->i_dev, dir->i_ino, name);
+
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_unlink: inode is NULL or not a directory\n");
iput(dir);
@@ -573,8 +735,10 @@ static int nfs_unlink(struct inode *dir, const char *name, int len)
}
if ((error = nfs_sillyrename(dir, name, len)) < 0) {
error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name);
- nfs_lookup_cache_remove(dir, NULL, name);
+ if (!error)
+ nfs_lookup_cache_remove(dir, NULL, name);
}
+ nfs_invalidate_dircache(dir);
iput(dir);
return error;
}
@@ -585,6 +749,9 @@ static int nfs_symlink(struct inode *dir, const char *name, int len,
struct nfs_sattr sattr;
int error;
+ dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
+ dir->i_dev, dir->i_ino, name, symname);
+
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_symlink: inode is NULL or not a directory\n");
iput(dir);
@@ -603,6 +770,7 @@ static int nfs_symlink(struct inode *dir, const char *name, int len,
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
name, symname, &sattr);
+ nfs_invalidate_dircache(dir);
iput(dir);
return error;
}
@@ -612,6 +780,10 @@ static int nfs_link(struct inode *oldinode, struct inode *dir,
{
int error;
+ dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n",
+ oldinode->i_dev, oldinode->i_ino,
+ dir->i_dev, dir->i_ino, name);
+
if (!oldinode) {
printk("nfs_link: old inode is NULL\n");
iput(oldinode);
@@ -631,19 +803,35 @@ static int nfs_link(struct inode *oldinode, struct inode *dir,
}
error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode),
NFS_FH(dir), name);
-
- nfs_lookup_cache_remove(dir, oldinode, NULL);
+ if (!error) {
+ nfs_lookup_cache_remove(dir, oldinode, NULL);
+ NFS_READTIME(oldinode) = 0; /* force getattr */
+ }
+ nfs_invalidate_dircache(dir);
iput(oldinode);
iput(dir);
return error;
}
+/*
+ * RENAME
+ * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
+ * different file handle for the same inode after a rename (e.g. when
+ * moving to a different directory). A fail-safe method to do so would
+ * be to look up old_dir/old_name, create a link to new_dir/new_name and
+ * rename the old file using the silly_rename stuff. This way, the original
+ * file in old_dir will go away when the last process iput()s the inode.
+ */
static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len,
struct inode *new_dir, const char *new_name, int new_len,
int must_be_dir)
{
int error;
+ dfprintk(VFS, "NFS: rename(%x/%ld, %s -> %x/%ld, %s)\n",
+ old_dir->i_dev, old_dir->i_ino, old_name,
+ new_dir->i_dev, new_dir->i_ino, new_name);
+
if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
printk("nfs_rename: old inode is NULL or not a directory\n");
iput(old_dir);
@@ -661,13 +849,20 @@ static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len,
iput(new_dir);
return -ENAMETOOLONG;
}
+
+ /* We don't do rename() with trailing slashes over NFS now. Hmm. */
+ if (must_be_dir)
+ return -EINVAL;
+
error = nfs_proc_rename(NFS_SERVER(old_dir),
NFS_FH(old_dir), old_name,
- NFS_FH(new_dir), new_name,
- must_be_dir);
-
- nfs_lookup_cache_remove(old_dir, NULL, old_name);
- nfs_lookup_cache_remove(new_dir, NULL, new_name);
+ NFS_FH(new_dir), new_name);
+ if (!error) {
+ nfs_lookup_cache_remove(old_dir, NULL, old_name);
+ nfs_lookup_cache_remove(new_dir, NULL, new_name);
+ }
+ nfs_invalidate_dircache(old_dir);
+ nfs_invalidate_dircache(new_dir);
iput(old_dir);
iput(new_dir);
return error;
@@ -683,6 +878,9 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
int was_empty;
+ dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
+ inode->i_dev, inode->i_ino, inode->i_count);
+
if (!inode || !fattr) {
printk("nfs_refresh_inode: inode or fattr is NULL\n");
return;
@@ -698,10 +896,16 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_gid = fattr->gid;
/* Size changed from outside: invalidate caches on next read */
- if (inode->i_size != fattr->size)
+ if (inode->i_size != fattr->size) {
+ dfprintk(PAGECACHE, "NFS: cacheinv(%x/%ld)\n",
+ inode->i_dev, inode->i_ino);
NFS_CACHEINV(inode);
- if (NFS_OLDMTIME(inode) != fattr->mtime.seconds)
+ }
+ if (NFS_OLDMTIME(inode) != fattr->mtime.seconds) {
+ dfprintk(PAGECACHE, "NFS: mtime change on %x/%ld\n",
+ inode->i_dev, inode->i_ino);
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ }
inode->i_size = fattr->size;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
inode->i_rdev = to_kdev_t(fattr->rdev);