diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-12-01 04:02:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-12-01 04:02:08 +0000 |
commit | fd095d09f2d475dc2e8599b1b8bae1cd65e91685 (patch) | |
tree | 217f87a997699505e0dd752931409b9f10fffe65 /fs | |
parent | c02e0599c4233f97071928f8118841954bacdadf (diff) |
Merge with 2.1.56 as first part of merging back my code.
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Makefile | 2 | ||||
-rw-r--r-- | fs/affs/dir.c | 2 | ||||
-rw-r--r-- | fs/bad_inode.c | 83 | ||||
-rw-r--r-- | fs/binfmt_aout.c | 4 | ||||
-rw-r--r-- | fs/binfmt_elf.c | 4 | ||||
-rw-r--r-- | fs/block_dev.c | 9 | ||||
-rw-r--r-- | fs/buffer.c | 17 | ||||
-rw-r--r-- | fs/dcache.c | 20 | ||||
-rw-r--r-- | fs/exec.c | 2 | ||||
-rw-r--r-- | fs/ext2/file.c | 5 | ||||
-rw-r--r-- | fs/ext2/fsync.c | 44 | ||||
-rw-r--r-- | fs/fat/mmap.c | 4 | ||||
-rw-r--r-- | fs/fcntl.c | 86 | ||||
-rw-r--r-- | fs/inode.c | 171 | ||||
-rw-r--r-- | fs/lockd/clntlock.c | 2 | ||||
-rw-r--r-- | fs/locks.c | 230 | ||||
-rw-r--r-- | fs/minix/fsync.c | 8 | ||||
-rw-r--r-- | fs/namei.c | 8 | ||||
-rw-r--r-- | fs/ncpfs/file.c | 2 | ||||
-rw-r--r-- | fs/ncpfs/mmap.c | 4 | ||||
-rw-r--r-- | fs/nfs/file.c | 17 | ||||
-rw-r--r-- | fs/nfs/write.c | 34 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 5 | ||||
-rw-r--r-- | fs/open.c | 2 | ||||
-rw-r--r-- | fs/pipe.c | 3 | ||||
-rw-r--r-- | fs/proc/generic.c | 6 | ||||
-rw-r--r-- | fs/proc/mem.c | 9 | ||||
-rw-r--r-- | fs/proc/scsi.c | 5 | ||||
-rw-r--r-- | fs/read_write.c | 19 | ||||
-rw-r--r-- | fs/smbfs/dir.c | 541 | ||||
-rw-r--r-- | fs/smbfs/file.c | 120 | ||||
-rw-r--r-- | fs/smbfs/inode.c | 232 | ||||
-rw-r--r-- | fs/smbfs/proc.c | 442 | ||||
-rw-r--r-- | fs/smbfs/sock.c | 371 | ||||
-rw-r--r-- | fs/sysv/fsync.c | 3 |
35 files changed, 1583 insertions, 933 deletions
diff --git a/fs/Makefile b/fs/Makefile index 6061adad6..871524350 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -13,7 +13,7 @@ O_TARGET := fs.o O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \ - inode.o dcache.o attr.o $(BINFMTS) + inode.o dcache.o attr.o bad_inode.o $(BINFMTS) MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = minix ext2 fat msdos vfat proc isofs nfs umsdos \ diff --git a/fs/affs/dir.c b/fs/affs/dir.c index e936e5bb5..cfd05640b 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -82,7 +82,7 @@ affs_readdir(struct file *filp, void *dirent, filldir_t filldir) struct buffer_head *dir_bh; struct buffer_head *fh_bh; struct inode *dir; - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = filp->f_dentry->d_inode; pr_debug("AFFS: readdir(ino=%ld,f_pos=%lu)\n",inode->i_ino,filp->f_pos); diff --git a/fs/bad_inode.c b/fs/bad_inode.c new file mode 100644 index 000000000..562a5d8b7 --- /dev/null +++ b/fs/bad_inode.c @@ -0,0 +1,83 @@ +/* + * linux/fs/bad_inode.c + * + * Copyright (C) 1997, Stephen Tweedie + * + * Provide stub functions for unreadable inodes + */ + +#include <linux/fs.h> +#include <linux/stat.h> +#include <linux/sched.h> + +/* + * The follow_symlink operation must dput() the base. + */ +static struct dentry * bad_follow_link(struct inode * ino, struct dentry *base) +{ + dput(base); + return ERR_PTR(-EIO); +} + +static int return_EIO() +{ + return -EIO; +} + +#define EIO_ERROR ((void *) (return_EIO)) + +static struct file_operations bad_file_ops = +{ + EIO_ERROR, /* lseek */ + EIO_ERROR, /* read */ + EIO_ERROR, /* write */ + EIO_ERROR, /* readdir */ + EIO_ERROR, /* select */ + EIO_ERROR, /* ioctl */ + EIO_ERROR, /* mmap */ + EIO_ERROR, /* open */ + EIO_ERROR, /* release */ + EIO_ERROR, /* fsync */ + EIO_ERROR, /* fasync */ + EIO_ERROR, /* check_media_change */ + EIO_ERROR /* revalidate */ +}; + +struct inode_operations bad_inode_ops = +{ + &bad_file_ops, /* default file operations */ + EIO_ERROR, /* create */ + EIO_ERROR, /* lookup */ + EIO_ERROR, /* link */ + EIO_ERROR, /* unlink */ + EIO_ERROR, /* symlink */ + EIO_ERROR, /* mkdir */ + EIO_ERROR, /* rmdir */ + EIO_ERROR, /* mknod */ + EIO_ERROR, /* rename */ + EIO_ERROR, /* readlink */ + bad_follow_link, /* follow_link */ + EIO_ERROR, /* readpage */ + EIO_ERROR, /* writepage */ + EIO_ERROR, /* bmap */ + EIO_ERROR, /* truncate */ + EIO_ERROR, /* permission */ + EIO_ERROR /* smap */ +}; + + +/* + * When a filesystem is unable to read an inode due to an I/O error in + * its read_inode() function, it can call make_bad_inode() to return a + * set of stubs which will return EIO errors as required. + * + * We only need to do limited initialisation: all other fields are + * preinitialised to zero automatically. + */ +void make_bad_inode(struct inode * inode) +{ + inode->i_mode = S_IFREG; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_op = &bad_inode_ops; +} + diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 0ff5b7ff6..2e9733706 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -62,7 +62,7 @@ while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_cor #define DUMP_SEEK(offset) \ if (file.f_op->llseek) { \ - if (file.f_op->llseek(inode,&file,(offset),0) != (offset)) \ + if (file.f_op->llseek(&file,(offset),0) != (offset)) \ goto close_coredump; \ } else file.f_pos = (offset) @@ -492,7 +492,7 @@ do_load_aout_library(int fd) /* Seek into the file */ if (file->f_op->llseek) { - if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0) + if ((error = file->f_op->llseek(file, 0, 0)) != 0) return -ENOEXEC; } else file->f_pos = 0; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 837ce563b..5187ae1cb 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -852,7 +852,7 @@ do_load_elf_library(int fd){ /* seek to the beginning of the file */ if (file->f_op->llseek) { - if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0) + if ((error = file->f_op->llseek(file, 0, 0)) != 0) return -ENOEXEC; } else file->f_pos = 0; @@ -961,7 +961,7 @@ static int dump_write(struct file *file, const void *addr, int nr) static int dump_seek(struct file *file, off_t off) { if (file->f_op->llseek) { - if (file->f_op->llseek(file->f_dentry->d_inode, file, off, 0) != off) + if (file->f_op->llseek(file, off, 0) != off) return 0; } else file->f_pos = off; diff --git a/fs/block_dev.c b/fs/block_dev.c index f42026ac2..dbb9d14dc 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -287,7 +287,12 @@ long block_read(struct inode * inode, struct file * filp, return read; } -int block_fsync(struct inode *inode, struct file *filp) +/* + * Filp may be NULL when we are called by an msync of a vma + * since the vma has no handle. + */ + +int block_fsync(struct file *filp, struct dentry *dentry) { - return fsync_dev (inode->i_rdev); + return fsync_dev(dentry->d_inode->i_rdev); } diff --git a/fs/buffer.c b/fs/buffer.c index 62c4aa318..6025dcd31 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -107,7 +107,7 @@ union bdflush_param{ int dummy3; /* unused */ } b_un; unsigned int data[N_PARAM]; -} bdf_prm = {{60, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}}; +} bdf_prm = {{40, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}}; /* These are the min and max parameter values that we will allow to be assigned */ int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 100, 100, 1, 1}; @@ -282,9 +282,13 @@ asmlinkage int sys_sync(void) return 0; } -int file_fsync (struct inode *inode, struct file *filp) +/* + * filp may be NULL if called via the msync of a vma. + */ + +int file_fsync(struct file *filp, struct dentry *dentry) { - return fsync_dev(inode->i_dev); + return fsync_dev(dentry->d_inode->i_dev); } asmlinkage int sys_fsync(unsigned int fd) @@ -316,7 +320,10 @@ asmlinkage int sys_fsync(unsigned int fd) if (!file->f_op || !file->f_op->fsync) goto out; - err = file->f_op->fsync(inode,file); + /* We need to protect against concurrent writers.. */ + down(&inode->i_sem); + err = file->f_op->fsync(file, file->f_dentry); + up(&inode->i_sem); out: unlock_kernel(); @@ -353,7 +360,7 @@ asmlinkage int sys_fdatasync(unsigned int fd) goto out; /* this needs further work, at the moment it is identical to fsync() */ - err = file->f_op->fsync(inode,file); + err = file->f_op->fsync(file, file->f_dentry); out: unlock_kernel(); diff --git a/fs/dcache.c b/fs/dcache.c index afc24db1e..600e5b0e3 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -119,7 +119,7 @@ int d_invalidate(struct dentry * dentry) * something (at which point we need to unuse * all dentries). */ -void shrink_dcache(void) +void prune_dcache(int count) { for (;;) { struct dentry *dentry; @@ -143,6 +143,8 @@ void shrink_dcache(void) parent = dentry->d_parent; d_free(dentry); dput(parent); + if (!--count) + break; } } } @@ -177,7 +179,12 @@ struct dentry * d_alloc(struct dentry * parent, const struct qstr *name) dentry->d_count = 1; dentry->d_flags = 0; dentry->d_inode = NULL; - dentry->d_parent = dget(parent); + dentry->d_parent = NULL; + dentry->d_sb = NULL; + if (parent) { + dentry->d_parent = dget(parent); + dentry->d_sb = parent->d_sb; + } dentry->d_mounts = dentry; dentry->d_covers = dentry; INIT_LIST_HEAD(&dentry->d_hash); @@ -211,8 +218,11 @@ struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root) if (root_inode) { res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }); - res->d_parent = res; - d_instantiate(res, root_inode); + if (res) { + res->d_sb = root_inode->i_sb; + res->d_parent = res; + d_instantiate(res, root_inode); + } } return res; } @@ -344,7 +354,7 @@ void d_add(struct dentry * entry, struct inode * inode) x = y; y = __tmp; } while (0) /* - * We cannibalize "newdentry" when moving dentry on top of it, + * We cannibalize "target" when moving dentry on top of it, * because it's going to be thrown away anyway. We could be more * polite about it, though. * @@ -369,7 +369,7 @@ int read_exec(struct dentry *dentry, unsigned long offset, if (!file.f_op->read) goto close_readexec; if (file.f_op->llseek) { - if (file.f_op->llseek(inode,&file,offset,0) != offset) + if (file.f_op->llseek(&file,offset,0) != offset) goto close_readexec; } else file.f_pos = offset; diff --git a/fs/ext2/file.c b/fs/ext2/file.c index d632133f1..b5f55efd6 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -36,7 +36,7 @@ #include <linux/fs.h> #include <linux/ext2_fs.h> -static long long ext2_file_lseek(struct inode *, struct file *, long long, int); +static long long ext2_file_lseek(struct file *, long long, int); static long ext2_file_write (struct inode *, struct file *, const char *, unsigned long); static int ext2_release_file (struct inode *, struct file *); @@ -84,12 +84,13 @@ struct inode_operations ext2_file_inode_operations = { /* * Make sure the offset never goes beyond the 32-bit mark.. */ -static long long ext2_file_lseek(struct inode *inode, +static long long ext2_file_lseek( struct file *file, long long offset, int origin) { long long retval; + struct inode *inode = file->f_dentry->d_inode; switch (origin) { case 2: diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index 9993af1a6..8a9bdf902 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -38,18 +38,12 @@ static int sync_block (struct inode * inode, u32 * block, int wait) { struct buffer_head * bh; - int tmp; if (!*block) return 0; - tmp = *block; bh = get_hash_table (inode->i_dev, *block, blocksize); if (!bh) return 0; - if (*block != tmp) { - brelse (bh); - return 1; - } if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { brelse (bh); return -1; @@ -67,18 +61,12 @@ static int sync_block (struct inode * inode, u32 * block, int wait) static int sync_block_swab32 (struct inode * inode, u32 * block, int wait) { struct buffer_head * bh; - int tmp; if (!le32_to_cpu(*block)) return 0; - tmp = le32_to_cpu(*block); bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize); if (!bh) return 0; - if (le32_to_cpu(*block) != tmp) { - brelse (bh); - return 1; - } if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { brelse (bh); return -1; @@ -109,11 +97,6 @@ static int sync_iblock (struct inode * inode, u32 * iblock, if (rc) return rc; *bh = bread (inode->i_dev, tmp, blocksize); - if (tmp != *iblock) { - brelse (*bh); - *bh = NULL; - return 1; - } if (!*bh) return -1; return 0; @@ -133,11 +116,6 @@ static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, if (rc) return rc; *bh = bread (inode->i_dev, tmp, blocksize); - if (tmp != le32_to_cpu(*iblock)) { - brelse (*bh); - *bh = NULL; - return 1; - } if (!*bh) return -1; return 0; @@ -153,8 +131,6 @@ static int sync_direct (struct inode * inode, int wait) for (i = 0; i < EXT2_NDIR_BLOCKS; i++) { rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait); - if (rc > 0) - break; if (rc) err = rc; } @@ -175,8 +151,6 @@ static int sync_indirect (struct inode * inode, u32 * iblock, int wait) rc = sync_block_swab32 (inode, ((u32 *) ind_bh->b_data) + i, wait); - if (rc > 0) - break; if (rc) err = rc; } @@ -199,8 +173,6 @@ static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, rc = sync_block_swab32 (inode, ((u32 *) ind_bh->b_data) + i, wait); - if (rc > 0) - break; if (rc) err = rc; } @@ -225,8 +197,6 @@ static int sync_dindirect (struct inode * inode, u32 * diblock, int wait) rc = sync_indirect_swab32 (inode, ((u32 *) dind_bh->b_data) + i, wait); - if (rc > 0) - break; if (rc) err = rc; } @@ -249,8 +219,6 @@ static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock rc = sync_indirect_swab32 (inode, ((u32 *) dind_bh->b_data) + i, wait); - if (rc > 0) - break; if (rc) err = rc; } @@ -275,8 +243,6 @@ static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) rc = sync_dindirect_swab32 (inode, ((u32 *) tind_bh->b_data) + i, wait); - if (rc > 0) - break; if (rc) err = rc; } @@ -284,9 +250,15 @@ static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) return err; } -int ext2_sync_file (struct inode * inode, struct file * file) +/* + * File may be NULL when we are called. Perhaps we shouldn't + * even pass file to fsync ? + */ + +int ext2_sync_file(struct file * file, struct dentry *dentry) { int wait, err = 0; + struct inode *inode = dentry->d_inode; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) @@ -312,5 +284,5 @@ int ext2_sync_file (struct inode * inode, struct file * file) } skip: err |= ext2_sync_inode (inode); - return (err < 0) ? -EIO : 0; + return err ? -EIO : 0; } diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c index a858cd2cc..fd66e51d1 100644 --- a/fs/fat/mmap.c +++ b/fs/fat/mmap.c @@ -91,8 +91,10 @@ struct vm_operations_struct fat_file_mmap = { * 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 inode * inode, struct file * file, struct vm_area_struct * vma) +int fat_mmap(struct file * file, struct vm_area_struct * vma) { + struct inode *inode = file->f_dentry->d_inode; + 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)) diff --git a/fs/fcntl.c b/fs/fcntl.c index 57eef2530..ce00e439f 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -83,7 +83,7 @@ static int setfl(struct file * filp, unsigned long arg) /* Did FASYNC state change? */ if ((arg ^ filp->f_flags) & FASYNC) { if (filp->f_op->fasync) - filp->f_op->fasync(inode, filp, (arg & FASYNC) != 0); + filp->f_op->fasync(filp, (arg & FASYNC) != 0); } /* required for strict SunOS emulation */ @@ -98,8 +98,6 @@ static int setfl(struct file * filp, unsigned long arg) asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file * filp; - struct task_struct *p; - int task_found = 0; long err = -EBADF; lock_kernel(); @@ -142,57 +140,13 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) * current syscall conventions, the only way * to fix this will be in libc. */ - err = filp->f_owner; + err = filp->f_owner.pid; break; case F_SETOWN: - /* - * Add the security checks - AC. Without - * this there is a massive Linux security - * hole here - consider what happens if - * you do something like - * - * fcntl(0,F_SETOWN,some_root_process); - * getchar(); - * - * and input a line! - * - * BTW: Don't try this for fun. Several Unix - * systems I tried this on fall for the - * trick! - * - * I had to fix this botch job as Linux - * kill_fasync asserts priv making it a - * free all user process killer! - * - * Changed to make the security checks more - * liberal. -- TYT - */ - if (current->pgrp == -arg || current->pid == arg) - goto fasync_ok; - - read_lock(&tasklist_lock); - for_each_task(p) { - if ((p->pid == arg) || (p->pid == -arg) || - (p->pgrp == -arg)) { - task_found++; - err = -EPERM; - if ((p->session != current->session) && - (p->uid != current->uid) && - (p->euid != current->euid) && - !suser()) { - read_unlock(&tasklist_lock); - goto out; - } - break; - } - } - read_unlock(&tasklist_lock); - err = -EINVAL; - if ((task_found == 0) && !suser()) - break; - fasync_ok: err = 0; - filp->f_owner = arg; + filp->f_owner.pid = arg; + filp->f_owner.uid = current->uid; + filp->f_owner.euid = current->euid; if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, F_SETOWN, arg); break; @@ -209,18 +163,40 @@ out: return err; } +static void send_sigio(int pid, uid_t uid, uid_t euid) +{ + struct task_struct * p; + + read_lock(&tasklist_lock); + for_each_task(p) { + int match = p->pid; + if (pid < 0) + match = -p->pgrp; + if (pid != match) + continue; + if (!euid && + (euid ^ p->suid) && (euid ^ p->uid) && + (uid ^ p->suid) && (uid ^ p->uid)) + continue; + p->signal |= 1 << (SIGIO-1); + if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked)) + wake_up_process(p); + } + read_unlock(&tasklist_lock); +} + void kill_fasync(struct fasync_struct *fa, int sig) { while (fa) { + struct fown_struct * fown; if (fa->magic != FASYNC_MAGIC) { printk("kill_fasync: bad magic number in " "fasync_struct!\n"); return; } - if (fa->fa_file->f_owner > 0) - kill_proc(fa->fa_file->f_owner, sig, 1); - else - kill_pg(-fa->fa_file->f_owner, sig, 1); + fown = &fa->fa_file->f_owner; + if (fown->pid) + send_sigio(fown->pid, fown->uid, fown->euid); fa = fa->fa_next; } } diff --git a/fs/inode.c b/fs/inode.c index 7c9881222..6a8423c39 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -125,33 +125,6 @@ static inline void init_once(struct inode * inode) sema_init(&inode->i_sem, 1); } - -/* - * Look out! This returns with the inode lock held if - * it got an inode.. - */ -static struct inode * grow_inodes(void) -{ - struct inode * inode = (struct inode *)__get_free_page(GFP_KERNEL); - - if (inode) { - int size; - struct inode * tmp; - - spin_lock(&inode_lock); - size = PAGE_SIZE - 2*sizeof(struct inode); - tmp = inode; - do { - tmp++; - init_once(tmp); - list_add(&tmp->i_list, &inode_unused); - size -= sizeof(struct inode); - } while (size >= 0); - init_once(inode); - } - return inode; -} - static inline void write_inode(struct inode *inode) { if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode) @@ -238,7 +211,8 @@ void write_inode_now(struct inode *inode) */ void clear_inode(struct inode *inode) { - truncate_inode_pages(inode, 0); + if (inode->i_nrpages) + truncate_inode_pages(inode, 0); wait_on_inode(inode); if (IS_WRITABLE(inode) && inode->i_sb && inode->i_sb->dq_op) inode->i_sb->dq_op->drop(inode); @@ -253,6 +227,7 @@ void clear_inode(struct inode *inode) static void dispose_list(struct list_head * head) { struct list_head *next; + int count = 0; next = head->next; for (;;) { @@ -263,12 +238,14 @@ static void dispose_list(struct list_head * head) if (tmp == head) break; inode = list_entry(tmp, struct inode, i_list); - truncate_inode_pages(inode, 0); + clear_inode(inode); + count++; } /* Add them all to the unused list in one fell swoop */ spin_lock(&inode_lock); list_splice(head, &inode_unused); + inodes_stat.nr_free_inodes += count; spin_unlock(&inode_lock); } @@ -326,25 +303,25 @@ int invalidate_inodes(struct super_block * sb) } /* - * This is called with the inode lock held. It just looks at the last - * inode on the in-use list, and if the inode is trivially freeable - * we just move it to the unused list. - * - * Otherwise we just move the inode to be the first inode and expect to - * get back to the problem later.. + * This is called with the inode lock held. It searches + * the in-use for the specified number of freeable inodes. + * Freeable inodes are moved to a temporary list and then + * placed on the unused list by dispose_list. + * + * N.B. The spinlock is released to call dispose_list. */ #define CAN_UNUSE(inode) \ (((inode)->i_count == 0) && \ - ((inode)->i_nrpages == 0) && \ (!(inode)->i_state)) -static void try_to_free_inodes(void) +static void try_to_free_inodes(int goal) { struct list_head * tmp; struct list_head *head = &inode_in_use; + LIST_HEAD(freeable); + int found = 0, search = goal << 1; - tmp = head->prev; - if (tmp != head) { + while ((tmp = head->prev) != head && search--) { struct inode * inode; list_del(tmp); @@ -352,13 +329,81 @@ static void try_to_free_inodes(void) if (CAN_UNUSE(inode)) { list_del(&inode->i_hash); INIT_LIST_HEAD(&inode->i_hash); - head = &inode_unused; + list_add(tmp, &freeable); + if (++found < goal) + continue; + break; } list_add(tmp, head); } + /* + * If we didn't free any inodes, do a limited + * pruning of the dcache to help the next time. + */ + spin_unlock(&inode_lock); + if (found) + dispose_list(&freeable); + else + prune_dcache(goal); + spin_lock(&inode_lock); +} + +/* + * This is called with the spinlock held, but releases + * the lock when freeing or allocating inodes. + * Look out! This returns with the inode lock held if + * it got an inode.. + */ +static struct inode * grow_inodes(void) +{ + struct inode * inode; + + /* + * Check whether to shrink the dcache ... if we've + * allocated more than half of the nominal maximum, + * try shrinking before allocating more. + */ + if (inodes_stat.nr_inodes >= (max_inodes >> 1)) { + struct list_head * tmp; + + spin_unlock(&inode_lock); + prune_dcache(128); + spin_lock(&inode_lock); + try_to_free_inodes(128); + tmp = inode_unused.next; + if (tmp != &inode_unused) { + inodes_stat.nr_free_inodes--; + list_del(tmp); + inode = list_entry(tmp, struct inode, i_list); + return inode; + } + } + + spin_unlock(&inode_lock); + inode = (struct inode *)__get_free_page(GFP_KERNEL); + if (inode) { + int size; + struct inode * tmp; + + spin_lock(&inode_lock); + size = PAGE_SIZE - 2*sizeof(struct inode); + tmp = inode; + do { + tmp++; + init_once(tmp); + list_add(&tmp->i_list, &inode_unused); + inodes_stat.nr_free_inodes++; + size -= sizeof(struct inode); + } while (size >= 0); + init_once(inode); + inodes_stat.nr_inodes += PAGE_SIZE / sizeof(struct inode); + } + return inode; } - +/* + * Called with the inode lock held. + */ static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head) { struct list_head *tmp; @@ -416,17 +461,22 @@ struct inode * get_empty_inode(void) struct list_head * tmp; spin_lock(&inode_lock); - try_to_free_inodes(); + /* + * Check whether to restock the unused list. + */ + if (inodes_stat.nr_free_inodes < 16) + try_to_free_inodes(8); tmp = inode_unused.next; if (tmp != &inode_unused) { list_del(tmp); + inodes_stat.nr_free_inodes--; inode = list_entry(tmp, struct inode, i_list); add_new_inode: + list_add(&inode->i_list, &inode_in_use); inode->i_sb = NULL; inode->i_dev = 0; inode->i_ino = ++last_ino; inode->i_count = 1; - list_add(&inode->i_list, &inode_in_use); inode->i_state = 0; spin_unlock(&inode_lock); clean_inode(inode); @@ -437,7 +487,6 @@ add_new_inode: * Warning: if this succeeded, we will now * return with the inode lock. */ - spin_unlock(&inode_lock); inode = grow_inodes(); if (inode) goto add_new_inode; @@ -455,6 +504,7 @@ static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, s if (tmp != &inode_unused) { list_del(tmp); + inodes_stat.nr_free_inodes--; inode = list_entry(tmp, struct inode, i_list); add_new_inode: list_add(&inode->i_list, &inode_in_use); @@ -477,20 +527,18 @@ add_new_inode: * state of the inode when it is locked, as we * just created it (so there can be no old holders * that haven't tested I_LOCK). - * - * Verify this some day! */ inode->i_state &= ~I_LOCK; + wake_up(&inode->i_wait); return inode; } /* - * Uhhuh.. We need to expand. Unlock for the allocation, - * but note that "grow_inodes()" will return with the - * lock held again if the allocation succeeded. + * Uhhuh.. We need to expand. Note that "grow_inodes()" will + * release the spinlock, but will return with the lock held + * again if the allocation succeeded. */ - spin_unlock(&inode_lock); inode = grow_inodes(); if (inode) { /* We released the lock, so.. */ @@ -498,6 +546,7 @@ add_new_inode: if (!old) goto add_new_inode; list_add(&inode->i_list, &inode_unused); + inodes_stat.nr_free_inodes++; spin_unlock(&inode_lock); wait_on_inode(old); return old; @@ -518,14 +567,25 @@ struct inode *iget(struct super_block *sb, unsigned long ino) struct inode * inode; spin_lock(&inode_lock); + if (!inodes_stat.nr_free_inodes) + goto restock; +search: inode = find_inode(sb, ino, head); if (!inode) { - try_to_free_inodes(); return get_new_inode(sb, ino, head); } spin_unlock(&inode_lock); wait_on_inode(inode); return inode; + + /* + * We restock the freelist before calling find, + * in order to avoid repeating the search. + * (The unused list usually won't be empty.) + */ +restock: + try_to_free_inodes(8); + goto search; } void insert_inode_hash(struct inode *inode) @@ -560,9 +620,18 @@ void iput(struct inode *inode) } if (list_empty(&inode->i_hash)) { list_del(&inode->i_list); + INIT_LIST_HEAD(&inode->i_list); + spin_unlock(&inode_lock); + clear_inode(inode); + spin_lock(&inode_lock); list_add(&inode->i_list, &inode_unused); + inodes_stat.nr_free_inodes++; } } + if (inode->i_count > (1<<15)) { + printk("iput: device %s inode %ld count wrapped\n", + kdevname(inode->i_dev), inode->i_ino); + } spin_unlock(&inode_lock); } } diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 98814d220..ad70f7b55 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -71,7 +71,7 @@ nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp) * nlmclnt_lock for an explanation. */ current->timeout = jiffies + 30 * HZ; - interruptible_sleep_on(&block.b_wait); + sleep_on(&block.b_wait); for (head = &nlm_blocked; *head; head = &(*head)->b_next) { if (*head == &block) { diff --git a/fs/locks.c b/fs/locks.c index 909c2eacb..738fa0bca 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -132,7 +132,9 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller, static int posix_locks_deadlock(struct file_lock *caller, struct file_lock *blocker); -static struct file_lock *locks_alloc_lock(struct file_lock *fl); +static struct file_lock *locks_empty_lock(void); +static struct file_lock *locks_init_lock(struct file_lock *, + struct file_lock *); static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl); static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait); static char *lock_get_status(struct file_lock *fl, int id, char *pfx); @@ -143,6 +145,15 @@ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait); struct file_lock *file_lock_table = NULL; +/* Allocate a new lock, and initialize its fields from fl. + * The lock is not inserted into any lists until locks_insert_lock() or + * locks_insert_block() are called. + */ +static inline struct file_lock *locks_alloc_lock(struct file_lock *fl) +{ + return locks_init_lock(locks_empty_lock(), fl); +} + /* Free lock not inserted in any queue. */ static inline void locks_free_lock(struct file_lock *fl) @@ -250,6 +261,7 @@ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait) struct file_lock *waiter; while ((waiter = blocker->fl_nextblock) != NULL) { + /* N.B. Is it possible for the notify function to block?? */ if (waiter->fl_notify) waiter->fl_notify(waiter); wake_up(&waiter->fl_wait); @@ -322,7 +334,7 @@ int fcntl_getlk(unsigned int fd, struct flock *l) return -EINVAL; if (filp->f_op->lock) { - error = filp->f_op->lock(inode, filp, F_GETLK, &file_lock); + error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) return error; fl = &file_lock; @@ -330,6 +342,7 @@ int fcntl_getlk(unsigned int fd, struct flock *l) fl = posix_test_lock(filp, &file_lock); } + flock.l_type = F_UNLCK; if (fl != NULL) { flock.l_pid = fl->fl_pid; flock.l_start = fl->fl_start; @@ -337,10 +350,7 @@ int fcntl_getlk(unsigned int fd, struct flock *l) fl->fl_end - fl->fl_start + 1; flock.l_whence = 0; flock.l_type = fl->fl_type; - return (copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0); - } else { - flock.l_type = F_UNLCK; - } + } return (copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0); } @@ -368,7 +378,12 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) if (!(inode = dentry->d_inode)) return -EINVAL; - + /* + * This might block, so we do it before checking the inode. + */ + if (copy_from_user(&flock, l, sizeof(flock))) + return (-EFAULT); + /* Don't allow mandatory locks on files that may be memory mapped * and shared. */ @@ -382,8 +397,6 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) } while ((vma = vma->vm_next_share) != NULL); } - if (copy_from_user(&flock, l, sizeof(flock))) - return (-EFAULT); if (!posix_make_lock(filp, &file_lock, &flock)) return (-EINVAL); @@ -420,7 +433,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) } if (filp->f_op->lock != NULL) { - error = filp->f_op->lock(inode, filp, cmd, &file_lock); + error = filp->f_op->lock(filp, cmd, &file_lock); if (error < 0) return (error); } @@ -441,8 +454,8 @@ void locks_remove_locks(struct task_struct *task, struct file *filp) * close on that file. */ inode = filp->f_dentry->d_inode; +repeat: before = &inode->i_flock; - while ((fl = *before) != NULL) { if (((fl->fl_flags & FL_POSIX) && (fl->fl_owner == task)) || ((fl->fl_flags & FL_FLOCK) && (fl->fl_file == filp) && @@ -451,13 +464,13 @@ void locks_remove_locks(struct task_struct *task, struct file *filp) locks_delete_lock(before, 0); if (filp->f_op->lock) { file_lock.fl_type = F_UNLCK; - filp->f_op->lock(inode, filp, F_SETLK, &file_lock); + filp->f_op->lock(filp, F_SETLK, &file_lock); /* List may have changed: */ - before = &inode->i_flock; + goto repeat; } - } else { - before = &fl->fl_next; + continue; } + before = &fl->fl_next; } return; @@ -762,16 +775,30 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller, unsigned int wait) { struct file_lock *fl; - struct file_lock *new_fl; + struct file_lock *new_fl = NULL; struct file_lock **before; struct inode * inode = filp->f_dentry->d_inode; - int change = 0; + int error, change; + int unlock = (caller->fl_type == F_UNLCK); + + /* + * If we need a new lock, get it in advance to avoid races. + */ + if (!unlock) { + error = -ENOLCK; + new_fl = locks_alloc_lock(caller); + if (!new_fl) + goto out; + } + error = 0; +search: + change = 0; before = &inode->i_flock; while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) { if (caller->fl_file == fl->fl_file) { if (caller->fl_type == fl->fl_type) - return (0); + goto out; change = 1; break; } @@ -780,44 +807,43 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller, /* change means that we are changing the type of an existing lock, or * or else unlocking it. */ - if (change) - locks_delete_lock(before, caller->fl_type != F_UNLCK); - if (caller->fl_type == F_UNLCK) - return (0); - if ((new_fl = locks_alloc_lock(caller)) == NULL) - return (-ENOLCK); + if (change) { + /* N.B. What if the wait argument is false? */ + locks_delete_lock(before, !unlock); + /* + * If we waited, another lock may have been added ... + */ + if (!unlock) + goto search; + } + if (unlock) + goto out; + repeat: + /* Check signals each time we start */ + error = -ERESTARTSYS; + if (current->signal & ~current->blocked) + goto out; for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK); fl = fl->fl_next) { if (!flock_locks_conflict(new_fl, fl)) continue; - if (!wait) { - locks_free_lock(new_fl); - return (-EAGAIN); - } - if (current->signal & ~current->blocked) { - /* Note: new_fl is not in any queue at this - * point, so we must use locks_free_lock() - * instead of locks_delete_lock() - * Dmitry Gorodchanin 09/02/96. - */ - locks_free_lock(new_fl); - return (-ERESTARTSYS); - } + error = -EAGAIN; + if (!wait) + goto out; locks_insert_block(fl, new_fl); interruptible_sleep_on(&new_fl->fl_wait); locks_delete_block(fl, new_fl); - if (current->signal & ~current->blocked) { - /* Awakened by a signal. Free the new - * lock and return an error. - */ - locks_free_lock(new_fl); - return (-ERESTARTSYS); - } goto repeat; } locks_insert_lock(&inode->i_flock, new_fl); - return (0); + new_fl = NULL; + error = 0; + +out: + if (new_fl) + locks_free_lock(new_fl); + return error; } /* Add a POSIX style lock to a file. @@ -836,36 +862,51 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, unsigned int wait) { struct file_lock *fl; - struct file_lock *new_fl; + struct file_lock *new_fl, *new_fl2; struct file_lock *left = NULL; struct file_lock *right = NULL; struct file_lock **before; struct inode * inode = filp->f_dentry->d_inode; - int added = 0; + int error, added = 0; + + /* + * We may need two file_lock structures for this operation, + * so we get them in advance to avoid races. + */ + new_fl = locks_empty_lock(); + new_fl2 = locks_empty_lock(); + error = -ENOLCK; /* "no luck" */ + if (!(new_fl && new_fl2)) + goto out; if (caller->fl_type != F_UNLCK) { repeat: + error = -ERESTARTSYS; + if (current->signal & ~current->blocked) + goto out; for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) continue; if (!posix_locks_conflict(caller, fl)) continue; + error = -EAGAIN; if (!wait) - return (-EAGAIN); - if (current->signal & ~current->blocked) - return (-ERESTARTSYS); + goto out; + error = -EDEADLK; if (posix_locks_deadlock(caller, fl)) - return (-EDEADLK); + goto out; locks_insert_block(fl, caller); interruptible_sleep_on(&caller->fl_wait); locks_delete_block(fl, caller); - if (current->signal & ~current->blocked) - return (-ERESTARTSYS); goto repeat; } } - /* Find the first old lock with the same owner as the new lock. + /* + * We've allocated the new locks in advance, so there are no + * errors possible (and no blocking operations) from here on. + * + * Find the first old lock with the same owner as the new lock. */ before = &inode->i_flock; @@ -958,25 +999,23 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, before = &fl->fl_next; } + error = 0; if (!added) { if (caller->fl_type == F_UNLCK) - return (0); - if ((new_fl = locks_alloc_lock(caller)) == NULL) - return (-ENOLCK); + goto out; + locks_init_lock(new_fl, caller); locks_insert_lock(before, new_fl); + new_fl = NULL; } if (right) { if (left == right) { - /* The new lock breaks the old one in two pieces, so we - * have to allocate one more lock (in this case, even - * F_UNLCK may fail!). + /* The new lock breaks the old one in two pieces, + * so we have to use the second new lock (in this + * case, even F_UNLCK may fail!). */ - if ((left = locks_alloc_lock(right)) == NULL) { - if (!added) - locks_delete_lock(before, 0); - return (-ENOLCK); - } + left = locks_init_lock(new_fl2, right); locks_insert_lock(before, left); + new_fl2 = NULL; } right->fl_start = caller->fl_end + 1; locks_wake_up_blocks(right, 0); @@ -985,35 +1024,48 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, left->fl_end = caller->fl_start - 1; locks_wake_up_blocks(left, 0); } - return (0); +out: + /* + * Free any unused locks. (They haven't + * ever been used, so we use kfree().) + */ + if (new_fl) + kfree(new_fl); + if (new_fl2) + kfree(new_fl2); + return error; } -/* Allocate new lock. - * Initialize its fields from fl. The lock is not inserted into any - * lists until locks_insert_lock() or locks_insert_block() are called. +/* + * Allocate an empty lock structure. We can use GFP_KERNEL now that + * all allocations are done in advance. */ -static struct file_lock *locks_alloc_lock(struct file_lock *fl) +static struct file_lock *locks_empty_lock(void) { - struct file_lock *tmp; - /* Okay, let's make a new file_lock structure... */ - if ((tmp = (struct file_lock *)kmalloc(sizeof(struct file_lock), - GFP_ATOMIC)) == NULL) - return (tmp); - - memset(tmp, 0, sizeof(*tmp)); - - tmp->fl_flags = fl->fl_flags; - tmp->fl_owner = fl->fl_owner; - tmp->fl_pid = fl->fl_pid; - tmp->fl_file = fl->fl_file; - tmp->fl_type = fl->fl_type; - tmp->fl_start = fl->fl_start; - tmp->fl_end = fl->fl_end; - tmp->fl_notify = fl->fl_notify; - tmp->fl_u = fl->fl_u; - - return (tmp); + return ((struct file_lock *) kmalloc(sizeof(struct file_lock), + GFP_KERNEL)); +} + +/* + * Initialize a new lock from an existing file_lock structure. + */ +static struct file_lock *locks_init_lock(struct file_lock *new, + struct file_lock *fl) +{ + if (new) { + memset(new, 0, sizeof(*new)); + new->fl_owner = fl->fl_owner; + new->fl_pid = fl->fl_pid; + new->fl_file = fl->fl_file; + new->fl_flags = fl->fl_flags; + new->fl_type = fl->fl_type; + new->fl_start = fl->fl_start; + new->fl_end = fl->fl_end; + new->fl_notify = fl->fl_notify; + new->fl_u = fl->fl_u; + } + return new; } /* Insert file lock fl into an inode's lock list at the position indicated diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c index 9510d6364..44606d260 100644 --- a/fs/minix/fsync.c +++ b/fs/minix/fsync.c @@ -328,10 +328,14 @@ int V2_minix_sync_file(struct inode * inode, struct file * file) } /* - * The function which is called for file synchronization. + * The function which is called for file synchronization. File may be + * NULL */ -int minix_sync_file(struct inode * inode, struct file * file) + +int minix_sync_file(struct file * file, struct dentry *dentry) { + struct inode *inode = dentry->d_inode; + if (INODE_VERSION(inode) == MINIX_V1) return V1_minix_sync_file(inode, file); else diff --git a/fs/namei.c b/fs/namei.c index 5aa6a5422..54714754d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -593,8 +593,12 @@ struct dentry * open_namei(const char * pathname, int flag, int mode) * An append-only file must be opened in append mode for writing. */ error = -EPERM; - if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) - goto exit; + if (IS_APPEND(inode)) { + if ((flag & FMODE_WRITE) && !(flag & O_APPEND)) + goto exit; + if (flag & O_TRUNC) + goto exit; + } if (flag & O_TRUNC) { error = get_write_access(inode); diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 9a302a213..9a2067848 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -24,7 +24,7 @@ static inline int min(int a, int b) return a < b ? a : b; } -static int ncp_fsync(struct inode *inode, struct file *file) +static int ncp_fsync(struct file *file, struct dentry *dentry) { return 0; } diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 269d3ef07..5dfdeb27f 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -116,8 +116,10 @@ struct vm_operations_struct ncp_file_mmap = /* This is used for a general mmap of a ncp file */ -int ncp_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma) +int ncp_mmap(struct file *file, struct vm_area_struct *vma) { + struct inode *inode = file->f_dentry->d_inode; + DPRINTK("ncp_mmap: called\n"); if (!ncp_conn_valid(NCP_SERVER(inode))) { diff --git a/fs/nfs/file.c b/fs/nfs/file.c index eb4735a6d..4587950ef 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -32,13 +32,12 @@ #define NFSDBG_FACILITY NFSDBG_FILE -static int nfs_file_mmap(struct inode *, struct file *, - struct vm_area_struct *); +static int nfs_file_mmap(struct file *, struct vm_area_struct *); static long nfs_file_read(struct inode *, struct file *, char *, unsigned long); static long nfs_file_write(struct inode *, struct file *, const char *, unsigned long); static int nfs_file_close(struct inode *, struct file *); -static int nfs_fsync(struct inode *, struct file *); +static int nfs_fsync(struct file *, struct dentry *dentry); static struct file_operations nfs_file_operations = { NULL, /* lseek - default */ @@ -114,20 +113,21 @@ nfs_file_read(struct inode * inode, struct file * file, } static int -nfs_file_mmap(struct inode * inode, struct file * file, - struct vm_area_struct * vma) +nfs_file_mmap(struct file * file, struct vm_area_struct * vma) { int status; + struct inode *inode = file->f_dentry->d_inode; dfprintk(VFS, "nfs: mmap(%x/%ld)\n", inode->i_dev, inode->i_ino); if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0) return status; - return generic_file_mmap(inode, file, vma); + return generic_file_mmap(file, vma); } -static int nfs_fsync(struct inode *inode, struct file *file) +static int nfs_fsync(struct file *file, struct dentry *dentry) { + struct inode *inode = dentry->d_inode; dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino); return nfs_flush_dirty_pages(inode, 0, 0); @@ -175,9 +175,10 @@ nfs_file_write(struct inode *inode, struct file *file, * Lock a (portion of) a file */ int -nfs_lock(struct inode *inode, struct file *filp, int cmd, struct file_lock *fl) +nfs_lock(struct file *filp, int cmd, struct file_lock *fl) { int status; + struct inode * inode; dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n", filp->f_dentry->d_inode->i_dev, filp->f_dentry->d_inode->i_ino, diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 449fc0d4e..ec5a1f7be 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -56,6 +56,16 @@ #include <linux/nfs_fs.h> #include <asm/uaccess.h> +/* + * NOTE! We must NOT default to soft-mounting: that breaks too many + * programs that depend on POSIX behaviour of uninterruptible reads + * and writes. + * + * Until we have a per-mount soft/hard mount policy that we can honour + * we must default to hard mounting! + */ +#define IS_SOFT 0 + #define NFSDBG_FACILITY NFSDBG_PAGECACHE static void nfs_wback_lock(struct rpc_task *task); @@ -397,19 +407,24 @@ wait_on_write_request(struct nfs_wreq *req) { struct wait_queue wait = { current, NULL }; struct page *page = req->wb_page; + int retval; add_wait_queue(&page->wait, &wait); atomic_inc(&page->count); -repeat: - current->state = TASK_INTERRUPTIBLE; - if (PageLocked(page)) { + for (;;) { + current->state = IS_SOFT ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE; + retval = 0; + if (!PageLocked(page)) + break; + retval = -ERESTARTSYS; + if (IS_SOFT && signalled()) + break; schedule(); - goto repeat; } remove_wait_queue(&page->wait, &wait); current->state = TASK_RUNNING; atomic_dec(&page->count); - return signalled()? -ERESTARTSYS : 0; + return retval; } /* @@ -613,10 +628,13 @@ nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len) inode->i_dev, inode->i_ino, current->pid, offset, len); - if (signalled()) + if (IS_SOFT && signalled()) nfs_cancel_dirty(inode, current->pid); - while (!signalled()) { + for (;;) { + if (IS_SOFT && signalled()) + return -ERESTARTSYS; + /* Flush all pending writes for this pid and file region */ last = nfs_flush_pages(inode, current->pid, offset, len, 0); if (last == NULL) @@ -624,7 +642,7 @@ nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len) wait_on_write_request(last); } - return signalled()? -ERESTARTSYS : 0; + return 0; } /* diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index dbbfbc78f..df6eb0e2c 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -294,7 +294,7 @@ nfsd_close(struct file *filp) void nfsd_sync(struct inode *inode, struct file *filp) { - filp->f_op->fsync(inode, filp); + filp->f_op->fsync(filp, filp->f_dentry); } /* @@ -928,7 +928,8 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, file.f_inode->i_dev, file.f_inode->i_ino, (int) file.f_pos, (int) oldlen, (int) cd.buflen); */ - err = file.f_op->readdir(&file, &cd, (filldir_t) func); + err = file.f_op->readdir(&file, + &cd, (filldir_t) func); if (err < 0) { nfsd_close(&file); @@ -717,7 +717,7 @@ int __fput(struct file *filp) struct inode * inode = dentry->d_inode; if (filp->f_op && filp->f_op->release) - error = filp->f_op->release(inode,filp); + error = filp->f_op->release(inode, filp); filp->f_dentry = NULL; if (filp->f_mode & FMODE_WRITE) put_write_access(inode); @@ -133,8 +133,7 @@ static long pipe_write(struct inode * inode, struct file * filp, return written; } -static long long pipe_lseek(struct inode * inode, struct file * file, - long long offset, int orig) +static long long pipe_lseek(struct file * file, long long offset, int orig) { return -ESPIPE; } diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 358060020..83139f492 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -20,8 +20,7 @@ static long proc_file_read(struct inode * inode, struct file * file, char * buf, unsigned long nbytes); static long proc_file_write(struct inode * inode, struct file * file, const char * buffer, unsigned long count); -static long long proc_file_lseek(struct inode * inode, struct file * file, - long long offset, int orig); +static long long proc_file_lseek(struct file * file, long long offset, int orig); int proc_match(int len, const char *name,struct proc_dir_entry * de) { @@ -190,8 +189,7 @@ proc_file_write(struct inode * inode, struct file * file, } -static long long proc_file_lseek(struct inode * inode, struct file * file, - long long offset, int orig) +static long long proc_file_lseek(struct file * file, long long offset, int orig) { switch (orig) { case 0: diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 97acb5ee8..126683ea4 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -187,8 +187,7 @@ static long mem_write(struct inode * inode, struct file * file, #endif -static long long mem_lseek(struct inode * inode, struct file * file, - long long offset, int orig) +static long long mem_lseek(struct file * file, long long offset, int orig) { switch (orig) { case 0: @@ -205,8 +204,7 @@ static long long mem_lseek(struct inode * inode, struct file * file, /* * This isn't really reliable by any means.. */ -int mem_mmap(struct inode * inode, struct file * file, - struct vm_area_struct * vma) +int mem_mmap(struct file * file, struct vm_area_struct * vma) { struct task_struct *tsk; pgd_t *src_dir, *dest_dir; @@ -214,7 +212,8 @@ int mem_mmap(struct inode * inode, struct file * file, pte_t *src_table, *dest_table; unsigned long stmp, dtmp; struct vm_area_struct *src_vma = NULL; - + struct inode *inode = file->f_dentry->d_inode; + /* Get the source's task information */ tsk = get_task(inode->i_ino >> 16); diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c index b1e77398c..831a2623d 100644 --- a/fs/proc/scsi.c +++ b/fs/proc/scsi.c @@ -33,7 +33,7 @@ static long proc_readscsi(struct inode * inode, struct file * file, char * buf, unsigned long count); static long proc_writescsi(struct inode * inode, struct file * file, const char * buf, unsigned long count); -static long long proc_scsilseek(struct inode *, struct file *, long long, int); +static long long proc_scsilseek(struct file *, long long, int); extern void build_proc_dir_hba_entries(uint); @@ -177,8 +177,7 @@ static long proc_writescsi(struct inode * inode, struct file * file, } -static long long proc_scsilseek(struct inode * inode, struct file * file, - long long offset, int orig) +static long long proc_scsilseek(struct file * file, long long offset, int orig) { switch (orig) { case 0: diff --git a/fs/read_write.c b/fs/read_write.c index 6d31c7af0..df3d34d3e 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -19,16 +19,13 @@ #include <asm/uaccess.h> -static long long default_llseek(struct inode *inode, - struct file *file, - long long offset, - int origin) +static long long default_llseek(struct file *file, long long offset, int origin) { long long retval; switch (origin) { case 2: - offset += inode->i_size; + offset += file->f_dentry->d_inode->i_size; break; case 1: offset += file->f_pos; @@ -45,15 +42,14 @@ static long long default_llseek(struct inode *inode, return retval; } -static inline long long llseek(struct inode * inode, struct file *file, - long long offset, unsigned int origin) +static inline long long llseek(struct file *file, long long offset, unsigned int origin) { - long long (*fn)(struct inode *, struct file *, long long, int); + long long (*fn)(struct file *, long long, int); fn = default_llseek; if (file->f_op && file->f_op->llseek) fn = file->f_op->llseek; - return fn(inode, file, offset, origin); + return fn(file, offset, origin); } asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin) @@ -73,7 +69,7 @@ asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin) retval = -EINVAL; if (origin > 2) goto bad; - retval = llseek(inode, file, offset, origin); + retval = llseek(file, offset, origin); bad: unlock_kernel(); return retval; @@ -100,8 +96,7 @@ asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, if (origin > 2) goto bad; - offset = llseek(inode, file, - (((unsigned long long) offset_high << 32) | offset_low), + offset = llseek(file, (((unsigned long long) offset_high << 32) | offset_low), origin); retval = offset; diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 2b0c4d1e2..3196e9009 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -19,6 +19,12 @@ #include <asm/uaccess.h> #include <asm/semaphore.h> +#define SMBFS_PARANOIA 1 +/* #define SMBFS_DEBUG_VERBOSE 1 */ +/* #define pr_debug printk */ + +#define this_dir_cached(dir) ((dir->i_sb == c_sb) && (dir->i_ino == c_ino)) + static long smb_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count); @@ -69,6 +75,15 @@ struct inode_operations smb_dir_inode_operations = NULL /* smap */ }; +static void smb_put_dentry(struct dentry *); +static struct dentry_operations smbfs_dentry_operations = +{ + NULL, /* revalidate */ + NULL, /* d_hash */ + NULL, /* d_compare */ + smb_put_dentry /* d_delete */ +}; + static long smb_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count) @@ -76,166 +91,240 @@ smb_dir_read(struct inode *inode, struct file *filp, char *buf, return -EISDIR; } +/* + * This is the callback from dput(). We close the file so that + * cached dentries don't keep the file open. + */ +void +smb_put_dentry(struct dentry *dentry) +{ + struct inode *ino = dentry->d_inode; + if (ino) + smb_close(ino); +} + +/* Static variables for the dir cache */ +static struct smb_dirent *c_entry = NULL; +static struct super_block * c_sb = NULL; static unsigned long c_ino = 0; -static kdev_t c_dev; -static int c_size; static int c_seen_eof; +static int c_size; static int c_last_returned_index; -static struct smb_dirent *c_entry = NULL; static struct smb_dirent * smb_search_in_cache(struct inode *dir, unsigned long f_pos) { int i; - if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino)) - { - return NULL; - } - for (i = 0; i < c_size; i++) - { - if (f_pos == c_entry[i].f_pos) + if (this_dir_cached(dir)) + for (i = 0; i < c_size; i++) { - c_last_returned_index = i; - return &(c_entry[i]); + if (c_entry[i].f_pos < f_pos) + continue; + if (c_entry[i].f_pos == f_pos) + { + c_last_returned_index = i; + return &(c_entry[i]); + } + break; } - } return NULL; } +/* + * Compute the hash for a qstr ... move to include/linux/dcache.h? + */ +static unsigned int hash_it(const char * name, unsigned int len) +{ + unsigned long hash; + hash = init_name_hash(); + while (len--) + hash = partial_name_hash(*name++, hash); + return end_name_hash(hash); +} + +static struct semaphore refill_cache_sem = MUTEX; +/* + * Called with the refill semaphore held. + */ static int smb_refill_dir_cache(struct dentry *dentry, unsigned long f_pos) { - int result; struct inode *dir = dentry->d_inode; - static struct semaphore sem = MUTEX; - int i; - ino_t ino; + ino_t ino_start; + int i, result; - do + result = smb_proc_readdir(dentry, f_pos, + SMB_READDIR_CACHE_SIZE, c_entry); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_refill_dir_cache: dir=%s/%s, pos=%lu, result=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, f_pos, result); +#endif + + if (result <= 0) { - down(&sem); - result = smb_proc_readdir(dentry, f_pos, - SMB_READDIR_CACHE_SIZE, c_entry); + /* + * If an error occurred, the cache may have been partially + * filled prior to failing, so we must invalidate. + * N.B. Might not need to for 0 return ... save cache? + */ + c_sb = NULL; + c_ino = 0; + c_seen_eof = 0; + goto out; + } - if (result <= 0) + /* Suppose there are a multiple of cache entries? */ + c_seen_eof = (result < SMB_READDIR_CACHE_SIZE); + c_sb = dir->i_sb; + c_ino = dir->i_ino; + c_size = result; + c_last_returned_index = 0; /* is this used? */ + + ino_start = smb_invent_inos(c_size); + /* + * If a dentry already exists, we have to give the cache entry + * the correct inode number. This is needed for getcwd(). + */ + for (i = 0; i < c_size; i++) + { + struct dentry * new_dentry; + struct qstr qname; + + c_entry[i].attr.f_ino = ino_start++; + qname.name = c_entry[i].name; + qname.len = c_entry[i].len; + qname.hash = hash_it(qname.name, qname.len); + new_dentry = d_lookup(dentry, &qname); + if (new_dentry) { - smb_invalid_dir_cache(dir->i_ino); - up(&sem); - return result; + struct inode * inode = new_dentry->d_inode; + if (inode) + c_entry[i].attr.f_ino = inode->i_ino; + dput(new_dentry); } - c_seen_eof = (result < SMB_READDIR_CACHE_SIZE); - c_dev = dir->i_dev; - c_ino = dir->i_ino; - c_size = result; - c_last_returned_index = 0; - - ino = smb_invent_inos(c_size); - - for (i = 0; i < c_size; i++) - c_entry[i].attr.f_ino = ino++; - - up(&sem); } - while ((c_dev != dir->i_dev) || (c_ino != dir->i_ino)); - +out: return result; } -static int smb_readdir(struct file *filp, - void *dirent, filldir_t filldir) +static int +smb_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct dentry *dentry = filp->f_dentry; struct inode *dir = dentry->d_inode; - int result, i = 0; - struct smb_dirent *entry = NULL; + struct smb_dirent *entry; + int result; pr_debug("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos); pr_debug("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n", dir->i_ino, c_ino); + result = -EBADF; if ((dir == NULL) || !S_ISDIR(dir->i_mode)) - { - return -EBADF; - } + goto out; + + /* + * Check whether the directory cache exists yet + */ if (c_entry == NULL) { - i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE; - c_entry = (struct smb_dirent *) smb_vmalloc(i); - if (c_entry == NULL) + int size = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE; + result = -ENOMEM; + entry = (struct smb_dirent *) smb_vmalloc(size); + /* + * Somebody else may have allocated the cache, + * so we check again to avoid a memory leak. + */ + if (!c_entry) { - return -ENOMEM; + if (!entry) + goto out; + c_entry = entry; + } else if (entry) { + printk("smb_readdir: cache already alloced!\n"); + smb_vfree(entry); } } - if (filp->f_pos == 0) - { - c_ino = 0; - c_dev = 0; - c_seen_eof = 0; - - if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino) < 0) - return 0; - filp->f_pos += 1; - } - if (filp->f_pos == 1) + result = 0; + switch ((unsigned int) filp->f_pos) { - if (filldir(dirent, "..", 2, filp->f_pos, - filp->f_dentry->d_parent->d_inode->i_ino) < 0) - return 0; - - filp->f_pos += 1; + case 0: + if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0) + goto out; + filp->f_pos = 1; + case 1: + if (filldir(dirent, "..", 2, 1, + dentry->d_parent->d_inode->i_ino) < 0) + goto out; + filp->f_pos = 2; } - entry = smb_search_in_cache(dir, filp->f_pos); + /* + * Since filldir() could block if dirent is paged out, + * we hold the refill semaphore while using the cache. + * N.B. It's possible that the directory could change + * between calls to readdir ... what to do?? + */ + down(&refill_cache_sem); + entry = smb_search_in_cache(dir, filp->f_pos); if (entry == NULL) { - if (c_seen_eof) + /* Past the end of _this_ directory? */ + if (this_dir_cached(dir) && c_seen_eof && + filp->f_pos == c_entry[c_size-1].f_pos + 1) { - /* End of directory */ - return 0; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_readdir: eof reached for %s/%s, c_size=%d, pos=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, c_size, (int) filp->f_pos); +#endif + goto up_and_out; } result = smb_refill_dir_cache(dentry, filp->f_pos); if (result <= 0) - { - return result; - } + goto up_and_out; entry = c_entry; } + while (entry < &(c_entry[c_size])) { - ino_t ino = entry->attr.f_ino; - pr_debug("smb_readdir: entry->name = %s\n", entry->name); - if (filldir(dirent, entry->name, strlen(entry->name), - entry->f_pos, ino) < 0) + if (filldir(dirent, entry->name, entry->len, + entry->f_pos, entry->attr.f_ino) < 0) break; - - if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino) - || (entry->f_pos != filp->f_pos)) - break; - +#if SMBFS_PARANOIA +/* should never happen */ +if (!this_dir_cached(dir) || (entry->f_pos != filp->f_pos)) +printk("smb_readdir: cache changed!\n"); +#endif filp->f_pos += 1; entry += 1; } - return 0; + result = 0; + +up_and_out: + up(&refill_cache_sem); +out: + return result; } void smb_init_dir_cache(void) { - c_ino = 0; - c_dev = 0; c_entry = NULL; + c_sb = NULL; + c_ino = 0; + c_seen_eof = 0; } void -smb_invalid_dir_cache(unsigned long ino) +smb_invalid_dir_cache(struct inode * dir) { - /* FIXME: check for dev as well */ - if (ino == c_ino) + if (this_dir_cached(dir)) { + c_sb = NULL; c_ino = 0; c_seen_eof = 0; } @@ -246,6 +335,7 @@ smb_free_dir_cache(void) { if (c_entry != NULL) { + /* N.B. can this block?? */ smb_vfree(c_entry); } c_entry = NULL; @@ -256,106 +346,111 @@ smb_lookup(struct inode *dir, struct dentry *d_entry) { struct smb_fattr finfo; struct inode *inode; - int len = d_entry->d_name.len; int error; - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("smb_lookup: inode is NULL or not a directory\n"); - return -ENOENT; - } - - if (len > SMB_MAXNAMELEN) - return -ENAMETOOLONG; + error = -ENAMETOOLONG; + if (d_entry->d_name.len > SMB_MAXNAMELEN) + goto out; - error = smb_proc_getattr(d_entry, &(d_entry->d_name), &finfo); + error = smb_proc_getattr(d_entry->d_parent, &(d_entry->d_name), &finfo); +#if SMBFS_PARANOIA +if (error && error != -ENOENT) +printk("smb_lookup: find %s/%s failed, error=%d\n", +d_entry->d_parent->d_name.name, d_entry->d_name.name, error); +#endif inode = NULL; - if (!error) { - error = -ENOENT; + if (error == -ENOENT) + goto add_entry; + if (!error) + { finfo.f_ino = smb_invent_inos(1); inode = smb_iget(dir->i_sb, &finfo); - if (!inode) - return -EACCES; - } else if (error != -ENOENT) - return error; - - d_add(d_entry, inode); - return 0; + error = -EACCES; + if (inode) + { + /* cache the dentry pointer */ + inode->u.smbfs_i.dentry = d_entry; + add_entry: + d_entry->d_op = &smbfs_dentry_operations; + d_add(d_entry, inode); + error = 0; + } + } +out: + return error; } -static int smb_create(struct inode *dir, struct dentry *dentry, int mode) +/* + * This code is common to all routines creating a new inode. + */ +static int +smb_instantiate(struct inode *dir, struct dentry *dentry) { struct smb_fattr fattr; - struct inode *inode; int error; - if (!dir || !S_ISDIR(dir->i_mode)) + smb_invalid_dir_cache(dir); + + error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr); + if (!error) { - printk("smb_create: inode is NULL or not a directory\n"); - return -ENOENT; + struct inode *inode; + error = -EACCES; + fattr.f_ino = smb_invent_inos(1); + inode = smb_iget(dir->i_sb, &fattr); + if (inode) + { + /* cache the dentry pointer */ + inode->u.smbfs_i.dentry = dentry; + d_instantiate(dentry, inode); + error = 0; + } } + return error; +} - if (dentry->d_name.len > SMB_MAXNAMELEN) - return -ENAMETOOLONG; - - error = smb_proc_create(dentry, &(dentry->d_name), 0, CURRENT_TIME); - if (error < 0) - return error; +/* N.B. Should the mode argument be put into the fattr? */ +static int +smb_create(struct inode *dir, struct dentry *dentry, int mode) +{ + int error; - smb_invalid_dir_cache(dir->i_ino); + error = -ENAMETOOLONG; + if (dentry->d_name.len > SMB_MAXNAMELEN) + goto out; /* FIXME: In the CIFS create call we get the file in open * state. Currently we close it directly again, although this * is not necessary anymore. */ - error = smb_proc_getattr(dentry, &(dentry->d_name), &fattr); - if (error < 0) - return error; - - fattr.f_ino = smb_invent_inos(1); - - inode = smb_iget(dir->i_sb, &fattr); - if (!inode) - return -EACCES; - - d_instantiate(dentry, inode); - return 0; + error = smb_proc_create(dentry->d_parent, &(dentry->d_name), + 0, CURRENT_TIME); + if (!error) + { + error = smb_instantiate(dir, dentry); + } +out: + return error; } +/* N.B. Should the mode argument be put into the fattr? */ static int smb_mkdir(struct inode *dir, struct dentry *dentry, int mode) { - struct smb_fattr fattr; - struct inode *inode; int error; - if (!dir || !S_ISDIR(dir->i_mode)) - { - printk("smb_mkdir: inode is NULL or not a directory\n"); - return -ENOENT; - } - + error = -ENAMETOOLONG; if (dentry->d_name.len > SMB_MAXNAMELEN) - return -ENAMETOOLONG; - - error = smb_proc_mkdir(dentry, &(dentry->d_name)); - if (error) - return error; - - smb_invalid_dir_cache(dir->i_ino); + goto out; - error = smb_proc_getattr(dentry, &(dentry->d_name), &fattr); - if (error < 0) - return error; - - fattr.f_ino = smb_invent_inos(1); - - inode = smb_iget(dir->i_sb, &fattr); - if (!inode) - return -EACCES; - - d_instantiate(dentry, inode); - return 0; + error = smb_proc_mkdir(dentry->d_parent, &(dentry->d_name)); + if (!error) + { + error = smb_instantiate(dir, dentry); + } +out: + return error; } static int @@ -363,21 +458,25 @@ smb_rmdir(struct inode *dir, struct dentry *dentry) { int error; - if (!dir || !S_ISDIR(dir->i_mode)) + error = -ENAMETOOLONG; + if (dentry->d_name.len > NFS_MAXNAMLEN) + goto out; + + /* + * Since the dentry is holding an inode, the file + * is in use, so we have to close it first. + */ + if (dentry->d_inode) + smb_close(dentry->d_inode); + smb_invalid_dir_cache(dir); + + error = smb_proc_rmdir(dentry->d_parent, &(dentry->d_name)); + if (!error) { - printk("smb_rmdir: inode is NULL or not a directory\n"); - return -ENOENT; + d_delete(dentry); } - - if (dentry->d_name.len > NFS_MAXNAMLEN) - return -ENAMETOOLONG; - - error = smb_proc_rmdir(dentry, &(dentry->d_name)); - if (error) - return error; - - d_delete(dentry); - return 0; +out: + return error; } static int @@ -385,64 +484,96 @@ smb_unlink(struct inode *dir, struct dentry *dentry) { int error; - if (!dir || !S_ISDIR(dir->i_mode)) + error = -ENAMETOOLONG; + if (dentry->d_name.len > SMB_MAXNAMELEN) + goto out; + + /* + * Since the dentry is holding an inode, the file + * is in use, so we have to close it first. + */ + if (dentry->d_inode) + smb_close(dentry->d_inode); + smb_invalid_dir_cache(dir); + + error = smb_proc_unlink(dentry->d_parent, &(dentry->d_name)); + if (!error) { - printk("smb_unlink: inode is NULL or not a directory\n"); - return -ENOENT; + d_delete(dentry); } - - if (dentry->d_name.len > SMB_MAXNAMELEN) - return -ENAMETOOLONG; - - error = smb_proc_unlink(dentry, &(dentry->d_name)); - if (error) - return error; - - smb_invalid_dir_cache(dir->i_ino); - - d_delete(dentry); - return 0; +out: + return error; } -static int smb_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) +static int +smb_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { int error; + error = -ENOTDIR; if (!old_dir || !S_ISDIR(old_dir->i_mode)) { printk("smb_rename: old inode is NULL or not a directory\n"); - return -ENOENT; + goto out; } if (!new_dir || !S_ISDIR(new_dir->i_mode)) { printk("smb_rename: new inode is NULL or not a directory\n"); - return -ENOENT; + goto out; } + error = -ENAMETOOLONG; if (old_dentry->d_name.len > SMB_MAXNAMELEN || new_dentry->d_name.len > SMB_MAXNAMELEN) - return -ENAMETOOLONG; - - error = smb_proc_mv(old_dentry, &(old_dentry->d_name), - new_dentry, &(new_dentry->d_name)); - + goto out; + + /* + * Since the old and new dentries are holding the files open, + * we have to close the files first. + */ + if (old_dentry->d_inode) + smb_close(old_dentry->d_inode); + if (new_dentry->d_inode) + smb_close(new_dentry->d_inode); + + /* Assume success and invalidate now */ + smb_invalid_dir_cache(old_dir); + smb_invalid_dir_cache(new_dir); + + error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name), + new_dentry->d_parent, &(new_dentry->d_name)); + /* + * If the new file exists, attempt to delete it. + */ if (error == -EEXIST) { - error = smb_proc_unlink(old_dentry, &(new_dentry->d_name)); - +#ifdef SMBFS_PARANOIA +printk("smb_rename: existing file %s/%s, d_count=%d\n", +new_dentry->d_parent->d_name.name, new_dentry->d_name.name, +new_dentry->d_count); +#endif + error = smb_proc_unlink(new_dentry->d_parent, + &(new_dentry->d_name)); +#ifdef SMBFS_PARANOIA +printk("smb_rename: after unlink error=%d\n", error); +#endif if (error) - return error; + goto out; + d_delete(new_dentry); - error = smb_proc_mv(old_dentry, &(old_dentry->d_name), - new_dentry, &(new_dentry->d_name)); + error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name), + new_dentry->d_parent, &(new_dentry->d_name)); } - if (error) - return error; - - smb_invalid_dir_cache(old_dir->i_ino); - smb_invalid_dir_cache(new_dir->i_ino); - return 0; + /* + * Update the dcache + */ + if (!error) + { + d_move(old_dentry, new_dentry); + } +out: + return error; } diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 33fe1778f..9653a7be6 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -19,6 +19,10 @@ #include <asm/uaccess.h> #include <asm/system.h> +#define SMBFS_PARANOIA 1 +/* #define SMBFS_DEBUG_VERBOSE 1 */ +/* #define pr_debug printk */ + static inline int min(int a, int b) { @@ -26,8 +30,10 @@ min(int a, int b) } static int -smb_fsync(struct inode *inode, struct file *file) +smb_fsync(struct file *file, struct dentry * dentry) { + printk("smb_fsync: sync file %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); return 0; } @@ -39,6 +45,7 @@ smb_readpage_sync(struct inode *inode, struct page *page) { unsigned long offset = page->offset; char *buffer = (char *) page_address(page); + struct dentry * dentry = inode->u.smbfs_i.dentry; int rsize = SMB_SERVER(inode)->opt.max_xmit - (SMB_HEADER_LEN+15); int result, refresh = 0; int count = PAGE_SIZE; @@ -46,14 +53,26 @@ smb_readpage_sync(struct inode *inode, struct page *page) pr_debug("SMB: smb_readpage_sync(%p)\n", page); clear_bit(PG_error, &page->flags); - result = smb_open(inode, O_RDONLY); + result = -EIO; + if (!dentry) { + printk("smb_readpage_sync: no dentry for inode %ld\n", + inode->i_ino); + goto io_error; + } + + result = smb_open(dentry, O_RDONLY); if (result < 0) goto io_error; + /* Should revalidate inode ... */ do { if (count < rsize) rsize = count; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_readpage: reading %s/%s, offset=%ld, buffer=%p, size=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, offset, buffer, rsize); +#endif result = smb_proc_read(inode, offset, rsize, buffer); if (result < 0) goto io_error; @@ -81,15 +100,17 @@ io_error: int smb_readpage(struct inode *inode, struct page *page) { - unsigned long address; - int error = -1; + int error; pr_debug("SMB: smb_readpage %08lx\n", page_address(page)); +#ifdef SMBFS_PARANOIA + if (test_bit(PG_locked, &page->flags)) + printk("smb_readpage: page already locked!\n"); +#endif set_bit(PG_locked, &page->flags); - address = page_address(page); atomic_inc(&page->count); error = smb_readpage_sync(inode, page); - free_page(address); + __free_page(page); return error; } @@ -123,6 +144,11 @@ smb_writepage_sync(struct inode *inode, struct page *page, clear_bit(PG_uptodate, &page->flags); goto io_error; } + /* N.B. what if result < wsize?? */ +#ifdef SMBFS_PARANOIA +if (result < wsize) +printk("smb_writepage_sync: short write, wsize=%d, result=%d\n", wsize, result); +#endif refresh = 1; buffer += wsize; offset += wsize; @@ -135,6 +161,7 @@ io_error: smb_refresh_inode(inode); clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); return written ? written : result; } @@ -145,7 +172,17 @@ io_error: static int smb_writepage(struct inode *inode, struct page *page) { - return smb_writepage_sync(inode, page, 0, PAGE_SIZE); + int result; + +#ifdef SMBFS_PARANOIA + if (test_bit(PG_locked, &page->flags)) + printk("smb_writepage: page already locked!\n"); +#endif + set_bit(PG_locked, &page->flags); + atomic_inc(&page->count); + result = smb_writepage_sync(inode, page, 0, PAGE_SIZE); + __free_page(page); + return result; } static int @@ -153,15 +190,35 @@ smb_updatepage(struct inode *inode, struct page *page, const char *buffer, unsigned long offset, unsigned int count, int sync) { u8 *page_addr; + int result; pr_debug("SMB: smb_updatepage(%x/%ld %d@%ld, sync=%d)\n", inode->i_dev, inode->i_ino, count, page->offset+offset, sync); +#ifdef SMBFS_PARANOIA + if (test_bit(PG_locked, &page->flags)) + printk("smb_updatepage: page already locked!\n"); +#endif + set_bit(PG_locked, &page->flags); + atomic_inc(&page->count); + page_addr = (u8 *) page_address(page); - copy_from_user(page_addr + offset, buffer, count); - return smb_writepage_sync(inode, page, offset, count); + if (copy_from_user(page_addr + offset, buffer, count)) + goto bad_fault; + result = smb_writepage_sync(inode, page, offset, count); +out: + __free_page(page); + return result; + +bad_fault: + printk("smb_updatepage: fault at page=%p buffer=%p\n", page, buffer); + result = -EFAULT; + clear_bit(PG_uptodate, &page->flags); + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); + goto out; } static long @@ -175,23 +232,26 @@ smb_file_read(struct inode * inode, struct file * file, count, (unsigned long) file->f_pos); status = smb_revalidate_inode(inode); - if (status < 0) - return status; - - return generic_file_read(inode, file, buf, count); + if (status >= 0) + { + status = generic_file_read(inode, file, buf, count); + } + return status; } static int -smb_file_mmap(struct inode * inode, struct file * file, - struct vm_area_struct * vma) +smb_file_mmap(struct file * file, struct vm_area_struct * vma) { + struct dentry * dentry = file->f_dentry; + struct inode * inode = dentry->d_inode; int status; status = smb_revalidate_inode(inode); - if (status < 0) - return status; - - return generic_file_mmap(inode, file, vma); + if (status >= 0) + { + status = generic_file_mmap(file, vma); + } + return status; } /* @@ -207,28 +267,34 @@ smb_file_write(struct inode *inode, struct file *file, inode->i_dev, inode->i_ino, inode->i_count, count, (unsigned long) file->f_pos); + result = -EINVAL; if (!inode) { printk("smb_file_write: inode = NULL\n"); - return -EINVAL; + goto out; } result = smb_revalidate_inode(inode); if (result < 0) - return result; + goto out; - result = smb_open(inode, O_WRONLY); + result = smb_open(file->f_dentry, O_WRONLY); if (result < 0) - return result; + goto out; + result = -EINVAL; if (!S_ISREG(inode->i_mode)) { printk("smb_file_write: write to non-file, mode %07o\n", inode->i_mode); - return -EINVAL; + goto out; } - if (count <= 0) - return 0; - return generic_file_write(inode, file, buf, count); + result = 0; + if (count > 0) + { + result = generic_file_write(inode, file, buf, count); + } +out: + return result; } static struct file_operations smb_file_operations = diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index edea04905..9203650e2 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -26,6 +26,9 @@ #include <asm/system.h> #include <asm/uaccess.h> +#define SB_of(server) ((struct super_block *) ((char *)(server) - \ + (unsigned long)(&((struct super_block *)0)->u.smbfs_sb))) + extern int close_fp(struct file *filp); static void smb_put_inode(struct inode *); @@ -82,6 +85,23 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr) } static void +smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) +{ + inode->i_dev = inode->i_sb->s_dev; + inode->i_mode = fattr->f_mode; + inode->i_nlink = fattr->f_nlink; + inode->i_uid = fattr->f_uid; + inode->i_gid = fattr->f_gid; + inode->i_rdev = fattr->f_rdev; + inode->i_size = fattr->f_size; + inode->i_mtime = fattr->f_mtime; + inode->i_ctime = fattr->f_ctime; + inode->i_atime = fattr->f_atime; + inode->i_blksize= fattr->f_blksize; + inode->i_blocks = fattr->f_blocks; +} + +static void smb_read_inode(struct inode *inode) { pr_debug("smb_iget: %p\n", read_fattr); @@ -92,18 +112,8 @@ smb_read_inode(struct inode *inode) printk("smb_read_inode called from invalid point\n"); return; } - inode->i_mode = read_fattr->f_mode; - inode->i_nlink = read_fattr->f_nlink; - inode->i_uid = read_fattr->f_uid; - inode->i_gid = read_fattr->f_gid; - inode->i_rdev = read_fattr->f_rdev; - inode->i_size = read_fattr->f_size; - inode->i_mtime = read_fattr->f_mtime; - inode->i_ctime = read_fattr->f_ctime; - inode->i_atime = read_fattr->f_atime; - inode->i_blksize = read_fattr->f_blksize; - inode->i_blocks = read_fattr->f_blocks; + smb_set_inode_attr(inode, read_fattr); memset(&(inode->u.smbfs_i), 0, sizeof(inode->u.smbfs_i)); if (S_ISREG(inode->i_mode)) @@ -114,39 +124,112 @@ smb_read_inode(struct inode *inode) inode->i_op = NULL; } +/* + * This is called if the connection has gone bad ... + * try to kill off all the current inodes. + */ void smb_invalidate_inodes(struct smb_sb_info *server) { printk("smb_invalidate_inodes\n"); + shrink_dcache(); /* should shrink only this sb */ + invalidate_inodes(SB_of(server)); } int -smb_revalidate_inode(struct inode *i) +smb_revalidate_inode(struct inode *ino) { + struct dentry * dentry = ino->u.smbfs_i.dentry; + int error = 0; + pr_debug("smb_revalidate_inode\n"); - return 0; + if (!ino) + goto bad_no_inode; + dentry = ino->u.smbfs_i.dentry; +#if 0 + if (dentry) + { + printk("smb_revalidate: checking %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + } +#endif +out: + return error; + +bad_no_inode: + printk("smb_revalidate: no inode!\n"); + error = -EINVAL; + goto out; } int -smb_refresh_inode(struct inode *i) +smb_refresh_inode(struct inode *ino) { + struct dentry * dentry = ino->u.smbfs_i.dentry; + struct smb_fattr fattr; + int error; + pr_debug("smb_refresh_inode\n"); - return 0; + error = -EIO; + if (!dentry) { + printk("smb_refresh_inode: no dentry, can't refresh\n"); + goto out; + } + + /* N.B. Should check for changes of important fields! cf. NFS */ + error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr); + if (!error) + { + smb_set_inode_attr(ino, &fattr); + } +out: + return error; } +/* + * This routine is called for every iput(). + */ static void smb_put_inode(struct inode *ino) { + struct dentry * dentry; pr_debug("smb_put_inode: count = %d\n", ino->i_count); - if (smb_close(ino)) - printk("smbfs: could not close inode\n"); + if (ino->i_count > 1) { + /* + * Check whether the dentry still holds this inode. + * This looks scary, but should work ... d_inode is + * cleared before iput() in the dcache. + */ + dentry = (struct dentry *) ino->u.smbfs_i.dentry; + if (dentry && dentry->d_inode != ino) { + ino->u.smbfs_i.dentry = NULL; +#if 1 +printk("smb_put_inode: cleared dentry for %s/%s (%ld), count=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, ino->i_ino, ino->i_count); +#endif + } + } else { + /* + * Last use ... clear i_nlink to force + * smb_delete_inode to be called. + */ + ino->i_nlink = 0; + } } +/* + * This routine is called when i_nlink == 0 and i_count goes to 0. + * All blocking cleanup operations need to go here to avoid races. + */ static void -smb_delete_inode(struct inode *i) +smb_delete_inode(struct inode *ino) { pr_debug("smb_delete_inode\n"); + if (smb_close(ino)) + printk("smb_delete_inode: could not close inode %ld\n", + ino->i_ino); + clear_inode(ino); } static void @@ -181,33 +264,20 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) struct smb_fattr root; kdev_t dev = sb->s_dev; unsigned char *packet; + struct inode *root_inode; + struct dentry *dentry; MOD_INC_USE_COUNT; - if (!data) { - printk("smb_read_super: missing data argument\n"); - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } + if (!data) + goto out_no_data; if (data->version != SMB_MOUNT_VERSION) - { - printk(KERN_ERR "smb_read_super: wrong data argument." - " Recompile smbmount.\n"); - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } + goto out_wrong_data; packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE); if (!packet) - { - pr_debug("smb_read_super: could not alloc packet\n"); - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } + goto out_no_mem; lock_super(sb); @@ -222,7 +292,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) sb->u.smbfs_sb.conn_pid = 0; sb->u.smbfs_sb.packet = packet; sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE; - sb->u.smbfs_sb.generation = 1; + sb->u.smbfs_sb.generation = 0; sb->u.smbfs_sb.m = *data; sb->u.smbfs_sb.m.file_mode = (sb->u.smbfs_sb.m.file_mode & @@ -232,18 +302,38 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) smb_init_root_dirent(&(sb->u.smbfs_sb), &root); + sb->s_root = NULL; unlock_super(sb); - sb->s_root = d_alloc_root(smb_iget(sb, &root), NULL); - if (!sb->s_root) - { - sb->s_dev = 0; - printk(KERN_ERR "smb_read_super: get root inode failed\n"); - smb_vfree(sb->u.smbfs_sb.packet); - MOD_DEC_USE_COUNT; - return NULL; - } + root_inode = smb_iget(sb, &root); + if (!root_inode) + goto out_no_root; + dentry = d_alloc_root(root_inode, NULL); + if (!dentry) + goto out_no_root; + root_inode->u.smbfs_i.dentry = dentry; + sb->s_root = dentry; return sb; + +out_no_data: + printk("smb_read_super: missing data argument\n"); + goto out; +out_wrong_data: + printk(KERN_ERR "smb_read_super: wrong data argument." + " Recompile smbmount.\n"); + goto out; +out_no_mem: + pr_debug("smb_read_super: could not alloc packet\n"); + goto out; +out_no_root: + sb->s_dev = 0; /* iput() might block */ + printk(KERN_ERR "smb_read_super: get root inode failed\n"); + iput(root_inode); + smb_vfree(packet); +out: + sb->s_dev = 0; + MOD_DEC_USE_COUNT; + return NULL; } static int @@ -265,32 +355,46 @@ smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) int smb_notify_change(struct inode *inode, struct iattr *attr) { - int error = 0; + struct dentry *dentry = inode->u.smbfs_i.dentry; + int error; + + error = -EIO; + if (!dentry) + { + printk("smb_notify_change: no dentry for inode!\n"); + goto out; + } if ((error = inode_change_ok(inode, attr)) < 0) - return error; + goto out; + error = -EPERM; if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != SMB_SERVER(inode)->m.uid))) - return -EPERM; + goto out; if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != SMB_SERVER(inode)->m.gid))) - return -EPERM; + goto out; if (((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) - return -EPERM; + goto out; + + /* + * Assume success and invalidate the parent's dir cache + */ + smb_invalid_dir_cache(dentry->d_parent->d_inode); if ((attr->ia_valid & ATTR_SIZE) != 0) { - if ((error = smb_open(inode, O_WRONLY)) < 0) - goto fail; + if ((error = smb_open(dentry, O_WRONLY)) < 0) + goto out; if ((error = smb_proc_trunc(SMB_SERVER(inode), inode->u.smbfs_i.fileid, attr->ia_size)) < 0) - goto fail; + goto out; } if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0) { @@ -300,37 +404,31 @@ smb_notify_change(struct inode *inode, struct iattr *attr) fattr.attr = 0; fattr.f_size = inode->i_size; fattr.f_blksize = inode->i_blksize; + fattr.f_ctime = inode->i_ctime; + fattr.f_mtime = inode->i_mtime; + fattr.f_atime = inode->i_atime; if ((attr->ia_valid & ATTR_CTIME) != 0) fattr.f_ctime = attr->ia_ctime; - else - fattr.f_ctime = inode->i_ctime; if ((attr->ia_valid & ATTR_MTIME) != 0) fattr.f_mtime = attr->ia_mtime; - else - fattr.f_mtime = inode->i_mtime; if ((attr->ia_valid & ATTR_ATIME) != 0) fattr.f_atime = attr->ia_atime; - else - fattr.f_atime = inode->i_atime; - if ((error = smb_proc_setattr(SMB_SERVER(inode), - inode, &fattr)) >= 0) + error = smb_proc_setattr(SMB_SERVER(inode), dentry, &fattr); + if (error >= 0) { inode->i_ctime = fattr.f_ctime; inode->i_mtime = fattr.f_mtime; inode->i_atime = fattr.f_atime; } } - fail: -/* smb_invalid_dir_cache(smb_info_ino(SMB_INOP(inode)->dir));*/ - +out: return error; } - #ifdef DEBUG_SMB_MALLOC int smb_malloced; int smb_current_kmalloced; diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 311457a74..57b4be6fb 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -29,7 +29,12 @@ #define SMB_DIRINFO_SIZE 43 #define SMB_STATUS_SIZE 21 +#define SMBFS_PARANOIA 1 +/* #define SMBFS_DEBUG_VERBOSE 1 */ +/* #define pr_debug printk */ + static int smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc); +void smb_close_socket(struct smb_sb_info *); static inline int min(int a, int b) @@ -79,6 +84,15 @@ smb_encode_smb_length(__u8 * p, __u32 len) return p + 4; } +/* + * Return the server for the specified dentry + * N.B. Make this a #define in the smb header + */ +static struct smb_sb_info * server_from_dentry(struct dentry * dentry) +{ + return &dentry->d_sb->u.smbfs_sb; +} + static int smb_d_path(struct dentry * entry, char * buf) { if (IS_ROOT(entry)) { @@ -283,11 +297,15 @@ smb_errno(int errcls, int error) return EEXIST; case 87: return 0; /* Unknown error!! */ + case 123: /* Invalid name?? e.g. .tmp* */ + return ENOENT; /* This next error seems to occur on an mv when * the destination exists */ case 183: return EEXIST; default: + printk("smb_errno: ERRDOS code %d, returning EIO\n", + error); return EIO; } else if (errcls == ERRSRV) switch (error) @@ -301,6 +319,8 @@ smb_errno(int errcls, int error) case ERRaccess: return EACCES; default: + printk("smb_errno: ERRSRV code %d, returning EIO\n", + error); return EIO; } else if (errcls == ERRHRD) switch (error) @@ -322,9 +342,14 @@ smb_errno(int errcls, int error) case ERRlock: return EDEADLK; default: + printk("smb_errno: ERRHRD code %d, returning EIO\n", + error); return EIO; } else if (errcls == ERRCMD) + { + printk("smb_errno: ERRCMD code %d, returning EIO\n", error); return EIO; + } return 0; } @@ -349,6 +374,7 @@ static int smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc) { int result = 0; + s->rcls = 0; s->err = 0; @@ -371,86 +397,133 @@ smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc) return result; } -/* smb_retry: This function should be called when smb_request_ok has +/* + * smb_retry: This function should be called when smb_request_ok has indicated an error. If the error was indicated because the connection was killed, we try to reconnect. If smb_retry returns 0, the error was indicated for another reason, so a retry would not be - of any use. */ + of any use. + * N.B. The server must be locked for this call. + */ static int smb_retry(struct smb_sb_info *server) { + int result = 0; + if (server->state != CONN_INVALID) - { - return 0; - } - if (server->sock_file != NULL) - { - close_fp(server->sock_file); - server->sock_file = NULL; - } + goto out; + + smb_close_socket(server); if (server->conn_pid == 0) { + printk("smb_retry: no connection process\n"); server->state = CONN_RETRIED; - return 0; + goto out; } + printk("smb_retry: signalling process %d\n", server->conn_pid); kill_proc(server->conn_pid, SIGUSR1, 0); server->conn_pid = 0; + /* + * Block here until we get a new connection. + * N.B. This needs to be fixed ... we should wait in an + * interruptible sleep for CONN_VALID. + */ + printk("smb_retry: blocking for new connection\n"); smb_lock_server(server); - if (server->sock_file != NULL) + if (server->state == CONN_VALID) { - server->state = CONN_VALID; - return 1; + printk("smb_retry: new connection pid=%d\n", server->conn_pid); + result = 1; } - return 0; +out: + return result; } +/* + * This is called with the server locked after a successful smb_newconn(). + * It installs the new connection pid, sets server->state to CONN_VALID, + * and unlocks the server. + * N.B. The first call is made without locking the server -- need to fix! + */ int smb_offerconn(struct smb_sb_info *server) { + int error; + + error = -EACCES; if (!suser() && (current->uid != server->m.mounted_uid)) - { - return -EACCES; - } + goto out; + server->conn_pid = current->pid; - return 0; + server->state = CONN_VALID; + printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid); + error = 0; + + /* + * The initial call may be made without the server locked? + */ +out: + if (atomic_read(&server->sem.count) != 1) + smb_unlock_server(server); + else + printk("smb_offerconn: server not locked, count=%d\n", + atomic_read(&server->sem.count)); + return error; } +/* + * This must be called with the server locked. + * N.B. The first call is made without locking the server -- need to fix! + */ int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) { struct file *filp; + int error; + error = -EBADF; if (opt->fd >= NR_OPEN || !(filp = current->files->fd[opt->fd])) - { - return -EBADF; - } + goto out_unlock; if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode)) - { - return -EBADF; - } + goto out_unlock; + if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode)) + goto out_unlock; + + error = -EACCES; if (!suser() && (current->uid != server->m.mounted_uid)) - { - return -EACCES; - } - if (server->sock_file != NULL) - { - close_fp(server->sock_file); - server->sock_file = NULL; - } + goto out_unlock; + + /* + * Make sure the old socket is closed + */ + smb_close_socket(server); + filp->f_count += 1; server->sock_file = filp; smb_catch_keepalive(server); server->opt = *opt; pr_debug("smb_newconn: protocol = %d\n", server->opt.protocol); - server->conn_pid = 0; server->generation += 1; - smb_unlock_server(server); - return 0; + error = 0; + +out: + return error; + + /* + * Unlock now if an error occurred. + */ +out_unlock: + if (atomic_read(&server->sem.count) != 1) + smb_unlock_server(server); + else + printk("smb_newconn: server not locked, count=%d\n", + atomic_read(&server->sem.count)); + goto out; } /* smb_setup_header: We completely set up the packet. You only have to @@ -503,17 +576,15 @@ smb_setup_bcc(struct smb_sb_info *server, __u8 * p) SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc); } - /* * We're called with the server locked, and we leave it that way. We * try maximum permissions. */ static int -smb_proc_open(struct dentry *dir) +smb_proc_open(struct smb_sb_info *server, struct dentry *dir) { struct inode *ino = dir->d_inode; - struct smb_sb_info *server = SMB_SERVER(ino); int error; char *p; @@ -532,7 +603,7 @@ smb_proc_open(struct dentry *dir) if ((error != -EACCES) && (error != -ETXTBSY) && (error != -EROFS)) - return error; + goto out; p = smb_setup_header(server, SMBopen, 2, 0); WSET(server->packet, smb_vwv0, 0x40); /* read only */ @@ -545,40 +616,56 @@ smb_proc_open(struct dentry *dir) { if (smb_retry(server)) goto retry; - - return error; + goto out; } } /* We should now have data in vwv[0..6]. */ ino->u.smbfs_i.fileid = WVAL(server->packet, smb_vwv0); - ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1); + ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1); ino->u.smbfs_i.access = WVAL(server->packet, smb_vwv6); ino->u.smbfs_i.access &= 3; ino->u.smbfs_i.open = server->generation; pr_debug("smb_proc_open: entry->access = %d\n", ino->u.smbfs_i.access); - return 0; +out: + return error; } int smb_open(struct dentry *dir, int wish) { - struct inode *i=dir->d_inode; - struct smb_sb_info *server = SMB_SERVER(i); - int result = -EACCES; + struct inode *i = dir->d_inode; + int result; - smb_lock_server(server); + result = -EIO; + if (!i) + { + printk("smb_open: no inode for dentry %s/%s\n", + dir->d_parent->d_name.name, dir->d_name.name); + goto out; + } - if (!smb_is_open(i)) { - int error = smb_proc_open(dir); - if (error) { - smb_unlock_server(server); - return error; + /* + * If the inode is already open, we don't need to lock the server. + */ + if (!smb_is_open(i)) + { + struct smb_sb_info *server = SMB_SERVER(i); + smb_lock_server(server); + result = smb_proc_open(server, dir); + smb_unlock_server(server); + if (result) + { + printk("smb_open: %s/%s open failed, result=%d\n", + dir->d_parent->d_name.name, dir->d_name.name, + result); + goto out; } } + result = -EACCES; if (((wish == O_RDONLY) && ((i->u.smbfs_i.access == O_RDONLY) || (i->u.smbfs_i.access == O_RDWR))) || ((wish == O_WRONLY) && ((i->u.smbfs_i.access == O_WRONLY) @@ -586,7 +673,7 @@ smb_open(struct dentry *dir, int wish) || ((wish == O_RDWR) && (i->u.smbfs_i.access == O_RDWR))) result = 0; - smb_unlock_server(server); +out: return result; } @@ -600,24 +687,21 @@ static int smb_proc_close(struct smb_sb_info *server, DSET(server->packet, smb_vwv1, mtime); return smb_request_ok(server, SMBclose, 0, 0); } - -int smb_close(struct dentry *dir) +int +smb_close(struct inode *ino) { - struct inode *ino = dir->d_inode; - struct smb_sb_info *server = SMB_SERVER(ino); - int result; - - smb_lock_server(server); + int result = 0; - if (!smb_is_open(ino)) { + if (smb_is_open(ino)) + { + struct smb_sb_info *server = SMB_SERVER(ino); + smb_lock_server(server); + result = smb_proc_close(server, ino->u.smbfs_i.fileid, + ino->i_mtime); smb_unlock_server(server); - return 0; + ino->u.smbfs_i.open = 0; } - - result = smb_proc_close(server, ino->u.smbfs_i.fileid, ino->i_mtime); - ino->u.smbfs_i.open = 0; - smb_unlock_server(server); return result; } @@ -633,38 +717,49 @@ smb_proc_read(struct inode *ino, off_t offset, long count, char *data) struct smb_sb_info *server = SMB_SERVER(ino); __u16 returned_count, data_len; char *buf; - int error; + int result; + struct dentry * dentry; + + if (!ino || !(dentry = ino->u.smbfs_i.dentry)) + { + printk("smb_proc_read: no inode!\n"); + return -EIO; + } smb_lock_server(server); smb_setup_header(server, SMBread, 5, 0); - buf = server->packet; + /* Achtung! Do not refer to the cached packet after the request! */ + buf = server->packet; WSET(buf, smb_vwv0, ino->u.smbfs_i.fileid); WSET(buf, smb_vwv1, count); DSET(buf, smb_vwv2, offset); WSET(buf, smb_vwv4, 0); - if ((error = smb_request_ok(server, SMBread, 5, -1)) < 0) - { - smb_unlock_server(server); - return error; - } - returned_count = WVAL(buf, smb_vwv0); + result = smb_request_ok(server, SMBread, 5, -1); + if (result < 0) + goto out; +#if 0 +printk("smb_proc_read: file %s/%s, result=%d, packet=%p\n", +dentry->d_parent->d_name.name, dentry->d_name.name, result, server->packet); +#endif + returned_count = WVAL(server->packet, smb_vwv0); buf = SMB_BUF(server->packet); data_len = WVAL(buf, 1); - memcpy(data, buf+3, data_len); - smb_unlock_server(server); - if (returned_count != data_len) { printk(KERN_NOTICE "smb_proc_read: returned != data_len\n"); printk(KERN_NOTICE "smb_proc_read: ret_c=%d, data_len=%d\n", returned_count, data_len); } - return data_len; + result = data_len; + +out: + smb_unlock_server(server); + return result; } int @@ -697,18 +792,17 @@ int smb_proc_create(struct dentry *dir, struct qstr *name, __u16 attr, time_t ctime) { + struct smb_sb_info *server; int error; char *p; - struct inode *i=dir->d_inode; - struct smb_sb_info *server = SMB_SERVER(i); - char *buf; + server = server_from_dentry(dir); smb_lock_server(server); + retry: - buf = server->packet; p = smb_setup_header(server, SMBcreate, 3, 0); - WSET(buf, smb_vwv0, attr); - DSET(buf, smb_vwv1, utc2local(ctime)); + WSET(server->packet, smb_vwv0, attr); + DSET(server->packet, smb_vwv1, utc2local(ctime)); *p++ = 4; p = smb_encode_path(server, p, dir, name); smb_setup_bcc(server, p); @@ -719,23 +813,25 @@ smb_proc_create(struct dentry *dir, struct qstr *name, { goto retry; } - smb_unlock_server(server); - return error; + goto out; } - smb_proc_close(server, WVAL(buf, smb_vwv0), CURRENT_TIME); - smb_unlock_server(server); + smb_proc_close(server, WVAL(server->packet, smb_vwv0), CURRENT_TIME); + error = 0; - return 0; +out: + smb_unlock_server(server); + return error; } int smb_proc_mv(struct dentry *odir, struct qstr *oname, struct dentry *ndir, struct qstr *nname) { + struct smb_sb_info *server; char *p; - struct smb_sb_info *server = SMB_SERVER(odir->d_inode); int result; + server = server_from_dentry(odir); smb_lock_server(server); retry: @@ -761,10 +857,11 @@ smb_proc_mv(struct dentry *odir, struct qstr *oname, int smb_proc_mkdir(struct dentry *dir, struct qstr *name) { + struct smb_sb_info *server; char *p; int result; - struct smb_sb_info *server = SMB_SERVER(dir->d_inode); + server = server_from_dentry(dir); smb_lock_server(server); retry: @@ -787,10 +884,11 @@ smb_proc_mkdir(struct dentry *dir, struct qstr *name) int smb_proc_rmdir(struct dentry *dir, struct qstr *name) { + struct smb_sb_info *server; char *p; int result; - struct smb_sb_info *server = SMB_SERVER(dir->d_inode); + server = server_from_dentry(dir); smb_lock_server(server); retry: @@ -813,10 +911,11 @@ smb_proc_rmdir(struct dentry *dir, struct qstr *name) int smb_proc_unlink(struct dentry *dir, struct qstr *name) { + struct smb_sb_info *server; char *p; - struct smb_sb_info *server = SMB_SERVER(dir->d_inode); int result; + server = server_from_dentry(dir); smb_lock_server(server); retry: @@ -882,22 +981,18 @@ smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) static void smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) { + fattr->f_mode = server->m.file_mode; if (fattr->attr & aDIR) { fattr->f_mode = server->m.dir_mode; fattr->f_size = 512; - } else - { - fattr->f_mode = server->m.file_mode; } + fattr->f_blocks = 0; /* already set to zero? */ if ((fattr->f_blksize != 0) && (fattr->f_size != 0)) { fattr->f_blocks = (fattr->f_size - 1) / fattr->f_blksize + 1; - } else - { - fattr->f_blocks = 0; } return; } @@ -982,13 +1077,13 @@ smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos, smb_lock_server(server); retry: - buf = server->packet; first = 1; total_count = 0; current_entry = entry; while (1) { + buf = server->packet; if (first == 1) { p = smb_setup_header(server, SMBsearch, 2, 0); @@ -1098,6 +1193,7 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, struct smb_dirent *entry, int level) { char *result; + unsigned int len; smb_init_dirent(server, &(entry->attr)); @@ -1105,20 +1201,24 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, { /* We might add more levels later... */ case 1: - entry->len = *(p+26); - strncpy(entry->name, p + 27, entry->len); - entry->name[entry->len] = '\0'; - entry->attr.f_size = DVAL(p, 16); - entry->attr.attr = *(p+24); - entry->attr.f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4)); entry->attr.f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8)); entry->attr.f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12)); - result = p + 28 + *(p+26); + entry->attr.f_size = DVAL(p, 16); + entry->attr.attr = *(p+24); + /* + * Achtung, lengths can go up to 255 + */ + len = *((unsigned char *) p + 26); + entry->len = len; + strncpy(entry->name, p + 27, len); + entry->name[len] = '\0'; + + result = p + 28 + len; break; default: - pr_debug("Unknown long filename format %d\n", level); + printk("smb_decode: Unknown long filename format %d\n", level); result = p + WVAL(p, 0); } @@ -1135,6 +1235,7 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, } smb_finish_dirent(server, &(entry->attr)); + return result; } @@ -1148,7 +1249,7 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos, char *p; char *lastname; - int lastname_len; + unsigned lastname_len; int i; int first, entries, entries_seen; @@ -1168,7 +1269,7 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos, int ff_dir_handle = 0; int loop_count = 0; - char param[SMB_MAXPATHLEN + 2 + 12]; + char param[SMB_MAXPATHLEN + 2 + 12]; /* too long for the stack! */ int mask_len; char *mask = &(param[12]); @@ -1197,6 +1298,7 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos, { printk(KERN_WARNING "smb_proc_readdir_long: " "Looping in FIND_NEXT??\n"); + entries = -EIO; break; } if (first != 0) @@ -1243,15 +1345,23 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos, { goto retry; } - pr_debug("smb_proc_readdir_long: " - "got error from trans2_request\n"); +#ifdef SMBFS_PARANOIA +printk("smb_proc_readdir_long: trans2_request error=%d\n", result); +#endif + entries = result; break; } if (server->rcls != 0) - { - result = -EIO; + { +#ifdef SMBFS_PARANOIA +printk("smb_proc_readdir_long: error, rcls=%d, err=%d\n", +server->rcls, server->err); +#endif + /* Why isn't this considered an error? */ + /* entries = -EIO; */ break; } + /* parse out some important return info */ if (first != 0) { @@ -1278,17 +1388,15 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos, lastname_len = 0; if (ff_lastname > 0) { + ff_resume_key = 0; + lastname = p + ff_lastname; switch (info_level) { case 260: - lastname = p + ff_lastname; lastname_len = resp_data_len - ff_lastname; - ff_resume_key = 0; break; case 1: - lastname = p + ff_lastname + 1; - lastname_len = *(p+ff_lastname); - ff_resume_key = 0; + lastname_len = *((unsigned char *) lastname++); break; } } @@ -1320,11 +1428,12 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos, entry->f_pos = entries_seen; entries += 1; } - if (entries >= cache_size) - { - goto finished; - } entries_seen += 1; + if (entries < cache_size) + continue; + + /* cache is full */ + goto finished; } pr_debug("received %d entries (eos=%d resume=%d)\n", @@ -1342,8 +1451,9 @@ int smb_proc_readdir(struct dentry *dir, int fpos, int cache_size, struct smb_dirent *entry) { - struct smb_sb_info *server = SMB_SERVER(dir->d_inode); + struct smb_sb_info *server; + server = server_from_dentry(dir); if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) return smb_proc_readdir_long(server, dir, fpos, cache_size, entry); @@ -1353,18 +1463,15 @@ smb_proc_readdir(struct dentry *dir, int fpos, } static int -smb_proc_getattr_core(struct dentry *dir, struct qstr *name, - struct smb_fattr *attr) +smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, + struct qstr *name, struct smb_fattr *attr) { int result; char *p; - struct smb_sb_info *server = SMB_SERVER(dir->d_inode); - char *buf; smb_lock_server(server); retry: - buf = server->packet; p = smb_setup_header(server, SMBgetatr, 0, 0); *p++ = 4; p = smb_encode_path(server, p, dir, name); @@ -1373,26 +1480,24 @@ smb_proc_getattr_core(struct dentry *dir, struct qstr *name, if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0) { if (smb_retry(server)) - { goto retry; - } - smb_unlock_server(server); - return result; + goto out; } - attr->attr = WVAL(buf, smb_vwv0); - attr->f_ctime = attr->f_atime = - attr->f_mtime = local2utc(DVAL(buf, smb_vwv1)); + attr->attr = WVAL(server->packet, smb_vwv0); + attr->f_ctime = attr->f_atime = attr->f_mtime = + local2utc(DVAL(server->packet, smb_vwv1)); + attr->f_size = DVAL(server->packet, smb_vwv3); + result = 0; - attr->f_size = DVAL(buf, smb_vwv3); +out: smb_unlock_server(server); - return 0; + return result; } static int -smb_proc_getattr_trans2(struct dentry *dir, struct qstr *name, - struct smb_fattr *attr) +smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, + struct qstr *name, struct smb_fattr *attr) { - struct smb_sb_info *server = SMB_SERVER(dir->d_inode); char param[SMB_MAXPATHLEN + 20]; char *p; int result; @@ -1412,26 +1517,22 @@ smb_proc_getattr_trans2(struct dentry *dir, struct qstr *name, 0, NULL, p - param, param, &resp_data_len, &resp_data, &resp_param_len, &resp_param); - + if (server->rcls != 0) { - smb_unlock_server(server); - return -smb_errno(server->rcls, server->err); + result = -smb_errno(server->rcls, server->err); + goto out; } if (result < 0) { if (smb_retry(server)) - { goto retry; - } - smb_unlock_server(server); - return result; + goto out; } + result = -ENOENT; if (resp_data_len < 22) - { - smb_unlock_server(server); - return -ENOENT; - } + goto out; + attr->f_ctime = date_dos2unix(WVAL(resp_data, 2), WVAL(resp_data, 0)); attr->f_atime = date_dos2unix(WVAL(resp_data, 6), @@ -1440,24 +1541,28 @@ smb_proc_getattr_trans2(struct dentry *dir, struct qstr *name, WVAL(resp_data, 8)); attr->f_size = DVAL(resp_data, 12); attr->attr = WVAL(resp_data, 20); - smb_unlock_server(server); + result = 0; - return 0; +out: + smb_unlock_server(server); + return result; } -int smb_proc_getattr(struct dentry *dir, struct qstr *name, +int +smb_proc_getattr(struct dentry *dir, struct qstr *name, struct smb_fattr *fattr) { - struct smb_sb_info *server = SMB_SERVER(dir->d_inode); - int result = 0; + struct smb_sb_info *server; + int result = -1; + server = server_from_dentry(dir); smb_init_dirent(server, fattr); if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) - result = smb_proc_getattr_trans2(dir, name, fattr); + result = smb_proc_getattr_trans2(server, dir, name, fattr); - if ((server->opt.protocol < SMB_PROTOCOL_LANMAN2) || (result < 0)) - result = smb_proc_getattr_core(dir, name, fattr); + if (result < 0) + result = smb_proc_getattr_core(server, dir, name, fattr); smb_finish_dirent(server, fattr); @@ -1546,12 +1651,12 @@ int smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *fattr) { - int result; + int result = -1; if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) result = smb_proc_setattr_trans2(server, dir, fattr); - if ((server->opt.protocol < SMB_PROTOCOL_LANMAN2) || (result < 0)) + if (result < 0) result = smb_proc_setattr_core(server, dir, fattr); return result; @@ -1560,9 +1665,9 @@ smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir, int smb_proc_dskattr(struct super_block *sb, struct statfs *attr) { + struct smb_sb_info *server = &(sb->u.smbfs_sb); int error; char *p; - struct smb_sb_info *server = &(sb->u.smbfs_sb); smb_lock_server(server); @@ -1573,16 +1678,17 @@ smb_proc_dskattr(struct super_block *sb, struct statfs *attr) { if (smb_retry(server)) goto retry; - - smb_unlock_server(server); - return error; + goto out; } p = SMB_VWV(server->packet); attr->f_bsize = WVAL(p, 2) * WVAL(p, 4); attr->f_blocks = WVAL(p, 0); attr->f_bavail = attr->f_bfree = WVAL(p, 6); + error = 0; + +out: smb_unlock_server(server); - return 0; + return error; } int diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c index 1687be10b..aceca122b 100644 --- a/fs/smbfs/sock.c +++ b/fs/smbfs/sock.c @@ -24,10 +24,13 @@ #include <asm/uaccess.h> +#define SMBFS_PARANOIA 1 +/* #define SMBFS_DEBUG_VERBOSE 1 */ + #define _S(nr) (1<<((nr)-1)) static int -_recvfrom(struct socket *sock, unsigned char *ubuf, int size, +_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags) { struct iovec iov; @@ -43,14 +46,14 @@ _recvfrom(struct socket *sock, unsigned char *ubuf, int size, iov.iov_len = size; memset(&scm, 0,sizeof(scm)); - size=sock->ops->recvmsg(sock, &msg, size, flags, &scm); + size=socket->ops->recvmsg(socket, &msg, size, flags, &scm); if(size>=0) - scm_recv(sock,&msg,&scm,flags); + scm_recv(socket,&msg,&scm,flags); return size; } static int -_send(struct socket *sock, const void *buff, int len) +_send(struct socket *socket, const void *buff, int len) { struct iovec iov; struct msghdr msg; @@ -69,163 +72,199 @@ _send(struct socket *sock, const void *buff, int len) msg.msg_flags = 0; - err = scm_send(sock, &msg, &scm); - if (err < 0) - return err; - err = sock->ops->sendmsg(sock, &msg, len, &scm); - scm_destroy(&scm); + err = scm_send(socket, &msg, &scm); + if (err >= 0) + { + err = socket->ops->sendmsg(socket, &msg, len, &scm); + scm_destroy(&scm); + } return err; } +/* + * N.B. What happens if we're in here when the socket closes?? + */ static void smb_data_callback(struct sock *sk, int len) { - struct socket *sock = sk->socket; + struct socket *socket = sk->socket; + unsigned char peek_buf[4]; + int result; + unsigned long fs; + + fs = get_fs(); + set_fs(get_ds()); - if (!sk->dead) + while (1) { - unsigned char peek_buf[4]; - int result; - unsigned long fs; + if (sk->dead) + { + printk("smb_data_callback: sock dead!\n"); + return; + } + result = _recvfrom(socket, (void *) peek_buf, 1, + MSG_PEEK | MSG_DONTWAIT); + if (result == -EAGAIN) + break; + if (peek_buf[0] != 0x85) + break; - fs = get_fs(); - set_fs(get_ds()); + /* got SESSION KEEP ALIVE */ + result = _recvfrom(socket, (void *) peek_buf, 4, + MSG_DONTWAIT); - result = _recvfrom(sock, (void *) peek_buf, 1, - MSG_PEEK | MSG_DONTWAIT); + pr_debug("smb_data_callback: got SESSION KEEPALIVE\n"); - while ((result != -EAGAIN) && (peek_buf[0] == 0x85)) - { - /* got SESSION KEEP ALIVE */ - result = _recvfrom(sock, (void *) peek_buf, 4, - MSG_DONTWAIT); - - pr_debug("smb_data_callback: got SESSION KEEPALIVE\n"); - - if (result == -EAGAIN) - { - break; - } - result = _recvfrom(sock, (void *) peek_buf, 1, - MSG_PEEK | MSG_DONTWAIT); - } - set_fs(fs); + if (result == -EAGAIN) + break; + } + set_fs(fs); - if (result != -EAGAIN) - { - wake_up_interruptible(sk->sleep); - } + if (result != -EAGAIN) + { + wake_up_interruptible(sk->sleep); } } -int -smb_catch_keepalive(struct smb_sb_info *server) +static struct socket * +server_sock(struct smb_sb_info *server) { struct file *file; struct inode *inode; - struct socket *sock; - struct sock *sk; - if ((server == NULL) - || ((file = server->sock_file) == NULL) - || ((inode = file->f_dentry->d_inode) == NULL) - || (!S_ISSOCK(inode->i_mode))) - { - pr_debug("smb_catch_keepalive: did not get valid server!\n"); - server->data_ready = NULL; - return -EINVAL; - } - sock = &(inode->u.socket_i); + if (server && + (file = server->sock_file) && + (inode = file->f_dentry->d_inode) && + S_ISSOCK(inode->i_mode) && + inode->u.socket_i.type == SOCK_STREAM) + return &(inode->u.socket_i); + return NULL; +} - if (sock->type != SOCK_STREAM) +int +smb_catch_keepalive(struct smb_sb_info *server) +{ + struct socket *socket; + struct sock *sk; + void *data_ready; + int error; + + error = -EINVAL; + socket = server_sock(server); + if (!socket) { - pr_debug("smb_catch_keepalive: did not get SOCK_STREAM\n"); + printk("smb_catch_keepalive: did not get valid server!\n"); server->data_ready = NULL; - return -EINVAL; + goto out; } - sk = sock->sk; + sk = socket->sk; if (sk == NULL) { pr_debug("smb_catch_keepalive: sk == NULL"); server->data_ready = NULL; - return -EINVAL; + goto out; } pr_debug("smb_catch_keepalive.: sk->d_r = %x, server->d_r = %x\n", (unsigned int) (sk->data_ready), (unsigned int) (server->data_ready)); - if (sk->data_ready == smb_data_callback) + /* + * Install the callback atomically to avoid races ... + */ + data_ready = xchg(&sk->data_ready, smb_data_callback); + if (data_ready != smb_data_callback) { + server->data_ready = data_ready; + error = 0; + } else printk(KERN_ERR "smb_catch_keepalive: already done\n"); - return -EINVAL; - } - server->data_ready = sk->data_ready; - sk->data_ready = smb_data_callback; - return 0; +out: + return error; } int smb_dont_catch_keepalive(struct smb_sb_info *server) { - struct file *file; - struct inode *inode; - struct socket *sock; + struct socket *socket; struct sock *sk; + void * data_ready; + int error; - if ((server == NULL) - || ((file = server->sock_file) == NULL) - || ((inode = file->f_dentry->d_inode) == NULL) - || (!S_ISSOCK(inode->i_mode))) + error = -EINVAL; + socket = server_sock(server); + if (!socket) { - printk("smb_dont_catch_keepalive: " - "did not get valid server!\n"); - return -EINVAL; - } - sock = &(inode->u.socket_i); - - if (sock->type != SOCK_STREAM) - { - printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n"); - return -EINVAL; + printk("smb_dont_catch_keepalive: did not get valid server!\n"); + goto out; } - sk = sock->sk; + sk = socket->sk; if (sk == NULL) { printk("smb_dont_catch_keepalive: sk == NULL"); - return -EINVAL; + goto out; } + + /* Is this really an error?? */ if (server->data_ready == NULL) { printk("smb_dont_catch_keepalive: " "server->data_ready == NULL\n"); - return -EINVAL; - } - if (sk->data_ready != smb_data_callback) - { - printk("smb_dont_catch_keepalive: " - "sk->data_callback != smb_data_callback\n"); - return -EINVAL; + goto out; } pr_debug("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n", (unsigned int) (sk->data_ready), (unsigned int) (server->data_ready)); - sk->data_ready = server->data_ready; + /* + * Restore the original callback atomically to avoid races ... + */ + data_ready = xchg(&sk->data_ready, server->data_ready); server->data_ready = NULL; - return 0; + if (data_ready != smb_data_callback) + { + printk("smb_dont_catch_keepalive: " + "sk->data_callback != smb_data_callback\n"); + } + error = 0; +out: + return error; +} + +/* + * Called with the server locked. + */ +void +smb_close_socket(struct smb_sb_info *server) +{ + struct file * file = server->sock_file; + + if (file) + { + struct socket * socket = server_sock(server); + + printk("smb_close_socket: closing socket %p\n", socket); + /* + * We need a way to check for tasks running the callback! + */ + if (socket->sk->data_ready == smb_data_callback) + printk("smb_close_socket: still catching keepalives!\n"); + + server->sock_file = NULL; + close_fp(file); + } } static int -smb_send_raw(struct socket *sock, unsigned char *source, int length) +smb_send_raw(struct socket *socket, unsigned char *source, int length) { int result; int already_sent = 0; while (already_sent < length) { - result = _send(sock, + result = _send(socket, (void *) (source + already_sent), length - already_sent); @@ -245,14 +284,14 @@ smb_send_raw(struct socket *sock, unsigned char *source, int length) } static int -smb_receive_raw(struct socket *sock, unsigned char *target, int length) +smb_receive_raw(struct socket *socket, unsigned char *target, int length) { int result; int already_read = 0; while (already_read < length) { - result = _recvfrom(sock, + result = _recvfrom(socket, (void *) (target + already_read), length - already_read, 0); @@ -272,7 +311,7 @@ smb_receive_raw(struct socket *sock, unsigned char *target, int length) } static int -smb_get_length(struct socket *sock, unsigned char *header) +smb_get_length(struct socket *socket, unsigned char *header) { int result; unsigned char peek_buf[4]; @@ -281,7 +320,7 @@ smb_get_length(struct socket *sock, unsigned char *header) re_recv: fs = get_fs(); set_fs(get_ds()); - result = smb_receive_raw(sock, peek_buf, 4); + result = smb_receive_raw(socket, peek_buf, 4); set_fs(fs); if (result < 0) @@ -312,21 +351,6 @@ smb_get_length(struct socket *sock, unsigned char *header) return smb_len(peek_buf); } -static struct socket * -server_sock(struct smb_sb_info *server) -{ - struct file *file; - struct inode *inode; - - if (server == NULL) - return NULL; - if ((file = server->sock_file) == NULL) - return NULL; - if ((inode = file->f_dentry->d_inode) == NULL) - return NULL; - return &(inode->u.socket_i); -} - /* * smb_receive * fs points to the correct segment @@ -334,12 +358,12 @@ server_sock(struct smb_sb_info *server) static int smb_receive(struct smb_sb_info *server) { - struct socket *sock = server_sock(server); + struct socket *socket = server_sock(server); int len; int result; unsigned char peek_buf[4]; - len = smb_get_length(sock, peek_buf); + len = smb_get_length(socket, peek_buf); if (len < 0) { @@ -352,6 +376,7 @@ smb_receive(struct smb_sb_info *server) pr_debug("smb_receive: Increase packet size from %d to %d\n", server->packet_size, len + 4); smb_vfree(server->packet); + server->packet = 0; server->packet_size = 0; server->packet = smb_vmalloc(len + 4); if (server->packet == NULL) @@ -361,7 +386,7 @@ smb_receive(struct smb_sb_info *server) server->packet_size = len + 4; } memcpy(server->packet, peek_buf, 4); - result = smb_receive_raw(sock, server->packet + 4, len); + result = smb_receive_raw(socket, server->packet + 4, len); if (result < 0) { @@ -371,11 +396,10 @@ smb_receive(struct smb_sb_info *server) server->rcls = *(server->packet+9); server->err = WVAL(server->packet, 11); - if (server->rcls != 0) - { - pr_debug("smb_receive: rcls=%d, err=%d\n", - server->rcls, server->err); - } +#ifdef SMBFS_DEBUG_VERBOSE +if (server->rcls != 0) +printk("smb_receive: rcls=%d, err=%d\n", server->rcls, server->err); +#endif return result; } @@ -469,6 +493,13 @@ smb_receive_trans2(struct smb_sb_info *server, total_data = WVAL(inbuf, smb_tdrcnt); total_param = WVAL(inbuf, smb_tprcnt); +#ifdef SMBFS_PARANOIA +if ((data_len >= total_data || param_len >= total_param) && + !(data_len >= total_data && param_len >= total_param)) +printk("smb_receive_trans2: dlen=%d, tdata=%d, plen=%d, tlen=%d\n", +data_len, total_data, param_len, total_param); +#endif + /* shouldn't this be an OR test? don't want to overrun */ if ((data_len >= total_data) && (param_len >= total_param)) { break; @@ -477,11 +508,9 @@ smb_receive_trans2(struct smb_sb_info *server, { goto fail; } + result = -EIO; if (server->rcls != 0) - { - result = -EIO; goto fail; - } } *ldata = data_len; *lparam = param_len; @@ -496,32 +525,33 @@ smb_receive_trans2(struct smb_sb_info *server, return result; } +/* + * Called with the server locked + */ int smb_request(struct smb_sb_info *server) { unsigned long old_mask; unsigned long fs; int len, result; + unsigned char *buffer; - unsigned char *buffer = (server == NULL) ? NULL : server->packet; + result = -EBADF; + if (!server) /* this can't happen */ + goto bad_no_server; + + buffer = server->packet; + if (!buffer) + goto bad_no_packet; - if (buffer == NULL) - { - pr_debug("smb_request: Bad server!\n"); - return -EBADF; - } + result = -EIO; if (server->state != CONN_VALID) - { - return -EIO; - } + goto bad_no_conn; + if ((result = smb_dont_catch_keepalive(server)) != 0) - { - server->state = CONN_INVALID; - smb_invalidate_inodes(server); - return result; - } - len = smb_len(buffer) + 4; + goto bad_conn; + len = smb_len(buffer) + 4; pr_debug("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]); old_mask = current->blocked; @@ -544,17 +574,31 @@ smb_request(struct smb_sb_info *server) int result2 = smb_catch_keepalive(server); if (result2 < 0) { + printk("smb_request: catch keepalive failed\n"); result = result2; } } if (result < 0) - { - server->state = CONN_INVALID; - smb_invalidate_inodes(server); - } - pr_debug("smb_request: result = %d\n", result); + goto bad_conn; +out: + pr_debug("smb_request: result = %d\n", result); return result; + +bad_conn: + printk("smb_request: result %d, setting invalid\n", result); + server->state = CONN_INVALID; + smb_invalidate_inodes(server); + goto out; +bad_no_server: + printk("smb_request: no server!\n"); + goto out; +bad_no_packet: + printk("smb_request: no packet!\n"); + goto out; +bad_no_conn: + printk("smb_request: connection %d not valid!\n", server->state); + goto out; } #define ROUND_UP(x) (((x)+3) & ~3) @@ -629,11 +673,11 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command, iov[3].iov_len = ldata; err = scm_send(sock, &msg, &scm); - if (err < 0) - return err; - - err = sock->ops->sendmsg(sock, &msg, packet_length, &scm); - scm_destroy(&scm); + if (err >= 0) + { + err = sock->ops->sendmsg(sock, &msg, packet_length, &scm); + scm_destroy(&scm); + } return err; } @@ -655,16 +699,19 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, pr_debug("smb_trans2_request: com=%d, ld=%d, lp=%d\n", trans2_command, ldata, lparam); + /* + * These are initialized in smb_request_ok, but not here?? + */ + server->rcls = 0; + server->err = 0; + + result = -EIO; if (server->state != CONN_VALID) - { - return -EIO; - } + goto out; + if ((result = smb_dont_catch_keepalive(server)) != 0) - { - server->state = CONN_INVALID; - smb_invalidate_inodes(server); - return result; - } + goto bad_conn; + old_mask = current->blocked; current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP)); fs = get_fs(); @@ -691,11 +738,15 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, } } if (result < 0) - { - server->state = CONN_INVALID; - smb_invalidate_inodes(server); - } + goto bad_conn; pr_debug("smb_trans2_request: result = %d\n", result); +out: return result; + +bad_conn: + printk("smb_trans2_request: connection bad, setting invalid\n"); + server->state = CONN_INVALID; + smb_invalidate_inodes(server); + goto out; } diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c index df3b948df..96b0649a4 100644 --- a/fs/sysv/fsync.c +++ b/fs/sysv/fsync.c @@ -178,9 +178,10 @@ static int sync_tindirect(struct inode *inode, unsigned long *tiblockp, int conv return err; } -int sysv_sync_file(struct inode * inode, struct file * file) +int sysv_sync_file(struct file * file, struct dentry *dentry) { int wait, err = 0; + struct inode *inode = dentry->d_inode; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) |