[LWN Logo]
[LWN.net]
From:	 Alexander Viro <viro@math.psu.edu>
To:	 Linus Torvalds <torvalds@transmeta.com>
Subject: [PATCH] nfsd as filesystem
Date:	 Wed, 13 Mar 2002 02:42:47 -0500 (EST)
Cc:	 linux-fsdevel@vger.kernel.org

	Patch below
* introduces a new filesystem - nfsd.  No, it's not a typo.  It's a small
  tree with fixed topology defined by nfsd and IO on its files does what
  we used to do by hand in nfsctl.c.
* turns sys_nfsservctl() into a sequence of open()/write()/read()/close()
  It works as it used to - we don't need nfsd to be mounted anywhere, etc.
* nfsd_linkage ugliness is gone.
* getfs and getfh demonstrate (rather trivial) example of "descriptor as
  transaction descriptor" behaviour.
* we are fairly close to the situation when driver-defined filesystems can
  be done with practically zero code overhead.  We are still not there, but
  it's a matter of adding a couple of helpers for populating the tree.

	One thing we get immediately is a cleanup of sys_nfsservctl() -
it got _much_ better.  Moreover, we get an alternative interface that
uses normal file IO and can be used without magic syscalls.

	Patch is against 2.4.7-pre1 + ftp.math.psu.edu/pub/viro/[01]-*C7-pre1
(already sent to Linus - one of them adds a library of helper functions for
simple filesystems, another plugs a leak in knfsd).

	It works here(tm).  Enjoy.

diff -urN C7-pre1-nfsd-fix/fs/Makefile C7-pre1-nfsd-fs/fs/Makefile
--- C7-pre1-nfsd-fix/fs/Makefile	Tue Mar 12 22:44:24 2002
+++ C7-pre1-nfsd-fs/fs/Makefile	Wed Mar 13 02:16:27 2002
@@ -22,6 +22,12 @@
 obj-y += noquot.o
 endif
 
+ifneq ($(CONFIG_NFSD),n)
+ifneq ($(CONFIG_NFSD),)
+obj-y += nfsctl.o
+endif
+endif
+
 subdir-$(CONFIG_PROC_FS)	+= proc
 subdir-y			+= partitions
 subdir-y			+= driverfs
diff -urN C7-pre1-nfsd-fix/fs/dcache.c C7-pre1-nfsd-fs/fs/dcache.c
--- C7-pre1-nfsd-fix/fs/dcache.c	Fri Mar  8 02:09:51 2002
+++ C7-pre1-nfsd-fs/fs/dcache.c	Wed Mar 13 02:16:27 2002
@@ -1252,6 +1252,7 @@
 /* SLAB cache for buffer_head structures */
 kmem_cache_t *bh_cachep;
 EXPORT_SYMBOL(bh_cachep);
+EXPORT_SYMBOL(d_genocide);
 
 extern void bdev_cache_init(void);
 extern void cdev_cache_init(void);
diff -urN C7-pre1-nfsd-fix/fs/filesystems.c C7-pre1-nfsd-fs/fs/filesystems.c
--- C7-pre1-nfsd-fix/fs/filesystems.c	Tue Feb 19 22:33:03 2002
+++ C7-pre1-nfsd-fs/fs/filesystems.c	Wed Mar 13 02:16:27 2002
@@ -3,39 +3,10 @@
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
  *
