module use counts for 2.1.57 /proc

Bill Hawes (whawes@star.net)
Fri, 03 Oct 1997 09:08:46 -0400


This is a multi-part message in MIME format.
--------------2DF424E29296A81A5488477D
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

I've attached an updated patch for 2.1.57 /proc that implements module
use count tracking at the filesystem level. This guarantees that the
module code can't be removed while you're CD'ed to or otherwise using a
/proc entry for the module.

It's implemented using the fill_inode() callback that Philip Gladstone
suggested. I've modified binfmt_misc to use the new fill_inode call,
and it should be very easy to add this to the other modules using /proc.
The sequence
cd /proc/sys/fs/binfmt_misc
ls
rmmod binfmt_misc
now gives a "resource busy" message as it should.

I've also implemented the unlink operation for dynamic /proc entries, so
modules won't need to use funny rules like "echo -1 >status" to delete
entries.

If any of the module maintainers would like to try out the new use count
tracking, just check the code for binfmt_misc. (It should only take a
couple of minutes to implement.)

Regards,
Bill
--------------2DF424E29296A81A5488477D
Content-Type: text/plain; charset=us-ascii; name="procfs_57-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="procfs_57-patch"

--- linux-2.1.57/include/linux/proc_fs.h.old Tue Sep 30 08:46:13 1997
+++ linux-2.1.57/include/linux/proc_fs.h Thu Oct 2 23:51:28 1997
@@ -237,13 +237,15 @@
unsigned long size;
struct inode_operations * ops;
int (*get_info)(char *, char **, off_t, int, int);
- void (*fill_inode)(struct inode *);
+ void (*fill_inode)(struct inode *, int);
struct proc_dir_entry *next, *parent, *subdir;
void *data;
int (*read_proc)(char *page, char **start, off_t off,
int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char *buffer,
unsigned long count, void *data);
+ unsigned int count; /* use count */
+ int deleted; /* delete flag */
};

extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
--- linux-2.1.57/fs/proc/root.c.old Wed Sep 10 09:21:27 1997
+++ linux-2.1.57/fs/proc/root.c Wed Oct 1 23:06:21 1997
@@ -25,6 +25,7 @@

static int proc_root_readdir(struct file *, void *, filldir_t);
static int proc_root_lookup(struct inode *,struct dentry *);
+static int proc_unlink(struct inode *, struct dentry *);

static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};

@@ -73,6 +74,29 @@
};

