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 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
|
/*
* linux/fs/nfsd/nfsctl.c
*
* Syscall interface to knfsd.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#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/unistd.h>
#include <linux/slab.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>
#include <asm/uaccess.h>
#include <linux/smp.h>
#include <linux/smp_lock.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_getfh(struct nfsctl_fhparm *, __u8 *);
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
static int initialized;
int exp_procfs_exports(char *buffer, char **start, off_t offset,
int length, int *eof, void *data);
void proc_export_init(void)
{
if (!proc_mkdir("fs/nfs", 0))
return;
create_proc_read_entry("fs/nfs/exports", 0, 0, exp_procfs_exports,NULL);
}
/*
* Initialize nfsd
*/
static void
nfsd_init(void)
{
nfsd_stat_init(); /* Statistics */
nfsd_cache_init(); /* RPC reply cache */
nfsd_export_init(); /* Exports table */
nfsd_lockd_init(); /* lockd->nfsd callbacks */
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_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res)
{
struct sockaddr_in *sin;
struct svc_client *clp;
int err = 0;
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;
exp_readlock();
if (!(clp = exp_getclient(sin)))
err = -EPERM;
else
err = exp_rootfh(clp, 0, 0, data->gd_path, res, data->gd_maxlen);
exp_unlock();
return err;
}
static inline int
nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res)
{
struct sockaddr_in *sin;
struct svc_client *clp;
int err = 0;
struct knfsd_fh fh;
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, &fh, NFS_FHSIZE);
exp_unlock();
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);
}
}
return err;
}
static inline int
nfsctl_getfh(struct nfsctl_fhparm *data, __u8 *res)
{
struct sockaddr_in *sin;
struct svc_client *clp;
int err = 0;
struct knfsd_fh fh;
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, &fh, NFS_FHSIZE);
exp_unlock();
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);
}
}
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)},
};
#define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1)
long
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;
int argsize, respsize;
MOD_INC_USE_COUNT;
lock_kernel ();
if (!initialized)
nfsd_init();
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;
}
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;
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);
unlock_kernel ();
MOD_DEC_USE_COUNT;
return err;
}
#ifdef MODULE
/* New-style module support since 2.1.18 */
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
MODULE_LICENSE("GPL");
struct nfsd_linkage nfsd_linkage_s = {
do_nfsservctl: handle_sys_nfsservctl,
};
/*
* Initialize the module
*/
int
init_module(void)
{
printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
nfsd_linkage = &nfsd_linkage_s;
return 0;
}
/*
* Clean up the mess before unloading the module
*/
void
cleanup_module(void)
{
nfsd_linkage = NULL;
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();
}
#endif
|