summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-01-27 23:45:22 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-01-27 23:45:22 +0000
commit5b35aa5cd29bb111d847b2a2ed18110acbfb1f44 (patch)
treec7bbaa1137528330d3c74d14056ef7016a52be72 /fs
parent511bcd7c5924ce9e98ad1cb851988f7448dfef0f (diff)
Merge with Linux 2.3.24.
Diffstat (limited to 'fs')
-rw-r--r--fs/Config.in12
-rw-r--r--fs/buffer.c16
-rw-r--r--fs/ext2/inode.c10
-rw-r--r--fs/fat/Makefile2
-rw-r--r--fs/fat/fatfs_syms.c2
-rw-r--r--fs/fat/file.c12
-rw-r--r--fs/fat/mmap.c131
-rw-r--r--fs/hpfs/file.c12
-rw-r--r--fs/inode.c10
-rw-r--r--fs/lockd/clntproc.c1
-rw-r--r--fs/ncpfs/dir.c6
-rw-r--r--fs/nfs/dir.c12
-rw-r--r--fs/nfs/read.c1
-rw-r--r--fs/nfs/symlink.c10
-rw-r--r--fs/nfsd/export.c11
-rw-r--r--fs/nfsd/nfsctl.c2
-rw-r--r--fs/nfsd/nfsfh.c1397
-rw-r--r--fs/nfsd/nfssvc.c12
-rw-r--r--fs/nfsd/vfs.c91
-rw-r--r--fs/romfs/inode.c3
-rw-r--r--fs/smbfs/cache.c32
21 files changed, 428 insertions, 1357 deletions
diff --git a/fs/Config.in b/fs/Config.in
index 0158b268d..78af8d3d4 100644
--- a/fs/Config.in
+++ b/fs/Config.in
@@ -59,11 +59,11 @@ fi
tristate 'ROM filesystem support' CONFIG_ROMFS_FS
tristate 'Second extended fs support' CONFIG_EXT2_FS
tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
-if [ "$CONFIG_SYSV_FS" != "n" -a "$CONFIG_EXPERIMENTAL" ]; then
+if [ "$CONFIG_SYSV_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' SYSV filesystem write support (DANGEROUS)' CONFIG_SYSV_FS_WRITE
fi
tristate 'UFS filesystem support (read only)' CONFIG_UFS_FS
-if [ "$CONFIG_UFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" ]; then
+if [ "$CONFIG_UFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' UFS filesystem write support (DANGEROUS)' CONFIG_UFS_FS_WRITE
fi
@@ -79,11 +79,9 @@ if [ "$CONFIG_INET" = "y" ]; then
if [ "$CONFIG_NFS_FS" = "y" -a "$CONFIG_IP_PNP" = "y" ]; then
bool ' Root file system on NFS' CONFIG_ROOT_NFS
fi
- if [ "$CONFIG_EXPERIMENTAL" ]; then
- tristate 'NFS server support (EXPERIMENTAL)' CONFIG_NFSD
- if [ "$CONFIG_NFSD" != "n" ]; then
- bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN
- fi
+ tristate 'NFS server support' CONFIG_NFSD
+ if [ "$CONFIG_NFSD" != "n" ]; then
+ bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN
fi
if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
define_bool CONFIG_SUNRPC y
diff --git a/fs/buffer.c b/fs/buffer.c
index 39dd880f8..7b2906868 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -751,9 +751,6 @@ static void end_buffer_io_async(struct buffer_head * bh, int uptodate)
if (test_and_clear_bit(PG_decr_after, &page->flags))
atomic_dec(&nr_async_pages);
- if (page->owner != (void *)-1)
- PAGE_BUG(page);
- page->owner = current;
UnlockPage(page);
return;
@@ -1181,8 +1178,6 @@ static int create_page_buffers(int rw, struct page *page, kdev_t dev, int b[], i
if (!PageLocked(page))
BUG();
- if (page->owner != current)
- PAGE_BUG(page);
/*
* Allocate async buffer heads pointing to this page, just for I/O.
* They don't show up in the buffer hash table, but they *are*
@@ -1297,15 +1292,16 @@ static void create_empty_buffers(struct page *page, struct inode *inode, unsigne
static void unmap_underlying_metadata(struct buffer_head * bh)
{
+#if 0
bh = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size);
- if (bh)
- {
+ if (bh) {
unmap_buffer(bh);
/* Here we could run brelse or bforget. We use
bforget because it will try to put the buffer
in the freelist. */
__bforget(bh);
}
+#endif
}
/*
@@ -1947,7 +1943,6 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size)
}
if (!page->buffers)
BUG();
- page->owner = (void *)-1;
head = page->buffers;
bh = head;
@@ -1989,7 +1984,6 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size)
} else {
if (!nr && rw == READ) {
SetPageUptodate(page);
- page->owner = current;
UnlockPage(page);
}
if (nr && (rw == WRITE))
@@ -2023,7 +2017,6 @@ int block_read_full_page(struct file * file, struct page * page)
blocks = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
iblock = page->offset >> inode->i_sb->s_blocksize_bits;
- page->owner = (void *)-1;
bh = head;
nr = 0;
@@ -2057,7 +2050,6 @@ int block_read_full_page(struct file * file, struct page * page)
* uptodate as well.
*/
SetPageUptodate(page);
- page->owner = current;
UnlockPage(page);
}
return 0;
@@ -2201,11 +2193,13 @@ busy_buffer_page:
void show_buffers(void)
{
+#ifdef __SMP__
struct buffer_head * bh;
int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0;
int protected = 0;
int nlist;
static char *buf_types[NR_LIST] = { "CLEAN", "LOCKED", "DIRTY" };
+#endif
printk("Buffer memory: %6dkB\n",
atomic_read(&buffermem_pages) << (PAGE_SHIFT-10));
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 52e97c585..4cf932d51 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -668,6 +668,16 @@ void ext2_read_inode (struct inode * inode)
inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+ /* We now have enough fields to check if the inode was active or not.
+ * This is needed because nfsd might try to access dead inodes
+ * the test is that same one that e2fsck uses
+ * NeilBrown 1999oct15
+ */
+ if (inode->i_nlink == 0 && (inode->i_mode == 0 || inode->u.ext2_i.i_dtime)) {
+ /* this inode is deleted */
+ brelse (bh);
+ goto bad_inode;
+ }
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
inode->i_version = ++event;
diff --git a/fs/fat/Makefile b/fs/fat/Makefile
index 8e8064c28..339250c7c 100644
--- a/fs/fat/Makefile
+++ b/fs/fat/Makefile
@@ -8,7 +8,7 @@
# Note 2! The CFLAGS definitions are now in the main makefile.
O_TARGET := fat.o
-O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o cvf.o
+O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o tables.o cvf.o
OX_OBJS := fatfs_syms.o
M_OBJS := $(O_TARGET)
diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
index 070361aa9..590b585dd 100644
--- a/fs/fat/fatfs_syms.c
+++ b/fs/fat/fatfs_syms.c
@@ -33,7 +33,6 @@ EXPORT_SYMBOL(fat_fs_panic);
EXPORT_SYMBOL(fat__get_entry);
EXPORT_SYMBOL(fat_lock_creation);
EXPORT_SYMBOL(fat_mark_buffer_dirty);
-EXPORT_SYMBOL(fat_mmap);
EXPORT_SYMBOL(fat_notify_change);
EXPORT_SYMBOL(fat_parent_ino);
EXPORT_SYMBOL(fat_put_super);
@@ -54,7 +53,6 @@ EXPORT_SYMBOL(fat_get_cluster);
EXPORT_SYMBOL(lock_fat);
EXPORT_SYMBOL(unlock_fat);
EXPORT_SYMBOL(fat_dir_ioctl);
-EXPORT_SYMBOL(fat_readpage);
EXPORT_SYMBOL(fat_add_entries);
EXPORT_SYMBOL(fat_dir_empty);
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 5baa51af4..505dc4e06 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -115,13 +115,13 @@ static int fat_write_partial_page(struct file *file, struct page *page, unsigned
struct inode *inode = dentry->d_inode;
struct page *new_page, **hash;
unsigned long pgpos;
- unsigned long page_cache = 0;
+ struct page *page_cache = NULL;
long status;
pgpos = MSDOS_I(inode)->i_realsize & PAGE_CACHE_MASK;
while (pgpos < page->offset) {
- hash = page_hash(inode, pgpos);
-repeat_find: new_page = __find_lock_page(inode, pgpos, hash);
+ hash = page_hash(&inode->i_data, pgpos);
+repeat_find: new_page = __find_lock_page(&inode->i_data, pgpos, hash);
if (!new_page) {
if (!page_cache) {
page_cache = page_cache_alloc();
@@ -130,10 +130,10 @@ repeat_find: new_page = __find_lock_page(inode, pgpos, hash);
status = -ENOMEM;
goto out;
}
- new_page = page_cache_entry(page_cache);
- if (add_to_page_cache_unique(new_page,inode,pgpos,hash))
+ new_page = page_cache;
+ if (add_to_page_cache_unique(new_page,&inode->i_data,pgpos,hash))
goto repeat_find;
- page_cache = 0;
+ page_cache = NULL;
}
status = block_write_cont_page(file, new_page, PAGE_SIZE, 0, NULL);
UnlockPage(new_page);
diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c
deleted file mode 100644
index 80ec56b54..000000000
--- a/fs/fat/mmap.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * linux/fs/fat/mmap.c
- *
- * Written by Jacques Gelinas (jacques@solucorp.qc.ca)
- * Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
- *
- * mmap handling for fat-based filesystems
- */
-
-#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
-#include <linux/version.h>
-
-#include <linux/stat.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/shm.h>
-#include <linux/errno.h>
-#include <linux/mman.h>
-#include <linux/string.h>
-#include <linux/malloc.h>
-#include <linux/msdos_fs.h>
-
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
-/*
- * Fill in the supplied page for mmap
- */
-static unsigned long fat_file_mmap_nopage(
- struct vm_area_struct * area,
- unsigned long address,
- int error_code)
-{
- struct inode * inode = area->vm_file->f_dentry->d_inode;
- unsigned long page;
- unsigned int clear;
- int pos;
- long gap; /* distance from eof to pos */
-
- page = __get_free_page(GFP_KERNEL);
- if (!page)
- return page;
- address &= PAGE_MASK;
- pos = address - area->vm_start + area->vm_offset;
-
- clear = 0;
- gap = inode->i_size - pos;
- if (gap <= 0){
- /* mmaping beyond end of file */
- clear = PAGE_SIZE;
- }else{
- int cur_read;
- int need_read;
- struct file filp;
- if (gap < PAGE_SIZE){
- clear = PAGE_SIZE - gap;
- }
- filp.f_reada = 0;
- filp.f_pos = pos;
- filp.f_dentry=area->vm_file->f_dentry;
- need_read = PAGE_SIZE - clear;
- {
- mm_segment_t cur_fs = get_fs();
- set_fs (KERNEL_DS);
- cur_read = fat_file_read (&filp, (char*)page,
- need_read, &filp.f_pos);
- set_fs (cur_fs);
- }
- if (cur_read != need_read){
- printk ("MSDOS: Error while reading an mmap file %d <> %d\n"
- ,cur_read,need_read);
- }
- }
- if (clear > 0){
- memset ((char*)page+PAGE_SIZE-clear,0,clear);
- }
- return page;
-}
-
-struct vm_operations_struct fat_file_mmap = {
- NULL, /* open */
- NULL, /* close */
- NULL, /* unmap */
- NULL, /* protect */
- NULL, /* sync */
- NULL, /* advise */
- fat_file_mmap_nopage, /* nopage */
- NULL, /* wppage */
- NULL /* swapout */
-};
-
-/*
- * This is used for a general mmap of an msdos file
- * Returns 0 if ok, or a negative error code if not.
- */
-int fat_mmap(struct file * file, struct vm_area_struct * vma)
-{
- struct inode *inode = file->f_dentry->d_inode;
- if (MSDOS_SB(inode->i_sb)->cvf_format &&
- MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap)
- return MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap(file,vma);
-
- if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
- return -EINVAL;
- if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
- return -EINVAL;
- if (!inode->i_sb || !S_ISREG(inode->i_mode))
- return -EACCES;
- if (!IS_RDONLY(inode)) {
- inode->i_atime = CURRENT_TIME;
- mark_inode_dirty(inode);
- }
-
- vma->vm_ops = &fat_file_mmap;
- return 0;
-}
-
-
-int fat_readpage(struct file *file, struct page * page)
-{
- struct dentry * dentry = file->f_dentry;
- struct inode * inode = dentry->d_inode;
- if (MSDOS_SB(inode->i_sb)->cvf_format &&
- MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage)
- return MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage(inode,page);
-
- printk("fat_readpage called with no handler (shouldn't happen)\n");
- return -1;
-}
-
diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c
index 066ce5c28..72f61c20a 100644
--- a/fs/hpfs/file.c
+++ b/fs/hpfs/file.c
@@ -91,7 +91,7 @@ static int hpfs_write_partial_page(struct file *file, struct page *page, unsigne
struct inode *inode = dentry->d_inode;
struct page *new_page, **hash;
unsigned long pgpos;
- unsigned long page_cache = 0;
+ struct page * page_cache = NULL;
long status;
printk("- off: %08x\n", (int)page->offset);
@@ -99,8 +99,8 @@ static int hpfs_write_partial_page(struct file *file, struct page *page, unsigne
while (pgpos < page->offset) {
long pgp = pgpos;
printk("pgpos: %08x, bl: %d\n", (int)pgpos, (int)inode->i_blocks);
- hash = page_hash(inode, pgpos);
-repeat_find: new_page = __find_lock_page(inode, pgpos, hash);
+ hash = page_hash(&inode->i_data, pgpos);
+repeat_find: new_page = __find_lock_page(&inode->i_data, pgpos, hash);
if (!new_page) {
if (!page_cache) {
page_cache = page_cache_alloc();
@@ -109,10 +109,10 @@ repeat_find: new_page = __find_lock_page(inode, pgpos, hash);
status = -ENOMEM;
goto out;
}
- new_page = page_cache_entry(page_cache);
- if (add_to_page_cache_unique(new_page,inode,pgpos,hash))
+ new_page = page_cache;
+ if (add_to_page_cache_unique(new_page,&inode->i_data,pgpos,hash))
goto repeat_find;
- page_cache = 0;
+ page_cache = NULL;
}
printk("A\n");
status = block_write_cont_page(file, new_page, PAGE_SIZE, 0, NULL);
diff --git a/fs/inode.c b/fs/inode.c
index f03295d5c..bb4e0031f 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -89,7 +89,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
memset(inode, 0, sizeof(*inode));
init_waitqueue_head(&inode->i_wait);
INIT_LIST_HEAD(&inode->i_hash);
- INIT_LIST_HEAD(&inode->i_pages);
+ INIT_LIST_HEAD(&inode->i_data.pages);
INIT_LIST_HEAD(&inode->i_dentry);
sema_init(&inode->i_sem, 1);
spin_lock_init(&inode->i_shared_lock);
@@ -247,7 +247,7 @@ void write_inode_now(struct inode *inode)
*/
void clear_inode(struct inode *inode)
{
- if (inode->i_nrpages)
+ if (inode->i_data.nrpages)
BUG();
if (!(inode->i_state & I_FREEING))
BUG();
@@ -274,7 +274,7 @@ static void dispose_list(struct list_head * head)
list_del(inode_entry);
inode = list_entry(inode_entry, struct inode, i_list);
- if (inode->i_nrpages)
+ if (inode->i_data.nrpages)
truncate_inode_pages(inode, 0);
clear_inode(inode);
destroy_inode(inode);
@@ -351,7 +351,7 @@ int invalidate_inodes(struct super_block * sb)
* dispose_list.
*/
#define CAN_UNUSE(inode) \
- (((inode)->i_state | (inode)->i_nrpages) == 0)
+ (((inode)->i_state | (inode)->i_data.nrpages) == 0)
#define INODE(entry) (list_entry(entry, struct inode, i_list))
void prune_icache(int goal)
@@ -658,7 +658,7 @@ void iput(struct inode *inode)
if (op && op->delete_inode) {
void (*delete)(struct inode *) = op->delete_inode;
spin_unlock(&inode_lock);
- if (inode->i_nrpages)
+ if (inode->i_data.nrpages)
truncate_inode_pages(inode, 0);
delete(inode);
spin_lock(&inode_lock);
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 8df091923..4160bf2e3 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -69,6 +69,7 @@ nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
call->a_args.lock = *lock;
call->a_args.lock.caller = system_utsname.nodename;
+ init_waitqueue_head(&lock->fl.fl_wait);
/* set default data area */
call->a_args.lock.oh.data = call->a_owner;
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index ea541e796..d3af541dd 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -576,9 +576,9 @@ printk("ncp_do_readdir: finding cache for %s/%s\n",
#endif
/* cache using inspired by smbfs and nfs */
- hash = page_hash(dir, 0);
+ hash = page_hash(&dir->i_data, 0);
- page = __find_lock_page(dir, 0, hash);
+ page = __find_lock_page(&dir->i_data, 0, hash);
if (!page) {
unsigned long page_cache;
@@ -586,7 +586,7 @@ printk("ncp_do_readdir: finding cache for %s/%s\n",
page_cache = page_cache_alloc();
if (page_cache) {
page = page_cache_entry(page_cache);
- if (add_to_page_cache_unique(page, dir, 0, hash)) {
+ if (add_to_page_cache_unique(page, &dir->i_data, 0, hash)) {
page_cache_release(page);
page = NULL;
page_cache_free(page_cache);
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index b7ec225ac..651b1152c 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -255,7 +255,7 @@ static long refetch_to_readdir_cookie(struct file *file, struct inode *inode)
again:
cur_off = 0;
for (;;) {
- page = find_get_page(inode, cur_off);
+ page = find_get_page(&inode->i_data, cur_off);
if (page) {
if (!Page_Uptodate(page))
goto out_error;
@@ -332,16 +332,16 @@ static struct page *try_to_get_dirent_page(struct file *file, __u32 cookie, int
goto out;
}
- hash = page_hash(inode, offset);
+ hash = page_hash(&inode->i_data, offset);
repeat:
- page = __find_lock_page(inode, offset, hash);
+ page = __find_lock_page(&inode->i_data, offset, hash);
if (page) {
page_cache_free(page_cache);
goto unlock_out;
}
page = page_cache;
- if (add_to_page_cache_unique(page, inode, offset, hash)) {
+ if (add_to_page_cache_unique(page, &inode->i_data, offset, hash)) {
page_cache_release(page);
goto repeat;
}
@@ -431,8 +431,8 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
if ((offset = nfs_readdir_offset(inode, filp->f_pos)) < 0)
goto no_dirent_page;
- hash = page_hash(inode, offset);
- page = __find_get_page(inode, offset, hash);
+ hash = page_hash(&inode->i_data, offset);
+ page = __find_get_page(&inode->i_data, offset, hash);
if (!page)
goto no_dirent_page;
if (!Page_Uptodate(page))
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index a7960c1e0..a85ef5e74 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -152,7 +152,6 @@ nfs_readpage_result(struct rpc_task *task)
fail++;
dprintk("NFS: %d successful reads, %d failures\n", succ, fail);
}
- page->owner = current; // HACK, FIXME, will go away.
UnlockPage(page);
free_page(address);
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 6b0d0f05b..1b1705ef2 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -66,16 +66,16 @@ static struct page *try_to_get_symlink_page(struct dentry *dentry, struct inode
if (!page_cache)
goto out;
- hash = page_hash(inode, 0);
+ hash = page_hash(&inode->i_data, 0);
repeat:
- page = __find_lock_page(inode, 0, hash);
+ page = __find_lock_page(&inode->i_data, 0, hash);
if (page) {
page_cache_free(page_cache);
goto unlock_out;
}
page = page_cache;
- if (add_to_page_cache_unique(page, inode, 0, hash)) {
+ if (add_to_page_cache_unique(page, &inode->i_data, 0, hash)) {
page_cache_release(page);
goto repeat;
}
@@ -107,7 +107,7 @@ static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
u32 *p, len;
/* Caller revalidated the directory inode already. */
- page = find_get_page(inode, 0);
+ page = find_get_page(&inode->i_data, 0);
if (!page)
goto no_readlink_page;
if (!Page_Uptodate(page))
@@ -142,7 +142,7 @@ nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
u32 *p;
/* Caller revalidated the directory inode already. */
- page = find_get_page(inode, 0);
+ page = find_get_page(&inode->i_data, 0);
if (!page)
goto no_followlink_page;
if (!Page_Uptodate(page))
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 1ad581ea8..d5b1537db 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -365,16 +365,6 @@ exp_do_unexport(svc_export *unexp)
exp->ex_parent = unexp->ex_parent;
}
- /*
- * Check whether this is the last export for this device,
- * and if so flush any cached dentries.
- */
- if (!exp_device_in_use(unexp->ex_dev)) {
-printk("exp_do_unexport: %s last use, flushing cache\n",
- kdevname(unexp->ex_dev));
- nfsd_fh_flush(unexp->ex_dev);
- }
-
dentry = unexp->ex_dentry;
inode = dentry->d_inode;
if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
@@ -628,6 +618,7 @@ struct flags {
{ NFSEXP_KERBEROS, { "kerberos", ""}},
{ NFSEXP_SUNSECURE, { "sunsecure", ""}},
{ NFSEXP_CROSSMNT, {"nohide", ""}},
+ { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
{ 0, {"", ""}}
};
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 150acc6d9..4ec7b691b 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -39,7 +39,6 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
-extern void nfsd_fh_init(void);
extern long sys_call_table[];
static int nfsctl_svc(struct nfsctl_svc *data);
@@ -79,7 +78,6 @@ nfsd_init(void)
nfsd_cache_init(); /* RPC reply cache */
nfsd_export_init(); /* Exports table */
nfsd_lockd_init(); /* lockd->nfsd callbacks */
- nfsd_fh_init(); /* FH table */
proc_export_init();
initialized = 1;
}
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index a1241da96..5f8e90a0e 100644
--- a/fs/nfsd/nfsfh.c
+++ b/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 @@ static int get_parent_ino(struct dentry *dentry, struct nfsd_dirent *dirent)
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 @@ static int get_parent_ino(struct dentry *dentry, struct nfsd_dirent *dirent)
if (old_seq == buffer.sequence)
break;
}
- dirent->ino = buffer.dirino;
out_close:
if (file.f_op->release)
@@ -374,663 +115,245 @@ out:
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;
- }
-
-out_page:
- free_page(page);
-out:
- 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;
+ 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;
}
}
- if (empty)
- expire_fhe(empty, cache);
-
-out:
- return;
-}
-
-/*
- * Expire any cache entries older than a certain age.
- */
-static void expire_old(int cache, int age)
-{
- 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);
+ result = d_alloc_root(inode);
+ if (result == NULL) {
+ iput(inode);
+ return ERR_PTR(-ENOMEM);
}
+ d_rehash(result); /* so a dput won't loose it */
+ return result;
}
-/*
- * Add a dentry to the file or dir cache.
- *
- * Note: As NFS file handles must have an inode, we don't accept
- * negative dentries.
+/* 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 int add_to_fhcache(struct dentry *dentry, int cache)
+int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name)
{
- 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;
- }
-
-repeat:
- fhe = find_fhe(dentry, cache, &empty);
- if (fhe) {
- 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);
- /*
- * 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;
+ list_del(&tdentry->d_child);
+ tdentry->d_parent = tdentry;
+ d_rehash(target);
+ dput(tdentry);
+ return 0;
}
-/*
- * 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;
-}
-
-/*
- * 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
+ 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
+ */
+ /* 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);
}
- 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 (pdentry == NULL) {
+ pdentry = d_alloc_root(igrab(tdentry->d_inode));
+ if (pdentry) d_rehash(pdentry);
}
- 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 ...
- */
- parent = dget(dentry->d_parent);
- if (add_to_fhcache(parent, NFSD_DIR_CACHE))
- nfsd_nr_verified++;
- else
- dput(parent);
- return dentry;
+ if (pdentry == NULL)
+ pdentry = ERR_PTR(-ENOMEM);
}
-out:
- return NULL;
+ dput(tdentry); /* it was never rehashed, it will be discarded */
+ return pdentry;
}
/*
- * 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);
- }
-}
-
-/*
- * 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)
-{
- 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;
+find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath)
+{
+ 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;
}
}
-
- /*
- * 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;
- }
-
- /*
- * 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;
+ 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;
}
-#ifdef NFSD_PARANOIA
-printk("find_fh_dentry: %s/%s lookup mismatch!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
- dput(dentry);
- }
-
- /*
- * 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.
+ /* I'm not sure that this is the best test for
+ * "is it not a floating dentry?"
*/
- dentry = lookup_by_inode(dget(parent), u32_to_ino_t(fh->fh_ino));
- dput(parent);
- if (dentry)
- goto out;
- }
+ if (!IS_ROOT(pdentry) || parent->i_sb->s_root == pdentry)
+ found = 1;
- /*
- * 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);
+ err = get_ino_name(pdentry, &qs, dentry->d_inode->i_ino);
+ if (err) {
+ dput(pdentry);
+ goto err_dentry;
+ }
+ 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;
- 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 @@ out:
*
* 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 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
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 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
/*
* 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 @@ check_type:
* 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 @@ dprintk("fh_verify: no root_squashed access.\n");
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_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
}
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 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
/*
* 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 @@ fh_update(struct svc_fh *fhp)
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 @@ out_negative:
}
/*
- * 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 @@ fh_put(struct svc_fh *fhp)
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;
@@ -1263,135 +612,3 @@ out_bad:
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));
-}
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index b7fa534e0..0057e1fa3 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -101,7 +101,7 @@ static void
nfsd(struct svc_rqst *rqstp)
{
struct svc_serv *serv = rqstp->rq_server;
- int oldumask, err, first = 0;
+ int oldumask, err;
/* Lock module and set up kernel thread */
MOD_INC_USE_COUNT;
@@ -117,7 +117,6 @@ nfsd(struct svc_rqst *rqstp)
current->fs->umask = 0;
if (!nfsd_active++) {
nfssvc_boot = xtime; /* record boot time */
- first = 1;
}
lockd_up(); /* start lockd */
@@ -136,13 +135,8 @@ nfsd(struct svc_rqst *rqstp)
* recvfrom routine.
*/
while ((err = svc_recv(serv, rqstp,
- first?5*HZ:MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) {
- if (first && 1) {
- exp_readlock();
- expire_all();
- exp_unlock();
- }
- }
+ MAX_SCHEDULE_TIMEOUT)) == -EAGAIN)
+ ;
if (err < 0)
break;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index fa1753633..bc849dd8e 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -234,6 +234,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
int ftype = 0;
int imode;
int err;
+ kernel_cap_t saved_cap = 0;
if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
accmode |= MAY_WRITE;
@@ -242,7 +243,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
/* Get inode */
err = fh_verify(rqstp, fhp, ftype, accmode);
- if (err)
+ if (err || !iap->ia_valid)
goto out;
dentry = fhp->fh_dentry;
@@ -252,7 +253,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
if (err)
goto out_nfserr;
- /* The size case is special... */
+ /* The size case is special. It changes the file as well as the attributes. */
if (iap->ia_valid & ATTR_SIZE) {
if (!S_ISREG(inode->i_mode))
printk("nfsd_setattr: size change??\n");
@@ -264,15 +265,14 @@ printk("nfsd_setattr: size change??\n");
err = get_write_access(inode);
if (err)
goto out_nfserr;
- /* N.B. Should we update the inode cache here? */
- inode->i_size = iap->ia_size;
- if (inode->i_op && inode->i_op->truncate)
- inode->i_op->truncate(inode);
- mark_inode_dirty(inode);
- put_write_access(inode);
- iap->ia_valid &= ~ATTR_SIZE;
- iap->ia_valid |= ATTR_MTIME;
- iap->ia_mtime = CURRENT_TIME;
+
+ err = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL,
+ iap->ia_size<inode->i_size ? iap->ia_size : inode->i_size,
+ abs(inode->i_size - iap->ia_size));
+
+ if (err)
+ goto out_nfserr;
+ DQUOT_INIT(inode);
}
imode = inode->i_mode;
@@ -294,23 +294,32 @@ printk("nfsd_setattr: size change??\n");
}
/* Change the attributes. */
- if (iap->ia_valid) {
- kernel_cap_t saved_cap = 0;
- iap->ia_valid |= ATTR_CTIME;
- iap->ia_ctime = CURRENT_TIME;
- if (current->fsuid != 0) {
- saved_cap = current->cap_effective;
- cap_clear(current->cap_effective);
- }
+
+ iap->ia_valid |= ATTR_CTIME;
+ if (current->fsuid != 0) {
+ saved_cap = current->cap_effective;
+ cap_clear(current->cap_effective);
+ }
+ if (iap->ia_valid & ATTR_SIZE) {
+ fh_lock(fhp);
err = notify_change(dentry, iap);
- if (current->fsuid != 0)
- current->cap_effective = saved_cap;
- if (err)
- goto out_nfserr;
- if (EX_ISSYNC(fhp->fh_export))
- write_inode_now(inode);
+ if (!err) {
+ vmtruncate(inode,iap->ia_size);
+ if (inode->i_op && inode->i_op->truncate)
+ inode->i_op->truncate(inode);
+ }
+ fh_unlock(fhp);
+ put_write_access(inode);
}
+ else
+ err = notify_change(dentry, iap);
+ if (current->fsuid != 0)
+ current->cap_effective = saved_cap;
+ if (err)
+ goto out_nfserr;
+ if (EX_ISSYNC(fhp->fh_export))
+ write_inode_now(inode);
err = 0;
out:
return err;
@@ -401,7 +410,6 @@ nfsd_close(struct file *filp)
filp->f_op->release(inode, filp);
if (filp->f_mode & FMODE_WRITE) {
put_write_access(inode);
- DQUOT_DROP(inode);
}
}
@@ -548,7 +556,6 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
if ((stable || (stable = EX_ISSYNC(exp))) && !EX_WGATHER(exp))
file.f_flags |= O_SYNC;
- fh_lock(fhp); /* lock inode */
file.f_pos = offset; /* set write offset */
/* Write the data. */
@@ -580,8 +587,6 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
current->cap_effective = saved_cap;
}
- fh_unlock(fhp); /* unlock inode */
-
if (err >= 0 && stable) {
static unsigned long last_ino = 0;
static kdev_t last_dev = NODEV;
@@ -651,9 +656,13 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = nfserr_perm;
if (!flen)
goto out;
- err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
- if (err)
- goto out;
+
+ /* from mkdir it won't be verified, from create it will */
+ if (!fhp->fh_dverified) {
+ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
+ if (err)
+ goto out;
+ }
dentry = fhp->fh_dentry;
dirp = dentry->d_inode;
@@ -731,7 +740,6 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
*/
DQUOT_INIT(dirp);
err = opfunc(dirp, dchild, iap->ia_mode, rdev);
- DQUOT_DROP(dirp);
if (err < 0)
goto out_nfserr;
@@ -786,6 +794,11 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
err = get_write_access(inode);
if (err)
goto out_nfserr;
+ err = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL,
+ size<inode->i_size ? size : inode->i_size,
+ abs(inode->i_size - size));
+ if (err)
+ goto out_nfserr;
/* Things look sane, lock and do it. */
fh_lock(fhp);
@@ -797,15 +810,14 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
cap_clear(current->cap_effective);
}
err = notify_change(dentry, &newattrs);
- if (current->fsuid != 0)
- current->cap_effective = saved_cap;
if (!err) {
vmtruncate(inode, size);
if (inode->i_op && inode->i_op->truncate)
inode->i_op->truncate(inode);
}
+ if (current->fsuid != 0)
+ current->cap_effective = saved_cap;
put_write_access(inode);
- DQUOT_DROP(inode);
fh_unlock(fhp);
out_nfserr:
if (err)
@@ -902,7 +914,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (!dnew->d_inode) {
DQUOT_INIT(dirp);
err = dirp->i_op->symlink(dirp, dnew, path);
- DQUOT_DROP(dirp);
if (!err) {
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(dirp);
@@ -981,7 +992,6 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
DQUOT_INIT(dirp);
err = dirp->i_op->link(dold, dirp, dnew);
- DQUOT_DROP(dirp);
if (!err) {
if (EX_ISSYNC(ffhp->fh_export)) {
write_inode_now(dirp);
@@ -1105,8 +1115,6 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
}
} else
dprintk("nfsd: Caught race in nfsd_rename");
- DQUOT_DROP(fdir);
- DQUOT_DROP(tdir);
nfsd_double_up(&tdir->i_sem, &fdir->i_sem);
dput(ndentry);
@@ -1157,7 +1165,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
goto out;
}
- expire_by_dentry(rdentry);
if (type != S_IFDIR) {
/* It's UNLINK */
@@ -1168,7 +1175,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
err = vfs_unlink(dirp, rdentry);
- DQUOT_DROP(dirp);
fh_unlock(fhp);
dput(rdentry);
@@ -1188,7 +1194,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
err = vfs_rmdir(dirp, rdentry);
rdentry->d_count--;
- DQUOT_DROP(dirp);
if (!fhp->fh_post_version)
fhp->fh_post_version = dirp->i_version;
fhp->fh_locked = 0;
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
index a30f382a6..539b60da9 100644
--- a/fs/romfs/inode.c
+++ b/fs/romfs/inode.c
@@ -404,9 +404,6 @@ romfs_readpage(struct file * file, struct page * page)
get_page(page);
buf = page_address(page);
- /* hack? */
- page->owner = current;
-
offset = page->offset;
if (offset < inode->i_size) {
avail = inode->i_size-offset;
diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c
index 950ecb3c1..efb472d4b 100644
--- a/fs/smbfs/cache.c
+++ b/fs/smbfs/cache.c
@@ -43,16 +43,16 @@ printk_name(const char *name, int len)
* the page if it isn't in memory. As I understand it the rest of the
* smb-cache code assumes we return a locked page.
*/
-unsigned long
-get_cached_page(struct inode * inode, unsigned long offset, int new)
+static unsigned long
+get_cached_page(struct address_space *mapping, unsigned long offset, int new)
{
struct page * page;
struct page ** hash;
unsigned long new_page;
again:
- hash = page_hash(inode, offset);
- page = __find_lock_page(inode, offset, hash);
+ hash = page_hash(mapping, offset);
+ page = __find_lock_page(mapping, offset, hash);
if(!page && new) {
/* not in cache, alloc a new page */
new_page = page_cache_alloc();
@@ -60,7 +60,7 @@ get_cached_page(struct inode * inode, unsigned long offset, int new)
return 0;
clear_page(new_page); /* smb code assumes pages are zeroed */
page = page_cache_entry(new_page);
- if (add_to_page_cache_unique(page, inode, offset, hash)) {
+ if (add_to_page_cache_unique(page, mapping, offset, hash)) {
/* Hmm, a page has materialized in the
cache. Fine. Go back and get that page
instead ... throwing away this one first. */
@@ -75,10 +75,10 @@ get_cached_page(struct inode * inode, unsigned long offset, int new)
return page_address(page);
}
-static inline struct inode *
+static inline struct address_space *
get_cache_inode(struct cache_head *cachep)
{
- return (mem_map + MAP_NR((unsigned long) cachep))->inode;
+ return (mem_map + MAP_NR((unsigned long) cachep))->mapping;
}
/*
@@ -89,14 +89,14 @@ get_cache_inode(struct cache_head *cachep)
struct cache_head *
smb_get_dircache(struct dentry * dentry)
{
- struct inode * inode = dentry->d_inode;
+ struct address_space * mapping = &dentry->d_inode->i_data;
struct cache_head * cachep;
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_get_dircache: finding cache for %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
- cachep = (struct cache_head *) get_cached_page(inode, 0, 1);
+ cachep = (struct cache_head *) get_cached_page(mapping, 0, 1);
if (!cachep)
goto out;
if (cachep->valid)
@@ -118,7 +118,7 @@ printk("smb_get_dircache: cache %s/%s has existing block!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
offset = PAGE_SIZE + (i << PAGE_SHIFT);
- block = (struct cache_block *) get_cached_page(inode,
+ block = (struct cache_block *) get_cached_page(mapping,
offset, 0);
if (!block)
goto out;
@@ -187,7 +187,7 @@ void
smb_add_to_cache(struct cache_head * cachep, struct cache_dirent *entry,
off_t fpos)
{
- struct inode * inode = get_cache_inode(cachep);
+ struct address_space * mapping = get_cache_inode(cachep);
struct cache_index * index;
struct cache_block * block;
unsigned long page_off;
@@ -195,8 +195,8 @@ smb_add_to_cache(struct cache_head * cachep, struct cache_dirent *entry,
unsigned int needed = len + sizeof(struct cache_entry);
#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_add_to_cache: cache inode %p, status %d, adding ",
- inode, cachep->status);
+printk("smb_add_to_cache: cache %p, status %d, adding ",
+ mapping, cachep->status);
printk_name(entry->name, entry->len);
printk(" at %ld\n", fpos);
#endif
@@ -251,14 +251,14 @@ printk("smb_add_to_cache: new index already has block!\n");
get_block:
cachep->pages++;
page_off = PAGE_SIZE + (cachep->idx << PAGE_SHIFT);
- block = (struct cache_block *) get_cached_page(inode, page_off, 1);
+ block = (struct cache_block *) get_cached_page(mapping, page_off, 1);
if (block)
{
index->block = block;
index->space = PAGE_SIZE;
#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_add_to_cache: inode=%p, pages=%d, block at %ld\n",
-inode, cachep->pages, page_off);
+printk("smb_add_to_cache: mapping=%p, pages=%d, block at %ld\n",
+mapping, cachep->pages, page_off);
#endif
goto add_entry;
}