- *  nfsservctl system-call when nfsd is not compiled in.
+ *  table of configured filesystems
  */
 
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/time.h>
-#include <linux/smp_lock.h>
-#include <linux/kmod.h>
-#include <linux/nfsd/interface.h>
-#include <linux/linkage.h>
-
-#if ! defined(CONFIG_NFSD)
-struct nfsd_linkage *nfsd_linkage;
-
-long
-asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp)
-{
-	int ret = -ENOSYS;
-	
-#if defined(CONFIG_MODULES)
-	lock_kernel();
-
-	if (nfsd_linkage ||
-	    (request_module ("nfsd") == 0 && nfsd_linkage)) {
-		__MOD_INC_USE_COUNT(nfsd_linkage->owner);
-		unlock_kernel();
-		ret = nfsd_linkage->do_nfsservctl(cmd, argp, resp);
-		__MOD_DEC_USE_COUNT(nfsd_linkage->owner);
-	} else
-		unlock_kernel();
-#endif
-	return ret;
-}
-EXPORT_SYMBOL(nfsd_linkage);
-
-#endif /* CONFIG_NFSD */
+/*
+ * Code will move here from fs/super.c and yes, it will be fs type handling
+ * stuff.
+ */
diff -urN C7-pre1-nfsd-fix/fs/nfsctl.c C7-pre1-nfsd-fs/fs/nfsctl.c
--- C7-pre1-nfsd-fix/fs/nfsctl.c	Wed Dec 31 19:00:00 1969
+++ C7-pre1-nfsd-fs/fs/nfsctl.c	Wed Mar 13 02:16:27 2002
@@ -0,0 +1,87 @@
+/*
+ *	fs/nfsctl.c
+ *
+ *	This should eventually move to userland.
+ *
+ */
+#include <linux/config.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/syscall.h>
+#include <linux/linkage.h>
+#include <asm/uaccess.h>
+
+/*
+ * open a file on nfsd fs
+ */
+
+struct vfsmount *do_kern_mount(const char *type, int flags, char *name, void *data);
+
+static struct file *do_open(char *name, int flags)
+{
+	struct nameidata nd;
+	int error;
+
+	nd.mnt = do_kern_mount("nfsd", 0, "nfsd", NULL);
+
+	if (IS_ERR(nd.mnt))
+		return (struct file *)nd.mnt;
+
+	nd.dentry = dget(nd.mnt->mnt_root);
+	nd.last_type = LAST_ROOT;
+	nd.flags = 0;
+
+	error = path_walk(name, &nd);
+	if (error)
+		return ERR_PTR(error);
+
+	return dentry_open(nd.dentry, nd.mnt, flags);
+}
+
+static struct {
+	char *name; int wsize; int rsize;
+} map[] = {
+	[NFSCTL_SVC]={"svc", sizeof(struct nfsctl_svc)},
+	[NFSCTL_ADDCLIENT]={"add", sizeof(struct nfsctl_client)},
+	[NFSCTL_DELCLIENT]={"del", sizeof(struct nfsctl_client)},
+	[NFSCTL_EXPORT]={"export", sizeof(struct nfsctl_export)},
+	[NFSCTL_UNEXPORT]={"unexport", sizeof(struct nfsctl_export)},
+#ifdef notyet
+	[NFSCTL_UGIDUPDATE]={"ugid", sizeof(struct nfsctl_uidmap)},
+#endif
+	[NFSCTL_GETFD]={"getfd", sizeof(struct nfsctl_fdparm), NFS_FHSIZE},
+	[NFSCTL_GETFS]={"getfs", sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)},
+};
+
+long
+asmlinkage sys_nfsservctl(int cmd, struct nfsctl_arg *arg, void *res)
+{
+	struct file *file;
+	void *p = &arg->u;
+	int version;
+	int err;
+
+	if (copy_from_user(&version, &arg->ca_version, sizeof(int)))
+		return -EFAULT;
+
+	if (version != NFSCTL_VERSION) {
+		printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
+		return -EINVAL;
+	}
+
+	if (cmd < 0 || cmd >= sizeof(map)/sizeof(map[0]) || !map[cmd].name)
+		return -EINVAL;
+
+	file = do_open(map[cmd].name, map[cmd].rsize ? O_RDWR : O_WRONLY);	
+	if (IS_ERR(file))
+		return PTR_ERR(file);
+	err = file->f_op->write(file, p, map[cmd].wsize, &file->f_pos);
+	if (err >= 0 && map[cmd].rsize)
+		err = file->f_op->read(file, res, map[cmd].rsize, &file->f_pos);
+	if (err >= 0)
+		err = 0;
+	fput(file);
+	return err;
+}
diff -urN C7-pre1-nfsd-fix/fs/nfsd/nfsctl.c C7-pre1-nfsd-fs/fs/nfsd/nfsctl.c
--- C7-pre1-nfsd-fix/fs/nfsd/nfsctl.c	Fri Mar  8 02:09:52 2002
+++ C7-pre1-nfsd-fs/fs/nfsd/nfsctl.c	Wed Mar 13 02:16:27 2002
@@ -21,6 +21,7 @@
 #include <linux/slab.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include <linux/pagemap.h>
 
 #include <linux/nfs.h>
 #include <linux/sunrpc/svc.h>
