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
- Lines: 1017
- Date:
Sat Oct 20 19:14:42 2001
- Orig file:
v2.4.12/linux/fs/binfmt_misc.c
- Orig date:
Fri Feb 9 11:29:44 2001
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)