#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>
#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)
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;
-
- if (data->gd_addr.sa_family != AF_INET)
+ struct nfsctl_fdparm data;
+ struct sockaddr_in *sin;
+ struct svc_client *clp;
+ int err = 0;
+ struct knfsd_fh fh;
+ 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;
- 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;
+ 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;
}
- 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;
+ 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;
- 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;
- }
-
- 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)