xref: /kernel/linux/linux-6.6/fs/binfmt_misc.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * binfmt_misc.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1997 Richard Günther
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * binfmt_misc detects binaries via a magic or filename extension and invokes
862306a36Sopenharmony_ci * a specified wrapper. See Documentation/admin-guide/binfmt-misc.rst for more details.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/sched/mm.h>
1762306a36Sopenharmony_ci#include <linux/magic.h>
1862306a36Sopenharmony_ci#include <linux/binfmts.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/ctype.h>
2162306a36Sopenharmony_ci#include <linux/string_helpers.h>
2262306a36Sopenharmony_ci#include <linux/file.h>
2362306a36Sopenharmony_ci#include <linux/pagemap.h>
2462306a36Sopenharmony_ci#include <linux/namei.h>
2562306a36Sopenharmony_ci#include <linux/mount.h>
2662306a36Sopenharmony_ci#include <linux/fs_context.h>
2762306a36Sopenharmony_ci#include <linux/syscalls.h>
2862306a36Sopenharmony_ci#include <linux/fs.h>
2962306a36Sopenharmony_ci#include <linux/uaccess.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "internal.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#ifdef DEBUG
3462306a36Sopenharmony_ci# define USE_DEBUG 1
3562306a36Sopenharmony_ci#else
3662306a36Sopenharmony_ci# define USE_DEBUG 0
3762306a36Sopenharmony_ci#endif
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cienum {
4062306a36Sopenharmony_ci	VERBOSE_STATUS = 1 /* make it zero to save 400 bytes kernel memory */
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic LIST_HEAD(entries);
4462306a36Sopenharmony_cistatic int enabled = 1;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cienum {Enabled, Magic};
4762306a36Sopenharmony_ci#define MISC_FMT_PRESERVE_ARGV0 (1UL << 31)
4862306a36Sopenharmony_ci#define MISC_FMT_OPEN_BINARY (1UL << 30)
4962306a36Sopenharmony_ci#define MISC_FMT_CREDENTIALS (1UL << 29)
5062306a36Sopenharmony_ci#define MISC_FMT_OPEN_FILE (1UL << 28)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_citypedef struct {
5362306a36Sopenharmony_ci	struct list_head list;
5462306a36Sopenharmony_ci	unsigned long flags;		/* type, status, etc. */
5562306a36Sopenharmony_ci	int offset;			/* offset of magic */
5662306a36Sopenharmony_ci	int size;			/* size of magic/mask */
5762306a36Sopenharmony_ci	char *magic;			/* magic or filename extension */
5862306a36Sopenharmony_ci	char *mask;			/* mask, NULL for exact match */
5962306a36Sopenharmony_ci	const char *interpreter;	/* filename of interpreter */
6062306a36Sopenharmony_ci	char *name;
6162306a36Sopenharmony_ci	struct dentry *dentry;
6262306a36Sopenharmony_ci	struct file *interp_file;
6362306a36Sopenharmony_ci} Node;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic DEFINE_RWLOCK(entries_lock);
6662306a36Sopenharmony_cistatic struct file_system_type bm_fs_type;
6762306a36Sopenharmony_cistatic struct vfsmount *bm_mnt;
6862306a36Sopenharmony_cistatic int entry_count;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/*
7162306a36Sopenharmony_ci * Max length of the register string.  Determined by:
7262306a36Sopenharmony_ci *  - 7 delimiters
7362306a36Sopenharmony_ci *  - name:   ~50 bytes
7462306a36Sopenharmony_ci *  - type:   1 byte
7562306a36Sopenharmony_ci *  - offset: 3 bytes (has to be smaller than BINPRM_BUF_SIZE)
7662306a36Sopenharmony_ci *  - magic:  128 bytes (512 in escaped form)
7762306a36Sopenharmony_ci *  - mask:   128 bytes (512 in escaped form)
7862306a36Sopenharmony_ci *  - interp: ~50 bytes
7962306a36Sopenharmony_ci *  - flags:  5 bytes
8062306a36Sopenharmony_ci * Round that up a bit, and then back off to hold the internal data
8162306a36Sopenharmony_ci * (like struct Node).
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_ci#define MAX_REGISTER_LENGTH 1920
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * Check if we support the binfmt
8762306a36Sopenharmony_ci * if we do, return the node, else NULL
8862306a36Sopenharmony_ci * locking is done in load_misc_binary
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic Node *check_file(struct linux_binprm *bprm)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	char *p = strrchr(bprm->interp, '.');
9362306a36Sopenharmony_ci	struct list_head *l;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* Walk all the registered handlers. */
9662306a36Sopenharmony_ci	list_for_each(l, &entries) {
9762306a36Sopenharmony_ci		Node *e = list_entry(l, Node, list);
9862306a36Sopenharmony_ci		char *s;
9962306a36Sopenharmony_ci		int j;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		/* Make sure this one is currently enabled. */
10262306a36Sopenharmony_ci		if (!test_bit(Enabled, &e->flags))
10362306a36Sopenharmony_ci			continue;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		/* Do matching based on extension if applicable. */
10662306a36Sopenharmony_ci		if (!test_bit(Magic, &e->flags)) {
10762306a36Sopenharmony_ci			if (p && !strcmp(e->magic, p + 1))
10862306a36Sopenharmony_ci				return e;
10962306a36Sopenharmony_ci			continue;
11062306a36Sopenharmony_ci		}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		/* Do matching based on magic & mask. */
11362306a36Sopenharmony_ci		s = bprm->buf + e->offset;
11462306a36Sopenharmony_ci		if (e->mask) {
11562306a36Sopenharmony_ci			for (j = 0; j < e->size; j++)
11662306a36Sopenharmony_ci				if ((*s++ ^ e->magic[j]) & e->mask[j])
11762306a36Sopenharmony_ci					break;
11862306a36Sopenharmony_ci		} else {
11962306a36Sopenharmony_ci			for (j = 0; j < e->size; j++)
12062306a36Sopenharmony_ci				if ((*s++ ^ e->magic[j]))
12162306a36Sopenharmony_ci					break;
12262306a36Sopenharmony_ci		}
12362306a36Sopenharmony_ci		if (j == e->size)
12462306a36Sopenharmony_ci			return e;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci	return NULL;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * the loader itself
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_cistatic int load_misc_binary(struct linux_binprm *bprm)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	Node *fmt;
13562306a36Sopenharmony_ci	struct file *interp_file = NULL;
13662306a36Sopenharmony_ci	int retval;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	retval = -ENOEXEC;
13962306a36Sopenharmony_ci	if (!enabled)
14062306a36Sopenharmony_ci		return retval;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* to keep locking time low, we copy the interpreter string */
14362306a36Sopenharmony_ci	read_lock(&entries_lock);
14462306a36Sopenharmony_ci	fmt = check_file(bprm);
14562306a36Sopenharmony_ci	if (fmt)
14662306a36Sopenharmony_ci		dget(fmt->dentry);
14762306a36Sopenharmony_ci	read_unlock(&entries_lock);
14862306a36Sopenharmony_ci	if (!fmt)
14962306a36Sopenharmony_ci		return retval;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* Need to be able to load the file after exec */
15262306a36Sopenharmony_ci	retval = -ENOENT;
15362306a36Sopenharmony_ci	if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
15462306a36Sopenharmony_ci		goto ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (fmt->flags & MISC_FMT_PRESERVE_ARGV0) {
15762306a36Sopenharmony_ci		bprm->interp_flags |= BINPRM_FLAGS_PRESERVE_ARGV0;
15862306a36Sopenharmony_ci	} else {
15962306a36Sopenharmony_ci		retval = remove_arg_zero(bprm);
16062306a36Sopenharmony_ci		if (retval)
16162306a36Sopenharmony_ci			goto ret;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (fmt->flags & MISC_FMT_OPEN_BINARY)
16562306a36Sopenharmony_ci		bprm->have_execfd = 1;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* make argv[1] be the path to the binary */
16862306a36Sopenharmony_ci	retval = copy_string_kernel(bprm->interp, bprm);
16962306a36Sopenharmony_ci	if (retval < 0)
17062306a36Sopenharmony_ci		goto ret;
17162306a36Sopenharmony_ci	bprm->argc++;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* add the interp as argv[0] */
17462306a36Sopenharmony_ci	retval = copy_string_kernel(fmt->interpreter, bprm);
17562306a36Sopenharmony_ci	if (retval < 0)
17662306a36Sopenharmony_ci		goto ret;
17762306a36Sopenharmony_ci	bprm->argc++;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Update interp in case binfmt_script needs it. */
18062306a36Sopenharmony_ci	retval = bprm_change_interp(fmt->interpreter, bprm);
18162306a36Sopenharmony_ci	if (retval < 0)
18262306a36Sopenharmony_ci		goto ret;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (fmt->flags & MISC_FMT_OPEN_FILE) {
18562306a36Sopenharmony_ci		interp_file = file_clone_open(fmt->interp_file);
18662306a36Sopenharmony_ci		if (!IS_ERR(interp_file))
18762306a36Sopenharmony_ci			deny_write_access(interp_file);
18862306a36Sopenharmony_ci	} else {
18962306a36Sopenharmony_ci		interp_file = open_exec(fmt->interpreter);
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci	retval = PTR_ERR(interp_file);
19262306a36Sopenharmony_ci	if (IS_ERR(interp_file))
19362306a36Sopenharmony_ci		goto ret;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	bprm->interpreter = interp_file;
19662306a36Sopenharmony_ci	if (fmt->flags & MISC_FMT_CREDENTIALS)
19762306a36Sopenharmony_ci		bprm->execfd_creds = 1;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	retval = 0;
20062306a36Sopenharmony_ciret:
20162306a36Sopenharmony_ci	dput(fmt->dentry);
20262306a36Sopenharmony_ci	return retval;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/* Command parsers */
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/*
20862306a36Sopenharmony_ci * parses and copies one argument enclosed in del from *sp to *dp,
20962306a36Sopenharmony_ci * recognising the \x special.
21062306a36Sopenharmony_ci * returns pointer to the copied argument or NULL in case of an
21162306a36Sopenharmony_ci * error (and sets err) or null argument length.
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_cistatic char *scanarg(char *s, char del)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	char c;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	while ((c = *s++) != del) {
21862306a36Sopenharmony_ci		if (c == '\\' && *s == 'x') {
21962306a36Sopenharmony_ci			s++;
22062306a36Sopenharmony_ci			if (!isxdigit(*s++))
22162306a36Sopenharmony_ci				return NULL;
22262306a36Sopenharmony_ci			if (!isxdigit(*s++))
22362306a36Sopenharmony_ci				return NULL;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	s[-1] ='\0';
22762306a36Sopenharmony_ci	return s;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic char *check_special_flags(char *sfs, Node *e)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	char *p = sfs;
23362306a36Sopenharmony_ci	int cont = 1;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* special flags */
23662306a36Sopenharmony_ci	while (cont) {
23762306a36Sopenharmony_ci		switch (*p) {
23862306a36Sopenharmony_ci		case 'P':
23962306a36Sopenharmony_ci			pr_debug("register: flag: P (preserve argv0)\n");
24062306a36Sopenharmony_ci			p++;
24162306a36Sopenharmony_ci			e->flags |= MISC_FMT_PRESERVE_ARGV0;
24262306a36Sopenharmony_ci			break;
24362306a36Sopenharmony_ci		case 'O':
24462306a36Sopenharmony_ci			pr_debug("register: flag: O (open binary)\n");
24562306a36Sopenharmony_ci			p++;
24662306a36Sopenharmony_ci			e->flags |= MISC_FMT_OPEN_BINARY;
24762306a36Sopenharmony_ci			break;
24862306a36Sopenharmony_ci		case 'C':
24962306a36Sopenharmony_ci			pr_debug("register: flag: C (preserve creds)\n");
25062306a36Sopenharmony_ci			p++;
25162306a36Sopenharmony_ci			/* this flags also implies the
25262306a36Sopenharmony_ci			   open-binary flag */
25362306a36Sopenharmony_ci			e->flags |= (MISC_FMT_CREDENTIALS |
25462306a36Sopenharmony_ci					MISC_FMT_OPEN_BINARY);
25562306a36Sopenharmony_ci			break;
25662306a36Sopenharmony_ci		case 'F':
25762306a36Sopenharmony_ci			pr_debug("register: flag: F: open interpreter file now\n");
25862306a36Sopenharmony_ci			p++;
25962306a36Sopenharmony_ci			e->flags |= MISC_FMT_OPEN_FILE;
26062306a36Sopenharmony_ci			break;
26162306a36Sopenharmony_ci		default:
26262306a36Sopenharmony_ci			cont = 0;
26362306a36Sopenharmony_ci		}
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return p;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci/*
27062306a36Sopenharmony_ci * This registers a new binary format, it recognises the syntax
27162306a36Sopenharmony_ci * ':name:type:offset:magic:mask:interpreter:flags'
27262306a36Sopenharmony_ci * where the ':' is the IFS, that can be chosen with the first char
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_cistatic Node *create_entry(const char __user *buffer, size_t count)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	Node *e;
27762306a36Sopenharmony_ci	int memsize, err;
27862306a36Sopenharmony_ci	char *buf, *p;
27962306a36Sopenharmony_ci	char del;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	pr_debug("register: received %zu bytes\n", count);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	/* some sanity checks */
28462306a36Sopenharmony_ci	err = -EINVAL;
28562306a36Sopenharmony_ci	if ((count < 11) || (count > MAX_REGISTER_LENGTH))
28662306a36Sopenharmony_ci		goto out;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	err = -ENOMEM;
28962306a36Sopenharmony_ci	memsize = sizeof(Node) + count + 8;
29062306a36Sopenharmony_ci	e = kmalloc(memsize, GFP_KERNEL);
29162306a36Sopenharmony_ci	if (!e)
29262306a36Sopenharmony_ci		goto out;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	p = buf = (char *)e + sizeof(Node);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	memset(e, 0, sizeof(Node));
29762306a36Sopenharmony_ci	if (copy_from_user(buf, buffer, count))
29862306a36Sopenharmony_ci		goto efault;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	del = *p++;	/* delimeter */
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	pr_debug("register: delim: %#x {%c}\n", del, del);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* Pad the buffer with the delim to simplify parsing below. */
30562306a36Sopenharmony_ci	memset(buf + count, del, 8);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* Parse the 'name' field. */
30862306a36Sopenharmony_ci	e->name = p;
30962306a36Sopenharmony_ci	p = strchr(p, del);
31062306a36Sopenharmony_ci	if (!p)
31162306a36Sopenharmony_ci		goto einval;
31262306a36Sopenharmony_ci	*p++ = '\0';
31362306a36Sopenharmony_ci	if (!e->name[0] ||
31462306a36Sopenharmony_ci	    !strcmp(e->name, ".") ||
31562306a36Sopenharmony_ci	    !strcmp(e->name, "..") ||
31662306a36Sopenharmony_ci	    strchr(e->name, '/'))
31762306a36Sopenharmony_ci		goto einval;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	pr_debug("register: name: {%s}\n", e->name);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* Parse the 'type' field. */
32262306a36Sopenharmony_ci	switch (*p++) {
32362306a36Sopenharmony_ci	case 'E':
32462306a36Sopenharmony_ci		pr_debug("register: type: E (extension)\n");
32562306a36Sopenharmony_ci		e->flags = 1 << Enabled;
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	case 'M':
32862306a36Sopenharmony_ci		pr_debug("register: type: M (magic)\n");
32962306a36Sopenharmony_ci		e->flags = (1 << Enabled) | (1 << Magic);
33062306a36Sopenharmony_ci		break;
33162306a36Sopenharmony_ci	default:
33262306a36Sopenharmony_ci		goto einval;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci	if (*p++ != del)
33562306a36Sopenharmony_ci		goto einval;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (test_bit(Magic, &e->flags)) {
33862306a36Sopenharmony_ci		/* Handle the 'M' (magic) format. */
33962306a36Sopenharmony_ci		char *s;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		/* Parse the 'offset' field. */
34262306a36Sopenharmony_ci		s = strchr(p, del);
34362306a36Sopenharmony_ci		if (!s)
34462306a36Sopenharmony_ci			goto einval;
34562306a36Sopenharmony_ci		*s = '\0';
34662306a36Sopenharmony_ci		if (p != s) {
34762306a36Sopenharmony_ci			int r = kstrtoint(p, 10, &e->offset);
34862306a36Sopenharmony_ci			if (r != 0 || e->offset < 0)
34962306a36Sopenharmony_ci				goto einval;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci		p = s;
35262306a36Sopenharmony_ci		if (*p++)
35362306a36Sopenharmony_ci			goto einval;
35462306a36Sopenharmony_ci		pr_debug("register: offset: %#x\n", e->offset);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		/* Parse the 'magic' field. */
35762306a36Sopenharmony_ci		e->magic = p;
35862306a36Sopenharmony_ci		p = scanarg(p, del);
35962306a36Sopenharmony_ci		if (!p)
36062306a36Sopenharmony_ci			goto einval;
36162306a36Sopenharmony_ci		if (!e->magic[0])
36262306a36Sopenharmony_ci			goto einval;
36362306a36Sopenharmony_ci		if (USE_DEBUG)
36462306a36Sopenharmony_ci			print_hex_dump_bytes(
36562306a36Sopenharmony_ci				KBUILD_MODNAME ": register: magic[raw]: ",
36662306a36Sopenharmony_ci				DUMP_PREFIX_NONE, e->magic, p - e->magic);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		/* Parse the 'mask' field. */
36962306a36Sopenharmony_ci		e->mask = p;
37062306a36Sopenharmony_ci		p = scanarg(p, del);
37162306a36Sopenharmony_ci		if (!p)
37262306a36Sopenharmony_ci			goto einval;
37362306a36Sopenharmony_ci		if (!e->mask[0]) {
37462306a36Sopenharmony_ci			e->mask = NULL;
37562306a36Sopenharmony_ci			pr_debug("register:  mask[raw]: none\n");
37662306a36Sopenharmony_ci		} else if (USE_DEBUG)
37762306a36Sopenharmony_ci			print_hex_dump_bytes(
37862306a36Sopenharmony_ci				KBUILD_MODNAME ": register:  mask[raw]: ",
37962306a36Sopenharmony_ci				DUMP_PREFIX_NONE, e->mask, p - e->mask);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		/*
38262306a36Sopenharmony_ci		 * Decode the magic & mask fields.
38362306a36Sopenharmony_ci		 * Note: while we might have accepted embedded NUL bytes from
38462306a36Sopenharmony_ci		 * above, the unescape helpers here will stop at the first one
38562306a36Sopenharmony_ci		 * it encounters.
38662306a36Sopenharmony_ci		 */
38762306a36Sopenharmony_ci		e->size = string_unescape_inplace(e->magic, UNESCAPE_HEX);
38862306a36Sopenharmony_ci		if (e->mask &&
38962306a36Sopenharmony_ci		    string_unescape_inplace(e->mask, UNESCAPE_HEX) != e->size)
39062306a36Sopenharmony_ci			goto einval;
39162306a36Sopenharmony_ci		if (e->size > BINPRM_BUF_SIZE ||
39262306a36Sopenharmony_ci		    BINPRM_BUF_SIZE - e->size < e->offset)
39362306a36Sopenharmony_ci			goto einval;
39462306a36Sopenharmony_ci		pr_debug("register: magic/mask length: %i\n", e->size);
39562306a36Sopenharmony_ci		if (USE_DEBUG) {
39662306a36Sopenharmony_ci			print_hex_dump_bytes(
39762306a36Sopenharmony_ci				KBUILD_MODNAME ": register: magic[decoded]: ",
39862306a36Sopenharmony_ci				DUMP_PREFIX_NONE, e->magic, e->size);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci			if (e->mask) {
40162306a36Sopenharmony_ci				int i;
40262306a36Sopenharmony_ci				char *masked = kmalloc(e->size, GFP_KERNEL);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci				print_hex_dump_bytes(
40562306a36Sopenharmony_ci					KBUILD_MODNAME ": register:  mask[decoded]: ",
40662306a36Sopenharmony_ci					DUMP_PREFIX_NONE, e->mask, e->size);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci				if (masked) {
40962306a36Sopenharmony_ci					for (i = 0; i < e->size; ++i)
41062306a36Sopenharmony_ci						masked[i] = e->magic[i] & e->mask[i];
41162306a36Sopenharmony_ci					print_hex_dump_bytes(
41262306a36Sopenharmony_ci						KBUILD_MODNAME ": register:  magic[masked]: ",
41362306a36Sopenharmony_ci						DUMP_PREFIX_NONE, masked, e->size);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci					kfree(masked);
41662306a36Sopenharmony_ci				}
41762306a36Sopenharmony_ci			}
41862306a36Sopenharmony_ci		}
41962306a36Sopenharmony_ci	} else {
42062306a36Sopenharmony_ci		/* Handle the 'E' (extension) format. */
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		/* Skip the 'offset' field. */
42362306a36Sopenharmony_ci		p = strchr(p, del);
42462306a36Sopenharmony_ci		if (!p)
42562306a36Sopenharmony_ci			goto einval;
42662306a36Sopenharmony_ci		*p++ = '\0';
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		/* Parse the 'magic' field. */
42962306a36Sopenharmony_ci		e->magic = p;
43062306a36Sopenharmony_ci		p = strchr(p, del);
43162306a36Sopenharmony_ci		if (!p)
43262306a36Sopenharmony_ci			goto einval;
43362306a36Sopenharmony_ci		*p++ = '\0';
43462306a36Sopenharmony_ci		if (!e->magic[0] || strchr(e->magic, '/'))
43562306a36Sopenharmony_ci			goto einval;
43662306a36Sopenharmony_ci		pr_debug("register: extension: {%s}\n", e->magic);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		/* Skip the 'mask' field. */
43962306a36Sopenharmony_ci		p = strchr(p, del);
44062306a36Sopenharmony_ci		if (!p)
44162306a36Sopenharmony_ci			goto einval;
44262306a36Sopenharmony_ci		*p++ = '\0';
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* Parse the 'interpreter' field. */
44662306a36Sopenharmony_ci	e->interpreter = p;
44762306a36Sopenharmony_ci	p = strchr(p, del);
44862306a36Sopenharmony_ci	if (!p)
44962306a36Sopenharmony_ci		goto einval;
45062306a36Sopenharmony_ci	*p++ = '\0';
45162306a36Sopenharmony_ci	if (!e->interpreter[0])
45262306a36Sopenharmony_ci		goto einval;
45362306a36Sopenharmony_ci	pr_debug("register: interpreter: {%s}\n", e->interpreter);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* Parse the 'flags' field. */
45662306a36Sopenharmony_ci	p = check_special_flags(p, e);
45762306a36Sopenharmony_ci	if (*p == '\n')
45862306a36Sopenharmony_ci		p++;
45962306a36Sopenharmony_ci	if (p != buf + count)
46062306a36Sopenharmony_ci		goto einval;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	return e;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ciout:
46562306a36Sopenharmony_ci	return ERR_PTR(err);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ciefault:
46862306a36Sopenharmony_ci	kfree(e);
46962306a36Sopenharmony_ci	return ERR_PTR(-EFAULT);
47062306a36Sopenharmony_cieinval:
47162306a36Sopenharmony_ci	kfree(e);
47262306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci/*
47662306a36Sopenharmony_ci * Set status of entry/binfmt_misc:
47762306a36Sopenharmony_ci * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
47862306a36Sopenharmony_ci */
47962306a36Sopenharmony_cistatic int parse_command(const char __user *buffer, size_t count)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	char s[4];
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (count > 3)
48462306a36Sopenharmony_ci		return -EINVAL;
48562306a36Sopenharmony_ci	if (copy_from_user(s, buffer, count))
48662306a36Sopenharmony_ci		return -EFAULT;
48762306a36Sopenharmony_ci	if (!count)
48862306a36Sopenharmony_ci		return 0;
48962306a36Sopenharmony_ci	if (s[count - 1] == '\n')
49062306a36Sopenharmony_ci		count--;
49162306a36Sopenharmony_ci	if (count == 1 && s[0] == '0')
49262306a36Sopenharmony_ci		return 1;
49362306a36Sopenharmony_ci	if (count == 1 && s[0] == '1')
49462306a36Sopenharmony_ci		return 2;
49562306a36Sopenharmony_ci	if (count == 2 && s[0] == '-' && s[1] == '1')
49662306a36Sopenharmony_ci		return 3;
49762306a36Sopenharmony_ci	return -EINVAL;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/* generic stuff */
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic void entry_status(Node *e, char *page)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	char *dp = page;
50562306a36Sopenharmony_ci	const char *status = "disabled";
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (test_bit(Enabled, &e->flags))
50862306a36Sopenharmony_ci		status = "enabled";
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (!VERBOSE_STATUS) {
51162306a36Sopenharmony_ci		sprintf(page, "%s\n", status);
51262306a36Sopenharmony_ci		return;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	dp += sprintf(dp, "%s\ninterpreter %s\n", status, e->interpreter);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* print the special flags */
51862306a36Sopenharmony_ci	dp += sprintf(dp, "flags: ");
51962306a36Sopenharmony_ci	if (e->flags & MISC_FMT_PRESERVE_ARGV0)
52062306a36Sopenharmony_ci		*dp++ = 'P';
52162306a36Sopenharmony_ci	if (e->flags & MISC_FMT_OPEN_BINARY)
52262306a36Sopenharmony_ci		*dp++ = 'O';
52362306a36Sopenharmony_ci	if (e->flags & MISC_FMT_CREDENTIALS)
52462306a36Sopenharmony_ci		*dp++ = 'C';
52562306a36Sopenharmony_ci	if (e->flags & MISC_FMT_OPEN_FILE)
52662306a36Sopenharmony_ci		*dp++ = 'F';
52762306a36Sopenharmony_ci	*dp++ = '\n';
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (!test_bit(Magic, &e->flags)) {
53062306a36Sopenharmony_ci		sprintf(dp, "extension .%s\n", e->magic);
53162306a36Sopenharmony_ci	} else {
53262306a36Sopenharmony_ci		dp += sprintf(dp, "offset %i\nmagic ", e->offset);
53362306a36Sopenharmony_ci		dp = bin2hex(dp, e->magic, e->size);
53462306a36Sopenharmony_ci		if (e->mask) {
53562306a36Sopenharmony_ci			dp += sprintf(dp, "\nmask ");
53662306a36Sopenharmony_ci			dp = bin2hex(dp, e->mask, e->size);
53762306a36Sopenharmony_ci		}
53862306a36Sopenharmony_ci		*dp++ = '\n';
53962306a36Sopenharmony_ci		*dp = '\0';
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic struct inode *bm_get_inode(struct super_block *sb, int mode)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct inode *inode = new_inode(sb);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (inode) {
54862306a36Sopenharmony_ci		inode->i_ino = get_next_ino();
54962306a36Sopenharmony_ci		inode->i_mode = mode;
55062306a36Sopenharmony_ci		inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode);
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci	return inode;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic void bm_evict_inode(struct inode *inode)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	Node *e = inode->i_private;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (e && e->flags & MISC_FMT_OPEN_FILE)
56062306a36Sopenharmony_ci		filp_close(e->interp_file, NULL);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	clear_inode(inode);
56362306a36Sopenharmony_ci	kfree(e);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void kill_node(Node *e)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct dentry *dentry;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	write_lock(&entries_lock);
57162306a36Sopenharmony_ci	list_del_init(&e->list);
57262306a36Sopenharmony_ci	write_unlock(&entries_lock);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	dentry = e->dentry;
57562306a36Sopenharmony_ci	drop_nlink(d_inode(dentry));
57662306a36Sopenharmony_ci	d_drop(dentry);
57762306a36Sopenharmony_ci	dput(dentry);
57862306a36Sopenharmony_ci	simple_release_fs(&bm_mnt, &entry_count);
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/* /<entry> */
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic ssize_t
58462306a36Sopenharmony_cibm_entry_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	Node *e = file_inode(file)->i_private;
58762306a36Sopenharmony_ci	ssize_t res;
58862306a36Sopenharmony_ci	char *page;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	page = (char *) __get_free_page(GFP_KERNEL);
59162306a36Sopenharmony_ci	if (!page)
59262306a36Sopenharmony_ci		return -ENOMEM;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	entry_status(e, page);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	res = simple_read_from_buffer(buf, nbytes, ppos, page, strlen(page));
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	free_page((unsigned long) page);
59962306a36Sopenharmony_ci	return res;
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic ssize_t bm_entry_write(struct file *file, const char __user *buffer,
60362306a36Sopenharmony_ci				size_t count, loff_t *ppos)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct dentry *root;
60662306a36Sopenharmony_ci	Node *e = file_inode(file)->i_private;
60762306a36Sopenharmony_ci	int res = parse_command(buffer, count);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	switch (res) {
61062306a36Sopenharmony_ci	case 1:
61162306a36Sopenharmony_ci		/* Disable this handler. */
61262306a36Sopenharmony_ci		clear_bit(Enabled, &e->flags);
61362306a36Sopenharmony_ci		break;
61462306a36Sopenharmony_ci	case 2:
61562306a36Sopenharmony_ci		/* Enable this handler. */
61662306a36Sopenharmony_ci		set_bit(Enabled, &e->flags);
61762306a36Sopenharmony_ci		break;
61862306a36Sopenharmony_ci	case 3:
61962306a36Sopenharmony_ci		/* Delete this handler. */
62062306a36Sopenharmony_ci		root = file_inode(file)->i_sb->s_root;
62162306a36Sopenharmony_ci		inode_lock(d_inode(root));
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci		if (!list_empty(&e->list))
62462306a36Sopenharmony_ci			kill_node(e);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		inode_unlock(d_inode(root));
62762306a36Sopenharmony_ci		break;
62862306a36Sopenharmony_ci	default:
62962306a36Sopenharmony_ci		return res;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return count;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic const struct file_operations bm_entry_operations = {
63662306a36Sopenharmony_ci	.read		= bm_entry_read,
63762306a36Sopenharmony_ci	.write		= bm_entry_write,
63862306a36Sopenharmony_ci	.llseek		= default_llseek,
63962306a36Sopenharmony_ci};
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci/* /register */
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic ssize_t bm_register_write(struct file *file, const char __user *buffer,
64462306a36Sopenharmony_ci			       size_t count, loff_t *ppos)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	Node *e;
64762306a36Sopenharmony_ci	struct inode *inode;
64862306a36Sopenharmony_ci	struct super_block *sb = file_inode(file)->i_sb;
64962306a36Sopenharmony_ci	struct dentry *root = sb->s_root, *dentry;
65062306a36Sopenharmony_ci	int err = 0;
65162306a36Sopenharmony_ci	struct file *f = NULL;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	e = create_entry(buffer, count);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (IS_ERR(e))
65662306a36Sopenharmony_ci		return PTR_ERR(e);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (e->flags & MISC_FMT_OPEN_FILE) {
65962306a36Sopenharmony_ci		f = open_exec(e->interpreter);
66062306a36Sopenharmony_ci		if (IS_ERR(f)) {
66162306a36Sopenharmony_ci			pr_notice("register: failed to install interpreter file %s\n",
66262306a36Sopenharmony_ci				 e->interpreter);
66362306a36Sopenharmony_ci			kfree(e);
66462306a36Sopenharmony_ci			return PTR_ERR(f);
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci		e->interp_file = f;
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	inode_lock(d_inode(root));
67062306a36Sopenharmony_ci	dentry = lookup_one_len(e->name, root, strlen(e->name));
67162306a36Sopenharmony_ci	err = PTR_ERR(dentry);
67262306a36Sopenharmony_ci	if (IS_ERR(dentry))
67362306a36Sopenharmony_ci		goto out;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	err = -EEXIST;
67662306a36Sopenharmony_ci	if (d_really_is_positive(dentry))
67762306a36Sopenharmony_ci		goto out2;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	inode = bm_get_inode(sb, S_IFREG | 0644);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	err = -ENOMEM;
68262306a36Sopenharmony_ci	if (!inode)
68362306a36Sopenharmony_ci		goto out2;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	err = simple_pin_fs(&bm_fs_type, &bm_mnt, &entry_count);
68662306a36Sopenharmony_ci	if (err) {
68762306a36Sopenharmony_ci		iput(inode);
68862306a36Sopenharmony_ci		inode = NULL;
68962306a36Sopenharmony_ci		goto out2;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	e->dentry = dget(dentry);
69362306a36Sopenharmony_ci	inode->i_private = e;
69462306a36Sopenharmony_ci	inode->i_fop = &bm_entry_operations;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	d_instantiate(dentry, inode);
69762306a36Sopenharmony_ci	write_lock(&entries_lock);
69862306a36Sopenharmony_ci	list_add(&e->list, &entries);
69962306a36Sopenharmony_ci	write_unlock(&entries_lock);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	err = 0;
70262306a36Sopenharmony_ciout2:
70362306a36Sopenharmony_ci	dput(dentry);
70462306a36Sopenharmony_ciout:
70562306a36Sopenharmony_ci	inode_unlock(d_inode(root));
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	if (err) {
70862306a36Sopenharmony_ci		if (f)
70962306a36Sopenharmony_ci			filp_close(f, NULL);
71062306a36Sopenharmony_ci		kfree(e);
71162306a36Sopenharmony_ci		return err;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci	return count;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic const struct file_operations bm_register_operations = {
71762306a36Sopenharmony_ci	.write		= bm_register_write,
71862306a36Sopenharmony_ci	.llseek		= noop_llseek,
71962306a36Sopenharmony_ci};
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci/* /status */
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic ssize_t
72462306a36Sopenharmony_cibm_status_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	char *s = enabled ? "enabled\n" : "disabled\n";
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	return simple_read_from_buffer(buf, nbytes, ppos, s, strlen(s));
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic ssize_t bm_status_write(struct file *file, const char __user *buffer,
73262306a36Sopenharmony_ci		size_t count, loff_t *ppos)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	int res = parse_command(buffer, count);
73562306a36Sopenharmony_ci	struct dentry *root;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	switch (res) {
73862306a36Sopenharmony_ci	case 1:
73962306a36Sopenharmony_ci		/* Disable all handlers. */
74062306a36Sopenharmony_ci		enabled = 0;
74162306a36Sopenharmony_ci		break;
74262306a36Sopenharmony_ci	case 2:
74362306a36Sopenharmony_ci		/* Enable all handlers. */
74462306a36Sopenharmony_ci		enabled = 1;
74562306a36Sopenharmony_ci		break;
74662306a36Sopenharmony_ci	case 3:
74762306a36Sopenharmony_ci		/* Delete all handlers. */
74862306a36Sopenharmony_ci		root = file_inode(file)->i_sb->s_root;
74962306a36Sopenharmony_ci		inode_lock(d_inode(root));
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		while (!list_empty(&entries))
75262306a36Sopenharmony_ci			kill_node(list_first_entry(&entries, Node, list));
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		inode_unlock(d_inode(root));
75562306a36Sopenharmony_ci		break;
75662306a36Sopenharmony_ci	default:
75762306a36Sopenharmony_ci		return res;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	return count;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic const struct file_operations bm_status_operations = {
76462306a36Sopenharmony_ci	.read		= bm_status_read,
76562306a36Sopenharmony_ci	.write		= bm_status_write,
76662306a36Sopenharmony_ci	.llseek		= default_llseek,
76762306a36Sopenharmony_ci};
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci/* Superblock handling */
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic const struct super_operations s_ops = {
77262306a36Sopenharmony_ci	.statfs		= simple_statfs,
77362306a36Sopenharmony_ci	.evict_inode	= bm_evict_inode,
77462306a36Sopenharmony_ci};
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic int bm_fill_super(struct super_block *sb, struct fs_context *fc)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	int err;
77962306a36Sopenharmony_ci	static const struct tree_descr bm_files[] = {
78062306a36Sopenharmony_ci		[2] = {"status", &bm_status_operations, S_IWUSR|S_IRUGO},
78162306a36Sopenharmony_ci		[3] = {"register", &bm_register_operations, S_IWUSR},
78262306a36Sopenharmony_ci		/* last one */ {""}
78362306a36Sopenharmony_ci	};
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	err = simple_fill_super(sb, BINFMTFS_MAGIC, bm_files);
78662306a36Sopenharmony_ci	if (!err)
78762306a36Sopenharmony_ci		sb->s_op = &s_ops;
78862306a36Sopenharmony_ci	return err;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic int bm_get_tree(struct fs_context *fc)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	return get_tree_single(fc, bm_fill_super);
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic const struct fs_context_operations bm_context_ops = {
79762306a36Sopenharmony_ci	.get_tree	= bm_get_tree,
79862306a36Sopenharmony_ci};
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int bm_init_fs_context(struct fs_context *fc)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	fc->ops = &bm_context_ops;
80362306a36Sopenharmony_ci	return 0;
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic struct linux_binfmt misc_format = {
80762306a36Sopenharmony_ci	.module = THIS_MODULE,
80862306a36Sopenharmony_ci	.load_binary = load_misc_binary,
80962306a36Sopenharmony_ci};
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cistatic struct file_system_type bm_fs_type = {
81262306a36Sopenharmony_ci	.owner		= THIS_MODULE,
81362306a36Sopenharmony_ci	.name		= "binfmt_misc",
81462306a36Sopenharmony_ci	.init_fs_context = bm_init_fs_context,
81562306a36Sopenharmony_ci	.kill_sb	= kill_litter_super,
81662306a36Sopenharmony_ci};
81762306a36Sopenharmony_ciMODULE_ALIAS_FS("binfmt_misc");
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic int __init init_misc_binfmt(void)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	int err = register_filesystem(&bm_fs_type);
82262306a36Sopenharmony_ci	if (!err)
82362306a36Sopenharmony_ci		insert_binfmt(&misc_format);
82462306a36Sopenharmony_ci	return err;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic void __exit exit_misc_binfmt(void)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	unregister_binfmt(&misc_format);
83062306a36Sopenharmony_ci	unregister_filesystem(&bm_fs_type);
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cicore_initcall(init_misc_binfmt);
83462306a36Sopenharmony_cimodule_exit(exit_misc_binfmt);
83562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
836