Merge branch 'hch.init_path' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / fs / file.c
index abb8b70..21c0893 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -10,6 +10,7 @@
 #include <linux/syscalls.h>
 #include <linux/export.h>
 #include <linux/fs.h>
+#include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/sched/signal.h>
 #include <linux/slab.h>
@@ -18,6 +19,8 @@
 #include <linux/bitops.h>
 #include <linux/spinlock.h>
 #include <linux/rcupdate.h>
+#include <linux/close_range.h>
+#include <net/sock.h>
 
 unsigned int sysctl_nr_open __read_mostly = 1024*1024;
 unsigned int sysctl_nr_open_min = BITS_PER_LONG;
@@ -264,12 +267,22 @@ static unsigned int count_open_files(struct fdtable *fdt)
        return i;
 }
 
+static unsigned int sane_fdtable_size(struct fdtable *fdt, unsigned int max_fds)
+{
+       unsigned int count;
+
+       count = count_open_files(fdt);
+       if (max_fds < NR_OPEN_DEFAULT)
+               max_fds = NR_OPEN_DEFAULT;
+       return min(count, max_fds);
+}
+
 /*
  * Allocate a new files structure and copy contents from the
  * passed in files structure.
  * errorp will be valid only when the returned files_struct is NULL.
  */
-struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
+struct files_struct *dup_fd(struct files_struct *oldf, unsigned int max_fds, int *errorp)
 {
        struct files_struct *newf;
        struct file **old_fds, **new_fds;
@@ -296,7 +309,7 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
 
        spin_lock(&oldf->file_lock);
        old_fdt = files_fdtable(oldf);
-       open_files = count_open_files(old_fdt);
+       open_files = sane_fdtable_size(old_fdt, max_fds);
 
        /*
         * Check whether we need to allocate a larger fd array and fd set.
@@ -327,7 +340,7 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
                 */
                spin_lock(&oldf->file_lock);
                old_fdt = files_fdtable(oldf);
-               open_files = count_open_files(old_fdt);
+               open_files = sane_fdtable_size(old_fdt, max_fds);
        }
 
        copy_fd_bitmaps(new_fdt, old_fdt, open_files);
@@ -613,6 +626,10 @@ void __fd_install(struct files_struct *files, unsigned int fd,
        rcu_read_unlock_sched();
 }
 
