linux-bk/fs/binfmt_misc.c
<<
>>
Prefs
   1/*
   2 *  binfmt_misc.c
   3 *
   4 *  Copyright (C) 1997 Richard Günther
   5 *
   6 *  binfmt_misc detects binaries via a magic or filename extension and invokes
   7 *  a specified wrapper. This should obsolete binfmt_java, binfmt_em86 and
   8 *  binfmt_mz.
   9 *
  10 *  1997-04-25 first version
  11 *  [...]
  12 *  1997-05-19 cleanup
  13 *  1997-06-26 hpa: pass the real filename rather than argv[0]
  14 *  1997-06-30 minor cleanup
  15 *  1997-08-09 removed extension stripping, locking cleanup
  16 *  2001-02-28 AV: rewritten into something that resembles C. Original didn't.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/init.h>
  21
  22#include <linux/binfmts.h>
  23#include <linux/slab.h>
  24#include <linux/ctype.h>
  25#include <linux/file.h>
  26#include <linux/pagemap.h>
  27#include <linux/namei.h>
  28
  29#include <asm/uaccess.h>
  30
  31enum {
  32        VERBOSE_STATUS = 1 /* make it zero to save 400 bytes kernel memory */
  33};
  34
  35static LIST_HEAD(entries);
  36static int enabled = 1;
  37
  38enum {Enabled, Magic};
  39
  40typedef struct {
  41        struct list_head list;
  42        unsigned long flags;            /* type, status, etc. */
  43        int offset;                     /* offset of magic */
  44        int size;                       /* size of magic/mask */
  45        char *magic;                    /* magic or filename extension */
  46        char *mask;                     /* mask, NULL for exact match */
  47        char *interpreter;              /* filename of interpreter */
  48        char *name;
  49        struct dentry *dentry;
  50} Node;
  51
  52static rwlock_t entries_lock __attribute__((unused)) = RW_LOCK_UNLOCKED;
  53static struct vfsmount *bm_mnt;
  54static int entry_count = 0;
  55
  56/* 
  57 * Check if we support the binfmt
  58 * if we do, return the node, else NULL
  59 * locking is done in load_misc_binary
  60 */
  61static Node *check_file(struct linux_binprm *bprm)
  62{
  63        char *p = strrchr(bprm->filename, '.');
  64        struct list_head *l;
  65
  66        list_for_each(l, &entries) {
  67                Node *e = list_entry(l, Node, list);
  68                char *s;
  69                int j;
  70
  71                if (!test_bit(Enabled, &e->flags))
  72                        continue;
  73
  74                if (!test_bit(Magic, &e->flags)) {
  75                        if (p && !strcmp(e->magic, p + 1))
  76                                return e;
  77                        continue;
  78                }
  79
  80                s = bprm->buf + e->offset;
  81                if (e->mask) {
  82                        for (j = 0; j < e->size; j++)
  83                                if ((*s++ ^ e->magic[j]) & e->mask[j])
  84                                        break;
  85                } else {
  86                        for (j = 0; j < e->size; j++)
  87                                if ((*s++ ^ e->magic[j]))
  88                                        break;
  89                }
  90                if (j == e->size)
  91                        return e;
  92        }
  93        return NULL;
  94}
  95
  96/*
  97 * the loader itself
  98 */
  99static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 100{
 101        Node *fmt;
 102        struct file * file;
 103        char iname[BINPRM_BUF_SIZE];
 104        char *iname_addr = iname;
 105        int retval;
 106
 107        retval = -ENOEXEC;
 108        if (!enabled)
 109                goto _ret;
 110
 111        /* to keep locking time low, we copy the interpreter string */
 112        read_lock(&entries_lock);
 113        fmt = check_file(bprm);
 114        if (fmt) {
 115                strncpy(iname, fmt->interpreter, BINPRM_BUF_SIZE - 1);
 116                iname[BINPRM_BUF_SIZE - 1] = '\0';
 117        }
 118        read_unlock(&entries_lock);
 119        if (!fmt)
 120                goto _ret;
 121
 122        allow_write_access(bprm->file);
 123        fput(bprm->file);
 124        bprm->file = NULL;
 125
 126        /* Build args for interpreter */
 127        remove_arg_zero(bprm);
 128        retval = copy_strings_kernel(1, &bprm->filename, bprm);
 129        if (retval < 0) goto _ret; 
 130        bprm->argc++;
 131        retval = copy_strings_kernel(1, &iname_addr, bprm);
 132        if (retval < 0) goto _ret; 
 133        bprm->argc++;
 134        bprm->filename = iname; /* for binfmt_script */
 135
 136        file = open_exec(iname);
 137        retval = PTR_ERR(file);
 138        if (IS_ERR(file))
 139                goto _ret;
 140        bprm->file = file;
 141
 142        retval = prepare_binprm(bprm);
 143        if (retval >= 0)
 144                retval = search_binary_handler(bprm, regs);
 145_ret:
 146        return retval;
 147}
 148
 149/* Command parsers */
 150
 151/*
 152 * parses and copies one argument enclosed in del from *sp to *dp,
 153 * recognising the \x special.
 154 * returns pointer to the copied argument or NULL in case of an
 155 * error (and sets err) or null argument length.
 156 */
 157static char *scanarg(char *s, char del)
 158{
 159        char c;
 160
 161        while ((c = *s++) != del) {
 162                if (c == '\\' && *s == 'x') {
 163                        s++;
 164                        if (!isxdigit(*s++))
 165                                return NULL;
 166                        if (!isxdigit(*s++))
 167                                return NULL;
 168                }
 169        }
 170        return s;
 171}
 172
 173static int unquote(char *from)
 174{
 175        char c = 0, *s = from, *p = from;
 176
 177        while ((c = *s++) != '\0') {
 178                if (c == '\\' && *s == 'x') {
 179                        s++;
 180                        c = toupper(*s++);
 181                        *p = (c - (isdigit(c) ? '0' : 'A' - 10)) << 4;
 182                        c = toupper(*s++);
 183                        *p++ |= c - (isdigit(c) ? '0' : 'A' - 10);
 184                        continue;
 185                }
 186                *p++ = c;
 187        }
 188        return p - from;
 189}
 190
 191/*
 192 * This registers a new binary format, it recognises the syntax
 193 * ':name:type:offset:magic:mask:interpreter:'
 194 * where the ':' is the IFS, that can be chosen with the first char
 195 */
 196static Node *create_entry(const char *buffer, size_t count)
 197{
 198        Node *e;
 199        int memsize, err;
 200        char *buf, *p;
 201        char del;
 202
 203        /* some sanity checks */
 204        err = -EINVAL;
 205        if ((count < 11) || (count > 256))
 206                goto out;
 207
 208        err = -ENOMEM;
 209        memsize = sizeof(Node) + count + 8;
 210        e = (Node *) kmalloc(memsize, GFP_USER);
 211        if (!e)
 212                goto out;
 213
 214        p = buf = (char *)e + sizeof(Node);
 215
 216        memset(e, 0, sizeof(Node));
 217        if (copy_from_user(buf, buffer, count))
 218                goto Efault;
 219
 220        del = *p++;     /* delimeter */
 221
 222        memset(buf+count, del, 8);
 223
 224        e->name = p;
 225        p = strchr(p, del);
 226        if (!p)
 227                goto Einval;
 228        *p++ = '\0';
 229        if (!e->name[0] ||
 230            !strcmp(e->name, ".") ||
 231            !strcmp(e->name, "..") ||
 232            strchr(e->name, '/'))
 233                goto Einval;
 234        switch (*p++) {
 235                case 'E': e->flags = 1<<Enabled; break;
 236                case 'M': e->flags = (1<<Enabled) | (1<<Magic); break;
 237                default: goto Einval;
 238        }
 239        if (*p++ != del)
 240                goto Einval;
 241        if (test_bit(Magic, &e->flags)) {
 242                char *s = strchr(p, del);
 243                if (!s)
 244                        goto Einval;
 245                *s++ = '\0';
 246                e->offset = simple_strtoul(p, &p, 10);
 247                if (*p++)
 248                        goto Einval;
 249                e->magic = p;
 250                p = scanarg(p, del);
 251                if (!p)
 252                        goto Einval;
 253                p[-1] = '\0';
 254                if (!e->magic[0])
 255                        goto Einval;
 256                e->mask = p;
 257                p = scanarg(p, del);
 258                if (!p)
 259                        goto Einval;
 260                p[-1] = '\0';
 261                if (!e->mask[0])
 262                        e->mask = NULL;
 263                e->size = unquote(e->magic);
 264                if (e->mask && unquote(e->mask) != e->size)
 265                        goto Einval;
 266                if (e->size + e->offset > BINPRM_BUF_SIZE)
 267                        goto Einval;
 268        } else {
 269                p = strchr(p, del);
 270                if (!p)
 271                        goto Einval;
 272                *p++ = '\0';
 273                e->magic = p;
 274                p = strchr(p, del);
 275                if (!p)
 276                        goto Einval;
 277                *p++ = '\0';
 278                if (!e->magic[0] || strchr(e->magic, '/'))
 279                        goto Einval;
 280                p = strchr(p, del);
 281                if (!p)
 282                        goto Einval;
 283                *p++ = '\0';
 284        }
 285        e->interpreter = p;
 286        p = strchr(p, del);
 287        if (!p)
 288                goto Einval;
 289        *p++ = '\0';
 290        if (!e->interpreter[0])
 291                goto Einval;
 292
 293        if (*p == '\n')
 294                p++;
 295        if (p != buf + count)
 296                goto Einval;
 297        return e;
 298
 299out:
 300        return ERR_PTR(err);
 301
 302Efault:
 303        kfree(e);
 304        return ERR_PTR(-EFAULT);
 305Einval:
 306        kfree(e);
 307        return ERR_PTR(-EINVAL);
 308}
 309
 310/*
 311 * Set status of entry/binfmt_misc:
 312 * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
 313 */
 314static int parse_command(const char *buffer, size_t count)
 315{
 316        char s[4];
 317
 318        if (!count)
 319                return 0;
 320        if (count > 3)
 321                return -EINVAL;
 322        if (copy_from_user(s, buffer, count))
 323                return -EFAULT;
 324        if (s[count-1] == '\n')
 325                count--;
 326        if (count == 1 && s[0] == '0')
 327                return 1;
 328        if (count == 1 && s[0] == '1')
 329                return 2;
 330        if (count == 2 && s[0] == '-' && s[1] == '1')
 331                return 3;
 332        return -EINVAL;
 333}
 334
 335/* generic stuff */
 336
 337static void entry_status(Node *e, char *page)
 338{
 339        char *dp;
 340        char *status = "disabled";
 341
 342        if (test_bit(Enabled, &e->flags))
 343                status = "enabled";
 344
 345        if (!VERBOSE_STATUS) {
 346                sprintf(page, "%s\n", status);
 347                return;
 348        }
 349
 350        sprintf(page, "%s\ninterpreter %s\n", status, e->interpreter);
 351        dp = page + strlen(page);
 352        if (!test_bit(Magic, &e->flags)) {
 353                sprintf(dp, "extension .%s\n", e->magic);
 354        } else {
 355                int i;
 356
 357                sprintf(dp, "offset %i\nmagic ", e->offset);
 358                dp = page + strlen(page);
 359                for (i = 0; i < e->size; i++) {
 360                        sprintf(dp, "%02x", 0xff & (int) (e->magic[i]));
 361                        dp += 2;
 362                }
 363                if (e->mask) {
 364                        sprintf(dp, "\nmask ");
 365                        dp += 6;
 366                        for (i = 0; i < e->size; i++) {
 367                                sprintf(dp, "%02x", 0xff & (int) (e->mask[i]));
 368                                dp += 2;
 369                        }
 370                }
 371                *dp++ = '\n';
 372                *dp = '\0';
 373        }
 374}
 375
 376static struct inode *bm_get_inode(struct super_block *sb, int mode)
 377{
 378        struct inode * inode = new_inode(sb);
 379
 380        if (inode) {
 381                inode->i_mode = mode;
 382                inode->i_uid = 0;
 383                inode->i_gid = 0;
 384                inode->i_blksize = PAGE_CACHE_SIZE;
 385                inode->i_blocks = 0;
 386                inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 387        }
 388        return inode;
 389}
 390
 391static void bm_clear_inode(struct inode *inode)
 392{
 393        Node *e = inode->u.generic_ip;
 394
 395        if (e) {
 396                struct vfsmount *mnt;
 397                write_lock(&entries_lock);
 398                list_del(&e->list);
 399                mnt = bm_mnt;
 400                if (!--entry_count)
 401                        bm_mnt = NULL;
 402                write_unlock(&entries_lock);
 403                kfree(e);
 404                mntput(mnt);
 405        }
 406}
 407
 408static void kill_node(Node *e)
 409{
 410        struct dentry *dentry;
 411
 412        write_lock(&entries_lock);
 413        dentry = e->dentry;
 414        if (dentry) {
 415                list_del_init(&e->list);
 416                e->dentry = NULL;
 417        }
 418        write_unlock(&entries_lock);
 419
 420        if (dentry) {
 421                dentry->d_inode->i_nlink--;
 422                d_drop(dentry);
 423                dput(dentry);
 424        }
 425}
 426
 427/* /<entry> */
 428
 429static ssize_t
 430bm_entry_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
 431{
 432        Node *e = file->f_dentry->d_inode->u.generic_ip;
 433        loff_t pos = *ppos;
 434        ssize_t res;
 435        char *page;
 436        int len;
 437
 438        if (!(page = (char*) __get_free_page(GFP_KERNEL)))
 439                return -ENOMEM;
 440
 441        entry_status(e, page);
 442        len = strlen(page);
 443
 444        res = -EINVAL;
 445        if (pos < 0)
 446                goto out;
 447        res = 0;
 448        if (pos >= len)
 449                goto out;
 450        if (len < pos + nbytes)
 451                nbytes = len - pos;
 452        res = -EFAULT;
 453        if (copy_to_user(buf, page + pos, nbytes))
 454                goto out;
 455        *ppos = pos + nbytes;
 456        res = nbytes;
 457out:
 458        free_page((unsigned long) page);
 459        return res;
 460}
 461
 462static ssize_t bm_entry_write(struct file *file, const char *buffer,
 463                                size_t count, loff_t *ppos)
 464{
 465        struct dentry *root;
 466        Node *e = file->f_dentry->d_inode->u.generic_ip;
 467        int res = parse_command(buffer, count);
 468
 469        switch (res) {
 470                case 1: clear_bit(Enabled, &e->flags);
 471                        break;
 472                case 2: set_bit(Enabled, &e->flags);
 473                        break;
 474                case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
 475                        down(&root->d_inode->i_sem);
 476
 477                        kill_node(e);
 478
 479                        up(&root->d_inode->i_sem);
 480                        dput(root);
 481                        break;
 482                default: return res;
 483        }
 484        return count;
 485}
 486
 487static struct file_operations bm_entry_operations = {
 488        .read           = bm_entry_read,
 489        .write          = bm_entry_write,
 490};
 491
 492static struct file_system_type bm_fs_type;
 493
 494/* /register */
 495
 496static ssize_t bm_register_write(struct file *file, const char *buffer,
 497                               size_t count, loff_t *ppos)
 498{
 499        Node *e;
 500        struct inode *inode;
 501        struct vfsmount *mnt = NULL;
 502        struct dentry *root, *dentry;
 503        struct super_block *sb = file->f_vfsmnt->mnt_sb;
 504        int err = 0;
 505
 506        e = create_entry(buffer, count);
 507
 508        if (IS_ERR(e))
 509                return PTR_ERR(e);
 510
 511        root = dget(sb->s_root);
 512        down(&root->d_inode->i_sem);
 513        dentry = lookup_one_len(e->name, root, strlen(e->name));
 514        err = PTR_ERR(dentry);
 515        if (IS_ERR(dentry))
 516                goto out;
 517
 518        err = -EEXIST;
 519        if (dentry->d_inode)
 520                goto out2;
 521
 522        inode = bm_get_inode(sb, S_IFREG | 0644);
 523
 524        err = -ENOMEM;
 525        if (!inode)
 526                goto out2;
 527
 528        write_lock(&entries_lock);
 529        if (!bm_mnt) {
 530                write_unlock(&entries_lock);
 531                mnt = kern_mount(&bm_fs_type);
 532                if (IS_ERR(mnt)) {
 533                        err = PTR_ERR(mnt);
 534                        iput(inode);
 535                        inode = NULL;
 536                        goto out2;
 537                }
 538                write_lock(&entries_lock);
 539                if (!bm_mnt)
 540                        bm_mnt = mnt;
 541        }
 542        mntget(bm_mnt);
 543        entry_count++;
 544
 545        e->dentry = dget(dentry);
 546        inode->u.generic_ip = e;
 547        inode->i_fop = &bm_entry_operations;
 548        d_instantiate(dentry, inode);
 549
 550        list_add(&e->list, &entries);
 551        write_unlock(&entries_lock);
 552
 553        mntput(mnt);
 554        err = 0;
 555out2:
 556        dput(dentry);
 557out:
 558        up(&root->d_inode->i_sem);
 559        dput(root);
 560
 561        if (err) {
 562                kfree(e);
 563                return -EINVAL;
 564        }
 565        return count;
 566}
 567
 568static struct file_operations bm_register_operations = {
 569        .write          = bm_register_write,
 570};
 571
 572/* /status */
 573
 574static ssize_t
 575bm_status_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
 576{
 577        char *s = enabled ? "enabled" : "disabled";
 578        int len = strlen(s);
 579        loff_t pos = *ppos;
 580
 581        if (pos < 0)
 582                return -EINVAL;
 583        if (pos >= len)
 584                return 0;
 585        if (len < pos + nbytes)
 586                nbytes = len - pos;
 587        if (copy_to_user(buf, s + pos, nbytes))
 588                return -EFAULT;
 589        *ppos = pos + nbytes;
 590        return nbytes;
 591}
 592
 593static ssize_t bm_status_write(struct file * file, const char * buffer,
 594                size_t count, loff_t *ppos)
 595{
 596        int res = parse_command(buffer, count);
 597        struct dentry *root;
 598
 599        switch (res) {
 600                case 1: enabled = 0; break;
 601                case 2: enabled = 1; break;
 602                case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
 603                        down(&root->d_inode->i_sem);
 604
 605                        while (!list_empty(&entries))
 606                                kill_node(list_entry(entries.next, Node, list));
 607
 608                        up(&root->d_inode->i_sem);
 609                        dput(root);
 610                default: return res;
 611        }
 612        return count;
 613}
 614
 615static struct file_operations bm_status_operations = {
 616        .read           = bm_status_read,
 617        .write          = bm_status_write,
 618};
 619
 620/* Superblock handling */
 621
 622static struct super_operations s_ops = {
 623        .statfs         = simple_statfs,
 624        .drop_inode     = generic_delete_inode,
 625        .clear_inode    = bm_clear_inode,
 626};
 627
 628static int bm_fill_super(struct super_block * sb, void * data, int silent)
 629{
 630        struct qstr names[2] = {{.name = "status"}, {.name = "register"}};
 631        struct inode * inode;
 632        struct dentry * dentry[3];
 633        int i;
 634
 635        for (i=0; i<sizeof(names)/sizeof(names[0]); i++) {
 636                names[i].len = strlen(names[i].name);
 637                names[i].hash = full_name_hash(names[i].name, names[i].len);
 638        }
 639
 640        sb->s_blocksize = PAGE_CACHE_SIZE;
 641        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
 642        sb->s_magic = 0x42494e4d;
 643        sb->s_op = &s_ops;
 644
 645        inode = bm_get_inode(sb, S_IFDIR | 0755);
 646        if (!inode)
 647                return -ENOMEM;
 648        inode->i_op = &simple_dir_inode_operations;
 649        inode->i_fop = &simple_dir_operations;
 650        dentry[0] = d_alloc_root(inode);
 651        if (!dentry[0]) {
 652                iput(inode);
 653                return -ENOMEM;
 654        }
 655        dentry[1] = d_alloc(dentry[0], &names[0]);
 656        if (!dentry[1])
 657                goto out1;
 658        dentry[2] = d_alloc(dentry[0], &names[1]);
 659        if (!dentry[2])
 660                goto out2;
 661        inode = bm_get_inode(sb, S_IFREG | 0644);
 662        if (!inode)
 663                goto out3;
 664        inode->i_fop = &bm_status_operations;
 665        d_add(dentry[1], inode);
 666        inode = bm_get_inode(sb, S_IFREG | 0400);
 667        if (!inode)
 668                goto out3;
 669        inode->i_fop = &bm_register_operations;
 670        d_add(dentry[2], inode);
 671
 672        sb->s_root = dentry[0];
 673        return 0;
 674
 675out3:
 676        dput(dentry[2]);
 677out2:
 678        dput(dentry[1]);
 679out1:
 680        dput(dentry[0]);
 681        return -ENOMEM;
 682}
 683
 684static struct super_block *bm_get_sb(struct file_system_type *fs_type,
 685        int flags, char *dev_name, void *data)
 686{
 687        return get_sb_single(fs_type, flags, data, bm_fill_super);
 688}
 689
 690static struct linux_binfmt misc_format = {
 691        .module = THIS_MODULE,
 692        .load_binary = load_misc_binary,
 693};
 694
 695static struct file_system_type bm_fs_type = {
 696        .owner          = THIS_MODULE,
 697        .name           = "binfmt_misc",
 698        .get_sb         = bm_get_sb,
 699        .kill_sb        = kill_litter_super,
 700};
 701
 702static int __init init_misc_binfmt(void)
 703{
 704        int err = register_filesystem(&bm_fs_type);
 705        if (!err) {
 706                err = register_binfmt(&misc_format);
 707                if (err)
 708                        unregister_filesystem(&bm_fs_type);
 709        }
 710        return err;
 711}
 712
 713static void __exit exit_misc_binfmt(void)
 714{
 715        unregister_binfmt(&misc_format);
 716        unregister_filesystem(&bm_fs_type);
 717}
 718
 719module_init(init_misc_binfmt);
 720module_exit(exit_misc_binfmt);
 721MODULE_LICENSE("GPL");
 722
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.