X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fbinfmt_misc.c;h=c2e08252af35d5dd3140ff32d1cf79bbfd3521c7;hb=refs%2Fheads%2Fvserver;hp=9f2c43abaf8cb85142a99995395101bee548b66c;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 9f2c43aba..c2e08252a 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -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 = {