new helper: replace_fd()
Al Viro [Tue, 21 Aug 2012 16:11:46 +0000 (12:11 -0400)]
analog of dup2(), except that it takes struct file * as source.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

fs/exec.c
fs/file.c
include/linux/file.h

index f2b6af5..3fc7468 100644 (file)
--- 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};
 
index 7f29544..a7bbe03 100644 (file)
--- 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;
index da84fa0..6239591 100644 (file)
@@ -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 *);