+/*
+ * This consumes the "file" refcount, so callers should treat it
+ * as if they had called fput(file).
+ */
 void fd_install(unsigned int fd, struct file *file)
 {
        __fd_install(current->files, fd, file);
@@ -620,12 +637,9 @@ void fd_install(unsigned int fd, struct file *file)
 
 EXPORT_SYMBOL(fd_install);
 
-/*
- * The same warnings as for __alloc_fd()/__fd_install() apply here...
- */
-int __close_fd(struct files_struct *files, unsigned fd)
+static struct file *pick_file(struct files_struct *files, unsigned fd)
 {
-       struct file *file;
+       struct file *file = NULL;
        struct fdtable *fdt;
 
        spin_lock(&files->file_lock);
@@ -637,15 +651,105 @@ int __close_fd(struct files_struct *files, unsigned fd)
                goto out_unlock;
        rcu_assign_pointer(fdt->fd[fd], NULL);
        __put_unused_fd(files, fd);
-       spin_unlock(&files->file_lock);
-       return filp_close(file, files);
 
 out_unlock:
        spin_unlock(&files->file_lock);
-       return -EBADF;
+       return file;
+}
+
+/*
+ * The same warnings as for __alloc_fd()/__fd_install() apply here...
+ */
+int __close_fd(struct files_struct *files, unsigned fd)
+{
+       struct file *file;
+
+       file = pick_file(files, fd);
+       if (!file)
+               return -EBADF;
+
+       return filp_close(file, files);
 }
 EXPORT_SYMBOL(__close_fd); /* for ksys_close() */
 
+/**
+ * __close_range() - Close all file descriptors in a given range.
+ *
+ * @fd:     starting file descriptor to close
+ * @max_fd: last file descriptor to close
+ *
+ * This closes a range of file descriptors. All file descriptors
+ * from @fd up to and including @max_fd are closed.
+ */
+int __close_range(unsigned fd, unsigned max_fd, unsigned int flags)
+{
+       unsigned int cur_max;
+       struct task_struct *me = current;
+       struct files_struct *cur_fds = me->files, *fds = NULL;
+
+       if (flags & ~CLOSE_RANGE_UNSHARE)
+               return -EINVAL;
+
+       if (fd > max_fd)
+               return -EINVAL;
+
+       rcu_read_lock();
+       cur_max = files_fdtable(cur_fds)->max_fds;
+       rcu_read_unlock();
+
+       /* cap to last valid index into fdtable */
+       cur_max--;
+
+       if (flags & CLOSE_RANGE_UNSHARE) {
+               int ret;
+               unsigned int max_unshare_fds = NR_OPEN_MAX;
+
+               /*
+                * If the requested range is greater than the current maximum,
+                * we're closing everything so only copy all file descriptors
+                * beneath the lowest file descriptor.
+                */
+               if (max_fd >= cur_max)
+                       max_unshare_fds = fd;
+
+               ret = unshare_fd(CLONE_FILES, max_unshare_fds, &fds);
+               if (ret)
+                       return ret;
+
+               /*
+                * We used to share our file descriptor table, and have now
+                * created a private one, make sure we're using it below.
+                */
+               if (fds)
+                       swap(cur_fds, fds);
+       }
+
+       max_fd = min(max_fd, cur_max);
+       while (fd <= max_fd) {
+               struct file *file;
+
+               file = pick_file(cur_fds, fd++);
+               if (!file)
+                       continue;
+
+               filp_close(file, cur_fds);
+               cond_resched();
+       }
+
+       if (fds) {
+               /*
+                * We're done closing the files we were supposed to. Time to install
+                * the new file descriptor table and drop the old one.
+                */
+               task_lock(me);
+               me->files = cur_fds;
+               task_unlock(me);
+               put_files_struct(fds);
+       }
+
+       return 0;
+}
+
 /*
  * variant of __close_fd that gets a ref on the file for later fput.
  * The caller must ensure that filp_close() called on the file, and then
@@ -931,6 +1035,62 @@ out_unlock:
        return err;
 }
 
+/**
+ * __receive_fd() - Install received file into file descriptor table
+ *
+ * @fd: fd to install into (if negative, a new fd will be allocated)
+ * @file: struct file that was received from another process
+ * @ufd: __user pointer to write new fd number to
+ * @o_flags: the O_* flags to apply to the new fd entry
+ *
+ * Installs a received file into the file descriptor table, with appropriate
+ * checks and count updates. Optionally writes the fd number to userspace, if
+ * @ufd is non-NULL.
+ *
+ * This helper handles its own reference counting of the incoming
+ * struct file.
+ *
+ * Returns newly install fd or -ve on error.
+ */
+int __receive_fd(int fd, struct file *file, int __user *ufd, unsigned int o_flags)
+{
+       int new_fd;
+       int error;
+
+       error = security_file_receive(file);
+       if (error)
+               return error;
+
+       if (fd < 0) {
+               new_fd = get_unused_fd_flags(o_flags);
+               if (new_fd < 0)
+                       return new_fd;
+       } else {
+               new_fd = fd;
+       }
+
+       if (ufd) {
+               error = put_user(new_fd, ufd);
+               if (error) {
+                       if (fd < 0)
+                               put_unused_fd(new_fd);
+                       return error;
+               }
+       }
+
+       if (fd < 0) {
+               fd_install(new_fd, get_file(file));
+       } else {
+               error = replace_fd(new_fd, file, o_flags);
+               if (error)
+                       return error;
+       }
+
+       /* Bump the sock usage counts, if any. */
+       __receive_sock(file);
+       return new_fd;
+}
+
 static int ksys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
 {
        int err = -EBADF;
@@ -985,7 +1145,7 @@ SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd)
        return ksys_dup3(oldfd, newfd, 0);
 }
 
-int ksys_dup(unsigned int fildes)
+SYSCALL_DEFINE1(dup, unsigned int, fildes)
 {
        int ret = -EBADF;
        struct file *file = fget_raw(fildes);
@@ -1000,11 +1160,6 @@ int ksys_dup(unsigned int fildes)
        return ret;
 }
 
-SYSCALL_DEFINE1(dup, unsigned int, fildes)
-{
-       return ksys_dup(fildes);
-}
-
 int f_dupfd(unsigned int from, struct file *file, unsigned flags)
 {
        int err;