1/* 2 * linux/fs/nfsd/nfsctl.c 3 * 4 * Syscall interface to knfsd. 5 * 6 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 7 */ 8 9#include <linux/config.h> 10#include <linux/module.h> 11#include <linux/version.h> 12 13#include <linux/linkage.h> 14#include <linux/sched.h> 15#include <linux/errno.h> 16#include <linux/fs.h> 17#include <linux/fcntl.h> 18#include <linux/net.h> 19#include <linux/in.h> 20#include <linux/unistd.h> 21#include <linux/slab.h> 22#include <linux/proc_fs.h> 23#include <linux/seq_file.h> 24 25#include <linux/nfs.h> 26#include <linux/sunrpc/svc.h> 27#include <linux/nfsd/nfsd.h> 28#include <linux/nfsd/cache.h> 29#include <linux/nfsd/xdr.h> 30#include <linux/nfsd/syscall.h> 31 32#include <asm/uaccess.h> 33#include <linux/smp.h> 34#include <linux/smp_lock.h> 35#include <linux/init.h> 36 37static int nfsctl_svc(struct nfsctl_svc *data); 38static int nfsctl_addclient(struct nfsctl_client *data); 39static int nfsctl_delclient(struct nfsctl_client *data); 40static int nfsctl_export(struct nfsctl_export *data); 41static int nfsctl_unexport(struct nfsctl_export *data); 42static int nfsctl_getfh(struct nfsctl_fhparm *, __u8 *); 43static int nfsctl_getfd(struct nfsctl_fdparm *, __u8 *); 44static int nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *); 45#ifdef notyet 46static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); 47#endif 48 49extern struct seq_operations nfs_exports_op; 50static int exports_open(struct inode *inode, struct file *file) 51{ 52 return seq_open(file, &nfs_exports_op); 53} 54static struct file_operations exports_operations = { 55 open: exports_open, 56 read: seq_read, 57 llseek: seq_lseek, 58 release: seq_release, 59}; 60 61void proc_export_init(void) 62{ 63 struct proc_dir_entry *entry; 64 if (!proc_mkdir("fs/nfs", 0)) 65 return; 66 entry = create_proc_entry("fs/nfs/exports", 0, NULL); 67 if (entry) 68 entry->proc_fops = &exports_operations; 69} 70 71static inline int 72nfsctl_svc(struct nfsctl_svc *data) 73{ 74 return nfsd_svc(data->svc_port, data->svc_nthreads); 75} 76 77static inline int 78nfsctl_addclient(struct nfsctl_client *data) 79{ 80 return exp_addclient(data); 81} 82 83static inline int 84nfsctl_delclient(struct nfsctl_client *data) 85{ 86 return exp_delclient(data); 87} 88 89static inline int 90nfsctl_export(struct nfsctl_export *data) 91{ 92 return exp_export(data); 93} 94 95static inline int 96nfsctl_unexport(struct nfsctl_export *data) 97{ 98 return exp_unexport(data); 99} 100 101#ifdef notyet 102static inline int 103nfsctl_ugidupdate(nfs_ugidmap *data) 104{ 105 return -EINVAL; 106} 107#endif 108 109static inline int 110nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res) 111{ 112 struct sockaddr_in *sin; 113 struct svc_client *clp; 114 int err = 0; 115 116 if (data->gd_addr.sa_family != AF_INET) 117 return -EPROTONOSUPPORT; 118 sin = (struct sockaddr_in *)&data->gd_addr; 119 if (data->gd_maxlen > NFS3_FHSIZE) 120 data->gd_maxlen = NFS3_FHSIZE; 121 exp_readlock(); 122 if (!(clp = exp_getclient(sin))) 123 err = -EPERM; 124 else 125 err = exp_rootfh(clp, 0, 0, data->gd_path, res, data->gd_maxlen); 126 exp_unlock(); 127 return err; 128} 129 130static inline int 131nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res) 132{ 133 struct sockaddr_in *sin; 134 struct svc_client *clp; 135 int err = 0; 136 struct knfsd_fh fh; 137 138 if (data->gd_addr.sa_family != AF_INET) 139 return -EPROTONOSUPPORT; 140 if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) 141 return -EINVAL; 142 sin = (struct sockaddr_in *)&data->gd_addr; 143 144 exp_readlock(); 145 if (!(clp = exp_getclient(sin))) 146 err = -EPERM; 147 else 148 err = exp_rootfh(clp, 0, 0, data->gd_path, &fh, NFS_FHSIZE); 149 exp_unlock(); 150 151 if (err == 0) { 152 if (fh.fh_size > NFS_FHSIZE) 153 err = -EINVAL; 154 else { 155 memset(res,0, NFS_FHSIZE); 156 memcpy(res, &fh.fh_base, fh.fh_size); 157 } 158 } 159 160 return err; 161} 162 163static inline int 164nfsctl_getfh(struct nfsctl_fhparm *data, __u8 *res) 165{ 166 struct sockaddr_in *sin; 167 struct svc_client *clp; 168 int err = 0; 169 struct knfsd_fh fh; 170 171 if (data->gf_addr.sa_family != AF_INET) 172 return -EPROTONOSUPPORT; 173 if (data->gf_version < 2 || data->gf_version > NFSSVC_MAXVERS) 174 return -EINVAL; 175 sin = (struct sockaddr_in *)&data->gf_addr; 176 177 exp_readlock(); 178 if (!(clp = exp_getclient(sin))) 179 err = -EPERM; 180 else 181 err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, &fh, NFS_FHSIZE); 182 exp_unlock(); 183 184 if (err == 0) { 185 if (fh.fh_size > NFS_FHSIZE) 186 err = -EINVAL; 187 else { 188 memset(res,0, NFS_FHSIZE); 189 memcpy(res, &fh.fh_base, fh.fh_size); 190 } 191 } 192 193 return err; 194} 195 196#ifdef CONFIG_NFSD 197#define handle_sys_nfsservctl sys_nfsservctl 198#endif 199 200static struct { 201 int argsize, respsize; 202} sizes[] = { 203 /* NFSCTL_SVC */ { sizeof(struct nfsctl_svc), 0 }, 204 /* NFSCTL_ADDCLIENT */ { sizeof(struct nfsctl_client), 0}, 205 /* NFSCTL_DELCLIENT */ { sizeof(struct nfsctl_client), 0}, 206 /* NFSCTL_EXPORT */ { sizeof(struct nfsctl_export), 0}, 207 /* NFSCTL_UNEXPORT */ { sizeof(struct nfsctl_export), 0}, 208 /* NFSCTL_UGIDUPDATE */ { sizeof(struct nfsctl_uidmap), 0}, 209 /* NFSCTL_GETFH */ { sizeof(struct nfsctl_fhparm), NFS_FHSIZE}, 210 /* NFSCTL_GETFD */ { sizeof(struct nfsctl_fdparm), NFS_FHSIZE}, 211 /* NFSCTL_GETFS */ { sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)}, 212}; 213#define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1) 214 215long 216asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp) 217{ 218 struct nfsctl_arg * argp = opaque_argp; 219 union nfsctl_res * resp = opaque_resp; 220 struct nfsctl_arg * arg = NULL; 221 union nfsctl_res * res = NULL; 222 int err; 223 int argsize, respsize; 224 225 lock_kernel (); 226 227 err = -EPERM; 228 if (!capable(CAP_SYS_ADMIN)) { 229 goto done; 230 } 231 err = -EINVAL; 232 if (cmd<0 || cmd > CMD_MAX) 233 goto done; 234 err = -EFAULT; 235 argsize = sizes[cmd].argsize + (int)&((struct nfsctl_arg *)0)->u; 236 respsize = sizes[cmd].respsize; /* maximum */ 237 if (!access_ok(VERIFY_READ, argp, argsize) 238 || (resp && !access_ok(VERIFY_WRITE, resp, respsize))) { 239 goto done; 240 } 241 err = -ENOMEM; /* ??? */ 242 if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) || 243 (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) { 244 goto done; 245 } 246 247 err = -EINVAL; 248 copy_from_user(arg, argp, argsize); 249 if (arg->ca_version != NFSCTL_VERSION) { 250 printk(KERN_WARNING "nfsd: incompatible version in syscall.\n"); 251 goto done; 252 } 253 254 switch(cmd) { 255 case NFSCTL_SVC: 256 err = nfsctl_svc(&arg->ca_svc); 257 break; 258 case NFSCTL_ADDCLIENT: 259 err = nfsctl_addclient(&arg->ca_client); 260 break; 261 case NFSCTL_DELCLIENT: 262 err = nfsctl_delclient(&arg->ca_client); 263 break; 264 case NFSCTL_EXPORT: 265 err = nfsctl_export(&arg->ca_export); 266 break; 267 case NFSCTL_UNEXPORT: 268 err = nfsctl_unexport(&arg->ca_export); 269 break; 270#ifdef notyet 271 case NFSCTL_UGIDUPDATE: 272 err = nfsctl_ugidupdate(&arg->ca_umap); 273 break; 274#endif 275 case NFSCTL_GETFH: 276 err = nfsctl_getfh(&arg->ca_getfh, res->cr_getfh); 277 break; 278 case NFSCTL_GETFD: 279 err = nfsctl_getfd(&arg->ca_getfd, res->cr_getfh); 280 break; 281 case NFSCTL_GETFS: 282 err = nfsctl_getfs(&arg->ca_getfs, &res->cr_getfs); 283 respsize = res->cr_getfs.fh_size+ (int)&((struct knfsd_fh*)0)->fh_base; 284 break; 285 default: 286 err = -EINVAL; 287 } 288 289 if (!err && resp && respsize) 290 copy_to_user(resp, res, respsize); 291 292done: 293 if (arg) 294 kfree(arg); 295 if (res) 296 kfree(res); 297 298 unlock_kernel (); 299 return err; 300} 301 302EXPORT_NO_SYMBOLS; 303MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); 304MODULE_LICENSE("GPL"); 305 306#ifdef MODULE 307struct nfsd_linkage nfsd_linkage_s = { 308 do_nfsservctl: handle_sys_nfsservctl, 309 owner: THIS_MODULE, 310}; 311#endif 312 313/* 314 * Initialize the module 315 */ 316static int __init 317nfsd_init(void) 318{ 319 printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); 320#ifdef MODULE 321 nfsd_linkage = &nfsd_linkage_s; 322#endif 323 nfsd_stat_init(); /* Statistics */ 324 nfsd_cache_init(); /* RPC reply cache */ 325 nfsd_export_init(); /* Exports table */ 326 nfsd_lockd_init(); /* lockd->nfsd callbacks */ 327 proc_export_init(); 328 return 0; 329} 330 331/* 332 * Clean up the mess before unloading the module 333 */ 334static void __exit 335nfsd_exit(void) 336{ 337#ifdef MODULE 338 nfsd_linkage = NULL; 339#endif 340 nfsd_export_shutdown(); 341 nfsd_cache_shutdown(); 342 remove_proc_entry("fs/nfs/exports", NULL); 343 remove_proc_entry("fs/nfs", NULL); 344 nfsd_stat_shutdown(); 345 nfsd_lockd_shutdown(); 346} 347 348module_init(nfsd_init); 349module_exit(nfsd_exit); 350