diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-09-28 22:25:29 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-09-28 22:25:29 +0000 |
commit | 0ae8dceaebe3659ee0c3352c08125f403e77ebca (patch) | |
tree | 5085c389f09da78182b899d19fe1068b619a69dd /fs | |
parent | 273767781288c35c9d679e908672b9996cda4c34 (diff) |
Merge with 2.3.10.
Diffstat (limited to 'fs')
43 files changed, 499 insertions, 316 deletions
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 4723c6802..52c1f7aab 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -461,7 +461,8 @@ beyond_if: return retval; } - current->mm->start_stack = create_aout_tables(bprm->p, bprm); + current->mm->start_stack = + (unsigned long) create_aout_tables((char *) bprm->p, bprm); #ifdef __alpha__ regs->gp = ex.a_gpvalue; #endif diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index bd3c8f490..e3087a18b 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -443,6 +443,8 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) retval = -ENOMEM; size = elf_ex.e_phentsize * elf_ex.e_phnum; + if (size > 65536) + goto out; elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL); if (!elf_phdata) goto out; diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 1c3b11199..d3412e52c 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -326,8 +326,8 @@ void coda_flag_inode_children(struct inode *inode, int flag) while ( alias != &inode->i_dentry ) { alias_de = list_entry(alias, struct dentry, d_alias); coda_flag_children(alias_de, flag); - shrink_dcache_parent(alias_de); alias = alias->next; + shrink_dcache_parent(alias_de); } } diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index dd1e03f5f..ffae7746f 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -14,6 +14,17 @@ extern int coda_debug; extern int coda_print_entry; +inline int coda_fideq(ViceFid *fid1, ViceFid *fid2) +{ + if (fid1->Vnode != fid2->Vnode) + return 0; + if (fid1->Volume != fid2->Volume) + return 0; + if (fid1->Unique != fid2->Unique) + return 0; + return 1; +} + /* cnode.c */ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) { @@ -71,8 +82,14 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) } cnp = ITOC(*inode); - if ( cnp->c_magic != 0 ) { - printk("coda_cnode make on initialized inode %ld, old %s new + /* see if we've got it already */ + if ( cnp->c_magic != 0 && coda_fideq(fid, &cnp->c_fid)) { + return 0; + } + + /* not fresh: collision */ + if ( cnp->c_magic != 0 ) { + printk("coda_cnode_make on initialized inode %ld, old %s new %s!\n", (*inode)->i_ino, coda_f2s(&cnp->c_fid), coda_f2s2(fid)); iput(*inode); @@ -106,14 +123,6 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) return 0; } -inline int coda_fideq(ViceFid *fid1, ViceFid *fid2) -{ - int eq; - eq = ( (fid1->Vnode == fid2->Vnode) && - (fid1->Volume == fid2->Volume) && - (fid1->Unique == fid2->Unique) ); - return eq; -} void coda_replace_fid(struct inode *inode, struct ViceFid *oldfid, struct ViceFid *newfid) diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index f684f3a78..5f2f3c467 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -215,6 +215,8 @@ void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr) if (attr->va_ctime.tv_sec != -1) inode->i_ctime = attr->va_ctime.tv_sec; } + + /* * BSD sets attributes that need not be modified to -1. * Linux uses the valid field to indicate what should be diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 4313edb69..10b1cb171 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -800,6 +800,10 @@ static int coda_dentry_revalidate(struct dentry *de, int flags) shrink_dcache_parent(de); + /* propagate for a flush */ + if (cii->c_flags & C_FLUSH) + coda_flag_inode_children(inode, C_FLUSH); + if (de->d_count > 1) { /* pretend it's valid, but don't change the flags */ CDEBUG(D_DOWNCALL, "BOOM for: ino %ld, %s\n", @@ -807,10 +811,6 @@ static int coda_dentry_revalidate(struct dentry *de, int flags) return 1; } - /* propagate for a flush */ - if (cii->c_flags & C_FLUSH) - coda_flag_inode_children(inode, C_FLUSH); - /* clear the flags. */ cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 49359f260..604e906a2 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -22,8 +22,6 @@ #include <asm/uaccess.h> #include <linux/fs.h> -#include <linux/stat.h> -#include <asm/uaccess.h> #include <linux/vmalloc.h> #include <asm/segment.h> @@ -75,6 +73,9 @@ static struct super_block * coda_read_super(struct super_block *sb, if ( sbi->sbi_sb ) { printk("Already mounted\n"); + unlock_super(sb); + EXIT; + MOD_DEC_USE_COUNT; return NULL; } @@ -98,7 +99,6 @@ static struct super_block * coda_read_super(struct super_block *sb, printk("coda_read_super: coda_get_rootfid failed with %d\n", error); sb->s_dev = 0; - unlock_super(sb); goto error; } printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid)); @@ -108,7 +108,6 @@ static struct super_block * coda_read_super(struct super_block *sb, if ( error || !root ) { printk("Failure of coda_cnode_make for root: error %d\n", error); sb->s_dev = 0; - unlock_super(sb); goto error; } @@ -121,6 +120,7 @@ static struct super_block * coda_read_super(struct super_block *sb, return sb; error: + unlock_super(sb); EXIT; MOD_DEC_USE_COUNT; if (sbi) { @@ -145,7 +145,6 @@ static void coda_put_super(struct super_block *sb) sb->s_dev = 0; coda_cache_clear_all(sb); sb_info = coda_sbp(sb); -/* sb_info->sbi_vcomm->vc_inuse = 0; You can not do this: psdev_release would see usagecount == 0 and would refuse to decrease MOD_USE_COUNT --pavel */ coda_super_info.sbi_sb = NULL; printk("Coda: Bye bye.\n"); memset(sb_info, 0, sizeof(* sb_info)); @@ -209,7 +208,7 @@ static void coda_delete_inode(struct inode *inode) EXIT; } -static int coda_notify_change(struct dentry *de, struct iattr *iattr) +static int coda_notify_change(struct dentry *de, struct iattr *iattr) { struct inode *inode = de->d_inode; struct coda_inode_info *cii; @@ -238,21 +237,32 @@ static int coda_notify_change(struct dentry *de, struct iattr *iattr) return error; } -/* we need _something_ for this routine. Let's mimic AFS */ static int coda_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; + int error; + + memset(&tmp, 0, sizeof(struct statfs)); + + error = venus_statfs(sb, &tmp); + if (error) { + /* fake something like AFS does */ + tmp.f_blocks = 9000000; + tmp.f_bfree = 9000000; + tmp.f_bavail = 9000000; + tmp.f_files = 9000000; + tmp.f_ffree = 9000000; + } + + /* and fill in the rest */ tmp.f_type = CODA_SUPER_MAGIC; tmp.f_bsize = 1024; - tmp.f_blocks = 9000000; - tmp.f_bfree = 9000000; - tmp.f_bavail = 9000000 ; - tmp.f_files = 9000000; - tmp.f_ffree = 9000000; - tmp.f_namelen = 0; + tmp.f_namelen = CODA_MAXNAMLEN; + copy_to_user(buf, &tmp, bufsiz); + return 0; } diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c index 79a4c7ebb..e35174e5c 100644 --- a/fs/coda/sysctl.c +++ b/fs/coda/sysctl.c @@ -71,6 +71,7 @@ struct coda_permission_stats coda_permission_stat; struct coda_cache_inv_stats coda_cache_inv_stat; struct coda_upcall_stats_entry coda_upcall_stat[CODA_NCALLS]; struct coda_upcallstats coda_callstats; +int coda_upcall_timestamping = 0; /* keep this in sync with coda.h! */ char *coda_upcall_names[] = { @@ -103,9 +104,12 @@ char *coda_upcall_names[] = { "purgeuser ", /* 26 */ "zapfile ", /* 27 */ "zapdir ", /* 28 */ - "zapvnode ", /* 28 */ + "noop2 ", /* 29 */ "purgefid ", /* 30 */ - "open_by_path" /* 31 */ + "open_by_path", /* 31 */ + "resolve ", /* 32 */ + "reintegrate ", /* 33 */ + "statfs " /* 34 */ }; @@ -133,8 +137,7 @@ void reset_coda_cache_inv_stats( void ) void do_time_stats( struct coda_upcall_stats_entry * pentry, unsigned long runtime ) { - - unsigned long time = runtime * 1000 /HZ; /* time in ms */ + unsigned long time = runtime; /* time in us */ CDEBUG(D_SPECIAL, "time: %ld\n", time); if ( pentry->count == 0 ) { @@ -221,9 +224,12 @@ int do_reset_coda_vfs_stats( ctl_table * table, int write, struct file * filp, { if ( write ) { reset_coda_vfs_stats(); + + filp->f_pos += *lenp; + } else { + *lenp = 0; } - - *lenp = 0; + return 0; } @@ -232,10 +238,19 @@ int do_reset_coda_upcall_stats( ctl_table * table, int write, size_t * lenp ) { if ( write ) { + if (*lenp > 0) { + char c; + if (get_user(c, (char *)buffer)) + return -EFAULT; + coda_upcall_timestamping = (c == '1'); + } reset_coda_upcall_stats(); + + filp->f_pos += *lenp; + } else { + *lenp = 0; } - - *lenp = 0; + return 0; } @@ -245,9 +260,12 @@ int do_reset_coda_permission_stats( ctl_table * table, int write, { if ( write ) { reset_coda_permission_stats(); + + filp->f_pos += *lenp; + } else { + *lenp = 0; } - - *lenp = 0; + return 0; } @@ -257,9 +275,12 @@ int do_reset_coda_cache_inv_stats( ctl_table * table, int write, { if ( write ) { reset_coda_cache_inv_stats(); + + filp->f_pos += *lenp; + } else { + *lenp = 0; } - *lenp = 0; return 0; } @@ -347,12 +368,12 @@ int coda_upcall_stats_get_info( char * buffer, char ** start, off_t offset, if ( offset < 160) len += sprintf( buffer + len,"%-79s\n", "======================"); if ( offset < 240) - len += sprintf( buffer + len,"%-79s\n", "upcall\t\t count\tavg time(ms)\tstd deviation(ms)"); + len += sprintf( buffer + len,"%-79s\n", "upcall count avg time(us) std deviation(us)"); if ( offset < 320) - len += sprintf( buffer + len,"%-79s\n", "------\t\t -----\t------------\t-----------------"); + len += sprintf( buffer + len,"%-79s\n", "------ ----- ------------ -----------------"); pos = 320; for ( i = 0 ; i < CODA_NCALLS ; i++ ) { - tmplen += sprintf(tmpbuf,"%s\t%9d\t%10ld\t%10ld", + tmplen += sprintf(tmpbuf,"%s %9d %10ld %10ld", coda_upcall_names[i], coda_upcall_stat[i].count, get_time_average(&coda_upcall_stat[i]), @@ -499,6 +520,7 @@ static void coda_proc_modcount(struct inode *inode, int fill) MOD_INC_USE_COUNT; else MOD_DEC_USE_COUNT; + } #endif diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index a0c1092b2..62fd62e35 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -580,6 +580,33 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, return error; } +int venus_statfs(struct super_block *sb, struct statfs *sfs) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = max(INSIZE(statfs), OUTSIZE(statfs)); + UPARG(CODA_STATFS); + + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + + if (!error) { + sfs->f_blocks = outp->coda_statfs.stat.f_blocks; + sfs->f_bfree = outp->coda_statfs.stat.f_bfree; + sfs->f_bavail = outp->coda_statfs.stat.f_bavail; + sfs->f_files = outp->coda_statfs.stat.f_files; + sfs->f_ffree = outp->coda_statfs.stat.f_ffree; + } else { + printk("coda_statfs: Venus returns: %d\n", error); + } + + if (inp) CODA_FREE(inp, insize); + CDEBUG(D_INODE, " result %d\n",error); + EXIT; + return error; +} + /* * coda_upcall and coda_downcall routines. * @@ -588,10 +615,12 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) { DECLARE_WAITQUEUE(wait, current); - unsigned long posttime; + struct timeval begin = { 0, 0 }, end = { 0, 0 }; vmp->uc_posttime = jiffies; - posttime = jiffies; + + if (coda_upcall_timestamping) + do_gettimeofday(&begin); add_wait_queue(&vmp->uc_sleep, &wait); for (;;) { @@ -620,9 +649,20 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) remove_wait_queue(&vmp->uc_sleep, &wait); current->state = TASK_RUNNING; - CDEBUG(D_SPECIAL, "posttime: %ld, returned: %ld\n", posttime, jiffies-posttime); - return (jiffies - posttime); + if (coda_upcall_timestamping && begin.tv_sec != 0) { + do_gettimeofday(&end); + + if (end.tv_usec < begin.tv_usec) { + end.tv_usec += 1000000; end.tv_sec--; + } + end.tv_sec -= begin.tv_sec; + end.tv_usec -= begin.tv_usec; + } + + CDEBUG(D_SPECIAL, "begin: %ld.%06ld, elapsed: %ld.%06ld\n", + begin.tv_sec, begin.tv_usec, end.tv_sec, end.tv_usec); + return ((end.tv_sec * 1000000) + end.tv_usec); } diff --git a/fs/devices.c b/fs/devices.c index 7bdadd5fb..a436f60de 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -277,6 +277,7 @@ struct inode_operations blkdev_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ @@ -333,6 +334,7 @@ struct inode_operations chrdev_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ diff --git a/fs/dquot.c b/fs/dquot.c index dfef0a63a..9dfbac082 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -577,32 +577,35 @@ we_slept: static void add_dquot_ref(kdev_t dev, short type) { struct super_block *sb = get_super(dev); - struct file *filp; + struct list_head *p; struct inode *inode; if (!sb || !sb->dq_op) return; /* nothing to do */ - for (filp = inuse_filps; filp; filp = filp->f_next) { + file_list_lock(); + for (p = sb->s_files.next; p != &sb->s_files; p = p->next) { + struct file *filp = list_entry(p, struct file, f_list); if (!filp->f_dentry) continue; - if (filp->f_dentry->d_sb != sb) - continue; inode = filp->f_dentry->d_inode; if (!inode) continue; /* N.B. race problem -- filp could become unused */ if (filp->f_mode & FMODE_WRITE) { + file_list_unlock(); sb->dq_op->initialize(inode, type); inode->i_flags |= S_QUOTA; + file_list_lock(); } } + file_list_unlock(); } static void reset_dquot_ptrs(kdev_t dev, short type) { struct super_block *sb = get_super(dev); - struct file *filp; + struct list_head *p; struct inode *inode; struct dquot *dquot; int cnt; @@ -614,11 +617,11 @@ restart: /* free any quota for unused dentries */ shrink_dcache_sb(sb); - for (filp = inuse_filps; filp; filp = filp->f_next) { + file_list_lock(); + for (p = sb->s_files.next; p != &sb->s_files; p = p->next) { + struct file *filp = list_entry(p, struct file, f_list); if (!filp->f_dentry) continue; - if (filp->f_dentry->d_sb != sb) - continue; inode = filp->f_dentry->d_inode; if (!inode) continue; @@ -637,12 +640,14 @@ restart: inode->i_flags &= ~S_QUOTA; put_it: if (dquot != NODQUOT) { + file_list_unlock(); dqput(dquot); /* we may have blocked ... */ goto restart; } } } + file_list_unlock(); } static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number) @@ -119,8 +119,12 @@ int open_dentry(struct dentry * dentry, int mode) { struct inode * inode = dentry->d_inode; struct file * f; + struct list_head * l = NULL; int fd, error; + if (inode->i_sb) + l = &inode->i_sb->s_files; + error = -EINVAL; if (!inode->i_op || !inode->i_op->default_file_ops) goto out; @@ -141,6 +145,7 @@ int open_dentry(struct dentry * dentry, int mode) if (error) goto out_filp; } + file_move(f, l); fd_install(fd, f); dget(dentry); } @@ -465,8 +470,7 @@ static inline void flush_old_files(struct files_struct * files) i = j * __NFDBITS; if (i >= files->max_fds) break; - set = files->close_on_exec.fds_bits[j]; - files->close_on_exec.fds_bits[j] = 0; + set = xchg(&files->close_on_exec.fds_bits[j], 0); j++; for ( ; set ; i++,set >>= 1) { if (set & 1) @@ -559,7 +563,7 @@ int prepare_binprm(struct linux_binprm *bprm) if ((retval = permission(inode, MAY_EXEC)) != 0) return retval; /* better not execute files which are being written to */ - if (inode->i_writecount > 0) + if (atomic_read(&inode->i_writecount) > 0) return -ETXTBSY; bprm->e_uid = current->euid; diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index ec21b0c4e..4d54fd354 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -27,28 +27,35 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, case EXT2_IOC_GETFLAGS: flags = inode->u.ext2_i.i_flags & EXT2_FL_USER_VISIBLE; return put_user(inode->u.ext2_i.i_flags, (int *) arg); - case EXT2_IOC_SETFLAGS: + case EXT2_IOC_SETFLAGS: { + unsigned int oldflags; + + if (IS_RDONLY(inode)) + return -EROFS; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + if (get_user(flags, (int *) arg)) return -EFAULT; - flags = flags & EXT2_FL_USER_MODIFIABLE; + + oldflags = inode->u.ext2_i.i_flags; + /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by - * the super user when the security level is zero. + * the relevant capability. + * + * This test looks nicer. Thanks to Pauline Middelink */ - if ((flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) ^ - (inode->u.ext2_i.i_flags & - (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) { - /* This test looks nicer. Thanks to Pauline Middelink */ + if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) return -EPERM; - } else - if ((current->fsuid != inode->i_uid) && - !capable(CAP_FOWNER)) - return -EPERM; - if (IS_RDONLY(inode)) - return -EROFS; - inode->u.ext2_i.i_flags = (inode->u.ext2_i.i_flags & - ~EXT2_FL_USER_MODIFIABLE) | flags; + } + + flags = flags & EXT2_FL_USER_MODIFIABLE; + flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE; + inode->u.ext2_i.i_flags = flags; + if (flags & EXT2_SYNC_FL) inode->i_flags |= MS_SYNCHRONOUS; else @@ -68,6 +75,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return 0; + } case EXT2_IOC_GETVERSION: return put_user(inode->i_generation, (int *) arg); case EXT2_IOC_SETVERSION: diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 594baa0e2..82d29a0df 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -295,6 +295,8 @@ static int fat_readdirx( char bufname[14]; char *ptname = bufname; int dotoffset = 0; + unsigned long *furrfu = &lpos; + unsigned long dummy; cpos = filp->f_pos; /* Fake . and .. for the root directory. */ @@ -305,8 +307,11 @@ static int fat_readdirx( cpos++; filp->f_pos++; } - if (cpos == 2) + if (cpos == 2) { + dummy = 2; + furrfu = &dummy; cpos = 0; + } } if (cpos & (sizeof(struct msdos_dir_entry)-1)) return -ENOENT; @@ -440,7 +445,7 @@ ParseLong: if (!long_slots||shortnames) { if (both) bufname[i] = '\0'; - if (filldir(dirent, bufname, i, lpos, inum) < 0) + if (filldir(dirent, bufname, i, *furrfu, inum) < 0) goto FillFailed; } else { char longname[275]; @@ -451,11 +456,12 @@ ParseLong: memcpy(&longname[long_len+1], bufname, i); long_len += i; } - if (filldir(dirent, longname, long_len, lpos, inum) < 0) + if (filldir(dirent, longname, long_len, *furrfu, inum) < 0) goto FillFailed; } RecEnd: + furrfu = &lpos; filp->f_pos = cpos; goto GetNew; EODir: diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c index 0494ad013..80ec56b54 100644 --- a/fs/fat/mmap.c +++ b/fs/fat/mmap.c @@ -87,8 +87,7 @@ struct vm_operations_struct fat_file_mmap = { NULL, /* advise */ fat_file_mmap_nopage, /* nopage */ NULL, /* wppage */ - NULL, /* swapout */ - NULL, /* swapin */ + NULL /* swapout */ }; /* diff --git a/fs/fcntl.c b/fs/fcntl.c index 666d88881..d12e66244 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -12,27 +12,19 @@ extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); -static inline int dupfd(unsigned int fd, unsigned int arg) +static inline int dupfd(struct file *file, unsigned int arg) { struct files_struct * files = current->files; - struct file * file; int error; - error = -EINVAL; - if (arg >= NR_OPEN) - goto out; - - error = -EBADF; - file = fget(fd); - if (!file) - goto out; - error = -EMFILE; + write_lock(&files->file_lock); arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg); if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur) goto out_putf; FD_SET(arg, &files->open_fds); FD_CLR(arg, &files->close_on_exec); + write_unlock(&files->file_lock); fd_install(arg, file); error = arg; out: @@ -46,31 +38,35 @@ out_putf: asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) { int err = -EBADF; + struct file * file; - lock_kernel(); - if (!fcheck(oldfd)) - goto out; + read_lock(¤t->files->file_lock); + if (!(file = fcheck(oldfd))) + goto out_unlock; err = newfd; if (newfd == oldfd) - goto out; + goto out_unlock; err = -EBADF; if (newfd >= NR_OPEN) - goto out; /* following POSIX.1 6.2.1 */ + goto out_unlock; /* following POSIX.1 6.2.1 */ + get_file(file); + read_unlock(¤t->files->file_lock); sys_close(newfd); - err = dupfd(oldfd, newfd); + err = dupfd(file, newfd); out: - unlock_kernel(); return err; +out_unlock: + read_unlock(¤t->files->file_lock); + goto out; } asmlinkage int sys_dup(unsigned int fildes) { - int ret; - - lock_kernel(); - ret = dupfd(fildes, 0); - unlock_kernel(); + int ret = -EBADF; + struct file * file = fget(fildes); + if (file) + ret = dupfd(file, 0); return ret; } @@ -107,14 +103,18 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) struct file * filp; long err = -EBADF; - lock_kernel(); filp = fget(fd); if (!filp) goto out; err = 0; + lock_kernel(); switch (cmd) { case F_DUPFD: - err = dupfd(fd, arg); + err = -EINVAL; + if (arg < NR_OPEN) { + get_file(filp); + err = dupfd(filp, arg); + } break; case F_GETFD: err = FD_ISSET(fd, ¤t->files->close_on_exec); @@ -178,8 +178,8 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) break; } fput(filp); -out: unlock_kernel(); +out: return err; } @@ -179,6 +179,7 @@ struct inode_operations fifo_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ diff --git a/fs/file_table.c b/fs/file_table.c index 80c3a08ba..cb9ef16e9 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -9,6 +9,7 @@ #include <linux/slab.h> #include <linux/file.h> #include <linux/init.h> +#include <linux/smp_lock.h> /* SLAB cache for filp's. */ static kmem_cache_t *filp_cache; @@ -18,37 +19,12 @@ int nr_files = 0; /* read only */ int nr_free_files = 0; /* read only */ int max_files = NR_FILE;/* tunable */ -/* Free list management, if you are here you must have f_count == 0 */ -static struct file * free_filps = NULL; - -static void insert_file_free(struct file *file) -{ - if((file->f_next = free_filps) != NULL) - free_filps->f_pprev = &file->f_next; - free_filps = file; - file->f_pprev = &free_filps; - nr_free_files++; -} - -/* The list of in-use filp's must be exported (ugh...) */ -struct file *inuse_filps = NULL; - -static inline void put_inuse(struct file *file) -{ - if((file->f_next = inuse_filps) != NULL) - inuse_filps->f_pprev = &file->f_next; - inuse_filps = file; - file->f_pprev = &inuse_filps; -} - -/* It does not matter which list it is on. */ -static inline void remove_filp(struct file *file) -{ - if(file->f_next) - file->f_next->f_pprev = file->f_pprev; - *file->f_pprev = file->f_next; -} - +/* Here the new files go */ +static LIST_HEAD(anon_list); +/* And here the free ones sit */ +static LIST_HEAD(free_list); +/* public *and* exported. Not pretty! */ +spinlock_t files_lock = SPIN_LOCK_UNLOCKED; void __init file_table_init(void) { @@ -67,24 +43,30 @@ void __init file_table_init(void) /* Find an unused file structure and return a pointer to it. * Returns NULL, if there are no more free file structures or * we run out of memory. + * + * SMP-safe. */ struct file * get_empty_filp(void) { static int old_max = 0; struct file * f; + file_list_lock(); if (nr_free_files > NR_RESERVED_FILES) { used_one: - f = free_filps; - remove_filp(f); + f = list_entry(free_list.next, struct file, f_list); + list_del(&f->f_list); nr_free_files--; new_one: + file_list_unlock(); memset(f, 0, sizeof(*f)); - atomic_set(&f->f_count, 1); + atomic_set(&f->f_count,1); f->f_version = ++event; f->f_uid = current->fsuid; f->f_gid = current->fsgid; - put_inuse(f); + file_list_lock(); + list_add(&f->f_list, &anon_list); + file_list_unlock(); return f; } /* @@ -96,7 +78,9 @@ struct file * get_empty_filp(void) * Allocate a new one if we're below the limit. */ if (nr_files < max_files) { + file_list_unlock(); f = kmem_cache_alloc(filp_cache, SLAB_KERNEL); + file_list_lock(); if (f) { nr_files++; goto new_one; @@ -108,6 +92,7 @@ struct file * get_empty_filp(void) printk("VFS: file-max limit %d reached\n", max_files); old_max = max_files; } + file_list_unlock(); return NULL; } @@ -131,20 +116,77 @@ int init_private_file(struct file *filp, struct dentry *dentry, int mode) return 0; } -void fput(struct file *file) +void _fput(struct file *file) { - if (atomic_dec_and_test(&file->f_count)) { - locks_remove_flock(file); - __fput(file); - remove_filp(file); - insert_file_free(file); - } + atomic_inc(&file->f_count); + + lock_kernel(); + locks_remove_flock(file); /* Still need the */ + __fput(file); /* big lock here. */ + unlock_kernel(); + + atomic_set(&file->f_count, 0); + file_list_lock(); + list_del(&file->f_list); + list_add(&file->f_list, &free_list); + nr_free_files++; + file_list_unlock(); } +/* Here. put_filp() is SMP-safe now. */ + void put_filp(struct file *file) { - if (atomic_dec_and_test(&file->f_count)) { - remove_filp(file); - insert_file_free(file); + if(atomic_dec_and_test(&file->f_count)) { + file_list_lock(); + list_del(&file->f_list); + list_add(&file->f_list, &free_list); + nr_free_files++; + file_list_unlock(); + } +} + +void file_move(struct file *file, struct list_head *list) +{ + if (!list) + return; + file_list_lock(); + list_del(&file->f_list); + list_add(&file->f_list, list); + file_list_unlock(); +} + +void file_moveto(struct file *new, struct file *old) +{ + file_list_lock(); + list_del(&new->f_list); + list_add(&new->f_list, &old->f_list); + file_list_unlock(); +} + +int fs_may_remount_ro(struct super_block *sb) +{ + struct list_head *p; + + /* Check that no files are currently opened for writing. */ + file_list_lock(); + for (p = sb->s_files.next; p != &sb->s_files; p = p->next) { + struct file *file = list_entry(p, struct file, f_list); + struct inode *inode = file->f_dentry->d_inode; + if (!inode) + continue; + + /* File with pending delete? */ + if (inode->i_nlink == 0) + goto too_bad; + + /* Writable file? */ + if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) + return 0; } + file_list_unlock(); + return 1; /* Tis' cool bro. */ +too_bad: + file_list_unlock(); + return 0; } diff --git a/fs/inode.c b/fs/inode.c index 01fc64d23..242696d87 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -130,6 +130,7 @@ static inline void init_once(struct inode * inode) INIT_LIST_HEAD(&inode->i_hash); INIT_LIST_HEAD(&inode->i_dentry); sema_init(&inode->i_sem, 1); + spin_lock_init(&inode->i_shared_lock); } static inline void write_inode(struct inode *inode) @@ -523,7 +524,7 @@ void clean_inode(struct inode *inode) inode->i_sock = 0; inode->i_op = NULL; inode->i_nlink = 1; - inode->i_writecount = 0; + atomic_set(&inode->i_writecount, 0); inode->i_size = 0; inode->i_generation = 0; memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); @@ -818,31 +819,6 @@ void __init inode_init(void) max_inodes = max; } -/* This belongs in file_table.c, not here... */ -int fs_may_remount_ro(struct super_block *sb) -{ - struct file *file; - - /* Check that no files are currently opened for writing. */ - for (file = inuse_filps; file; file = file->f_next) { - struct inode *inode; - if (!file->f_dentry) - continue; - inode = file->f_dentry->d_inode; - if (!inode || inode->i_sb != sb) - continue; - - /* File with pending delete? */ - if (inode->i_nlink == 0) - return 0; - - /* Writable file? */ - if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) - return 0; - } - return 1; /* Tis' cool bro. */ -} - void update_atime (struct inode *inode) { if ( IS_NOATIME (inode) ) return; diff --git a/fs/ioctl.c b/fs/ioctl.c index e9f8e09b6..b9f2363e6 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -25,6 +25,8 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) return -EBADF; if (inode->i_op->get_block == NULL) return -EINVAL; + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; if ((error = get_user(block, (int *) arg)) != 0) return error; @@ -52,11 +54,11 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) unsigned int flag; int on, error = -EBADF; - lock_kernel(); filp = fget(fd); if (!filp) goto out; error = 0; + lock_kernel(); switch (cmd) { case FIOCLEX: FD_SET(fd, ¤t->files->close_on_exec); @@ -107,8 +109,8 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); } fput(filp); + unlock_kernel(); out: - unlock_kernel(); return error; } diff --git a/fs/locks.c b/fs/locks.c index 728985c34..5cb324374 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -341,7 +341,11 @@ int fcntl_getlk(unsigned int fd, struct flock *l) error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) goto out_putf; - fl = &file_lock; + else if (error == LOCK_USE_CLNT) + /* Bypass for NFS with no locking - 2.0.36 compat */ + fl = posix_test_lock(filp, &file_lock); + else + fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { fl = posix_test_lock(filp, &file_lock); } @@ -402,14 +406,17 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) * and shared. */ if (IS_MANDLOCK(inode) && - (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID && - inode->i_mmap) { - struct vm_area_struct *vma = inode->i_mmap; - error = -EAGAIN; - do { - if (vma->vm_flags & VM_MAYSHARE) - goto out_putf; - } while ((vma = vma->vm_next_share) != NULL); + (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { + struct vm_area_struct *vma; + spin_lock(&inode->i_shared_lock); + for(vma = inode->i_mmap;vma;vma = vma->vm_next_share) { + if (!(vma->vm_flags & VM_MAYSHARE)) + continue; + spin_unlock(&inode->i_shared_lock); + error = -EAGAIN; + goto out_putf; + } + spin_unlock(&inode->i_shared_lock); } error = -EINVAL; @@ -546,47 +553,23 @@ posix_test_lock(struct file *filp, struct file_lock *fl) return (cfl); } -int locks_verify_locked(struct inode *inode) -{ - /* Candidates for mandatory locking have the setgid bit set - * but no group execute bit - an otherwise meaningless combination. - */ - if (IS_MANDLOCK(inode) && - (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) - return (locks_mandatory_locked(inode)); - return (0); -} - -int locks_verify_area(int read_write, struct inode *inode, struct file *filp, - loff_t offset, size_t count) -{ - /* Candidates for mandatory locking have the setgid bit set - * but no group execute bit - an otherwise meaningless combination. - */ - if (IS_MANDLOCK(inode) && (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { - int retval; - lock_kernel(); - retval = locks_mandatory_area(read_write, inode, filp, offset, count); - unlock_kernel(); - return retval; - } - return 0; -} - int locks_mandatory_locked(struct inode *inode) { fl_owner_t owner = current->files; struct file_lock *fl; - /* Search the lock list for this inode for any POSIX locks. + /* + * Search the lock list for this inode for any POSIX locks. */ + lock_kernel(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) continue; if (fl->fl_owner != owner) - return (-EAGAIN); + break; } - return (0); + unlock_kernel(); + return fl ? -EAGAIN : 0; } int locks_mandatory_area(int read_write, struct inode *inode, @@ -595,6 +578,7 @@ int locks_mandatory_area(int read_write, struct inode *inode, { struct file_lock *fl; struct file_lock tfl; + int error; memset(&tfl, 0, sizeof(tfl)); @@ -607,31 +591,39 @@ int locks_mandatory_area(int read_write, struct inode *inode, tfl.fl_start = offset; tfl.fl_end = offset + count - 1; + error = 0; + lock_kernel(); + repeat: /* Search the lock list for this inode for locks that conflict with * the proposed read/write. */ - for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + for (fl = inode->i_flock; ; fl = fl->fl_next) { + error = 0; + if (!fl) + break; if (!(fl->fl_flags & FL_POSIX)) continue; /* Block for writes against a "read" lock, * and both reads and writes against a "write" lock. */ if (posix_locks_conflict(fl, &tfl)) { + error = -EAGAIN; if (filp && (filp->f_flags & O_NONBLOCK)) - return (-EAGAIN); + break; + error = -ERESTARTSYS; if (signal_pending(current)) - return (-ERESTARTSYS); + break; + error = -EDEADLK; if (posix_locks_deadlock(&tfl, fl)) - return (-EDEADLK); + break; locks_insert_block(fl, &tfl); interruptible_sleep_on(&tfl.fl_wait); locks_delete_block(fl, &tfl); - if (signal_pending(current)) - return (-ERESTARTSYS); - /* If we've been sleeping someone might have + /* + * If we've been sleeping someone might have * changed the permissions behind our back. */ if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID) @@ -639,7 +631,8 @@ repeat: goto repeat; } } - return (0); + unlock_kernel(); + return error; } /* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX diff --git a/fs/minix/file.c b/fs/minix/file.c index 14d478bde..9eab1fb17 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -7,7 +7,6 @@ */ #include <linux/sched.h> -#include <linux/minix_fs.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/fcntl.h> @@ -25,7 +24,6 @@ #define MAX(a,b) (((a)>(b))?(a):(b)) #include <linux/fs.h> -#include <linux/minix_fs.h> /* * Write to a file (through the page cache). diff --git a/fs/namei.c b/fs/namei.c index 9769ce1bb..e39cd24e1 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -171,18 +171,22 @@ int permission(struct inode * inode,int mask) * 0: no writers, no VM_DENYWRITE mappings * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist * > 0: (i_writecount) users are writing to the file. + * + * WARNING: as soon as we will move get_write_access(), do_mmap() or + * prepare_binfmt() out of the big lock we will need a spinlock protecting + * the checks in all 3. For the time being it is not needed. */ int get_write_access(struct inode * inode) { - if (inode->i_writecount < 0) + if (atomic_read(&inode->i_writecount) < 0) return -ETXTBSY; - inode->i_writecount++; + atomic_inc(&inode->i_writecount); return 0; } void put_write_access(struct inode * inode) { - inode->i_writecount--; + atomic_dec(&inode->i_writecount); } /* diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 1f92a89fb..cf4c13fde 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -19,7 +19,6 @@ #include <linux/mm.h> #include <asm/uaccess.h> #include <asm/byteorder.h> -#include <linux/errno.h> #include <linux/locks.h> #include <linux/ncp_fs.h> diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index c9254f4f6..776e9366b 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -98,8 +98,7 @@ struct vm_operations_struct ncp_file_mmap = NULL, /* advise */ ncp_file_mmap_nopage, /* nopage */ NULL, /* wppage */ - NULL, /* swapout */ - NULL, /* swapin */ + NULL /* swapout */ }; diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 843f6b23e..899b62c82 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -90,8 +90,9 @@ nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page) /* Set up arguments and perform rpc call */ nfs_readreq_setup(&rqst, NFS_FH(dentry), offset, buffer, rsize); - result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ, - &rqst.ra_args, &rqst.ra_res, flags); + lock_kernel(); + result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ, &rqst.ra_args, &rqst.ra_res, flags); + unlock_kernel(); /* * Even if we had a partial success we can't mark the page diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 8b63cbf66..77c1db091 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -305,6 +305,7 @@ create_write_request(struct file * file, struct page *page, unsigned int offset, goto out_req; /* Put the task on inode's writeback request list. */ + get_file(file); wreq->wb_file = file; wreq->wb_pid = current->pid; wreq->wb_page = page; @@ -467,7 +468,6 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig * The IO completion will then free the page and the dentry. */ get_page(page); - atomic_inc(&file->f_count); /* Schedule request */ synchronous = schedule_write_request(req, synchronous); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index fd7a5b227..150acc6d9 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -18,7 +18,6 @@ #include <linux/fcntl.h> #include <linux/net.h> #include <linux/in.h> -#include <linux/version.h> #include <linux/unistd.h> #include <linux/malloc.h> #include <linux/proc_fs.h> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 63294ae29..14cb7d50f 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -585,7 +585,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, * nice and simple solution (IMHO), and it seems to * work:-) */ - if (EX_WGATHER(exp) && (inode->i_writecount > 1 + if (EX_WGATHER(exp) && (atomic_read(&inode->i_writecount) > 1 || (last_ino == inode->i_ino && last_dev == inode->i_dev))) { #if 0 interruptible_sleep_on_timeout(&inode->i_wait, 10 * HZ / 1000); @@ -37,29 +37,21 @@ asmlinkage int sys_statfs(const char * path, struct statfs * buf) asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf) { struct file * file; - struct inode * inode; - struct dentry * dentry; struct super_block * sb; int error; - lock_kernel(); error = -EBADF; file = fget(fd); if (!file) goto out; - error = -ENOENT; - if (!(dentry = file->f_dentry)) - goto out_putf; - if (!(inode = dentry->d_inode)) - goto out_putf; error = -ENODEV; - sb = inode->i_sb; + sb = file->f_dentry->d_inode->i_sb; + lock_kernel(); if (sb && sb->s_op && sb->s_op->statfs) error = sb->s_op->statfs(sb, buf, sizeof(struct statfs)); -out_putf: + unlock_kernel(); fput(file); out: - unlock_kernel(); return error; } @@ -143,7 +135,6 @@ asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length) struct file * file; int error; - lock_kernel(); error = -EBADF; file = fget(fd); if (!file) @@ -162,12 +153,13 @@ asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length) error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, length<inode->i_size ? length : inode->i_size, abs(inode->i_size - length)); + lock_kernel(); if (!error) error = do_truncate(dentry, length); + unlock_kernel(); out_putf: fput(file); out: - unlock_kernel(); return error; } @@ -361,8 +353,6 @@ asmlinkage int sys_fchdir(unsigned int fd) struct inode *inode; int error; - lock_kernel(); - error = -EBADF; file = fget(fd); if (!file) @@ -378,16 +368,17 @@ asmlinkage int sys_fchdir(unsigned int fd) if (!S_ISDIR(inode->i_mode)) goto out_putf; + lock_kernel(); error = permission(inode, MAY_EXEC); if (!error) { struct dentry *tmp = current->fs->pwd; current->fs->pwd = dget(dentry); dput(tmp); } + unlock_kernel(); out_putf: fput(file); out: - unlock_kernel(); return error; } @@ -439,7 +430,6 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) int err = -EBADF; struct iattr newattrs; - lock_kernel(); file = fget(fd); if (!file) goto out; @@ -460,12 +450,13 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + lock_kernel(); err = notify_change(dentry, &newattrs); + unlock_kernel(); out_putf: fput(file); out: - unlock_kernel(); return err; } @@ -601,17 +592,17 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) struct file * file; int error = -EBADF; - lock_kernel(); file = fget(fd); if (!file) goto out; error = -ENOENT; + lock_kernel(); if ((dentry = file->f_dentry) != NULL) error = chown_common(dentry, user, group); + unlock_kernel(); fput(file); out: - unlock_kernel(); return error; } @@ -663,6 +654,8 @@ struct file *filp_open(const char * filename, int flags, int mode) f->f_op = NULL; if (inode->i_op) f->f_op = inode->i_op->default_file_ops; + if (inode->i_sb) + file_move(f, &inode->i_sb->s_files); if (f->f_op && f->f_op->open) { error = f->f_op->open(inode,f); if (error) @@ -693,6 +686,8 @@ int get_unused_fd(void) int fd, error; error = -EMFILE; + + write_lock(&files->file_lock); fd = find_first_zero_bit(&files->open_fds, NR_OPEN); /* * N.B. For clone tasks sharing a files structure, this test @@ -715,12 +710,15 @@ int get_unused_fd(void) error = fd; out: + write_unlock(&files->file_lock); return error; } inline void put_unused_fd(unsigned int fd) { + write_lock(¤t->files->file_lock); FD_CLR(fd, ¤t->files->open_fds); + write_unlock(¤t->files->file_lock); } asmlinkage int sys_open(const char * filename, int flags, int mode) @@ -731,17 +729,18 @@ asmlinkage int sys_open(const char * filename, int flags, int mode) tmp = getname(filename); fd = PTR_ERR(tmp); if (!IS_ERR(tmp)) { - lock_kernel(); fd = get_unused_fd(); if (fd >= 0) { - struct file * f = filp_open(tmp, flags, mode); + struct file * f; + lock_kernel(); + f = filp_open(tmp, flags, mode); + unlock_kernel(); error = PTR_ERR(f); if (IS_ERR(f)) goto out_error; fd_install(fd, f); } out: - unlock_kernel(); putname(tmp); } return fd; @@ -790,7 +789,7 @@ int filp_close(struct file *filp, fl_owner_t id) int retval; struct dentry *dentry = filp->f_dentry; - if (!atomic_read(&filp->f_count)) { + if (!file_count(filp)) { printk("VFS: Close: file count is 0\n"); return 0; } @@ -812,19 +811,24 @@ asmlinkage int sys_close(unsigned int fd) { int error; struct file * filp; + struct files_struct * files = current->files; - lock_kernel(); error = -EBADF; - filp = fcheck(fd); - if (filp) { - struct files_struct * files = current->files; - files->fd[fd] = NULL; - put_unused_fd(fd); - FD_CLR(fd, &files->close_on_exec); - error = filp_close(filp, files); - } + write_lock(&files->file_lock); + filp = frip(fd); + if (!filp) + goto out_unlock; + FD_CLR(fd, &files->close_on_exec); + write_unlock(&files->file_lock); + put_unused_fd(fd); + lock_kernel(); + error = filp_close(filp, files); unlock_kernel(); +out: return error; +out_unlock: + write_unlock(&files->file_lock); + goto out; } /* @@ -835,14 +839,14 @@ asmlinkage int sys_vhangup(void) { int ret = -EPERM; - lock_kernel(); if (!capable(CAP_SYS_TTY_CONFIG)) goto out; /* If there is a controlling tty, hang it up */ + lock_kernel(); if (current->tty) tty_vhangup(current->tty); + unlock_kernel(); ret = 0; out: - unlock_kernel(); return ret; } @@ -27,7 +27,7 @@ /* Florian Coosmann (FGC) ^ current = 1 */ /* Additionally, we now use locking technique. This prevents race condition */ /* in case of paging and multiple read/write on the same pipe. (FGC) */ - +/* Reads with count = 0 should always return 0. Julian Bradfield 1999-06-07. */ static ssize_t do_pipe_read(struct file * filp, char * buf, size_t count) { @@ -144,9 +144,12 @@ static ssize_t pipe_read(struct file * filp, char * buf, size_t count, loff_t *p { ssize_t retval; + if (ppos != &filp->f_pos) return -ESPIPE; + if ( !count ) return 0; + lock_kernel(); retval = do_pipe_read(filp, buf, count); unlock_kernel(); @@ -480,6 +483,7 @@ struct inode_operations pipe_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ diff --git a/fs/proc/fd.c b/fs/proc/fd.c index a900d01bf..195ca41b8 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -104,7 +104,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) read_lock(&tasklist_lock); file = NULL; p = find_task_by_pid(pid); - if (p) + if (p && p->files) file = fcheck_task(p, fd); read_unlock(&tasklist_lock); @@ -114,7 +114,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) * is NULL */ - if (!file || !file->f_dentry) + if (!file) goto out; ino = (pid << 16) + PROC_PID_FD_DIR + fd; @@ -161,10 +161,9 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) for (fd -= 2 ; p->files && fd < p->files->max_fds; fd++, filp->f_pos++) { - struct file * file = fcheck_task(p, fd); unsigned int i,j; - if (!file || !file->f_dentry) + if (!fcheck_task(p, fd)) continue; j = NUMBUF; diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 970e63a96..a5596e4ee 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -87,13 +87,26 @@ static void proc_delete_inode(struct inode *inode) } } +struct super_block *proc_super_blocks = NULL; + +static void proc_put_super(struct super_block *sb) +{ + struct super_block **p = &proc_super_blocks; + while (*p != sb) { + if (!*p) /* should never happen */ + return; + p = (struct super_block **)&(*p)->u.generic_sbp; + } + *p = (struct super_block *)(*p)->u.generic_sbp; +} + static struct super_operations proc_sops = { proc_read_inode, proc_write_inode, proc_put_inode, proc_delete_inode, /* delete_inode(struct inode *) */ NULL, - NULL, + proc_put_super, NULL, proc_statfs, NULL @@ -323,6 +336,8 @@ struct super_block *proc_read_super(struct super_block *s,void *data, if (!s->s_root) goto out_no_root; parse_options(data, &root_inode->i_uid, &root_inode->i_gid); + s->u.generic_sbp = (void*) proc_super_blocks; + proc_super_blocks = s; unlock_super(s); return s; @@ -385,9 +400,12 @@ void proc_read_inode(struct inode * inode) if (ino & PROC_PID_FD_DIR) { struct file * file; ino &= 0x7fff; + if (!p->files) /* can we ever get here if that's the case? */ + goto out_unlock; + read_lock(&p->files->file_lock); file = fcheck_task(p, ino); if (!file) - goto out_unlock; + goto out_unlock2; inode->i_op = &proc_link_inode_operations; inode->i_size = 64; @@ -396,6 +414,8 @@ void proc_read_inode(struct inode * inode) inode->i_mode |= S_IRUSR | S_IXUSR; if (file->f_mode & 2) inode->i_mode |= S_IWUSR | S_IXUSR; +out_unlock2: + read_unlock(&p->files->file_lock); } out_unlock: /* Defer unlocking until we're done with the task */ diff --git a/fs/proc/link.c b/fs/proc/link.c index 647dc339f..6f5c63ec3 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -119,10 +119,14 @@ static struct dentry * proc_follow_link(struct dentry *dentry, if (ino & PROC_PID_FD_DIR) { struct file * file; ino &= 0x7fff; + if (!p->files) /* shouldn't happen here */ + goto out_unlock; + read_lock(&p->files->file_lock); file = fcheck_task(p, ino); if (!file || !file->f_dentry) goto out_unlock; result = file->f_dentry; + read_unlock(&p->files->file_lock); goto out_dget; } } @@ -146,6 +150,9 @@ static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen) char * tmp = (char*)__get_free_page(GFP_KERNEL), *path, *pattern; int len; + if(tmp==NULL) + return -ENOMEM; + /* Check for special dentries.. */ pattern = NULL; inode = dentry->d_inode; diff --git a/fs/proc/root.c b/fs/proc/root.c index d08860c26..df3c0b049 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -365,24 +365,36 @@ int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) */ static void proc_kill_inodes(int ino) { - struct file *filp; - - /* inuse_filps is protected by the single kernel lock */ - for (filp = inuse_filps; filp; filp = filp->f_next) { - struct dentry * dentry; - struct inode * inode; - - dentry = filp->f_dentry; - if (!dentry) - continue; - if (dentry->d_op != &proc_dentry_operations) - continue; - inode = dentry->d_inode; - if (!inode) - continue; - if (inode->i_ino != ino) - continue; - filp->f_op = NULL; + struct list_head *p; + struct super_block *sb; + + /* + * Actually it's a partial revoke(). We have to go through all + * copies of procfs. proc_super_blocks is protected by the big + * lock for the time being. + */ + for (sb = proc_super_blocks; + sb; + sb = (struct super_block*)sb->u.generic_sbp) { + file_list_lock(); + for (p = sb->s_files.next; p != &sb->s_files; p = p->next) { + struct file * filp = list_entry(p, struct file, f_list); + struct dentry * dentry; + struct inode * inode; + + dentry = filp->f_dentry; + if (!dentry) + continue; + if (dentry->d_op != &proc_dentry_operations) + continue; + inode = dentry->d_inode; + if (!inode) + continue; + if (inode->i_ino != ino) + continue; + filp->f_op = NULL; + } + file_list_unlock(); } } diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c index 8b27142a3..0fbae7262 100644 --- a/fs/qnx4/dir.c +++ b/fs/qnx4/dir.c @@ -41,6 +41,11 @@ static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) while (filp->f_pos < inode->i_size) { bh = bread(inode->i_dev, blknum, QNX4_BLOCK_SIZE); + if(bh==NULL) + { + printk(KERN_ERR "qnx4_readdir: bread failed (%ld)\n", blknum); + break; + } i = (filp->f_pos - (((filp->f_pos >> 6) >> 3) << 9)) & 0x3f; while (i < QNX4_INODES_PER_BLOCK) { offset = i * QNX4_DIR_ENTRY_SIZE; diff --git a/fs/qnx4/symlinks.c b/fs/qnx4/symlinks.c index c360d8b72..0b3f9ae03 100644 --- a/fs/qnx4/symlinks.c +++ b/fs/qnx4/symlinks.c @@ -98,12 +98,12 @@ static int qnx4_readlink(struct dentry *dentry, char *buffer, int buflen) } bh = bread(inode->i_dev, qnx4_ino->i_first_xtnt.xtnt_blk, QNX4_BLOCK_SIZE); - QNX4DEBUG(("qnx4: qnx4_bread sym called -> [%s]\n", - bh->b_data)); if (bh == NULL) { QNX4DEBUG(("qnx4: NULL symlink bh\n")); return 0; } + QNX4DEBUG(("qnx4: qnx4_bread sym called -> [%s]\n", + bh->b_data)); if (bh->b_data[0] != 0) { i = 0; while (i < buflen && (c = bh->b_data[i])) { diff --git a/fs/select.c b/fs/select.c index 4cb1a6d55..3f278187e 100644 --- a/fs/select.c +++ b/fs/select.c @@ -64,9 +64,9 @@ void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table if (p->nr < __MAX_POLL_TABLE_ENTRIES) { struct poll_table_entry * entry; ok_table: - entry = p->entry + p->nr; - entry->filp = filp; - atomic_inc(&filp->f_count); + entry = p->entry + p->nr; + get_file(filp); + entry->filp = filp; entry->wait_address = wait_address; init_waitqueue_entry(&entry->wait, current); add_wait_queue(wait_address,&entry->wait); @@ -164,9 +164,11 @@ int do_select(int n, fd_set_bits *fds, long *timeout) wait = wait_table; } - lock_kernel(); - + read_lock(¤t->files->file_lock); retval = max_select_fd(n, fds); + read_unlock(¤t->files->file_lock); + + lock_kernel(); if (retval < 0) goto out; n = retval; diff --git a/fs/super.c b/fs/super.c index 5cf518959..f70815094 100644 --- a/fs/super.c +++ b/fs/super.c @@ -531,6 +531,7 @@ static struct super_block *get_empty_super(void) INIT_LIST_HEAD(&s->s_dirty); list_add (&s->s_list, super_blocks.prev); init_waitqueue_head(&s->s_wait); + INIT_LIST_HEAD(&s->s_files); } return s; } diff --git a/fs/sysv/file.c b/fs/sysv/file.c index 3019c913f..36650f141 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -30,9 +30,6 @@ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) -#include <linux/fs.h> -#include <linux/sysv_fs.h> - /* * Write to a file (through the page cache). */ diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index b464318a7..c75a3a405 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -360,14 +360,22 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment, if (result) { for (i = 0; i < oldcount; i++) { bh = bread (sb->s_dev, tmp + i, sb->s_blocksize); - mark_buffer_clean (bh); - bh->b_blocknr = result + i; - mark_buffer_dirty (bh, 0); - if (IS_SYNC(inode)) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); + if(bh) + { + mark_buffer_clean (bh); + bh->b_blocknr = result + i; + mark_buffer_dirty (bh, 0); + if (IS_SYNC(inode)) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } + brelse (bh); + } + else + { + printk(KERN_ERR "ufs_new_fragments: bread fail\n"); + return 0; } - brelse (bh); } *p = SWAB32(result); *err = 0; diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 12937adcf..b6a1f8ef6 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -467,7 +467,8 @@ struct super_block * ufs_read_super (struct super_block * sb, void * data, } if (!(sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE)) { printk("You didn't specify the type of your ufs filesystem\n\n" - " mount -t ufs -o ufstype=sun|sunx86|44bsd|old|nextstep|netxstep-cd|openstep ...\n\n" + "mount -t ufs -o ufstype=" + "sun|sunx86|44bsd|old|nextstep|netxstep-cd|openstep ...\n\n" ">>>WARNING<<< Wrong ufstype may corrupt your filesystem, " "default is ufstype=old\n"); ufs_set_opt (sb->u.ufs_sb.s_mount_opt, UFSTYPE_OLD); diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c index d3d8a74e6..b930c46ae 100644 --- a/fs/umsdos/file.c +++ b/fs/umsdos/file.c @@ -13,7 +13,6 @@ #include <linux/errno.h> #include <linux/fcntl.h> #include <linux/stat.h> -#include <linux/msdos_fs.h> #include <linux/umsdos_fs.h> #include <asm/uaccess.h> |