patch-2.1.43 linux/fs/binfmt_misc.c
Next file: linux/fs/buffer.c
Previous file: linux/fs/binfmt_elf.c
Back to the patch index
Back to the overall index
- Lines: 506
- Date:
Thu Jun 12 16:22:08 1997
- Orig file:
v2.1.42/linux/fs/binfmt_misc.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.42/linux/fs/binfmt_misc.c linux/fs/binfmt_misc.c
@@ -0,0 +1,505 @@
+/*
+ * binfmt_misc.c
+ *
+ * Copyright (C) 1997 Richard Günther
+ *
+ * binfmt_misc detects binaries via a magic or filename extension and invokes
+ * a specified wrapper. This should obsolete binfmt_java, binfmt_em86 and
+ * binfmt_mz.
+ *
+ * 25.4.97 first version
+ * [...]
+ * 19.5.97 cleanup
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/binfmts.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <asm/uaccess.h>
+#include <asm/spinlock.h>
+
+
+#define VERBOSE_STATUS /* undef this to save 400 bytes kernel memory */
+
+#ifndef MIN
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif
+
+struct binfmt_entry {
+ struct binfmt_entry *next;
+ int id;
+ int flags; /* type, status, etc. */
+ int offset; /* offset of magic */
+ int size; /* size of magic/mask */
+ char *magic; /* magic or filename extension */
+ char *mask; /* mask, NULL for exact match */
+ char *interpreter; /* filename of interpreter */
+ char *proc_name;
+ struct proc_dir_entry *proc_dir;
+};
+
+#define ENTRY_ENABLED 1 /* the old binfmt_entry.enabled */
+#define ENTRY_MAGIC 8 /* not filename detection */
+#define ENTRY_STRIP_EXT 32 /* strip of last filename extension */
+
+static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs);
+static void entry_proc_cleanup(struct binfmt_entry *e);
+static int entry_proc_setup(struct binfmt_entry *e);
+
+static struct linux_binfmt misc_format = {
+#ifndef MODULE
+ NULL, 0, load_misc_binary, NULL, NULL
+#else
+ NULL, &__this_module, load_misc_binary, NULL, NULL
+#endif
+};
+
+static struct proc_dir_entry *bm_dir = NULL;
+
+static struct binfmt_entry *entries = NULL;
+static int free_id = 1;
+static int enabled = 1;
+static rwlock_t entries_lock = RW_LOCK_UNLOCKED;
+
+
+/*
+ * Unregister one entry
+ */
+static void clear_entry(int id)
+{
+ struct binfmt_entry **ep, *e;
+
+ write_lock(&entries_lock);
+ ep = &entries;
+ while (*ep && ((*ep)->id != id))
+ ep = &((*ep)->next);
+ if ((e = *ep)) {
+ *ep = e->next;
+ entry_proc_cleanup(e);
+ kfree(e);
+ MOD_DEC_USE_COUNT;
+ }
+ write_unlock(&entries_lock);
+}
+
+/*
+ * Clear all registered binary formats
+ */
+static void clear_entries(void)
+{
+ struct binfmt_entry *e;
+
+ write_lock(&entries_lock);
+ while ((e = entries)) {
+ entries = entries->next;
+ entry_proc_cleanup(e);
+ kfree(e);
+ MOD_DEC_USE_COUNT;
+ }
+ write_unlock(&entries_lock);
+}
+
+/*
+ * Find entry through id - caller has to do locking
+ */
+static struct binfmt_entry *get_entry(int id)
+{
+ struct binfmt_entry *e = entries;
+
+ while (e && (e->id != id))
+ e = e->next;
+ return e;
+}
+
+
+/*
+ * Check if we support the binfmt
+ * if we do, return the binfmt_entry, else NULL
+ * locking is done in load_misc_binary
+ */
+static struct binfmt_entry *check_file(struct linux_binprm *bprm)
+{
+ struct binfmt_entry *e = entries;
+ char *p = strrchr(bprm->filename, '.');
+ int j;
+
+ while (e) {
+ if (e->flags & ENTRY_ENABLED) {
+ if (!(e->flags & ENTRY_MAGIC)) {
+ if (p && !strcmp(e->magic, p + 1))
+ return e;
+ } else {
+ j = 0;
+ while ((j < e->size) &&
+ !((bprm->buf[e->offset + j] ^ e->magic[j])
+ & (e->mask ? e->mask[j] : 0xff)))
+ j++;
+ if (j == e->size)
+ return e;
+ }
+ }
+ e = e->next;
+ };
+ return NULL;
+}
+
+/*
+ * the loader itself
+ */
+static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+{
+ struct binfmt_entry *fmt;
+ char iname[128];
+ char *iname_addr = iname, *p;
+ int retval, fmt_flags = 0;
+
+ MOD_INC_USE_COUNT;
+ if (!enabled) {
+ retval = -ENOEXEC;
+ goto _ret;
+ }
+
+ /* to keep locking time low, we copy the interpreter string */
+ read_lock(&entries_lock);
+ if ((fmt = check_file(bprm))) {
+ strncpy(iname, fmt->interpreter, 127);
+ iname[127] = '\0';
+ fmt_flags = fmt->flags;
+ }
+ read_unlock(&entries_lock);
+ if (!fmt) {
+ retval = -ENOEXEC;
+ goto _ret;
+ }
+
+ iput(bprm->inode);
+ bprm->dont_iput = 1;
+
+ /* Build args for interpreter */
+ if ((fmt_flags & ENTRY_STRIP_EXT) &&
+ (p = strrchr(bprm->filename, '.'))) {
+ *p = '\0';
+ remove_arg_zero(bprm);
+ bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
+ bprm->argc++;
+ }
+ bprm->p = copy_strings(1, &iname_addr, bprm->page, bprm->p, 2);
+ bprm->argc++;
+ if (!bprm->p) {
+ retval = -E2BIG;
+ goto _ret;
+ }
+ bprm->filename = iname; /* for binfmt_script */
+
+ if ((retval = open_namei(iname, 0, 0, &bprm->inode, NULL)))
+ goto _ret;
+ bprm->dont_iput = 0;
+
+ if ((retval = prepare_binprm(bprm)) >= 0)
+ retval = search_binary_handler(bprm, regs);
+_ret:
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+
+
+/*
+ * /proc handling routines
+ */
+
+/*
+ * parses and copies one argument enclosed in del from *sp to *dp,
+ * recognising the \x special.
+ * returns pointer to the copied argument or NULL in case of an
+ * error (and sets err) or null argument length.
+ */
+static char *copyarg(char **dp, const char **sp, int *count,
+ char del, int special, int *err)
+{
+ char c, *res = *dp;
+
+ while (!*err && ((c = *((*sp)++)), (*count)--) && (c != del)) {
+ switch (c) {
+ case '\\':
+ if (special && (**sp == 'x')) {
+ if (!isxdigit(c = toupper(*(++*sp))))
+ *err = -EINVAL;
+ **dp = (c - (isdigit(c) ? '0' : 'A' - 10)) * 16;
+ if (!isxdigit(c = toupper(*(++*sp))))
+ *err = -EINVAL;
+ *((*dp)++) += c - (isdigit(c) ? '0' : 'A' - 10);
+ ++*sp;
+ *count -= 3;
+ break;
+ }
+ default:
+ *((*dp)++) = c;
+ }
+ }
+ if (*err || (c != del) || (res == *dp))
+ res = NULL;
+ else if (!special)
+ *((*dp)++) = '\0';
+ return res;
+}
+
+/*
+ * This registers a new binary format, it recognises the syntax
+ * ':name:type:offset:magic:mask:interpreter:'
+ * where the ':' is the IFS, that can be chosen with the first char
+ */
+static int proc_write_register(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ const char *sp;
+ char del, *dp;
+ struct binfmt_entry *e;
+ int memsize, cnt = count - 1, err = 0;
+
+ MOD_INC_USE_COUNT;
+ /* some sanity checks */
+ if ((count < 11) || (count > 256)) {
+ err = -EINVAL;
+ goto _err;
+ }
+
+ memsize = sizeof(struct binfmt_entry) + count;
+ if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER))) {
+ err = -ENOMEM;
+ goto _err;
+ }
+
+ sp = buffer + 1;
+ del = buffer[0];
+ dp = (char *)e + sizeof(struct binfmt_entry);
+
+ e->proc_name = copyarg(&dp, &sp, &cnt, del, 0, &err);
+
+ /* we can use bit 3 and 5 of type for ext/magic and ext-strip
+ flag due to the nice encoding of E, M, e and m */
+ if ((*sp & 0x92) || (sp[1] != del))
+ err = -EINVAL;
+ else
+ e->flags = (*sp++ & (ENTRY_MAGIC | ENTRY_STRIP_EXT))
+ | ENTRY_ENABLED;
+ cnt -= 2; sp++;
+
+ e->offset = 0;
+ while (cnt-- && isdigit(*sp))
+ e->offset = e->offset * 10 + *sp++ - '0';
+ if (*sp++ != del)
+ err = -EINVAL;
+
+ e->magic = copyarg(&dp, &sp, &cnt, del, (e->flags & ENTRY_MAGIC), &err);
+ e->size = dp - e->magic;
+ e->mask = copyarg(&dp, &sp, &cnt, del, 1, &err);
+ if (e->mask && ((dp - e->mask) != e->size))
+ err = -EINVAL;
+ e->interpreter = copyarg(&dp, &sp, &cnt, del, 0, &err);
+ e->id = free_id++;
+
+ /* more sanity checks */
+ if (err || !(!cnt || (!(--cnt) && (*sp == '\n'))) ||
+ (e->size < 1) || ((e->size + e->offset) > 127) ||
+ !(e->proc_name) || !(e->interpreter) ||
+ entry_proc_setup(e)) {
+ kfree(e);
+ err = -EINVAL;
+ goto _err;
+ }
+
+ write_lock(&entries_lock);
+ e->next = entries;
+ entries = e;
+ write_unlock(&entries_lock);
+
+ return count;
+_err:
+ MOD_DEC_USE_COUNT;
+ return err;
+}
+
+/*
+ * Get status of entry/binfmt_misc
+ * FIXME? should an entry be marked disabled if binfmt_misc is disabled though
+ * entry is enabled?
+ */
+static int proc_read_status(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct binfmt_entry *e;
+ char *dp;
+ int elen, i;
+
+ MOD_INC_USE_COUNT;
+#ifndef VERBOSE_STATUS
+ if (data) {
+ read_lock(&entries_lock);
+ if (!(e = get_entry((int) data)))
+ i = 0;
+ else
+ i = e->flags & ENTRY_ENABLED;
+ read_unlock(&entries_lock);
+ } else {
+ i = enabled;
+ }
+ sprintf(page, "%s\n", (i ? "enabled" : "disabled"));
+#else
+ if (!data)
+ sprintf(page, "%s\n", (enabled ? "enabled" : "disabled"));
+ else {
+ read_lock(&entries_lock);
+ if (!(e = get_entry((int) data))) {
+ *page = '\0';
+ goto _out;
+ }
+ sprintf(page, "%s\ninterpreter %s\n",
+ (e->flags & ENTRY_ENABLED ? "enabled" : "disabled"),
+ e->interpreter);
+ dp = page + strlen(page);
+ if (!(e->flags & ENTRY_MAGIC)) {
+ sprintf(dp, "extension .%s\n", e->magic);
+ dp = page + strlen(page);
+ } else {
+ sprintf(dp, "offset %i\nmagic ", e->offset);
+ dp = page + strlen(page);
+ for (i = 0; i < e->size; i++) {
+ sprintf(dp, "%02x", 0xff & (int) (e->magic[i]));
+ dp += 2;
+ }
+ if (e->mask) {
+ sprintf(dp, "\nmask ");
+ dp += 6;
+ for (i = 0; i < e->size; i++) {
+ sprintf(dp, "%02x", 0xff & (int) (e->mask[i]));
+ dp += 2;
+ }
+ }
+ *dp++ = '\n';
+ *dp = '\0';
+ }
+ if (e->flags & ENTRY_STRIP_EXT)
+ sprintf(dp, "extension stripped\n");
+_out:
+ read_unlock(&entries_lock);
+ }
+#endif
+
+ elen = strlen(page) - off;
+ if (elen < 0)
+ elen = 0;
+ *eof = (elen <= count) ? 1 : 0;
+ *start = page + off;
+
+ MOD_DEC_USE_COUNT;
+ return elen;
+}
+
+/*
+ * Set status of entry/binfmt_misc:
+ * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
+ */
+static int proc_write_status(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ struct binfmt_entry *e;
+ int res = count;
+
+ MOD_INC_USE_COUNT;
+ if (((buffer[0] == '1') || (buffer[0] == '0')) &&
+ ((count == 1) || ((count == 2) && (buffer[1] == '\n')))) {
+ if (data) {
+ read_lock(&entries_lock);
+ if ((e = get_entry((int) data)))
+ e->flags = (e->flags & -2) | (int) (buffer[0] - '0');
+ read_unlock(&entries_lock);
+ } else {
+ enabled = buffer[0] - '0';
+ }
+ } else if ((buffer[0] == '-') && (buffer[1] == '1') &&
+ ((count == 2) || ((count == 3) && (buffer[2] == '\n')))) {
+ if (data)
+ clear_entry((int) data);
+ else
+ clear_entries();
+ } else {
+ res = -EINVAL;
+ }
+ MOD_DEC_USE_COUNT;
+ return res;
+}
+
+/*
+ * Remove the /proc-dir entries of one binfmt
+ */
+static void entry_proc_cleanup(struct binfmt_entry *e)
+{
+ remove_proc_entry(e->proc_name, bm_dir);
+}
+
+/*
+ * Create the /proc-dir entry for binfmt
+ */
+static int entry_proc_setup(struct binfmt_entry *e)
+{
+ if (!(e->proc_dir = create_proc_entry(e->proc_name,
+ S_IFREG | S_IRUGO | S_IWUSR, bm_dir)))
+ return -ENOMEM;
+
+ e->proc_dir->data = (void *) (e->id);
+ e->proc_dir->read_proc = proc_read_status;
+ e->proc_dir->write_proc = proc_write_status;
+
+ return 0;
+}
+
+
+__initfunc(int init_misc_binfmt(void))
+{
+ struct proc_dir_entry *status = NULL, *reg;
+
+ if (!(bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR,
+ NULL)) ||
+ !(status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR,
+ bm_dir)) ||
+ !(reg = create_proc_entry("register", S_IFREG | S_IWUSR,
+ bm_dir))) {
+ if (status)
+ remove_proc_entry("status", bm_dir);
+ if (bm_dir)
+ remove_proc_entry("sys/fs/binfmt_misc", NULL);
+ return -ENOMEM;
+ }
+ status->read_proc = proc_read_status;
+ status->write_proc = proc_write_status;
+
+ reg->write_proc = proc_write_register;
+
+ return register_binfmt(&misc_format);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+int init_module(void)
+{
+ return init_misc_binfmt();
+}
+
+void cleanup_module(void)
+{
+ unregister_binfmt(&misc_format);
+ remove_proc_entry("register", bm_dir);
+ remove_proc_entry("status", bm_dir);
+ remove_proc_entry("sys/fs/binfmt_misc", NULL);
+}
+#endif
+#undef VERBOSE_STATUS
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov