Merge tag 'binfmt-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb...
[sfrench/cifs-2.6.git] / fs / binfmt_misc.c
index 5417516f6e59ea0556dc2005a17a9d7d5069b264..6103a6362ccd1ad711117226879e65c6bac9c87f 100644 (file)
@@ -26,6 +26,8 @@
 #include <linux/fs.h>
 #include <linux/uaccess.h>
 
 #include <linux/fs.h>
 #include <linux/uaccess.h>
 
+#include "internal.h"
+
 #ifdef DEBUG
 # define USE_DEBUG 1
 #else
 #ifdef DEBUG
 # define USE_DEBUG 1
 #else
@@ -43,6 +45,7 @@ enum {Enabled, Magic};
 #define MISC_FMT_PRESERVE_ARGV0 (1 << 31)
 #define MISC_FMT_OPEN_BINARY (1 << 30)
 #define MISC_FMT_CREDENTIALS (1 << 29)
 #define MISC_FMT_PRESERVE_ARGV0 (1 << 31)
 #define MISC_FMT_OPEN_BINARY (1 << 30)
 #define MISC_FMT_CREDENTIALS (1 << 29)
+#define MISC_FMT_OPEN_FILE (1 << 28)
 
 typedef struct {
        struct list_head list;
 
 typedef struct {
        struct list_head list;
@@ -54,6 +57,7 @@ typedef struct {
        char *interpreter;              /* filename of interpreter */
        char *name;
        struct dentry *dentry;
        char *interpreter;              /* filename of interpreter */
        char *name;
        struct dentry *dentry;
+       struct file *interp_file;
 } Node;
 
 static DEFINE_RWLOCK(entries_lock);
 } Node;
 
 static DEFINE_RWLOCK(entries_lock);
@@ -201,7 +205,13 @@ static int load_misc_binary(struct linux_binprm *bprm)
        if (retval < 0)
                goto error;
 
        if (retval < 0)
                goto error;
 
-       interp_file = open_exec(iname);
+       if (fmt->flags & MISC_FMT_OPEN_FILE && fmt->interp_file) {
+               interp_file = filp_clone_open(fmt->interp_file);
+               if (!IS_ERR(interp_file))
+                       deny_write_access(interp_file);
+       } else {
+               interp_file = open_exec(iname);
+       }
        retval = PTR_ERR(interp_file);
        if (IS_ERR(interp_file))
                goto error;
        retval = PTR_ERR(interp_file);
        if (IS_ERR(interp_file))
                goto error;
@@ -285,6 +295,11 @@ static char *check_special_flags(char *sfs, Node *e)
                        e->flags |= (MISC_FMT_CREDENTIALS |
                                        MISC_FMT_OPEN_BINARY);
                        break;
                        e->flags |= (MISC_FMT_CREDENTIALS |
                                        MISC_FMT_OPEN_BINARY);
                        break;
+               case 'F':
+                       pr_debug("register: flag: F: open interpreter file now\n");
+                       p++;
+                       e->flags |= MISC_FMT_OPEN_FILE;
+                       break;
                default:
                        cont = 0;
                }
                default:
                        cont = 0;
                }
@@ -543,6 +558,8 @@ static void entry_status(Node *e, char *page)
                *dp++ = 'O';
        if (e->flags & MISC_FMT_CREDENTIALS)
                *dp++ = 'C';
                *dp++ = 'O';
        if (e->flags & MISC_FMT_CREDENTIALS)
                *dp++ = 'C';
+       if (e->flags & MISC_FMT_OPEN_FILE)
+               *dp++ = 'F';
        *dp++ = '\n';
 
        if (!test_bit(Magic, &e->flags)) {
        *dp++ = '\n';
 
        if (!test_bit(Magic, &e->flags)) {
@@ -590,6 +607,11 @@ static void kill_node(Node *e)
        }
        write_unlock(&entries_lock);
 
        }
        write_unlock(&entries_lock);
 
+       if ((e->flags & MISC_FMT_OPEN_FILE) && e->interp_file) {
+               filp_close(e->interp_file, NULL);
+               e->interp_file = NULL;
+       }
+
        if (dentry) {
                drop_nlink(d_inode(dentry));
                d_drop(dentry);
        if (dentry) {
                drop_nlink(d_inode(dentry));
                d_drop(dentry);
@@ -696,6 +718,21 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer,
                goto out2;
        }
 
                goto out2;
        }
 
+       if (e->flags & MISC_FMT_OPEN_FILE) {
+               struct file *f;
+
+               f = open_exec(e->interpreter);
+               if (IS_ERR(f)) {
+                       err = PTR_ERR(f);
+                       pr_notice("register: failed to install interpreter file %s\n", e->interpreter);
+                       simple_release_fs(&bm_mnt, &entry_count);
+                       iput(inode);
+                       inode = NULL;
+                       goto out2;
+               }
+               e->interp_file = f;
+       }
+
        e->dentry = dget(dentry);
        inode->i_private = e;
        inode->i_fop = &bm_entry_operations;
        e->dentry = dget(dentry);
        inode->i_private = e;
        inode->i_fop = &bm_entry_operations;
@@ -713,7 +750,7 @@ out:
 
        if (err) {
                kfree(e);
 
        if (err) {
                kfree(e);
-               return -EINVAL;
+               return err;
        }
        return count;
 }
        }
        return count;
 }