diff options
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r-- | fs/nfs/dir.c | 500 |
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); |