@@ -30,20 +31,96 @@
 #include <linux/nfsd/syscall.h>
 
 #include <asm/uaccess.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
-
-static int	nfsctl_svc(struct nfsctl_svc *data);
-static int	nfsctl_addclient(struct nfsctl_client *data);
-static int	nfsctl_delclient(struct nfsctl_client *data);
-static int	nfsctl_export(struct nfsctl_export *data);
-static int	nfsctl_unexport(struct nfsctl_export *data);
-static int	nfsctl_getfd(struct nfsctl_fdparm *, __u8 *);
-static int	nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *);
-#ifdef notyet
-static int	nfsctl_ugidupdate(struct nfsctl_ugidmap *data);
-#endif
+
+/*
+ *	We have a single directory with 8 nodes in it.
+ */
+enum {
+	NFSD_Root = 1,
+	NFSD_Svc,
+	NFSD_Add,
+	NFSD_Del,
+	NFSD_Export,
+	NFSD_Unexport,
+	NFSD_Getfd,
+	NFSD_Getfs,
+	NFSD_List,
+};
+
+/*
+ * write() for these nodes.
+ */
+static ssize_t write_svc(struct file *file, const char *buf, size_t size);
+static ssize_t write_add(struct file *file, const char *buf, size_t size);
+static ssize_t write_del(struct file *file, const char *buf, size_t size);
+static ssize_t write_export(struct file *file, const char *buf, size_t size);
+static ssize_t write_unexport(struct file *file, const char *buf, size_t size);
+static ssize_t write_getfd(struct file *file, const char *buf, size_t size);
+static ssize_t write_getfs(struct file *file, const char *buf, size_t size);
+
+static ssize_t (*write_op[])(struct file *, const char *, size_t) = {
+	[NFSD_Svc] = write_svc,
+	[NFSD_Add] = write_add,
+	[NFSD_Del] = write_del,
+	[NFSD_Export] = write_export,
+	[NFSD_Unexport] = write_unexport,
+	[NFSD_Getfd] = write_getfd,
+	[NFSD_Getfs] = write_getfs,
+};
+
+static ssize_t fs_write(struct file *file, const char *buf, size_t size, loff_t *pos)
+{
+	ino_t ino =  file->f_dentry->d_inode->i_ino;
+	if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino])
+		return -EINVAL;
+	return write_op[ino](file, buf, size);
+}
+
+/*
+ * read(), open() and release() for getfs and getfd (read/write ones).
+ * IO on these is a simple transaction - you open() the file, write() to it
+ * and that generates a (stored) response.  After that read() will simply
+ * access that response.
+ */
+
+static ssize_t TA_read(struct file *file, char *buf, size_t size, loff_t *pos)
+{
+	if (!file->private_data)
+		return 0;
+	if (*pos >= file->f_dentry->d_inode->i_size)
+		return 0;
+	if (*pos + size > file->f_dentry->d_inode->i_size)
+		size = file->f_dentry->d_inode->i_size - *pos;
+	if (copy_to_user(buf, file->private_data + *pos, size))
+		return -EFAULT;
+	*pos += size;
+	return size;
+}
+
+static ssize_t TA_open(struct inode *inode, struct file *file)
+{
+	file->private_data = NULL;
+	return 0;
+}
+
+static ssize_t TA_release(struct inode *inode, struct file *file)
+{
+	void *p = file->private_data;
+	file->private_data = NULL;
+	kfree(p);
+	return 0;
+}
+
+static struct file_operations writer_ops = {
+	write:	fs_write,
+};
+
+static struct file_operations reader_ops = {
+	write:	fs_write,
+	read:	TA_read,
+	open:	TA_open,
+	release:TA_release,
+};
 
 extern struct seq_operations nfs_exports_op;
 static int exports_open(struct inode *inode, struct file *file)
@@ -57,254 +134,277 @@
 	release:	seq_release,
 };
 
