/* * linux/fs/nfsd/nfsctl.c * * Syscall interface to knfsd. * * Copyright (C) 1995, 1996 Olaf Kirch */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 "); 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