diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-01-27 23:45:22 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-01-27 23:45:22 +0000 |
commit | 5b35aa5cd29bb111d847b2a2ed18110acbfb1f44 (patch) | |
tree | c7bbaa1137528330d3c74d14056ef7016a52be72 /fs | |
parent | 511bcd7c5924ce9e98ad1cb851988f7448dfef0f (diff) |
Merge with Linux 2.3.24.
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Config.in | 12 | ||||
-rw-r--r-- | fs/buffer.c | 16 | ||||
-rw-r--r-- | fs/ext2/inode.c | 10 | ||||
-rw-r--r-- | fs/fat/Makefile | 2 | ||||
-rw-r--r-- | fs/fat/fatfs_syms.c | 2 | ||||
-rw-r--r-- | fs/fat/file.c | 12 | ||||
-rw-r--r-- | fs/fat/mmap.c | 131 | ||||
-rw-r--r-- | fs/hpfs/file.c | 12 | ||||
-rw-r--r-- | fs/inode.c | 10 | ||||
-rw-r--r-- | fs/lockd/clntproc.c | 1 | ||||
-rw-r--r-- | fs/ncpfs/dir.c | 6 | ||||
-rw-r--r-- | fs/nfs/dir.c | 12 | ||||
-rw-r--r-- | fs/nfs/read.c | 1 | ||||
-rw-r--r-- | fs/nfs/symlink.c | 10 | ||||
-rw-r--r-- | fs/nfsd/export.c | 11 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 2 | ||||
-rw-r--r-- | fs/nfsd/nfsfh.c | 1397 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 12 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 91 | ||||
-rw-r--r-- | fs/romfs/inode.c | 3 | ||||
-rw-r--r-- | fs/smbfs/cache.c | 32 |
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; } |