fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / binfmt_misc.c
index 9f2c43a..c2e0825 100644 (file)
@@ -39,6 +39,8 @@ static int enabled = 1;
 
 enum {Enabled, Magic};
 #define MISC_FMT_PRESERVE_ARGV0 (1<<31)
+#define MISC_FMT_OPEN_BINARY (1<<30)
+#define MISC_FMT_CREDENTIALS (1<<29)
 
 typedef struct {
        struct list_head list;
@@ -52,7 +54,8 @@ typedef struct {
        struct dentry *dentry;
 } Node;
 
-static rwlock_t entries_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(entries_lock);
+static struct file_system_type bm_fs_type;
 static struct vfsmount *bm_mnt;
 static int entry_count;
 
@@ -102,10 +105,12 @@ static Node *check_file(struct linux_binprm *bprm)
 static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 {
        Node *fmt;
-       struct file * file;
+       struct file * interp_file = NULL;
        char iname[BINPRM_BUF_SIZE];
        char *iname_addr = iname;
        int retval;
+       int fd_binary = -1;
+       struct files_struct *files = NULL;
 
        retval = -ENOEXEC;
        if (!enabled)
@@ -120,33 +125,99 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
        if (!fmt)
                goto _ret;
 
-       allow_write_access(bprm->file);
-       fput(bprm->file);
-       bprm->file = NULL;
-
-       /* Build args for interpreter */
        if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) {
                remove_arg_zero(bprm);
        }
-       retval = copy_strings_kernel(1, &bprm->interp, bprm);
-       if (retval < 0) goto _ret; 
-       bprm->argc++;
-       retval = copy_strings_kernel(1, &iname_addr, bprm);
-       if (retval < 0) goto _ret; 
+
+       if (fmt->flags & MISC_FMT_OPEN_BINARY) {
+
+               files = current->files;
+               retval = unshare_files();
+               if (retval < 0)
+                       goto _ret;
+               if (files == current->files) {
+                       put_files_struct(files);
+                       files = NULL;
+               }
+               /* if the binary should be opened on behalf of the
+                * interpreter than keep it open and assign descriptor
+                * to it */
+               fd_binary = get_unused_fd();
+               if (fd_binary < 0) {
+                       retval = fd_binary;
+                       goto _unshare;
+               }
+               fd_install(fd_binary, bprm->file);
+
+               /* if the binary is not readable than enforce mm->dumpable=0
+                  regardless of the interpreter's permissions */
+               if (file_permission(bprm->file, MAY_READ))
+                       bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
+
+               allow_write_access(bprm->file);
+               bprm->file = NULL;
+
+               /* mark the bprm that fd should be passed to interp */
+               bprm->interp_flags |= BINPRM_FLAGS_EXECFD;
+               bprm->interp_data = fd_binary;
+
+       } else {
+               allow_write_access(bprm->file);
+               fput(bprm->file);
+               bprm->file = NULL;
+       }
+       /* make argv[1] be the path to the binary */
+       retval = copy_strings_kernel (1, &bprm->interp, bprm);
+       if (retval < 0)
+               goto _error;
        bprm->argc++;
-       bprm->interp = iname;   /* for binfmt_script */
 
-       file = open_exec(iname);
-       retval = PTR_ERR(file);
-       if (IS_ERR(file))
-               goto _ret;
-       bprm->file = file;
+       /* add the interp as argv[0] */
+       retval = copy_strings_kernel (1, &iname_addr, bprm);
+       if (retval < 0)
+               goto _error;
+       bprm->argc ++;
+
+       bprm->interp = iname;   /* for binfmt_script */
 
-       retval = prepare_binprm(bprm);
-       if (retval >= 0)
-               retval = search_binary_handler(bprm, regs);
+       interp_file = open_exec (iname);
+       retval = PTR_ERR (interp_file);
+       if (IS_ERR (interp_file))
+               goto _error;
+
+       bprm->file = interp_file;
+       if (fmt->flags & MISC_FMT_CREDENTIALS) {
+               /*
+                * No need to call prepare_binprm(), it's already been
+                * done.  bprm->buf is stale, update from interp_file.
+                */
+               memset(bprm->buf, 0, BINPRM_BUF_SIZE);
+               retval = kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
+       } else
+               retval = prepare_binprm (bprm);
+
+       if (retval < 0)
+               goto _error;
+
+       retval = search_binary_handler (bprm, regs);
+       if (retval < 0)
+               goto _error;
+
+       if (files) {
+               put_files_struct(files);
+               files = NULL;
+       }
 _ret:
        return retval;
+_error:
+       if (fd_binary > 0)
+               sys_close(fd_binary);
+       bprm->interp_flags = 0;
+       bprm->interp_data = 0;
+_unshare:
+       if (files)
+               reset_files_struct(current, files);
+       goto _ret;
 }
 
 /* Command parsers */
