patch-2.3.24 linux/fs/nfsd/nfsfh.c
Next file: linux/fs/nfsd/nfssvc.c
Previous file: linux/fs/nfsd/nfsctl.c
Back to the patch index
Back to the overall index
- Lines: 1600
- Date:
Mon Oct 25 10:43:43 1999
- Orig file:
v2.3.23/linux/fs/nfsd/nfsfh.c
- Orig date:
Sat Oct 9 11:47:50 1999
diff -u --recursive --new-file v2.3.23/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
@@ -4,6 +4,8 @@
* NFS server file handle treatment.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org>
+ * Extensive cleanup by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
*/
#include <linux/sched.h>
@@ -22,308 +24,48 @@
#define NFSD_PARANOIA 1
/* #define NFSD_DEBUG_VERBOSE 1 */
-extern unsigned long max_mapnr;
-
-#define NFSD_FILE_CACHE 0
-#define NFSD_DIR_CACHE 1
-struct fh_entry {
- struct dentry * dentry;
- unsigned long reftime;
- ino_t ino;
- kdev_t dev;
-};
-
-#define NFSD_MAXFH PAGE_SIZE/sizeof(struct fh_entry)
-static struct fh_entry filetable[NFSD_MAXFH];
-static struct fh_entry dirstable[NFSD_MAXFH];
static int nfsd_nr_verified = 0;
static int nfsd_nr_put = 0;
-static unsigned long nfsd_next_expire = 0;
-
-static int add_to_fhcache(struct dentry *, int);
-static int nfsd_d_validate(struct dentry *);
-struct dentry * lookup_inode(kdev_t, ino_t, ino_t);
-
-static LIST_HEAD(fixup_head);
-static LIST_HEAD(path_inuse);
-static int nfsd_nr_fixups = 0;
-static int nfsd_nr_paths = 0;
-#define NFSD_MAX_PATHS 500
-#define NFSD_MAX_FIXUPAGE 60*HZ
-
-struct nfsd_fixup {
- struct list_head lru;
- ino_t dir;
- ino_t ino;
- kdev_t dev;
- struct dentry *dentry;
- unsigned long reftime;
-};
-
-struct nfsd_path {
- struct list_head lru;
- unsigned long reftime;
- int users;
- ino_t ino;
- kdev_t dev;
- char name[1];
-};
-
-static struct nfsd_fixup * find_cached_lookup(kdev_t dev, ino_t dir, ino_t ino)
-{
- struct list_head *tmp = fixup_head.next;
-
- for (; tmp != &fixup_head; tmp = tmp->next) {
- struct nfsd_fixup *fp;
-
- fp = list_entry(tmp, struct nfsd_fixup, lru);
- if (fp->ino != ino)
- continue;
- if (fp->dir != dir)
- continue;
- if (fp->dev != dev)
- continue;
- list_del(tmp);
- list_add(tmp, &fixup_head);
- fp->reftime = jiffies;
- return fp;
- }
- return NULL;
-}
-
-/*
- * Save the dentry pointer from a successful lookup.
- */
-static void add_to_lookup_cache(struct dentry *dentry, struct knfs_fh *fh)
-{
- struct nfsd_fixup *fp;
-
- fp = find_cached_lookup(u32_to_kdev_t(fh->fh_dev),
- u32_to_ino_t(fh->fh_dirino),
- u32_to_ino_t(fh->fh_ino));
- if (fp) {
- fp->dentry = dentry;
- return;
- }
-
- /*
- * Add a new entry. The small race here is unimportant:
- * if another task adds the same lookup, both entries
- * will be consistent.
- */
- fp = kmalloc(sizeof(struct nfsd_fixup), GFP_KERNEL);
- if (fp) {
- fp->dir = u32_to_kdev_t(fh->fh_dirino);
- fp->ino = u32_to_ino_t(fh->fh_ino);
- fp->dev = u32_to_ino_t(fh->fh_dev);
- fp->dentry = dentry;
- fp->reftime = jiffies;
- list_add(&fp->lru, &fixup_head);
- nfsd_nr_fixups++;
- }
-}
-
-static void free_fixup_entry(struct nfsd_fixup *fp)
-{
- list_del(&fp->lru);
- kfree(fp);
- nfsd_nr_fixups--;
-}
-
-/*
- * Copy a dentry's path into the specified buffer.
- */
-static int copy_path(char *buffer, struct dentry *dentry, int namelen)
-{
- char *p, *b = buffer;
- int result = 0, totlen = 0, len;
-
- while (1) {
- struct dentry *parent;
- dentry = dentry->d_covers;
- parent = dentry->d_parent;
- len = dentry->d_name.len;
- p = (char *) dentry->d_name.name + len;
- totlen += len;
- if (totlen > namelen)
- goto out;
- while (len--)
- *b++ = *(--p);
- if (dentry == parent)
- break;
- dentry = parent;
- totlen++;
- if (totlen > namelen)
- goto out;
- *b++ = '/';
- }
- *b = 0;
-
- /*
- * Now reverse in place ...
- */
- p = buffer;
- while (p < b) {
- char c = *(--b);
- *b = *p;
- *p++ = c;
- }
- result = 1;
-out:
- return result;
-}
-
-/*
- * Add a dentry's path to the path cache.
- */
-static int add_to_path_cache(struct dentry *dentry)
-{
- struct inode *inode = dentry->d_inode;
- struct dentry *this;
- struct nfsd_path *new;
- int len, result = 0;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("add_to_path_cache: caching %s/%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
- /*
- * Get the length of the full pathname.
- */
-restart:
- len = 0;
- this = dentry;
- while (1) {
- struct dentry *parent;
- this = this->d_covers;
- parent = this->d_parent;
- len += this->d_name.len;
- if (this == parent)
- break;
- this = parent;
- len++;
- }
- /*
- * Allocate a structure to hold the path.
- */
- new = kmalloc(sizeof(struct nfsd_path) + len, GFP_KERNEL);
- if (new) {
- new->users = 0;
- new->reftime = jiffies;
- new->ino = inode->i_ino;
- new->dev = inode->i_dev;
- result = copy_path(new->name, dentry, len);
- if (!result)
- goto retry;
- list_add(&new->lru, &path_inuse);
- nfsd_nr_paths++;
-#ifdef NFSD_DEBUG_VERBOSE
-printk("add_to_path_cache: added %s, paths=%d\n", new->name, nfsd_nr_paths);
-#endif
- }
- return result;
- /*
- * If the dentry's path length changed, just try again.
- */
-retry:
- kfree(new);
- printk(KERN_DEBUG "add_to_path_cache: path length changed, retrying\n");
- goto restart;
-}
-
-/*
- * Search for a path entry for the specified (dev, inode).
- */
-static struct nfsd_path *get_path_entry(kdev_t dev, ino_t ino)
-{
- struct nfsd_path *pe;
- struct list_head *tmp;
-
- for (tmp = path_inuse.next; tmp != &path_inuse; tmp = tmp->next) {
- pe = list_entry(tmp, struct nfsd_path, lru);
- if (pe->ino != ino)
- continue;
- if (pe->dev != dev)
- continue;
- list_del(tmp);
- list_add(tmp, &path_inuse);
- pe->users++;
- pe->reftime = jiffies;
-#ifdef NFSD_PARANOIA
-printk("get_path_entry: found %s for %s/%ld\n", pe->name, kdevname(dev), ino);
-#endif
- return pe;
- }
- return NULL;
-}
-
-static void put_path(struct nfsd_path *pe)
-{
- pe->users--;
-}
-
-static void free_path_entry(struct nfsd_path *pe)
-{
- if (pe->users)
- printk(KERN_DEBUG "free_path_entry: %s in use, users=%d\n",
- pe->name, pe->users);
- list_del(&pe->lru);
- kfree(pe);
- nfsd_nr_paths--;
-}
struct nfsd_getdents_callback {
- struct nfsd_dirent *dirent;
- ino_t dirino; /* parent inode number */
- int found; /* dirent inode matched? */
+ struct qstr *name; /* name that was found. name->name already points to a buffer */
+ unsigned long ino; /* the inum we are looking for */
+ int found; /* inode matched? */
int sequence; /* sequence counter */
};
-struct nfsd_dirent {
- ino_t ino; /* preset to desired entry */
- int len;
- char name[256];
-};
-
/*
- * A rather strange filldir function to capture the inode number
- * for the second entry (the parent inode) and the name matching
- * the specified inode number.
+ * A rather strange filldir function to capture
+ * the name matching the specified inode number.
*/
-static int filldir_one(void * __buf, const char * name, int len,
+static int filldir_one(void * __buf, const char * name, int len,
off_t pos, ino_t ino)
{
struct nfsd_getdents_callback *buf = __buf;
- struct nfsd_dirent *dirent = buf->dirent;
+ struct qstr *qs = buf->name;
+ char *nbuf = (char*)qs->name; /* cast is to get rid of "const" */
int result = 0;
buf->sequence++;
#ifdef NFSD_DEBUG_VERBOSE
-printk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
+dprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
#endif
- if (buf->sequence == 2) {
- buf->dirino = ino;
- goto out;
- }
- if (dirent->ino == ino) {
- dirent->len = len;
- memcpy(dirent->name, name, len);
- dirent->name[len] = 0;
+ if (buf->ino == ino) {
+ qs->len = len;
+ memcpy(nbuf, name, len);
+ nbuf[len] = '\0';
buf->found = 1;
result = -1;
}
-out:
return result;
}
/*
- * Read a directory and return the parent inode number and the name
- * of the specified entry. The dirent must be initialized with the
- * inode number of the desired entry.
+ * Read a directory and return the name of the specified entry.
*/
-static int get_parent_ino(struct dentry *dentry, struct nfsd_dirent *dirent)
+static int get_ino_name(struct dentry *dentry, struct qstr *name, unsigned long ino)
{
struct inode *dir = dentry->d_inode;
int error;
@@ -346,8 +88,8 @@
if (!file.f_op->readdir)
goto out_close;
- buffer.dirent = dirent;
- buffer.dirino = 0;
+ buffer.name = name;
+ buffer.ino = ino;
buffer.found = 0;
buffer.sequence = 0;
while (1) {
@@ -365,7 +107,6 @@
if (old_seq == buffer.sequence)
break;
}
- dirent->ino = buffer.dirino;
out_close:
if (file.f_op->release)
@@ -374,663 +115,245 @@
return error;
}
-/*
- * Look up a dentry given inode and parent inode numbers.
- *
- * This relies on the ability of a Unix-like filesystem to return
- * the parent inode of a directory as the ".." (second) entry.
- *
- * This could be further optimized if we had an efficient way of
- * searching for a dentry given the inode: as we walk up the tree,
- * it's likely that a dentry exists before we reach the root.
+/* this should be provided by each filesystem in an nfsd_operations interface as
+ * iget isn't really the right interface
*/
-struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino)
+static inline struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation)
{
- struct super_block *sb;
- struct dentry *root, *dentry, *result;
- struct inode *dir;
- char *name;
- unsigned long page;
- ino_t root_ino;
- int error;
- struct nfsd_dirent dirent;
-
- result = ERR_PTR(-ENOMEM);
- page = __get_free_page(GFP_KERNEL);
- if (!page)
- goto out;
-
- /*
- * Get the root dentry for the device.
- */
- result = ERR_PTR(-ENOENT);
- sb = get_super(dev);
- if (!sb)
- goto out_page;
- result = ERR_PTR(-ENOSYS);
- if (!sb->s_op->read_inode) /* No working iget(), e.g. FAT */
- goto out_page;
- root = dget(sb->s_root);
- root_ino = root->d_inode->i_ino; /* usually 2 */
- name = (char *) page + PAGE_SIZE;
- *(--name) = 0;
-
- /*
- * Walk up the tree to construct the name string.
- * When we reach the root inode, look up the name
- * relative to the root dentry.
+ /* iget isn't really right if the inode is currently unallocated!!
+ * This should really all be done inside each filesystem
+ *
+ * ext2fs' read_inode has been strengthed to return a bad_inode if the inode
+ * had been deleted.
+ *
+ * Currently we don't know the generation for parent directory, so a generation
+ * of 0 means "accept any"
*/
- while (1) {
- if (ino == root_ino) {
- if (*name == '/')
- name++;
- /*
- * Note: this dput()s the root dentry.
- */
- result = lookup_dentry(name, root, 0);
- break;
- }
-
- result = ERR_PTR(-ENOENT);
- dir = iget(sb, dirino);
- if (!dir)
- goto out_root;
- dentry = d_alloc_root(dir);
- if (!dentry)
- goto out_iput;
-
- /*
- * Get the name for this inode and the next parent inode.
- */
- dirent.ino = ino;
- error = get_parent_ino(dentry, &dirent);
- result = ERR_PTR(error);
- dput(dentry);
- if (error)
- goto out_root;
- /*
- * Prepend the name to the buffer.
- */
- result = ERR_PTR(-ENAMETOOLONG);
- name -= (dirent.len + 1);
- if ((unsigned long) name <= page)
- goto out_root;
- memcpy(name + 1, dirent.name, dirent.len);
- *name = '/';
-
- /*
- * Make sure we can't get caught in a loop ...
- */
- if (dirino == dirent.ino && dirino != root_ino) {
- printk(KERN_DEBUG
- "lookup_inode: looping?? (ino=%ld, path=%s)\n",
- dirino, name);
- goto out_root;
- }
- ino = dirino;
- dirino = dirent.ino;
+ struct inode *inode;
+ struct list_head *lp;
+ struct dentry *result;
+ inode = iget(sb, ino);
+ if (is_bad_inode(inode)
+ || (generation && inode->i_generation != generation)
+ ) {
+ /* we didn't find the right inode.. */
+ dprintk("fh_verify: Inode %lu, Bad count: %d %d or version %u %u\n",
+ inode->i_ino,
+ inode->i_nlink, inode->i_count,
+ inode->i_generation,
+ generation);
+
+ iput(inode);
+ return NULL;
+ }
+ /* now to find a dentry.
+ * If possible, get a well-connected one
+ */
+ for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
+ result = list_entry(lp,struct dentry, d_alias);
+ if (! IS_ROOT(result) || inode->i_sb->s_root == result) {
+ dget(result);
+ iput(inode);
+ return result;
+ }
+ }
+ result = d_alloc_root(inode);
+ if (result == NULL) {
+ iput(inode);
+ return ERR_PTR(-ENOMEM);
}
-
-out_page:
- free_page(page);
-out:
+ d_rehash(result); /* so a dput won't loose it */
return result;
-
- /*
- * Error exits ...
- */
-out_iput:
- result = ERR_PTR(-ENOMEM);
- iput(dir);
-out_root:
- dput(root);
- goto out;
-}
-
-/*
- * Find an entry in the cache matching the given dentry pointer.
- */
-static struct fh_entry *find_fhe(struct dentry *dentry, int cache,
- struct fh_entry **empty)
-{
- struct fh_entry *fhe;
- int i, found = (empty == NULL) ? 1 : 0;
-
- if (!dentry)
- goto out;
-
- fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
- for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
- if (fhe->dentry == dentry) {
- fhe->reftime = jiffies;
- return fhe;
- }
- if (!found && !fhe->dentry) {
- found = 1;
- *empty = fhe;
- }
- }
-out:
- return NULL;
-}
-
-/*
- * Expire a cache entry.
- */
-static void expire_fhe(struct fh_entry *empty, int cache)
-{
- struct dentry *dentry = empty->dentry;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("expire_fhe: expiring %s/%s, d_count=%d, ino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino);
-#endif
- empty->dentry = NULL; /* no dentry */
- /*
- * Add the parent to the dir cache before releasing the dentry,
- * and check whether to save a copy of the dentry's path.
- */
- if (!IS_ROOT(dentry)) {
- struct dentry *parent = dget(dentry->d_parent);
- if (add_to_fhcache(parent, NFSD_DIR_CACHE))
- nfsd_nr_verified++;
- else
- dput(parent);
- /*
- * If we're expiring a directory, copy its path.
- */
- if (cache == NFSD_DIR_CACHE) {
- add_to_path_cache(dentry);
- }
- }
- dput(dentry);
- nfsd_nr_put++;
-}
-
-/*
- * Look for an empty slot, or select one to expire.
- */
-static void expire_slot(int cache)
-{
- struct fh_entry *fhe, *empty = NULL;
- unsigned long oldest = -1;
- int i;
-
- fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
- for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
- if (!fhe->dentry)
- goto out;
- if (fhe->reftime < oldest) {
- oldest = fhe->reftime;
- empty = fhe;
- }
- }
- if (empty)
- expire_fhe(empty, cache);
-
-out:
- return;
}
-/*
- * Expire any cache entries older than a certain age.
+/* this routine links an IS_ROOT dentry into the dcache tree. It gains "parent"
+ * as a parent and "name" as a name
+ * It should possibly go in dcache.c
*/
-static void expire_old(int cache, int age)
+int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name)
{
- struct list_head *tmp;
- struct fh_entry *fhe;
- int i;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("expire_old: expiring %s older than %d\n",
-(cache == NFSD_FILE_CACHE) ? "file" : "dir", age);
-#endif
- fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
- for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
- if (!fhe->dentry)
- continue;
- if ((jiffies - fhe->reftime) > age)
- expire_fhe(fhe, cache);
- }
-
- /*
- * Remove old entries from the patch-up cache.
- */
- while ((tmp = fixup_head.prev) != &fixup_head) {
- struct nfsd_fixup *fp;
- fp = list_entry(tmp, struct nfsd_fixup, lru);
- if ((jiffies - fp->reftime) < NFSD_MAX_FIXUPAGE)
- break;
- free_fixup_entry(fp);
- }
-
- /*
- * Trim the path cache ...
- */
- while (nfsd_nr_paths > NFSD_MAX_PATHS) {
- struct nfsd_path *pe;
- pe = list_entry(path_inuse.prev, struct nfsd_path, lru);
- if (pe->users)
- break;
- free_path_entry(pe);
- }
-}
-
-/*
- * Add a dentry to the file or dir cache.
- *
- * Note: As NFS file handles must have an inode, we don't accept
- * negative dentries.
- */
-static int add_to_fhcache(struct dentry *dentry, int cache)
-{
- struct fh_entry *fhe, *empty = NULL;
- struct inode *inode = dentry->d_inode;
-
- if (!inode) {
+ struct dentry *tdentry;
#ifdef NFSD_PARANOIA
-printk("add_to_fhcache: %s/%s rejected, no inode!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+ if (!IS_ROOT(target))
+ printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);
#endif
- return 0;
- }
+ name->hash = full_name_hash(name->name, name->len);
+ tdentry = d_alloc(parent, name);
+ if (tdentry == NULL)
+ return -ENOMEM;
+ d_move(target, tdentry);
-repeat:
- fhe = find_fhe(dentry, cache, &empty);
- if (fhe) {
- return 0;
- }
-
- /*
- * Not found ... make a new entry.
+ /* tdentry will have been made a "child" of target (the parent of target)
+ * make it an IS_ROOT instead
*/
- if (empty) {
- empty->dentry = dentry;
- empty->reftime = jiffies;
- empty->ino = inode->i_ino;
- empty->dev = inode->i_dev;
- return 1;
- }
-
- expire_slot(cache);
- goto repeat;
-}
-
-/*
- * Find an entry in the dir cache for the specified inode number.
- */
-static struct fh_entry *find_fhe_by_ino(kdev_t dev, ino_t ino)
-{
- struct fh_entry * fhe = &dirstable[0];
- int i;
-
- for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
- if (fhe->ino == ino && fhe->dev == dev) {
- fhe->reftime = jiffies;
- return fhe;
- }
- }
- return NULL;
+ list_del(&tdentry->d_child);
+ tdentry->d_parent = tdentry;
+ d_rehash(target);
+ dput(tdentry);
+ return 0;
}
-/*
- * Find the (directory) dentry with the specified (dev, inode) number.
- * Note: this leaves the dentry in the cache.
+/* this routine finds the dentry of the parent of a given directory
+ * it should be in the filesystem accessed by nfsd_operations
+ * it assumes lookup("..") works.
*/
-static struct dentry *find_dentry_by_ino(kdev_t dev, ino_t ino)
+struct dentry *nfsd_findparent(struct dentry *child)
{
- struct fh_entry *fhe;
- struct nfsd_path *pe;
- struct dentry * dentry;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_dentry_by_ino: looking for inode %ld\n", ino);
-#endif
- /*
- * Special case: inode number 2 is the root inode,
- * so we can use the root dentry for the device.
- */
- if (ino == 2) {
- struct super_block *sb = get_super(dev);
- if (sb) {
-#ifdef NFSD_PARANOIA
-printk("find_dentry_by_ino: getting root dentry for %s\n", kdevname(dev));
-#endif
- if (sb->s_root) {
- dentry = dget(sb->s_root);
- goto out;
- } else {
-#ifdef NFSD_PARANOIA
- printk("find_dentry_by_ino: %s has no root??\n",
- kdevname(dev));
-#endif
- }
- }
- }
+ struct dentry *tdentry, *pdentry;
+ tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0});
+ if (!tdentry)
+ return ERR_PTR(-ENOMEM);
- /*
- * Search the dentry cache ...
+ /* I'm going to assume that if the returned dentry is different, then
+ * it is well connected. But nobody returns different dentrys do they?
*/
- fhe = find_fhe_by_ino(dev, ino);
- if (fhe) {
- dentry = dget(fhe->dentry);
- goto out;
- }
- /*
- * Search the path cache ...
- */
- dentry = NULL;
- pe = get_path_entry(dev, ino);
- if (pe) {
- struct dentry *res;
- res = lookup_dentry(pe->name, NULL, 0);
- if (!IS_ERR(res)) {
- struct inode *inode = res->d_inode;
- if (inode && inode->i_ino == ino &&
- inode->i_dev == dev) {
- dentry = res;
-#ifdef NFSD_PARANOIA
-printk("find_dentry_by_ino: found %s/%s, ino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, ino);
-#endif
- if (add_to_fhcache(dentry, NFSD_DIR_CACHE)) {
- dget(dentry);
- nfsd_nr_verified++;
- }
- } else {
- dput(res);
- }
- } else {
-#ifdef NFSD_PARANOIA
-printk("find_dentry_by_ino: %s lookup failed\n", pe->name);
-#endif
- }
- put_path(pe);
- }
-out:
- return dentry;
-}
-
-/*
- * Look for an entry in the file cache matching the dentry pointer,
- * and verify that the (dev, inode) numbers are correct. If found,
- * the entry is removed from the cache.
- */
-static struct dentry *find_dentry_in_fhcache(struct knfs_fh *fh)
-{
- struct fh_entry * fhe;
-
- fhe = find_fhe(fh->fh_dcookie, NFSD_FILE_CACHE, NULL);
- if (fhe) {
- struct dentry *parent, *dentry;
- struct inode *inode;
-
- dentry = fhe->dentry;
- inode = dentry->d_inode;
-
- if (!inode) {
-#ifdef NFSD_PARANOIA
-printk("find_dentry_in_fhcache: %s/%s has no inode!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
- goto out;
- }
- if (inode->i_ino != u32_to_ino_t(fh->fh_ino))
- goto out;
- if (inode->i_dev != u32_to_kdev_t(fh->fh_dev))
- goto out;
-
- fhe->dentry = NULL;
- fhe->ino = 0;
- fhe->dev = 0;
- nfsd_nr_put++;
- /*
- * Make sure the parent is in the dir cache ...
+ pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry);
+ if (!pdentry) {
+ /* I don't want to return a ".." dentry.
+ * I would prefer to return an unconnected "IS_ROOT" dentry,
+ * though a properly connected dentry is even better
*/
- parent = dget(dentry->d_parent);
- if (add_to_fhcache(parent, NFSD_DIR_CACHE))
- nfsd_nr_verified++;
- else
- dput(parent);
- return dentry;
- }
-out:
- return NULL;
-}
-
-/*
- * Look for an entry in the parent directory with the specified
- * inode number.
- */
-static struct dentry *lookup_by_inode(struct dentry *parent, ino_t ino)
-{
- struct dentry *dentry;
- int error;
- struct nfsd_dirent dirent;
-
- /*
- * Search the directory for the inode number.
- */
- dirent.ino = ino;
- error = get_parent_ino(parent, &dirent);
- if (error) {
-#ifdef NFSD_PARANOIA
-printk("lookup_by_inode: ino %ld not found in %s\n", ino, parent->d_name.name);
-#endif
- goto no_entry;
- }
-#ifdef NFSD_PARANOIA
-printk("lookup_by_inode: found %s\n", dirent.name);
-#endif
-
- dentry = lookup_dentry(dirent.name, parent, 0);
- if (!IS_ERR(dentry)) {
- if (dentry->d_inode && dentry->d_inode->i_ino == ino)
- goto out;
-#ifdef NFSD_PARANOIA
-printk("lookup_by_inode: %s/%s inode mismatch??\n",
-parent->d_name.name, dentry->d_name.name);
-#endif
- dput(dentry);
- } else {
-#ifdef NFSD_PARANOIA
-printk("lookup_by_inode: %s lookup failed, error=%ld\n",
-dirent.name, PTR_ERR(dentry));
-#endif
- }
-
-no_entry:
- dentry = NULL;
-out:
- return dentry;
-
-}
-
-/*
- * Search the fix-up list for a dentry from a prior lookup.
- */
-static struct dentry *nfsd_cached_lookup(struct knfs_fh *fh)
-{
- struct nfsd_fixup *fp;
-
- fp = find_cached_lookup(u32_to_kdev_t(fh->fh_dev),
- u32_to_ino_t(fh->fh_dirino),
- u32_to_ino_t(fh->fh_ino));
- if (fp)
- return fp->dentry;
- return NULL;
-}
-
-void
-expire_all(void)
-{
- if (time_after_eq(jiffies, nfsd_next_expire)) {
- expire_old(NFSD_FILE_CACHE, 5*HZ);
- expire_old(NFSD_DIR_CACHE , 60*HZ);
- nfsd_next_expire = jiffies + 5*HZ;
- }
-}
-
-/*
- * Free cache after unlink/rmdir.
- */
-void
-expire_by_dentry(struct dentry *dentry)
-{
- struct fh_entry *fhe;
-
- fhe = find_fhe(dentry, NFSD_FILE_CACHE, NULL);
- if (fhe) {
- expire_fhe(fhe, NFSD_FILE_CACHE);
- }
- fhe = find_fhe(dentry, NFSD_DIR_CACHE, NULL);
- if (fhe) {
- expire_fhe(fhe, NFSD_DIR_CACHE);
- }
+ /* if first or last of alias list is not tdentry, use that
+ * else make a root dentry
+ */
+ struct list_head *aliases = &tdentry->d_inode->i_dentry;
+ if (aliases->next != aliases) {
+ pdentry = list_entry(aliases->next, struct dentry, d_alias);
+ if (pdentry == tdentry)
+ pdentry = list_entry(aliases->prev, struct dentry, d_alias);
+ if (pdentry == tdentry)
+ pdentry = NULL;
+ if (pdentry) dget(pdentry);
+ }
+ if (pdentry == NULL) {
+ pdentry = d_alloc_root(igrab(tdentry->d_inode));
+ if (pdentry) d_rehash(pdentry);
+ }
+ if (pdentry == NULL)
+ pdentry = ERR_PTR(-ENOMEM);
+ }
+ dput(tdentry); /* it was never rehashed, it will be discarded */
+ return pdentry;
}
/*
- * The is the basic lookup mechanism for turning an NFS file handle
- * into a dentry. There are several levels to the search:
- * (1) Look for the dentry pointer the short-term fhcache,
- * and verify that it has the correct inode number.
- *
- * (2) Try to validate the dentry pointer in the file handle,
- * and verify that it has the correct inode number. If this
- * fails, check for a cached lookup in the fix-up list and
- * repeat step (2) using the new dentry pointer.
- *
- * (3) Look up the dentry by using the inode and parent inode numbers
- * to build the name string. This should succeed for any Unix-like
- * filesystem.
- *
- * (4) Search for the parent dentry in the dir cache, and then
- * look for the name matching the inode number.
- *
- * (5) The most general case ... search the whole volume for the inode.
- *
- * If successful, we return a dentry with the use count incremented.
- *
- * Note: steps (4) and (5) above are probably unnecessary now that (3)
- * is working. Remove the code once this is verified ...
+ * This is the basic lookup mechanism for turning an NFS file handle
+ * into a dentry.
+ * We use nfsd_iget and if that doesn't return a suitably connected dentry,
+ * we try to find the parent, and the parent of that and so-on until a
+ * connection if made.
*/
static struct dentry *
-find_fh_dentry(struct knfs_fh *fh)
+find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath)
{
- struct dentry *dentry, *parent;
- int looked_up = 0, retry = 0;
-
- /*
- * Stage 1: Look for the dentry in the short-term fhcache.
- */
- dentry = find_dentry_in_fhcache(fh);
- if (dentry) {
- nfsdstats.fh_cached++;
- goto out;
- }
-
- /*
- * Stage 2: Attempt to validate the dentry in the file handle.
- */
- dentry = fh->fh_dcookie;
-recheck:
- if (nfsd_d_validate(dentry)) {
- struct inode * dir = dentry->d_parent->d_inode;
-
- if (dir->i_ino == u32_to_ino_t(fh->fh_dirino) &&
- dir->i_dev == u32_to_kdev_t(fh->fh_dev)) {
- struct inode * inode = dentry->d_inode;
- /*
- * NFS file handles must always have an inode,
- * so we won't accept a negative dentry.
- */
- if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) {
- dget(dentry);
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_fh_dentry: validated %s/%s, ino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino);
-#endif
- if (!retry)
- nfsdstats.fh_valid++;
- else {
- nfsdstats.fh_fixup++;
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_fh_dentry: retried validation successful\n");
-#endif
- }
- goto out;
+ struct dentry *dentry, *result = NULL;
+ struct qstr qs;
+ char namebuf[256];
+ int found =0;
+ u32 err;
+
+ qs.name = namebuf;
+ /*
+ * Attempt to find the inode.
+ */
+ result = nfsd_iget(sb, fh->fh_ino, fh->fh_generation);
+ err = PTR_ERR(result);
+ if (IS_ERR(result))
+ goto err_out;
+ err = -ESTALE;
+ if (!result) {
+ dprintk("find_fh_dentry: No inode found.\n");
+ goto err_out;
+ }
+ if (!IS_ROOT(result) || result->d_inode->i_sb->s_root ==result)
+ return result;
+
+ /* result is now a "root" dentry, which may be adequate as it stands, or else
+ * will get spliced into the dcache tree */
+
+ if (!S_ISDIR(result->d_inode->i_mode) && ! needpath) {
+ return result;
+ }
+
+ /* It's a directory, or we are required to confirm the file's
+ * location in the tree.
+ */
+ found = 0;
+ if (!S_ISDIR(result->d_inode->i_mode)) {
+ if (fh->fh_dirino == 0)
+ goto err_result; /* don't know how to find parent */
+ else {
+ /* need to iget fh->fh_dirino and make sure this inode is in that directory */
+ dentry = nfsd_iget(sb, fh->fh_dirino, 0);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto err_result;
+ err = -ESTALE;
+ if (!dentry->d_inode
+ || !S_ISDIR(dentry->d_inode->i_mode)) {
+ goto err_dentry;
}
+ if (!IS_ROOT(dentry) || dentry->d_inode->i_sb->s_root ==dentry)
+ found = 1;
+ err = get_ino_name(dentry, &qs, result->d_inode->i_ino);
+ if (err)
+ goto err_dentry;
+
+ /* OK, we have the name in parent of inode, lets fill in the dentry */
+ err = d_splice(result, dentry, &qs);
+ if (err)
+ goto err_dentry;
+ }
+ }
+ else
+ dentry = dget(result);
+
+ while(!found) {
+ /* LOOP INVARIANT */
+ /* haven't found a place in the tree yet, but we do have a path
+ * from dentry down to result, and dentry is a directory.
+ * Have a hold on dentry and result */
+ struct dentry *pdentry;
+ struct inode *parent;
+
+ pdentry = nfsd_findparent(dentry);
+ err = PTR_ERR(pdentry);
+ if (IS_ERR(pdentry))
+ goto err_dentry;
+ parent = pdentry->d_inode;
+ err = -EACCES;
+ if (!parent) {
+ dput(pdentry);
+ goto err_dentry;
}
- }
-
- /*
- * Before proceeding to a lookup, check whether we cached a
- * prior lookup. If so, try to validate that dentry ...
- */
- if (!retry && (dentry = nfsd_cached_lookup(fh)) != NULL) {
- retry = 1;
- goto recheck;
- }
+ /* I'm not sure that this is the best test for
+ * "is it not a floating dentry?"
+ */
+ if (!IS_ROOT(pdentry) || parent->i_sb->s_root == pdentry)
+ found = 1;
- /*
- * Stage 3: Look up the dentry based on the inode and parent inode
- * numbers. This should work for all Unix-like filesystems.
- */
- looked_up = 1;
- dentry = lookup_inode(u32_to_kdev_t(fh->fh_dev),
- u32_to_ino_t(fh->fh_dirino),
- u32_to_ino_t(fh->fh_ino));
- if (!IS_ERR(dentry)) {
- struct inode * inode = dentry->d_inode;
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_fh_dentry: looked up %s/%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
- if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) {
- nfsdstats.fh_lookup++;
- goto out;
+ err = get_ino_name(pdentry, &qs, dentry->d_inode->i_ino);
+ if (err) {
+ dput(pdentry);
+ goto err_dentry;
}
-#ifdef NFSD_PARANOIA
-printk("find_fh_dentry: %s/%s lookup mismatch!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
+ err = d_splice(dentry, pdentry, &qs);
+ dprintk("nfsd_fh: found name %s for ino %ld\n", dentry->d_name.name, dentry->d_inode->i_ino);
dput(dentry);
+ dentry = pdentry;
}
+ dput(dentry);
+ return result;
- /*
- * Stage 4: Look for the parent dentry in the fhcache ...
- */
- parent = find_dentry_by_ino(u32_to_kdev_t(fh->fh_dev),
- u32_to_ino_t(fh->fh_dirino));
- if (parent) {
- /*
- * ... then search for the inode in the parent directory.
- */
- dentry = lookup_by_inode(dget(parent), u32_to_ino_t(fh->fh_ino));
- dput(parent);
- if (dentry)
- goto out;
- }
-
- /*
- * Stage 5: Search the whole volume.
- */
-#ifdef NFSD_PARANOIA
-printk("find_fh_dentry: %s, %u/%u not found -- need full search!\n",
-kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_dirino, fh->fh_ino);
-#endif
- dentry = NULL;
- nfsdstats.fh_stale++;
-
-out:
- if (looked_up && dentry) {
- add_to_lookup_cache(dentry, fh);
- }
-
- expire_all();
-
- return dentry;
+err_dentry:
+ dput(dentry);
+err_result:
+ dput(result);
+err_out:
+ if (err == -ESTALE)
+ nfsdstats.fh_stale++;
+ return ERR_PTR(err);
}
/*
@@ -1038,6 +361,9 @@
*
* Note that the file handle dentry may need to be freed even after
* an error return.
+ *
+ * This is only called at the start of an nfsproc call, so fhp points to
+ * a svc_fh which is all 0 except for the over-the-wire file handle.
*/
u32
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
@@ -1048,20 +374,36 @@
struct inode *inode;
u32 error = 0;
- dprintk("nfsd: fh_verify(exp %x/%u cookie %p)\n",
- fh->fh_xdev, fh->fh_xino, fh->fh_dcookie);
+ dprintk("nfsd: fh_verify(exp %s/%u file (%s/%u dir %u)\n",
+ kdevname(fh->fh_xdev),
+ fh->fh_xino,
+ kdevname(fh->fh_dev),
+ fh->fh_ino,
+ fh->fh_dirino);
+
+ /*
+ * Security: Check that the fh is internally consistant (from <gam3@acm.org>)
+ */
+ if (fh->fh_dev != fh->fh_xdev) {
+ printk("fh_verify: Security: export on other device (%s, %s).\n",
+ kdevname(fh->fh_dev), kdevname(fh->fh_xdev));
+ error = nfserr_stale;
+ nfsdstats.fh_stale++;
+ goto out;
+ }
- if (fhp->fh_dverified)
- goto check_type;
/*
* Look up the export entry.
*/
error = nfserr_stale;
exp = exp_get(rqstp->rq_client,
- u32_to_kdev_t(fh->fh_xdev),
- u32_to_ino_t(fh->fh_xino));
- if (!exp) /* export entry revoked */
+ u32_to_kdev_t(fh->fh_xdev),
+ u32_to_ino_t(fh->fh_xino));
+ if (!exp) {
+ /* export entry revoked */
+ nfsdstats.fh_stale++;
goto out;
+ }
/* Check if the request originated from a secure port. */
error = nfserr_perm;
@@ -1079,32 +421,31 @@
/*
* Look up the dentry using the NFS file handle.
*/
- error = nfserr_noent;
- dentry = find_fh_dentry(fh);
- if (!dentry)
+
+ dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
+ fh,
+ !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+
+ if (IS_ERR(dentry)) {
+ error = nfserrno(-PTR_ERR(dentry));
goto out;
+ }
- /*
- * Note: it's possible the returned dentry won't be the one in the
- * file handle. We can correct the file handle for our use, but
- * unfortunately the client will keep sending the broken one. Let's
- * hope the lookup will keep patching things up.
- */
fhp->fh_dentry = dentry;
fhp->fh_export = exp;
fhp->fh_dverified = 1;
nfsd_nr_verified++;
+ inode = dentry->d_inode;
+
/* Type check. The correct error return for type mismatches
* does not seem to be generally agreed upon. SunOS seems to
* use EISDIR if file isn't S_IFREG; a comment in the NFSv3
* spec says this is incorrect (implementation notes for the
* write call).
*/
-check_type:
- dentry = fhp->fh_dentry;
- inode = dentry->d_inode;
- exp = fhp->fh_export;
+
+ /* When is type ever negative? */
if (type > 0 && (inode->i_mode & S_IFMT) != type) {
error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
goto out;
@@ -1118,35 +459,37 @@
* Security: Check that the export is valid for dentry <gam3@acm.org>
*/
error = 0;
- if (fh->fh_dev != fh->fh_xdev) {
- printk("fh_verify: Security: export on other device"
- " (%d, %d).\n", fh->fh_dev, fh->fh_xdev);
- error = nfserr_stale;
- } else if (exp->ex_dentry != dentry) {
- struct dentry *tdentry = dentry;
- do {
- tdentry = tdentry->d_parent;
- if (exp->ex_dentry == tdentry)
- break;
- /* executable only by root and we can't be root */
- if (current->fsuid &&
- !(tdentry->d_inode->i_uid &&
- (tdentry->d_inode->i_mode & S_IXUSR)) &&
- !(tdentry->d_inode->i_gid &&
- (tdentry->d_inode->i_mode & S_IXGRP)) &&
- !(tdentry->d_inode->i_mode & S_IXOTH) &&
- (exp->ex_flags & NFSEXP_ROOTSQUASH)) {
+ if (!(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
+ if (exp->ex_dentry != dentry) {
+ struct dentry *tdentry = dentry;
+
+ do {
+ tdentry = tdentry->d_parent;
+ if (exp->ex_dentry == tdentry)
+ break;
+ /* executable only by root and we can't be root */
+ if (current->fsuid
+ && (exp->ex_flags & NFSEXP_ROOTSQUASH)
+ && !(tdentry->d_inode->i_uid
+ && (tdentry->d_inode->i_mode & S_IXUSR))
+ && !(tdentry->d_inode->i_gid
+ && (tdentry->d_inode->i_mode & S_IXGRP))
+ && !(tdentry->d_inode->i_mode & S_IXOTH)
+ ) {
+ error = nfserr_stale;
+ nfsdstats.fh_stale++;
+ dprintk("fh_verify: no root_squashed access.\n");
+ }
+ } while ((tdentry != tdentry->d_parent));
+ if (exp->ex_dentry != tdentry) {
error = nfserr_stale;
-dprintk("fh_verify: no root_squashed access.\n");
+ nfsdstats.fh_stale++;
+ printk("nfsd Security: %s/%s bad export.\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ goto out;
}
- } while (!IS_ROOT(tdentry));
- if (exp->ex_dentry != tdentry) {
- error = nfserr_stale;
- printk("nfsd Security: %s/%s bad export.\n",
- dentry->d_parent->d_name.name,
- dentry->d_name.name);
- goto out;
}
}
@@ -1155,9 +498,10 @@
error = nfsd_permission(exp, dentry, access);
}
#ifdef NFSD_PARANOIA
-if (error)
-printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
+ if (error) {
+ printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
+ }
#endif
out:
return error;
@@ -1192,14 +536,17 @@
}
fh_init(fhp);
- fhp->fh_handle.fh_dcookie = dentry;
+ fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino);
+ fhp->fh_handle.fh_dev = kdev_t_to_u32(parent->d_inode->i_dev);
+ fhp->fh_handle.fh_xdev = kdev_t_to_u32(exp->ex_dev);
+ fhp->fh_handle.fh_xino = ino_t_to_u32(exp->ex_ino);
+ fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
if (inode) {
fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
+ fhp->fh_handle.fh_generation = inode->i_generation;
+ if (S_ISDIR(inode->i_mode) || (exp->ex_flags & NFSEXP_NOSUBTREECHECK))
+ fhp->fh_handle.fh_dirino = 0;
}
- fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino);
- fhp->fh_handle.fh_dev = kdev_t_to_u32(parent->d_inode->i_dev);
- fhp->fh_handle.fh_xdev = kdev_t_to_u32(exp->ex_dev);
- fhp->fh_handle.fh_xino = ino_t_to_u32(exp->ex_ino);
fhp->fh_dentry = dentry; /* our internal copy */
fhp->fh_export = exp;
@@ -1211,6 +558,7 @@
/*
* Update file handle information after changing a dentry.
+ * This is only called by nfsd_create
*/
void
fh_update(struct svc_fh *fhp)
@@ -1226,6 +574,10 @@
if (!inode)
goto out_negative;
fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
+ fhp->fh_handle.fh_generation = inode->i_generation;
+ if (S_ISDIR(inode->i_mode) || (fhp->fh_export->ex_flags & NFSEXP_NOSUBTREECHECK))
+ fhp->fh_handle.fh_dirino = 0;
+
out:
return;
@@ -1239,8 +591,7 @@
}
/*
- * Release a file handle. If the file handle carries a dentry count,
- * we add the dentry to the short-term cache rather than release it.
+ * Release a file handle.
*/
void
fh_put(struct svc_fh *fhp)
@@ -1251,10 +602,8 @@
fhp->fh_dverified = 0;
if (!dentry->d_count)
goto out_bad;
- if (!dentry->d_inode || !add_to_fhcache(dentry, 0)) {
- dput(dentry);
- nfsd_nr_put++;
- }
+ dput(dentry);
+ nfsd_nr_put++;
}
return;
@@ -1262,136 +611,4 @@
printk(KERN_ERR "fh_put: %s/%s has d_count 0!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
return;
-}
-
-/*
- * Verify that the FH dentry is still a valid dentry pointer.
- * After making some preliminary checks, we ask VFS to verify
- * that it is indeed a dentry.
- */
-static int nfsd_d_validate(struct dentry *dentry)
-{
- unsigned long dent_addr = (unsigned long) dentry;
- unsigned long min_addr = PAGE_OFFSET;
- unsigned long max_addr = min_addr + (max_mapnr << PAGE_SHIFT);
- unsigned long align_mask = 0x0F;
- unsigned int len;
- int valid = 0;
-
- if (dent_addr < min_addr)
- goto bad_addr;
- if (dent_addr > max_addr - sizeof(struct dentry))
- goto bad_addr;
- if ((dent_addr & ~align_mask) != dent_addr)
- goto bad_align;
- if (!kern_addr_valid(dent_addr))
- goto bad_addr;
- /*
- * Looks safe enough to dereference ...
- */
- len = dentry->d_name.len;
- if (len > NFS_MAXNAMLEN)
- goto out;
- /*
- * Note: d_validate doesn't dereference the parent pointer ...
- * just combines it with the name hash to find the hash chain.
- */
- valid = d_validate(dentry, dentry->d_parent, dentry->d_name.hash, len);
-
-out:
- return valid;
-
-bad_addr:
- printk(KERN_DEBUG "nfsd_d_validate: invalid address %lx\n", dent_addr);
- goto out;
-bad_align:
- printk(KERN_DEBUG "nfsd_d_validate: unaligned address %lx\n", dent_addr);
- goto out;
-}
-
-/*
- * Flush any cached dentries for the specified device
- * or for all devices.
- *
- * This is called when revoking the last export for a
- * device, so that it can be unmounted cleanly.
- */
-void nfsd_fh_flush(kdev_t dev)
-{
- struct fh_entry *fhe;
- int i, pass = 2;
-
- fhe = &filetable[0];
- while (pass--) {
- for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
- struct dentry *dentry = fhe->dentry;
- if (!dentry)
- continue;
- if (dev && dentry->d_inode->i_dev != dev)
- continue;
- fhe->dentry = NULL;
- dput(dentry);
- nfsd_nr_put++;
- }
- fhe = &dirstable[0];
- }
-}
-
-/*
- * Free the dentry and path caches.
- */
-void nfsd_fh_free(void)
-{
- struct list_head *tmp;
- int i;
-
- /* Flush dentries for all devices */
- nfsd_fh_flush(0);
-
- /*
- * N.B. write a destructor for these lists ...
- */
- i = 0;
- while ((tmp = fixup_head.next) != &fixup_head) {
- struct nfsd_fixup *fp;
- fp = list_entry(tmp, struct nfsd_fixup, lru);
- free_fixup_entry(fp);
- i++;
- }
- printk(KERN_DEBUG "nfsd_fh_free: %d fixups freed\n", i);
-
- i = 0;
- while ((tmp = path_inuse.next) != &path_inuse) {
- struct nfsd_path *pe;
- pe = list_entry(tmp, struct nfsd_path, lru);
- free_path_entry(pe);
- i++;
- }
- printk(KERN_DEBUG "nfsd_fh_free: %d paths freed\n", i);
-
- printk(KERN_DEBUG "nfsd_fh_free: verified %d, put %d\n",
- nfsd_nr_verified, nfsd_nr_put);
-}
-
-void nfsd_fh_init(void)
-{
- /* Sanity check */
- extern void __my_nfsfh_is_too_big(void);
- if (sizeof(struct nfs_fhbase) > 32)
- __my_nfsfh_is_too_big();
-
- memset(filetable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
- memset(dirstable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
- INIT_LIST_HEAD(&path_inuse);
- INIT_LIST_HEAD(&fixup_head);
-
- printk(KERN_DEBUG
- "nfsd_init: initialized fhcache, entries=%lu\n", NFSD_MAXFH);
- /*
- * Display a warning if the ino_t is larger than 32 bits.
- */
- if (sizeof(ino_t) > sizeof(__u32))
- printk(KERN_INFO
- "NFSD: ino_t is %d bytes, using lower 4 bytes\n",
- sizeof(ino_t));
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)