patch-2.4.13 linux/fs/binfmt_misc.c

Next file: linux/fs/block_dev.c
Previous file: linux/fs/binfmt_elf.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.12/linux/fs/binfmt_misc.c linux/fs/binfmt_misc.c
@@ -13,165 +13,80 @@
  *  1997-06-26 hpa: pass the real filename rather than argv[0]
  *  1997-06-30 minor cleanup
  *  1997-08-09 removed extension stripping, locking cleanup
+ *  2001-02-28 AV: rewritten into something that resembles C. Original didn't.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
+#include <linux/init.h>
 
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
 #include <linux/binfmts.h>
-#include <linux/init.h>
-#include <linux/proc_fs.h>
-#include <linux/string.h>
+#include <linux/slab.h>
 #include <linux/ctype.h>
 #include <linux/file.h>
-#include <linux/spinlock.h>
+#include <linux/pagemap.h>
+
 #include <asm/uaccess.h>
 
-/*
- * We should make this work with a "stub-only" /proc,
- * which would just not be able to be configured.
- * Right now the /proc-fs support is too black and white,
- * though, so just remind people that this should be
- * fixed..
- */
-#ifndef CONFIG_PROC_FS
-#error You really need /proc support for binfmt_misc. Please reconfigure!
-#endif
-
-#define VERBOSE_STATUS /* undef this to save 400 bytes kernel memory */
-
-struct binfmt_entry {
-	struct binfmt_entry *next;
-	long id;
+enum {
+	VERBOSE_STATUS = 1 /* make it zero to save 400 bytes kernel memory */
+};
+
+static LIST_HEAD(entries);
+static int enabled = 1;
+
+enum {Enabled, Magic};
+
+typedef struct {
+	struct list_head list;
 	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 */
-
-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 = {
-	NULL, THIS_MODULE, load_misc_binary, NULL, NULL, 0
-};
-
-static struct proc_dir_entry *bm_dir;
-
-static struct binfmt_entry *entries;
-static int free_id = 1;
-static int enabled = 1;
+	char *name;
+	struct dentry *dentry;
+} Node;
 
 static rwlock_t entries_lock __attribute__((unused)) = 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;
-	write_unlock(&entries_lock);
-
-	if (e) {
-		entry_proc_cleanup(e);
-		kfree(e);
-	}
-}
-
-/*
- * Clear all registered binary formats
- */
-static void clear_entries(void)
-{
-	struct binfmt_entry *e, *n;
-
-	write_lock(&entries_lock);
-	n = entries;
-	entries = NULL;
-	write_unlock(&entries_lock);
-
-	while ((e = n)) {
-		n = e->next;
-		entry_proc_cleanup(e);
-		kfree(e);
-	}
-}
-
-/*
- * Find entry through id and lock it
- */
-static struct binfmt_entry *get_entry(int id)
-{
-	struct binfmt_entry *e;
-
-	read_lock(&entries_lock);
-	e = entries;
-	while (e && (e->id != id))
-		e = e->next;
-	if (!e)
-		read_unlock(&entries_lock);
-	return e;
-}
-
-/*
- * unlock entry
- */
-static inline void put_entry(struct binfmt_entry *e)
-{
-	if (e)
-		read_unlock(&entries_lock);
-}
-
-
 /* 
  * Check if we support the binfmt
- * if we do, return the binfmt_entry, else NULL
+ * if we do, return the node, else NULL
  * locking is done in load_misc_binary
  */
-static struct binfmt_entry *check_file(struct linux_binprm *bprm)
+static Node *check_file(struct linux_binprm *bprm)
 {
-	struct binfmt_entry *e;
 	char *p = strrchr(bprm->filename, '.');
-	int j;
+	struct list_head *l;
 
-	e = entries;
-	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;
-			}
+	for (l = entries.next; l != &entries; l = l->next) {
+		Node *e = list_entry(l, Node, list);
+		char *s;
+		int j;
+
+		if (!test_bit(Enabled, &e->flags))
+			continue;
+
+		if (!test_bit(Magic, &e->flags)) {
+			if (p && !strcmp(e->magic, p + 1))
+				return e;
+			continue;
 		}
-		e = e->next;
-	};
+
+		s = bprm->buf + e->offset;
+		if (e->mask) {
+			for (j = 0; j < e->size; j++)
+				if ((*s++ ^ e->magic[j]) & e->mask[j])
+					break;
+		} else {
+			for (j = 0; j < e->size; j++)
+				if ((*s++ ^ e->magic[j]))
+					break;
+		}
+		if (j == e->size)
+			return e;
+	}
 	return NULL;
 }
 
