diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-25 19:05:55 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-25 19:05:55 -0700 |
commit | bc84e0a160e383deb56568f4e03bc51b1ce16775 (patch) | |
tree | 3c8cf1ae23c2f9c165bd7ce63a40b1d5c3aee275 | |
parent | a92910723a5af54f81373875fd95133c88df94bd (diff) | |
parent | f8f95702f0c4529b0f59488f4509608f0c160e77 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6:
[PATCH] sanitize locate_fd()
[PATCH] sanitize unshare_files/reset_files_struct
[PATCH] sanitize handling of shared descriptor tables in failing execve()
[PATCH] close race in unshare_files()
[PATCH] restore sane ->umount_begin() API
cifs: timeout dfs automounts +little fix.
-rw-r--r-- | fs/9p/vfs_super.c | 7 | ||||
-rw-r--r-- | fs/binfmt_elf.c | 23 | ||||
-rw-r--r-- | fs/binfmt_misc.c | 18 | ||||
-rw-r--r-- | fs/binfmt_som.c | 10 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 29 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 10 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 8 | ||||
-rw-r--r-- | fs/exec.c | 28 | ||||
-rw-r--r-- | fs/fcntl.c | 40 | ||||
-rw-r--r-- | fs/fuse/inode.c | 5 | ||||
-rw-r--r-- | fs/namespace.c | 9 | ||||
-rw-r--r-- | fs/nfs/super.c | 8 | ||||
-rw-r--r-- | include/linux/file.h | 3 | ||||
-rw-r--r-- | include/linux/fs.h | 5 | ||||
-rw-r--r-- | kernel/exit.c | 6 | ||||
-rw-r--r-- | kernel/fork.c | 58 |
16 files changed, 95 insertions, 172 deletions
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 678c02f1ae2..a452ac67fc9 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -224,12 +224,11 @@ static int v9fs_show_options(struct seq_file *m, struct vfsmount *mnt) } static void -v9fs_umount_begin(struct vfsmount *vfsmnt, int flags) +v9fs_umount_begin(struct super_block *sb) { - struct v9fs_session_info *v9ses = vfsmnt->mnt_sb->s_fs_info; + struct v9fs_session_info *v9ses = sb->s_fs_info; - if (flags & MNT_FORCE) - v9fs_session_cancel(v9ses); + v9fs_session_cancel(v9ses); } static const struct super_operations v9fs_super_ops = { diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 5e1a4fb5cac..9924581df6f 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -543,7 +543,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) unsigned long interp_load_addr = 0; unsigned long start_code, end_code, start_data, end_data; unsigned long reloc_func_desc = 0; - struct files_struct *files; int executable_stack = EXSTACK_DEFAULT; unsigned long def_flags = 0; struct { @@ -593,20 +592,9 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) goto out_free_ph; } - files = current->files; /* Refcounted so ok */ - retval = unshare_files(); - if (retval < 0) - goto out_free_ph; - if (files == current->files) { - put_files_struct(files); - files = NULL; - } - - /* exec will make our files private anyway, but for the a.out - loader stuff we need to do it earlier */ retval = get_unused_fd(); if (retval < 0) - goto out_free_fh; + goto out_free_ph; get_file(bprm->file); fd_install(elf_exec_fileno = retval, bprm->file); @@ -728,12 +716,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (retval) goto out_free_dentry; - /* Discard our unneeded old files struct */ - if (files) { - put_files_struct(files); - files = NULL; - } - /* OK, This is the point of no return */ current->flags &= ~PF_FORKNOEXEC; current->mm->def_flags = def_flags; @@ -1016,9 +998,6 @@ out_free_interp: kfree(elf_interpreter); out_free_file: sys_close(elf_exec_fileno); -out_free_fh: - if (files) - reset_files_struct(current, files); out_free_ph: kfree(elf_phdata); goto out; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index b53c7e5f41b..dbf0ac0523d 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -110,7 +110,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) char *iname_addr = iname; int retval; int fd_binary = -1; - struct files_struct *files = NULL; retval = -ENOEXEC; if (!enabled) @@ -133,21 +132,13 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (fmt->flags & MISC_FMT_OPEN_BINARY) { - files = current->files; - retval = unshare_files(); - if (retval < 0) - goto _ret; - if (files == current->files) { - put_files_struct(files); - files = NULL; - } /* if the binary should be opened on behalf of the * interpreter than keep it open and assign descriptor * to it */ fd_binary = get_unused_fd(); if (fd_binary < 0) { retval = fd_binary; - goto _unshare; + goto _ret; } fd_install(fd_binary, bprm->file); @@ -205,10 +196,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (retval < 0) goto _error; - if (files) { - put_files_struct(files); - files = NULL; - } _ret: return retval; _error: @@ -216,9 +203,6 @@ _error: sys_close(fd_binary); bprm->interp_flags = 0; bprm->interp_data = 0; -_unshare: - if (files) - reset_files_struct(current, files); goto _ret; } diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c index 14c63527c76..fdc36bfd6a7 100644 --- a/fs/binfmt_som.c +++ b/fs/binfmt_som.c @@ -194,7 +194,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs) unsigned long som_entry; struct som_hdr *som_ex; struct som_exec_auxhdr *hpuxhdr; - struct files_struct *files; /* Get the exec-header */ som_ex = (struct som_hdr *) bprm->buf; @@ -221,15 +220,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs) goto out_free; } - files = current->files; /* Refcounted so ok */ - retval = unshare_files(); - if (retval < 0) - goto out_free; - if (files == current->files) { - put_files_struct(files); - files = NULL; - } - retval = get_unused_fd(); if (retval < 0) goto out_free; diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index f53f41ff166..95024c066d8 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -25,14 +25,26 @@ static LIST_HEAD(cifs_dfs_automount_list); -/* - * DFS functions -*/ +static void cifs_dfs_expire_automounts(struct work_struct *work); +static DECLARE_DELAYED_WORK(cifs_dfs_automount_task, + cifs_dfs_expire_automounts); +static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ; + +static void cifs_dfs_expire_automounts(struct work_struct *work) +{ + struct list_head *list = &cifs_dfs_automount_list; + + mark_mounts_for_expiry(list); + if (!list_empty(list)) + schedule_delayed_work(&cifs_dfs_automount_task, + cifs_dfs_mountpoint_expiry_timeout); +} -void dfs_shrink_umount_helper(struct vfsmount *vfsmnt) +void cifs_dfs_release_automount_timer(void) { - mark_mounts_for_expiry(&cifs_dfs_automount_list); - mark_mounts_for_expiry(&cifs_dfs_automount_list); + BUG_ON(!list_empty(&cifs_dfs_automount_list)); + cancel_delayed_work(&cifs_dfs_automount_task); + flush_scheduled_work(); } /** @@ -261,10 +273,11 @@ static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, err = do_add_mount(newmnt, nd, nd->path.mnt->mnt_flags, mntlist); switch (err) { case 0: - dput(nd->path.dentry); - mntput(nd->path.mnt); + path_put(&nd->path); nd->path.mnt = newmnt; nd->path.dentry = dget(newmnt->mnt_root); + schedule_delayed_work(&cifs_dfs_automount_task, + cifs_dfs_mountpoint_expiry_timeout); break; case -EBUSY: /* someone else made a mount here whilst we were busy */ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index a04b17e5a9d..39c2cbdface 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -466,16 +466,11 @@ static struct quotactl_ops cifs_quotactl_ops = { }; #endif -static void cifs_umount_begin(struct vfsmount *vfsmnt, int flags) +static void cifs_umount_begin(struct super_block *sb) { - struct cifs_sb_info *cifs_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *tcon; - dfs_shrink_umount_helper(vfsmnt); - - if (!(flags & MNT_FORCE)) - return; - cifs_sb = CIFS_SB(vfsmnt->mnt_sb); if (cifs_sb == NULL) return; @@ -1100,6 +1095,7 @@ exit_cifs(void) cFYI(DBG2, ("exit_cifs")); cifs_proc_clean(); #ifdef CONFIG_CIFS_DFS_UPCALL + cifs_dfs_release_automount_timer(); unregister_key_type(&key_type_dns_resolver); #endif #ifdef CONFIG_CIFS_UPCALL diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 0c83da4a7da..50f9fdae19b 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -104,13 +104,7 @@ extern int mode_to_acl(struct inode *inode, const char *path, __u64); extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); extern int cifs_umount(struct super_block *, struct cifs_sb_info *); -#ifdef CONFIG_CIFS_DFS_UPCALL -extern void dfs_shrink_umount_helper(struct vfsmount *vfsmnt); -#else -static inline void dfs_shrink_umount_helper(struct vfsmount *vfsmnt) -{ -} -#endif /* DFS_UPCALL */ +extern void cifs_dfs_release_automount_timer(void); void cifs_proc_init(void); void cifs_proc_clean(void); diff --git a/fs/exec.c b/fs/exec.c index 54a0a557b67..b152029f18f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -953,7 +953,6 @@ int flush_old_exec(struct linux_binprm * bprm) { char * name; int i, ch, retval; - struct files_struct *files; char tcomm[sizeof(current->comm)]; /* @@ -965,26 +964,15 @@ int flush_old_exec(struct linux_binprm * bprm) goto out; /* - * Make sure we have private file handles. Ask the - * fork helper to do the work for us and the exit - * helper to do the cleanup of the old one. - */ - files = current->files; /* refcounted so safe to hold */ - retval = unshare_files(); - if (retval) - goto out; - /* * Release all of the old mmap stuff */ retval = exec_mmap(bprm->mm); if (retval) - goto mmap_failed; + goto out; bprm->mm = NULL; /* We're using it now */ /* This is the point of no return */ - put_files_struct(files); - current->sas_ss_sp = current->sas_ss_size = 0; if (current->euid == current->uid && current->egid == current->gid) @@ -1034,8 +1022,6 @@ int flush_old_exec(struct linux_binprm * bprm) return 0; -mmap_failed: - reset_files_struct(current, files); out: return retval; } @@ -1283,12 +1269,17 @@ int do_execve(char * filename, struct linux_binprm *bprm; struct file *file; unsigned long env_p; + struct files_struct *displaced; int retval; + retval = unshare_files(&displaced); + if (retval) + goto out_ret; + retval = -ENOMEM; bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm) - goto out_ret; + goto out_files; file = open_exec(filename); retval = PTR_ERR(file); @@ -1343,6 +1334,8 @@ int do_execve(char * filename, security_bprm_free(bprm); acct_update_integrals(current); kfree(bprm); + if (displaced) + put_files_struct(displaced); return retval; } @@ -1363,6 +1356,9 @@ out_file: out_kfree: kfree(bprm); +out_files: + if (displaced) + reset_files_struct(displaced); out_ret: return retval; } diff --git a/fs/fcntl.c b/fs/fcntl.c index e632da761fc..3f3ac630ccd 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -55,14 +55,16 @@ static int get_close_on_exec(unsigned int fd) * file_lock held for write. */ -static int locate_fd(struct files_struct *files, - struct file *file, unsigned int orig_start) +static int locate_fd(unsigned int orig_start, int cloexec) { + struct files_struct *files = current->files; unsigned int newfd; unsigned int start; int error; struct fdtable *fdt; + spin_lock(&files->file_lock); + error = -EINVAL; if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) goto out; @@ -97,42 +99,28 @@ repeat: if (error) goto repeat; - /* - * We reacquired files_lock, so we are safe as long as - * we reacquire the fdtable pointer and use it while holding - * the lock, no one can free it during that time. - */ if (start <= files->next_fd) files->next_fd = newfd + 1; + FD_SET(newfd, fdt->open_fds); + if (cloexec) + FD_SET(newfd, fdt->close_on_exec); + else + FD_CLR(newfd, fdt->close_on_exec); error = newfd; - + out: + spin_unlock(&files->file_lock); return error; } static int dupfd(struct file *file, unsigned int start, int cloexec) { - struct files_struct * files = current->files; - struct fdtable *fdt; - int fd; - - spin_lock(&files->file_lock); - fd = locate_fd(files, file, start); - if (fd >= 0) { - /* locate_fd() may have expanded fdtable, load the ptr */ - fdt = files_fdtable(files); - FD_SET(fd, fdt->open_fds); - if (cloexec) - FD_SET(fd, fdt->close_on_exec); - else - FD_CLR(fd, fdt->close_on_exec); - spin_unlock(&files->file_lock); + int fd = locate_fd(start, cloexec); + if (fd >= 0) fd_install(fd, file); - } else { - spin_unlock(&files->file_lock); + else fput(file); - } return fd; } diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 033f7bdd47e..4df34da2284 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -242,10 +242,9 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, return inode; } -static void fuse_umount_begin(struct vfsmount *vfsmnt, int flags) +static void fuse_umount_begin(struct super_block *sb) { - if (flags & MNT_FORCE) - fuse_abort_conn(get_fuse_conn_super(vfsmnt->mnt_sb)); + fuse_abort_conn(get_fuse_conn_super(sb)); } static void fuse_send_destroy(struct fuse_conn *fc) diff --git a/fs/namespace.c b/fs/namespace.c index 0505fb61aa7..f48f98110c3 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1061,10 +1061,11 @@ static int do_umount(struct vfsmount *mnt, int flags) * about for the moment. */ - lock_kernel(); - if (sb->s_op->umount_begin) - sb->s_op->umount_begin(mnt, flags); - unlock_kernel(); + if (flags & MNT_FORCE && sb->s_op->umount_begin) { + lock_kernel(); + sb->s_op->umount_begin(sb); + unlock_kernel(); + } /* * No sense to grab the lock for this test, but test itself looks diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 20a1cb1810f..fa220dc7460 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -198,7 +198,7 @@ static match_table_t nfs_secflavor_tokens = { }; -static void nfs_umount_begin(struct vfsmount *, int); +static void nfs_umount_begin(struct super_block *); static int nfs_statfs(struct dentry *, struct kstatfs *); static int nfs_show_options(struct seq_file *, struct vfsmount *); static int nfs_show_stats(struct seq_file *, struct vfsmount *); @@ -647,13 +647,11 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) * Begin unmount by attempting to remove all automounted mountpoints we added * in response to xdev traversals and referrals */ -static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags) +static void nfs_umount_begin(struct super_block *sb) { - struct nfs_server *server = NFS_SB(vfsmnt->mnt_sb); + struct nfs_server *server = NFS_SB(sb); struct rpc_clnt *rpc; - if (!(flags & MNT_FORCE)) - return; /* -EIO all pending I/O */ rpc = server->client_acl; if (!IS_ERR(rpc)) diff --git a/include/linux/file.h b/include/linux/file.h index 653477021e4..69baf5a4f0a 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -117,7 +117,8 @@ struct task_struct; struct files_struct *get_files_struct(struct task_struct *); void put_files_struct(struct files_struct *fs); -void reset_files_struct(struct task_struct *, struct files_struct *); +void reset_files_struct(struct files_struct *); +int unshare_files(struct files_struct **); extern struct kmem_cache *files_cachep; diff --git a/include/linux/fs.h b/include/linux/fs.h index 6556f2f967e..d6d7c52055c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1309,7 +1309,7 @@ struct super_operations { int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); - void (*umount_begin) (struct vfsmount *, int); + void (*umount_begin) (struct super_block *); int (*show_options)(struct seq_file *, struct vfsmount *); int (*show_stats)(struct seq_file *, struct vfsmount *); @@ -2034,9 +2034,6 @@ static inline ino_t parent_ino(struct dentry *dentry) return res; } -/* kernel/fork.c */ -extern int unshare_files(void); - /* Transaction based IO helpers */ /* diff --git a/kernel/exit.c b/kernel/exit.c index cece89f80ab..97f609f574b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -507,10 +507,9 @@ void put_files_struct(struct files_struct *files) } } -EXPORT_SYMBOL(put_files_struct); - -void reset_files_struct(struct task_struct *tsk, struct files_struct *files) +void reset_files_struct(struct files_struct *files) { + struct task_struct *tsk = current; struct files_struct *old; old = tsk->files; @@ -519,7 +518,6 @@ void reset_files_struct(struct task_struct *tsk, struct files_struct *files) task_unlock(tsk); put_files_struct(old); } -EXPORT_SYMBOL(reset_files_struct); void exit_files(struct task_struct *tsk) { diff --git a/kernel/fork.c b/kernel/fork.c index 89fe414645e..efb618fc8ff 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -805,12 +805,6 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) goto out; } - /* - * Note: we may be using current for both targets (See exec.c) - * This works because we cache current->files (old) as oldf. Don't - * break this. - */ - tsk->files = NULL; newf = dup_fd(oldf, &error); if (!newf) goto out; @@ -846,34 +840,6 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk) return 0; } -/* - * Helper to unshare the files of the current task. - * We don't want to expose copy_files internals to - * the exec layer of the kernel. - */ - -int unshare_files(void) -{ - struct files_struct *files = current->files; - int rc; - - BUG_ON(!files); - - /* This can race but the race causes us to copy when we don't - need to and drop the copy */ - if(atomic_read(&files->count) == 1) - { - atomic_inc(&files->count); - return 0; - } - rc = copy_files(0, current); - if(rc) - current->files = files; - return rc; -} - -EXPORT_SYMBOL(unshare_files); - static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk) { struct sighand_struct *sig; @@ -1811,3 +1777,27 @@ bad_unshare_cleanup_thread: bad_unshare_out: return err; } + +/* + * Helper to unshare the files of the current task. + * We don't want to expose copy_files internals to + * the exec layer of the kernel. + */ + +int unshare_files(struct files_struct **displaced) +{ + struct task_struct *task = current; + struct files_struct *copy; + int error; + + error = unshare_fd(CLONE_FILES, ©); + if (error || !copy) { + *displaced = NULL; + return error; + } + *displaced = task->files; + task_lock(task); + task->files = copy; + task_unlock(task); + return 0; +} |