diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2012-08-21 12:11:46 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-09-26 21:09:57 -0400 |
commit | 8280d16172243702ed43432f826ca6130edb4086 (patch) | |
tree | 020dff359c5b717a110432159bcc3ec1acf6594e | |
parent | fe17f22d7fd0e344ef6447238f799bb49f670c6f (diff) | |
download | lwn-8280d16172243702ed43432f826ca6130edb4086.tar.gz lwn-8280d16172243702ed43432f826ca6130edb4086.zip |
new helper: replace_fd()
analog of dup2(), except that it takes struct file * as source.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/exec.c | 11 | ||||
-rw-r--r-- | fs/file.c | 91 | ||||
-rw-r--r-- | include/linux/file.h | 1 |
3 files changed, 64 insertions, 39 deletions
diff --git a/fs/exec.c b/fs/exec.c index f2b6af585d4a..3fc74681cc6c 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2041,23 +2041,14 @@ static void wait_for_dump_helpers(struct file *file) static int umh_pipe_setup(struct subprocess_info *info, struct cred *new) { struct file *files[2]; - struct fdtable *fdt; struct coredump_params *cp = (struct coredump_params *)info->data; - struct files_struct *cf = current->files; int err = create_pipe_files(files, 0); if (err) return err; cp->file = files[1]; - sys_close(0); - fd_install(0, files[0]); - spin_lock(&cf->file_lock); - fdt = files_fdtable(cf); - __set_open_fd(0, fdt); - __clear_close_on_exec(0, fdt); - spin_unlock(&cf->file_lock); - + replace_fd(0, files[0], 0); /* and disallow core files too */ current->signal->rlim[RLIMIT_CORE] = (struct rlimit){1, 1}; diff --git a/fs/file.c b/fs/file.c index 7f29544755d0..a7bbe0324478 100644 --- a/fs/file.c +++ b/fs/file.c @@ -821,29 +821,12 @@ bool get_close_on_exec(unsigned int fd) return res; } -SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) +static int do_dup2(struct files_struct *files, + struct file *file, unsigned fd, unsigned flags) { - int err = -EBADF; - struct file * file, *tofree; - struct files_struct * files = current->files; + struct file *tofree; struct fdtable *fdt; - if ((flags & ~O_CLOEXEC) != 0) - return -EINVAL; - - if (newfd >= rlimit(RLIMIT_NOFILE)) - return -EMFILE; - - spin_lock(&files->file_lock); - err = expand_files(files, newfd); - file = fcheck(oldfd); - if (unlikely(!file)) - goto Ebadf; - if (unlikely(err < 0)) { - if (err == -EMFILE) - goto Ebadf; - goto out_unlock; - } /* * We need to detect attempts to do dup2() over allocated but still * not finished descriptor. NB: OpenBSD avoids that at the price of @@ -858,24 +841,74 @@ SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) * scope of POSIX or SUS, since neither considers shared descriptor * tables and this condition does not arise without those. */ - err = -EBUSY; fdt = files_fdtable(files); - tofree = fdt->fd[newfd]; - if (!tofree && fd_is_open(newfd, fdt)) - goto out_unlock; + tofree = fdt->fd[fd]; + if (!tofree && fd_is_open(fd, fdt)) + goto Ebusy; get_file(file); - rcu_assign_pointer(fdt->fd[newfd], file); - __set_open_fd(newfd, fdt); + rcu_assign_pointer(fdt->fd[fd], file); + __set_open_fd(fd, fdt); if (flags & O_CLOEXEC) - __set_close_on_exec(newfd, fdt); + __set_close_on_exec(fd, fdt); else - __clear_close_on_exec(newfd, fdt); + __clear_close_on_exec(fd, fdt); spin_unlock(&files->file_lock); if (tofree) filp_close(tofree, files); - return newfd; + return fd; + +Ebusy: + spin_unlock(&files->file_lock); + return -EBUSY; +} + +int replace_fd(unsigned fd, struct file *file, unsigned flags) +{ + int err; + struct files_struct *files = current->files; + + if (!file) + return __close_fd(files, fd); + + if (fd >= rlimit(RLIMIT_NOFILE)) + return -EMFILE; + + spin_lock(&files->file_lock); + err = expand_files(files, fd); + if (unlikely(err < 0)) + goto out_unlock; + return do_dup2(files, file, fd, flags); + +out_unlock: + spin_unlock(&files->file_lock); + return err; +} + +SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) +{ + int err = -EBADF; + struct file *file; + struct files_struct *files = current->files; + + if ((flags & ~O_CLOEXEC) != 0) + return -EINVAL; + + if (newfd >= rlimit(RLIMIT_NOFILE)) + return -EMFILE; + + spin_lock(&files->file_lock); + err = expand_files(files, newfd); + file = fcheck(oldfd); + if (unlikely(!file)) + goto Ebadf; + if (unlikely(err < 0)) { + if (err == -EMFILE) + goto Ebadf; + goto out_unlock; + } + return do_dup2(files, file, newfd, flags); Ebadf: err = -EBADF; diff --git a/include/linux/file.h b/include/linux/file.h index da84fa0f4579..6239591a6122 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -31,6 +31,7 @@ extern struct file *fget_light(unsigned int fd, int *fput_needed); extern struct file *fget_raw(unsigned int fd); extern struct file *fget_raw_light(unsigned int fd, int *fput_needed); extern int f_dupfd(unsigned int from, struct file *file, unsigned flags); +extern int replace_fd(unsigned fd, struct file *file, unsigned flags); extern void set_close_on_exec(unsigned int fd, int flag); extern bool get_close_on_exec(unsigned int fd); extern void put_filp(struct file *); |