@@ -191,9 +262,39 @@ static int unquote(char *from)
        return p - from;
 }
 
+static char * check_special_flags (char * sfs, Node * e)
+{
+       char * p = sfs;
+       int cont = 1;
+
+       /* special flags */
+       while (cont) {
+               switch (*p) {
+                       case 'P':
+                               p++;
+                               e->flags |= MISC_FMT_PRESERVE_ARGV0;
+                               break;
+                       case 'O':
+                               p++;
+                               e->flags |= MISC_FMT_OPEN_BINARY;
+                               break;
+                       case 'C':
+                               p++;
+                               /* this flags also implies the
+                                  open-binary flag */
+                               e->flags |= (MISC_FMT_CREDENTIALS |
+                                               MISC_FMT_OPEN_BINARY);
+                               break;
+                       default:
+                               cont = 0;
+               }
+       }
+
+       return p;
+}
 /*
  * This registers a new binary format, it recognises the syntax
- * ':name:type:offset:magic:mask:interpreter:'
+ * ':name:type:offset:magic:mask:interpreter:flags'
  * where the ':' is the IFS, that can be chosen with the first char
  */
 static Node *create_entry(const char __user *buffer, size_t count)
@@ -210,7 +311,7 @@ static Node *create_entry(const char __user *buffer, size_t count)
 
        err = -ENOMEM;
        memsize = sizeof(Node) + count + 8;
-       e = (Node *) kmalloc(memsize, GFP_USER);
+       e = kmalloc(memsize, GFP_USER);
        if (!e)
                goto out;
 
@@ -293,10 +394,8 @@ static Node *create_entry(const char __user *buffer, size_t count)
        if (!e->interpreter[0])
                goto Einval;
 
-       if (*p == 'P') {
-               p++;
-               e->flags |= MISC_FMT_PRESERVE_ARGV0;
-       }
+
+       p = check_special_flags (p, e);
 
        if (*p == '\n')
                p++;
@@ -346,6 +445,7 @@ static void entry_status(Node *e, char *page)
 {
        char *dp;
        char *status = "disabled";
+       const char * flags = "flags: ";
 
        if (test_bit(Enabled, &e->flags))
                status = "enabled";
@@ -357,6 +457,22 @@ static void entry_status(Node *e, char *page)
 
        sprintf(page, "%s\ninterpreter %s\n", status, e->interpreter);
        dp = page + strlen(page);
+
+       /* print the special flags */
+       sprintf (dp, "%s", flags);
+       dp += strlen (flags);
+       if (e->flags & MISC_FMT_PRESERVE_ARGV0) {
+               *dp ++ = 'P';
+       }
+       if (e->flags & MISC_FMT_OPEN_BINARY) {
+               *dp ++ = 'O';
+       }
+       if (e->flags & MISC_FMT_CREDENTIALS) {
+               *dp ++ = 'C';
+       }
+       *dp ++ = '\n';
+
+
        if (!test_bit(Magic, &e->flags)) {
                sprintf(dp, "extension .%s\n", e->magic);
        } else {
@@ -389,16 +505,16 @@ static struct inode *bm_get_inode(struct super_block *sb, int mode)
                inode->i_mode = mode;
                inode->i_uid = 0;
                inode->i_gid = 0;
-               inode->i_blksize = PAGE_CACHE_SIZE;
                inode->i_blocks = 0;
-               inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               inode->i_atime = inode->i_mtime = inode->i_ctime =
+                       current_fs_time(inode->i_sb);
        }
        return inode;
 }
 
 static void bm_clear_inode(struct inode *inode)
 {
-       kfree(inode->u.generic_ip);
+       kfree(inode->i_private);
 }
 
 static void kill_node(Node *e)