-void proc_export_init(void)
-{
-	struct proc_dir_entry *entry;
-	if (!proc_mkdir("fs/nfs", 0))
-		return;
-	entry = create_proc_entry("fs/nfs/exports", 0, NULL);
-	if (entry)
-		entry->proc_fops =  &exports_operations;
-}
+/*
+ *	Description of fs contents.
+ */
+static struct { char *name; struct file_operations *ops; int mode; } files[] = {
+	[NFSD_Svc] = {"svc", &writer_ops, S_IWUSR},
+	[NFSD_Add] = {"add", &writer_ops, S_IWUSR},
+	[NFSD_Del] = {"del", &writer_ops, S_IWUSR},
+	[NFSD_Export] = {"export", &writer_ops, S_IWUSR},
+	[NFSD_Unexport] = {"unexport", &writer_ops, S_IWUSR},
+	[NFSD_Getfd] = {"getfd", &reader_ops, S_IWUSR|S_IRUSR},
+	[NFSD_Getfs] = {"getfs", &reader_ops, S_IWUSR|S_IRUSR},
+	[NFSD_List] = {"exports", &exports_operations, S_IRUGO},
+};
 
-static inline int
-nfsctl_svc(struct nfsctl_svc *data)
-{
-	return nfsd_svc(data->svc_port, data->svc_nthreads);
-}
+/*----------------------------------------------------------------------------*/
+/*
+ * payload - write methods
+ */
 
-static inline int
-nfsctl_addclient(struct nfsctl_client *data)
+static ssize_t write_svc(struct file *file, const char *buf, size_t size)
 {
-	return exp_addclient(data);
+	struct nfsctl_svc data;
+	if (size < sizeof(data))
+		return -EINVAL;
+	if (copy_from_user(&data, buf, size))
+		return -EFAULT;
+	return nfsd_svc(data.svc_port, data.svc_nthreads);
 }
 
-static inline int
-nfsctl_delclient(struct nfsctl_client *data)
+static ssize_t write_add(struct file *file, const char *buf, size_t size)
 {
-	return exp_delclient(data);
+	struct nfsctl_client data;
+	if (size < sizeof(data))
+		return -EINVAL;
+	if (copy_from_user(&data, buf, size))
+		return -EFAULT;
+	return exp_addclient(&data);
 }
 
-static inline int
-nfsctl_export(struct nfsctl_export *data)
+static ssize_t write_del(struct file *file, const char *buf, size_t size)
 {
-	return exp_export(data);
+	struct nfsctl_client data;
+	if (size < sizeof(data))
+		return -EINVAL;
+	if (copy_from_user(&data, buf, size))
+		return -EFAULT;
+	return exp_delclient(&data);
 }
 
-static inline int
-nfsctl_unexport(struct nfsctl_export *data)
+static ssize_t write_export(struct file *file, const char *buf, size_t size)
 {
-	return exp_unexport(data);
+	struct nfsctl_export data;
+	if (size < sizeof(data))
+		return -EINVAL;
+	if (copy_from_user(&data, buf, size))
+		return -EFAULT;
+	return exp_export(&data);
 }
 
-#ifdef notyet
-static inline int
-nfsctl_ugidupdate(nfs_ugidmap *data)
+static ssize_t write_unexport(struct file *file, const char *buf, size_t size)
 {
-	return -EINVAL;
+	struct nfsctl_export data;
+	if (size < sizeof(data))
+		return -EINVAL;
+	if (copy_from_user(&data, buf, size))
+		return -EFAULT;
+	return exp_unexport(&data);
 }
-#endif
 
-static inline int
-nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res)
+static ssize_t write_getfs(struct file *file, const char *buf, size_t size)
 {
-	struct sockaddr_in	*sin;
-	struct svc_client	*clp;
-	int			err = 0;
+	struct nfsctl_fsparm data;
+	struct sockaddr_in *sin;
+	struct svc_client *clp;
+	int err = 0;
+	struct knfsd_fh *res;
 
-	if (data->gd_addr.sa_family != AF_INET)
+	if (file->private_data)
+		return -EINVAL;
+	if (size < sizeof(data))
+		return -EINVAL;
+	if (copy_from_user(&data, buf, size))
+		return -EFAULT;
+	if (data.gd_addr.sa_family != AF_INET)
 		return -EPROTONOSUPPORT;
-	sin = (struct sockaddr_in *)&data->gd_addr;
-	if (data->gd_maxlen > NFS3_FHSIZE)
-		data->gd_maxlen = NFS3_FHSIZE;
+	sin = (struct sockaddr_in *)&data.gd_addr;
+	if (data.gd_maxlen > NFS3_FHSIZE)
+		data.gd_maxlen = NFS3_FHSIZE;
+	res = kmalloc(sizeof(struct knfsd_fh), GFP_KERNEL);
+	memset(res, 0, sizeof(struct knfsd_fh));
+	if (!res)
+		return -ENOMEM;
 	exp_readlock();
 	if (!(clp = exp_getclient(sin)))
 		err = -EPERM;
 	else
-		err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
+		err = exp_rootfh(clp, data.gd_path, res, data.gd_maxlen);
 	exp_readunlock();
+
+	down(&file->f_dentry->d_inode->i_sem);
+	if (file->private_data)
+		err = -EINVAL;
+	if (err)
+		kfree(res);
+	else {
+		file->f_dentry->d_inode->i_size = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base;
+		file->private_data = res;
+		err = sizeof(data);
+	}
+	up(&file->f_dentry->d_inode->i_sem);
+
 	return err;
 }
 