@@ -180,7 +95,7 @@
  */
 static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 {
-	struct binfmt_entry *fmt;
+	Node *fmt;
 	struct file * file;
 	char iname[BINPRM_BUF_SIZE];
 	char *iname_addr = iname;
@@ -228,11 +143,7 @@
 	return retval;
 }
 
-
-
-/*
- * /proc handling routines
- */
+/* Command parsers */
 
 /*
  * parses and copies one argument enclosed in del from *sp to *dp,
@@ -240,34 +151,38 @@
  * 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)
+static char *scanarg(char *s, char del)
 {
-	char c = 0, *res = *dp;
+	char c;
 
-	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;
+	while ((c = *s++) != del) {
+		if (c == '\\' && *s == 'x') {
+			s++;
+			if (!isxdigit(*s++))
+				return NULL;
+			if (!isxdigit(*s++))
+				return NULL;
 		}
 	}
-	if (*err || (c != del) || (res == *dp))
-		res = NULL;
-	else if (!special)
-		*((*dp)++) = '\0';
-	return res;
+	return s;
+}
+
+static int unquote(char *from)
+{
+	char c = 0, *s = from, *p = from;
+
+	while ((c = *s++) != '\0') {
+		if (c == '\\' && *s == 'x') {
+			s++;
+			c = toupper(*s++);
+			*p = (c - (isdigit(c) ? '0' : 'A' - 10)) << 4;
+			c = toupper(*s++);
+			*p++ |= c - (isdigit(c) ? '0' : 'A' - 10);
+			continue;
+		}
+		*p++ = c;
+	}
+	return p - from;
 }
 
 /*
@@ -275,245 +190,533 @@
  * ':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)
+static Node *create_entry(const char *buffer, size_t count)
 {
-	const char *sp;
-	char del, *dp;
-	struct binfmt_entry *e;
-	int memsize, cnt = count - 1, err;
+	Node *e;
+	int memsize, err;
+	char *buf, *p;
+	char del;
 
 	/* some sanity checks */
 	err = -EINVAL;
 	if ((count < 11) || (count > 256))
-		goto _err;
+		goto out;
 
 	err = -ENOMEM;
-	memsize = sizeof(struct binfmt_entry) + count;
-	if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER)))
-		goto _err;
-
-	err = 0;
-	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 of type for ext/magic
-	   flag due to the nice encoding of E and M */
-	if ((*sp & ~('E' | 'M')) || (sp[1] != del))
-		err = -EINVAL;
-	else
-		e->flags = (*sp++ & (ENTRY_MAGIC | 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) > (BINPRM_BUF_SIZE - 1)) ||
-	    !(e->proc_name) || !(e->interpreter) || entry_proc_setup(e))
-		goto free_err;
+	memsize = sizeof(Node) + count + 8;
+	e = (Node *) kmalloc(memsize, GFP_USER);
+	if (!e)
+		goto out;
 
-	write_lock(&entries_lock);
-	e->next = entries;
-	entries = e;
-	write_unlock(&entries_lock);
+	p = buf = (char *)e + sizeof(Node);
 