@@ -426,7 +542,7 @@ static void kill_node(Node *e)
 static ssize_t
 bm_entry_read(struct file * file, char __user * buf, size_t nbytes, loff_t *ppos)
 {
-       Node *e = file->f_dentry->d_inode->u.generic_ip;
+       Node *e = file->f_path.dentry->d_inode->i_private;
        loff_t pos = *ppos;
        ssize_t res;
        char *page;
@@ -460,7 +576,7 @@ static ssize_t bm_entry_write(struct file *file, const char __user *buffer,
                                size_t count, loff_t *ppos)
 {
        struct dentry *root;
-       Node *e = file->f_dentry->d_inode->u.generic_ip;
+       Node *e = file->f_path.dentry->d_inode->i_private;
        int res = parse_command(buffer, count);
 
        switch (res) {
@@ -468,12 +584,12 @@ static ssize_t bm_entry_write(struct file *file, const char __user *buffer,
                        break;
                case 2: set_bit(Enabled, &e->flags);
                        break;
-               case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
-                       down(&root->d_inode->i_sem);
+               case 3: root = dget(file->f_path.mnt->mnt_sb->s_root);
+                       mutex_lock(&root->d_inode->i_mutex);
 
                        kill_node(e);
 
-                       up(&root->d_inode->i_sem);
+                       mutex_unlock(&root->d_inode->i_mutex);
                        dput(root);
                        break;
                default: return res;
@@ -481,7 +597,7 @@ static ssize_t bm_entry_write(struct file *file, const char __user *buffer,
        return count;
 }
 
-static struct file_operations bm_entry_operations = {
+static const struct file_operations bm_entry_operations = {
        .read           = bm_entry_read,
        .write          = bm_entry_write,
 };
@@ -494,7 +610,7 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer,
        Node *e;
        struct inode *inode;
        struct dentry *root, *dentry;
-       struct super_block *sb = file->f_vfsmnt->mnt_sb;
+       struct super_block *sb = file->f_path.mnt->mnt_sb;
        int err = 0;
 
        e = create_entry(buffer, count);
@@ -503,7 +619,7 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer,
                return PTR_ERR(e);
 
        root = dget(sb->s_root);
-       down(&root->d_inode->i_sem);
+       mutex_lock(&root->d_inode->i_mutex);
        dentry = lookup_one_len(e->name, root, strlen(e->name));
        err = PTR_ERR(dentry);
        if (IS_ERR(dentry))
@@ -519,7 +635,7 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer,
        if (!inode)
                goto out2;
 
-       err = simple_pin_fs("binfmt_misc", &bm_mnt, &entry_count);
+       err = simple_pin_fs(&bm_fs_type, &bm_mnt, &entry_count);
        if (err) {
                iput(inode);
                inode = NULL;
@@ -527,7 +643,7 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer,
        }
 
        e->dentry = dget(dentry);
-       inode->u.generic_ip = e;
+       inode->i_private = e;
        inode->i_fop = &bm_entry_operations;
 
        d_instantiate(dentry, inode);
@@ -539,7 +655,7 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer,
 out2:
        dput(dentry);
 out:
-       up(&root->d_inode->i_sem);
+       mutex_unlock(&root->d_inode->i_mutex);
        dput(root);
 
        if (err) {
@@ -549,7 +665,7 @@ out:
        return count;
 }
 
-static struct file_operations bm_register_operations = {
+static const struct file_operations bm_register_operations = {
        .write          = bm_register_write,
 };
 
@@ -583,20 +699,20 @@ static ssize_t bm_status_write(struct file * file, const char __user * buffer,
        switch (res) {
                case 1: enabled = 0; break;
                case 2: enabled = 1; break;
-               case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
-                       down(&root->d_inode->i_sem);
+               case 3: root = dget(file->f_path.mnt->mnt_sb->s_root);
+                       mutex_lock(&root->d_inode->i_mutex);
 
                        while (!list_empty(&entries))
                                kill_node(list_entry(entries.next, Node, list));
 
-                       up(&root->d_inode->i_sem);
+                       mutex_unlock(&root->d_inode->i_mutex);
                        dput(root);
                default: return res;
        }
        return count;
 }
 
-static struct file_operations bm_status_operations = {
+static const struct file_operations bm_status_operations = {
        .read           = bm_status_read,
        .write          = bm_status_write,
 };
@@ -621,10 +737,10 @@ static int bm_fill_super(struct super_block * sb, void * data, int silent)
        return err;
 }
 
-static struct super_block *bm_get_sb(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *data)
+static int bm_get_sb(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *data, struct vfsmount *mnt)
 {
-       return get_sb_single(fs_type, flags, data, bm_fill_super);
+       return get_sb_single(fs_type, flags, data, bm_fill_super, mnt);
 }
 
 static struct linux_binfmt misc_format = {