new helper: iterate_fd()
Al Viro [Wed, 22 Aug 2012 02:32:06 +0000 (22:32 -0400)]
iterates through the opened files in given descriptor table,
calling a supplied function; we stop once non-zero is returned.
Callback gets struct file *, descriptor number and const void *
argument passed to iterator.  It is called with files->file_lock
held, so it is not allowed to block.

tty_io, netprio_cgroup and selinux flush_unauthorized_files()
converted to its use.

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

drivers/tty/tty_io.c
fs/file.c
include/linux/fdtable.h
net/core/netprio_cgroup.c
security/selinux/hooks.c

index b425c79..71d95cf 100644 (file)
@@ -2791,6 +2791,13 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
 }
 #endif
 
+static int this_tty(const void *t, struct file *file, unsigned fd)
+{
+       if (likely(file->f_op->read != tty_read))
+               return 0;
+       return file_tty(file) != t ? 0 : fd + 1;
+}
+       
 /*
  * This implements the "Secure Attention Key" ---  the idea is to
  * prevent trojan horses by killing all processes associated with this
@@ -2818,8 +2825,6 @@ void __do_SAK(struct tty_struct *tty)
        struct task_struct *g, *p;
        struct pid *session;
        int             i;
-       struct file     *filp;
-       struct fdtable *fdt;
 
        if (!tty)
                return;
@@ -2849,27 +2854,12 @@ void __do_SAK(struct tty_struct *tty)
                        continue;
                }
                task_lock(p);
-               if (p->files) {
-                       /*
-                        * We don't take a ref to the file, so we must
-                        * hold ->file_lock instead.
-                        */
-                       spin_lock(&p->files->file_lock);
-                       fdt = files_fdtable(p->files);
-                       for (i = 0; i < fdt->max_fds; i++) {
-                               filp = fcheck_files(p->files, i);
-                               if (!filp)
-                                       continue;
-                               if (filp->f_op->read == tty_read &&
-                                   file_tty(filp) == tty) {
-                                       printk(KERN_NOTICE "SAK: killed process %d"
-                                           " (%s): fd#%d opened to the tty\n",
-                                           task_pid_nr(p), p->comm, i);
-                                       force_sig(SIGKILL, p);
-                                       break;
-                               }
-                       }
-                       spin_unlock(&p->files->file_lock);
+               i = iterate_fd(p->files, 0, this_tty, tty);
+               if (i != 0) {
+                       printk(KERN_NOTICE "SAK: killed process %d"
+                           " (%s): fd#%d opened to the tty\n",
+                                   task_pid_nr(p), p->comm, i - 1);
+                       force_sig(SIGKILL, p);
                }
                task_unlock(p);
        } while_each_thread(g, p);
index 967bd0d..e6e4181 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -979,3 +979,24 @@ int f_dupfd(unsigned int from, struct file *file, unsigned flags)
        }
        return err;
 }
+
+int iterate_fd(struct files_struct *files, unsigned n,
+               int (*f)(const void *, struct file *, unsigned),
+               const void *p)
+{
+       struct fdtable *fdt;
+       struct file *file;
+       int res = 0;
+       if (!files)
+               return 0;
+       spin_lock(&files->file_lock);
+       fdt = files_fdtable(files);
+       while (!res && n < fdt->max_fds) {
+               file = rcu_dereference_check_fdtable(files, fdt->fd[n++]);
+               if (file)
+                       res = f(p, file, n);
+       }
+       spin_unlock(&files->file_lock);
+       return res;
+}
+EXPORT_SYMBOL(iterate_fd);
index de2b71c..fb7daca 100644 (file)
@@ -98,6 +98,9 @@ void reset_files_struct(struct files_struct *);
 int unshare_files(struct files_struct **);
 struct files_struct *dup_fd(struct files_struct *, int *);
 void do_close_on_exec(struct files_struct *);
+int iterate_fd(struct files_struct *, unsigned,
+               int (*)(const void *, struct file *, unsigned),
+               const void *);
 
 extern int __alloc_fd(struct files_struct *files,
                      unsigned start, unsigned end, unsigned flags);