-	err = count;
-_err:
-	return err;
-free_err:
+	memset(e, 0, sizeof(Node));
+	if (copy_from_user(buf, buffer, count))
+		goto Efault;
+
+	del = *p++;	/* delimeter */
+
+	memset(buf+count, del, 8);
+
+	e->name = p;
+	p = strchr(p, del);
+	if (!p)
+		goto Einval;
+	*p++ = '\0';
+	if (!e->name[0] ||
+	    !strcmp(e->name, ".") ||
+	    !strcmp(e->name, "..") ||
+	    strchr(e->name, '/'))
+		goto Einval;
+	switch (*p++) {
+		case 'E': e->flags = 1<<Enabled; break;
+		case 'M': e->flags = (1<<Enabled) | (1<<Magic); break;
+		default: goto Einval;
+	}
+	if (*p++ != del)
+		goto Einval;
+	if (test_bit(Magic, &e->flags)) {
+		char *s = strchr(p, del);
+		if (!s)
+			goto Einval;
+		*s++ = '\0';
+		e->offset = simple_strtoul(p, &p, 10);
+		if (*p++)
+			goto Einval;
+		e->magic = p;
+		p = scanarg(p, del);
+		if (!p)
+			goto Einval;
+		p[-1] = '\0';
+		if (!e->magic[0])
+			goto Einval;
+		e->mask = p;
+		p = scanarg(p, del);
+		if (!p)
+			goto Einval;
+		p[-1] = '\0';
+		if (!e->mask[0])
+			e->mask = NULL;
+		e->size = unquote(e->magic);
+		if (e->mask && unquote(e->mask) != e->size)
+			goto Einval;
+		if (e->size + e->offset > BINPRM_BUF_SIZE)
+			goto Einval;
+	} else {
+		p = strchr(p, del);
+		if (!p)
+			goto Einval;
+		*p++ = '\0';
+		e->magic = p;
+		p = strchr(p, del);
+		if (!p)
+			goto Einval;
+		*p++ = '\0';
+		if (!e->magic[0] || strchr(e->magic, '/'))
+			goto Einval;
+		p = strchr(p, del);
+		if (!p)
+			goto Einval;
+		*p++ = '\0';
+	}
+	e->interpreter = p;
+	p = strchr(p, del);
+	if (!p)
+		goto Einval;
+	*p++ = '\0';
+	if (!e->interpreter[0])
+		goto Einval;
+
+	if (*p == '\n')
+		p++;
+	if (p != buf + count)
+		goto Einval;
+	return e;
+
+out:
+	return ERR_PTR(err);
+
+Efault:
 	kfree(e);
-	err = -EINVAL;
-	goto _err;
+	return ERR_PTR(-EFAULT);
+Einval:
+	kfree(e);
+	return ERR_PTR(-EINVAL);
 }
 
 /*
- * Get status of entry/binfmt_misc
- * FIXME? should an entry be marked disabled if binfmt_misc is disabled though
- *        entry is enabled?
+ * Set status of entry/binfmt_misc:
+ * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
  */
