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 391da177e4SLinus Torvalds #include <asm/uaccess.h> 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds /* 421da177e4SLinus Torvalds * We have a single directory with 9 nodes in it. 431da177e4SLinus Torvalds */ 441da177e4SLinus Torvalds enum { 451da177e4SLinus Torvalds NFSD_Root = 1, 461da177e4SLinus Torvalds NFSD_Svc, 471da177e4SLinus Torvalds NFSD_Add, 481da177e4SLinus Torvalds NFSD_Del, 491da177e4SLinus Torvalds NFSD_Export, 501da177e4SLinus Torvalds NFSD_Unexport, 511da177e4SLinus Torvalds NFSD_Getfd, 521da177e4SLinus Torvalds NFSD_Getfs, 531da177e4SLinus Torvalds NFSD_List, 541da177e4SLinus Torvalds NFSD_Fh, 551da177e4SLinus Torvalds NFSD_Threads, 56eed2965aSGreg Banks NFSD_Pool_Threads, 5770c3b76cSNeilBrown NFSD_Versions, 5880212d59SNeilBrown NFSD_Ports, 59596bbe53SNeilBrown NFSD_MaxBlkSize, 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); 82eed2965aSGreg 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); 85596bbe53SNeilBrown static ssize_t write_maxblksize(struct file *file, char *buf, size_t size); 8670c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4 871da177e4SLinus Torvalds static ssize_t write_leasetime(struct file *file, char *buf, size_t size); 880964a3d3SNeilBrown static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); 8970c3b76cSNeilBrown #endif 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds static ssize_t (*write_op[])(struct file *, char *, size_t) = { 921da177e4SLinus Torvalds [NFSD_Svc] = write_svc, 931da177e4SLinus Torvalds [NFSD_Add] = write_add, 941da177e4SLinus Torvalds [NFSD_Del] = write_del, 951da177e4SLinus Torvalds [NFSD_Export] = write_export, 961da177e4SLinus Torvalds [NFSD_Unexport] = write_unexport, 971da177e4SLinus Torvalds [NFSD_Getfd] = write_getfd, 981da177e4SLinus Torvalds [NFSD_Getfs] = write_getfs, 991da177e4SLinus Torvalds [NFSD_Fh] = write_filehandle, 1001da177e4SLinus Torvalds [NFSD_Threads] = write_threads, 101eed2965aSGreg Banks [NFSD_Pool_Threads] = write_pool_threads, 10270c3b76cSNeilBrown [NFSD_Versions] = write_versions, 10380212d59SNeilBrown [NFSD_Ports] = write_ports, 104596bbe53SNeilBrown [NFSD_MaxBlkSize] = write_maxblksize, 10570c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4 1061da177e4SLinus Torvalds [NFSD_Leasetime] = write_leasetime, 1070964a3d3SNeilBrown [NFSD_RecoveryDir] = write_recoverydir, 10870c3b76cSNeilBrown #endif 1091da177e4SLinus Torvalds }; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) 1121da177e4SLinus Torvalds { 1137eaa36e2SJosef "Jeff" Sipek ino_t ino = file->f_path.dentry->d_inode->i_ino; 1141da177e4SLinus Torvalds char *data; 1151da177e4SLinus Torvalds ssize_t rv; 1161da177e4SLinus Torvalds 117e8c96f8cSTobias Klauser if (ino >= ARRAY_SIZE(write_op) || !write_op[ino]) 1181da177e4SLinus Torvalds return -EINVAL; 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds data = simple_transaction_get(file, buf, size); 1211da177e4SLinus Torvalds if (IS_ERR(data)) 1221da177e4SLinus Torvalds return PTR_ERR(data); 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds rv = write_op[ino](file, data, size); 1258971a101SNeilBrown if (rv >= 0) { 1261da177e4SLinus Torvalds simple_transaction_set(file, rv); 1271da177e4SLinus Torvalds rv = size; 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds return rv; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 1327390022dSNeilBrown static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos) 1337390022dSNeilBrown { 1347390022dSNeilBrown if (! file->private_data) { 1357390022dSNeilBrown /* An attempt to read a transaction file without writing 1367390022dSNeilBrown * causes a 0-byte write so that the file can return 1377390022dSNeilBrown * state information 1387390022dSNeilBrown */ 1397390022dSNeilBrown ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos); 1407390022dSNeilBrown if (rv < 0) 1417390022dSNeilBrown return rv; 1427390022dSNeilBrown } 1437390022dSNeilBrown return simple_transaction_read(file, buf, size, pos); 1447390022dSNeilBrown } 1457390022dSNeilBrown 1464b6f5d20SArjan van de Ven static const struct file_operations transaction_ops = { 1471da177e4SLinus Torvalds .write = nfsctl_transaction_write, 1487390022dSNeilBrown .read = nfsctl_transaction_read, 1491da177e4SLinus Torvalds .release = simple_transaction_release, 1501da177e4SLinus Torvalds }; 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds extern struct seq_operations nfs_exports_op; 1531da177e4SLinus Torvalds static int exports_open(struct inode *inode, struct file *file) 1541da177e4SLinus Torvalds { 1551da177e4SLinus Torvalds return seq_open(file, &nfs_exports_op); 1561da177e4SLinus Torvalds } 1571da177e4SLinus Torvalds 1584b6f5d20SArjan van de Ven static const struct file_operations exports_operations = { 1591da177e4SLinus Torvalds .open = exports_open, 1601da177e4SLinus Torvalds .read = seq_read, 1611da177e4SLinus Torvalds .llseek = seq_lseek, 1621da177e4SLinus Torvalds .release = seq_release, 1631da177e4SLinus Torvalds }; 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds /*----------------------------------------------------------------------------*/ 1661da177e4SLinus Torvalds /* 1671da177e4SLinus Torvalds * payload - write methods 1681da177e4SLinus Torvalds * If the method has a response, the response should be put in buf, 1691da177e4SLinus Torvalds * and the length returned. Otherwise return 0 or and -error. 1701da177e4SLinus Torvalds */ 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds static ssize_t write_svc(struct file *file, char *buf, size_t size) 1731da177e4SLinus Torvalds { 1741da177e4SLinus Torvalds struct nfsctl_svc *data; 1751da177e4SLinus Torvalds if (size < sizeof(*data)) 1761da177e4SLinus Torvalds return -EINVAL; 1771da177e4SLinus Torvalds data = (struct nfsctl_svc*) buf; 1781da177e4SLinus Torvalds return nfsd_svc(data->svc_port, data->svc_nthreads); 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds static ssize_t write_add(struct file *file, char *buf, size_t size) 1821da177e4SLinus Torvalds { 1831da177e4SLinus Torvalds struct nfsctl_client *data; 1841da177e4SLinus Torvalds if (size < sizeof(*data)) 1851da177e4SLinus Torvalds return -EINVAL; 1861da177e4SLinus Torvalds data = (struct nfsctl_client *)buf; 1871da177e4SLinus Torvalds return exp_addclient(data); 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds static ssize_t write_del(struct file *file, char *buf, size_t size) 1911da177e4SLinus Torvalds { 1921da177e4SLinus Torvalds struct nfsctl_client *data; 1931da177e4SLinus Torvalds if (size < sizeof(*data)) 1941da177e4SLinus Torvalds return -EINVAL; 1951da177e4SLinus Torvalds data = (struct nfsctl_client *)buf; 1961da177e4SLinus Torvalds return exp_delclient(data); 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds static ssize_t write_export(struct file *file, char *buf, size_t size) 2001da177e4SLinus Torvalds { 2011da177e4SLinus Torvalds struct nfsctl_export *data; 2021da177e4SLinus Torvalds if (size < sizeof(*data)) 2031da177e4SLinus Torvalds return -EINVAL; 2041da177e4SLinus Torvalds data = (struct nfsctl_export*)buf; 2051da177e4SLinus Torvalds return exp_export(data); 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds static ssize_t write_unexport(struct file *file, char *buf, size_t size) 2091da177e4SLinus Torvalds { 2101da177e4SLinus Torvalds struct nfsctl_export *data; 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds if (size < sizeof(*data)) 2131da177e4SLinus Torvalds return -EINVAL; 2141da177e4SLinus Torvalds data = (struct nfsctl_export*)buf; 2151da177e4SLinus Torvalds return exp_unexport(data); 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds static ssize_t write_getfs(struct file *file, char *buf, size_t size) 2191da177e4SLinus Torvalds { 2201da177e4SLinus Torvalds struct nfsctl_fsparm *data; 2211da177e4SLinus Torvalds struct sockaddr_in *sin; 2221da177e4SLinus Torvalds struct auth_domain *clp; 2231da177e4SLinus Torvalds int err = 0; 2241da177e4SLinus Torvalds struct knfsd_fh *res; 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds if (size < sizeof(*data)) 2271da177e4SLinus Torvalds return -EINVAL; 2281da177e4SLinus Torvalds data = (struct nfsctl_fsparm*)buf; 2291da177e4SLinus Torvalds err = -EPROTONOSUPPORT; 2301da177e4SLinus Torvalds if (data->gd_addr.sa_family != AF_INET) 2311da177e4SLinus Torvalds goto out; 2321da177e4SLinus Torvalds sin = (struct sockaddr_in *)&data->gd_addr; 2331da177e4SLinus Torvalds if (data->gd_maxlen > NFS3_FHSIZE) 2341da177e4SLinus Torvalds data->gd_maxlen = NFS3_FHSIZE; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds res = (struct knfsd_fh*)buf; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds exp_readlock(); 2391da177e4SLinus Torvalds if (!(clp = auth_unix_lookup(sin->sin_addr))) 2401da177e4SLinus Torvalds err = -EPERM; 2411da177e4SLinus Torvalds else { 2421da177e4SLinus Torvalds err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen); 2431da177e4SLinus Torvalds auth_domain_put(clp); 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds exp_readunlock(); 2461da177e4SLinus Torvalds if (err == 0) 24712127498SAndrew Morton err = res->fh_size + offsetof(struct knfsd_fh, fh_base); 2481da177e4SLinus Torvalds out: 2491da177e4SLinus Torvalds return err; 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds static ssize_t write_getfd(struct file *file, char *buf, size_t size) 2531da177e4SLinus Torvalds { 2541da177e4SLinus Torvalds struct nfsctl_fdparm *data; 2551da177e4SLinus Torvalds struct sockaddr_in *sin; 2561da177e4SLinus Torvalds struct auth_domain *clp; 2571da177e4SLinus Torvalds int err = 0; 2581da177e4SLinus Torvalds struct knfsd_fh fh; 2591da177e4SLinus Torvalds char *res; 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds if (size < sizeof(*data)) 2621da177e4SLinus Torvalds return -EINVAL; 2631da177e4SLinus Torvalds data = (struct nfsctl_fdparm*)buf; 2641da177e4SLinus Torvalds err = -EPROTONOSUPPORT; 2651da177e4SLinus Torvalds if (data->gd_addr.sa_family != AF_INET) 2661da177e4SLinus Torvalds goto out; 2671da177e4SLinus Torvalds err = -EINVAL; 2681da177e4SLinus Torvalds if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) 2691da177e4SLinus Torvalds goto out; 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds res = buf; 2721da177e4SLinus Torvalds sin = (struct sockaddr_in *)&data->gd_addr; 2731da177e4SLinus Torvalds exp_readlock(); 2741da177e4SLinus Torvalds if (!(clp = auth_unix_lookup(sin->sin_addr))) 2751da177e4SLinus Torvalds err = -EPERM; 2761da177e4SLinus Torvalds else { 2771da177e4SLinus Torvalds err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE); 2781da177e4SLinus Torvalds auth_domain_put(clp); 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds exp_readunlock(); 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds if (err == 0) { 2831da177e4SLinus Torvalds memset(res,0, NFS_FHSIZE); 2841da177e4SLinus Torvalds memcpy(res, &fh.fh_base, fh.fh_size); 2851da177e4SLinus Torvalds err = NFS_FHSIZE; 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds out: 2881da177e4SLinus Torvalds return err; 2891da177e4SLinus Torvalds } 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds static ssize_t write_filehandle(struct file *file, char *buf, size_t size) 2921da177e4SLinus Torvalds { 2931da177e4SLinus Torvalds /* request is: 2941da177e4SLinus Torvalds * domain path maxsize 2951da177e4SLinus Torvalds * response is 2961da177e4SLinus Torvalds * filehandle 2971da177e4SLinus Torvalds * 2981da177e4SLinus Torvalds * qword quoting is used, so filehandle will be \x.... 2991da177e4SLinus Torvalds */ 3001da177e4SLinus Torvalds char *dname, *path; 301246d95baSAndrew Morton int uninitialized_var(maxsize); 3021da177e4SLinus Torvalds char *mesg = buf; 3031da177e4SLinus Torvalds int len; 3041da177e4SLinus Torvalds struct auth_domain *dom; 3051da177e4SLinus Torvalds struct knfsd_fh fh; 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds if (buf[size-1] != '\n') 3081da177e4SLinus Torvalds return -EINVAL; 3091da177e4SLinus Torvalds buf[size-1] = 0; 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds dname = mesg; 3121da177e4SLinus Torvalds len = qword_get(&mesg, dname, size); 3131da177e4SLinus Torvalds if (len <= 0) return -EINVAL; 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds path = dname+len+1; 3161da177e4SLinus Torvalds len = qword_get(&mesg, path, size); 3171da177e4SLinus Torvalds if (len <= 0) return -EINVAL; 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds len = get_int(&mesg, &maxsize); 3201da177e4SLinus Torvalds if (len) 3211da177e4SLinus Torvalds return len; 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds if (maxsize < NFS_FHSIZE) 3241da177e4SLinus Torvalds return -EINVAL; 3251da177e4SLinus Torvalds if (maxsize > NFS3_FHSIZE) 3261da177e4SLinus Torvalds maxsize = NFS3_FHSIZE; 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds if (qword_get(&mesg, mesg, size)>0) 3291da177e4SLinus Torvalds return -EINVAL; 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds /* we have all the words, they are in buf.. */ 3321da177e4SLinus Torvalds dom = unix_domain_find(dname); 3331da177e4SLinus Torvalds if (!dom) 3341da177e4SLinus Torvalds return -ENOMEM; 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds len = exp_rootfh(dom, path, &fh, maxsize); 3371da177e4SLinus Torvalds auth_domain_put(dom); 3381da177e4SLinus Torvalds if (len) 3391da177e4SLinus Torvalds return len; 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds mesg = buf; len = SIMPLE_TRANSACTION_LIMIT; 3421da177e4SLinus Torvalds qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size); 3431da177e4SLinus Torvalds mesg[-1] = '\n'; 3441da177e4SLinus Torvalds return mesg - buf; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds extern int nfsd_nrthreads(void); 3481da177e4SLinus Torvalds 3491da177e4SLinus Torvalds static ssize_t write_threads(struct file *file, char *buf, size_t size) 3501da177e4SLinus Torvalds { 3511da177e4SLinus Torvalds /* if size > 0, look for a number of threads and call nfsd_svc 3521da177e4SLinus Torvalds * then write out number of threads as reply 3531da177e4SLinus Torvalds */ 3541da177e4SLinus Torvalds char *mesg = buf; 3551da177e4SLinus Torvalds int rv; 3561da177e4SLinus Torvalds if (size > 0) { 3571da177e4SLinus Torvalds int newthreads; 3581da177e4SLinus Torvalds rv = get_int(&mesg, &newthreads); 3591da177e4SLinus Torvalds if (rv) 3601da177e4SLinus Torvalds return rv; 3611da177e4SLinus Torvalds if (newthreads <0) 3621da177e4SLinus Torvalds return -EINVAL; 3631da177e4SLinus Torvalds rv = nfsd_svc(2049, newthreads); 3641da177e4SLinus Torvalds if (rv) 3651da177e4SLinus Torvalds return rv; 3661da177e4SLinus Torvalds } 3671da177e4SLinus Torvalds sprintf(buf, "%d\n", nfsd_nrthreads()); 3681da177e4SLinus Torvalds return strlen(buf); 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 371eed2965aSGreg Banks extern int nfsd_nrpools(void); 372eed2965aSGreg Banks extern int nfsd_get_nrthreads(int n, int *); 373eed2965aSGreg Banks extern int nfsd_set_nrthreads(int n, int *); 374eed2965aSGreg Banks 375eed2965aSGreg Banks static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) 376eed2965aSGreg Banks { 377eed2965aSGreg Banks /* if size > 0, look for an array of number of threads per node 378eed2965aSGreg Banks * and apply them then write out number of threads per node as reply 379eed2965aSGreg Banks */ 380eed2965aSGreg Banks char *mesg = buf; 381eed2965aSGreg Banks int i; 382eed2965aSGreg Banks int rv; 383eed2965aSGreg Banks int len; 384eed2965aSGreg Banks int npools = nfsd_nrpools(); 385eed2965aSGreg Banks int *nthreads; 386eed2965aSGreg Banks 387eed2965aSGreg Banks if (npools == 0) { 388eed2965aSGreg Banks /* 389eed2965aSGreg Banks * NFS is shut down. The admin can start it by 390eed2965aSGreg Banks * writing to the threads file but NOT the pool_threads 391eed2965aSGreg Banks * file, sorry. Report zero threads. 392eed2965aSGreg Banks */ 393eed2965aSGreg Banks strcpy(buf, "0\n"); 394eed2965aSGreg Banks return strlen(buf); 395eed2965aSGreg Banks } 396eed2965aSGreg Banks 397eed2965aSGreg Banks nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); 398eed2965aSGreg Banks if (nthreads == NULL) 399eed2965aSGreg Banks return -ENOMEM; 400eed2965aSGreg Banks 401eed2965aSGreg Banks if (size > 0) { 402eed2965aSGreg Banks for (i = 0; i < npools; i++) { 403eed2965aSGreg Banks rv = get_int(&mesg, &nthreads[i]); 404eed2965aSGreg Banks if (rv == -ENOENT) 405eed2965aSGreg Banks break; /* fewer numbers than pools */ 406eed2965aSGreg Banks if (rv) 407eed2965aSGreg Banks goto out_free; /* syntax error */ 408eed2965aSGreg Banks rv = -EINVAL; 409eed2965aSGreg Banks if (nthreads[i] < 0) 410eed2965aSGreg Banks goto out_free; 411eed2965aSGreg Banks } 412eed2965aSGreg Banks rv = nfsd_set_nrthreads(i, nthreads); 413eed2965aSGreg Banks if (rv) 414eed2965aSGreg Banks goto out_free; 415eed2965aSGreg Banks } 416eed2965aSGreg Banks 417eed2965aSGreg Banks rv = nfsd_get_nrthreads(npools, nthreads); 418eed2965aSGreg Banks if (rv) 419eed2965aSGreg Banks goto out_free; 420eed2965aSGreg Banks 421eed2965aSGreg Banks mesg = buf; 422eed2965aSGreg Banks size = SIMPLE_TRANSACTION_LIMIT; 423eed2965aSGreg Banks for (i = 0; i < npools && size > 0; i++) { 424eed2965aSGreg Banks snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' ')); 425eed2965aSGreg Banks len = strlen(mesg); 426eed2965aSGreg Banks size -= len; 427eed2965aSGreg Banks mesg += len; 428eed2965aSGreg Banks } 429eed2965aSGreg Banks 430eed2965aSGreg Banks return (mesg-buf); 431eed2965aSGreg Banks 432eed2965aSGreg Banks out_free: 433eed2965aSGreg Banks kfree(nthreads); 434eed2965aSGreg Banks return rv; 435eed2965aSGreg Banks } 436eed2965aSGreg Banks 43770c3b76cSNeilBrown static ssize_t write_versions(struct file *file, char *buf, size_t size) 43870c3b76cSNeilBrown { 43970c3b76cSNeilBrown /* 44070c3b76cSNeilBrown * Format: 44170c3b76cSNeilBrown * [-/+]vers [-/+]vers ... 44270c3b76cSNeilBrown */ 44370c3b76cSNeilBrown char *mesg = buf; 44470c3b76cSNeilBrown char *vers, sign; 44570c3b76cSNeilBrown int len, num; 44670c3b76cSNeilBrown ssize_t tlen = 0; 44770c3b76cSNeilBrown char *sep; 44870c3b76cSNeilBrown 44970c3b76cSNeilBrown if (size>0) { 45070c3b76cSNeilBrown if (nfsd_serv) 4516658d3a7SNeilBrown /* Cannot change versions without updating 4526658d3a7SNeilBrown * nfsd_serv->sv_xdrsize, and reallocing 4536658d3a7SNeilBrown * rq_argp and rq_resp 4546658d3a7SNeilBrown */ 45570c3b76cSNeilBrown return -EBUSY; 45670c3b76cSNeilBrown if (buf[size-1] != '\n') 45770c3b76cSNeilBrown return -EINVAL; 45870c3b76cSNeilBrown buf[size-1] = 0; 45970c3b76cSNeilBrown 46070c3b76cSNeilBrown vers = mesg; 46170c3b76cSNeilBrown len = qword_get(&mesg, vers, size); 46270c3b76cSNeilBrown if (len <= 0) return -EINVAL; 46370c3b76cSNeilBrown do { 46470c3b76cSNeilBrown sign = *vers; 46570c3b76cSNeilBrown if (sign == '+' || sign == '-') 46670c3b76cSNeilBrown num = simple_strtol((vers+1), NULL, 0); 46770c3b76cSNeilBrown else 46870c3b76cSNeilBrown num = simple_strtol(vers, NULL, 0); 46970c3b76cSNeilBrown switch(num) { 47070c3b76cSNeilBrown case 2: 47170c3b76cSNeilBrown case 3: 47270c3b76cSNeilBrown case 4: 4736658d3a7SNeilBrown nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET); 47470c3b76cSNeilBrown break; 47570c3b76cSNeilBrown default: 47670c3b76cSNeilBrown return -EINVAL; 47770c3b76cSNeilBrown } 47870c3b76cSNeilBrown vers += len + 1; 47970c3b76cSNeilBrown tlen += len; 48070c3b76cSNeilBrown } while ((len = qword_get(&mesg, vers, size)) > 0); 48170c3b76cSNeilBrown /* If all get turned off, turn them back on, as 48270c3b76cSNeilBrown * having no versions is BAD 48370c3b76cSNeilBrown */ 4846658d3a7SNeilBrown nfsd_reset_versions(); 48570c3b76cSNeilBrown } 48670c3b76cSNeilBrown /* Now write current state into reply buffer */ 48770c3b76cSNeilBrown len = 0; 48870c3b76cSNeilBrown sep = ""; 48970c3b76cSNeilBrown for (num=2 ; num <= 4 ; num++) 4906658d3a7SNeilBrown if (nfsd_vers(num, NFSD_AVAIL)) { 49170c3b76cSNeilBrown len += sprintf(buf+len, "%s%c%d", sep, 4926658d3a7SNeilBrown nfsd_vers(num, NFSD_TEST)?'+':'-', 49370c3b76cSNeilBrown num); 49470c3b76cSNeilBrown sep = " "; 49570c3b76cSNeilBrown } 49670c3b76cSNeilBrown len += sprintf(buf+len, "\n"); 49770c3b76cSNeilBrown return len; 49870c3b76cSNeilBrown } 49970c3b76cSNeilBrown 50080212d59SNeilBrown static ssize_t write_ports(struct file *file, char *buf, size_t size) 50180212d59SNeilBrown { 502b41b66d6SNeilBrown if (size == 0) { 50380212d59SNeilBrown int len = 0; 50480212d59SNeilBrown lock_kernel(); 50580212d59SNeilBrown if (nfsd_serv) 506b41b66d6SNeilBrown len = svc_sock_names(buf, nfsd_serv, NULL); 50780212d59SNeilBrown unlock_kernel(); 50880212d59SNeilBrown return len; 50980212d59SNeilBrown } 510b41b66d6SNeilBrown /* Either a single 'fd' number is written, in which 511b41b66d6SNeilBrown * case it must be for a socket of a supported family/protocol, 512b41b66d6SNeilBrown * and we use it as an nfsd socket, or 513b41b66d6SNeilBrown * A '-' followed by the 'name' of a socket in which case 514b41b66d6SNeilBrown * we close the socket. 515b41b66d6SNeilBrown */ 516b41b66d6SNeilBrown if (isdigit(buf[0])) { 517b41b66d6SNeilBrown char *mesg = buf; 518b41b66d6SNeilBrown int fd; 519b41b66d6SNeilBrown int err; 520b41b66d6SNeilBrown err = get_int(&mesg, &fd); 521b41b66d6SNeilBrown if (err) 522b41b66d6SNeilBrown return -EINVAL; 523b41b66d6SNeilBrown if (fd < 0) 524b41b66d6SNeilBrown return -EINVAL; 525b41b66d6SNeilBrown err = nfsd_create_serv(); 526b41b66d6SNeilBrown if (!err) { 527b41b66d6SNeilBrown int proto = 0; 528b41b66d6SNeilBrown err = svc_addsock(nfsd_serv, fd, buf, &proto); 5295680c446SNeilBrown if (err >= 0) { 5305680c446SNeilBrown err = lockd_up(proto); 5315680c446SNeilBrown if (err < 0) 5325680c446SNeilBrown svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf); 5333dfb4210SNeilBrown } 534b41b66d6SNeilBrown /* Decrease the count, but don't shutdown the 535b41b66d6SNeilBrown * the service 536b41b66d6SNeilBrown */ 537cda9e0cdSNeilBrown lock_kernel(); 538b41b66d6SNeilBrown nfsd_serv->sv_nrthreads--; 539cda9e0cdSNeilBrown unlock_kernel(); 540b41b66d6SNeilBrown } 5415680c446SNeilBrown return err < 0 ? err : 0; 542b41b66d6SNeilBrown } 543b41b66d6SNeilBrown if (buf[0] == '-') { 544b41b66d6SNeilBrown char *toclose = kstrdup(buf+1, GFP_KERNEL); 545b41b66d6SNeilBrown int len = 0; 546b41b66d6SNeilBrown if (!toclose) 547b41b66d6SNeilBrown return -ENOMEM; 548b41b66d6SNeilBrown lock_kernel(); 549b41b66d6SNeilBrown if (nfsd_serv) 550b41b66d6SNeilBrown len = svc_sock_names(buf, nfsd_serv, toclose); 551b41b66d6SNeilBrown unlock_kernel(); 55237a03472SNeilBrown if (len >= 0) 55337a03472SNeilBrown lockd_down(); 554b41b66d6SNeilBrown kfree(toclose); 555b41b66d6SNeilBrown return len; 556b41b66d6SNeilBrown } 557b41b66d6SNeilBrown return -EINVAL; 558b41b66d6SNeilBrown } 55980212d59SNeilBrown 560596bbe53SNeilBrown int nfsd_max_blksize; 561596bbe53SNeilBrown 562596bbe53SNeilBrown static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) 563596bbe53SNeilBrown { 564596bbe53SNeilBrown char *mesg = buf; 565596bbe53SNeilBrown if (size > 0) { 566596bbe53SNeilBrown int bsize; 567596bbe53SNeilBrown int rv = get_int(&mesg, &bsize); 568596bbe53SNeilBrown if (rv) 569596bbe53SNeilBrown return rv; 570596bbe53SNeilBrown /* force bsize into allowed range and 571596bbe53SNeilBrown * required alignment. 572596bbe53SNeilBrown */ 573596bbe53SNeilBrown if (bsize < 1024) 574596bbe53SNeilBrown bsize = 1024; 575596bbe53SNeilBrown if (bsize > NFSSVC_MAXBLKSIZE) 576596bbe53SNeilBrown bsize = NFSSVC_MAXBLKSIZE; 577596bbe53SNeilBrown bsize &= ~(1024-1); 578596bbe53SNeilBrown lock_kernel(); 579596bbe53SNeilBrown if (nfsd_serv && nfsd_serv->sv_nrthreads) { 580596bbe53SNeilBrown unlock_kernel(); 581596bbe53SNeilBrown return -EBUSY; 582596bbe53SNeilBrown } 583596bbe53SNeilBrown nfsd_max_blksize = bsize; 584596bbe53SNeilBrown unlock_kernel(); 585596bbe53SNeilBrown } 586596bbe53SNeilBrown return sprintf(buf, "%d\n", nfsd_max_blksize); 587596bbe53SNeilBrown } 588596bbe53SNeilBrown 58970c3b76cSNeilBrown #ifdef CONFIG_NFSD_V4 5901da177e4SLinus Torvalds extern time_t nfs4_leasetime(void); 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds static ssize_t write_leasetime(struct file *file, char *buf, size_t size) 5931da177e4SLinus Torvalds { 5941da177e4SLinus Torvalds /* if size > 10 seconds, call 5951da177e4SLinus Torvalds * nfs4_reset_lease() then write out the new lease (seconds) as reply 5961da177e4SLinus Torvalds */ 5971da177e4SLinus Torvalds char *mesg = buf; 5981da177e4SLinus Torvalds int rv; 5991da177e4SLinus Torvalds 6001da177e4SLinus Torvalds if (size > 0) { 6011da177e4SLinus Torvalds int lease; 6021da177e4SLinus Torvalds rv = get_int(&mesg, &lease); 6031da177e4SLinus Torvalds if (rv) 6041da177e4SLinus Torvalds return rv; 6051da177e4SLinus Torvalds if (lease < 10 || lease > 3600) 6061da177e4SLinus Torvalds return -EINVAL; 6071da177e4SLinus Torvalds nfs4_reset_lease(lease); 6081da177e4SLinus Torvalds } 6091da177e4SLinus Torvalds sprintf(buf, "%ld\n", nfs4_lease_time()); 6101da177e4SLinus Torvalds return strlen(buf); 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds 6130964a3d3SNeilBrown static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) 6140964a3d3SNeilBrown { 6150964a3d3SNeilBrown char *mesg = buf; 6160964a3d3SNeilBrown char *recdir; 6170964a3d3SNeilBrown int len, status; 6180964a3d3SNeilBrown 6190964a3d3SNeilBrown if (size > PATH_MAX || buf[size-1] != '\n') 6200964a3d3SNeilBrown return -EINVAL; 6210964a3d3SNeilBrown buf[size-1] = 0; 6220964a3d3SNeilBrown 6230964a3d3SNeilBrown recdir = mesg; 6240964a3d3SNeilBrown len = qword_get(&mesg, recdir, size); 6250964a3d3SNeilBrown if (len <= 0) 6260964a3d3SNeilBrown return -EINVAL; 6270964a3d3SNeilBrown 6280964a3d3SNeilBrown status = nfs4_reset_recoverydir(recdir); 6290964a3d3SNeilBrown return strlen(buf); 6300964a3d3SNeilBrown } 63170c3b76cSNeilBrown #endif 6320964a3d3SNeilBrown 6331da177e4SLinus Torvalds /*----------------------------------------------------------------------------*/ 6341da177e4SLinus Torvalds /* 6351da177e4SLinus Torvalds * populating the filesystem. 6361da177e4SLinus Torvalds */ 6371da177e4SLinus Torvalds 6381da177e4SLinus Torvalds static int nfsd_fill_super(struct super_block * sb, void * data, int silent) 6391da177e4SLinus Torvalds { 6401da177e4SLinus Torvalds static struct tree_descr nfsd_files[] = { 6411da177e4SLinus Torvalds [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR}, 6421da177e4SLinus Torvalds [NFSD_Add] = {".add", &transaction_ops, S_IWUSR}, 6431da177e4SLinus Torvalds [NFSD_Del] = {".del", &transaction_ops, S_IWUSR}, 6441da177e4SLinus Torvalds [NFSD_Export] = {".export", &transaction_ops, S_IWUSR}, 6451da177e4SLinus Torvalds [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR}, 6461da177e4SLinus Torvalds [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR}, 6471da177e4SLinus Torvalds [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR}, 6481da177e4SLinus Torvalds [NFSD_List] = {"exports", &exports_operations, S_IRUGO}, 6491da177e4SLinus Torvalds [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, 6501da177e4SLinus Torvalds [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, 651eed2965aSGreg Banks [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, 65270c3b76cSNeilBrown [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, 65380212d59SNeilBrown [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, 654596bbe53SNeilBrown [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, 6551da177e4SLinus Torvalds #ifdef CONFIG_NFSD_V4 6561da177e4SLinus Torvalds [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, 6570964a3d3SNeilBrown [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, 6581da177e4SLinus Torvalds #endif 6591da177e4SLinus Torvalds /* last one */ {""} 6601da177e4SLinus Torvalds }; 6611da177e4SLinus Torvalds return simple_fill_super(sb, 0x6e667364, nfsd_files); 6621da177e4SLinus Torvalds } 6631da177e4SLinus Torvalds 664454e2398SDavid Howells static int nfsd_get_sb(struct file_system_type *fs_type, 665454e2398SDavid Howells int flags, const char *dev_name, void *data, struct vfsmount *mnt) 6661da177e4SLinus Torvalds { 667454e2398SDavid Howells return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt); 6681da177e4SLinus Torvalds } 6691da177e4SLinus Torvalds 6701da177e4SLinus Torvalds static struct file_system_type nfsd_fs_type = { 6711da177e4SLinus Torvalds .owner = THIS_MODULE, 6721da177e4SLinus Torvalds .name = "nfsd", 6731da177e4SLinus Torvalds .get_sb = nfsd_get_sb, 6741da177e4SLinus Torvalds .kill_sb = kill_litter_super, 6751da177e4SLinus Torvalds }; 6761da177e4SLinus Torvalds 677*e331f606SJ. Bruce Fields #ifdef CONFIG_PROC_FS 678*e331f606SJ. Bruce Fields static int create_proc_exports_entry(void) 679*e331f606SJ. Bruce Fields { 680*e331f606SJ. Bruce Fields struct proc_dir_entry *entry; 681*e331f606SJ. Bruce Fields 682*e331f606SJ. Bruce Fields entry = proc_mkdir("fs/nfs", NULL); 683*e331f606SJ. Bruce Fields if (!entry) 684*e331f606SJ. Bruce Fields return -ENOMEM; 685*e331f606SJ. Bruce Fields entry = create_proc_entry("fs/nfs/exports", 0, NULL); 686*e331f606SJ. Bruce Fields if (!entry) 687*e331f606SJ. Bruce Fields return -ENOMEM; 688*e331f606SJ. Bruce Fields entry->proc_fops = &exports_operations; 689*e331f606SJ. Bruce Fields return 0; 690*e331f606SJ. Bruce Fields } 691*e331f606SJ. Bruce Fields #else /* CONFIG_PROC_FS */ 692*e331f606SJ. Bruce Fields static int create_proc_exports_entry(void) 693*e331f606SJ. Bruce Fields { 694*e331f606SJ. Bruce Fields return 0; 695*e331f606SJ. Bruce Fields } 696*e331f606SJ. Bruce Fields #endif 697*e331f606SJ. Bruce Fields 6981da177e4SLinus Torvalds static int __init init_nfsd(void) 6991da177e4SLinus Torvalds { 7001da177e4SLinus Torvalds int retval; 7011da177e4SLinus Torvalds printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); 7021da177e4SLinus Torvalds 703e8ff2a84SJ. Bruce Fields retval = nfs4_state_init(); /* nfs4 locking state */ 704e8ff2a84SJ. Bruce Fields if (retval) 705e8ff2a84SJ. Bruce Fields return retval; 7061da177e4SLinus Torvalds nfsd_stat_init(); /* Statistics */ 707d5c3428bSJ. Bruce Fields retval = nfsd_reply_cache_init(); 708d5c3428bSJ. Bruce Fields if (retval) 709d5c3428bSJ. Bruce Fields goto out_free_stat; 7101da177e4SLinus Torvalds nfsd_export_init(); /* Exports table */ 7111da177e4SLinus Torvalds nfsd_lockd_init(); /* lockd->nfsd callbacks */ 7121da177e4SLinus Torvalds nfsd_idmap_init(); /* Name to ID mapping */ 713*e331f606SJ. Bruce Fields retval = create_proc_exports_entry(); 714*e331f606SJ. Bruce Fields if (retval) 715*e331f606SJ. Bruce Fields goto out_free_idmap; 7161da177e4SLinus Torvalds retval = register_filesystem(&nfsd_fs_type); 71726808d3fSJ. Bruce Fields if (retval) 71826808d3fSJ. Bruce Fields goto out_free_all; 71926808d3fSJ. Bruce Fields return 0; 72026808d3fSJ. Bruce Fields out_free_all: 7211da177e4SLinus Torvalds remove_proc_entry("fs/nfs/exports", NULL); 7221da177e4SLinus Torvalds remove_proc_entry("fs/nfs", NULL); 723*e331f606SJ. Bruce Fields nfsd_idmap_shutdown(); 724*e331f606SJ. Bruce Fields out_free_idmap: 7251da177e4SLinus Torvalds nfsd_lockd_shutdown(); 726*e331f606SJ. Bruce Fields nfsd_export_shutdown(); 727*e331f606SJ. Bruce Fields nfsd_reply_cache_shutdown(); 728d5c3428bSJ. Bruce Fields out_free_stat: 729d5c3428bSJ. Bruce Fields nfsd_stat_shutdown(); 73046b25895SJ. Bruce Fields nfsd4_free_slabs(); 7311da177e4SLinus Torvalds return retval; 7321da177e4SLinus Torvalds } 7331da177e4SLinus Torvalds 7341da177e4SLinus Torvalds static void __exit exit_nfsd(void) 7351da177e4SLinus Torvalds { 7361da177e4SLinus Torvalds nfsd_export_shutdown(); 737d5c3428bSJ. Bruce Fields nfsd_reply_cache_shutdown(); 7381da177e4SLinus Torvalds remove_proc_entry("fs/nfs/exports", NULL); 7391da177e4SLinus Torvalds remove_proc_entry("fs/nfs", NULL); 7401da177e4SLinus Torvalds nfsd_stat_shutdown(); 7411da177e4SLinus Torvalds nfsd_lockd_shutdown(); 7421da177e4SLinus Torvalds nfsd_idmap_shutdown(); 743e8ff2a84SJ. Bruce Fields nfsd4_free_slabs(); 7441da177e4SLinus Torvalds unregister_filesystem(&nfsd_fs_type); 7451da177e4SLinus Torvalds } 7461da177e4SLinus Torvalds 7471da177e4SLinus Torvalds MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); 7481da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 7491da177e4SLinus Torvalds module_init(init_nfsd) 7501da177e4SLinus Torvalds module_exit(exit_nfsd) 751