-static inline int
-nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res)
+static ssize_t write_getfd(struct file *file, const char *buf, size_t size)
 {
-	struct sockaddr_in	*sin;
-	struct svc_client	*clp;
-	int			err = 0;
-	struct	knfsd_fh	fh;
+	struct nfsctl_fdparm data;
+	struct sockaddr_in *sin;
+	struct svc_client *clp;
+	int err = 0;
+	struct knfsd_fh fh;
+	char *res;
 
-	if (data->gd_addr.sa_family != AF_INET)
+	if (file->private_data)
+		return -EINVAL;
+	if (size < sizeof(data))
+		return -EINVAL;
+	if (copy_from_user(&data, buf, size))
+		return -EFAULT;
+	if (data.gd_addr.sa_family != AF_INET)
 		return -EPROTONOSUPPORT;
-	if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
+	if (data.gd_version < 2 || data.gd_version > NFSSVC_MAXVERS)
 		return -EINVAL;
-	sin = (struct sockaddr_in *)&data->gd_addr;
-
+	res = kmalloc(NFS_FHSIZE, GFP_KERNEL);
+	if (!res)
+		return -ENOMEM;
+	sin = (struct sockaddr_in *)&data.gd_addr;
 	exp_readlock();
 	if (!(clp = exp_getclient(sin)))
 		err = -EPERM;
 	else
-		err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
+		err = exp_rootfh(clp, data.gd_path, &fh, NFS_FHSIZE);
 	exp_readunlock();
 
-	if (err == 0) {
-		if (fh.fh_size > NFS_FHSIZE)
-			err = -EINVAL;
-		else {
-			memset(res,0, NFS_FHSIZE);
-			memcpy(res, &fh.fh_base, fh.fh_size);
-		}
+	down(&file->f_dentry->d_inode->i_sem);
+	if (file->private_data)
+		err = -EINVAL;
+	if (!err && fh.fh_size > NFS_FHSIZE)
+		err = -EINVAL;
+	if (err)
+		kfree(res);
+	else {
+		memset(res,0, NFS_FHSIZE);
+		memcpy(res, &fh.fh_base, fh.fh_size);
+		file->f_dentry->d_inode->i_size = NFS_FHSIZE;
+		file->private_data = res;
+		err = sizeof(data);
 	}
+	up(&file->f_dentry->d_inode->i_sem);
 
 	return err;
 }
 
-#ifdef CONFIG_NFSD
-#define handle_sys_nfsservctl sys_nfsservctl
-#endif
-
-static struct {
-	int argsize, respsize;
-}  sizes[] = {
-	/* NFSCTL_SVC        */ { sizeof(struct nfsctl_svc), 0 },
-	/* NFSCTL_ADDCLIENT  */ { sizeof(struct nfsctl_client), 0},
-	/* NFSCTL_DELCLIENT  */ { sizeof(struct nfsctl_client), 0},
-	/* NFSCTL_EXPORT     */ { sizeof(struct nfsctl_export), 0},
-	/* NFSCTL_UNEXPORT   */ { sizeof(struct nfsctl_export), 0},
-	/* NFSCTL_UGIDUPDATE */ { sizeof(struct nfsctl_uidmap), 0},
-	/* NFSCTL_GETFH      */ { sizeof(struct nfsctl_fhparm), NFS_FHSIZE},
-	/* NFSCTL_GETFD      */ { sizeof(struct nfsctl_fdparm), NFS_FHSIZE},
-	/* NFSCTL_GETFS      */ { sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)},
+/*----------------------------------------------------------------------------*/
+/*
+ *	populating the filesystem.
+ */
+
+static struct super_operations s_ops = {
+	statfs:		simple_statfs,
 };
-#define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1)
 
-long
-asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
+static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
 {
-	struct nfsctl_arg *	argp = opaque_argp;
-	union nfsctl_res *	resp = opaque_resp;
-	struct nfsctl_arg *	arg = NULL;
-	union nfsctl_res *	res = NULL;
-	int			err;
-	int			argsize, respsize;
-
-
-	err = -EPERM;
-	if (!capable(CAP_SYS_ADMIN)) {
-		goto done;
-	}
-	err = -EINVAL;
-	if (cmd<0 || cmd > CMD_MAX)
-		goto done;
-	err = -EFAULT;
-	argsize = sizes[cmd].argsize + (int)&((struct nfsctl_arg *)0)->u;
-	respsize = sizes[cmd].respsize;	/* maximum */
-	if (!access_ok(VERIFY_READ, argp, argsize)
-	 || (resp && !access_ok(VERIFY_WRITE, resp, respsize))) {
-		goto done;
-	}
-	err = -ENOMEM;	/* ??? */
-	if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
-	    (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
-		goto done;
-	}
-
-	err = -EINVAL;
-	copy_from_user(arg, argp, argsize);
-	if (arg->ca_version != NFSCTL_VERSION) {
-		printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
-		goto done;
+	struct inode *inode;
+	struct dentry *root;
+	struct dentry *dentry;
+	int i;
+
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic = 0x6e667364;
+	sb->s_op = &s_ops;
+
+	inode = new_inode(sb);
+	if (!inode)
+		return -ENOMEM;
+	inode->i_mode = S_IFDIR | 0755;
+	inode->i_uid = 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;
+	inode->i_op = &simple_dir_inode_operations;
+	inode->i_fop = &simple_dir_operations;
+	root = d_alloc_root(inode);
+	if (!root) {
+		iput(inode);
+		return -ENOMEM;
 	}
-
-	switch(cmd) {
-	case NFSCTL_SVC:
-		err = nfsctl_svc(&arg->ca_svc);
-		break;
-	case NFSCTL_ADDCLIENT:
-		err = nfsctl_addclient(&arg->ca_client);
-		break;
-	case NFSCTL_DELCLIENT:
-		err = nfsctl_delclient(&arg->ca_client);
-		break;
-	case NFSCTL_EXPORT:
-		err = nfsctl_export(&arg->ca_export);
-		break;
-	case NFSCTL_UNEXPORT:
-		err = nfsctl_unexport(&arg->ca_export);
-		break;
-#ifdef notyet
-	case NFSCTL_UGIDUPDATE:
-		err = nfsctl_ugidupdate(&arg->ca_umap);
-		break;
-#endif
-	case NFSCTL_GETFD:
-		err = nfsctl_getfd(&arg->ca_getfd, res->cr_getfh);
-		break;
-	case NFSCTL_GETFS:
-		err = nfsctl_getfs(&arg->ca_getfs, &res->cr_getfs);
-		respsize = res->cr_getfs.fh_size+ (int)&((struct knfsd_fh*)0)->fh_base;
-		break;
-	default:
-		err = -EINVAL;
+	for (i = NFSD_Svc; i <= NFSD_List; i++) {
+		struct qstr name;
+		name.name = files[i].name;
+		name.len = strlen(name.name);
+		name.hash = full_name_hash(name.name, name.len);
+		dentry = d_alloc(root, &name);
+		if (!dentry)
+			goto out;
+		inode = new_inode(sb);
+		if (!inode)
+			goto out;
+		inode->i_mode = S_IFREG | files[i].mode;
+		inode->i_uid = 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;
+		inode->i_fop = files[i].ops;
+		inode->i_ino = i;
+		d_add(dentry, inode);
 	}
+	sb->s_root = root;
+	return 0;
 
-	if (!err && resp && respsize)
-		copy_to_user(resp, res, respsize);
-
-done:
-	if (arg)
-		kfree(arg);
-	if (res)
-		kfree(res);
-
-	return err;
+out:
+	d_genocide(root);
+	dput(root);
+	return -ENOMEM;
 }
 
-EXPORT_NO_SYMBOLS;
-MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
-MODULE_LICENSE("GPL");
+static struct super_block *nfsd_get_sb(struct file_system_type *fs_type,
+	int flags, char *dev_name, void *data)
+{
+	return get_sb_single(fs_type, flags, data, nfsd_fill_super);
+}
 
-#ifdef MODULE
-struct nfsd_linkage nfsd_linkage_s = {
-	do_nfsservctl: handle_sys_nfsservctl,
-	owner: THIS_MODULE,
+static struct file_system_type nfsd_fs_type = {
+	owner:		THIS_MODULE,
+	name:		"nfsd",
+	get_sb:		nfsd_get_sb,
+	kill_sb:	kill_litter_super,
 };
-#endif
 
-/*
- * Initialize the module
- */
-static int __init
-nfsd_init(void)
+static int __init init_nfsd(void)
 {
 	printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
-#ifdef MODULE
-	nfsd_linkage = &nfsd_linkage_s;
-#endif
 	nfsd_stat_init();	/* Statistics */
 	nfsd_cache_init();	/* RPC reply cache */
 	nfsd_export_init();	/* Exports table */
 	nfsd_lockd_init();	/* lockd->nfsd callbacks */
-	proc_export_init();
+	if (proc_mkdir("fs/nfs", 0)) {
+		struct proc_dir_entry *entry;
+		entry = create_proc_entry("fs/nfs/exports", 0, NULL);
+		if (entry)
+			entry->proc_fops =  &exports_operations;
+	}
+	register_filesystem(&nfsd_fs_type);
 	return 0;
 }
 
-/*
- * Clean up the mess before unloading the module
- */
-static void __exit
-nfsd_exit(void)
+static void __exit exit_nfsd(void)
 {
-#ifdef MODULE
-	nfsd_linkage = NULL;
-#endif
 	nfsd_export_shutdown();
 	nfsd_cache_shutdown();
 	remove_proc_entry("fs/nfs/exports", NULL);
 	remove_proc_entry("fs/nfs", NULL);
 	nfsd_stat_shutdown();
 	nfsd_lockd_shutdown();
+	unregister_filesystem(&nfsd_fs_type);
 }
 
-module_init(nfsd_init);
-module_exit(nfsd_exit);
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
+MODULE_LICENSE("GPL");
+module_init(init_nfsd)
+module_exit(exit_nfsd)
diff -urN C7-pre1-nfsd-fix/include/linux/nfsd/interface.h C7-pre1-nfsd-fs/include/linux/nfsd/interface.h
--- C7-pre1-nfsd-fix/include/linux/nfsd/interface.h	Fri Mar  8 02:09:56 2002
+++ C7-pre1-nfsd-fs/include/linux/nfsd/interface.h	Wed Mar 13 02:16:27 2002
@@ -10,17 +10,4 @@
 #ifndef LINUX_NFSD_INTERFACE_H
 #define LINUX_NFSD_INTERFACE_H
 
-#include <linux/config.h>
-
-#ifndef CONFIG_NFSD
-#ifdef CONFIG_MODULES
-
-extern struct nfsd_linkage {
-	long (*do_nfsservctl)(int cmd, void *argp, void *resp);
-	struct module *owner;
-} * nfsd_linkage;
-
-#endif
-#endif
-
 #endif /* LINUX_NFSD_INTERFACE_H */
diff -urN C7-pre1-nfsd-fix/include/linux/nfsd/syscall.h C7-pre1-nfsd-fs/include/linux/nfsd/syscall.h
--- C7-pre1-nfsd-fix/include/linux/nfsd/syscall.h	Wed Mar  6 18:13:07 2002
+++ C7-pre1-nfsd-fs/include/linux/nfsd/syscall.h	Wed Mar 13 02:16:27 2002
@@ -133,7 +133,7 @@
  * Kernel syscall implementation.
  */
 #if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
-extern asmlinkage long	sys_nfsservctl(int, void *, void *);
+extern asmlinkage long	sys_nfsservctl(int, struct nfsctl_arg *, void *);
 #else
 #define sys_nfsservctl		sys_ni_syscall
 #endif

-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html