-static int proc_read_status(char *page, char **start, off_t off,
-			    int count, int *eof, void *data)
+static int parse_command(const char *buffer, size_t count)
+{
+	char s[4];
+
+	if (!count)
+		return 0;
+	if (count > 3)
+		return -EINVAL;
+	if (copy_from_user(s, buffer, count))
+		return -EFAULT;
+	if (s[count-1] == '\n')
+		count--;
+	if (count == 1 && s[0] == '0')
+		return 1;
+	if (count == 1 && s[0] == '1')
+		return 2;
+	if (count == 2 && s[0] == '-' && s[1] == '1')
+		return 3;
+	return -EINVAL;
+}
+
+/* generic stuff */
+
+static void entry_status(Node *e, char *page)
 {
-	struct binfmt_entry *e;
 	char *dp;
-	int elen, i, err;
+	char *status = "disabled";
 
-#ifndef VERBOSE_STATUS
-	if (data) {
-		if (!(e = get_entry((int) data))) {
-			err = -ENOENT;
-			goto _err;
-		}
-		i = e->flags & ENTRY_ENABLED;
-		put_entry(e);
+	if (test_bit(Enabled, &e->flags))
+		status = "enabled";
+
+	if (!VERBOSE_STATUS) {
+		sprintf(page, "%s\n", status);
+		return;
+	}
+
+	sprintf(page, "%s\ninterpreter %s\n", status, e->interpreter);
+	dp = page + strlen(page);
+	if (!test_bit(Magic, &e->flags)) {
+		sprintf(dp, "extension .%s\n", e->magic);
 	} else {
-		i = enabled;
-	} 
-	sprintf(page, "%s\n", (i ? "enabled" : "disabled"));
-#else
-	if (!data)
-		sprintf(page, "%s\n", (enabled ? "enabled" : "disabled"));
-	else {
-		if (!(e = get_entry((long) data))) {
-			err = -ENOENT;
-			goto _err;
-		}
-		sprintf(page, "%s\ninterpreter %s\n",
-		        (e->flags & ENTRY_ENABLED ? "enabled" : "disabled"),
-			e->interpreter);
+		int i;
+
+		sprintf(dp, "offset %i\nmagic ", e->offset);
 		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->magic[i]));
+				sprintf(dp, "%02x", 0xff & (int) (e->mask[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';
 		}
-		put_entry(e);
+		*dp++ = '\n';
+		*dp = '\0';
 	}
-#endif
+}
 
-	elen = strlen(page) - off;
-	if (elen < 0)
-		elen = 0;
-	*eof = (elen <= count) ? 1 : 0;
-	*start = page + off;
-	err = elen;
+static struct inode *bm_get_inode(struct super_block *sb, int mode)
+{
+	struct inode * inode = new_inode(sb);
 
-_err:
-	return err;
+	if (inode) {
+		inode->i_mode = mode;
+		inode->i_uid = 0;
+		inode->i_gid = 0;
+		inode->i_blksize = PAGE_CACHE_SIZE;
+		inode->i_blocks = 0;
+		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	}
+	return inode;
 }
 
-/*
- * 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)
+static void bm_clear_inode(struct inode *inode)
 {
-	struct binfmt_entry *e;
-	int res = count;
+	Node *e = inode->u.generic_ip;
 
-	if (buffer[count-1] == '\n')
-		count--;
-	if ((count == 1) && !(buffer[0] & ~('0' | '1'))) {
-		if (data) {
-			if ((e = get_entry((long) data)))
-				e->flags = (e->flags & ~ENTRY_ENABLED)
-					    | (int)(buffer[0] - '0');
-			put_entry(e);
+	if (e) {
+		write_lock(&entries_lock);
+		list_del(&e->list);
+		write_unlock(&entries_lock);
+		kfree(e);
+	}
+}
+
+static void kill_node(Node *e)
+{
+	struct dentry *dentry;
+
+	write_lock(&entries_lock);
+	dentry = e->dentry;
+	if (dentry) {
+		list_del(&e->list);
+		INIT_LIST_HEAD(&e->list);
+		e->dentry = NULL;
+	}
+	write_unlock(&entries_lock);
+
+	if (dentry) {
+		dentry->d_inode->i_nlink--;
+		d_drop(dentry);
+		dput(dentry);
+	}
+}
+
+/* /<entry> */
+
+static ssize_t
+bm_entry_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
+{
+	Node *e = file->f_dentry->d_inode->u.generic_ip;
+	loff_t pos = *ppos;
+	ssize_t res;
+	char *page;
+	int len;
+
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+
+	entry_status(e, page);
+	len = strlen(page);
+
+	res = -EINVAL;
+	if (pos < 0)
+		goto out;
+	res = 0;
+	if (pos >= len)
+		goto out;
+	if (len < pos + nbytes)
+		nbytes = len - pos;
+	res = -EFAULT;
+	if (copy_to_user(buf, page + pos, nbytes))
+		goto out;
+	*ppos = pos + nbytes;
+	res = nbytes;
+out:
+	free_page((unsigned long) page);
+	return res;
+}
+
+static ssize_t bm_entry_write(struct file *file, const char *buffer,
+				size_t count, loff_t *ppos)
+{
+	struct dentry *root;
+	Node *e = file->f_dentry->d_inode->u.generic_ip;
+	int res = parse_command(buffer, count);
+
+	switch (res) {
+		case 1: clear_bit(Enabled, &e->flags);
+			break;
+		case 2: set_bit(Enabled, &e->flags);
+			break;
+		case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
+			down(&root->d_inode->i_sem);
+			down(&root->d_inode->i_zombie);
+
+			kill_node(e);
+
+			up(&root->d_inode->i_zombie);
+			up(&root->d_inode->i_sem);
+			dput(root);
+			break;
+		default: return res;
+	}
+	return count;
+}
+
+static struct file_operations bm_entry_operations = {
+	read:		bm_entry_read,
+	write:		bm_entry_write,
+};
+
+/* /register */
+
+static ssize_t bm_register_write(struct file *file, const char *buffer,
+			       size_t count, loff_t *ppos)
+{
+	Node *e;
+	struct dentry *root, *dentry;
+	struct super_block *sb = file->f_vfsmnt->mnt_sb;
+	int err = 0;
+
+	e = create_entry(buffer, count);
+
+	if (IS_ERR(e))
+		return PTR_ERR(e);
+
+	root = dget(sb->s_root);
+	down(&root->d_inode->i_sem);
+	dentry = lookup_one_len(e->name, root, strlen(e->name));
+	err = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		down(&root->d_inode->i_zombie);
+		if (dentry->d_inode) {
+			err = -EEXIST;
 		} else {
-			enabled = buffer[0] - '0';
+			struct inode * inode = bm_get_inode(sb, S_IFREG | 0644);
+			err = -ENOMEM;
+
+			if (inode) {
+				write_lock(&entries_lock);
+
+				e->dentry = dget(dentry);
+				inode->u.generic_ip = e;
+				inode->i_fop = &bm_entry_operations;
+				d_instantiate(dentry, inode);
+
+				list_add(&e->list, &entries);
+				write_unlock(&entries_lock);
+
+				err = 0;
+			}
 		}
-	} else if ((count == 2) && (buffer[0] == '-') && (buffer[1] == '1')) {
-		if (data)
-			clear_entry((long) data);
-		else
-			clear_entries();
-	} else {
-		res = -EINVAL;
+		up(&root->d_inode->i_zombie);
+		dput(dentry);
 	}
-	return res;
+	up(&root->d_inode->i_sem);
+	dput(root);
+
+	if (err) {
+		kfree(e);
+		return -EINVAL;
+	}
+	return count;
 }
 
