summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2012-08-21 12:11:46 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2012-09-26 21:09:57 -0400
commit8280d16172243702ed43432f826ca6130edb4086 (patch)
tree020dff359c5b717a110432159bcc3ec1acf6594e
parentfe17f22d7fd0e344ef6447238f799bb49f670c6f (diff)
downloadlwn-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.c11
-rw-r--r--fs/file.c91
-rw-r--r--include/linux/file.h1
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 *);