/*
+ * /proc dynamic directories now support unlinking
+ */
+struct inode_operations proc_dyna_dir_inode_operations = {
+ &proc_dir_operations, /* default proc dir ops */
+ NULL, /* create */
+ proc_lookup, /* lookup */
+ NULL, /* link */
+ proc_unlink, /* unlink(struct inode *, struct dentry *) */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+/*
* The root /proc directory is special, as it has the
* <pid> directories. Thus we don't use the generic
* directory handling functions for that..
@@ -173,7 +197,8 @@

int proc_openprom_regdev(struct openpromfs_dev *d)
{
- if (proc_openpromdev_ino == PROC_OPENPROMD_FIRST + PROC_NOPENPROMD) return -1;
+ if (proc_openpromdev_ino == PROC_OPENPROMD_FIRST + PROC_NOPENPROMD)
+ return -1;
d->next = proc_openprom_devices;
d->inode = proc_openpromdev_ino++;
proc_openprom_devices = d;
@@ -218,6 +243,7 @@
(inode, filp, dirent, filldir);
return -EINVAL;
}
+#define OPENPROM_DEFREADDIR proc_openprom_defreaddir

static int
proc_openprom_deflookup(struct inode * dir, struct dentry *dentry)
@@ -229,17 +255,17 @@
(dir, dentry);
return -ENOENT;
}
+#define OPENPROM_DEFLOOKUP proc_openprom_deflookup
+#else
+#define OPENPROM_DEFREADDIR NULL
+#define OPENPROM_DEFLOOKUP NULL
#endif

static struct file_operations proc_openprom_operations = {
NULL, /* lseek - default */
NULL, /* read - bad */
NULL, /* write - bad */
-#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD)
- proc_openprom_defreaddir,/* readdir */
-#else
- NULL, /* readdir */
-#endif
+ OPENPROM_DEFREADDIR, /* readdir */
NULL, /* poll - default */
NULL, /* ioctl - default */
NULL, /* mmap */
@@ -251,11 +277,7 @@
struct inode_operations proc_openprom_inode_operations = {
&proc_openprom_operations,/* default net directory file-ops */
NULL, /* create */
-#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD)
- proc_openprom_deflookup,/* lookup */
-#else
- NULL, /* lookup */
-#endif
+ OPENPROM_DEFLOOKUP, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
@@ -639,6 +661,26 @@
}

/*
+ * As some entries in /proc are volatile, we want to
+ * get rid of unused dentries. This could be made
+ * smarter: we could keep a "volatile" flag in the
+ * inode to indicate which ones to keep.
+ */
+static void
+proc_delete_dentry(struct dentry * dentry)
+{
+ d_drop(dentry);
+}
+
+static struct dentry_operations proc_dentry_operations =
+{
+ NULL, /* revalidate */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ proc_delete_dentry /* d_delete(struct dentry *) */
+};
+
+/*
* Don't create negative dentries here, return -ENOENT by hand
* instead.
*/
@@ -646,12 +688,15 @@
{
struct inode *inode;
struct proc_dir_entry * de;
+ int error;

+ error = -ENOTDIR;
if (!dir || !S_ISDIR(dir->i_mode))
- return -ENOTDIR;
+ goto out;

- de = (struct proc_dir_entry *) dir->u.generic_ip;
+ error = -ENOENT;
inode = NULL;
+ de = (struct proc_dir_entry *) dir->u.generic_ip;
if (de) {
for (de = de->subdir; de ; de = de->next) {
if (!de || !de->low_ino)
@@ -660,18 +705,20 @@
continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
int ino = de->low_ino | (dir->i_ino & ~(0xffff));
+ error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de);
- if (!inode)
- return -EINVAL;
break;
}
}
}
- if (!inode)
- return -ENOENT;

- d_add(dentry, inode);
- return 0;
+ if (inode) {
+ dentry->d_op = &proc_dentry_operations;
+ d_add(dentry, inode);
+ error = 0;
+ }
+out:
+ return error;
}

static int proc_root_lookup(struct inode * dir, struct dentry * dentry)
@@ -721,6 +768,8 @@
if (!inode)
return -EINVAL;
}
+
+ dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
return 0;
}
@@ -825,5 +874,17 @@
filp->f_pos++;
}
read_unlock(&tasklist_lock);
+ return 0;
+}
+
+static int proc_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry * dp = dir->u.generic_ip;
+
+printk("proc_file_unlink: deleting %s/%s\n", dp->name, dentry->d_name.name);
+
+ remove_proc_entry(dentry->d_name.name, dp);
+ dentry->d_inode->i_nlink = 0;
+ d_delete(dentry);
return 0;
}
--- linux-2.1.57/fs/proc/inode.c.old Sat Jul 19 08:17:14 1997
+++ linux-2.1.57/fs/proc/inode.c Thu Oct 2 23:50:34 1997
@@ -17,23 +17,66 @@
#include <asm/system.h>
#include <asm/uaccess.h>

+extern void free_proc_entry(struct proc_dir_entry *);
+
+struct proc_dir_entry * de_get(struct proc_dir_entry *de)
+{
+ if (de)
+ de->count++;
+ return de;
+}
+
+/*
+ * Decrements the use count and checks for deferred deletion.
+ */
+void de_put(struct proc_dir_entry *de)
+{
+ if (de) {
+ if (!de->count) {
+ printk("de_put: entry %s already free!\n", de->name);
+ return;
+ }
+
+ if (!--de->count) {
+ if (de->deleted) {
+ printk("de_put: deferred delete of %s\n",
+ de->name);
+ free_proc_entry(de);
+ }
+ }
+ }
+}
+
static void proc_put_inode(struct inode *inode)
{
#ifdef CONFIG_SUN_OPENPROMFS_MODULE
- if ((inode->i_ino >= PROC_OPENPROM_FIRST)
- && (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM)
- && proc_openprom_use)
+ if ((inode->i_ino >= PROC_OPENPROM_FIRST) &&
+ (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) &&
+ proc_openprom_use)
(*proc_openprom_use)(inode, 0);
#endif
+ /*
+ * Kill off unused inodes ... VFS will unhash and
+ * delete the inode if we set i_nlink to zero.
+ */
+ if (inode->i_count == 1)
+ inode->i_nlink = 0;
}

