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