linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: PROPOSAL: dot-proc interface [was: /proc stuff]
@ 2001-11-04 23:06 Craig Thrall
  2001-11-04 23:39 ` Jakob Østergaard
  0 siblings, 1 reply; 118+ messages in thread
From: Craig Thrall @ 2001-11-04 23:06 UTC (permalink / raw)
  To: 'jakob@unthought.net'; +Cc: 'linux-kernel@vger.kernel.org'

> Problem:  Could it be made simpler to parse from scripting languages,
> without making it less elegant to parse in plain C ?

Yes.  At one point, somebody suggested XML.  Now, as much as I hate the fact
that people somehow equate high-tech with tags, I think whomever originally
suggested it might be on to something.  :)

Fact is, just about EVERY language out there has some sort of utility to
parse XML.  There's expat for C, Perl and Python have libs, etc.  We could
even write a proc DTD that could specify the valid data types.

There are two problems:

1. Performance - it's slower to go through a library that outputs XML than
do a printf("%d", pid) or the like.

2. Space - based on a little experience using XML as a transport, the space
used by the tags adds up.

3. Work - writing a good package to do this, and rewriting bits of the
kernel to use it.  I'll volunteer my time.

Just a thought,

Craig

^ permalink raw reply	[flat|nested] 118+ messages in thread
[parent not found: <Pine.LNX.4.33.0111041141100.14150-100000@penguin.transmeta.com>]
* [PATCH] 2.5 PROPOSAL: Replacement for current /proc of shit.
@ 2001-11-01 10:32 Rusty Russell
  2001-11-04 12:30 ` Tim Jansen
  0 siblings, 1 reply; 118+ messages in thread
From: Rusty Russell @ 2001-11-01 10:32 UTC (permalink / raw)
  To: linux-kernel

[Sorry: been wanting to use that phrase here for the longest time]

	This proof-of-concept implementation is pretty poor but the
principle (and interface) is simple:

	int my_foo;

	static int __init initfn(void)
	{
		return proc("net", "foo", my_foo, int, 0644);
	}

	static void __exit exitfn(void)
	{
		unproc("net", "foo");
	}

No kernel-formatted tables: use a directory.  (eg. kernel symbols
become a directory of symbol names, each containing the symbol value).

For cases when you don't want to take the overhead of creating a new
proc entry (eg. tcp socket creation), you can create directories on
demand when a user reads them using:

	proc_dir("net", "subdir", dirfunc, NULL);
	unproc_dir("net", "subdir");

Note that with kbuild 2.5, you can do something like:

	proc(KBUILD_OBJECT, "foo", my_foo, int, 0644);

And with my previous parameter patch:
	PARAM(foo, int, 0444);

declares a boot time parameter "KBUILD_OBJECT.foo=", or a module
parameter "foo=", and places it as readable in
/proc/KBUILD_OBJECT/foo.

I believe that rewriting /proc (and /proc/sys should simply die) is a
better solution than extending the interface, or avoiding it
altogether by using a new filesystem.

Of course, I don't care if it's *NOT* under /proc...
Rusty.
--
Premature optmztion is rt of all evl. --DK

diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/current-dontdiff --minimal linux-2.4.13-uml/include/linux/simpleproc.h working-2.4.13-uml-proc/include/linux/simpleproc.h
--- linux-2.4.13-uml/include/linux/simpleproc.h	Thu Jan  1 10:00:00 1970
+++ working-2.4.13-uml-proc/include/linux/simpleproc.h	Thu Nov  1 20:17:42 2001
@@ -0,0 +1,206 @@
+/* Dynamic proc filesystem that doesn't suck.  (C) 2001 Rusty Russell. */
+#ifndef _LINUX_SIMPLE_PROC_H
+#define _LINUX_SIMPLE_PROC_H
+#include <linux/config.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/stat.h>
+
+/* Commit the contents of this (NUL-terminated) buffer if possible.
+   -errno indicates error. */
+typedef int (proc_commitfn_t)(const char *dirname,
+			      const char *filename,
+			      const char *buffer,
+			      unsigned int size,
+			      void *arg);
+/* Fetch the contents into buffer: return size used (or needed), or
+   -errno. */
+typedef int (proc_fetchfn_t)(const char *dirname,
+			     const char *filename,
+			     char *buffer,
+			     unsigned int size,
+			     void *arg);
+
+/* If we're a dynamic directory, this routine gets dir contents:
+   returns size used (or needed), or -errno. */
+struct proc_dircontents;
+typedef int (proc_dirfn_t)(const char *dirname,
+			   const char *filename,
+			   struct proc_dircontents *buffer,
+			   unsigned int maxlen,
+			   void *arg);
+
+/* Register a proc entry of the given type. */
+#define proc(dir, fname, var, type, perms)				 \
+	__proc(dir, fname, S_IFREG|(perms),				 \
+	       __new_proc(&var,						 \
+			  ((perms)&S_IRUGO) ? proc_fetch_##type : NULL,	 \
+			  ((perms)&S_IWUGO) ? proc_commit_##type : NULL, \
+			  NULL))
+
+/* Register a proc entry protected by a spinlock. */
+#define proc_spinlock(dir, fname, var, type, lock, p)			   \
+	__proc(dir, fname, S_IFREG|(p),					   \
+	       __new_proc_lock(&var, lock,				   \
+			       ((p)&S_IRUGO) ? proc_fetch_##type : NULL,   \
+			       ((p)&S_IWUGO) ? proc_commit_##type : NULL))
+
+/* Register a proc entry protected by a semaphore. */
+#define proc_sem(dir, fname, var, type, sem, p)				  \
+	__proc(dir, fname, S_IFREG|(p),					  \
+	       __new_proc_sem(&var, sem,				  \
+			      ((p)&S_IRUGO) ? proc_fetch_##type : NULL,	  \
+			      ((p)&S_IWUGO) ? proc_commit_##type : NULL))
+
+/* These exist, believe me */
+struct semaphore;
+struct proc_data;
+
+#ifdef CONFIG_SIMPLE_PROC_FS
+/* Low level functions */
+int __proc(const char *dirname, const char *fname, int mode,
+	   struct proc_data *pdata);
+struct proc_data *__new_proc(void *arg, proc_fetchfn_t *, proc_commitfn_t *,
+			     proc_dirfn_t *);
+struct proc_data *__new_proc_lock(void *arg, spinlock_t *lock,
+				  proc_fetchfn_t *, proc_commitfn_t *);
+struct proc_data *__new_proc_sem(void *arg, struct semaphore *sem,
+				 proc_fetchfn_t *, proc_commitfn_t *);
+
+/* Register a whole dynamic directory */
+static inline int proc_dir(const char *dir, const char *dirname,
+			   proc_dirfn_t *dirfunc, void *arg)
+{
+	return __proc(dir, dirname, S_IFDIR|0555, 
+		      __new_proc(arg, NULL, NULL, dirfunc));
+}
+
+/* Release a dynamic proc directory */
+void unproc_dir(const char *dir, const char *fname);
+
+/* Release a proc entry */
+void unproc(const char *dir, const char *fname);
+
+#else
+static inline int proc_dir(const char *dir, const char *dirname,
+			   proc_dirfn_t *dirfunc, void *arg)
+{
+	return 0;
+}
+
+static inline void unproc(const char *dir, const char *fname)
+{
+}
+
+static inline void unproc_dir(const char *dir, const char *fname)
+{
+}
+
+static inline int __proc(const char *dirname, const char *fname, int mode,
+			 struct proc_data *pdata)
+{
+	return 0;
+}
+
+struct proc_data *__new_proc(void *arg,
+			     proc_fetchfn_t *fetch,
+			     proc_commitfn_t *commit,
+			     proc_dirfn_t *dir)
+{
+	return (struct proc_data *)-1;
+}
+struct proc_data *__new_proc_lock(void *arg, spinlock_t *lock,
+				  proc_fetchfn_t *fetch,
+				  proc_commitfn_t *commit)
+{
+	return (struct proc_data *)-1;
+}
+struct proc_data *__new_proc_sem(void *arg, struct semaphore *sem,
+				 proc_fetchfn_t *fetch,
+				 proc_commitfn_t *commit)
+{
+	return (struct proc_data *)-1;
+}
+#endif /*CONFIG_PROC_FS*/
+
+/* Helper parsing routines.  You can write your own, too. */
+proc_fetchfn_t proc_fetch_short;
+proc_fetchfn_t proc_fetch_ushort;
+proc_fetchfn_t proc_fetch_int;
+proc_fetchfn_t proc_fetch_uint;
+proc_fetchfn_t proc_fetch_long;
+proc_fetchfn_t proc_fetch_ulong;
+proc_fetchfn_t proc_fetch_bool;
+
+proc_commitfn_t proc_commit_short;
+proc_commitfn_t proc_commit_ushort;
+proc_commitfn_t proc_commit_int;
+proc_commitfn_t proc_commit_uint;
+proc_commitfn_t proc_commit_long;
+proc_commitfn_t proc_commit_ulong;
+proc_commitfn_t proc_commit_bool;
+
+/* Filled in by dir functions */
+struct proc_dircontents
+{
+	/* Mode of file.  0 terminates list. */
+	int mode;
+
+	/* Fetch, commit and dir functions for entry. */
+	proc_fetchfn_t *fetch;
+	proc_commitfn_t *commit;
+	proc_dirfn_t *dir;
+
+	/* Arg */
+	void *arg;
+
+	/* Name is nul-terminated, and padded to alignof this struct */
+	char name[0];
+};
+
+/* Helper to add another dircontents to the list, return updated "used" */
+static inline unsigned int proc_add_dircontents(struct proc_dircontents *pd,
+						unsigned int used,
+						unsigned int maxlen,
+						int mode,
+						proc_fetchfn_t *fetch,
+						proc_commitfn_t *commit,
+						proc_dirfn_t *dir,
+						void *arg,
+						const char *name)
+{
+	unsigned int thislen;
+
+	thislen = sizeof(*pd) + strlen(name) + 1;
+	thislen = (thislen + __alignof__(*pd) - 1) & ~(__alignof__(*pd) - 1);
+	if (used + thislen <= maxlen) {
+		pd = (void *)pd + used;
+		pd->mode = mode;
+		pd->fetch = fetch;
+		pd->commit = commit;
+		pd->dir = dir;
+		pd->arg = arg;
+		strcpy(pd->name, name);
+	}
+	return used + thislen;
+}
+
+static inline unsigned int proc_end_dircontents(struct proc_dircontents *pd,
+						unsigned int used,
+						unsigned int maxlen)
+{
+	return proc_add_dircontents(pd, used, maxlen, 0,
+				    NULL, NULL, NULL, NULL, "");
+}
+
+/* Internal use */
+struct proc_data
+{
+	/* User-defined argument for routines */
+	void *arg;
+
+	proc_dirfn_t *dir;
+	proc_commitfn_t *commit;
+	proc_fetchfn_t *fetch;
+};
+#endif /* _LINUX_SIMPLE_PROC_H */
diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/current-dontdiff --minimal linux-2.4.13-uml/fs/Config.in working-2.4.13-uml-proc/fs/Config.in
--- linux-2.4.13-uml/fs/Config.in	Thu Oct 25 11:29:49 2001
+++ working-2.4.13-uml-proc/fs/Config.in	Tue Oct 30 12:47:11 2001
@@ -50,7 +50,10 @@
 
 tristate 'OS/2 HPFS file system support' CONFIG_HPFS_FS
 
-bool '/proc file system support' CONFIG_PROC_FS
+bool 'Simple /proc file system support (EXPERIMENTAL)' CONFIG_SIMPLE_PROC_FS
+if [ "$CONFIG_SIMPLE_PROC_FS" != y ]; then
+   bool '/proc file system support' CONFIG_PROC_FS
+fi
 
 dep_bool '/dev file system support (EXPERIMENTAL)' CONFIG_DEVFS_FS $CONFIG_EXPERIMENTAL
 dep_bool '  Automatically mount at boot' CONFIG_DEVFS_MOUNT $CONFIG_DEVFS_FS
diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/current-dontdiff --minimal linux-2.4.13-uml/fs/Makefile working-2.4.13-uml-proc/fs/Makefile
--- linux-2.4.13-uml/fs/Makefile	Thu Oct 25 11:29:49 2001
+++ working-2.4.13-uml-proc/fs/Makefile	Tue Oct 30 12:47:11 2001
@@ -23,6 +23,7 @@
 endif
 
 subdir-$(CONFIG_PROC_FS)	+= proc
+subdir-$(CONFIG_SIMPLE_PROC_FS)	+= simpleproc
 subdir-y			+= partitions
 
 # Do not add any filesystems before this line
diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/current-dontdiff --minimal linux-2.4.13-uml/fs/proc/simple_proc.c working-2.4.13-uml-proc/fs/proc/simple_proc.c
--- linux-2.4.13-uml/fs/proc/simple_proc.c	Thu Jan  1 10:00:00 1970
+++ working-2.4.13-uml-proc/fs/proc/simple_proc.c	Tue Oct 30 12:47:11 2001
@@ -0,0 +1,517 @@
+/* Those of you who read this, give quiet thanks that you did not
+   suffer the endless frustration of dealing with the old /proc
+   interface.
+
+   Copyright (C) 2001 Rusty Russell.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#include <linux/proc.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+
+/* Simplistic approach: semaphore protects all proc accesses. */
+static DECLARE_MUTEX(simple_proc_sem);
+
+/* FIXME: Use reference counts and "dead" marker to return -ENOENT if
+   unregistered while open -RR */
+struct proc_data
+{
+	void *arg;
+	int (*get)(void *, char *, int);
+	int (*set)(void *, const char *);
+
+	/* FIXME: Belongs in struct file --RR */
+	int readlen, maxreadlen, writelen, maxwritelen;
+	char *readdata, *writedata;
+};
+
+static int fill_buffer(char **buffer,
+			int *maxlen,
+			struct proc_data *pdata)
+{
+	int len;
+
+	for (;;) {
+		len = pdata->get(pdata->arg, *buffer, *maxlen);
+		/* Need more room? */
+		if (len > *maxlen) {
+			/* We need some restriction here, to avoid
+			   DoS.  fs/proc/generic.c wants this, but we
+			   should make one or two pages eventually. */
+			if (len > PAGE_SIZE - 1024)
+				BUG();
+			kfree(*buffer);
+			*buffer = kmalloc(len, GFP_KERNEL);
+			if (!*buffer) return -ENOMEM;
+			*maxlen = len;
+		} else
+			return len;
+	}
+}
+
+/* FIXME: Get the struct file, and we can use ->private_data to store
+   this per file descriptor, rather than per file --RR */
+static int simple_read(char *page, char **start, off_t off, int count, 
+		       int *eof, void *data)
+{
+	struct proc_data *pdata = data;
+	int ret;
+
+	/* Start of read?  Get fresh buffer */
+	if (off == 0) {
+		int readlen, maxreadlen;
+		char *buffer;
+
+		maxreadlen = pdata->maxreadlen;
+		buffer = kmalloc(maxreadlen, GFP_KERNEL);
+		if (!buffer) {
+			*eof = 1;
+			return -ENOMEM;
+		}
+		readlen = fill_buffer(&buffer, &maxreadlen, pdata);
+		if (readlen < 0) {
+			*eof = 1;
+			kfree(buffer);
+			return readlen;
+		}
+
+		/* Substitute buffer */
+		if (down_interruptible(&simple_proc_sem) != 0) {
+			*eof = 1;
+			kfree(buffer);
+			return -EINTR;
+		}
+		kfree(pdata->readdata);
+		pdata->maxreadlen = maxreadlen;
+		pdata->readlen = readlen;
+		pdata->readdata = buffer;
+		up(&simple_proc_sem);
+	}
+
+	/* Serve from buffer */
+	if (down_interruptible(&simple_proc_sem) != 0) {
+		*eof = 1;
+		return -EINTR;
+	}
+
+	if (off <= pdata->readlen) {
+		ret = pdata->readlen - off;
+		memcpy(page + off, pdata->readdata + off, ret);
+	} else {
+		*eof = 1;
+		ret = 0;
+	}
+	up(&simple_proc_sem);
+
+	return ret;
+}
+
+/* FIXME: Don't share the write buffer: use file->private_data */
+static int simple_write(struct file *file,
+			const char *userbuffer,
+			unsigned long count, 
+			void *data)
+{
+	struct proc_data *pdata = data;
+
+	/* FIXME: commit the write(s) on close or seek.  We don't have
+	   that control under the current proc system, so simply
+	   terminate on \n. --RR */
+	if (file->f_pos + count > pdata->maxwritelen) {
+		char *newbuffer;
+		int newmax = file->f_pos + count;
+
+		/* As in read, we need some limit, and this is from
+                   fs/proc/generic.c */
+		if (newmax > PAGE_SIZE - 1024)
+			return -ENOSPC;
+
+		newbuffer = kmalloc(newmax, GFP_KERNEL);
+		if (!newbuffer)
+			return -ENOMEM;
+
+		/* Substitute buffer */
+		if (down_interruptible(&simple_proc_sem) != 0) {
+			kfree(newbuffer);
+			return -EINTR;
+		}
+		memcpy(newbuffer, pdata->writedata, pdata->writelen);
+		kfree(pdata->writedata);
+		pdata->maxwritelen = newmax;
+		pdata->writedata = newbuffer;
+		up(&simple_proc_sem);
+	}
+
+	/* Copy into buffer */
+	if (down_interruptible(&simple_proc_sem) != 0)
+		return -EINTR;
+
+	if (copy_from_user(pdata->writedata+file->f_pos, userbuffer, count)
+	    != 0) {
+		up(&simple_proc_sem);
+		return -EFAULT;
+	}
+
+	file->f_pos += count;
+
+	/* If there is now a '\n' at the end of the buffer, commit */
+	if (file->f_pos > 0 && pdata->writedata[file->f_pos-1] == '\n') {
+		int set;
+		pdata->writedata[file->f_pos-1] = '\0';
+		set = pdata->set(pdata->arg, pdata->writedata);
+
+		if (set < 0) {
+			up(&simple_proc_sem);
+			return set;
+		}
+	}
+	up(&simple_proc_sem);
+	return count;
+}
+
+/* This implementation serves only as a demonstration. --RR */
+static int do_register(const char *dir,
+		       const char *fname,
+		       int perms,
+		       struct proc_data *pdata)
+{
+	struct proc_dir_entry *entry;
+	char fullpath[strlen(dir) + 1 + strlen(fname) + 1];
+
+	sprintf(fullpath, "%s/%s", dir, fname);
+	entry = create_proc_entry(fullpath, perms, NULL);
+	if (!entry) return -EINVAL; /* -ERANDOM */
+
+	/* Populate data */
+	entry->data = pdata;
+
+	/* Set up read and write callbacks */
+	if (pdata->set) entry->read_proc = &simple_read;
+	if (pdata->get) entry->write_proc = &simple_write;
+	return 0;
+}
+
+int __register_proc(const char *dir,
+		    const char *fname,
+		    void *arg,
+		    unsigned int perms,
+		    int (*get)(void *arg, char *, int),
+		    int (*set)(void *arg, const char *))
+{
+	struct proc_data *pdata;
+	int ret;
+
+	pdata = kmalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+	
+	pdata->arg = arg;
+	pdata->get = get;
+	pdata->set = set;
+	pdata->writelen = pdata->readlen = 0;
+	pdata->readdata = pdata->writedata = NULL;
+
+	ret = do_register(dir, fname, perms, pdata);
+	if (ret < 0)
+		kfree(pdata);
+	return ret;
+}
+
+/* Wrapper for user's real proc functions */
+struct pdata_wrapper
+{
+	struct proc_data pdata;
+	int (*get)(void *, char *, int);
+	int (*set)(void *, const char *);
+	void *lock;
+	void *userarg;
+};
+
+static struct pdata_wrapper *
+new_pdata_wrapper(void *arg,
+		  int (*userget)(void *, char *, int),
+		  int (*userset)(void *, const char *),
+		  int (*wrapperget)(void *, char *, int),
+		  int (*wrapperset)(void *, const char *),
+		  void *lock)
+{
+	struct pdata_wrapper *pwrap;
+
+	pwrap = kmalloc(sizeof(*pwrap), GFP_KERNEL);
+	if (pwrap) {
+		pwrap->pdata.arg = pwrap;
+		pwrap->pdata.writelen = pwrap->pdata.readlen = 0;
+		pwrap->pdata.readdata = pwrap->pdata.writedata = NULL;
+		pwrap->pdata.get = wrapperget;
+		pwrap->pdata.set = wrapperset;
+		pwrap->lock = lock;
+		pwrap->userarg = arg;
+		pwrap->get = userget;
+		pwrap->set = userset;
+	}
+	return pwrap;
+}
+
+static int do_register_wrap(const char *dir,
+			    const char *fname,
+			    int perms,
+			    void *arg,
+			    int (*userget)(void *, char *, int),
+			    int (*userset)(void *, const char *),
+			    int (*wrapperget)(void *, char *, int),
+			    int (*wrapperset)(void *, const char *),
+			    void *lock)
+{
+	struct pdata_wrapper *pwrap;
+	int ret;
+
+	pwrap = new_pdata_wrapper(arg, userget, userset, wrapperget,
+				  wrapperset, lock);
+	if (!pwrap)
+		return -ENOMEM;
+	ret = do_register(dir, fname, perms, &pwrap->pdata);
+	if (ret < 0)
+		kfree(pwrap);
+	return ret;
+}
+
+static int spinlock_get(void *arg, char *buffer, int size)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	spin_lock_irq(pwrap->lock);
+	ret = pwrap->get(pwrap->userarg, buffer, size);
+	spin_unlock_irq(pwrap->lock);
+
+	return ret;
+}
+
+static int spinlock_set(void *arg, const char *buffer)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	spin_lock_irq(pwrap->lock);
+	ret = pwrap->set(pwrap->userarg, buffer);
+	spin_unlock_irq(pwrap->lock);
+
+	return ret;
+}
+
+int __register_proc_spinlock(const char *dir,
+			     const char *fname,
+			     void *arg,
+			     unsigned int perms,
+			     spinlock_t *lock,
+			     int (*get)(void *arg, char *, int),
+			     int (*set)(void *arg, const char *))
+{
+	return do_register_wrap(dir, fname, perms, arg, get, set,
+				spinlock_get, spinlock_set, lock);
+}
+
+static int rwlock_get(void *arg, char *buffer, int size)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	read_lock_irq(pwrap->lock);
+	ret = pwrap->get(pwrap->userarg, buffer, size);
+	read_unlock_irq(pwrap->lock);
+
+	return ret;
+}
+
+static int rwlock_set(void *arg, const char *buffer)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	write_lock_irq(pwrap->lock);
+	ret = pwrap->set(pwrap->userarg, buffer);
+	write_unlock_irq(pwrap->lock);
+
+	return ret;
+}
+
+int __register_proc_rwlock(const char *dir,
+			   const char *fname,
+			   void *arg,
+			   unsigned int perms,
+			   rwlock_t *lock,
+			   int (*get)(void *arg, char *, int),
+			   int (*set)(void *arg, const char *))
+{
+	return do_register_wrap(dir, fname, perms, arg, get, set,
+				rwlock_get, rwlock_set, lock);
+}
+
+static int semaphore_get(void *arg, char *buffer, int size)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	if (down_interruptible(pwrap->lock) != 0)
+		return -EINTR;
+	ret = pwrap->get(pwrap->userarg, buffer, size);
+	up(pwrap->lock);
+
+	return ret;
+}
+
+static int semaphore_set(void *arg, const char *buffer)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	if (down_interruptible(pwrap->lock) != 0)
+		return -EINTR;
+	ret = pwrap->set(pwrap->userarg, buffer);
+	up(pwrap->lock);
+
+	return ret;
+}
+
+int __register_proc_semaphore(const char *dir,
+			      const char *fname,
+			      void *arg,
+			      unsigned int perms,
+			      struct semaphore *lock,
+			      int (*get)(void *arg, char *, int),
+			      int (*set)(void *arg, const char *))
+{
+	return do_register_wrap(dir, fname, perms, arg, get, set,
+				semaphore_get, semaphore_set, lock);
+}
+	
+static int rwsemaphore_get(void *arg, char *buffer, int size)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	down_read(pwrap->lock);
+	ret = pwrap->get(pwrap->userarg, buffer, size);
+	up_read(pwrap->lock);
+
+	return ret;
+}
+
+static int rwsemaphore_set(void *arg, const char *buffer)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	down_write(pwrap->lock);
+	ret = pwrap->set(pwrap->userarg, buffer);
+	up_write(pwrap->lock);
+
+	return ret;
+}
+
+int __register_proc_rwsemaphore(const char *dir,
+				const char *fname,
+				void *arg,
+				unsigned int perms,
+				struct rw_semaphore *lock,
+				int (*get)(void *arg, char *, int),
+				int (*set)(void *arg, const char *))
+{
+	return do_register_wrap(dir, fname, perms, arg, get, set,
+				rwsemaphore_get, rwsemaphore_set, lock);
+}
+
+int __proc_read_short(void *shortp, char *outbuf, int len)
+{
+	return snprintf(outbuf, len, "%hi", *(short *)shortp);
+}
+
+int __proc_write_short(void *shortp, const char *inbuf)
+{
+	if (sscanf(inbuf, "%hi", (short *)shortp) != 1) return -EINVAL;
+	return 0;
+}
+
+int __proc_read_ushort(void *ushortp, char *outbuf, int len)
+{
+	return snprintf(outbuf, len, "%hu", *(unsigned short *)ushortp);
+}
+
+int __proc_write_ushort(void *ushortp, const char *inbuf)
+{
+	if (sscanf(inbuf, "%hu", (unsigned short *)ushortp) != 1)
+		return -EINVAL;
+	return 0;
+}
+
+int __proc_read_int(void *intp, char *outbuf, int len)
+{
+	return snprintf(outbuf, len, "%i", *(int *)intp);
+}
+
+int __proc_write_int(void *intp, const char *inbuf)
+{
+	if (sscanf(inbuf, "%i", (int *)intp) != 1) return -EINVAL;
+	return 0;
+}
+
+int __proc_read_uint(void *uintp, char *outbuf, int len)
+{
+	return snprintf(outbuf, len, "%u", *(unsigned int *)uintp);
+}
+
+int __proc_write_uint(void *uintp, const char *inbuf)
+{
+	if (sscanf(inbuf, "%u", (unsigned int *)uintp) != 1) return -EINVAL;
+	return 0;
+}
+
+int __proc_read_long(void *longp, char *outbuf, int len)
+{
+	return snprintf(outbuf, len, "%li", *(long *)longp);
+}
+
+int __proc_write_long(void *longp, const char *inbuf)
+{
+	if (sscanf(inbuf, "%li", (long *)longp) != 1) return -EINVAL;
+	return 0;
+}
+
+int __proc_read_ulong(void *ulongp, char *outbuf, int len)
+{
+	return snprintf(outbuf, len, "%lu", *(long *)ulongp);
+}
+
+int __proc_write_ulong(void *ulongp, const char *inbuf)
+{
+	if (sscanf(inbuf, "%lu", (unsigned long *)ulongp) != 1) return -EINVAL;
+	return 0;
+}
+
+int __proc_read_bool(void *boolp, char *outbuf, int len)
+{
+	if (*(int *)boolp) return snprintf(outbuf, len, "y");
+	else return snprintf(outbuf, len, "n");
+}
+
+int __proc_write_bool(void *boolp, const char *inbuf)
+{
+	if (inbuf[0] == 'y' || inbuf[0] == 'Y')
+		*(int *)boolp = 1;
+	else if (inbuf[0] == 'n' || inbuf[0] == 'N')
+		*(int *)boolp = 0;
+	else return __proc_write_int(boolp, inbuf);
+	return 0;
+}
diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/current-dontdiff --minimal linux-2.4.13-uml/fs/simpleproc/Makefile working-2.4.13-uml-proc/fs/simpleproc/Makefile
--- linux-2.4.13-uml/fs/simpleproc/Makefile	Thu Jan  1 10:00:00 1970
+++ working-2.4.13-uml-proc/fs/simpleproc/Makefile	Tue Oct 30 12:47:11 2001
@@ -0,0 +1,14 @@
+#
+# Makefile for the Linux proc filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := simpleproc.o
+
+obj-y    := inode.o helper.o
+
+include $(TOPDIR)/Rules.make
diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/current-dontdiff --minimal linux-2.4.13-uml/fs/simpleproc/helper.c working-2.4.13-uml-proc/fs/simpleproc/helper.c
--- linux-2.4.13-uml/fs/simpleproc/helper.c	Thu Jan  1 10:00:00 1970
+++ working-2.4.13-uml-proc/fs/simpleproc/helper.c	Thu Nov  1 20:58:13 2001
@@ -0,0 +1,277 @@
+/* Those of you who read this, give quiet thanks that you did not
+   suffer the endless frustration of dealing with the old /proc
+   interface.
+
+   Copyright (C) 2001 Rusty Russell.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#include <linux/simpleproc.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/dcache.h>
+#include <linux/init.h>
+
+/* Wrapper for user's real proc functions */
+struct pdata_wrapper
+{
+	struct proc_data pdata;
+	proc_fetchfn_t *fetch;
+	proc_commitfn_t *commit;
+	void *lock;
+	void *userarg;
+};
+
+static struct proc_data *new_wrapper(proc_fetchfn_t *userfetch,
+				     proc_commitfn_t *usercommit,
+				     void *userarg,
+				     proc_fetchfn_t *wrapfetch,
+				     proc_commitfn_t *wrapcommit,
+				     void *lock)
+{
+	struct pdata_wrapper *pwrap;
+
+	pwrap = kmalloc(sizeof(*pwrap), GFP_KERNEL);
+	if (pwrap) {
+		pwrap->pdata.arg = pwrap;
+		pwrap->pdata.fetch = wrapfetch;
+		pwrap->pdata.commit = wrapcommit;
+		pwrap->pdata.dir = NULL;
+		pwrap->fetch = userfetch;
+		pwrap->commit = usercommit;
+		pwrap->lock = lock;
+		pwrap->userarg = userarg;
+	}
+	return &pwrap->pdata;
+}
+
+static int lock_fetch(const char *dirname, const char *fname,
+		      char *buffer, unsigned int size, void *arg)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	spin_lock_irq(pwrap->lock);
+	ret = pwrap->fetch(dirname, fname, pwrap->userarg, size, buffer);
+	spin_unlock_irq(pwrap->lock);
+
+	return ret;
+}
+
+static int lock_commit(const char *dirname, const char *fname,
+		       const char *buffer, unsigned int size, void *arg)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	spin_lock_irq(pwrap->lock);
+	ret = pwrap->commit(dirname, fname, buffer, size, pwrap->userarg);
+	spin_unlock_irq(pwrap->lock);
+
+	return ret;
+}
+
+struct proc_data *__new_proc_lock(void *arg, spinlock_t *lock,
+				  proc_fetchfn_t *fetch,
+				  proc_commitfn_t *commit)
+{
+	return new_wrapper(fetch, commit, arg, lock_fetch, lock_commit, lock);
+}
+
+static int sem_fetch(const char *dirname, const char *fname,
+		     char *buffer, unsigned int size, void *arg)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	if (down_interruptible(pwrap->lock) != 0)
+		return -EINTR;
+	ret = pwrap->fetch(dirname, fname, pwrap->userarg, size, buffer);
+	up(pwrap->lock);
+
+	return ret;
+}
+
+static int sem_commit(const char *dirname, const char *fname,
+		      const char *buffer, unsigned int size, void *arg)
+{
+	struct pdata_wrapper *pwrap = arg;
+	int ret;
+
+	if (down_interruptible(pwrap->lock) != 0)
+		return -EINTR;
+	ret = pwrap->commit(dirname, fname, buffer, size, pwrap->userarg);
+	up(pwrap->lock);
+
+	return ret;
+}
+
+struct proc_data *__new_proc_sem(void *arg, struct semaphore *sem,
+				 proc_fetchfn_t *fetch,
+				 proc_commitfn_t *commit)
+{
+	return new_wrapper(fetch, commit, arg, sem_fetch, sem_commit, sem);
+}
+
+int proc_fetch_short(const char *dir, const char *fname,
+		     char *outbuf, unsigned int size, void *shortp)
+{
+	return snprintf(outbuf, size, "%hi\n", *(short *)shortp);
+}
+
+int proc_commit_short(const char *dir, const char *fname,
+		      const char *inbuf, unsigned int size, void *shortp)
+{
+	if (sscanf(inbuf, "%hi", (short *)shortp) != 1) return -EINVAL;
+	return 0;
+}
+
+int proc_fetch_ushort(const char *dir, const char *fname,
+		      char *outbuf, unsigned int size, void *ushortp)
+{
+	return snprintf(outbuf, size, "%hu\n", *(unsigned short *)ushortp);
+}
+
+int proc_commit_ushort(const char *dir, const char *fname,
+		       const char *inbuf, unsigned int size, void *ushortp)
+{
+	if (sscanf(inbuf, "%hu", (unsigned short *)ushortp) != 1)
+		return -EINVAL;
+	return 0;
+}
+
+int proc_fetch_int(const char *dir, const char *fname,
+		    char *outbuf, unsigned int size, void *intp)
+{
+	return snprintf(outbuf, size, "%i\n", *(int *)intp);
+}
+
+int proc_commit_int(const char *dir, const char *fname,
+		    const char *inbuf, unsigned int size, void *intp)
+{
+	if (sscanf(inbuf, "%i", (int *)intp) != 1) return -EINVAL;
+	return 0;
+}
+
+int proc_fetch_uint(const char *dir, const char *fname,
+		    char *outbuf, unsigned int size, void *uintp)
+{
+	return snprintf(outbuf, size, "%u\n", *(unsigned int *)uintp);
+}
+
+int proc_commit_uint(const char *dir, const char *fname,
+		     const char *inbuf, unsigned int size, void *uintp)
+{
+	if (sscanf(inbuf, "%u", (unsigned int *)uintp) != 1) return -EINVAL;
+	return 0;
+}
+
+int proc_fetch_long(const char *dir, const char *fname,
+		    char *outbuf, unsigned int size, void *longp)
+{
+	return snprintf(outbuf, size, "%li\n", *(long *)longp);
+}
+
+int proc_commit_long(const char *dir, const char *fname,
+		     const char *inbuf, unsigned int size, void *longp)
+{
+	if (sscanf(inbuf, "%li", (long *)longp) != 1) return -EINVAL;
+	return 0;
+}
+
+int proc_fetch_ulong(const char *dir, const char *fname,
+		     char *outbuf, unsigned int size, void *ulongp)
+{
+	return snprintf(outbuf, size, "%lu\n", *(long *)ulongp);
+}
+
+int proc_commit_ulong(const char *dir, const char *fname,
+		      const char *inbuf, unsigned int size, void *ulongp)
+{
+	if (sscanf(inbuf, "%lu", (unsigned long *)ulongp) != 1) return -EINVAL;
+	return 0;
+}
+
+int proc_fetch_bool(const char *dir, const char *fname,
+		    char *outbuf, unsigned int size, void *boolp)
+{
+	if (*(int *)boolp) return snprintf(outbuf, size, "y\n");
+	else return snprintf(outbuf, size, "n\n");
+}
+
+int proc_commit_bool(const char *dir, const char *fname,
+		     const char *inbuf, unsigned int size, void *boolp)
+{
+	if (inbuf[0] == 'y' || inbuf[0] == 'Y')
+		*(int *)boolp = 1;
+	else if (inbuf[0] == 'n' || inbuf[0] == 'N')
+		*(int *)boolp = 0;
+	else return proc_commit_int(dir, fname, inbuf, size, boolp);
+	return 0;
+}
+
+/* Test code: delete me */
+static int number = 7;
+
+static int testfetch(const char *dirname,
+		     const char *filename,
+		     char *buffer,
+		     unsigned int size,
+		     void *arg)
+{
+	/* As an example, each one holds its own name */
+	return snprintf(buffer, size, "%s/%s\n", dirname, filename);
+}
+
+static int dirfunc(const char *dirname,
+		   const char *filename,
+		   struct proc_dircontents *buffer,
+		   unsigned int maxlen,
+		   void *arg)
+{
+	unsigned int used = 0;
+	char name[100];
+	unsigned int i;
+
+	for (i = 0; i < 10; i++) {
+		sprintf(name, "file-%u", i);
+		used = proc_add_dircontents(buffer, used, maxlen,
+					    S_IFREG|0400, testfetch,
+					    NULL, NULL, NULL, name);
+	}
+	/* And one infinite subdirectory example */
+	used = proc_add_dircontents(buffer, used, maxlen,
+				    S_IFDIR|0555, NULL, NULL, dirfunc, NULL,
+				    "subdir");
+	return proc_end_dircontents(buffer, used, maxlen);
+}
+
+static int __init init_test(void)
+{
+	int ret;
+	ret = proc("testdir", "number", number, int, 0644);
+	if (ret)
+		printk("Proc registration failed: %i\n", ret);
+	proc_dir("testdir", "subdir", dirfunc, NULL);
+	return 0;
+}
+
+static void __exit exit_test(void)
+{
+	unproc("testdir", "number");
+	unproc_dir("testdir", "subdir");
+}
+
+module_init(init_test);
+module_exit(exit_test);
diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/current-dontdiff --minimal linux-2.4.13-uml/fs/simpleproc/inode.c working-2.4.13-uml-proc/fs/simpleproc/inode.c
--- linux-2.4.13-uml/fs/simpleproc/inode.c	Thu Jan  1 10:00:00 1970
+++ working-2.4.13-uml-proc/fs/simpleproc/inode.c	Thu Nov  1 21:29:14 2001
@@ -0,0 +1,790 @@
+/*
+ * Simple /proc filesystem for Linux.
+ *
+ * Conceptually, there are two types of directories here: static
+ * (entries are created and deleted using
+ * register_proc/unregister_proc), and dynamic (contents are created
+ * on demand using a callback).
+ *
+ *   Copyright (C) 2001 Rusty Russell.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/simpleproc.h>
+#include <asm/semaphore.h>
+
+#include <asm/uaccess.h>
+
+/* Proc mount point */
+struct vfsmount *proc_mnt;
+
+/* Serialize insert/delete and mounts */
+static DECLARE_MUTEX(proc_semaphore);
+
+#define SIMPLE_PROCFS_MAGIC	0x62121174
+
+/* Start with this many bytes allocated for file read */
+#define PROCFS_START_FILE	64
+/* Start with this many bytes allocated for directory read */
+#define PROCFS_START_DIR	PAGE_SIZE
+/* Approximate upper ceiling for memory usage per fs */
+#define PROCFS_MAX_SIZE		PAGE_SIZE
+
+/* Pre-decls for assigning */
+static struct inode_operations proc_punt_inodeops;
+static struct file_operations proc_helper_fileops;
+static struct file_operations proc_helper_dirops;
+static struct file_operations proc_punt_dirops;
+static struct inode_operations proc_helper_inodeops;
+static struct super_operations proc_ops;
+static struct dentry_operations proc_dentry_ops;
+
+struct proc_buffer
+{
+	unsigned int maxlen;
+	unsigned int len;
+	/* One is for the nul terminator */
+	char buffer[1];
+};
+
+struct proc_data *__new_proc(void *arg,
+			     proc_fetchfn_t *fetch,
+			     proc_commitfn_t *commit,
+			     proc_dirfn_t *dir)
+{
+	struct proc_data *pdata;
+
+	pdata = kmalloc(sizeof(*pdata), GFP_KERNEL);
+	if (pdata) {
+		pdata->arg = arg;
+		pdata->fetch = fetch;
+		pdata->commit = commit;
+		pdata->dir = dir;
+	}
+	return pdata;
+}
+
+/* FIXME: I have no idea what all this does: stolen from old /proc --RR */
+static int proc_statfs(struct super_block *sb, struct statfs *buf)
+{
+	buf->f_type = SIMPLE_PROCFS_MAGIC;
+	buf->f_bsize = PAGE_SIZE/sizeof(long);
+	buf->f_bfree = 0;
+	buf->f_bavail = 0;
+	buf->f_ffree = 0;
+	buf->f_namelen = NAME_MAX;
+	return 0;
+}
+
+/* Convenience routine to make an inode */
+static struct inode *
+new_proc_inode(struct super_block *sb, int mode, int is_dynamic)
+{
+	struct inode * inode = new_inode(sb);
+
+	if (!inode)
+		return NULL;
+
+	inode->i_mode = mode;
+	inode->i_uid = 0;
+	inode->i_gid = 0;
+	inode->i_blksize = PAGE_CACHE_SIZE;
+	inode->i_blocks = 0;
+	inode->i_rdev = NODEV;
+	inode->i_mapping->a_ops = NULL;
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	switch (mode & S_IFMT) {
+	case S_IFREG:
+		inode->i_fop = &proc_helper_fileops;
+		break;
+	case S_IFDIR:
+		if (is_dynamic) {
+			inode->i_fop = &proc_punt_dirops;
+			inode->i_op = &proc_punt_inodeops;
+		} else {
+			inode->i_fop = &proc_helper_dirops;
+			inode->i_op = &proc_helper_inodeops;
+		}
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	return inode;
+}
+
+/* Make a new proc entry in this directory: must be holding proc_semapore */
+static int make_proc_entry(struct dentry *dir,
+			   const char *fname,
+			   int mode,
+			   struct proc_data *pdata,
+			   int is_dynamic)
+{
+	struct inode *inode;
+	struct dentry *dentry;
+	struct qstr qstr;
+
+	/* Create qstr for this entry */
+	qstr.name = fname;
+	qstr.len = strlen(fname);
+	qstr.hash = full_name_hash(qstr.name, qstr.len);
+
+	/* You can't put a static proc entry in a dynamic dir */
+	if (dir->d_inode->i_op == &proc_punt_inodeops)
+		BUG();
+
+	/* Does it already exist? */
+	dentry = d_lookup(dir, &qstr);
+	if (dentry) {
+		dput(dentry);
+		return -EEXIST;
+	}
+
+	/* Doesn't exist: create inode */
+	inode = new_proc_inode(dir->d_sb, mode, is_dynamic);
+	if (!inode)
+		return -ENOMEM;
+
+	/* Create dentry */
+	dentry = d_alloc(dir, &qstr);
+	if (!dentry) {
+		iput(inode);
+		return -ENOMEM;
+	}
+	dentry->d_op = &proc_dentry_ops;
+	dentry->d_fsdata = pdata;
+	d_add(dentry, inode);
+
+	/* Pin the dentry here, so it doesn't get pruned */
+	dget(dentry);
+	return 0;
+}
+
+/* Create (static) proc directory if neccessary. */
+/* FIXME: Keep refcnt, so we can delete when no more users */
+static struct dentry *get_proc_dir(const char *dirname)
+{
+	struct dentry *dentry;
+	struct qstr qstr;
+	const char *delim;
+
+	/* FIXME: Definitely need a better way --RR */
+	dentry = dget(proc_mnt->mnt_sb->s_root);
+	delim = dirname;
+
+	for (;;) {
+		struct dentry *newdentry;
+
+		/* Ignore multiple slashes */ 
+		while (*delim == '/') delim++;
+		qstr.name = delim;
+		delim = strchr(qstr.name, '/');
+		if (!delim) delim = qstr.name + strlen(qstr.name);
+		qstr.len = delim-(char *)qstr.name;
+		qstr.hash = full_name_hash(qstr.name, qstr.len);
+
+		if (qstr.len == 0)
+			break;
+
+		/* If entry doesn't exist, create it */
+		while (!(newdentry = d_lookup(dentry, &qstr))) {
+			char fname[qstr.len+1];
+			int ret;
+
+			strncpy(fname, qstr.name, qstr.len);
+			fname[qstr.len] = '\0';
+			down(&proc_semaphore);
+			ret = make_proc_entry(dentry, fname, S_IFDIR|0555,
+					       NULL, 0);
+			up(&proc_semaphore);
+
+			if (ret < 0) {
+				dput(dentry);
+				return ERR_PTR(ret);
+			}
+		}
+		dput(dentry);
+		dentry = newdentry;
+	}
+	return dentry;
+}
+
+/* Actually add a proc file or dynamic directory */
+int __proc(const char *dirname, const char *fname, int mode,
+	   struct proc_data *pdata)
+{
+	struct dentry *dir;
+	int ret;
+
+	if (!pdata)
+		return -ENOMEM;
+
+	dir = get_proc_dir(dirname);
+	if (IS_ERR(dir))
+		return PTR_ERR(dir);
+
+	ret = make_proc_entry(dir, fname, mode, pdata, S_ISDIR(mode) ? 1 : 0);
+	dput(dir);
+	return ret;
+}
+
+static int proc_nofetch(const char *dirname, const char *fname,
+			char *outbuf, unsigned int len, void *arg)
+{
+	return -ENOENT;
+}
+
+static int proc_nocommit(const char *dirname, const char *fname,
+			 const char *inbuf, unsigned int len, void *arg)
+{
+	return -ENOENT;
+}
+
+static int proc_nodir(const char *dirname,
+		      const char *filename,
+		      struct proc_dircontents *buffer,
+		      unsigned int size,
+		      void *arg)
+{
+	return -ENOENT;
+}
+
+/* Release a proc entry */
+void unproc(const char *dir, const char *fname)
+{
+	struct dentry *dentry;
+	const char *delim;
+	struct qstr qstr;
+	struct proc_data *pdata;
+
+	/* FIXME: There's a better way, right? --RR */
+	dentry = dget(proc_mnt->mnt_sb->s_root);
+
+	delim = dir;
+	for (;;) {
+		/* Ignore multiple slashes */ 
+		while (*delim == '/') delim++;
+		qstr.name = delim;
+		delim = strchr(qstr.name, '/');
+		if (!delim) delim = qstr.name + strlen(qstr.name);
+		qstr.len = delim-(char *)qstr.name;
+		qstr.hash = full_name_hash(qstr.name, qstr.len);
+
+		if (qstr.len == 0)
+			break;
+
+		dentry = d_lookup(dentry, &qstr);
+		if (!dentry)
+			BUG();
+		dput(dentry->d_parent);
+	}
+
+	qstr.name = fname;
+	qstr.len = strlen(fname);
+	qstr.hash = full_name_hash(qstr.name, qstr.len);
+	dentry = d_lookup(dentry, &qstr);
+	if (!dentry)
+		BUG();
+	dput(dentry->d_parent);
+
+	/* We have the dentry: change the private area so it doesn't
+           enter the caller any more. */
+	pdata = dentry->d_fsdata;
+	pdata->commit = proc_nocommit;
+	pdata->fetch = proc_nofetch;
+	pdata->dir = proc_nodir;
+
+	/* This will probably free the dentry immediately, but if not,
+           too bad. */
+	dput(dentry);
+	dput(dentry);
+}
+
+void unproc_dir(const char *dir, const char *fname)
+{
+	unproc(dir, fname);
+}
+
+/* See if /proc entry exists (entries registered in directory). */
+static struct dentry *proc_lookup(struct inode *dir,
+					 struct dentry *dentry)
+{
+	/* Since we place new staticn entries in the dcache, if we get
+	   here, we know the entry does not exist.  Create a negative
+	   dentry, and return NULL */
+	d_add(dentry, NULL);
+	return NULL;
+}
+
+/* Call callback to get directory contents */
+static struct proc_dircontents *get_dir_contents(const char *dirname,
+						 const char *filename,
+						 struct proc_data *pdata)
+{
+	struct proc_dircontents *ret;
+	unsigned int size = PROCFS_START_DIR;
+
+	ret = kmalloc(size, GFP_KERNEL);
+	while (ret) {
+		int used;
+		used = pdata->dir(dirname, filename, ret, size, pdata->arg);
+		if (used < 0) {
+			kfree(ret);
+			return ERR_PTR(used);
+		}
+		if (used <= size)
+			return ret;
+
+		/* Realloc larger and loop */
+		kfree(ret);
+		size = used;
+		ret = kmalloc(size, GFP_KERNEL);
+	}
+	return ERR_PTR(-ENOMEM);
+}
+
+/* Incrementing is a little tricky: round up to alignment */
+static struct proc_dircontents *next_dcont(struct proc_dircontents *dcontents)
+{
+	unsigned int len;
+
+	len = ((sizeof(*dcontents) + strlen(dcontents->name) + 1
+		+ __alignof__(*dcontents) - 1)
+	       & ~(__alignof__(*dcontents) - 1));
+	return (void *)dcontents + len;
+}
+
+/* Search results from callback for this name, and if found create inode */
+static struct proc_dircontents *
+find_dcontents(struct proc_dircontents *dir_contents,
+	       struct dentry *dentry)
+{
+	while (dir_contents->mode) {
+		if (strcmp(dentry->d_name.name, dir_contents->name) == 0)
+			return dir_contents;
+		dir_contents = next_dcont(dir_contents);
+	}
+	/* Not found... */
+	return NULL;
+}
+
+/* Since there are no hard links in this filesystem, we can simply map
+   inodes to dentries.  This is not possibly in general! */
+static struct dentry *inode_to_dentry(struct inode *inode)
+{
+	if (inode->i_dentry.next->next != &inode->i_dentry)
+		BUG();
+	return list_entry(inode->i_dentry.next, struct dentry, d_alias);
+}
+
+/* See if /proc entry exists (user controls contents of directory). */
+static struct dentry *proc_punt_lookup(struct inode *dir,
+				       struct dentry *dentry)
+{
+	/* We do the whole callback on every lookup. */
+	struct proc_data *pdata;
+	struct proc_dircontents *dir_contents, *dc;
+	struct inode *inode;
+	struct dentry *parent;
+
+	/* Since we know the inode is a directory, there is only one
+           inode in the dentry alias list, so mapping inode -> dentry
+           is easy */
+	parent = inode_to_dentry(dir);
+	dir_contents = get_dir_contents(parent->d_name.name,
+					dentry->d_name.name,
+					parent->d_fsdata);
+	if (!dir_contents || IS_ERR(dir_contents))
+		return (struct dentry *)dir_contents;
+
+	/* Looks through callback-supplied list for this dentry */ 
+	dc = find_dcontents(dir_contents, dentry);
+	if (!dc) {
+		kfree(dir_contents);
+		return NULL;
+	}
+	inode = new_proc_inode(dentry->d_sb, dc->mode, 1);
+	if (!inode) {
+		kfree(dir_contents);
+		return ERR_PTR(-ENOMEM);
+	}
+	pdata = __new_proc(dc->arg, dc->fetch, dc->commit, dc->dir);
+	if (!pdata) {
+		iput(inode);
+		kfree(dir_contents);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	dentry->d_op = &proc_dentry_ops;
+	dentry->d_fsdata = pdata;
+	d_add(dentry, inode);
+	kfree(dir_contents);
+	return NULL;
+}
+
+/* On open, we grab contents if we're readable... */
+static int proc_file_snapshot(struct inode *inode, struct file *filp)
+{
+	unsigned int size;
+	struct proc_buffer *buf;
+	struct proc_data *pdata;
+	char *dirname;
+	unsigned long page;
+
+	pdata = filp->f_dentry->d_fsdata;
+	/* Start at this, and work up */
+	size = PROCFS_START_FILE;
+
+	if (!(filp->f_mode & FMODE_READ)) {
+		/* Allocate write buffer: */
+		buf = kmalloc(sizeof(*buf) + size, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+		buf->maxlen = size;
+		buf->len = 0;
+		filp->private_data = buf;
+		return 0;
+	}
+
+	/* For the moment, you can't open for read & write.  Later,
+           when seek resets snapshot/commit, we should allow this. */
+	if (filp->f_mode & FMODE_WRITE)
+		return -EINVAL;
+
+	page = __get_free_page(GFP_USER);
+	if (!page)
+		return -ENOMEM;
+
+	/* FIXME: This is not right: the callbacks don't care what the
+	   process's idea of root is, it only wants path after proc/. */
+	dirname = d_path(filp->f_dentry, filp->f_vfsmnt,
+			 (char *)page, PAGE_SIZE);
+	if (dirname < (char *)page)
+		BUG();
+
+	/* buf[0] holds the size */
+	buf = kmalloc(sizeof(*buf)+size, GFP_KERNEL);
+	while (buf) {
+		int used;
+		used = pdata->fetch(dirname, filp->f_dentry->d_name.name,
+				    buf->buffer, size, pdata->arg);
+		if (used < 0) {
+			kfree(buf);
+			free_page(page);
+			/* FIXME: if used == -ENOENT, destroy dcache entry */
+			return used;
+		}
+		if (used <= size) {
+			free_page(page);
+			filp->private_data = buf;
+			/* Nul terminate and save size */
+			buf->maxlen = size;
+			buf->len = used;
+			buf->buffer[used] = '\0';
+			return 0;
+		}
+
+		/* Realloc larger and loop */
+		kfree(buf);
+		size = used;
+		if (size > PROCFS_MAX_SIZE)
+			break;
+		buf = kmalloc(sizeof(*buf)+size, GFP_KERNEL);
+	}
+	free_page(page);
+	return -ENOMEM;
+}
+
+/* On close, we commit contents if we've been written to... */
+static int proc_file_commit(struct inode *inode, struct file *filp)
+{
+	int ret;
+	struct proc_data *pdata;
+	struct proc_buffer *buf;
+	char *dirname;
+	unsigned long page;
+
+	pdata = filp->f_dentry->d_fsdata;
+	if (!(filp->f_mode & FMODE_WRITE)) {
+		kfree(filp->private_data);
+		return 0;
+	}
+
+	page = __get_free_page(GFP_USER);
+	if (!page) {
+		kfree(filp->private_data);
+		return -ENOMEM;
+	}
+
+	/* FIXME: This is not right: the callbacks don't care what the
+	   process's idea of root is, it only wants path after proc/. */
+	dirname = d_path(filp->f_dentry, filp->f_vfsmnt,
+			 (char *)page, PAGE_SIZE);
+	if (dirname < (char *)page)
+		BUG();
+
+	/* nul-terminate buffer */
+	buf = filp->private_data;
+	buf->buffer[buf->len] = '\0';
+	ret = pdata->commit(dirname, filp->f_dentry->d_name.name,
+			    buf->buffer, buf->len, pdata->arg);
+	
+	kfree(filp->private_data);
+	free_page(page);
+	return ret;
+}
+
+/* Copy from buffer */
+static ssize_t proc_file_read(struct file *filp, char *ubuf, size_t size,
+			      loff_t *off)
+{
+	struct proc_buffer *buf;
+	struct inode *inode;
+
+	/* Use inode semaphore to serialize against writes. */
+	inode = filp->f_dentry->d_inode;
+	if (down_interruptible(&inode->i_sem) != 0)
+		return -EINTR;
+
+	buf = filp->private_data;
+	if (size + *off > buf->len)
+		size = buf->len - *off;
+
+	/* Copy from static buffer */
+	if (copy_to_user(ubuf, buf->buffer, size) != 0) {
+		up(&inode->i_sem);
+		return -EFAULT;
+	}
+	up(&inode->i_sem);
+
+	*off += size;
+	return (ssize_t)size;
+}
+
+/* Copy to buffer */
+static ssize_t proc_file_write(struct file *filp,
+			       const char *ubuf,
+			       size_t size,
+			       loff_t *off)
+{
+	struct inode *inode;
+	struct proc_buffer *buf;
+	struct proc_data *pdata;
+
+	pdata = filp->f_dentry->d_fsdata;
+
+	/* Use inode semaphore to serialize writes & reads. */
+	inode = filp->f_dentry->d_inode;
+	if (down_interruptible(&inode->i_sem) != 0)
+		return -EINTR;
+
+	buf = filp->private_data;
+	if (*off + size > buf->maxlen) {
+		struct proc_buffer *newbuffer;
+		/* Prevent them using too much memory */
+		if (*off + size > PROCFS_MAX_SIZE) {
+			up(&inode->i_sem);
+			return -ENOSPC;
+		}
+		/* Room for count at head */
+		newbuffer = kmalloc(sizeof(*newbuffer) + *off + size,
+				    GFP_USER);
+		if (!newbuffer) {
+			up(&inode->i_sem);
+			return -ENOMEM;
+		}
+		memcpy(newbuffer, buf, sizeof(*buf) + buf->len);
+		kfree(filp->private_data);
+		filp->private_data = buf = newbuffer;
+	}
+
+	/* Do actual copy */
+	if (copy_from_user(buf->buffer + *off, ubuf, size) != 0) {
+		up(&inode->i_sem);
+		return -EFAULT;
+	}
+	up(&inode->i_sem);
+	buf->len += size;
+	*off += size;
+
+	return size;
+}
+
+/* Call the user's callback to get contents of this directory.
+   Generate . and .. automagically. */
+static int proc_dynamic_readdir(struct file *filp,
+				void *dirent,
+				filldir_t filldir)
+{
+	int i;
+	struct proc_dircontents *dcontents, *dp;
+	char *dirname;
+	unsigned long page;
+	struct proc_data *pdata;
+	struct dentry *dentry = filp->f_dentry;
+
+	pdata = filp->f_dentry->d_fsdata;
+
+	i = filp->f_pos;
+	switch (i) {
+	case 0:
+		if (filldir(dirent, ".", 1, 0, dentry->d_inode->i_ino, DT_DIR)
+		    < 0)
+			break;
+		i++;
+		filp->f_pos++;
+		/* fallthrough */
+	case 1:
+		if (filldir(dirent, "..", 2, 0,
+			    dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
+			break;
+		i++;
+		filp->f_pos++;
+	}
+
+	page = __get_free_page(GFP_USER);
+	if (!page) return -ENOMEM;
+
+	/* FIXME: This is not right: the callbacks don't care what the
+	   process's idea of root is, it only wants path after proc/. */
+	dirname = d_path(filp->f_dentry, filp->f_vfsmnt,
+			 (char *)page, PAGE_SIZE);
+	if (dirname < (char *)page)
+		BUG();
+
+	/* Call user callback to get directory */
+	dcontents = get_dir_contents(dirname,
+				     dentry->d_name.name,
+				     pdata);
+	if (IS_ERR(dcontents)) {
+		free_page(page);
+		return PTR_ERR(dcontents);
+	}
+
+	/* Skip any already-read entries... */
+	for (dp = dcontents, i -= 2; dp->mode && i; dp = next_dcont(dp), i++);
+
+	for (; dp->mode; dp = next_dcont(dp)) {
+		/* FIXME: Use non-zero inode numbers */
+		if (filldir(dirent, dp->name, strlen(dp->name),
+			    filp->f_pos,
+			    filp->f_pos,
+			    S_ISDIR(dp->mode) ? DT_DIR : DT_REG) < 0)
+			break;
+		filp->f_pos++;
+	}
+	free_page(page);
+	kfree(dcontents);
+	return 0;
+}
+
+/* Free the private area when dentry is freed. */
+static void proc_release(struct dentry *dentry)
+{
+	kfree(dentry->d_fsdata);
+}
+
+static struct super_block *proc_read_super(struct super_block *s,
+					   void *data, 
+					   int silent)
+{
+	struct inode * root_inode;
+
+	s->s_blocksize = 1024;
+	s->s_blocksize_bits = 10;
+	s->s_magic = SIMPLE_PROCFS_MAGIC;
+	s->s_op = &proc_ops;
+
+	root_inode = new_proc_inode(s, S_IFDIR|0555, 0);
+	if (!root_inode) return NULL;
+
+	/* Block concurrent mounts */
+	down(&proc_semaphore);
+
+	s->s_root = d_alloc_root(root_inode);
+	if (!s->s_root) {
+		iput(root_inode);
+		up(&proc_semaphore);
+		return NULL;
+	}
+	up(&proc_semaphore);
+	return s;
+}
+
+/* Proc files use these wrappers */
+static struct file_operations proc_helper_fileops = {
+	open:		proc_file_snapshot,
+	release:	proc_file_commit,
+	read:		proc_file_read,
+	write:		proc_file_write,
+};
+
+/* Directories which use normal registration mechanism, which sit in
+   the dcache */
+static struct file_operations proc_helper_dirops = {
+	read:		generic_read_dir,
+	readdir:	dcache_readdir,
+};
+
+/* Directories which have their own dynamic content */
+static struct file_operations proc_punt_dirops = {
+	read:		generic_read_dir,
+	readdir:	proc_dynamic_readdir,
+};
+
+/* You can only do lookups through these dirs: dynamic ones do callbacks... */
+static struct inode_operations proc_punt_inodeops = {
+	lookup:		proc_punt_lookup,
+};
+
+/* ... static ones look up registrations */
+static struct inode_operations proc_helper_inodeops = {
+	lookup:		proc_lookup,
+};
+
+static struct super_operations proc_ops = {
+	statfs:		proc_statfs,
+	put_inode:	force_delete,
+};
+
+static struct dentry_operations proc_dentry_ops = {
+	d_release:	proc_release,
+};
+
+static DECLARE_FSTYPE(proc_fs_type, "proc", proc_read_super, FS_SINGLE);
+
+static int __init init_proc_fs(void)
+{
+	register_filesystem(&proc_fs_type);
+	proc_mnt = kern_mount(&proc_fs_type);
+	return 0;
+}
+
+static void __exit exit_proc_fs(void)
+{
+	unregister_filesystem(&proc_fs_type);
+}
+
+module_init(init_proc_fs);
+module_exit(exit_proc_fs);
+

^ permalink raw reply	[flat|nested] 118+ messages in thread

end of thread, other threads:[~2001-11-13 18:25 UTC | newest]

Thread overview: 118+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20011104214229Z17052-23341+37@humbolt.nl.linux.org>
2001-11-04 23:42 ` PROPOSAL: dot-proc interface [was: /proc stuff] Alexander Viro
2001-11-05  0:10   ` Daniel Phillips
2001-11-04 23:06 Craig Thrall
2001-11-04 23:39 ` Jakob Østergaard
     [not found] <Pine.LNX.4.33.0111041141100.14150-100000@penguin.transmeta.com>
2001-11-04 19:53 ` Daniel Phillips
  -- strict thread matches above, loose matches on Subject: below --
2001-11-01 10:32 [PATCH] 2.5 PROPOSAL: Replacement for current /proc of shit Rusty Russell
2001-11-04 12:30 ` Tim Jansen
2001-11-04 13:36   ` Daniel Kobras
2001-11-04 14:13     ` Tim Jansen
2001-11-04 15:33       ` PROPOSAL: dot-proc interface [was: /proc stuff] Jakob Østergaard
2001-11-04 16:05         ` Gábor Lénárt
2001-11-04 16:31         ` Daniel Phillips
2001-11-04 17:30           ` Jakob Østergaard
2001-11-04 16:45         ` Tim Jansen
2001-11-04 17:28           ` Daniel Phillips
2001-11-04 17:41             ` Jakob Østergaard
2001-11-04 17:54               ` SpaceWalker
2001-11-04 20:45                 ` Albert D. Cahalan
2001-11-04 17:59               ` John Levon
2001-11-04 18:31                 ` Jakob Østergaard
2001-11-04 18:40                   ` Alexander Viro
2001-11-04 19:04                     ` Jakob Østergaard
2001-11-04 19:24                       ` Alex Bligh - linux-kernel
2001-11-04 19:45                         ` Jakob Østergaard
2001-11-04 19:52                           ` Alexander Viro
2001-11-04 20:06                             ` Jakob Østergaard
2001-11-04 22:01                             ` Daniel Phillips
2001-11-04 21:12                           ` Albert D. Cahalan
2001-11-04 21:20                             ` Jakob Østergaard
2001-11-04 21:42                               ` Tim Jansen
2001-11-04 22:13                               ` Albert D. Cahalan
2001-11-05 11:23                                 ` Martin Dalecki
2001-11-05 15:58                                   ` Alexander Viro
2001-11-05 18:30                                     ` Martin Dalecki
2001-11-05 23:00                                       ` Albert D. Cahalan
2001-11-06 13:47                                         ` Martin Dalecki
2001-11-06 17:13                                         ` Gerhard Mack
2001-11-05 16:38                                 ` Stephen Satchell
2001-11-05 18:39                                   ` Martin Dalecki
2001-11-05 18:28                                     ` Ben Greear
2001-11-05 18:40                                       ` Rik van Riel
2001-11-05 21:03                                         ` Tim Jansen
2001-11-05 21:58                                           ` Ben Greear
2001-11-05 22:51                                             ` Tim Jansen
2001-11-05 22:59                                               ` Erik Andersen
2001-11-05 23:35                                                 ` Tim Jansen
2001-11-05 23:41                                                   ` Alexander Viro
2001-11-06 13:49                                                     ` Martin Dalecki
2001-11-06 19:49                                                 ` dank
2001-11-06 22:22                                                   ` Erik Andersen
2001-11-06 22:47                                                     ` dank
2001-11-06 23:11                                                       ` Erik Andersen
2001-11-06 23:39                                                       ` Ricky Beam
2001-11-07 12:45                                                     ` Remco Post
2001-11-07  1:06                                                   ` George Greer
2001-11-05 19:58                                 ` Jonathan Lundell
2001-11-05 21:43                                 ` Stephen Satchell
2001-11-06  5:22                                   ` Ragnar Hojland Espinosa
2001-11-04 21:22                             ` Alex Bligh - linux-kernel
2001-11-05  4:03                           ` Stuart Young
2001-11-05  4:05                             ` Alexander Viro
2001-11-05  4:55                             ` Stuart Young
2001-11-05 16:32                               ` SpaceWalker
2001-11-06  6:46                                 ` Jakob Østergaard
2001-11-04 19:29                       ` Alexander Viro
2001-11-04 19:50                         ` Jakob Østergaard
2001-11-04 20:01                           ` Alexander Viro
2001-11-04 20:09                             ` Jakob Østergaard
2001-11-06  7:23                           ` Kai Henningsen
2001-11-06 14:00                             ` Jakob Østergaard
2001-11-04 18:27               ` Tim Jansen
2001-11-04 18:35                 ` Alexander Viro
2001-11-04 18:39                 ` Jakob Østergaard
2001-11-07  1:20                 ` Pavel Machek
2001-11-07 21:14                   ` Rik van Riel
2000-01-01  0:13                     ` Pavel Machek
2001-11-04 18:20             ` Tim Jansen
2001-11-04 18:30               ` Alexander Viro
2001-11-04 18:52                 ` Jakob Østergaard
2001-11-04 19:18                   ` Daniel Phillips
2001-11-04 21:41                 ` Albert D. Cahalan
2001-11-05 11:06                 ` Martin Dalecki
2001-11-05 10:28                   ` Daniel Phillips
2001-11-05 22:46                     ` Albert D. Cahalan
2001-11-06  0:54                       ` Daniel Phillips
2001-11-06  1:11                       ` Stephen Satchell
2001-11-04 18:46               ` Jakob Østergaard
2001-11-04 19:07             ` Linus Torvalds
2001-11-04 19:20               ` Jakob Østergaard
2001-11-04 19:32                 ` Dave Jones
2001-11-04 19:52                   ` Jakob Østergaard
2001-11-04 20:06                     ` Alexander Viro
2001-11-04 20:11                       ` Jakob Østergaard
2001-11-11 10:06                     ` Kai Henningsen
2001-11-11 19:43                       ` Jakob Østergaard
2001-11-12 13:43                         ` Pascal Schmidt
2001-11-13 12:09                           ` Jakob Østergaard
2001-11-13 14:41                         ` Riley Williams
2001-11-04 22:09               ` Luigi Genoni
2001-11-04 17:48           ` Jakob Østergaard
2001-11-04 18:02             ` John Levon
2001-11-04 18:34             ` Tim Jansen
2001-11-04 18:59               ` Jakob Østergaard
2001-11-04 19:19                 ` Tim Jansen
2001-11-04 19:24                   ` Jakob Østergaard
2001-11-04 19:41                     ` Tim Jansen
2001-11-04 19:55                       ` Jakob Østergaard
2001-11-04 20:13                         ` Tim Jansen
2001-11-04 20:11                           ` Jakob Østergaard
2001-11-04 20:47                             ` Alex Bligh - linux-kernel
2001-11-04 21:02                               ` Jakob Østergaard
2001-11-04 22:53                         ` Stephen Satchell
2001-11-05 11:04         ` zmwillow
2001-11-05 13:41         ` Petr Baudis
2001-11-05 20:49           ` Tim Jansen
2001-11-05 22:01             ` Ben Greear
     [not found]             ` <20011105223413.U11619@pasky.ji.cz>
     [not found]               ` <160rly-1tl3XUC@fmrl05.sul.t-online.com>
2001-11-05 22:07                 ` Petr Baudis
2001-11-06  7:25           ` Jakob Østergaard
2001-11-06  8:21             ` Petr Baudis
2001-11-06  8:34               ` Alexander Viro
2001-11-06 13:43                 ` Jakob Østergaard
2001-11-06 17:01                 ` Petr Baudis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).