index c75e3f9..5ffd084 100644 (file)
@@ -272,38 +272,24 @@ out_free_devname:
        return ret;
 }
 
+static int update_netprio(const void *v, struct file *file, unsigned n)
+{
+       int err;
+       struct socket *sock = sock_from_file(file, &err);
+       if (sock)
+               sock->sk->sk_cgrp_prioidx = (u32)(unsigned long)v;
+       return 0;
+}
+
 void net_prio_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
 {
        struct task_struct *p;
+       void *v;
 
        cgroup_taskset_for_each(p, cgrp, tset) {
-               unsigned int fd;
-               struct fdtable *fdt;
-               struct files_struct *files;
-
                task_lock(p);
-               files = p->files;
-               if (!files) {
-                       task_unlock(p);
-                       continue;
-               }
-
-               spin_lock(&files->file_lock);
-               fdt = files_fdtable(files);
-               for (fd = 0; fd < fdt->max_fds; fd++) {
-                       struct file *file;
-                       struct socket *sock;
-                       int err;
-
-                       file = fcheck_files(files, fd);
-                       if (!file)
-                               continue;
-
-                       sock = sock_from_file(file, &err);
-                       if (sock)
-                               sock_update_netprioidx(sock->sk, p);
-               }
-               spin_unlock(&files->file_lock);
+               v = (void *)(unsigned long)task_netprioidx(p);
+               iterate_fd(p->files, 0, update_netprio, v);
                task_unlock(p);
        }
 }
index 00b5011..4dfbcea 100644 (file)
@@ -2088,15 +2088,19 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
        return (atsecure || cap_bprm_secureexec(bprm));
 }
 
+static int match_file(const void *p, struct file *file, unsigned fd)
+{
+       return file_has_perm(p, file, file_to_av(file)) ? fd + 1 : 0;
+}
+
 /* Derived from fs/exec.c:flush_old_files. */
 static inline void flush_unauthorized_files(const struct cred *cred,
                                            struct files_struct *files)
 {
        struct file *file, *devnull = NULL;
        struct tty_struct *tty;
-       struct fdtable *fdt;
-       long j = -1;
        int drop_tty = 0;
+       unsigned n;
 
        tty = get_current_tty();
        if (tty) {
@@ -2123,41 +2127,24 @@ static inline void flush_unauthorized_files(const struct cred *cred,
                no_tty();
 
        /* Revalidate access to inherited open files. */
-       spin_lock(&files->file_lock);
-       for (;;) {
-               unsigned long set, i;
-               j++;
-               i = j * BITS_PER_LONG;
-               fdt = files_fdtable(files);
-               if (i >= fdt->max_fds)
-                       break;
-               set = fdt->open_fds[j];
-               if (!set)
-                       continue;
-               spin_unlock(&files->file_lock);
-               for ( ; set ; i++, set >>= 1) {
-                       if (!(set & 1))
-                               continue;
-                       file = fget(i);
-                       if (!file)
-                               continue;
-                       if (file_has_perm(cred, file, file_to_av(file))) {
-                               if (devnull) {
-                                       get_file(devnull);
-                               } else {
-                                       devnull = dentry_open(&selinux_null,
-                                                               O_RDWR, cred);
-                                       if (IS_ERR(devnull))
-                                               devnull = NULL;
-                               }
-                               replace_fd(i, devnull, 0);
-                       }
-                       fput(file);
-               }
-               spin_lock(&files->file_lock);
+       n = iterate_fd(files, 0, match_file, cred);
+       if (!n) /* none found? */
+               return;
 
+       devnull = dentry_open(&selinux_null, O_RDWR, cred);
+       if (!IS_ERR(devnull)) {
+               /* replace all the matching ones with this */
+               do {
+                       get_file(devnull);
+                       replace_fd(n - 1, devnull, 0);
+               } while ((n = iterate_fd(files, n, match_file, cred)) != 0);
+               fput(devnull);
+       } else {
+               /* just close all the matching ones */
+               do {
+                       replace_fd(n - 1, NULL, 0);
+               } while ((n = iterate_fd(files, n, match_file, cred)) != 0);
        }
-       spin_unlock(&files->file_lock);
 }
 
 /*