Commit 063b009f authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] nfsd as filesystem

* 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.
parent edb1bea5
...@@ -22,6 +22,12 @@ else ...@@ -22,6 +22,12 @@ else
obj-y += noquot.o obj-y += noquot.o
endif endif
ifneq ($(CONFIG_NFSD),n)
ifneq ($(CONFIG_NFSD),)
obj-y += nfsctl.o
endif
endif
subdir-$(CONFIG_PROC_FS) += proc subdir-$(CONFIG_PROC_FS) += proc
subdir-y += partitions subdir-y += partitions
subdir-y += driverfs subdir-y += driverfs
......
...@@ -1252,6 +1252,7 @@ kmem_cache_t *dquot_cachep; ...@@ -1252,6 +1252,7 @@ kmem_cache_t *dquot_cachep;
/* SLAB cache for buffer_head structures */ /* SLAB cache for buffer_head structures */
kmem_cache_t *bh_cachep; kmem_cache_t *bh_cachep;
EXPORT_SYMBOL(bh_cachep); EXPORT_SYMBOL(bh_cachep);
EXPORT_SYMBOL(d_genocide);
extern void bdev_cache_init(void); extern void bdev_cache_init(void);
extern void cdev_cache_init(void); extern void cdev_cache_init(void);
......
...@@ -3,39 +3,10 @@ ...@@ -3,39 +3,10 @@
* *
* Copyright (C) 1991, 1992 Linus Torvalds * 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> * Code will move here from fs/super.c and yes, it will be fs type handling
#include <linux/time.h> * stuff.
#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 */
/*
* 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;
}
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/pagemap.h>
#include <linux/nfs.h> #include <linux/nfs.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
...@@ -30,20 +31,96 @@ ...@@ -30,20 +31,96 @@
#include <linux/nfsd/syscall.h> #include <linux/nfsd/syscall.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/smp.h>
#include <linux/smp_lock.h> /*
#include <linux/init.h> * We have a single directory with 8 nodes in it.
*/
static int nfsctl_svc(struct nfsctl_svc *data); enum {
static int nfsctl_addclient(struct nfsctl_client *data); NFSD_Root = 1,
static int nfsctl_delclient(struct nfsctl_client *data); NFSD_Svc,
static int nfsctl_export(struct nfsctl_export *data); NFSD_Add,
static int nfsctl_unexport(struct nfsctl_export *data); NFSD_Del,
static int nfsctl_getfd(struct nfsctl_fdparm *, __u8 *); NFSD_Export,
static int nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *); NFSD_Unexport,
#ifdef notyet NFSD_Getfd,
static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); NFSD_Getfs,
#endif 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; extern struct seq_operations nfs_exports_op;
static int exports_open(struct inode *inode, struct file *file) static int exports_open(struct inode *inode, struct file *file)
...@@ -57,254 +134,277 @@ static struct file_operations exports_operations = { ...@@ -57,254 +134,277 @@ static struct file_operations exports_operations = {
release: seq_release, release: seq_release,
}; };
void proc_export_init(void) /*
{ * Description of fs contents.
struct proc_dir_entry *entry; */
if (!proc_mkdir("fs/nfs", 0)) static struct { char *name; struct file_operations *ops; int mode; } files[] = {
return; [NFSD_Svc] = {"svc", &writer_ops, S_IWUSR},
entry = create_proc_entry("fs/nfs/exports", 0, NULL); [NFSD_Add] = {"add", &writer_ops, S_IWUSR},
if (entry) [NFSD_Del] = {"del", &writer_ops, S_IWUSR},
entry->proc_fops = &exports_operations; [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) /*
{ * payload - write methods
return nfsd_svc(data->svc_port, data->svc_nthreads); */
}
static inline int static ssize_t write_svc(struct file *file, const char *buf, size_t size)
nfsctl_addclient(struct nfsctl_client *data)
{ {
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 static ssize_t write_add(struct file *file, const char *buf, size_t size)
nfsctl_delclient(struct nfsctl_client *data)
{ {
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 static ssize_t write_del(struct file *file, const char *buf, size_t size)
nfsctl_export(struct nfsctl_export *data)
{ {
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 static ssize_t write_export(struct file *file, const char *buf, size_t size)
nfsctl_unexport(struct nfsctl_export *data)
{ {
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 ssize_t write_unexport(struct file *file, const char *buf, size_t size)
static inline int
nfsctl_ugidupdate(nfs_ugidmap *data)
{ {
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 static ssize_t write_getfs(struct file *file, const char *buf, size_t size)
nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res)
{ {
struct sockaddr_in *sin; struct nfsctl_fsparm data;
struct svc_client *clp; struct sockaddr_in *sin;
int err = 0; 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; return -EPROTONOSUPPORT;
sin = (struct sockaddr_in *)&data->gd_addr; sin = (struct sockaddr_in *)&data.gd_addr;
if (data->gd_maxlen > NFS3_FHSIZE) if (data.gd_maxlen > NFS3_FHSIZE)
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(); exp_readlock();
if (!(clp = exp_getclient(sin))) if (!(clp = exp_getclient(sin)))
err = -EPERM; err = -EPERM;
else 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(); 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; return err;
} }
static inline int static ssize_t write_getfd(struct file *file, const char *buf, size_t size)
nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res)
{ {
struct sockaddr_in *sin; struct nfsctl_fdparm data;
struct svc_client *clp; struct sockaddr_in *sin;
int err = 0; struct svc_client *clp;
struct knfsd_fh fh; int err = 0;
struct knfsd_fh fh;
if (data->gd_addr.sa_family != AF_INET) char *res;
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; return -EPROTONOSUPPORT;
if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) if (data.gd_version < 2 || data.gd_version > NFSSVC_MAXVERS)
return -EINVAL; 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(); exp_readlock();
if (!(clp = exp_getclient(sin))) if (!(clp = exp_getclient(sin)))
err = -EPERM; err = -EPERM;
else else
err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE); err = exp_rootfh(clp, data.gd_path, &fh, NFS_FHSIZE);
exp_readunlock(); exp_readunlock();
if (err == 0) { down(&file->f_dentry->d_inode->i_sem);
if (fh.fh_size > NFS_FHSIZE) if (file->private_data)
err = -EINVAL; err = -EINVAL;
else { if (!err && fh.fh_size > NFS_FHSIZE)
memset(res,0, NFS_FHSIZE); err = -EINVAL;
memcpy(res, &fh.fh_base, fh.fh_size); 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; return err;
} }
#ifdef CONFIG_NFSD /*----------------------------------------------------------------------------*/
#define handle_sys_nfsservctl sys_nfsservctl /*
#endif * populating the filesystem.
*/
static struct {
int argsize, respsize; static struct super_operations s_ops = {
} sizes[] = { statfs: simple_statfs,
/* 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)},
}; };
#define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1)
long static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
{ {
struct nfsctl_arg * argp = opaque_argp; struct inode *inode;
union nfsctl_res * resp = opaque_resp; struct dentry *root;
struct nfsctl_arg * arg = NULL; struct dentry *dentry;
union nfsctl_res * res = NULL; int i;
int err;
int argsize, respsize; sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = 0x6e667364;
err = -EPERM; sb->s_op = &s_ops;
if (!capable(CAP_SYS_ADMIN)) {
goto done; inode = new_inode(sb);
} if (!inode)
err = -EINVAL; return -ENOMEM;
if (cmd<0 || cmd > CMD_MAX) inode->i_mode = S_IFDIR | 0755;
goto done; inode->i_uid = inode->i_gid = 0;
err = -EFAULT; inode->i_blksize = PAGE_CACHE_SIZE;
argsize = sizes[cmd].argsize + (int)&((struct nfsctl_arg *)0)->u; inode->i_blocks = 0;
respsize = sizes[cmd].respsize; /* maximum */ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
if (!access_ok(VERIFY_READ, argp, argsize) inode->i_op = &simple_dir_inode_operations;
|| (resp && !access_ok(VERIFY_WRITE, resp, respsize))) { inode->i_fop = &simple_dir_operations;
goto done; root = d_alloc_root(inode);
if (!root) {
iput(inode);
return -ENOMEM;
} }
err = -ENOMEM; /* ??? */ for (i = NFSD_Svc; i <= NFSD_List; i++) {
if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) || struct qstr name;
(resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) { name.name = files[i].name;
goto done; name.len = strlen(name.name);
} name.hash = full_name_hash(name.name, name.len);
dentry = d_alloc(root, &name);
err = -EINVAL; if (!dentry)
copy_from_user(arg, argp, argsize); goto out;
if (arg->ca_version != NFSCTL_VERSION) { inode = new_inode(sb);
printk(KERN_WARNING "nfsd: incompatible version in syscall.\n"); if (!inode)
goto done; 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;
switch(cmd) { out:
case NFSCTL_SVC: d_genocide(root);
err = nfsctl_svc(&arg->ca_svc); dput(root);
break; return -ENOMEM;
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;
}
if (!err && resp && respsize)
copy_to_user(resp, res, respsize);
done:
if (arg)
kfree(arg);
if (res)
kfree(res);
return err;
} }
EXPORT_NO_SYMBOLS; static struct super_block *nfsd_get_sb(struct file_system_type *fs_type,
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); int flags, char *dev_name, void *data)
MODULE_LICENSE("GPL"); {
return get_sb_single(fs_type, flags, data, nfsd_fill_super);
}
#ifdef MODULE static struct file_system_type nfsd_fs_type = {
struct nfsd_linkage nfsd_linkage_s = { owner: THIS_MODULE,
do_nfsservctl: handle_sys_nfsservctl, name: "nfsd",
owner: THIS_MODULE, get_sb: nfsd_get_sb,
kill_sb: kill_litter_super,
}; };
#endif
/* static int __init init_nfsd(void)
* Initialize the module
*/
static int __init
nfsd_init(void)
{ {
printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); 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_stat_init(); /* Statistics */
nfsd_cache_init(); /* RPC reply cache */ nfsd_cache_init(); /* RPC reply cache */
nfsd_export_init(); /* Exports table */ nfsd_export_init(); /* Exports table */
nfsd_lockd_init(); /* lockd->nfsd callbacks */ 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; return 0;
} }
/* static void __exit exit_nfsd(void)
* Clean up the mess before unloading the module
*/
static void __exit
nfsd_exit(void)
{ {
#ifdef MODULE
nfsd_linkage = NULL;
#endif
nfsd_export_shutdown(); nfsd_export_shutdown();
nfsd_cache_shutdown(); nfsd_cache_shutdown();
remove_proc_entry("fs/nfs/exports", NULL); remove_proc_entry("fs/nfs/exports", NULL);
remove_proc_entry("fs/nfs", NULL); remove_proc_entry("fs/nfs", NULL);
nfsd_stat_shutdown(); nfsd_stat_shutdown();
nfsd_lockd_shutdown(); nfsd_lockd_shutdown();
unregister_filesystem(&nfsd_fs_type);
} }
module_init(nfsd_init); EXPORT_NO_SYMBOLS;
module_exit(nfsd_exit); MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
MODULE_LICENSE("GPL");
module_init(init_nfsd)
module_exit(exit_nfsd)
...@@ -10,17 +10,4 @@ ...@@ -10,17 +10,4 @@
#ifndef LINUX_NFSD_INTERFACE_H #ifndef LINUX_NFSD_INTERFACE_H
#define 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 */ #endif /* LINUX_NFSD_INTERFACE_H */
...@@ -133,7 +133,7 @@ union nfsctl_res { ...@@ -133,7 +133,7 @@ union nfsctl_res {
* Kernel syscall implementation. * Kernel syscall implementation.
*/ */
#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE) #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 #else
#define sys_nfsservctl sys_ni_syscall #define sys_nfsservctl sys_ni_syscall
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment