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 */
  17
  18#include <linux/config.h>
  19#include <linux/module.h>
  20
  21#include <linux/kernel.h>
  22#include <linux/errno.h>
  23#include <linux/fs.h>
  24#include <linux/malloc.h>
  25#include <linux/binfmts.h>
  26#include <linux/init.h>
  27#include <linux/proc_fs.h>
  28#include <linux/string.h>
  29#include <linux/ctype.h>
  30#include <asm/uaccess.h>
  31#include <asm/spinlock.h>
  32
  33/*
  34 * We should make this work with a "stub-only" /proc,
  35 * which would just not be able to be configured.
  36 * Right now the /proc-fs support is too black and white,
  37 * though, so just remind people that this should be
  38 * fixed..
  39 */
  40#ifndef CONFIG_PROC_FS
  41#error You really need /proc support for binfmt_misc. Please reconfigure!
  42#endif
  43
  44enum {
  45        VERBOSE_STATUS = 1 /* define as zero to save 400 bytes kernel memory */
  46};
  47
  48typedef struct binfmt_entry {
  49        struct binfmt_entry *next;
  50        long id;
  51        long flags;                     /* type, status, etc. */
  52        int offset;                     /* offset of magic */
  53        int size;                       /* size of magic/mask */
  54        char *magic;                    /* magic or filename extension */
  55        char *mask;                     /* mask, NULL for exact match */
  56        char *interpreter;              /* filename of interpreter */
  57        char *name;
  58        struct proc_dir_entry *proc_dir;
  59} Node;
  60
  61enum { Enabled, Magic };
  62
  63static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs);
  64static void entry_proc_cleanup(Node *e);
  65static int entry_proc_setup(Node *e);
  66
  67static struct linux_binfmt misc_format = {
  68        module:         THIS_MODULE,
  69        load_binary:    load_misc_binary,
  70};
  71
  72static struct proc_dir_entry *bm_dir = NULL;
  73
  74static Node *entries = NULL;
  75static int free_id = 1;
  76static int enabled = 1;
  77
  78static rwlock_t entries_lock __attribute__((unused)) = RW_LOCK_UNLOCKED;
  79
  80
  81/*
  82 * Unregister one entry
  83 */
  84static void clear_entry(int id)
  85{
  86        Node **ep, *e;
  87
  88        write_lock(&entries_lock);
  89        ep = &entries;
  90        while (*ep && ((*ep)->id != id))
  91                ep = &((*ep)->next);
  92        if ((e = *ep))
  93                *ep = e->next;
  94        write_unlock(&entries_lock);
  95
  96        if (e) {
  97                entry_proc_cleanup(e);
  98                kfree(e);
  99        }
 100}
 101
 102/*
 103 * Clear all registered binary formats
 104 */
 105static void clear_entries(void)
 106{
 107        Node *e, *n;
 108
 109        write_lock(&entries_lock);
 110        n = entries;
 111        entries = NULL;
 112        write_unlock(&entries_lock);
 113
 114        while ((e = n)) {
 115                n = e->next;
 116                entry_proc_cleanup(e);
 117                kfree(e);
 118        }
 119}
 120
 121/*
 122 * Find entry through id and lock it
 123 */
 124static Node *get_entry(int id)
 125{
 126        Node *e;
 127
 128        read_lock(&entries_lock);
 129        e = entries;
 130        while (e && (e->id != id))
 131                e = e->next;
 132        if (!e)
 133                read_unlock(&entries_lock);
 134        return e;
 135}
 136
 137/*
 138 * unlock entry
 139 */
 140static inline void put_entry(Node *e)
 141{
 142        if (e)
 143                read_unlock(&entries_lock);
 144}
 145
 146
 147/* 
 148 * Check if we support the binfmt
 149 * if we do, return the node, else NULL
 150 * locking is done in load_misc_binary
 151 */
 152static Node *check_file(struct linux_binprm *bprm)
 153{
 154        Node *e;
 155        char *p = strrchr(bprm->filename, '.');
 156        int j;
 157
 158        e = entries;
 159        while (e) {
 160                if (test_bit(Enabled, &e->flags)) {
 161                        if (!test_bit(Magic, &e->flags)) {
 162                                if (p && !strcmp(e->magic, p + 1))
 163                                        return e;
 164                        } else {
 165                                j = 0;
 166                                while ((j < e->size) &&
 167                                  !((bprm->buf[e->offset + j] ^ e->magic[j])
 168                                   & (e->mask ? e->mask[j] : 0xff)))
 169                                        j++;
 170                                if (j == e->size)
 171                                        return e;
 172                        }
 173                }
 174                e = e->next;
 175        };
 176        return NULL;
 177}
 178
 179/*
 180 * the loader itself
 181 */
 182static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 183{
 184        Node *fmt;
 185        struct dentry * dentry;
 186        char iname[128];
 187        char *iname_addr = iname;
 188        int retval;
 189
 190        retval = -ENOEXEC;
 191        if (!enabled)
 192                goto _ret;
 193
 194        /* to keep locking time low, we copy the interpreter string */
 195        read_lock(&entries_lock);
 196        fmt = check_file(bprm);
 197        if (fmt) {
 198                strncpy(iname, fmt->interpreter, 127);
 199                iname[127] = '\0';
 200        }
 201        read_unlock(&entries_lock);
 202        if (!fmt)
 203                goto _ret;
 204
 205        dput(bprm->dentry);
 206        bprm->dentry = NULL;
 207
 208        /* Build args for interpreter */
 209        remove_arg_zero(bprm);
 210        bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
 211        bprm->argc++;
 212        bprm->p = copy_strings(1, &iname_addr, bprm->page, bprm->p, 2);
 213        bprm->argc++;
 214        retval = (long)bprm->p;
 215        if ((long)bprm->p < 0)
 216                goto _ret;
 217        bprm->filename = iname; /* for binfmt_script */
 218
 219        dentry = open_namei(iname, 0, 0);
 220        retval = PTR_ERR(dentry);
 221        if (IS_ERR(dentry))
 222                goto _ret;
 223        bprm->dentry = dentry;
 224
 225        retval = prepare_binprm(bprm);
 226        if (retval >= 0)
 227                retval = search_binary_handler(bprm, regs);
 228_ret:
 229        return retval;
 230}
 231
 232/*
 233 * parses and copies one argument enclosed in del from *sp to *dp,
 234 * recognising the \x special.
 235 * returns pointer to the copied argument or NULL in case of an
 236 * error (and sets err) or null argument length.
 237 */
 238static char *scanarg(char *s, char del)
 239{
 240        char c;
 241
 242        while ((c = *s++) != del) {
 243                if (c == '\\' && *s == 'x') {
 244                        s++;
 245                        if (!isxdigit(*s++))
 246                                return NULL;
 247                        if (!isxdigit(*s++))
 248                                return NULL;
 249                }
 250        }
 251        return s;
 252}
 253
 254static int unquote(char *from)
 255{
 256        char c = 0, *s = from, *p = from;
 257
 258        while ((c = *s++) != '\0') {
 259                if (c == '\\' && *s == 'x') {
 260                        s++;
 261                        c = toupper(*s++);
 262                        *p = (c - (isdigit(c) ? '0' : 'A' - 10)) << 4;
 263                        c = toupper(*s++);
 264                        *p++ |= c - (isdigit(c) ? '0' : 'A' - 10);
 265                        continue;
 266                }
 267                *p++ = c;
 268        }
 269        return p - from;
 270}
 271
 272/*
 273 * This registers a new binary format, it recognises the syntax
 274 * ':name:type:offset:magic:mask:interpreter:'
 275 * where the ':' is the IFS, that can be chosen with the first char
 276 */
 277static Node *create_entry(const char *buffer, size_t count)
 278{
 279        Node *e;
 280        int memsize, err;
 281        char *buf, *p;
 282        char del;
 283
 284        /* some sanity checks */
 285        err = -EINVAL;
 286        if ((count < 11) || (count > 256))
 287                goto out;
 288
 289        err = -ENOMEM;
 290        memsize = sizeof(Node) + count + 8;
 291        e = (Node *) kmalloc(memsize, GFP_USER);
 292        if (!e)
 293                goto out;
 294
 295        p = buf = (char *)e + sizeof(Node);
 296
 297        memset(e, 0, sizeof(Node));
 298        if (copy_from_user(buf, buffer, count))
 299                goto Efault;
 300
 301        del = *p++;     /* delimeter */
 302
 303        memset(buf+count, del, 8);
 304
 305        e->name = p;
 306        p = strchr(p, del);
 307        if (!p)
 308                goto Einval;
 309        *p++ = '\0';
 310        if (!e->name[0] ||
 311            !strcmp(e->name, ".") ||
 312            !strcmp(e->name, "..") ||
 313            strchr(e->name, '/'))
 314                goto Einval;
 315        switch (*p++) {
 316                case 'E': e->flags = 1<<Enabled; break;
 317                case 'M': e->flags = (1<<Enabled) | (1<<Magic); break;
 318                default: goto Einval;
 319        }
 320        if (*p++ != del)
 321                goto Einval;
 322        if (test_bit(Magic, &e->flags)) {
 323                char *s = strchr(p, del);
 324                if (!s)
 325                        goto Einval;
 326                *s++ = '\0';
 327                e->offset = simple_strtoul(p, &p, 10);
 328                if (*p++)
 329                        goto Einval;
 330                e->magic = p;
 331                p = scanarg(p, del);
 332                if (!p)
 333                        goto Einval;
 334                p[-1] = '\0';
 335                if (!e->magic[0])
 336                        goto Einval;
 337                e->mask = p;
 338                p = scanarg(p, del);
 339                if (!p)
 340                        goto Einval;
 341                p[-1] = '\0';
 342                if (!e->mask[0])
 343                        e->mask = NULL;
 344                e->size = unquote(e->magic);
 345                if (e->mask && unquote(e->mask) != e->size)
 346                        goto Einval;
 347                if (e->size + e->offset > 128)
 348                        goto Einval;
 349        } else {
 350                p = strchr(p, del);
 351                if (!p)
 352                        goto Einval;
 353                *p++ = '\0';
 354                e->magic = p;
 355                p = strchr(p, del);
 356                if (!p)
 357                        goto Einval;
 358                *p++ = '\0';
 359                if (!e->magic[0] || strchr(e->magic, '/'))
 360                        goto Einval;
 361                p = strchr(p, del);
 362                if (!p)
 363                        goto Einval;
 364                *p++ = '\0';
 365        }
 366        e->interpreter = p;
 367        p = strchr(p, del);
 368        if (!p)
 369                goto Einval;
 370        *p++ = '\0';
 371        if (!e->interpreter[0])
 372                goto Einval;
 373
 374        if (*p == '\n')
 375                p++;
 376        if (p != buf + count)
 377                goto Einval;
 378        return e;
 379
 380out:
 381        return ERR_PTR(err);
 382
 383Efault:
 384        kfree(e);
 385        return ERR_PTR(-EFAULT);
 386Einval:
 387        kfree(e);
 388        return ERR_PTR(-EINVAL);
 389}
 390
 391/*
 392 * Set status of entry/binfmt_misc:
 393 * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
 394 */
 395static int parse_command(const char *buffer, size_t count)
 396{
 397        char s[4];
 398
 399        if (!count)
 400                return 0;
 401        if (count > 3)
 402                return -EINVAL;
 403        if (copy_from_user(s, buffer, count))
 404                return -EFAULT;
 405        if (s[count-1] == '\n')
 406                count--;
 407        if (count == 1 && s[0] == '0')
 408                return 1;
 409        if (count == 1 && s[0] == '1')
 410                return 2;
 411        if (count == 2 && s[0] == '-' && s[1] == '1')
 412                return 3;
 413        return -EINVAL;
 414}
 415
 416static void entry_status(Node *e, char *page)
 417{
 418        char *dp;
 419        char *status = "disabled";
 420
 421        if (test_bit(Enabled, &e->flags))
 422                status = "enabled";
 423
 424        if (!VERBOSE_STATUS) {
 425                sprintf(page, "%s\n", status);
 426                return;
 427        }
 428
 429        sprintf(page, "%s\ninterpreter %s\n", status, e->interpreter);
 430        dp = page + strlen(page);
 431        if (!test_bit(Magic, &e->flags)) {
 432                sprintf(dp, "extension .%s\n", e->magic);
 433        } else {
 434                int i;
 435
 436                sprintf(dp, "offset %i\nmagic ", e->offset);
 437                dp = page + strlen(page);
 438                for (i = 0; i < e->size; i++) {
 439                        sprintf(dp, "%02x", 0xff & (int) (e->magic[i]));
 440                        dp += 2;
 441                }
 442                if (e->mask) {
 443                        sprintf(dp, "\nmask ");
 444                        dp += 6;
 445                        for (i = 0; i < e->size; i++) {
 446                                sprintf(dp, "%02x", 0xff & (int) (e->mask[i]));
 447                                dp += 2;
 448                        }
 449                }
 450                *dp++ = '\n';
 451                *dp = '\0';
 452        }
 453}
 454
 455static int proc_write_register(struct file *file, const char *buffer,
 456                               unsigned long count, void *data)
 457{
 458        Node *e = create_entry(buffer, count);
 459        int err;
 460
 461        if (IS_ERR(e))
 462                return PTR_ERR(e);
 463        
 464        if (entry_proc_setup(e))
 465                goto free_err;
 466
 467        e->id = free_id++;
 468        write_lock(&entries_lock);
 469        e->next = entries;
 470        entries = e;
 471        write_unlock(&entries_lock);
 472
 473        err = count;
 474_err:
 475        return err;
 476free_err:
 477        kfree(e);
 478        err = -EINVAL;
 479        goto _err;
 480}
 481
 482/*
 483 * Get status of entry/binfmt_misc
 484 * FIXME? should an entry be marked disabled if binfmt_misc is disabled though
 485 *        entry is enabled?
 486 */
 487static int proc_read_status(char *page, char **start, off_t off,
 488                            int count, int *eof, void *data)
 489{
 490        Node *e;
 491        int elen;
 492
 493        if (!data) {
 494                sprintf(page, "%s\n", (enabled ? "enabled" : "disabled"));
 495        } else {
 496                if (!(e = get_entry((long) data)))
 497                        return -ENOENT;
 498                entry_status(e, page);
 499                put_entry(e);
 500        }
 501
 502        elen = strlen(page) - off;
 503        if (elen < 0)
 504                elen = 0;
 505        *eof = (elen <= count) ? 1 : 0;
 506        *start = page + off;
 507        return elen;
 508}
 509
 510/*
 511 * Set status of entry/binfmt_misc:
 512 * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
 513 */
 514static int proc_write_status(struct file *file, const char *buffer,
 515                             unsigned long count, void *data)
 516{
 517        Node *e;
 518        int res = parse_command(buffer, count);
 519
 520        switch(res) {
 521                case 1: if (data) {
 522                                if ((e = get_entry((long) data)))
 523                                        clear_bit(Enabled, &e->flags);
 524                                put_entry(e);
 525                        } else {
 526                                enabled = 0;
 527                        }
 528                        break;
 529                case 2: if (data) {
 530                                if ((e = get_entry((long) data)))
 531                                        set_bit(Enabled, &e->flags);
 532                                put_entry(e);
 533                        } else {
 534                                enabled = 1;
 535                        }
 536                        break;
 537                case 3: if (data)
 538                                clear_entry((long) data);
 539                        else
 540                                clear_entries();
 541                        break;
 542                default: return res;
 543        }
 544        return count;
 545}
 546
 547/*
 548 * Remove the /proc-dir entries of one binfmt
 549 */
 550static void entry_proc_cleanup(Node *e)
 551{
 552        remove_proc_entry(e->name, bm_dir);
 553}
 554
 555/*
 556 * Create the /proc-dir entry for binfmt
 557 */
 558static int entry_proc_setup(Node *e)
 559{
 560        if (!(e->proc_dir = create_proc_entry(e->name,
 561                                S_IFREG | S_IRUGO | S_IWUSR, bm_dir)))
 562        {
 563                printk(KERN_WARNING "Unable to create /proc entry.\n");
 564                return -ENOENT;
 565        }
 566        e->proc_dir->data = (void *) (e->id);
 567        e->proc_dir->read_proc = proc_read_status;
 568        e->proc_dir->write_proc = proc_write_status;
 569        return 0;
 570}
 571
 572#ifdef MODULE
 573/*
 574 * This is called as the fill_inode function when an inode
 575 * is going into (fill = 1) or out of service (fill = 0).
 576 * We use it here to manage the module use counts.
 577 *
 578 * Note: only the top-level directory needs to do this; if
 579 * a lower level is referenced, the parent will be as well.
 580 */
 581static void bm_modcount(struct inode *inode, int fill)
 582{
 583        if (fill)
 584                MOD_INC_USE_COUNT;
 585        else
 586                MOD_DEC_USE_COUNT;
 587}
 588#endif
 589
 590int __init init_misc_binfmt(void)
 591{
 592        int error = -ENOENT;
 593        struct proc_dir_entry *status = NULL, *reg;
 594
 595        bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR, NULL);
 596        if (!bm_dir)
 597                goto out;
 598#ifdef MODULE
 599        bm_dir->fill_inode = bm_modcount;
 600#endif
 601
 602        status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR,
 603                                        bm_dir);
 604        if (!status)
 605                goto cleanup_bm;
 606        status->read_proc = proc_read_status;
 607        status->write_proc = proc_write_status;
 608
 609        reg = create_proc_entry("register", S_IFREG | S_IWUSR, bm_dir);
 610        if (!reg)
 611                goto cleanup_status;
 612        reg->write_proc = proc_write_register;
 613
 614        error = register_binfmt(&misc_format);
 615out:
 616        return error;
 617
 618cleanup_status:
 619        remove_proc_entry("status", bm_dir);
 620cleanup_bm:
 621        remove_proc_entry("sys/fs/binfmt_misc", NULL);
 622        goto out;
 623}
 624
 625#ifdef MODULE
 626EXPORT_NO_SYMBOLS;
 627int init_module(void)
 628{
 629        return init_misc_binfmt();
 630}
 631
 632void cleanup_module(void)
 633{
 634        unregister_binfmt(&misc_format);
 635        remove_proc_entry("register", bm_dir);
 636        remove_proc_entry("status", bm_dir);
 637        clear_entries();
 638        remove_proc_entry("sys/fs/binfmt_misc", NULL);
 639}
 640#endif
 641
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.