From 78d31a3a87f84cf56004b7bc154831f2ee1186e8 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 24 Apr 2008 12:56:07 +0400 Subject: cifs: timeout dfs automounts +little fix. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/cifs/cifs_dfs_ref.c | 29 +++++++++++++++++++++-------- fs/cifs/cifsfs.c | 3 +-- fs/cifs/cifsproto.h | 8 +------- 3 files changed, 23 insertions(+), 17 deletions(-) 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..dbb2cd678bf 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -471,8 +471,6 @@ static void cifs_umount_begin(struct vfsmount *vfsmnt, int flags) struct cifs_sb_info *cifs_sb; struct cifsTconInfo *tcon; - dfs_shrink_umount_helper(vfsmnt); - if (!(flags & MNT_FORCE)) return; cifs_sb = CIFS_SB(vfsmnt->mnt_sb); @@ -1100,6 +1098,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); -- cgit v1.2.3-18-g5258 From 42faad99658eed7ca8bd328ffa4bcb7d78c9bcca Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Apr 2008 07:21:56 -0400 Subject: [PATCH] restore sane ->umount_begin() API Signed-off-by: Al Viro --- fs/9p/vfs_super.c | 7 +++---- fs/cifs/cifsfs.c | 7 ++----- fs/fuse/inode.c | 5 ++--- fs/namespace.c | 9 +++++---- fs/nfs/super.c | 8 +++----- include/linux/fs.h | 2 +- 6 files changed, 16 insertions(+), 22 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/cifs/cifsfs.c b/fs/cifs/cifsfs.c index dbb2cd678bf..39c2cbdface 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -466,14 +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; - if (!(flags & MNT_FORCE)) - return; - cifs_sb = CIFS_SB(vfsmnt->mnt_sb); if (cifs_sb == NULL) return; 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/fs.h b/include/linux/fs.h index cc2be2cf7d4..ad41d0bbcb4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1308,7 +1308,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 *); -- cgit v1.2.3-18-g5258 From 6b335d9c80d7f3c2a3f6545f664ae9007a0f3821 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 22 Apr 2008 04:45:46 -0400 Subject: [PATCH] close race in unshare_files() updating current->files requires task_lock Signed-off-by: Al Viro --- kernel/fork.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/kernel/fork.c b/kernel/fork.c index 89fe414645e..76f05a08062 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; @@ -855,7 +849,8 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk) int unshare_files(void) { struct files_struct *files = current->files; - int rc; + struct files_struct *newf; + int error = 0; BUG_ON(!files); @@ -866,10 +861,13 @@ int unshare_files(void) atomic_inc(&files->count); return 0; } - rc = copy_files(0, current); - if(rc) - current->files = files; - return rc; + newf = dup_fd(files, &error); + if (newf) { + task_lock(current); + current->files = newf; + task_unlock(current); + } + return error; } EXPORT_SYMBOL(unshare_files); -- cgit v1.2.3-18-g5258 From fd8328be874f4190a811c58cd4778ec2c74d2c05 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 22 Apr 2008 05:11:59 -0400 Subject: [PATCH] sanitize handling of shared descriptor tables in failing execve() * unshare_files() can fail; doing it after irreversible actions is wrong and de_thread() is certainly irreversible. * since we do it unconditionally anyway, we might as well do it in do_execve() and save ourselves the PITA in binfmt handlers, etc. * while we are at it, binfmt_som actually leaked files_struct on failure. As a side benefit, unshare_files(), put_files_struct() and reset_files_struct() become unexported. Signed-off-by: Al Viro --- fs/binfmt_elf.c | 23 +---------------------- fs/binfmt_misc.c | 18 +----------------- fs/binfmt_som.c | 10 ---------- fs/exec.c | 34 ++++++++++++++++++---------------- kernel/exit.c | 3 --- kernel/fork.c | 2 -- 6 files changed, 20 insertions(+), 70 deletions(-) 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/exec.c b/fs/exec.c index 54a0a557b67..475543002f1 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)]; /* @@ -964,27 +963,16 @@ int flush_old_exec(struct linux_binprm * bprm) if (retval) 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,23 @@ int do_execve(char * filename, struct linux_binprm *bprm; struct file *file; unsigned long env_p; + struct files_struct *files; int retval; + files = current->files; + retval = unshare_files(); + if (retval) + goto out_ret; + + if (files == current->files) { + put_files_struct(files); + files = NULL; + } + 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 +1340,8 @@ int do_execve(char * filename, security_bprm_free(bprm); acct_update_integrals(current); kfree(bprm); + if (files) + put_files_struct(files); return retval; } @@ -1363,6 +1362,9 @@ out_file: out_kfree: kfree(bprm); +out_files: + if (files) + reset_files_struct(current, files); out_ret: return retval; } diff --git a/kernel/exit.c b/kernel/exit.c index cece89f80ab..3d320003cc0 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -507,8 +507,6 @@ 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) { struct files_struct *old; @@ -519,7 +517,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 76f05a08062..2fc11f2e2b2 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -870,8 +870,6 @@ int unshare_files(void) return error; } -EXPORT_SYMBOL(unshare_files); - static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk) { struct sighand_struct *sig; -- cgit v1.2.3-18-g5258 From 3b1253880b7a9e6db54b943b2d40bcf2202f58ab Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 22 Apr 2008 05:31:30 -0400 Subject: [PATCH] sanitize unshare_files/reset_files_struct * let unshare_files() give caller the displaced files_struct * don't bother with grabbing reference only to drop it in the caller if it hadn't been shared in the first place * in that form unshare_files() is trivially implemented via unshare_fd(), so we eliminate the duplicate logics in fork.c * reset_files_struct() is not just only called for current; it will break the system if somebody ever calls it for anything else (we can't modify ->files of somebody else). Lose the task_struct * argument. Signed-off-by: Al Viro --- fs/exec.c | 18 ++++++------------ include/linux/file.h | 3 ++- include/linux/fs.h | 3 --- kernel/exit.c | 3 ++- kernel/fork.c | 54 +++++++++++++++++++++++----------------------------- 5 files changed, 34 insertions(+), 47 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 475543002f1..b152029f18f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1269,19 +1269,13 @@ int do_execve(char * filename, struct linux_binprm *bprm; struct file *file; unsigned long env_p; - struct files_struct *files; + struct files_struct *displaced; int retval; - files = current->files; - retval = unshare_files(); + retval = unshare_files(&displaced); if (retval) goto out_ret; - if (files == current->files) { - put_files_struct(files); - files = NULL; - } - retval = -ENOMEM; bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm) @@ -1340,8 +1334,8 @@ int do_execve(char * filename, security_bprm_free(bprm); acct_update_integrals(current); kfree(bprm); - if (files) - put_files_struct(files); + if (displaced) + put_files_struct(displaced); return retval; } @@ -1363,8 +1357,8 @@ out_kfree: kfree(bprm); out_files: - if (files) - reset_files_struct(current, files); + if (displaced) + reset_files_struct(displaced); out_ret: return retval; } 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 ad41d0bbcb4..e057438a05a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2033,9 +2033,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 3d320003cc0..97f609f574b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -507,8 +507,9 @@ void put_files_struct(struct files_struct *files) } } -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; diff --git a/kernel/fork.c b/kernel/fork.c index 2fc11f2e2b2..efb618fc8ff 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -840,36 +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; - struct files_struct *newf; - int error = 0; - - 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; - } - newf = dup_fd(files, &error); - if (newf) { - task_lock(current); - current->files = newf; - task_unlock(current); - } - return error; -} - static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk) { struct sighand_struct *sig; @@ -1807,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; +} -- cgit v1.2.3-18-g5258 From f8f95702f0c4529b0f59488f4509608f0c160e77 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 23 Apr 2008 20:38:10 -0400 Subject: [PATCH] sanitize locate_fd() * 'file' argument is unused; lose it. * move setting flags from the caller (dupfd()) to locate_fd(); pass cloexec flag as new argument. Note that files_fdtable() that used to be in dupfd() isn't needed in the place in locate_fd() where the moved code ends up - we know that ->file_lock hadn't been dropped since the last time we calculated fdt because we can get there only if expand_files() returns 0 and it doesn't drop/reacquire in that case. * move getting/dropping ->file_lock into locate_fd(). Now the caller doesn't need to do anything with files_struct *files anymore and we can move that inside locate_fd() as well, killing the struct files_struct * argument. At that point locate_fd() is extremely similar to get_unused_fd_flags() and the next patches will merge those two. Signed-off-by: Al Viro --- fs/fcntl.c | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) 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; } -- cgit v1.2.3-18-g5258