11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/fs/nfsd/nfsctl.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Syscall interface to knfsd. 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 71da177e4SLinus Torvalds */ 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds #include <linux/module.h> 101da177e4SLinus Torvalds 111da177e4SLinus Torvalds #include <linux/linkage.h> 121da177e4SLinus Torvalds #include <linux/time.h> 131da177e4SLinus Torvalds #include <linux/errno.h> 141da177e4SLinus Torvalds #include <linux/fs.h> 151da177e4SLinus Torvalds #include <linux/fcntl.h> 161da177e4SLinus Torvalds #include <linux/net.h> 171da177e4SLinus Torvalds #include <linux/in.h> 181da177e4SLinus Torvalds #include <linux/syscalls.h> 191da177e4SLinus Torvalds #include <linux/unistd.h> 201da177e4SLinus Torvalds #include <linux/slab.h> 211da177e4SLinus Torvalds #include <linux/proc_fs.h> 221da177e4SLinus Torvalds #include <linux/seq_file.h> 231da177e4SLinus Torvalds #include <linux/pagemap.h> 241da177e4SLinus Torvalds #include <linux/init.h> 2570c3b76cSNeilBrown #include <linux/string.h> 2680212d59SNeilBrown #include <linux/smp_lock.h> 27b41b66d6SNeilBrown #include <linux/ctype.h> 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #include <linux/nfs.h> 301da177e4SLinus Torvalds #include <linux/nfsd_idmap.h> 31b41b66d6SNeilBrown #include <linux/lockd/bind.h> 321da177e4SLinus Torvalds #include <linux/sunrpc/svc.h> 3380212d59SNeilBrown #include <linux/sunrpc/svcsock.h> 341da177e4SLinus Torvalds #include <linux/nfsd/nfsd.h> 351da177e4SLinus Torvalds #include <linux/nfsd/cache.h> 361da177e4SLinus Torvalds #include <linux/nfsd/xdr.h> 371da177e4SLinus Torvalds #include <linux/nfsd/syscall.h> 381da177e4SLinus Torvalds #include <linux/nfsd/interface.h> 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds #include <asm/uaccess.h> 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds /* 431da177e4SLinus Torvalds * We have a single directory with 9 nodes in it. 441da177e4SLinus Torvalds */ 451da177e4SLinus Torvalds enum { 461da177e4SLinus Torvalds NFSD_Root = 1, 471da177e4SLinus Torvalds NFSD_Svc, 481da177e4SLinus Torvalds NFSD_Add, 491da177e4SLinus Torvalds NFSD_Del, 501da177e4SLinus Torvalds NFSD_Export, 511da177e4SLinus Torvalds NFSD_Unexport, 521da177e4SLinus Torvalds NFSD_Getfd, 531da177e4SLinus Torvalds NFSD_Getfs, 541da177e4SLinus Torvalds NFSD_List, 551da177e4SLinus Torvalds NFSD_Fh, 561da177e4SLinus Torvalds NFSD_Threads, 57*eed2965aSGreg Banks NFSD_Pool_Threads, 5870c3b76cSNeilBrown NFSD_Versions, 5980212d59SNeilBrown NFSD_Ports, 6070c3b76cSNeilBrown /* 6170c3b76cSNeilBrown * The below MUST come last. Otherwise we leave a hole in nfsd_files[] 6270c3b76cSNeilBrown * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops 6370c3b76cSNeilBrown */ 6470c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4 651da177e4SLinus Torvalds NFSD_Leasetime, 660964a3d3SNeilBrown NFSD_RecoveryDir, 6770c3b76cSNeilBrown #endif 681da177e4SLinus Torvalds }; 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds /* 711da177e4SLinus Torvalds * write() for these nodes. 721da177e4SLinus Torvalds */ 731da177e4SLinus Torvalds static ssize_t write_svc(struct file *file, char *buf, size_t size); 741da177e4SLinus Torvalds static ssize_t write_add(struct file *file, char *buf, size_t size); 751da177e4SLinus Torvalds static ssize_t write_del(struct file *file, char *buf, size_t size); 761da177e4SLinus Torvalds static ssize_t write_export(struct file *file, char *buf, size_t size); 771da177e4SLinus Torvalds static ssize_t write_unexport(struct file *file, char *buf, size_t size); 781da177e4SLinus Torvalds static ssize_t write_getfd(struct file *file, char *buf, size_t size); 791da177e4SLinus Torvalds static ssize_t write_getfs(struct file *file, char *buf, size_t size); 801da177e4SLinus Torvalds static ssize_t write_filehandle(struct file *file, char *buf, size_t size); 811da177e4SLinus Torvalds static ssize_t write_threads(struct file *file, char *buf, size_t size); 82*eed2965aSGreg Banks static ssize_t write_pool_threads(struct file *file, char *buf, size_t size); 8370c3b76cSNeilBrown static ssize_t write_versions(struct file *file, char *buf, size_t size); 8480212d59SNeilBrown static ssize_t write_ports(struct file *file, char *buf, size_t size); 8570c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4 861da177e4SLinus Torvalds static ssize_t write_leasetime(struct file *file, char *buf, size_t size); 870964a3d3SNeilBrown static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); 8870c3b76cSNeilBrown #endif 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static ssize_t (*write_op[])(struct file *, char *, size_t) = { 911da177e4SLinus Torvalds [NFSD_Svc] = write_svc, 921da177e4SLinus Torvalds [NFSD_Add] = write_add, 931da177e4SLinus Torvalds [NFSD_Del] = write_del, 941da177e4SLinus Torvalds [NFSD_Export] = write_export, 951da177e4SLinus Torvalds [NFSD_Unexport] = write_unexport, 961da177e4SLinus Torvalds [NFSD_Getfd] = write_getfd, 971da177e4SLinus Torvalds [NFSD_Getfs] = write_getfs, 981da177e4SLinus Torvalds [NFSD_Fh] = write_filehandle, 991da177e4SLinus Torvalds [NFSD_Threads] = write_threads, 100*eed2965aSGreg Banks [NFSD_Pool_Threads] = write_pool_threads, 10170c3b76cSNeilBrown [NFSD_Versions] = write_versions, 10280212d59SNeilBrown [NFSD_Ports] = write_ports, 10370c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4 1041da177e4SLinus Torvalds [NFSD_Leasetime] = write_leasetime, 1050964a3d3SNeilBrown [NFSD_RecoveryDir] = write_recoverydir, 10670c3b76cSNeilBrown #endif 1071da177e4SLinus Torvalds }; 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds ino_t ino = file->f_dentry->d_inode->i_ino; 1121da177e4SLinus Torvalds char *data; 1131da177e4SLinus Torvalds ssize_t rv; 1141da177e4SLinus Torvalds 115e8c96f8cSTobias Klauser if (ino >= ARRAY_SIZE(write_op) || !write_op[ino]) 1161da177e4SLinus Torvalds return -EINVAL; 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds data = simple_transaction_get(file, buf, size); 1191da177e4SLinus Torvalds if (IS_ERR(data)) 1201da177e4SLinus Torvalds return PTR_ERR(data); 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds rv = write_op[ino](file, data, size); 1231da177e4SLinus Torvalds if (rv>0) { 1241da177e4SLinus Torvalds simple_transaction_set(file, rv); 1251da177e4SLinus Torvalds rv = size; 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds return rv; 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds 1307390022dSNeilBrown static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos) 1317390022dSNeilBrown { 1327390022dSNeilBrown if (! file->private_data) { 1337390022dSNeilBrown /* An attempt to read a transaction file without writing 1347390022dSNeilBrown * causes a 0-byte write so that the file can return 1357390022dSNeilBrown * state information 1367390022dSNeilBrown */ 1377390022dSNeilBrown ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos); 1387390022dSNeilBrown if (rv < 0) 1397390022dSNeilBrown return rv; 1407390022dSNeilBrown } 1417390022dSNeilBrown return simple_transaction_read(file, buf, size, pos); 1427390022dSNeilBrown } 1437390022dSNeilBrown 1444b6f5d20SArjan van de Ven static const struct file_operations transaction_ops = { 1451da177e4SLinus Torvalds .write = nfsctl_transaction_write, 1467390022dSNeilBrown .read = nfsctl_transaction_read, 1471da177e4SLinus Torvalds .release = simple_transaction_release, 1481da177e4SLinus Torvalds }; 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds extern struct seq_operations nfs_exports_op; 1511da177e4SLinus Torvalds static int exports_open(struct inode *inode, struct file *file) 1521da177e4SLinus Torvalds { 1531da177e4SLinus Torvalds return seq_open(file, &nfs_exports_op); 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds 1564b6f5d20SArjan van de Ven static const struct file_operations exports_operations = { 1571da177e4SLinus Torvalds .open = exports_open, 1581da177e4SLinus Torvalds .read = seq_read, 1591da177e4SLinus Torvalds .llseek = seq_lseek, 1601da177e4SLinus Torvalds .release = seq_release, 1611da177e4SLinus Torvalds }; 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds /*----------------------------------------------------------------------------*/ 1641da177e4SLinus Torvalds /* 1651da177e4SLinus Torvalds * payload - write methods 1661da177e4SLinus Torvalds * If the method has a response, the response should be put in buf, 1671da177e4SLinus Torvalds * and the length returned. Otherwise return 0 or and -error. 1681da177e4SLinus Torvalds */ 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds static ssize_t write_svc(struct file *file, char *buf, size_t size) 1711da177e4SLinus Torvalds { 1721da177e4SLinus Torvalds struct nfsctl_svc *data; 1731da177e4SLinus Torvalds if (size < sizeof(*data)) 1741da177e4SLinus Torvalds return -EINVAL; 1751da177e4SLinus Torvalds data = (struct nfsctl_svc*) buf; 1761da177e4SLinus Torvalds return nfsd_svc(data->svc_port, data->svc_nthreads); 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds static ssize_t write_add(struct file *file, char *buf, size_t size) 1801da177e4SLinus Torvalds { 1811da177e4SLinus Torvalds struct nfsctl_client *data; 1821da177e4SLinus Torvalds if (size < sizeof(*data)) 1831da177e4SLinus Torvalds return -EINVAL; 1841da177e4SLinus Torvalds data = (struct nfsctl_client *)buf; 1851da177e4SLinus Torvalds return exp_addclient(data); 1861da177e4SLinus Torvalds } 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds static ssize_t write_del(struct file *file, char *buf, size_t size) 1891da177e4SLinus Torvalds { 1901da177e4SLinus Torvalds struct nfsctl_client *data; 1911da177e4SLinus Torvalds if (size < sizeof(*data)) 1921da177e4SLinus Torvalds return -EINVAL; 1931da177e4SLinus Torvalds data = (struct nfsctl_client *)buf; 1941da177e4SLinus Torvalds return exp_delclient(data); 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds static ssize_t write_export(struct file *file, char *buf, size_t size) 1981da177e4SLinus Torvalds { 1991da177e4SLinus Torvalds struct nfsctl_export *data; 2001da177e4SLinus Torvalds if (size < sizeof(*data)) 2011da177e4SLinus Torvalds return -EINVAL; 2021da177e4SLinus Torvalds data = (struct nfsctl_export*)buf; 2031da177e4SLinus Torvalds return exp_export(data); 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds static ssize_t write_unexport(struct file *file, char *buf, size_t size) 2071da177e4SLinus Torvalds { 2081da177e4SLinus Torvalds struct nfsctl_export *data; 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds if (size < sizeof(*data)) 2111da177e4SLinus Torvalds return -EINVAL; 2121da177e4SLinus Torvalds data = (struct nfsctl_export*)buf; 2131da177e4SLinus Torvalds return exp_unexport(data); 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds static ssize_t write_getfs(struct file *file, char *buf, size_t size) 2171da177e4SLinus Torvalds { 2181da177e4SLinus Torvalds struct nfsctl_fsparm *data; 2191da177e4SLinus Torvalds struct sockaddr_in *sin; 2201da177e4SLinus Torvalds struct auth_domain *clp; 2211da177e4SLinus Torvalds int err = 0; 2221da177e4SLinus Torvalds struct knfsd_fh *res; 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds if (size < sizeof(*data)) 2251da177e4SLinus Torvalds return -EINVAL; 2261da177e4SLinus Torvalds data = (struct nfsctl_fsparm*)buf; 2271da177e4SLinus Torvalds err = -EPROTONOSUPPORT; 2281da177e4SLinus Torvalds if (data->gd_addr.sa_family != AF_INET) 2291da177e4SLinus Torvalds goto out; 2301da177e4SLinus Torvalds sin = (struct sockaddr_in *)&data->gd_addr; 2311da177e4SLinus Torvalds if (data->gd_maxlen > NFS3_FHSIZE) 2321da177e4SLinus Torvalds data->gd_maxlen = NFS3_FHSIZE; 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds res = (struct knfsd_fh*)buf; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds exp_readlock(); 2371da177e4SLinus Torvalds if (!(clp = auth_unix_lookup(sin->sin_addr))) 2381da177e4SLinus Torvalds err = -EPERM; 2391da177e4SLinus Torvalds else { 2401da177e4SLinus Torvalds err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen); 2411da177e4SLinus Torvalds auth_domain_put(clp); 2421da177e4SLinus Torvalds } 2431da177e4SLinus Torvalds exp_readunlock(); 2441da177e4SLinus Torvalds if (err == 0) 2451da177e4SLinus Torvalds err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base; 2461da177e4SLinus Torvalds out: 2471da177e4SLinus Torvalds return err; 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds static ssize_t write_getfd(struct file *file, char *buf, size_t size) 2511da177e4SLinus Torvalds { 2521da177e4SLinus Torvalds struct nfsctl_fdparm *data; 2531da177e4SLinus Torvalds struct sockaddr_in *sin; 2541da177e4SLinus Torvalds struct auth_domain *clp; 2551da177e4SLinus Torvalds int err = 0; 2561da177e4SLinus Torvalds struct knfsd_fh fh; 2571da177e4SLinus Torvalds char *res; 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds if (size < sizeof(*data)) 2601da177e4SLinus Torvalds return -EINVAL; 2611da177e4SLinus Torvalds data = (struct nfsctl_fdparm*)buf; 2621da177e4SLinus Torvalds err = -EPROTONOSUPPORT; 2631da177e4SLinus Torvalds if (data->gd_addr.sa_family != AF_INET) 2641da177e4SLinus Torvalds goto out; 2651da177e4SLinus Torvalds err = -EINVAL; 2661da177e4SLinus Torvalds if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) 2671da177e4SLinus Torvalds goto out; 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds res = buf; 2701da177e4SLinus Torvalds sin = (struct sockaddr_in *)&data->gd_addr; 2711da177e4SLinus Torvalds exp_readlock(); 2721da177e4SLinus Torvalds if (!(clp = auth_unix_lookup(sin->sin_addr))) 2731da177e4SLinus Torvalds err = -EPERM; 2741da177e4SLinus Torvalds else { 2751da177e4SLinus Torvalds err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE); 2761da177e4SLinus Torvalds auth_domain_put(clp); 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds exp_readunlock(); 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds if (err == 0) { 2811da177e4SLinus Torvalds memset(res,0, NFS_FHSIZE); 2821da177e4SLinus Torvalds memcpy(res, &fh.fh_base, fh.fh_size); 2831da177e4SLinus Torvalds err = NFS_FHSIZE; 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds out: 2861da177e4SLinus Torvalds return err; 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds static ssize_t write_filehandle(struct file *file, char *buf, size_t size) 2901da177e4SLinus Torvalds { 2911da177e4SLinus Torvalds /* request is: 2921da177e4SLinus Torvalds * domain path maxsize 2931da177e4SLinus Torvalds * response is 2941da177e4SLinus Torvalds * filehandle 2951da177e4SLinus Torvalds * 2961da177e4SLinus Torvalds * qword quoting is used, so filehandle will be \x.... 2971da177e4SLinus Torvalds */ 2981da177e4SLinus Torvalds char *dname, *path; 2991da177e4SLinus Torvalds int maxsize; 3001da177e4SLinus Torvalds char *mesg = buf; 3011da177e4SLinus Torvalds int len; 3021da177e4SLinus Torvalds struct auth_domain *dom; 3031da177e4SLinus Torvalds struct knfsd_fh fh; 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds if (buf[size-1] != '\n') 3061da177e4SLinus Torvalds return -EINVAL; 3071da177e4SLinus Torvalds buf[size-1] = 0; 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds dname = mesg; 3101da177e4SLinus Torvalds len = qword_get(&mesg, dname, size); 3111da177e4SLinus Torvalds if (len <= 0) return -EINVAL; 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds path = dname+len+1; 3141da177e4SLinus Torvalds len = qword_get(&mesg, path, size); 3151da177e4SLinus Torvalds if (len <= 0) return -EINVAL; 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds len = get_int(&mesg, &maxsize); 3181da177e4SLinus Torvalds if (len) 3191da177e4SLinus Torvalds return len; 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvalds if (maxsize < NFS_FHSIZE) 3221da177e4SLinus Torvalds return -EINVAL; 3231da177e4SLinus Torvalds if (maxsize > NFS3_FHSIZE) 3241da177e4SLinus Torvalds maxsize = NFS3_FHSIZE; 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds if (qword_get(&mesg, mesg, size)>0) 3271da177e4SLinus Torvalds return -EINVAL; 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds /* we have all the words, they are in buf.. */ 3301da177e4SLinus Torvalds dom = unix_domain_find(dname); 3311da177e4SLinus Torvalds if (!dom) 3321da177e4SLinus Torvalds return -ENOMEM; 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds len = exp_rootfh(dom, path, &fh, maxsize); 3351da177e4SLinus Torvalds auth_domain_put(dom); 3361da177e4SLinus Torvalds if (len) 3371da177e4SLinus Torvalds return len; 3381da177e4SLinus Torvalds 3391da177e4SLinus Torvalds mesg = buf; len = SIMPLE_TRANSACTION_LIMIT; 3401da177e4SLinus Torvalds qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size); 3411da177e4SLinus Torvalds mesg[-1] = '\n'; 3421da177e4SLinus Torvalds return mesg - buf; 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds extern int nfsd_nrthreads(void); 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds static ssize_t write_threads(struct file *file, char *buf, size_t size) 3481da177e4SLinus Torvalds { 3491da177e4SLinus Torvalds /* if size > 0, look for a number of threads and call nfsd_svc 3501da177e4SLinus Torvalds * then write out number of threads as reply 3511da177e4SLinus Torvalds */ 3521da177e4SLinus Torvalds char *mesg = buf; 3531da177e4SLinus Torvalds int rv; 3541da177e4SLinus Torvalds if (size > 0) { 3551da177e4SLinus Torvalds int newthreads; 3561da177e4SLinus Torvalds rv = get_int(&mesg, &newthreads); 3571da177e4SLinus Torvalds if (rv) 3581da177e4SLinus Torvalds return rv; 3591da177e4SLinus Torvalds if (newthreads <0) 3601da177e4SLinus Torvalds return -EINVAL; 3611da177e4SLinus Torvalds rv = nfsd_svc(2049, newthreads); 3621da177e4SLinus Torvalds if (rv) 3631da177e4SLinus Torvalds return rv; 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds sprintf(buf, "%d\n", nfsd_nrthreads()); 3661da177e4SLinus Torvalds return strlen(buf); 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds 369*eed2965aSGreg Banks extern int nfsd_nrpools(void); 370*eed2965aSGreg Banks extern int nfsd_get_nrthreads(int n, int *); 371*eed2965aSGreg Banks extern int nfsd_set_nrthreads(int n, int *); 372*eed2965aSGreg Banks 373*eed2965aSGreg Banks static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) 374*eed2965aSGreg Banks { 375*eed2965aSGreg Banks /* if size > 0, look for an array of number of threads per node 376*eed2965aSGreg Banks * and apply them then write out number of threads per node as reply 377*eed2965aSGreg Banks */ 378*eed2965aSGreg Banks char *mesg = buf; 379*eed2965aSGreg Banks int i; 380*eed2965aSGreg Banks int rv; 381*eed2965aSGreg Banks int len; 382*eed2965aSGreg Banks int npools = nfsd_nrpools(); 383*eed2965aSGreg Banks int *nthreads; 384*eed2965aSGreg Banks 385*eed2965aSGreg Banks if (npools == 0) { 386*eed2965aSGreg Banks /* 387*eed2965aSGreg Banks * NFS is shut down. The admin can start it by 388*eed2965aSGreg Banks * writing to the threads file but NOT the pool_threads 389*eed2965aSGreg Banks * file, sorry. Report zero threads. 390*eed2965aSGreg Banks */ 391*eed2965aSGreg Banks strcpy(buf, "0\n"); 392*eed2965aSGreg Banks return strlen(buf); 393*eed2965aSGreg Banks } 394*eed2965aSGreg Banks 395*eed2965aSGreg Banks nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); 396*eed2965aSGreg Banks if (nthreads == NULL) 397*eed2965aSGreg Banks return -ENOMEM; 398*eed2965aSGreg Banks 399*eed2965aSGreg Banks if (size > 0) { 400*eed2965aSGreg Banks for (i = 0; i < npools; i++) { 401*eed2965aSGreg Banks rv = get_int(&mesg, &nthreads[i]); 402*eed2965aSGreg Banks if (rv == -ENOENT) 403*eed2965aSGreg Banks break; /* fewer numbers than pools */ 404*eed2965aSGreg Banks if (rv) 405*eed2965aSGreg Banks goto out_free; /* syntax error */ 406*eed2965aSGreg Banks rv = -EINVAL; 407*eed2965aSGreg Banks if (nthreads[i] < 0) 408*eed2965aSGreg Banks goto out_free; 409*eed2965aSGreg Banks } 410*eed2965aSGreg Banks rv = nfsd_set_nrthreads(i, nthreads); 411*eed2965aSGreg Banks if (rv) 412*eed2965aSGreg Banks goto out_free; 413*eed2965aSGreg Banks } 414*eed2965aSGreg Banks 415*eed2965aSGreg Banks rv = nfsd_get_nrthreads(npools, nthreads); 416*eed2965aSGreg Banks if (rv) 417*eed2965aSGreg Banks goto out_free; 418*eed2965aSGreg Banks 419*eed2965aSGreg Banks mesg = buf; 420*eed2965aSGreg Banks size = SIMPLE_TRANSACTION_LIMIT; 421*eed2965aSGreg Banks for (i = 0; i < npools && size > 0; i++) { 422*eed2965aSGreg Banks snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' ')); 423*eed2965aSGreg Banks len = strlen(mesg); 424*eed2965aSGreg Banks size -= len; 425*eed2965aSGreg Banks mesg += len; 426*eed2965aSGreg Banks } 427*eed2965aSGreg Banks 428*eed2965aSGreg Banks return (mesg-buf); 429*eed2965aSGreg Banks 430*eed2965aSGreg Banks out_free: 431*eed2965aSGreg Banks kfree(nthreads); 432*eed2965aSGreg Banks return rv; 433*eed2965aSGreg Banks } 434*eed2965aSGreg Banks 43570c3b76cSNeilBrown static ssize_t write_versions(struct file *file, char *buf, size_t size) 43670c3b76cSNeilBrown { 43770c3b76cSNeilBrown /* 43870c3b76cSNeilBrown * Format: 43970c3b76cSNeilBrown * [-/+]vers [-/+]vers ... 44070c3b76cSNeilBrown */ 44170c3b76cSNeilBrown char *mesg = buf; 44270c3b76cSNeilBrown char *vers, sign; 44370c3b76cSNeilBrown int len, num; 44470c3b76cSNeilBrown ssize_t tlen = 0; 44570c3b76cSNeilBrown char *sep; 44670c3b76cSNeilBrown 44770c3b76cSNeilBrown if (size>0) { 44870c3b76cSNeilBrown if (nfsd_serv) 4496658d3a7SNeilBrown /* Cannot change versions without updating 4506658d3a7SNeilBrown * nfsd_serv->sv_xdrsize, and reallocing 4516658d3a7SNeilBrown * rq_argp and rq_resp 4526658d3a7SNeilBrown */ 45370c3b76cSNeilBrown return -EBUSY; 45470c3b76cSNeilBrown if (buf[size-1] != '\n') 45570c3b76cSNeilBrown return -EINVAL; 45670c3b76cSNeilBrown buf[size-1] = 0; 45770c3b76cSNeilBrown 45870c3b76cSNeilBrown vers = mesg; 45970c3b76cSNeilBrown len = qword_get(&mesg, vers, size); 46070c3b76cSNeilBrown if (len <= 0) return -EINVAL; 46170c3b76cSNeilBrown do { 46270c3b76cSNeilBrown sign = *vers; 46370c3b76cSNeilBrown if (sign == '+' || sign == '-') 46470c3b76cSNeilBrown num = simple_strtol((vers+1), NULL, 0); 46570c3b76cSNeilBrown else 46670c3b76cSNeilBrown num = simple_strtol(vers, NULL, 0); 46770c3b76cSNeilBrown switch(num) { 46870c3b76cSNeilBrown case 2: 46970c3b76cSNeilBrown case 3: 47070c3b76cSNeilBrown case 4: 4716658d3a7SNeilBrown nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET); 47270c3b76cSNeilBrown break; 47370c3b76cSNeilBrown default: 47470c3b76cSNeilBrown return -EINVAL; 47570c3b76cSNeilBrown } 47670c3b76cSNeilBrown vers += len + 1; 47770c3b76cSNeilBrown tlen += len; 47870c3b76cSNeilBrown } while ((len = qword_get(&mesg, vers, size)) > 0); 47970c3b76cSNeilBrown /* If all get turned off, turn them back on, as 48070c3b76cSNeilBrown * having no versions is BAD 48170c3b76cSNeilBrown */ 4826658d3a7SNeilBrown nfsd_reset_versions(); 48370c3b76cSNeilBrown } 48470c3b76cSNeilBrown /* Now write current state into reply buffer */ 48570c3b76cSNeilBrown len = 0; 48670c3b76cSNeilBrown sep = ""; 48770c3b76cSNeilBrown for (num=2 ; num <= 4 ; num++) 4886658d3a7SNeilBrown if (nfsd_vers(num, NFSD_AVAIL)) { 48970c3b76cSNeilBrown len += sprintf(buf+len, "%s%c%d", sep, 4906658d3a7SNeilBrown nfsd_vers(num, NFSD_TEST)?'+':'-', 49170c3b76cSNeilBrown num); 49270c3b76cSNeilBrown sep = " "; 49370c3b76cSNeilBrown } 49470c3b76cSNeilBrown len += sprintf(buf+len, "\n"); 49570c3b76cSNeilBrown return len; 49670c3b76cSNeilBrown } 49770c3b76cSNeilBrown 49880212d59SNeilBrown static ssize_t write_ports(struct file *file, char *buf, size_t size) 49980212d59SNeilBrown { 500b41b66d6SNeilBrown if (size == 0) { 50180212d59SNeilBrown int len = 0; 50280212d59SNeilBrown lock_kernel(); 50380212d59SNeilBrown if (nfsd_serv) 504b41b66d6SNeilBrown len = svc_sock_names(buf, nfsd_serv, NULL); 50580212d59SNeilBrown unlock_kernel(); 50680212d59SNeilBrown return len; 50780212d59SNeilBrown } 508b41b66d6SNeilBrown /* Either a single 'fd' number is written, in which 509b41b66d6SNeilBrown * case it must be for a socket of a supported family/protocol, 510b41b66d6SNeilBrown * and we use it as an nfsd socket, or 511b41b66d6SNeilBrown * A '-' followed by the 'name' of a socket in which case 512b41b66d6SNeilBrown * we close the socket. 513b41b66d6SNeilBrown */ 514b41b66d6SNeilBrown if (isdigit(buf[0])) { 515b41b66d6SNeilBrown char *mesg = buf; 516b41b66d6SNeilBrown int fd; 517b41b66d6SNeilBrown int err; 518b41b66d6SNeilBrown err = get_int(&mesg, &fd); 519b41b66d6SNeilBrown if (err) 520b41b66d6SNeilBrown return -EINVAL; 521b41b66d6SNeilBrown if (fd < 0) 522b41b66d6SNeilBrown return -EINVAL; 523b41b66d6SNeilBrown err = nfsd_create_serv(); 524b41b66d6SNeilBrown if (!err) { 525b41b66d6SNeilBrown int proto = 0; 5263dfb4210SNeilBrown err = lockd_up(proto); 5273dfb4210SNeilBrown if (!err) { 528b41b66d6SNeilBrown err = svc_addsock(nfsd_serv, fd, buf, &proto); 5293dfb4210SNeilBrown if (err) 5303dfb4210SNeilBrown lockd_down(); 5313dfb4210SNeilBrown } 532b41b66d6SNeilBrown /* Decrease the count, but don't shutdown the 533b41b66d6SNeilBrown * the service 534b41b66d6SNeilBrown */ 535b41b66d6SNeilBrown nfsd_serv->sv_nrthreads--; 536b41b66d6SNeilBrown } 537b41b66d6SNeilBrown return err; 538b41b66d6SNeilBrown } 539b41b66d6SNeilBrown if (buf[0] == '-') { 540b41b66d6SNeilBrown char *toclose = kstrdup(buf+1, GFP_KERNEL); 541b41b66d6SNeilBrown int len = 0; 542b41b66d6SNeilBrown if (!toclose) 543b41b66d6SNeilBrown return -ENOMEM; 544b41b66d6SNeilBrown lock_kernel(); 545b41b66d6SNeilBrown if (nfsd_serv) 546b41b66d6SNeilBrown len = svc_sock_names(buf, nfsd_serv, toclose); 547b41b66d6SNeilBrown unlock_kernel(); 548b41b66d6SNeilBrown kfree(toclose); 549b41b66d6SNeilBrown return len; 550b41b66d6SNeilBrown } 551b41b66d6SNeilBrown return -EINVAL; 552b41b66d6SNeilBrown } 55380212d59SNeilBrown 55470c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4 5551da177e4SLinus Torvalds extern time_t nfs4_leasetime(void); 5561da177e4SLinus Torvalds 5571da177e4SLinus Torvalds static ssize_t write_leasetime(struct file *file, char *buf, size_t size) 5581da177e4SLinus Torvalds { 5591da177e4SLinus Torvalds /* if size > 10 seconds, call 5601da177e4SLinus Torvalds * nfs4_reset_lease() then write out the new lease (seconds) as reply 5611da177e4SLinus Torvalds */ 5621da177e4SLinus Torvalds char *mesg = buf; 5631da177e4SLinus Torvalds int rv; 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds if (size > 0) { 5661da177e4SLinus Torvalds int lease; 5671da177e4SLinus Torvalds rv = get_int(&mesg, &lease); 5681da177e4SLinus Torvalds if (rv) 5691da177e4SLinus Torvalds return rv; 5701da177e4SLinus Torvalds if (lease < 10 || lease > 3600) 5711da177e4SLinus Torvalds return -EINVAL; 5721da177e4SLinus Torvalds nfs4_reset_lease(lease); 5731da177e4SLinus Torvalds } 5741da177e4SLinus Torvalds sprintf(buf, "%ld\n", nfs4_lease_time()); 5751da177e4SLinus Torvalds return strlen(buf); 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds 5780964a3d3SNeilBrown static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) 5790964a3d3SNeilBrown { 5800964a3d3SNeilBrown char *mesg = buf; 5810964a3d3SNeilBrown char *recdir; 5820964a3d3SNeilBrown int len, status; 5830964a3d3SNeilBrown 5840964a3d3SNeilBrown if (size > PATH_MAX || buf[size-1] != '\n') 5850964a3d3SNeilBrown return -EINVAL; 5860964a3d3SNeilBrown buf[size-1] = 0; 5870964a3d3SNeilBrown 5880964a3d3SNeilBrown recdir = mesg; 5890964a3d3SNeilBrown len = qword_get(&mesg, recdir, size); 5900964a3d3SNeilBrown if (len <= 0) 5910964a3d3SNeilBrown return -EINVAL; 5920964a3d3SNeilBrown 5930964a3d3SNeilBrown status = nfs4_reset_recoverydir(recdir); 5940964a3d3SNeilBrown return strlen(buf); 5950964a3d3SNeilBrown } 59670c3b76cSNeilBrown #endif 5970964a3d3SNeilBrown 5981da177e4SLinus Torvalds /*----------------------------------------------------------------------------*/ 5991da177e4SLinus Torvalds /* 6001da177e4SLinus Torvalds * populating the filesystem. 6011da177e4SLinus Torvalds */ 6021da177e4SLinus Torvalds 6031da177e4SLinus Torvalds static int nfsd_fill_super(struct super_block * sb, void * data, int silent) 6041da177e4SLinus Torvalds { 6051da177e4SLinus Torvalds static struct tree_descr nfsd_files[] = { 6061da177e4SLinus Torvalds [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR}, 6071da177e4SLinus Torvalds [NFSD_Add] = {".add", &transaction_ops, S_IWUSR}, 6081da177e4SLinus Torvalds [NFSD_Del] = {".del", &transaction_ops, S_IWUSR}, 6091da177e4SLinus Torvalds [NFSD_Export] = {".export", &transaction_ops, S_IWUSR}, 6101da177e4SLinus Torvalds [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR}, 6111da177e4SLinus Torvalds [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR}, 6121da177e4SLinus Torvalds [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR}, 6131da177e4SLinus Torvalds [NFSD_List] = {"exports", &exports_operations, S_IRUGO}, 6141da177e4SLinus Torvalds [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, 6151da177e4SLinus Torvalds [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, 616*eed2965aSGreg Banks [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, 61770c3b76cSNeilBrown [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, 61880212d59SNeilBrown [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, 6191da177e4SLinus Torvalds #ifdef CONFIG_NFSD_V4 6201da177e4SLinus Torvalds [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, 6210964a3d3SNeilBrown [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, 6221da177e4SLinus Torvalds #endif 6231da177e4SLinus Torvalds /* last one */ {""} 6241da177e4SLinus Torvalds }; 6251da177e4SLinus Torvalds return simple_fill_super(sb, 0x6e667364, nfsd_files); 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 628454e2398SDavid Howells static int nfsd_get_sb(struct file_system_type *fs_type, 629454e2398SDavid Howells int flags, const char *dev_name, void *data, struct vfsmount *mnt) 6301da177e4SLinus Torvalds { 631454e2398SDavid Howells return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt); 6321da177e4SLinus Torvalds } 6331da177e4SLinus Torvalds 6341da177e4SLinus Torvalds static struct file_system_type nfsd_fs_type = { 6351da177e4SLinus Torvalds .owner = THIS_MODULE, 6361da177e4SLinus Torvalds .name = "nfsd", 6371da177e4SLinus Torvalds .get_sb = nfsd_get_sb, 6381da177e4SLinus Torvalds .kill_sb = kill_litter_super, 6391da177e4SLinus Torvalds }; 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds static int __init init_nfsd(void) 6421da177e4SLinus Torvalds { 6431da177e4SLinus Torvalds int retval; 6441da177e4SLinus Torvalds printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); 6451da177e4SLinus Torvalds 6461da177e4SLinus Torvalds nfsd_stat_init(); /* Statistics */ 6471da177e4SLinus Torvalds nfsd_cache_init(); /* RPC reply cache */ 6481da177e4SLinus Torvalds nfsd_export_init(); /* Exports table */ 6491da177e4SLinus Torvalds nfsd_lockd_init(); /* lockd->nfsd callbacks */ 650ac4d8ff2SNeilBrown nfs4_state_init(); /* NFSv4 locking state */ 6511da177e4SLinus Torvalds nfsd_idmap_init(); /* Name to ID mapping */ 6521da177e4SLinus Torvalds if (proc_mkdir("fs/nfs", NULL)) { 6531da177e4SLinus Torvalds struct proc_dir_entry *entry; 6541da177e4SLinus Torvalds entry = create_proc_entry("fs/nfs/exports", 0, NULL); 6551da177e4SLinus Torvalds if (entry) 6561da177e4SLinus Torvalds entry->proc_fops = &exports_operations; 6571da177e4SLinus Torvalds } 6581da177e4SLinus Torvalds retval = register_filesystem(&nfsd_fs_type); 6591da177e4SLinus Torvalds if (retval) { 6601da177e4SLinus Torvalds nfsd_export_shutdown(); 6611da177e4SLinus Torvalds nfsd_cache_shutdown(); 6621da177e4SLinus Torvalds remove_proc_entry("fs/nfs/exports", NULL); 6631da177e4SLinus Torvalds remove_proc_entry("fs/nfs", NULL); 6641da177e4SLinus Torvalds nfsd_stat_shutdown(); 6651da177e4SLinus Torvalds nfsd_lockd_shutdown(); 6661da177e4SLinus Torvalds } 6671da177e4SLinus Torvalds return retval; 6681da177e4SLinus Torvalds } 6691da177e4SLinus Torvalds 6701da177e4SLinus Torvalds static void __exit exit_nfsd(void) 6711da177e4SLinus Torvalds { 6721da177e4SLinus Torvalds nfsd_export_shutdown(); 6731da177e4SLinus Torvalds nfsd_cache_shutdown(); 6741da177e4SLinus Torvalds remove_proc_entry("fs/nfs/exports", NULL); 6751da177e4SLinus Torvalds remove_proc_entry("fs/nfs", NULL); 6761da177e4SLinus Torvalds nfsd_stat_shutdown(); 6771da177e4SLinus Torvalds nfsd_lockd_shutdown(); 6781da177e4SLinus Torvalds nfsd_idmap_shutdown(); 6791da177e4SLinus Torvalds unregister_filesystem(&nfsd_fs_type); 6801da177e4SLinus Torvalds } 6811da177e4SLinus Torvalds 6821da177e4SLinus Torvalds MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); 6831da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 6841da177e4SLinus Torvalds module_init(init_nfsd) 6851da177e4SLinus Torvalds module_exit(exit_nfsd) 686