-/*
- * Remove the /proc-dir entries of one binfmt
- */
-static void entry_proc_cleanup(struct binfmt_entry *e)
+static struct file_operations bm_register_operations = {
+	write:		bm_register_write,
+};
+
+/* /status */
+
+static ssize_t
+bm_status_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
 {
-	remove_proc_entry(e->proc_name, bm_dir);
+	char *s = enabled ? "enabled" : "disabled";
+	int len = strlen(s);
+	loff_t pos = *ppos;
+
+	if (pos < 0)
+		return -EINVAL;
+	if (pos >= len)
+		return 0;
+	if (len < pos + nbytes)
+		nbytes = len - pos;
+	if (copy_to_user(buf, s + pos, nbytes))
+		return -EFAULT;
+	*ppos = pos + nbytes;
+	return nbytes;
+}
+
+static ssize_t bm_status_write(struct file * file, const char * buffer,
+		size_t count, loff_t *ppos)
+{
+	int res = parse_command(buffer, count);
+	struct dentry *root;
+
+	switch (res) {
+		case 1: enabled = 0; break;
+		case 2: enabled = 1; break;
+		case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
+			down(&root->d_inode->i_sem);
+			down(&root->d_inode->i_zombie);
+
+			while (!list_empty(&entries))
+				kill_node(list_entry(entries.next, Node, list));
+
+			up(&root->d_inode->i_zombie);
+			up(&root->d_inode->i_sem);
+			dput(root);
+		default: return res;
+	}
+	return count;
 }
 
-/*
- * Create the /proc-dir entry for binfmt
- */
-static int entry_proc_setup(struct binfmt_entry *e)
+static struct file_operations bm_status_operations = {
+	read:		bm_status_read,
+	write:		bm_status_write,
+};
+
+/* / */
+
+static struct dentry * bm_lookup(struct inode *dir, struct dentry *dentry)
+{
+	d_add(dentry, NULL);
+	return NULL;
+}
+
+static struct file_operations bm_dir_operations = {
+	read:		generic_read_dir,
+	readdir:	dcache_readdir,
+};
+
+static struct inode_operations bm_dir_inode_operations = {
+	lookup:		bm_lookup,
+};
+
+/* Superblock handling */
+
+static int bm_statfs(struct super_block *sb, struct statfs *buf)
 {
-	if (!(e->proc_dir = create_proc_entry(e->proc_name,
-			 	S_IFREG | S_IRUGO | S_IWUSR, bm_dir)))
-	{
-		printk(KERN_WARNING "Unable to create /proc entry.\n");
-		return -ENOENT;
-	}
-	e->proc_dir->data = (void *) (e->id);
-	e->proc_dir->read_proc = proc_read_status;
-	e->proc_dir->write_proc = proc_write_status;
+	buf->f_type = sb->s_magic;
+	buf->f_bsize = PAGE_CACHE_SIZE;
+	buf->f_namelen = 255;
 	return 0;
 }
 