/*
- * Does this ever happen?
+ * Decrement the use count of the proc_dir_entry.
*/
static void proc_delete_inode(struct inode *inode)
{
- printk("proc_delete_inode()?\n");
- inode->i_size = 0;
+ struct proc_dir_entry *de = inode->u.generic_ip;
+ if (de) {
+ /*
+ * Call the fill_inode hook to release module counts.
+ */
+ if (de->fill_inode)
+ de->fill_inode(inode, 0);
+ de_put(de);
+ }
}

static void proc_put_super(struct super_block *sb)
@@ -47,7 +90,7 @@
proc_read_inode,
proc_write_inode,
proc_put_inode,
- proc_delete_inode,
+ proc_delete_inode, /* delete_inode(struct inode *) */
NULL,
proc_put_super,
NULL,
@@ -85,9 +128,24 @@
return 1;
}

-struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_entry * de)
+struct inode * proc_get_inode(struct super_block * sb, int ino,
+ struct proc_dir_entry * de)
{
- struct inode * inode = iget(s, ino);
+ struct inode * inode;
+
+ /*
+ * Increment the use count so the dir entry can't disappear.
+ */
+ de_get(de);
+#if 1
+/* shouldn't ever happen */
+if (de && de->deleted)
+printk("proc_iget: using deleted entry %s, count=%d\n", de->name, de->count);
+#endif
+
+ inode = iget(sb, ino);
+ if (!inode)
+ goto out_fail;

#ifdef CONFIG_SUN_OPENPROMFS_MODULE
if ((inode->i_ino >= PROC_OPENPROM_FIRST)
@@ -95,23 +153,29 @@
&& proc_openprom_use)
(*proc_openprom_use)(inode, 1);
#endif
- if (inode && inode->i_sb == s) {
- inode->u.generic_ip = (void *) de;
- if (de) {
- if (de->mode) {
- inode->i_mode = de->mode;
- inode->i_uid = de->uid;
- inode->i_gid = de->gid;
- }
- if (de->size)
- inode->i_size = de->size;
- if (de->ops)
- inode->i_op = de->ops;
- if (de->nlink)
- inode->i_nlink = de->nlink;
- if (de->fill_inode)
- de->fill_inode(inode);
+ /* N.B. How can this test ever fail?? */
+ if (inode->i_sb != sb)
+ printk("proc_get_inode: inode fubar\n");
+
+ inode->u.generic_ip = (void *) de;
+ if (de) {
+ if (de->mode) {
+ inode->i_mode = de->mode;
+ inode->i_uid = de->uid;
+ inode->i_gid = de->gid;
}
+ if (de->size)
+ inode->i_size = de->size;
+ if (de->ops)
+ inode->i_op = de->ops;
+ if (de->nlink)
+ inode->i_nlink = de->nlink;
+ /*
+ * The fill_inode routine should use this call
+ * to increment module counts, if necessary.
+ */
+ if (de->fill_inode)
+ de->fill_inode(inode, 1);
}
/*
* Fixup the root inode's nlink value
@@ -126,26 +190,40 @@
}
read_unlock(&tasklist_lock);
}
+out:
return inode;
+
+out_fail:
+ de_put(de);
+ goto out;
}

struct super_block *proc_read_super(struct super_block *s,void *data,
int silent)
{
+ struct inode * root_inode;
+
lock_super(s);
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = PROC_SUPER_MAGIC;
s->s_op = &proc_sops;
+ root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
+ if (!root_inode)
+ goto out_no_root;
+ s->s_root = d_alloc_root(root_inode, NULL);
+ if (!s->s_root)
+ goto out_no_root;
+ parse_options(data, &root_inode->i_uid, &root_inode->i_gid);
unlock_super(s);
- s->s_root = d_alloc_root(proc_get_inode(s, PROC_ROOT_INO, &proc_root), NULL);
- if (!s->s_root) {
- s->s_dev = 0;
- printk("get root inode failed\n");
- return NULL;
- }
- parse_options(data, &s->s_root->d_inode->i_uid, &s->s_root->d_inode->i_gid);
return s;
+
+out_no_root:
+ printk("proc_read_super: get root inode failed\n");
+ iput(root_inode);
+ s->s_dev = 0;
+ unlock_super(s);
+ return NULL;
}

int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
--- linux-2.1.57/fs/proc/generic.c.old Sat Sep 20 08:16:14 1997
+++ linux-2.1.57/fs/proc/generic.c Wed Oct 1 23:05:06 1997
@@ -16,11 +16,13 @@
#include <linux/stat.h>
#include <asm/bitops.h>

+extern struct inode_operations proc_dyna_dir_inode_operations;
+
static long proc_file_read(struct inode * inode, struct file * file,
char * buf, unsigned long nbytes);
static long proc_file_write(struct inode * inode, struct file * file,
const char * buffer, unsigned long count);
-static long long proc_file_lseek(struct file * file, long long offset, int orig);
+static long long proc_file_lseek(struct file *, long long, int);

int proc_match(int len, const char *name,struct proc_dir_entry * de)
{
@@ -44,17 +46,14 @@
NULL /* can't fsync */
};

-/*
- * proc files can do almost nothing..
- */
struct inode_operations proc_file_inode_operations = {
- &proc_file_operations, /* default proc file-ops */
- NULL, /* create */
- NULL, /* lookup */
- NULL, /* link */
- NULL, /* unlink */
- NULL, /* symlink */
- NULL, /* mkdir */
+ &proc_file_operations, /* default proc file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
@@ -240,57 +239,77 @@
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
- struct proc_dir_entry *ent;
- const char *fn;
+ struct proc_dir_entry *ent = NULL;
+ const char *fn = name;
+ int len;

- if (parent)
- fn = name;
- else {
- if (xlate_proc_name(name, &parent, &fn))
- return NULL;
- }
+ if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+ goto out;
+ len = strlen(fn);

- ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+ ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
if (!ent)
- return NULL;
+ goto out;
memset(ent, 0, sizeof(struct proc_dir_entry));
+ memcpy(((char *) ent) + sizeof(*ent), fn, len + 1);
+ ent->name = ((char *) ent) + sizeof(*ent);
+ ent->namelen = len;

- if (mode == S_IFDIR)
+ if (mode == S_IFDIR) {
mode |= S_IRUGO | S_IXUGO;
- else if (mode == 0)
- mode = S_IFREG | S_IRUGO;
-
- ent->name = fn;
- ent->namelen = strlen(fn);
- ent->mode = mode;
- if (S_ISDIR(mode))
+ ent->ops = &proc_dyna_dir_inode_operations;
ent->nlink = 2;
- else
+ }
+ else if (mode == 0) {
+ mode = S_IFREG | S_IRUGO;
ent->nlink = 1;
+ }
+ ent->mode = mode;

proc_register(parent, ent);

+out:
return ent;
}

+extern void free_proc_entry(struct proc_dir_entry *);
+void free_proc_entry(struct proc_dir_entry *de)
+{
+ kfree(de);
+}
+
+/*
+ * Remove a /proc entry and free it if it's not currently in use.
+ * If it is in use, we set the 'deleted' flag.
+ */
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
{
struct proc_dir_entry *de;
- const char *fn;
+ const char *fn = name;
int len;

- if (parent)
- fn = name;
- else
- if (xlate_proc_name(name, &parent, &fn))
- return;
+ if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+ goto out;
len = strlen(fn);

for (de = parent->subdir; de ; de = de->next) {
if (proc_match(len, fn, de))
break;
}
- if (de)
+
+ if (de) {
+printk("remove_proc_entry: parent nlink=%d, file nlink=%d\n",
+parent->nlink, de->nlink);
proc_unregister(parent, de->low_ino);
- kfree(de);
+ de->nlink = 0;
+ de->deleted = 1;
+ if (!de->count)
+ free_proc_entry(de);
+ else {
+ printk("remove_proc_entry: %s/%s busy, count=%d\n",
+ parent->name, de->name, de->count);
+ }
+ }
+out:
+ return;
}
--- linux-2.1.57/fs/proc/array.c.old Fri Sep 26 08:10:51 1997
+++ linux-2.1.57/fs/proc/array.c Wed Oct 1 10:47:01 1997
@@ -348,6 +348,12 @@

if (!p || !p->mm || ptr >= TASK_SIZE)
return 0;
+ /* Check for NULL pgd .. shouldn't happen! */
+ if (!p->mm->pgd) {
+ printk("get_phys_addr: pid %d has NULL pgd!\n", p->pid);
+ return 0;
+ }
+
page_dir = pgd_offset(p->mm,ptr);
if (pgd_none(*page_dir))
return 0;
@@ -917,24 +923,34 @@
#define MAPS_LINE_MAX MAPS_LINE_MAX8


-static long read_maps (int pid, struct file * file,
- char * buf, unsigned long count)
+static long read_maps (int pid, struct file * file, char * buf,
+ unsigned long count)
{
- struct task_struct *p = find_task_by_pid(pid);
- char * destptr;
+ struct task_struct *p;
+ struct vm_area_struct * map, * next;
+ char * destptr = buf, * buffer;
loff_t lineno;
- int column;
- struct vm_area_struct * map;
- int i;
- char * buffer;
+ int column, i, volatile_task;
+ long retval;

+ /*
+ * We might sleep getting the page, so get it first.
+ */
+ retval = -ENOMEM;
+ buffer = (char*)__get_free_page(GFP_KERNEL);
+ if (!buffer)
+ goto out;
+
+ retval = -EINVAL;
+ p = find_task_by_pid(pid);
if (!p)
- return -EINVAL;
+ goto freepage_out;

if (!p->mm || p->mm == &init_mm || count == 0)
- return 0;
+ goto getlen_out;

- buffer = (char*)__get_free_page(GFP_KERNEL);
+ /* Check whether the mmaps could change if we sleep */
+ volatile_task = (p != current || p->mm->count > 1);

/* decode f_pos */
lineno = file->f_pos >> MAPS_LINE_SHIFT;
@@ -944,9 +960,7 @@
for (map = p->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++)
continue;

- destptr = buf;
-
- for ( ; map ; ) {
+ for ( ; map ; map = next ) {
/* produce the next line */
char *line;
char str[5], *cp = str;
@@ -957,6 +971,10 @@
MAPS_LINE_MAX4 : MAPS_LINE_MAX8;
int len;

+ /*
+ * Get the next vma now (but it won't be used if we sleep).
+ */
+ next = map->vm_next;
flags = map->vm_flags;

*cp++ = flags & VM_READ ? 'r' : '-';
@@ -993,20 +1011,19 @@
if (column >= len) {
column = 0; /* continue with next line at column 0 */
lineno++;
- map = map->vm_next;
- continue;
+ continue; /* we haven't slept */
}

i = len-column;
if (i > count)
i = count;
- copy_to_user(destptr, line+column, i);
- destptr += i; count -= i;
- column += i;
+ copy_to_user(destptr, line+column, i); /* may have slept */
+ destptr += i;
+ count -= i;
+ column += i;
if (column >= len) {
column = 0; /* next time: next line at column 0 */
lineno++;
- map = map->vm_next;
}

/* done? */
@@ -1016,15 +1033,20 @@
/* By writing to user space, we might have slept.
* Stop the loop, to avoid a race condition.
*/
- if (p != current)
+ if (volatile_task)
break;
}

/* encode f_pos */
file->f_pos = (lineno << MAPS_LINE_SHIFT) + column;

+getlen_out:
+ retval = destptr - buf;
+
+freepage_out:
free_page((unsigned long)buffer);
- return destptr-buf;
+out:
+ return retval;
}

#ifdef CONFIG_MODULES
--- linux-2.1.57/fs/proc/base.c.old Sat Jul 19 08:14:48 1997
+++ linux-2.1.57/fs/proc/base.c Thu Oct 2 23:43:23 1997
@@ -50,13 +50,17 @@
NULL /* permission */
};

-static void proc_pid_fill_inode(struct inode * inode)
+/*
+ * The fill argument is non-zero when the inode is being filled ...
+ * we don't need to do anything when it's being deleted.
+ */
+static void proc_pid_fill_inode(struct inode * inode, int fill)
{
struct task_struct *p;
int pid = inode->i_ino >> 16;
int ino = inode->i_ino & 0xffff;

- if ((p = find_task_by_pid(pid)) != NULL) {
+ if (fill && (p = find_task_by_pid(pid)) != NULL) {
if (p->dumpable || ino == PROC_PID_INO) {
inode->i_uid = p->euid;
inode->i_gid = p->gid;
--- linux-2.1.57/fs/binfmt_misc.c.old Thu Sep 4 12:33:50 1997
+++ linux-2.1.57/fs/binfmt_misc.c Fri Oct 3 08:40:48 1997
@@ -281,21 +281,19 @@
const char *sp;
char del, *dp;
struct binfmt_entry *e;
- int memsize, cnt = count - 1, err = 0;
+ int memsize, cnt = count - 1, err;

- MOD_INC_USE_COUNT;
/* some sanity checks */
- if ((count < 11) || (count > 256)) {
- err = -EINVAL;
+ err = -EINVAL;
+ if ((count < 11) || (count > 256))
goto _err;
- }

+ err = -ENOMEM;
memsize = sizeof(struct binfmt_entry) + count;
- if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER))) {
- err = -ENOMEM;
+ 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);
@@ -327,12 +325,8 @@
/* 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;
- }
+ !(e->proc_name) || !(e->interpreter) || entry_proc_setup(e))
+ goto free_err;

write_lock(&entries_lock);
e->next = entries;
@@ -341,8 +335,11 @@

err = count;
_err:
- MOD_DEC_USE_COUNT;
return err;
+free_err:
+ kfree(e);
+ err = -EINVAL;
+ goto _err;
}

/*
@@ -357,7 +354,6 @@
char *dp;
int elen, i, err;

- MOD_INC_USE_COUNT;
#ifndef VERBOSE_STATUS
if (data) {
if (!(e = get_entry((int) data))) {
@@ -415,7 +411,6 @@
err = elen;

_err:
- MOD_DEC_USE_COUNT;
return err;
}

@@ -429,7 +424,6 @@
struct binfmt_entry *e;
int res = count;

- MOD_INC_USE_COUNT;
if (buffer[count-1] == '\n')
count--;
if ((count == 1) && !(buffer[0] & ~('0' | '1'))) {
@@ -449,7 +443,6 @@
} else {
res = -EINVAL;
}
- MOD_DEC_USE_COUNT;
return res;
}

@@ -477,29 +470,57 @@
return 0;
}

+#ifdef MODULE
+/*
+ * This is called as the fill_inode function when an inode
+ * is going into (fill = 1) or out of service (fill = 0).
+ * We use it here to manage the module use counts.
+ *
+ * Note: only the top-level directory needs to do this; if
+ * a lower level is referenced, the parent will be as well.
+ */
+static void bm_modcount(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+#endif

__initfunc(int init_misc_binfmt(void))
{
struct proc_dir_entry *status = NULL, *reg;
+ int error = -ENOMEM;

- 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;
- }
+ bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR, NULL);
+ if (!bm_dir)
+ goto out;
+#ifdef MODULE
+ bm_dir->fill_inode = bm_modcount;
+#endif
+
+ 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;

- return register_binfmt(&misc_format);
+ error = register_binfmt(&misc_format);
+out:
+ return error;
+
+cleanup_status:
+ remove_proc_entry("status", bm_dir);
+cleanup_bm:
+ remove_proc_entry("sys/fs/binfmt_misc", NULL);
+ goto out;
}

#ifdef MODULE

--------------2DF424E29296A81A5488477D--