162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Syscall interface to knfsd. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/namei.h> 1062306a36Sopenharmony_ci#include <linux/ctype.h> 1162306a36Sopenharmony_ci#include <linux/fs_context.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/sunrpc/svcsock.h> 1462306a36Sopenharmony_ci#include <linux/lockd/lockd.h> 1562306a36Sopenharmony_ci#include <linux/sunrpc/addr.h> 1662306a36Sopenharmony_ci#include <linux/sunrpc/gss_api.h> 1762306a36Sopenharmony_ci#include <linux/sunrpc/rpc_pipe_fs.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/fsnotify.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "idmap.h" 2262306a36Sopenharmony_ci#include "nfsd.h" 2362306a36Sopenharmony_ci#include "cache.h" 2462306a36Sopenharmony_ci#include "state.h" 2562306a36Sopenharmony_ci#include "netns.h" 2662306a36Sopenharmony_ci#include "pnfs.h" 2762306a36Sopenharmony_ci#include "filecache.h" 2862306a36Sopenharmony_ci#include "trace.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * We have a single directory with several nodes in it. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cienum { 3462306a36Sopenharmony_ci NFSD_Root = 1, 3562306a36Sopenharmony_ci NFSD_List, 3662306a36Sopenharmony_ci NFSD_Export_Stats, 3762306a36Sopenharmony_ci NFSD_Export_features, 3862306a36Sopenharmony_ci NFSD_Fh, 3962306a36Sopenharmony_ci NFSD_FO_UnlockIP, 4062306a36Sopenharmony_ci NFSD_FO_UnlockFS, 4162306a36Sopenharmony_ci NFSD_Threads, 4262306a36Sopenharmony_ci NFSD_Pool_Threads, 4362306a36Sopenharmony_ci NFSD_Pool_Stats, 4462306a36Sopenharmony_ci NFSD_Reply_Cache_Stats, 4562306a36Sopenharmony_ci NFSD_Versions, 4662306a36Sopenharmony_ci NFSD_Ports, 4762306a36Sopenharmony_ci NFSD_MaxBlkSize, 4862306a36Sopenharmony_ci NFSD_MaxConnections, 4962306a36Sopenharmony_ci NFSD_Filecache, 5062306a36Sopenharmony_ci /* 5162306a36Sopenharmony_ci * The below MUST come last. Otherwise we leave a hole in nfsd_files[] 5262306a36Sopenharmony_ci * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4 5562306a36Sopenharmony_ci NFSD_Leasetime, 5662306a36Sopenharmony_ci NFSD_Gracetime, 5762306a36Sopenharmony_ci NFSD_RecoveryDir, 5862306a36Sopenharmony_ci NFSD_V4EndGrace, 5962306a36Sopenharmony_ci#endif 6062306a36Sopenharmony_ci NFSD_MaxReserved 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * write() for these nodes. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic ssize_t write_filehandle(struct file *file, char *buf, size_t size); 6762306a36Sopenharmony_cistatic ssize_t write_unlock_ip(struct file *file, char *buf, size_t size); 6862306a36Sopenharmony_cistatic ssize_t write_unlock_fs(struct file *file, char *buf, size_t size); 6962306a36Sopenharmony_cistatic ssize_t write_threads(struct file *file, char *buf, size_t size); 7062306a36Sopenharmony_cistatic ssize_t write_pool_threads(struct file *file, char *buf, size_t size); 7162306a36Sopenharmony_cistatic ssize_t write_versions(struct file *file, char *buf, size_t size); 7262306a36Sopenharmony_cistatic ssize_t write_ports(struct file *file, char *buf, size_t size); 7362306a36Sopenharmony_cistatic ssize_t write_maxblksize(struct file *file, char *buf, size_t size); 7462306a36Sopenharmony_cistatic ssize_t write_maxconn(struct file *file, char *buf, size_t size); 7562306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4 7662306a36Sopenharmony_cistatic ssize_t write_leasetime(struct file *file, char *buf, size_t size); 7762306a36Sopenharmony_cistatic ssize_t write_gracetime(struct file *file, char *buf, size_t size); 7862306a36Sopenharmony_cistatic ssize_t write_recoverydir(struct file *file, char *buf, size_t size); 7962306a36Sopenharmony_cistatic ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size); 8062306a36Sopenharmony_ci#endif 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic ssize_t (*const write_op[])(struct file *, char *, size_t) = { 8362306a36Sopenharmony_ci [NFSD_Fh] = write_filehandle, 8462306a36Sopenharmony_ci [NFSD_FO_UnlockIP] = write_unlock_ip, 8562306a36Sopenharmony_ci [NFSD_FO_UnlockFS] = write_unlock_fs, 8662306a36Sopenharmony_ci [NFSD_Threads] = write_threads, 8762306a36Sopenharmony_ci [NFSD_Pool_Threads] = write_pool_threads, 8862306a36Sopenharmony_ci [NFSD_Versions] = write_versions, 8962306a36Sopenharmony_ci [NFSD_Ports] = write_ports, 9062306a36Sopenharmony_ci [NFSD_MaxBlkSize] = write_maxblksize, 9162306a36Sopenharmony_ci [NFSD_MaxConnections] = write_maxconn, 9262306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4 9362306a36Sopenharmony_ci [NFSD_Leasetime] = write_leasetime, 9462306a36Sopenharmony_ci [NFSD_Gracetime] = write_gracetime, 9562306a36Sopenharmony_ci [NFSD_RecoveryDir] = write_recoverydir, 9662306a36Sopenharmony_ci [NFSD_V4EndGrace] = write_v4_end_grace, 9762306a36Sopenharmony_ci#endif 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci ino_t ino = file_inode(file)->i_ino; 10362306a36Sopenharmony_ci char *data; 10462306a36Sopenharmony_ci ssize_t rv; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (ino >= ARRAY_SIZE(write_op) || !write_op[ino]) 10762306a36Sopenharmony_ci return -EINVAL; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci data = simple_transaction_get(file, buf, size); 11062306a36Sopenharmony_ci if (IS_ERR(data)) 11162306a36Sopenharmony_ci return PTR_ERR(data); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci rv = write_op[ino](file, data, size); 11462306a36Sopenharmony_ci if (rv < 0) 11562306a36Sopenharmony_ci return rv; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci simple_transaction_set(file, rv); 11862306a36Sopenharmony_ci return size; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci if (! file->private_data) { 12462306a36Sopenharmony_ci /* An attempt to read a transaction file without writing 12562306a36Sopenharmony_ci * causes a 0-byte write so that the file can return 12662306a36Sopenharmony_ci * state information 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos); 12962306a36Sopenharmony_ci if (rv < 0) 13062306a36Sopenharmony_ci return rv; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci return simple_transaction_read(file, buf, size, pos); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic const struct file_operations transaction_ops = { 13662306a36Sopenharmony_ci .write = nfsctl_transaction_write, 13762306a36Sopenharmony_ci .read = nfsctl_transaction_read, 13862306a36Sopenharmony_ci .release = simple_transaction_release, 13962306a36Sopenharmony_ci .llseek = default_llseek, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int exports_net_open(struct net *net, struct file *file) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int err; 14562306a36Sopenharmony_ci struct seq_file *seq; 14662306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(net, nfsd_net_id); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci err = seq_open(file, &nfs_exports_op); 14962306a36Sopenharmony_ci if (err) 15062306a36Sopenharmony_ci return err; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci seq = file->private_data; 15362306a36Sopenharmony_ci seq->private = nn->svc_export_cache; 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int exports_nfsd_open(struct inode *inode, struct file *file) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci return exports_net_open(inode->i_sb->s_fs_info, file); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic const struct file_operations exports_nfsd_operations = { 16362306a36Sopenharmony_ci .open = exports_nfsd_open, 16462306a36Sopenharmony_ci .read = seq_read, 16562306a36Sopenharmony_ci .llseek = seq_lseek, 16662306a36Sopenharmony_ci .release = seq_release, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int export_features_show(struct seq_file *m, void *v) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci seq_printf(m, "0x%x 0x%x\n", NFSEXP_ALLFLAGS, NFSEXP_SECINFO_FLAGS); 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(export_features); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic const struct file_operations pool_stats_operations = { 17862306a36Sopenharmony_ci .open = nfsd_pool_stats_open, 17962306a36Sopenharmony_ci .read = seq_read, 18062306a36Sopenharmony_ci .llseek = seq_lseek, 18162306a36Sopenharmony_ci .release = nfsd_pool_stats_release, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(nfsd_reply_cache_stats); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(nfsd_file_cache_stats); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 18962306a36Sopenharmony_ci/* 19062306a36Sopenharmony_ci * payload - write methods 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic inline struct net *netns(struct file *file) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci return file_inode(file)->i_sb->s_fs_info; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* 19962306a36Sopenharmony_ci * write_unlock_ip - Release all locks used by a client 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * Experimental. 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * Input: 20462306a36Sopenharmony_ci * buf: '\n'-terminated C string containing a 20562306a36Sopenharmony_ci * presentation format IP address 20662306a36Sopenharmony_ci * size: length of C string in @buf 20762306a36Sopenharmony_ci * Output: 20862306a36Sopenharmony_ci * On success: returns zero if all specified locks were released; 20962306a36Sopenharmony_ci * returns one if one or more locks were not released 21062306a36Sopenharmony_ci * On error: return code is negative errno value 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_cistatic ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct sockaddr_storage address; 21562306a36Sopenharmony_ci struct sockaddr *sap = (struct sockaddr *)&address; 21662306a36Sopenharmony_ci size_t salen = sizeof(address); 21762306a36Sopenharmony_ci char *fo_path; 21862306a36Sopenharmony_ci struct net *net = netns(file); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* sanity check */ 22162306a36Sopenharmony_ci if (size == 0) 22262306a36Sopenharmony_ci return -EINVAL; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (buf[size-1] != '\n') 22562306a36Sopenharmony_ci return -EINVAL; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci fo_path = buf; 22862306a36Sopenharmony_ci if (qword_get(&buf, fo_path, size) < 0) 22962306a36Sopenharmony_ci return -EINVAL; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (rpc_pton(net, fo_path, size, sap, salen) == 0) 23262306a36Sopenharmony_ci return -EINVAL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci trace_nfsd_ctl_unlock_ip(net, buf); 23562306a36Sopenharmony_ci return nlmsvc_unlock_all_by_ip(sap); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* 23962306a36Sopenharmony_ci * write_unlock_fs - Release all locks on a local file system 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * Experimental. 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * Input: 24462306a36Sopenharmony_ci * buf: '\n'-terminated C string containing the 24562306a36Sopenharmony_ci * absolute pathname of a local file system 24662306a36Sopenharmony_ci * size: length of C string in @buf 24762306a36Sopenharmony_ci * Output: 24862306a36Sopenharmony_ci * On success: returns zero if all specified locks were released; 24962306a36Sopenharmony_ci * returns one if one or more locks were not released 25062306a36Sopenharmony_ci * On error: return code is negative errno value 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_cistatic ssize_t write_unlock_fs(struct file *file, char *buf, size_t size) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct path path; 25562306a36Sopenharmony_ci char *fo_path; 25662306a36Sopenharmony_ci int error; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* sanity check */ 25962306a36Sopenharmony_ci if (size == 0) 26062306a36Sopenharmony_ci return -EINVAL; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (buf[size-1] != '\n') 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci fo_path = buf; 26662306a36Sopenharmony_ci if (qword_get(&buf, fo_path, size) < 0) 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci trace_nfsd_ctl_unlock_fs(netns(file), fo_path); 26962306a36Sopenharmony_ci error = kern_path(fo_path, 0, &path); 27062306a36Sopenharmony_ci if (error) 27162306a36Sopenharmony_ci return error; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * XXX: Needs better sanity checking. Otherwise we could end up 27562306a36Sopenharmony_ci * releasing locks on the wrong file system. 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * For example: 27862306a36Sopenharmony_ci * 1. Does the path refer to a directory? 27962306a36Sopenharmony_ci * 2. Is that directory a mount point, or 28062306a36Sopenharmony_ci * 3. Is that directory the root of an exported file system? 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci path_put(&path); 28562306a36Sopenharmony_ci return error; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * write_filehandle - Get a variable-length NFS file handle by path 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * On input, the buffer contains a '\n'-terminated C string comprised of 29262306a36Sopenharmony_ci * three alphanumeric words separated by whitespace. The string may 29362306a36Sopenharmony_ci * contain escape sequences. 29462306a36Sopenharmony_ci * 29562306a36Sopenharmony_ci * Input: 29662306a36Sopenharmony_ci * buf: 29762306a36Sopenharmony_ci * domain: client domain name 29862306a36Sopenharmony_ci * path: export pathname 29962306a36Sopenharmony_ci * maxsize: numeric maximum size of 30062306a36Sopenharmony_ci * @buf 30162306a36Sopenharmony_ci * size: length of C string in @buf 30262306a36Sopenharmony_ci * Output: 30362306a36Sopenharmony_ci * On success: passed-in buffer filled with '\n'-terminated C 30462306a36Sopenharmony_ci * string containing a ASCII hex text version 30562306a36Sopenharmony_ci * of the NFS file handle; 30662306a36Sopenharmony_ci * return code is the size in bytes of the string 30762306a36Sopenharmony_ci * On error: return code is negative errno value 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic ssize_t write_filehandle(struct file *file, char *buf, size_t size) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci char *dname, *path; 31262306a36Sopenharmony_ci int maxsize; 31362306a36Sopenharmony_ci char *mesg = buf; 31462306a36Sopenharmony_ci int len; 31562306a36Sopenharmony_ci struct auth_domain *dom; 31662306a36Sopenharmony_ci struct knfsd_fh fh; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (size == 0) 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (buf[size-1] != '\n') 32262306a36Sopenharmony_ci return -EINVAL; 32362306a36Sopenharmony_ci buf[size-1] = 0; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci dname = mesg; 32662306a36Sopenharmony_ci len = qword_get(&mesg, dname, size); 32762306a36Sopenharmony_ci if (len <= 0) 32862306a36Sopenharmony_ci return -EINVAL; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci path = dname+len+1; 33162306a36Sopenharmony_ci len = qword_get(&mesg, path, size); 33262306a36Sopenharmony_ci if (len <= 0) 33362306a36Sopenharmony_ci return -EINVAL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci len = get_int(&mesg, &maxsize); 33662306a36Sopenharmony_ci if (len) 33762306a36Sopenharmony_ci return len; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (maxsize < NFS_FHSIZE) 34062306a36Sopenharmony_ci return -EINVAL; 34162306a36Sopenharmony_ci maxsize = min(maxsize, NFS3_FHSIZE); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (qword_get(&mesg, mesg, size) > 0) 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci trace_nfsd_ctl_filehandle(netns(file), dname, path, maxsize); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* we have all the words, they are in buf.. */ 34962306a36Sopenharmony_ci dom = unix_domain_find(dname); 35062306a36Sopenharmony_ci if (!dom) 35162306a36Sopenharmony_ci return -ENOMEM; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci len = exp_rootfh(netns(file), dom, path, &fh, maxsize); 35462306a36Sopenharmony_ci auth_domain_put(dom); 35562306a36Sopenharmony_ci if (len) 35662306a36Sopenharmony_ci return len; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci mesg = buf; 35962306a36Sopenharmony_ci len = SIMPLE_TRANSACTION_LIMIT; 36062306a36Sopenharmony_ci qword_addhex(&mesg, &len, fh.fh_raw, fh.fh_size); 36162306a36Sopenharmony_ci mesg[-1] = '\n'; 36262306a36Sopenharmony_ci return mesg - buf; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* 36662306a36Sopenharmony_ci * write_threads - Start NFSD, or report the current number of running threads 36762306a36Sopenharmony_ci * 36862306a36Sopenharmony_ci * Input: 36962306a36Sopenharmony_ci * buf: ignored 37062306a36Sopenharmony_ci * size: zero 37162306a36Sopenharmony_ci * Output: 37262306a36Sopenharmony_ci * On success: passed-in buffer filled with '\n'-terminated C 37362306a36Sopenharmony_ci * string numeric value representing the number of 37462306a36Sopenharmony_ci * running NFSD threads; 37562306a36Sopenharmony_ci * return code is the size in bytes of the string 37662306a36Sopenharmony_ci * On error: return code is zero 37762306a36Sopenharmony_ci * 37862306a36Sopenharmony_ci * OR 37962306a36Sopenharmony_ci * 38062306a36Sopenharmony_ci * Input: 38162306a36Sopenharmony_ci * buf: C string containing an unsigned 38262306a36Sopenharmony_ci * integer value representing the 38362306a36Sopenharmony_ci * number of NFSD threads to start 38462306a36Sopenharmony_ci * size: non-zero length of C string in @buf 38562306a36Sopenharmony_ci * Output: 38662306a36Sopenharmony_ci * On success: NFS service is started; 38762306a36Sopenharmony_ci * passed-in buffer filled with '\n'-terminated C 38862306a36Sopenharmony_ci * string numeric value representing the number of 38962306a36Sopenharmony_ci * running NFSD threads; 39062306a36Sopenharmony_ci * return code is the size in bytes of the string 39162306a36Sopenharmony_ci * On error: return code is zero or a negative errno value 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_cistatic ssize_t write_threads(struct file *file, char *buf, size_t size) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci char *mesg = buf; 39662306a36Sopenharmony_ci int rv; 39762306a36Sopenharmony_ci struct net *net = netns(file); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (size > 0) { 40062306a36Sopenharmony_ci int newthreads; 40162306a36Sopenharmony_ci rv = get_int(&mesg, &newthreads); 40262306a36Sopenharmony_ci if (rv) 40362306a36Sopenharmony_ci return rv; 40462306a36Sopenharmony_ci if (newthreads < 0) 40562306a36Sopenharmony_ci return -EINVAL; 40662306a36Sopenharmony_ci trace_nfsd_ctl_threads(net, newthreads); 40762306a36Sopenharmony_ci rv = nfsd_svc(newthreads, net, file->f_cred); 40862306a36Sopenharmony_ci if (rv < 0) 40962306a36Sopenharmony_ci return rv; 41062306a36Sopenharmony_ci } else 41162306a36Sopenharmony_ci rv = nfsd_nrthreads(net); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/* 41762306a36Sopenharmony_ci * write_pool_threads - Set or report the current number of threads per pool 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * Input: 42062306a36Sopenharmony_ci * buf: ignored 42162306a36Sopenharmony_ci * size: zero 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * OR 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * Input: 42662306a36Sopenharmony_ci * buf: C string containing whitespace- 42762306a36Sopenharmony_ci * separated unsigned integer values 42862306a36Sopenharmony_ci * representing the number of NFSD 42962306a36Sopenharmony_ci * threads to start in each pool 43062306a36Sopenharmony_ci * size: non-zero length of C string in @buf 43162306a36Sopenharmony_ci * Output: 43262306a36Sopenharmony_ci * On success: passed-in buffer filled with '\n'-terminated C 43362306a36Sopenharmony_ci * string containing integer values representing the 43462306a36Sopenharmony_ci * number of NFSD threads in each pool; 43562306a36Sopenharmony_ci * return code is the size in bytes of the string 43662306a36Sopenharmony_ci * On error: return code is zero or a negative errno value 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_cistatic ssize_t write_pool_threads(struct file *file, char *buf, size_t size) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci /* if size > 0, look for an array of number of threads per node 44162306a36Sopenharmony_ci * and apply them then write out number of threads per node as reply 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ci char *mesg = buf; 44462306a36Sopenharmony_ci int i; 44562306a36Sopenharmony_ci int rv; 44662306a36Sopenharmony_ci int len; 44762306a36Sopenharmony_ci int npools; 44862306a36Sopenharmony_ci int *nthreads; 44962306a36Sopenharmony_ci struct net *net = netns(file); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci mutex_lock(&nfsd_mutex); 45262306a36Sopenharmony_ci npools = nfsd_nrpools(net); 45362306a36Sopenharmony_ci if (npools == 0) { 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * NFS is shut down. The admin can start it by 45662306a36Sopenharmony_ci * writing to the threads file but NOT the pool_threads 45762306a36Sopenharmony_ci * file, sorry. Report zero threads. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci mutex_unlock(&nfsd_mutex); 46062306a36Sopenharmony_ci strcpy(buf, "0\n"); 46162306a36Sopenharmony_ci return strlen(buf); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); 46562306a36Sopenharmony_ci rv = -ENOMEM; 46662306a36Sopenharmony_ci if (nthreads == NULL) 46762306a36Sopenharmony_ci goto out_free; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (size > 0) { 47062306a36Sopenharmony_ci for (i = 0; i < npools; i++) { 47162306a36Sopenharmony_ci rv = get_int(&mesg, &nthreads[i]); 47262306a36Sopenharmony_ci if (rv == -ENOENT) 47362306a36Sopenharmony_ci break; /* fewer numbers than pools */ 47462306a36Sopenharmony_ci if (rv) 47562306a36Sopenharmony_ci goto out_free; /* syntax error */ 47662306a36Sopenharmony_ci rv = -EINVAL; 47762306a36Sopenharmony_ci if (nthreads[i] < 0) 47862306a36Sopenharmony_ci goto out_free; 47962306a36Sopenharmony_ci trace_nfsd_ctl_pool_threads(net, i, nthreads[i]); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci rv = nfsd_set_nrthreads(i, nthreads, net); 48262306a36Sopenharmony_ci if (rv) 48362306a36Sopenharmony_ci goto out_free; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci rv = nfsd_get_nrthreads(npools, nthreads, net); 48762306a36Sopenharmony_ci if (rv) 48862306a36Sopenharmony_ci goto out_free; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci mesg = buf; 49162306a36Sopenharmony_ci size = SIMPLE_TRANSACTION_LIMIT; 49262306a36Sopenharmony_ci for (i = 0; i < npools && size > 0; i++) { 49362306a36Sopenharmony_ci snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' ')); 49462306a36Sopenharmony_ci len = strlen(mesg); 49562306a36Sopenharmony_ci size -= len; 49662306a36Sopenharmony_ci mesg += len; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci rv = mesg - buf; 49962306a36Sopenharmony_ciout_free: 50062306a36Sopenharmony_ci kfree(nthreads); 50162306a36Sopenharmony_ci mutex_unlock(&nfsd_mutex); 50262306a36Sopenharmony_ci return rv; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic ssize_t 50662306a36Sopenharmony_cinfsd_print_version_support(struct nfsd_net *nn, char *buf, int remaining, 50762306a36Sopenharmony_ci const char *sep, unsigned vers, int minor) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci const char *format = minor < 0 ? "%s%c%u" : "%s%c%u.%u"; 51062306a36Sopenharmony_ci bool supported = !!nfsd_vers(nn, vers, NFSD_TEST); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (vers == 4 && minor >= 0 && 51362306a36Sopenharmony_ci !nfsd_minorversion(nn, minor, NFSD_TEST)) 51462306a36Sopenharmony_ci supported = false; 51562306a36Sopenharmony_ci if (minor == 0 && supported) 51662306a36Sopenharmony_ci /* 51762306a36Sopenharmony_ci * special case for backward compatability. 51862306a36Sopenharmony_ci * +4.0 is never reported, it is implied by 51962306a36Sopenharmony_ci * +4, unless -4.0 is present. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci return snprintf(buf, remaining, format, sep, 52362306a36Sopenharmony_ci supported ? '+' : '-', vers, minor); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic ssize_t __write_versions(struct file *file, char *buf, size_t size) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci char *mesg = buf; 52962306a36Sopenharmony_ci char *vers, *minorp, sign; 53062306a36Sopenharmony_ci int len, num, remaining; 53162306a36Sopenharmony_ci ssize_t tlen = 0; 53262306a36Sopenharmony_ci char *sep; 53362306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (size > 0) { 53662306a36Sopenharmony_ci if (nn->nfsd_serv) 53762306a36Sopenharmony_ci /* Cannot change versions without updating 53862306a36Sopenharmony_ci * nn->nfsd_serv->sv_xdrsize, and reallocing 53962306a36Sopenharmony_ci * rq_argp and rq_resp 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci return -EBUSY; 54262306a36Sopenharmony_ci if (buf[size-1] != '\n') 54362306a36Sopenharmony_ci return -EINVAL; 54462306a36Sopenharmony_ci buf[size-1] = 0; 54562306a36Sopenharmony_ci trace_nfsd_ctl_version(netns(file), buf); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci vers = mesg; 54862306a36Sopenharmony_ci len = qword_get(&mesg, vers, size); 54962306a36Sopenharmony_ci if (len <= 0) return -EINVAL; 55062306a36Sopenharmony_ci do { 55162306a36Sopenharmony_ci enum vers_op cmd; 55262306a36Sopenharmony_ci unsigned minor; 55362306a36Sopenharmony_ci sign = *vers; 55462306a36Sopenharmony_ci if (sign == '+' || sign == '-') 55562306a36Sopenharmony_ci num = simple_strtol((vers+1), &minorp, 0); 55662306a36Sopenharmony_ci else 55762306a36Sopenharmony_ci num = simple_strtol(vers, &minorp, 0); 55862306a36Sopenharmony_ci if (*minorp == '.') { 55962306a36Sopenharmony_ci if (num != 4) 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci if (kstrtouint(minorp+1, 0, &minor) < 0) 56262306a36Sopenharmony_ci return -EINVAL; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET; 56662306a36Sopenharmony_ci switch(num) { 56762306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V2 56862306a36Sopenharmony_ci case 2: 56962306a36Sopenharmony_ci#endif 57062306a36Sopenharmony_ci case 3: 57162306a36Sopenharmony_ci nfsd_vers(nn, num, cmd); 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci case 4: 57462306a36Sopenharmony_ci if (*minorp == '.') { 57562306a36Sopenharmony_ci if (nfsd_minorversion(nn, minor, cmd) < 0) 57662306a36Sopenharmony_ci return -EINVAL; 57762306a36Sopenharmony_ci } else if ((cmd == NFSD_SET) != nfsd_vers(nn, num, NFSD_TEST)) { 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * Either we have +4 and no minors are enabled, 58062306a36Sopenharmony_ci * or we have -4 and at least one minor is enabled. 58162306a36Sopenharmony_ci * In either case, propagate 'cmd' to all minors. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci minor = 0; 58462306a36Sopenharmony_ci while (nfsd_minorversion(nn, minor, cmd) >= 0) 58562306a36Sopenharmony_ci minor++; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci default: 58962306a36Sopenharmony_ci /* Ignore requests to disable non-existent versions */ 59062306a36Sopenharmony_ci if (cmd == NFSD_SET) 59162306a36Sopenharmony_ci return -EINVAL; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci vers += len + 1; 59462306a36Sopenharmony_ci } while ((len = qword_get(&mesg, vers, size)) > 0); 59562306a36Sopenharmony_ci /* If all get turned off, turn them back on, as 59662306a36Sopenharmony_ci * having no versions is BAD 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci nfsd_reset_versions(nn); 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Now write current state into reply buffer */ 60262306a36Sopenharmony_ci sep = ""; 60362306a36Sopenharmony_ci remaining = SIMPLE_TRANSACTION_LIMIT; 60462306a36Sopenharmony_ci for (num=2 ; num <= 4 ; num++) { 60562306a36Sopenharmony_ci int minor; 60662306a36Sopenharmony_ci if (!nfsd_vers(nn, num, NFSD_AVAIL)) 60762306a36Sopenharmony_ci continue; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci minor = -1; 61062306a36Sopenharmony_ci do { 61162306a36Sopenharmony_ci len = nfsd_print_version_support(nn, buf, remaining, 61262306a36Sopenharmony_ci sep, num, minor); 61362306a36Sopenharmony_ci if (len >= remaining) 61462306a36Sopenharmony_ci goto out; 61562306a36Sopenharmony_ci remaining -= len; 61662306a36Sopenharmony_ci buf += len; 61762306a36Sopenharmony_ci tlen += len; 61862306a36Sopenharmony_ci minor++; 61962306a36Sopenharmony_ci if (len) 62062306a36Sopenharmony_ci sep = " "; 62162306a36Sopenharmony_ci } while (num == 4 && minor <= NFSD_SUPPORTED_MINOR_VERSION); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ciout: 62462306a36Sopenharmony_ci len = snprintf(buf, remaining, "\n"); 62562306a36Sopenharmony_ci if (len >= remaining) 62662306a36Sopenharmony_ci return -EINVAL; 62762306a36Sopenharmony_ci return tlen + len; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci/* 63162306a36Sopenharmony_ci * write_versions - Set or report the available NFS protocol versions 63262306a36Sopenharmony_ci * 63362306a36Sopenharmony_ci * Input: 63462306a36Sopenharmony_ci * buf: ignored 63562306a36Sopenharmony_ci * size: zero 63662306a36Sopenharmony_ci * Output: 63762306a36Sopenharmony_ci * On success: passed-in buffer filled with '\n'-terminated C 63862306a36Sopenharmony_ci * string containing positive or negative integer 63962306a36Sopenharmony_ci * values representing the current status of each 64062306a36Sopenharmony_ci * protocol version; 64162306a36Sopenharmony_ci * return code is the size in bytes of the string 64262306a36Sopenharmony_ci * On error: return code is zero or a negative errno value 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * OR 64562306a36Sopenharmony_ci * 64662306a36Sopenharmony_ci * Input: 64762306a36Sopenharmony_ci * buf: C string containing whitespace- 64862306a36Sopenharmony_ci * separated positive or negative 64962306a36Sopenharmony_ci * integer values representing NFS 65062306a36Sopenharmony_ci * protocol versions to enable ("+n") 65162306a36Sopenharmony_ci * or disable ("-n") 65262306a36Sopenharmony_ci * size: non-zero length of C string in @buf 65362306a36Sopenharmony_ci * Output: 65462306a36Sopenharmony_ci * On success: status of zero or more protocol versions has 65562306a36Sopenharmony_ci * been updated; passed-in buffer filled with 65662306a36Sopenharmony_ci * '\n'-terminated C string containing positive 65762306a36Sopenharmony_ci * or negative integer values representing the 65862306a36Sopenharmony_ci * current status of each protocol version; 65962306a36Sopenharmony_ci * return code is the size in bytes of the string 66062306a36Sopenharmony_ci * On error: return code is zero or a negative errno value 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_cistatic ssize_t write_versions(struct file *file, char *buf, size_t size) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci ssize_t rv; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci mutex_lock(&nfsd_mutex); 66762306a36Sopenharmony_ci rv = __write_versions(file, buf, size); 66862306a36Sopenharmony_ci mutex_unlock(&nfsd_mutex); 66962306a36Sopenharmony_ci return rv; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci/* 67362306a36Sopenharmony_ci * Zero-length write. Return a list of NFSD's current listener 67462306a36Sopenharmony_ci * transports. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_cistatic ssize_t __write_ports_names(char *buf, struct net *net) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(net, nfsd_net_id); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (nn->nfsd_serv == NULL) 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci return svc_xprt_names(nn->nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/* 68662306a36Sopenharmony_ci * A single 'fd' number was written, in which case it must be for 68762306a36Sopenharmony_ci * a socket of a supported family/protocol, and we use it as an 68862306a36Sopenharmony_ci * nfsd listener. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_cistatic ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred *cred) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci char *mesg = buf; 69362306a36Sopenharmony_ci int fd, err; 69462306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(net, nfsd_net_id); 69562306a36Sopenharmony_ci struct svc_serv *serv; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci err = get_int(&mesg, &fd); 69862306a36Sopenharmony_ci if (err != 0 || fd < 0) 69962306a36Sopenharmony_ci return -EINVAL; 70062306a36Sopenharmony_ci trace_nfsd_ctl_ports_addfd(net, fd); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci err = nfsd_create_serv(net); 70362306a36Sopenharmony_ci if (err != 0) 70462306a36Sopenharmony_ci return err; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci serv = nn->nfsd_serv; 70762306a36Sopenharmony_ci err = svc_addsock(serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (err < 0 && !serv->sv_nrthreads && !nn->keep_active) 71062306a36Sopenharmony_ci nfsd_last_thread(net); 71162306a36Sopenharmony_ci else if (err >= 0 && !serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) 71262306a36Sopenharmony_ci svc_get(serv); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci svc_put(serv); 71562306a36Sopenharmony_ci return err; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/* 71962306a36Sopenharmony_ci * A transport listener is added by writing its transport name and 72062306a36Sopenharmony_ci * a port number. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_cistatic ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cred *cred) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci char transport[16]; 72562306a36Sopenharmony_ci struct svc_xprt *xprt; 72662306a36Sopenharmony_ci int port, err; 72762306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(net, nfsd_net_id); 72862306a36Sopenharmony_ci struct svc_serv *serv; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (sscanf(buf, "%15s %5u", transport, &port) != 2) 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (port < 1 || port > USHRT_MAX) 73462306a36Sopenharmony_ci return -EINVAL; 73562306a36Sopenharmony_ci trace_nfsd_ctl_ports_addxprt(net, transport, port); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci err = nfsd_create_serv(net); 73862306a36Sopenharmony_ci if (err != 0) 73962306a36Sopenharmony_ci return err; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci serv = nn->nfsd_serv; 74262306a36Sopenharmony_ci err = svc_xprt_create(serv, transport, net, 74362306a36Sopenharmony_ci PF_INET, port, SVC_SOCK_ANONYMOUS, cred); 74462306a36Sopenharmony_ci if (err < 0) 74562306a36Sopenharmony_ci goto out_err; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci err = svc_xprt_create(serv, transport, net, 74862306a36Sopenharmony_ci PF_INET6, port, SVC_SOCK_ANONYMOUS, cred); 74962306a36Sopenharmony_ci if (err < 0 && err != -EAFNOSUPPORT) 75062306a36Sopenharmony_ci goto out_close; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (!serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) 75362306a36Sopenharmony_ci svc_get(serv); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci svc_put(serv); 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ciout_close: 75862306a36Sopenharmony_ci xprt = svc_find_xprt(serv, transport, net, PF_INET, port); 75962306a36Sopenharmony_ci if (xprt != NULL) { 76062306a36Sopenharmony_ci svc_xprt_close(xprt); 76162306a36Sopenharmony_ci svc_xprt_put(xprt); 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ciout_err: 76462306a36Sopenharmony_ci if (!serv->sv_nrthreads && !nn->keep_active) 76562306a36Sopenharmony_ci nfsd_last_thread(net); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci svc_put(serv); 76862306a36Sopenharmony_ci return err; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic ssize_t __write_ports(struct file *file, char *buf, size_t size, 77262306a36Sopenharmony_ci struct net *net) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci if (size == 0) 77562306a36Sopenharmony_ci return __write_ports_names(buf, net); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (isdigit(buf[0])) 77862306a36Sopenharmony_ci return __write_ports_addfd(buf, net, file->f_cred); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (isalpha(buf[0])) 78162306a36Sopenharmony_ci return __write_ports_addxprt(buf, net, file->f_cred); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return -EINVAL; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci/* 78762306a36Sopenharmony_ci * write_ports - Pass a socket file descriptor or transport name to listen on 78862306a36Sopenharmony_ci * 78962306a36Sopenharmony_ci * Input: 79062306a36Sopenharmony_ci * buf: ignored 79162306a36Sopenharmony_ci * size: zero 79262306a36Sopenharmony_ci * Output: 79362306a36Sopenharmony_ci * On success: passed-in buffer filled with a '\n'-terminated C 79462306a36Sopenharmony_ci * string containing a whitespace-separated list of 79562306a36Sopenharmony_ci * named NFSD listeners; 79662306a36Sopenharmony_ci * return code is the size in bytes of the string 79762306a36Sopenharmony_ci * On error: return code is zero or a negative errno value 79862306a36Sopenharmony_ci * 79962306a36Sopenharmony_ci * OR 80062306a36Sopenharmony_ci * 80162306a36Sopenharmony_ci * Input: 80262306a36Sopenharmony_ci * buf: C string containing an unsigned 80362306a36Sopenharmony_ci * integer value representing a bound 80462306a36Sopenharmony_ci * but unconnected socket that is to be 80562306a36Sopenharmony_ci * used as an NFSD listener; listen(3) 80662306a36Sopenharmony_ci * must be called for a SOCK_STREAM 80762306a36Sopenharmony_ci * socket, otherwise it is ignored 80862306a36Sopenharmony_ci * size: non-zero length of C string in @buf 80962306a36Sopenharmony_ci * Output: 81062306a36Sopenharmony_ci * On success: NFS service is started; 81162306a36Sopenharmony_ci * passed-in buffer filled with a '\n'-terminated C 81262306a36Sopenharmony_ci * string containing a unique alphanumeric name of 81362306a36Sopenharmony_ci * the listener; 81462306a36Sopenharmony_ci * return code is the size in bytes of the string 81562306a36Sopenharmony_ci * On error: return code is a negative errno value 81662306a36Sopenharmony_ci * 81762306a36Sopenharmony_ci * OR 81862306a36Sopenharmony_ci * 81962306a36Sopenharmony_ci * Input: 82062306a36Sopenharmony_ci * buf: C string containing a transport 82162306a36Sopenharmony_ci * name and an unsigned integer value 82262306a36Sopenharmony_ci * representing the port to listen on, 82362306a36Sopenharmony_ci * separated by whitespace 82462306a36Sopenharmony_ci * size: non-zero length of C string in @buf 82562306a36Sopenharmony_ci * Output: 82662306a36Sopenharmony_ci * On success: returns zero; NFS service is started 82762306a36Sopenharmony_ci * On error: return code is a negative errno value 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_cistatic ssize_t write_ports(struct file *file, char *buf, size_t size) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci ssize_t rv; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci mutex_lock(&nfsd_mutex); 83462306a36Sopenharmony_ci rv = __write_ports(file, buf, size, netns(file)); 83562306a36Sopenharmony_ci mutex_unlock(&nfsd_mutex); 83662306a36Sopenharmony_ci return rv; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ciint nfsd_max_blksize; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/* 84362306a36Sopenharmony_ci * write_maxblksize - Set or report the current NFS blksize 84462306a36Sopenharmony_ci * 84562306a36Sopenharmony_ci * Input: 84662306a36Sopenharmony_ci * buf: ignored 84762306a36Sopenharmony_ci * size: zero 84862306a36Sopenharmony_ci * 84962306a36Sopenharmony_ci * OR 85062306a36Sopenharmony_ci * 85162306a36Sopenharmony_ci * Input: 85262306a36Sopenharmony_ci * buf: C string containing an unsigned 85362306a36Sopenharmony_ci * integer value representing the new 85462306a36Sopenharmony_ci * NFS blksize 85562306a36Sopenharmony_ci * size: non-zero length of C string in @buf 85662306a36Sopenharmony_ci * Output: 85762306a36Sopenharmony_ci * On success: passed-in buffer filled with '\n'-terminated C string 85862306a36Sopenharmony_ci * containing numeric value of the current NFS blksize 85962306a36Sopenharmony_ci * setting; 86062306a36Sopenharmony_ci * return code is the size in bytes of the string 86162306a36Sopenharmony_ci * On error: return code is zero or a negative errno value 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_cistatic ssize_t write_maxblksize(struct file *file, char *buf, size_t size) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci char *mesg = buf; 86662306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (size > 0) { 86962306a36Sopenharmony_ci int bsize; 87062306a36Sopenharmony_ci int rv = get_int(&mesg, &bsize); 87162306a36Sopenharmony_ci if (rv) 87262306a36Sopenharmony_ci return rv; 87362306a36Sopenharmony_ci trace_nfsd_ctl_maxblksize(netns(file), bsize); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* force bsize into allowed range and 87662306a36Sopenharmony_ci * required alignment. 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_ci bsize = max_t(int, bsize, 1024); 87962306a36Sopenharmony_ci bsize = min_t(int, bsize, NFSSVC_MAXBLKSIZE); 88062306a36Sopenharmony_ci bsize &= ~(1024-1); 88162306a36Sopenharmony_ci mutex_lock(&nfsd_mutex); 88262306a36Sopenharmony_ci if (nn->nfsd_serv) { 88362306a36Sopenharmony_ci mutex_unlock(&nfsd_mutex); 88462306a36Sopenharmony_ci return -EBUSY; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci nfsd_max_blksize = bsize; 88762306a36Sopenharmony_ci mutex_unlock(&nfsd_mutex); 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", 89162306a36Sopenharmony_ci nfsd_max_blksize); 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/* 89562306a36Sopenharmony_ci * write_maxconn - Set or report the current max number of connections 89662306a36Sopenharmony_ci * 89762306a36Sopenharmony_ci * Input: 89862306a36Sopenharmony_ci * buf: ignored 89962306a36Sopenharmony_ci * size: zero 90062306a36Sopenharmony_ci * OR 90162306a36Sopenharmony_ci * 90262306a36Sopenharmony_ci * Input: 90362306a36Sopenharmony_ci * buf: C string containing an unsigned 90462306a36Sopenharmony_ci * integer value representing the new 90562306a36Sopenharmony_ci * number of max connections 90662306a36Sopenharmony_ci * size: non-zero length of C string in @buf 90762306a36Sopenharmony_ci * Output: 90862306a36Sopenharmony_ci * On success: passed-in buffer filled with '\n'-terminated C string 90962306a36Sopenharmony_ci * containing numeric value of max_connections setting 91062306a36Sopenharmony_ci * for this net namespace; 91162306a36Sopenharmony_ci * return code is the size in bytes of the string 91262306a36Sopenharmony_ci * On error: return code is zero or a negative errno value 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_cistatic ssize_t write_maxconn(struct file *file, char *buf, size_t size) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci char *mesg = buf; 91762306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); 91862306a36Sopenharmony_ci unsigned int maxconn = nn->max_connections; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (size > 0) { 92162306a36Sopenharmony_ci int rv = get_uint(&mesg, &maxconn); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci if (rv) 92462306a36Sopenharmony_ci return rv; 92562306a36Sopenharmony_ci trace_nfsd_ctl_maxconn(netns(file), maxconn); 92662306a36Sopenharmony_ci nn->max_connections = maxconn; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%u\n", maxconn); 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4 93362306a36Sopenharmony_cistatic ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, 93462306a36Sopenharmony_ci time64_t *time, struct nfsd_net *nn) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct dentry *dentry = file_dentry(file); 93762306a36Sopenharmony_ci char *mesg = buf; 93862306a36Sopenharmony_ci int rv, i; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (size > 0) { 94162306a36Sopenharmony_ci if (nn->nfsd_serv) 94262306a36Sopenharmony_ci return -EBUSY; 94362306a36Sopenharmony_ci rv = get_int(&mesg, &i); 94462306a36Sopenharmony_ci if (rv) 94562306a36Sopenharmony_ci return rv; 94662306a36Sopenharmony_ci trace_nfsd_ctl_time(netns(file), dentry->d_name.name, 94762306a36Sopenharmony_ci dentry->d_name.len, i); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* 95062306a36Sopenharmony_ci * Some sanity checking. We don't have a reason for 95162306a36Sopenharmony_ci * these particular numbers, but problems with the 95262306a36Sopenharmony_ci * extremes are: 95362306a36Sopenharmony_ci * - Too short: the briefest network outage may 95462306a36Sopenharmony_ci * cause clients to lose all their locks. Also, 95562306a36Sopenharmony_ci * the frequent polling may be wasteful. 95662306a36Sopenharmony_ci * - Too long: do you really want reboot recovery 95762306a36Sopenharmony_ci * to take more than an hour? Or to make other 95862306a36Sopenharmony_ci * clients wait an hour before being able to 95962306a36Sopenharmony_ci * revoke a dead client's locks? 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci if (i < 10 || i > 3600) 96262306a36Sopenharmony_ci return -EINVAL; 96362306a36Sopenharmony_ci *time = i; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%lld\n", *time); 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, 97062306a36Sopenharmony_ci time64_t *time, struct nfsd_net *nn) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci ssize_t rv; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci mutex_lock(&nfsd_mutex); 97562306a36Sopenharmony_ci rv = __nfsd4_write_time(file, buf, size, time, nn); 97662306a36Sopenharmony_ci mutex_unlock(&nfsd_mutex); 97762306a36Sopenharmony_ci return rv; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/* 98162306a36Sopenharmony_ci * write_leasetime - Set or report the current NFSv4 lease time 98262306a36Sopenharmony_ci * 98362306a36Sopenharmony_ci * Input: 98462306a36Sopenharmony_ci * buf: ignored 98562306a36Sopenharmony_ci * size: zero 98662306a36Sopenharmony_ci * 98762306a36Sopenharmony_ci * OR 98862306a36Sopenharmony_ci * 98962306a36Sopenharmony_ci * Input: 99062306a36Sopenharmony_ci * buf: C string containing an unsigned 99162306a36Sopenharmony_ci * integer value representing the new 99262306a36Sopenharmony_ci * NFSv4 lease expiry time 99362306a36Sopenharmony_ci * size: non-zero length of C string in @buf 99462306a36Sopenharmony_ci * Output: 99562306a36Sopenharmony_ci * On success: passed-in buffer filled with '\n'-terminated C 99662306a36Sopenharmony_ci * string containing unsigned integer value of the 99762306a36Sopenharmony_ci * current lease expiry time; 99862306a36Sopenharmony_ci * return code is the size in bytes of the string 99962306a36Sopenharmony_ci * On error: return code is zero or a negative errno value 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_cistatic ssize_t write_leasetime(struct file *file, char *buf, size_t size) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); 100462306a36Sopenharmony_ci return nfsd4_write_time(file, buf, size, &nn->nfsd4_lease, nn); 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci/* 100862306a36Sopenharmony_ci * write_gracetime - Set or report current NFSv4 grace period time 100962306a36Sopenharmony_ci * 101062306a36Sopenharmony_ci * As above, but sets the time of the NFSv4 grace period. 101162306a36Sopenharmony_ci * 101262306a36Sopenharmony_ci * Note this should never be set to less than the *previous* 101362306a36Sopenharmony_ci * lease-period time, but we don't try to enforce this. (In the common 101462306a36Sopenharmony_ci * case (a new boot), we don't know what the previous lease time was 101562306a36Sopenharmony_ci * anyway.) 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_cistatic ssize_t write_gracetime(struct file *file, char *buf, size_t size) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); 102062306a36Sopenharmony_ci return nfsd4_write_time(file, buf, size, &nn->nfsd4_grace, nn); 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic ssize_t __write_recoverydir(struct file *file, char *buf, size_t size, 102462306a36Sopenharmony_ci struct nfsd_net *nn) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci char *mesg = buf; 102762306a36Sopenharmony_ci char *recdir; 102862306a36Sopenharmony_ci int len, status; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (size > 0) { 103162306a36Sopenharmony_ci if (nn->nfsd_serv) 103262306a36Sopenharmony_ci return -EBUSY; 103362306a36Sopenharmony_ci if (size > PATH_MAX || buf[size-1] != '\n') 103462306a36Sopenharmony_ci return -EINVAL; 103562306a36Sopenharmony_ci buf[size-1] = 0; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci recdir = mesg; 103862306a36Sopenharmony_ci len = qword_get(&mesg, recdir, size); 103962306a36Sopenharmony_ci if (len <= 0) 104062306a36Sopenharmony_ci return -EINVAL; 104162306a36Sopenharmony_ci trace_nfsd_ctl_recoverydir(netns(file), recdir); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci status = nfs4_reset_recoverydir(recdir); 104462306a36Sopenharmony_ci if (status) 104562306a36Sopenharmony_ci return status; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%s\n", 104962306a36Sopenharmony_ci nfs4_recoverydir()); 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci/* 105362306a36Sopenharmony_ci * write_recoverydir - Set or report the pathname of the recovery directory 105462306a36Sopenharmony_ci * 105562306a36Sopenharmony_ci * Input: 105662306a36Sopenharmony_ci * buf: ignored 105762306a36Sopenharmony_ci * size: zero 105862306a36Sopenharmony_ci * 105962306a36Sopenharmony_ci * OR 106062306a36Sopenharmony_ci * 106162306a36Sopenharmony_ci * Input: 106262306a36Sopenharmony_ci * buf: C string containing the pathname 106362306a36Sopenharmony_ci * of the directory on a local file 106462306a36Sopenharmony_ci * system containing permanent NFSv4 106562306a36Sopenharmony_ci * recovery data 106662306a36Sopenharmony_ci * size: non-zero length of C string in @buf 106762306a36Sopenharmony_ci * Output: 106862306a36Sopenharmony_ci * On success: passed-in buffer filled with '\n'-terminated C string 106962306a36Sopenharmony_ci * containing the current recovery pathname setting; 107062306a36Sopenharmony_ci * return code is the size in bytes of the string 107162306a36Sopenharmony_ci * On error: return code is zero or a negative errno value 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_cistatic ssize_t write_recoverydir(struct file *file, char *buf, size_t size) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci ssize_t rv; 107662306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci mutex_lock(&nfsd_mutex); 107962306a36Sopenharmony_ci rv = __write_recoverydir(file, buf, size, nn); 108062306a36Sopenharmony_ci mutex_unlock(&nfsd_mutex); 108162306a36Sopenharmony_ci return rv; 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci/* 108562306a36Sopenharmony_ci * write_v4_end_grace - release grace period for nfsd's v4.x lock manager 108662306a36Sopenharmony_ci * 108762306a36Sopenharmony_ci * Input: 108862306a36Sopenharmony_ci * buf: ignored 108962306a36Sopenharmony_ci * size: zero 109062306a36Sopenharmony_ci * OR 109162306a36Sopenharmony_ci * 109262306a36Sopenharmony_ci * Input: 109362306a36Sopenharmony_ci * buf: any value 109462306a36Sopenharmony_ci * size: non-zero length of C string in @buf 109562306a36Sopenharmony_ci * Output: 109662306a36Sopenharmony_ci * passed-in buffer filled with "Y" or "N" with a newline 109762306a36Sopenharmony_ci * and NULL-terminated C string. This indicates whether 109862306a36Sopenharmony_ci * the grace period has ended in the current net 109962306a36Sopenharmony_ci * namespace. Return code is the size in bytes of the 110062306a36Sopenharmony_ci * string. Writing a string that starts with 'Y', 'y', or 110162306a36Sopenharmony_ci * '1' to the file will end the grace period for nfsd's v4 110262306a36Sopenharmony_ci * lock manager. 110362306a36Sopenharmony_ci */ 110462306a36Sopenharmony_cistatic ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (size > 0) { 110962306a36Sopenharmony_ci switch(buf[0]) { 111062306a36Sopenharmony_ci case 'Y': 111162306a36Sopenharmony_ci case 'y': 111262306a36Sopenharmony_ci case '1': 111362306a36Sopenharmony_ci if (!nn->nfsd_serv) 111462306a36Sopenharmony_ci return -EBUSY; 111562306a36Sopenharmony_ci trace_nfsd_end_grace(netns(file)); 111662306a36Sopenharmony_ci nfsd4_end_grace(nn); 111762306a36Sopenharmony_ci break; 111862306a36Sopenharmony_ci default: 111962306a36Sopenharmony_ci return -EINVAL; 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n", 112462306a36Sopenharmony_ci nn->grace_ended ? 'Y' : 'N'); 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci#endif 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 113062306a36Sopenharmony_ci/* 113162306a36Sopenharmony_ci * populating the filesystem. 113262306a36Sopenharmony_ci */ 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci/* Basically copying rpc_get_inode. */ 113562306a36Sopenharmony_cistatic struct inode *nfsd_get_inode(struct super_block *sb, umode_t mode) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci struct inode *inode = new_inode(sb); 113862306a36Sopenharmony_ci if (!inode) 113962306a36Sopenharmony_ci return NULL; 114062306a36Sopenharmony_ci /* Following advice from simple_fill_super documentation: */ 114162306a36Sopenharmony_ci inode->i_ino = iunique(sb, NFSD_MaxReserved); 114262306a36Sopenharmony_ci inode->i_mode = mode; 114362306a36Sopenharmony_ci inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode); 114462306a36Sopenharmony_ci switch (mode & S_IFMT) { 114562306a36Sopenharmony_ci case S_IFDIR: 114662306a36Sopenharmony_ci inode->i_fop = &simple_dir_operations; 114762306a36Sopenharmony_ci inode->i_op = &simple_dir_inode_operations; 114862306a36Sopenharmony_ci inc_nlink(inode); 114962306a36Sopenharmony_ci break; 115062306a36Sopenharmony_ci case S_IFLNK: 115162306a36Sopenharmony_ci inode->i_op = &simple_symlink_inode_operations; 115262306a36Sopenharmony_ci break; 115362306a36Sopenharmony_ci default: 115462306a36Sopenharmony_ci break; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci return inode; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic int __nfsd_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode, struct nfsdfs_client *ncl) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci struct inode *inode; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci inode = nfsd_get_inode(dir->i_sb, mode); 116462306a36Sopenharmony_ci if (!inode) 116562306a36Sopenharmony_ci return -ENOMEM; 116662306a36Sopenharmony_ci if (ncl) { 116762306a36Sopenharmony_ci inode->i_private = ncl; 116862306a36Sopenharmony_ci kref_get(&ncl->cl_ref); 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci d_add(dentry, inode); 117162306a36Sopenharmony_ci inc_nlink(dir); 117262306a36Sopenharmony_ci fsnotify_mkdir(dir, dentry); 117362306a36Sopenharmony_ci return 0; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic struct dentry *nfsd_mkdir(struct dentry *parent, struct nfsdfs_client *ncl, char *name) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct inode *dir = parent->d_inode; 117962306a36Sopenharmony_ci struct dentry *dentry; 118062306a36Sopenharmony_ci int ret = -ENOMEM; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci inode_lock(dir); 118362306a36Sopenharmony_ci dentry = d_alloc_name(parent, name); 118462306a36Sopenharmony_ci if (!dentry) 118562306a36Sopenharmony_ci goto out_err; 118662306a36Sopenharmony_ci ret = __nfsd_mkdir(d_inode(parent), dentry, S_IFDIR | 0600, ncl); 118762306a36Sopenharmony_ci if (ret) 118862306a36Sopenharmony_ci goto out_err; 118962306a36Sopenharmony_ciout: 119062306a36Sopenharmony_ci inode_unlock(dir); 119162306a36Sopenharmony_ci return dentry; 119262306a36Sopenharmony_ciout_err: 119362306a36Sopenharmony_ci dput(dentry); 119462306a36Sopenharmony_ci dentry = ERR_PTR(ret); 119562306a36Sopenharmony_ci goto out; 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_GSS) 119962306a36Sopenharmony_cistatic int __nfsd_symlink(struct inode *dir, struct dentry *dentry, 120062306a36Sopenharmony_ci umode_t mode, const char *content) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci struct inode *inode; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci inode = nfsd_get_inode(dir->i_sb, mode); 120562306a36Sopenharmony_ci if (!inode) 120662306a36Sopenharmony_ci return -ENOMEM; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci inode->i_link = (char *)content; 120962306a36Sopenharmony_ci inode->i_size = strlen(content); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci d_add(dentry, inode); 121262306a36Sopenharmony_ci inc_nlink(dir); 121362306a36Sopenharmony_ci fsnotify_create(dir, dentry); 121462306a36Sopenharmony_ci return 0; 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci/* 121862306a36Sopenharmony_ci * @content is assumed to be a NUL-terminated string that lives 121962306a36Sopenharmony_ci * longer than the symlink itself. 122062306a36Sopenharmony_ci */ 122162306a36Sopenharmony_cistatic void _nfsd_symlink(struct dentry *parent, const char *name, 122262306a36Sopenharmony_ci const char *content) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci struct inode *dir = parent->d_inode; 122562306a36Sopenharmony_ci struct dentry *dentry; 122662306a36Sopenharmony_ci int ret; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci inode_lock(dir); 122962306a36Sopenharmony_ci dentry = d_alloc_name(parent, name); 123062306a36Sopenharmony_ci if (!dentry) 123162306a36Sopenharmony_ci goto out; 123262306a36Sopenharmony_ci ret = __nfsd_symlink(d_inode(parent), dentry, S_IFLNK | 0777, content); 123362306a36Sopenharmony_ci if (ret) 123462306a36Sopenharmony_ci dput(dentry); 123562306a36Sopenharmony_ciout: 123662306a36Sopenharmony_ci inode_unlock(dir); 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci#else 123962306a36Sopenharmony_cistatic inline void _nfsd_symlink(struct dentry *parent, const char *name, 124062306a36Sopenharmony_ci const char *content) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci#endif 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_cistatic void clear_ncl(struct inode *inode) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci struct nfsdfs_client *ncl = inode->i_private; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci inode->i_private = NULL; 125162306a36Sopenharmony_ci kref_put(&ncl->cl_ref, ncl->cl_release); 125262306a36Sopenharmony_ci} 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cistatic struct nfsdfs_client *__get_nfsdfs_client(struct inode *inode) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct nfsdfs_client *nc = inode->i_private; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (nc) 125962306a36Sopenharmony_ci kref_get(&nc->cl_ref); 126062306a36Sopenharmony_ci return nc; 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_cistruct nfsdfs_client *get_nfsdfs_client(struct inode *inode) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci struct nfsdfs_client *nc; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci inode_lock_shared(inode); 126862306a36Sopenharmony_ci nc = __get_nfsdfs_client(inode); 126962306a36Sopenharmony_ci inode_unlock_shared(inode); 127062306a36Sopenharmony_ci return nc; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci/* from __rpc_unlink */ 127362306a36Sopenharmony_cistatic void nfsdfs_remove_file(struct inode *dir, struct dentry *dentry) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci int ret; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci clear_ncl(d_inode(dentry)); 127862306a36Sopenharmony_ci dget(dentry); 127962306a36Sopenharmony_ci ret = simple_unlink(dir, dentry); 128062306a36Sopenharmony_ci d_drop(dentry); 128162306a36Sopenharmony_ci fsnotify_unlink(dir, dentry); 128262306a36Sopenharmony_ci dput(dentry); 128362306a36Sopenharmony_ci WARN_ON_ONCE(ret); 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_cistatic void nfsdfs_remove_files(struct dentry *root) 128762306a36Sopenharmony_ci{ 128862306a36Sopenharmony_ci struct dentry *dentry, *tmp; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci list_for_each_entry_safe(dentry, tmp, &root->d_subdirs, d_child) { 129162306a36Sopenharmony_ci if (!simple_positive(dentry)) { 129262306a36Sopenharmony_ci WARN_ON_ONCE(1); /* I think this can't happen? */ 129362306a36Sopenharmony_ci continue; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci nfsdfs_remove_file(d_inode(root), dentry); 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci/* XXX: cut'n'paste from simple_fill_super; figure out if we could share 130062306a36Sopenharmony_ci * code instead. */ 130162306a36Sopenharmony_cistatic int nfsdfs_create_files(struct dentry *root, 130262306a36Sopenharmony_ci const struct tree_descr *files, 130362306a36Sopenharmony_ci struct dentry **fdentries) 130462306a36Sopenharmony_ci{ 130562306a36Sopenharmony_ci struct inode *dir = d_inode(root); 130662306a36Sopenharmony_ci struct inode *inode; 130762306a36Sopenharmony_ci struct dentry *dentry; 130862306a36Sopenharmony_ci int i; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci inode_lock(dir); 131162306a36Sopenharmony_ci for (i = 0; files->name && files->name[0]; i++, files++) { 131262306a36Sopenharmony_ci dentry = d_alloc_name(root, files->name); 131362306a36Sopenharmony_ci if (!dentry) 131462306a36Sopenharmony_ci goto out; 131562306a36Sopenharmony_ci inode = nfsd_get_inode(d_inode(root)->i_sb, 131662306a36Sopenharmony_ci S_IFREG | files->mode); 131762306a36Sopenharmony_ci if (!inode) { 131862306a36Sopenharmony_ci dput(dentry); 131962306a36Sopenharmony_ci goto out; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci inode->i_fop = files->ops; 132262306a36Sopenharmony_ci inode->i_private = __get_nfsdfs_client(dir); 132362306a36Sopenharmony_ci d_add(dentry, inode); 132462306a36Sopenharmony_ci fsnotify_create(dir, dentry); 132562306a36Sopenharmony_ci if (fdentries) 132662306a36Sopenharmony_ci fdentries[i] = dentry; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci inode_unlock(dir); 132962306a36Sopenharmony_ci return 0; 133062306a36Sopenharmony_ciout: 133162306a36Sopenharmony_ci nfsdfs_remove_files(root); 133262306a36Sopenharmony_ci inode_unlock(dir); 133362306a36Sopenharmony_ci return -ENOMEM; 133462306a36Sopenharmony_ci} 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci/* on success, returns positive number unique to that client. */ 133762306a36Sopenharmony_cistruct dentry *nfsd_client_mkdir(struct nfsd_net *nn, 133862306a36Sopenharmony_ci struct nfsdfs_client *ncl, u32 id, 133962306a36Sopenharmony_ci const struct tree_descr *files, 134062306a36Sopenharmony_ci struct dentry **fdentries) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci struct dentry *dentry; 134362306a36Sopenharmony_ci char name[11]; 134462306a36Sopenharmony_ci int ret; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci sprintf(name, "%u", id); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name); 134962306a36Sopenharmony_ci if (IS_ERR(dentry)) /* XXX: tossing errors? */ 135062306a36Sopenharmony_ci return NULL; 135162306a36Sopenharmony_ci ret = nfsdfs_create_files(dentry, files, fdentries); 135262306a36Sopenharmony_ci if (ret) { 135362306a36Sopenharmony_ci nfsd_client_rmdir(dentry); 135462306a36Sopenharmony_ci return NULL; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci return dentry; 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci/* Taken from __rpc_rmdir: */ 136062306a36Sopenharmony_civoid nfsd_client_rmdir(struct dentry *dentry) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci struct inode *dir = d_inode(dentry->d_parent); 136362306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 136462306a36Sopenharmony_ci int ret; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci inode_lock(dir); 136762306a36Sopenharmony_ci nfsdfs_remove_files(dentry); 136862306a36Sopenharmony_ci clear_ncl(inode); 136962306a36Sopenharmony_ci dget(dentry); 137062306a36Sopenharmony_ci ret = simple_rmdir(dir, dentry); 137162306a36Sopenharmony_ci WARN_ON_ONCE(ret); 137262306a36Sopenharmony_ci d_drop(dentry); 137362306a36Sopenharmony_ci fsnotify_rmdir(dir, dentry); 137462306a36Sopenharmony_ci dput(dentry); 137562306a36Sopenharmony_ci inode_unlock(dir); 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cistatic int nfsd_fill_super(struct super_block *sb, struct fs_context *fc) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, 138162306a36Sopenharmony_ci nfsd_net_id); 138262306a36Sopenharmony_ci struct dentry *dentry; 138362306a36Sopenharmony_ci int ret; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci static const struct tree_descr nfsd_files[] = { 138662306a36Sopenharmony_ci [NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO}, 138762306a36Sopenharmony_ci /* Per-export io stats use same ops as exports file */ 138862306a36Sopenharmony_ci [NFSD_Export_Stats] = {"export_stats", &exports_nfsd_operations, S_IRUGO}, 138962306a36Sopenharmony_ci [NFSD_Export_features] = {"export_features", 139062306a36Sopenharmony_ci &export_features_fops, S_IRUGO}, 139162306a36Sopenharmony_ci [NFSD_FO_UnlockIP] = {"unlock_ip", 139262306a36Sopenharmony_ci &transaction_ops, S_IWUSR|S_IRUSR}, 139362306a36Sopenharmony_ci [NFSD_FO_UnlockFS] = {"unlock_filesystem", 139462306a36Sopenharmony_ci &transaction_ops, S_IWUSR|S_IRUSR}, 139562306a36Sopenharmony_ci [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, 139662306a36Sopenharmony_ci [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, 139762306a36Sopenharmony_ci [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, 139862306a36Sopenharmony_ci [NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO}, 139962306a36Sopenharmony_ci [NFSD_Reply_Cache_Stats] = {"reply_cache_stats", 140062306a36Sopenharmony_ci &nfsd_reply_cache_stats_fops, S_IRUGO}, 140162306a36Sopenharmony_ci [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, 140262306a36Sopenharmony_ci [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, 140362306a36Sopenharmony_ci [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, 140462306a36Sopenharmony_ci [NFSD_MaxConnections] = {"max_connections", &transaction_ops, S_IWUSR|S_IRUGO}, 140562306a36Sopenharmony_ci [NFSD_Filecache] = {"filecache", &nfsd_file_cache_stats_fops, S_IRUGO}, 140662306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4 140762306a36Sopenharmony_ci [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, 140862306a36Sopenharmony_ci [NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR}, 140962306a36Sopenharmony_ci [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, 141062306a36Sopenharmony_ci [NFSD_V4EndGrace] = {"v4_end_grace", &transaction_ops, S_IWUSR|S_IRUGO}, 141162306a36Sopenharmony_ci#endif 141262306a36Sopenharmony_ci /* last one */ {""} 141362306a36Sopenharmony_ci }; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci ret = simple_fill_super(sb, 0x6e667364, nfsd_files); 141662306a36Sopenharmony_ci if (ret) 141762306a36Sopenharmony_ci return ret; 141862306a36Sopenharmony_ci _nfsd_symlink(sb->s_root, "supported_krb5_enctypes", 141962306a36Sopenharmony_ci "/proc/net/rpc/gss_krb5_enctypes"); 142062306a36Sopenharmony_ci dentry = nfsd_mkdir(sb->s_root, NULL, "clients"); 142162306a36Sopenharmony_ci if (IS_ERR(dentry)) 142262306a36Sopenharmony_ci return PTR_ERR(dentry); 142362306a36Sopenharmony_ci nn->nfsd_client_dir = dentry; 142462306a36Sopenharmony_ci return 0; 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_cistatic int nfsd_fs_get_tree(struct fs_context *fc) 142862306a36Sopenharmony_ci{ 142962306a36Sopenharmony_ci return get_tree_keyed(fc, nfsd_fill_super, get_net(fc->net_ns)); 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_cistatic void nfsd_fs_free_fc(struct fs_context *fc) 143362306a36Sopenharmony_ci{ 143462306a36Sopenharmony_ci if (fc->s_fs_info) 143562306a36Sopenharmony_ci put_net(fc->s_fs_info); 143662306a36Sopenharmony_ci} 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic const struct fs_context_operations nfsd_fs_context_ops = { 143962306a36Sopenharmony_ci .free = nfsd_fs_free_fc, 144062306a36Sopenharmony_ci .get_tree = nfsd_fs_get_tree, 144162306a36Sopenharmony_ci}; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic int nfsd_init_fs_context(struct fs_context *fc) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci put_user_ns(fc->user_ns); 144662306a36Sopenharmony_ci fc->user_ns = get_user_ns(fc->net_ns->user_ns); 144762306a36Sopenharmony_ci fc->ops = &nfsd_fs_context_ops; 144862306a36Sopenharmony_ci return 0; 144962306a36Sopenharmony_ci} 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_cistatic void nfsd_umount(struct super_block *sb) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci struct net *net = sb->s_fs_info; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci nfsd_shutdown_threads(net); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci kill_litter_super(sb); 145862306a36Sopenharmony_ci put_net(net); 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_cistatic struct file_system_type nfsd_fs_type = { 146262306a36Sopenharmony_ci .owner = THIS_MODULE, 146362306a36Sopenharmony_ci .name = "nfsd", 146462306a36Sopenharmony_ci .init_fs_context = nfsd_init_fs_context, 146562306a36Sopenharmony_ci .kill_sb = nfsd_umount, 146662306a36Sopenharmony_ci}; 146762306a36Sopenharmony_ciMODULE_ALIAS_FS("nfsd"); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_cistatic int exports_proc_open(struct inode *inode, struct file *file) 147262306a36Sopenharmony_ci{ 147362306a36Sopenharmony_ci return exports_net_open(current->nsproxy->net_ns, file); 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic const struct proc_ops exports_proc_ops = { 147762306a36Sopenharmony_ci .proc_open = exports_proc_open, 147862306a36Sopenharmony_ci .proc_read = seq_read, 147962306a36Sopenharmony_ci .proc_lseek = seq_lseek, 148062306a36Sopenharmony_ci .proc_release = seq_release, 148162306a36Sopenharmony_ci}; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_cistatic int create_proc_exports_entry(void) 148462306a36Sopenharmony_ci{ 148562306a36Sopenharmony_ci struct proc_dir_entry *entry; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci entry = proc_mkdir("fs/nfs", NULL); 148862306a36Sopenharmony_ci if (!entry) 148962306a36Sopenharmony_ci return -ENOMEM; 149062306a36Sopenharmony_ci entry = proc_create("exports", 0, entry, &exports_proc_ops); 149162306a36Sopenharmony_ci if (!entry) { 149262306a36Sopenharmony_ci remove_proc_entry("fs/nfs", NULL); 149362306a36Sopenharmony_ci return -ENOMEM; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci return 0; 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ci#else /* CONFIG_PROC_FS */ 149862306a36Sopenharmony_cistatic int create_proc_exports_entry(void) 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci return 0; 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci#endif 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ciunsigned int nfsd_net_id; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci/** 150762306a36Sopenharmony_ci * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace 150862306a36Sopenharmony_ci * @net: a freshly-created network namespace 150962306a36Sopenharmony_ci * 151062306a36Sopenharmony_ci * This information stays around as long as the network namespace is 151162306a36Sopenharmony_ci * alive whether or not there is an NFSD instance running in the 151262306a36Sopenharmony_ci * namespace. 151362306a36Sopenharmony_ci * 151462306a36Sopenharmony_ci * Returns zero on success, or a negative errno otherwise. 151562306a36Sopenharmony_ci */ 151662306a36Sopenharmony_cistatic __net_init int nfsd_net_init(struct net *net) 151762306a36Sopenharmony_ci{ 151862306a36Sopenharmony_ci int retval; 151962306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(net, nfsd_net_id); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci retval = nfsd_export_init(net); 152262306a36Sopenharmony_ci if (retval) 152362306a36Sopenharmony_ci goto out_export_error; 152462306a36Sopenharmony_ci retval = nfsd_idmap_init(net); 152562306a36Sopenharmony_ci if (retval) 152662306a36Sopenharmony_ci goto out_idmap_error; 152762306a36Sopenharmony_ci retval = nfsd_net_reply_cache_init(nn); 152862306a36Sopenharmony_ci if (retval) 152962306a36Sopenharmony_ci goto out_repcache_error; 153062306a36Sopenharmony_ci nn->nfsd_versions = NULL; 153162306a36Sopenharmony_ci nn->nfsd4_minorversions = NULL; 153262306a36Sopenharmony_ci nfsd4_init_leases_net(nn); 153362306a36Sopenharmony_ci get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key)); 153462306a36Sopenharmony_ci seqlock_init(&nn->writeverf_lock); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci return 0; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ciout_repcache_error: 153962306a36Sopenharmony_ci nfsd_idmap_shutdown(net); 154062306a36Sopenharmony_ciout_idmap_error: 154162306a36Sopenharmony_ci nfsd_export_shutdown(net); 154262306a36Sopenharmony_ciout_export_error: 154362306a36Sopenharmony_ci return retval; 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci/** 154762306a36Sopenharmony_ci * nfsd_net_exit - Release the nfsd_net portion of a net namespace 154862306a36Sopenharmony_ci * @net: a network namespace that is about to be destroyed 154962306a36Sopenharmony_ci * 155062306a36Sopenharmony_ci */ 155162306a36Sopenharmony_cistatic __net_exit void nfsd_net_exit(struct net *net) 155262306a36Sopenharmony_ci{ 155362306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(net, nfsd_net_id); 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci nfsd_net_reply_cache_destroy(nn); 155662306a36Sopenharmony_ci nfsd_idmap_shutdown(net); 155762306a36Sopenharmony_ci nfsd_export_shutdown(net); 155862306a36Sopenharmony_ci nfsd_netns_free_versions(nn); 155962306a36Sopenharmony_ci} 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_cistatic struct pernet_operations nfsd_net_ops = { 156262306a36Sopenharmony_ci .init = nfsd_net_init, 156362306a36Sopenharmony_ci .exit = nfsd_net_exit, 156462306a36Sopenharmony_ci .id = &nfsd_net_id, 156562306a36Sopenharmony_ci .size = sizeof(struct nfsd_net), 156662306a36Sopenharmony_ci}; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_cistatic int __init init_nfsd(void) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci int retval; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci retval = nfsd4_init_slabs(); 157362306a36Sopenharmony_ci if (retval) 157462306a36Sopenharmony_ci return retval; 157562306a36Sopenharmony_ci retval = nfsd4_init_pnfs(); 157662306a36Sopenharmony_ci if (retval) 157762306a36Sopenharmony_ci goto out_free_slabs; 157862306a36Sopenharmony_ci retval = nfsd_stat_init(); /* Statistics */ 157962306a36Sopenharmony_ci if (retval) 158062306a36Sopenharmony_ci goto out_free_pnfs; 158162306a36Sopenharmony_ci retval = nfsd_drc_slab_create(); 158262306a36Sopenharmony_ci if (retval) 158362306a36Sopenharmony_ci goto out_free_stat; 158462306a36Sopenharmony_ci nfsd_lockd_init(); /* lockd->nfsd callbacks */ 158562306a36Sopenharmony_ci retval = create_proc_exports_entry(); 158662306a36Sopenharmony_ci if (retval) 158762306a36Sopenharmony_ci goto out_free_lockd; 158862306a36Sopenharmony_ci retval = register_pernet_subsys(&nfsd_net_ops); 158962306a36Sopenharmony_ci if (retval < 0) 159062306a36Sopenharmony_ci goto out_free_exports; 159162306a36Sopenharmony_ci retval = register_cld_notifier(); 159262306a36Sopenharmony_ci if (retval) 159362306a36Sopenharmony_ci goto out_free_subsys; 159462306a36Sopenharmony_ci retval = nfsd4_create_laundry_wq(); 159562306a36Sopenharmony_ci if (retval) 159662306a36Sopenharmony_ci goto out_free_cld; 159762306a36Sopenharmony_ci retval = register_filesystem(&nfsd_fs_type); 159862306a36Sopenharmony_ci if (retval) 159962306a36Sopenharmony_ci goto out_free_all; 160062306a36Sopenharmony_ci return 0; 160162306a36Sopenharmony_ciout_free_all: 160262306a36Sopenharmony_ci nfsd4_destroy_laundry_wq(); 160362306a36Sopenharmony_ciout_free_cld: 160462306a36Sopenharmony_ci unregister_cld_notifier(); 160562306a36Sopenharmony_ciout_free_subsys: 160662306a36Sopenharmony_ci unregister_pernet_subsys(&nfsd_net_ops); 160762306a36Sopenharmony_ciout_free_exports: 160862306a36Sopenharmony_ci remove_proc_entry("fs/nfs/exports", NULL); 160962306a36Sopenharmony_ci remove_proc_entry("fs/nfs", NULL); 161062306a36Sopenharmony_ciout_free_lockd: 161162306a36Sopenharmony_ci nfsd_lockd_shutdown(); 161262306a36Sopenharmony_ci nfsd_drc_slab_free(); 161362306a36Sopenharmony_ciout_free_stat: 161462306a36Sopenharmony_ci nfsd_stat_shutdown(); 161562306a36Sopenharmony_ciout_free_pnfs: 161662306a36Sopenharmony_ci nfsd4_exit_pnfs(); 161762306a36Sopenharmony_ciout_free_slabs: 161862306a36Sopenharmony_ci nfsd4_free_slabs(); 161962306a36Sopenharmony_ci return retval; 162062306a36Sopenharmony_ci} 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_cistatic void __exit exit_nfsd(void) 162362306a36Sopenharmony_ci{ 162462306a36Sopenharmony_ci unregister_filesystem(&nfsd_fs_type); 162562306a36Sopenharmony_ci nfsd4_destroy_laundry_wq(); 162662306a36Sopenharmony_ci unregister_cld_notifier(); 162762306a36Sopenharmony_ci unregister_pernet_subsys(&nfsd_net_ops); 162862306a36Sopenharmony_ci nfsd_drc_slab_free(); 162962306a36Sopenharmony_ci remove_proc_entry("fs/nfs/exports", NULL); 163062306a36Sopenharmony_ci remove_proc_entry("fs/nfs", NULL); 163162306a36Sopenharmony_ci nfsd_stat_shutdown(); 163262306a36Sopenharmony_ci nfsd_lockd_shutdown(); 163362306a36Sopenharmony_ci nfsd4_free_slabs(); 163462306a36Sopenharmony_ci nfsd4_exit_pnfs(); 163562306a36Sopenharmony_ci} 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ciMODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); 163862306a36Sopenharmony_ciMODULE_DESCRIPTION("In-kernel NFS server"); 163962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 164062306a36Sopenharmony_cimodule_init(init_nfsd) 164162306a36Sopenharmony_cimodule_exit(exit_nfsd) 1642