-static int __init init_misc_binfmt(void)
+static struct super_operations s_ops = {
+	statfs:		bm_statfs,
+	put_inode:	force_delete,
+	clear_inode:	bm_clear_inode,
+};
+
+static struct super_block *bm_read_super(struct super_block * sb, void * data, int silent)
 {
-	int error = -ENOENT;
-	struct proc_dir_entry *status = NULL, *reg;
+	struct qstr names[2] = {{name:"status"}, {name:"register"}};
+	struct inode * inode;
+	struct dentry * dentry[3];
+	int i;
+
+	for (i=0; i<sizeof(names)/sizeof(names[0]); i++) {
+		names[i].len = strlen(names[i].name);
+		names[i].hash = full_name_hash(names[i].name, names[i].len);
+	}
 
-	bm_dir = proc_mkdir("sys/fs/binfmt_misc", NULL); /* WTF??? */
-	if (!bm_dir)
-		goto out;
-	bm_dir->owner = THIS_MODULE;
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic = 0x42494e4d;
+	sb->s_op = &s_ops;
+
+	inode = bm_get_inode(sb, S_IFDIR | 0755);
+	if (!inode)
+		return NULL;
+	inode->i_op = &bm_dir_inode_operations;
+	inode->i_fop = &bm_dir_operations;
+	dentry[0] = d_alloc_root(inode);
+	if (!dentry[0]) {
+		iput(inode);
+		return NULL;
+	}
+	dentry[1] = d_alloc(dentry[0], &names[0]);
+	if (!dentry[1])
+		goto out1;
+	dentry[2] = d_alloc(dentry[0], &names[1]);
+	if (!dentry[2])
+		goto out2;
+	inode = bm_get_inode(sb, S_IFREG | 0644);
+	if (!inode)
+		goto out3;
+	inode->i_fop = &bm_status_operations;
+	d_add(dentry[1], inode);
+	inode = bm_get_inode(sb, S_IFREG | 0400);
+	if (!inode)
+		goto out3;
+	inode->i_fop = &bm_register_operations;
+	d_add(dentry[2], inode);
+
+	sb->s_root = dentry[0];
+	return sb;
+
+out3:
+	dput(dentry[2]);
+out2:
+	dput(dentry[1]);
+out1:
+	dput(dentry[0]);
+	return NULL;
+}
+
+static struct linux_binfmt misc_format = {
+	NULL, THIS_MODULE, load_misc_binary, NULL, NULL, 0
+};
 
-	status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR,
-					bm_dir);
-	if (!status)
-		goto cleanup_bm;
-	status->read_proc = proc_read_status;
-	status->write_proc = proc_write_status;
-
-	reg = create_proc_entry("register", S_IFREG | S_IWUSR, bm_dir);
-	if (!reg)
-		goto cleanup_status;
-	reg->write_proc = proc_write_register;
+static DECLARE_FSTYPE(bm_fs_type, "binfmt_misc", bm_read_super, FS_SINGLE|FS_LITTER);
 
-	error = register_binfmt(&misc_format);
-out:
-	return error;
+static struct vfsmount *bm_mnt;
 
-cleanup_status:
-	remove_proc_entry("status", bm_dir);
-cleanup_bm:
-	remove_proc_entry("sys/fs/binfmt_misc", NULL);
-	goto out;
+static int __init init_misc_binfmt(void)
+{
+	int err = register_filesystem(&bm_fs_type);
+	if (!err) {
+		bm_mnt = kern_mount(&bm_fs_type);
+		err = PTR_ERR(bm_mnt);
+		if (IS_ERR(bm_mnt))
+			unregister_filesystem(&bm_fs_type);
+		else {
+			err = register_binfmt(&misc_format);
+			if (err) {
+				unregister_filesystem(&bm_fs_type);
+				kern_umount(bm_mnt);
+			}
+		}
+	}
+	return err;
 }
 
 static void __exit exit_misc_binfmt(void)
 {
 	unregister_binfmt(&misc_format);
-	remove_proc_entry("register", bm_dir);
-	remove_proc_entry("status", bm_dir);
-	clear_entries();
-	remove_proc_entry("sys/fs/binfmt_misc", NULL);
+	unregister_filesystem(&bm_fs_type);
+	kern_umount(bm_mnt);
 }
 
 EXPORT_NO_SYMBOLS;
 
 module_init(init_misc_binfmt);
 module_exit(exit_misc_binfmt);
+MODULE_LICENSE("GPL");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)