1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
|
/*
* linux/fs/nfsd/nfsctl.c
*
* Syscall interface to knfsd.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#define NFS_GETFH_NEW
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/linkage.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/version.h>
#include <linux/unistd.h>
#include <linux/malloc.h>
#include <linux/proc_fs.h>
#include <linux/nfs.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/cache.h>
#include <linux/nfsd/xdr.h>
#include <linux/nfsd/syscall.h>
#if LINUX_VERSION_CODE >= 0x020100
#include <asm/uaccess.h>
#else
# define copy_from_user memcpy_fromfs
# define copy_to_user memcpy_tofs
# define access_ok !verify_area
#endif
#include <linux/smp.h>
#include <linux/smp_lock.h>
extern void nfsd_fh_init(void);
extern long sys_call_table[];
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_getfh(struct nfsctl_fhparm *, struct knfs_fh *);
static int nfsctl_getfd(struct nfsctl_fdparm *, struct knfs_fh *);
/* static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); */
static int initialized = 0;
int exp_procfs_exports(char *buffer, char **start, off_t offset,
int length, int *eof, void *data);
void proc_export_init(void)
{
struct proc_dir_entry *nfs_export_ent = NULL;
if (!(nfs_export_ent = create_proc_entry("fs/nfs", S_IFDIR, 0)))
return;
if (!(nfs_export_ent = create_proc_entry("fs/nfs/exports", 0, 0)))
return;
nfs_export_ent->read_proc = exp_procfs_exports;
}
/*
* Initialize nfsd
*/
static void
nfsd_init(void)
{
nfsd_xdr_init(); /* XDR */
nfsd_stat_init(); /* Statistics */
nfsd_cache_init(); /* RPC reply cache */
nfsd_export_init(); /* Exports table */
nfsd_lockd_init(); /* lockd->nfsd callbacks */
nfsd_fh_init(); /* FH table */
proc_export_init();
initialized = 1;
}
static inline int
nfsctl_svc(struct nfsctl_svc *data)
{
return nfsd_svc(data->svc_port, data->svc_nthreads);
}
static inline int
nfsctl_addclient(struct nfsctl_client *data)
{
return exp_addclient(data);
}
static inline int
nfsctl_delclient(struct nfsctl_client *data)
{
return exp_delclient(data);
}
static inline int
nfsctl_export(struct nfsctl_export *data)
{
return exp_export(data);
}
static inline int
nfsctl_unexport(struct nfsctl_export *data)
{
return exp_unexport(data);
}
#ifdef notyet
static inline int
nfsctl_ugidupdate(nfs_ugidmap *data)
{
return -EINVAL;
}
#endif
static inline int
nfsctl_getfd(struct nfsctl_fdparm *data, struct knfs_fh *res)
{
struct sockaddr_in *sin;
struct svc_client *clp;
int err = 0;
if (data->gd_addr.sa_family != AF_INET)
return -EPROTONOSUPPORT;
if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
return -EINVAL;
sin = (struct sockaddr_in *)&data->gd_addr;
exp_readlock();
if (!(clp = exp_getclient(sin)))
err = -EPERM;
else
err = exp_rootfh(clp, 0, 0, data->gd_path, res);
exp_unlock();
return err;
}
static inline int
nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res)
{
struct sockaddr_in *sin;
struct svc_client *clp;
int err = 0;
if (data->gf_addr.sa_family != AF_INET)
return -EPROTONOSUPPORT;
if (data->gf_version < 2 || data->gf_version > NFSSVC_MAXVERS)
return -EINVAL;
sin = (struct sockaddr_in *)&data->gf_addr;
exp_readlock();
if (!(clp = exp_getclient(sin)))
err = -EPERM;
else
err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, res);
exp_unlock();
return err;
}
#ifdef CONFIG_NFSD
#define handle_sys_nfsservctl sys_nfsservctl
#endif
int
asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
{
struct nfsctl_arg * argp = opaque_argp;
union nfsctl_res * resp = opaque_resp;
struct nfsctl_arg * arg = NULL;
union nfsctl_res * res = NULL;
int err;
MOD_INC_USE_COUNT;
lock_kernel ();
if (!initialized)
nfsd_init();
err = -EPERM;
if (!capable(CAP_SYS_ADMIN)) {
goto done;
}
err = -EFAULT;
if (!access_ok(VERIFY_READ, argp, sizeof(*argp))
|| (resp && !access_ok(VERIFY_WRITE, resp, sizeof(*resp)))) {
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, sizeof(*argp));
if (arg->ca_version != NFSCTL_VERSION) {
printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
goto done;
}
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_GETFH:
err = nfsctl_getfh(&arg->ca_getfh, &res->cr_getfh);
break;
case NFSCTL_GETFD:
err = nfsctl_getfd(&arg->ca_getfd, &res->cr_getfh);
break;
default:
err = -EINVAL;
}
if (!err && resp)
copy_to_user(resp, res, sizeof(*resp));
done:
if (arg)
kfree(arg);
if (res)
kfree(res);
unlock_kernel ();
MOD_DEC_USE_COUNT;
return err;
}
#ifdef MODULE
/* New-style module support since 2.1.18 */
#if LINUX_VERSION_CODE >= 131346
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
#endif
extern int (*do_nfsservctl)(int, void *, void *);
/*
* This is called as the fill_inode function when an inode
* is going into (fill = 1) or out of service (fill = 0).
*
* We use it here to make sure the module can't be unloaded
* while a /proc inode is in use.
*/
void nfsd_modcount(struct inode *inode, int fill)
{
if (fill)
MOD_INC_USE_COUNT;
else
MOD_DEC_USE_COUNT;
}
/*
* Initialize the module
*/
int
init_module(void)
{
printk("Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
do_nfsservctl = handle_sys_nfsservctl;
return 0;
}
/*
* Clean up the mess before unloading the module
*/
void
cleanup_module(void)
{
if (MOD_IN_USE) {
printk("nfsd: nfsd busy, remove delayed\n");
return;
}
do_nfsservctl = NULL;
nfsd_export_shutdown();
nfsd_cache_shutdown();
nfsd_fh_free();
remove_proc_entry("fs/nfs/exports", NULL);
remove_proc_entry("fs/nfs", NULL);
nfsd_stat_shutdown();
nfsd_lockd_shutdown();
}
#endif
|