162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Server-side procedures for NFSv4. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2002 The Regents of the University of Michigan. 562306a36Sopenharmony_ci * All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Kendrick Smith <kmsmith@umich.edu> 862306a36Sopenharmony_ci * Andy Adamson <andros@umich.edu> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1162306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 1262306a36Sopenharmony_ci * are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1562306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1662306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1762306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1862306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 1962306a36Sopenharmony_ci * 3. Neither the name of the University nor the names of its 2062306a36Sopenharmony_ci * contributors may be used to endorse or promote products derived 2162306a36Sopenharmony_ci * from this software without specific prior written permission. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 2462306a36Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 2662306a36Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2762306a36Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2862306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2962306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 3062306a36Sopenharmony_ci * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 3162306a36Sopenharmony_ci * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 3262306a36Sopenharmony_ci * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 3362306a36Sopenharmony_ci * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci#include <linux/fs_struct.h> 3662306a36Sopenharmony_ci#include <linux/file.h> 3762306a36Sopenharmony_ci#include <linux/falloc.h> 3862306a36Sopenharmony_ci#include <linux/slab.h> 3962306a36Sopenharmony_ci#include <linux/kthread.h> 4062306a36Sopenharmony_ci#include <linux/namei.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <linux/sunrpc/addr.h> 4362306a36Sopenharmony_ci#include <linux/nfs_ssc.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include "idmap.h" 4662306a36Sopenharmony_ci#include "cache.h" 4762306a36Sopenharmony_ci#include "xdr4.h" 4862306a36Sopenharmony_ci#include "vfs.h" 4962306a36Sopenharmony_ci#include "current_stateid.h" 5062306a36Sopenharmony_ci#include "netns.h" 5162306a36Sopenharmony_ci#include "acl.h" 5262306a36Sopenharmony_ci#include "pnfs.h" 5362306a36Sopenharmony_ci#include "trace.h" 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic bool inter_copy_offload_enable; 5662306a36Sopenharmony_cimodule_param(inter_copy_offload_enable, bool, 0644); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(inter_copy_offload_enable, 5862306a36Sopenharmony_ci "Enable inter server to server copy offload. Default: false"); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_2_INTER_SSC 6162306a36Sopenharmony_cistatic int nfsd4_ssc_umount_timeout = 900000; /* default to 15 mins */ 6262306a36Sopenharmony_cimodule_param(nfsd4_ssc_umount_timeout, int, 0644); 6362306a36Sopenharmony_ciMODULE_PARM_DESC(nfsd4_ssc_umount_timeout, 6462306a36Sopenharmony_ci "idle msecs before unmount export from source server"); 6562306a36Sopenharmony_ci#endif 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define NFSDDBG_FACILITY NFSDDBG_PROC 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic u32 nfsd_attrmask[] = { 7062306a36Sopenharmony_ci NFSD_WRITEABLE_ATTRS_WORD0, 7162306a36Sopenharmony_ci NFSD_WRITEABLE_ATTRS_WORD1, 7262306a36Sopenharmony_ci NFSD_WRITEABLE_ATTRS_WORD2 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic u32 nfsd41_ex_attrmask[] = { 7662306a36Sopenharmony_ci NFSD_SUPPATTR_EXCLCREAT_WORD0, 7762306a36Sopenharmony_ci NFSD_SUPPATTR_EXCLCREAT_WORD1, 7862306a36Sopenharmony_ci NFSD_SUPPATTR_EXCLCREAT_WORD2 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic __be32 8262306a36Sopenharmony_cicheck_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 8362306a36Sopenharmony_ci u32 *bmval, u32 *writable) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct dentry *dentry = cstate->current_fh.fh_dentry; 8662306a36Sopenharmony_ci struct svc_export *exp = cstate->current_fh.fh_export; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (!nfsd_attrs_supported(cstate->minorversion, bmval)) 8962306a36Sopenharmony_ci return nfserr_attrnotsupp; 9062306a36Sopenharmony_ci if ((bmval[0] & FATTR4_WORD0_ACL) && !IS_POSIXACL(d_inode(dentry))) 9162306a36Sopenharmony_ci return nfserr_attrnotsupp; 9262306a36Sopenharmony_ci if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) && 9362306a36Sopenharmony_ci !(exp->ex_flags & NFSEXP_SECURITY_LABEL)) 9462306a36Sopenharmony_ci return nfserr_attrnotsupp; 9562306a36Sopenharmony_ci if (writable && !bmval_is_subset(bmval, writable)) 9662306a36Sopenharmony_ci return nfserr_inval; 9762306a36Sopenharmony_ci if (writable && (bmval[2] & FATTR4_WORD2_MODE_UMASK) && 9862306a36Sopenharmony_ci (bmval[1] & FATTR4_WORD1_MODE)) 9962306a36Sopenharmony_ci return nfserr_inval; 10062306a36Sopenharmony_ci return nfs_ok; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic __be32 10462306a36Sopenharmony_cinfsd4_check_open_attributes(struct svc_rqst *rqstp, 10562306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, struct nfsd4_open *open) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci __be32 status = nfs_ok; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (open->op_create == NFS4_OPEN_CREATE) { 11062306a36Sopenharmony_ci if (open->op_createmode == NFS4_CREATE_UNCHECKED 11162306a36Sopenharmony_ci || open->op_createmode == NFS4_CREATE_GUARDED) 11262306a36Sopenharmony_ci status = check_attr_support(rqstp, cstate, 11362306a36Sopenharmony_ci open->op_bmval, nfsd_attrmask); 11462306a36Sopenharmony_ci else if (open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1) 11562306a36Sopenharmony_ci status = check_attr_support(rqstp, cstate, 11662306a36Sopenharmony_ci open->op_bmval, nfsd41_ex_attrmask); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return status; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int 12362306a36Sopenharmony_ciis_create_with_attrs(struct nfsd4_open *open) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return open->op_create == NFS4_OPEN_CREATE 12662306a36Sopenharmony_ci && (open->op_createmode == NFS4_CREATE_UNCHECKED 12762306a36Sopenharmony_ci || open->op_createmode == NFS4_CREATE_GUARDED 12862306a36Sopenharmony_ci || open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic inline void 13262306a36Sopenharmony_cifh_dup2(struct svc_fh *dst, struct svc_fh *src) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci fh_put(dst); 13562306a36Sopenharmony_ci dget(src->fh_dentry); 13662306a36Sopenharmony_ci if (src->fh_export) 13762306a36Sopenharmony_ci exp_get(src->fh_export); 13862306a36Sopenharmony_ci *dst = *src; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic __be32 14262306a36Sopenharmony_cido_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open, int accmode) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (open->op_truncate && 14662306a36Sopenharmony_ci !(open->op_share_access & NFS4_SHARE_ACCESS_WRITE)) 14762306a36Sopenharmony_ci return nfserr_inval; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci accmode |= NFSD_MAY_READ_IF_EXEC; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (open->op_share_access & NFS4_SHARE_ACCESS_READ) 15262306a36Sopenharmony_ci accmode |= NFSD_MAY_READ; 15362306a36Sopenharmony_ci if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) 15462306a36Sopenharmony_ci accmode |= (NFSD_MAY_WRITE | NFSD_MAY_TRUNC); 15562306a36Sopenharmony_ci if (open->op_share_deny & NFS4_SHARE_DENY_READ) 15662306a36Sopenharmony_ci accmode |= NFSD_MAY_WRITE; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return fh_verify(rqstp, current_fh, S_IFREG, accmode); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic __be32 nfsd_check_obj_isreg(struct svc_fh *fh) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci umode_t mode = d_inode(fh->fh_dentry)->i_mode; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (S_ISREG(mode)) 16662306a36Sopenharmony_ci return nfs_ok; 16762306a36Sopenharmony_ci if (S_ISDIR(mode)) 16862306a36Sopenharmony_ci return nfserr_isdir; 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * Using err_symlink as our catch-all case may look odd; but 17162306a36Sopenharmony_ci * there's no other obvious error for this case in 4.0, and we 17262306a36Sopenharmony_ci * happen to know that it will cause the linux v4 client to do 17362306a36Sopenharmony_ci * the right thing on attempts to open something other than a 17462306a36Sopenharmony_ci * regular file. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci return nfserr_symlink; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void nfsd4_set_open_owner_reply_cache(struct nfsd4_compound_state *cstate, struct nfsd4_open *open, struct svc_fh *resfh) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci if (nfsd4_has_session(cstate)) 18262306a36Sopenharmony_ci return; 18362306a36Sopenharmony_ci fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh, 18462306a36Sopenharmony_ci &resfh->fh_handle); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic inline bool nfsd4_create_is_exclusive(int createmode) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci return createmode == NFS4_CREATE_EXCLUSIVE || 19062306a36Sopenharmony_ci createmode == NFS4_CREATE_EXCLUSIVE4_1; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic __be32 19462306a36Sopenharmony_cinfsd4_vfs_create(struct svc_fh *fhp, struct dentry *child, 19562306a36Sopenharmony_ci struct nfsd4_open *open) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct file *filp; 19862306a36Sopenharmony_ci struct path path; 19962306a36Sopenharmony_ci int oflags; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci oflags = O_CREAT | O_LARGEFILE; 20262306a36Sopenharmony_ci switch (open->op_share_access & NFS4_SHARE_ACCESS_BOTH) { 20362306a36Sopenharmony_ci case NFS4_SHARE_ACCESS_WRITE: 20462306a36Sopenharmony_ci oflags |= O_WRONLY; 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci case NFS4_SHARE_ACCESS_BOTH: 20762306a36Sopenharmony_ci oflags |= O_RDWR; 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci default: 21062306a36Sopenharmony_ci oflags |= O_RDONLY; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci path.mnt = fhp->fh_export->ex_path.mnt; 21462306a36Sopenharmony_ci path.dentry = child; 21562306a36Sopenharmony_ci filp = dentry_create(&path, oflags, open->op_iattr.ia_mode, 21662306a36Sopenharmony_ci current_cred()); 21762306a36Sopenharmony_ci if (IS_ERR(filp)) 21862306a36Sopenharmony_ci return nfserrno(PTR_ERR(filp)); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci open->op_filp = filp; 22162306a36Sopenharmony_ci return nfs_ok; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* 22562306a36Sopenharmony_ci * Implement NFSv4's unchecked, guarded, and exclusive create 22662306a36Sopenharmony_ci * semantics for regular files. Open state for this new file is 22762306a36Sopenharmony_ci * subsequently fabricated in nfsd4_process_open2(). 22862306a36Sopenharmony_ci * 22962306a36Sopenharmony_ci * Upon return, caller must release @fhp and @resfhp. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_cistatic __be32 23262306a36Sopenharmony_cinfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, 23362306a36Sopenharmony_ci struct svc_fh *resfhp, struct nfsd4_open *open) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct iattr *iap = &open->op_iattr; 23662306a36Sopenharmony_ci struct nfsd_attrs attrs = { 23762306a36Sopenharmony_ci .na_iattr = iap, 23862306a36Sopenharmony_ci .na_seclabel = &open->op_label, 23962306a36Sopenharmony_ci }; 24062306a36Sopenharmony_ci struct dentry *parent, *child; 24162306a36Sopenharmony_ci __u32 v_mtime, v_atime; 24262306a36Sopenharmony_ci struct inode *inode; 24362306a36Sopenharmony_ci __be32 status; 24462306a36Sopenharmony_ci int host_err; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (isdotent(open->op_fname, open->op_fnamelen)) 24762306a36Sopenharmony_ci return nfserr_exist; 24862306a36Sopenharmony_ci if (!(iap->ia_valid & ATTR_MODE)) 24962306a36Sopenharmony_ci iap->ia_mode = 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); 25262306a36Sopenharmony_ci if (status != nfs_ok) 25362306a36Sopenharmony_ci return status; 25462306a36Sopenharmony_ci parent = fhp->fh_dentry; 25562306a36Sopenharmony_ci inode = d_inode(parent); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci host_err = fh_want_write(fhp); 25862306a36Sopenharmony_ci if (host_err) 25962306a36Sopenharmony_ci return nfserrno(host_err); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (is_create_with_attrs(open)) 26262306a36Sopenharmony_ci nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci inode_lock_nested(inode, I_MUTEX_PARENT); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci child = lookup_one_len(open->op_fname, parent, open->op_fnamelen); 26762306a36Sopenharmony_ci if (IS_ERR(child)) { 26862306a36Sopenharmony_ci status = nfserrno(PTR_ERR(child)); 26962306a36Sopenharmony_ci goto out; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (d_really_is_negative(child)) { 27362306a36Sopenharmony_ci status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); 27462306a36Sopenharmony_ci if (status != nfs_ok) 27562306a36Sopenharmony_ci goto out; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci status = fh_compose(resfhp, fhp->fh_export, child, fhp); 27962306a36Sopenharmony_ci if (status != nfs_ok) 28062306a36Sopenharmony_ci goto out; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci v_mtime = 0; 28362306a36Sopenharmony_ci v_atime = 0; 28462306a36Sopenharmony_ci if (nfsd4_create_is_exclusive(open->op_createmode)) { 28562306a36Sopenharmony_ci u32 *verifier = (u32 *)open->op_verf.data; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * Solaris 7 gets confused (bugid 4218508) if these have 28962306a36Sopenharmony_ci * the high bit set, as do xfs filesystems without the 29062306a36Sopenharmony_ci * "bigtime" feature. So just clear the high bits. If this 29162306a36Sopenharmony_ci * is ever changed to use different attrs for storing the 29262306a36Sopenharmony_ci * verifier, then do_open_lookup() will also need to be 29362306a36Sopenharmony_ci * fixed accordingly. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci v_mtime = verifier[0] & 0x7fffffff; 29662306a36Sopenharmony_ci v_atime = verifier[1] & 0x7fffffff; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (d_really_is_positive(child)) { 30062306a36Sopenharmony_ci /* NFSv4 protocol requires change attributes even though 30162306a36Sopenharmony_ci * no change happened. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci status = fh_fill_both_attrs(fhp); 30462306a36Sopenharmony_ci if (status != nfs_ok) 30562306a36Sopenharmony_ci goto out; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci switch (open->op_createmode) { 30862306a36Sopenharmony_ci case NFS4_CREATE_UNCHECKED: 30962306a36Sopenharmony_ci if (!d_is_reg(child)) 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * In NFSv4, we don't want to truncate the file 31462306a36Sopenharmony_ci * now. This would be wrong if the OPEN fails for 31562306a36Sopenharmony_ci * some other reason. Furthermore, if the size is 31662306a36Sopenharmony_ci * nonzero, we should ignore it according to spec! 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci open->op_truncate = (iap->ia_valid & ATTR_SIZE) && 31962306a36Sopenharmony_ci !iap->ia_size; 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci case NFS4_CREATE_GUARDED: 32262306a36Sopenharmony_ci status = nfserr_exist; 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case NFS4_CREATE_EXCLUSIVE: 32562306a36Sopenharmony_ci if (d_inode(child)->i_mtime.tv_sec == v_mtime && 32662306a36Sopenharmony_ci d_inode(child)->i_atime.tv_sec == v_atime && 32762306a36Sopenharmony_ci d_inode(child)->i_size == 0) { 32862306a36Sopenharmony_ci open->op_created = true; 32962306a36Sopenharmony_ci break; /* subtle */ 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci status = nfserr_exist; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case NFS4_CREATE_EXCLUSIVE4_1: 33462306a36Sopenharmony_ci if (d_inode(child)->i_mtime.tv_sec == v_mtime && 33562306a36Sopenharmony_ci d_inode(child)->i_atime.tv_sec == v_atime && 33662306a36Sopenharmony_ci d_inode(child)->i_size == 0) { 33762306a36Sopenharmony_ci open->op_created = true; 33862306a36Sopenharmony_ci goto set_attr; /* subtle */ 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci status = nfserr_exist; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci goto out; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!IS_POSIXACL(inode)) 34662306a36Sopenharmony_ci iap->ia_mode &= ~current_umask(); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci status = fh_fill_pre_attrs(fhp); 34962306a36Sopenharmony_ci if (status != nfs_ok) 35062306a36Sopenharmony_ci goto out; 35162306a36Sopenharmony_ci status = nfsd4_vfs_create(fhp, child, open); 35262306a36Sopenharmony_ci if (status != nfs_ok) 35362306a36Sopenharmony_ci goto out; 35462306a36Sopenharmony_ci open->op_created = true; 35562306a36Sopenharmony_ci fh_fill_post_attrs(fhp); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* A newly created file already has a file size of zero. */ 35862306a36Sopenharmony_ci if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0)) 35962306a36Sopenharmony_ci iap->ia_valid &= ~ATTR_SIZE; 36062306a36Sopenharmony_ci if (nfsd4_create_is_exclusive(open->op_createmode)) { 36162306a36Sopenharmony_ci iap->ia_valid = ATTR_MTIME | ATTR_ATIME | 36262306a36Sopenharmony_ci ATTR_MTIME_SET|ATTR_ATIME_SET; 36362306a36Sopenharmony_ci iap->ia_mtime.tv_sec = v_mtime; 36462306a36Sopenharmony_ci iap->ia_atime.tv_sec = v_atime; 36562306a36Sopenharmony_ci iap->ia_mtime.tv_nsec = 0; 36662306a36Sopenharmony_ci iap->ia_atime.tv_nsec = 0; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciset_attr: 37062306a36Sopenharmony_ci status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (attrs.na_labelerr) 37362306a36Sopenharmony_ci open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; 37462306a36Sopenharmony_ci if (attrs.na_aclerr) 37562306a36Sopenharmony_ci open->op_bmval[0] &= ~FATTR4_WORD0_ACL; 37662306a36Sopenharmony_ciout: 37762306a36Sopenharmony_ci inode_unlock(inode); 37862306a36Sopenharmony_ci nfsd_attrs_free(&attrs); 37962306a36Sopenharmony_ci if (child && !IS_ERR(child)) 38062306a36Sopenharmony_ci dput(child); 38162306a36Sopenharmony_ci fh_drop_write(fhp); 38262306a36Sopenharmony_ci return status; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/** 38662306a36Sopenharmony_ci * set_change_info - set up the change_info4 for a reply 38762306a36Sopenharmony_ci * @cinfo: pointer to nfsd4_change_info to be populated 38862306a36Sopenharmony_ci * @fhp: pointer to svc_fh to use as source 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Many operations in NFSv4 require change_info4 in the reply. This function 39162306a36Sopenharmony_ci * populates that from the info that we (should!) have already collected. In 39262306a36Sopenharmony_ci * the event that we didn't get any pre-attrs, just zero out both. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_cistatic void 39562306a36Sopenharmony_ciset_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci cinfo->atomic = (u32)(fhp->fh_pre_saved && fhp->fh_post_saved && !fhp->fh_no_atomic_attr); 39862306a36Sopenharmony_ci cinfo->before_change = fhp->fh_pre_change; 39962306a36Sopenharmony_ci cinfo->after_change = fhp->fh_post_change; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* 40262306a36Sopenharmony_ci * If fetching the pre-change attributes failed, then we should 40362306a36Sopenharmony_ci * have already failed the whole operation. We could have still 40462306a36Sopenharmony_ci * failed to fetch post-change attributes however. 40562306a36Sopenharmony_ci * 40662306a36Sopenharmony_ci * If we didn't get post-op attrs, just zero-out the after 40762306a36Sopenharmony_ci * field since we don't know what it should be. If the pre_saved 40862306a36Sopenharmony_ci * field isn't set for some reason, throw warning and just copy 40962306a36Sopenharmony_ci * whatever is in the after field. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci if (WARN_ON_ONCE(!fhp->fh_pre_saved)) 41262306a36Sopenharmony_ci cinfo->before_change = 0; 41362306a36Sopenharmony_ci if (!fhp->fh_post_saved) 41462306a36Sopenharmony_ci cinfo->after_change = cinfo->before_change + 1; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic __be32 41862306a36Sopenharmony_cido_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_open *open, struct svc_fh **resfh) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct svc_fh *current_fh = &cstate->current_fh; 42162306a36Sopenharmony_ci int accmode; 42262306a36Sopenharmony_ci __be32 status; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci *resfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL); 42562306a36Sopenharmony_ci if (!*resfh) 42662306a36Sopenharmony_ci return nfserr_jukebox; 42762306a36Sopenharmony_ci fh_init(*resfh, NFS4_FHSIZE); 42862306a36Sopenharmony_ci open->op_truncate = false; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (open->op_create) { 43162306a36Sopenharmony_ci /* FIXME: check session persistence and pnfs flags. 43262306a36Sopenharmony_ci * The nfsv4.1 spec requires the following semantics: 43362306a36Sopenharmony_ci * 43462306a36Sopenharmony_ci * Persistent | pNFS | Server REQUIRED | Client Allowed 43562306a36Sopenharmony_ci * Reply Cache | server | | 43662306a36Sopenharmony_ci * -------------+--------+-----------------+-------------------- 43762306a36Sopenharmony_ci * no | no | EXCLUSIVE4_1 | EXCLUSIVE4_1 43862306a36Sopenharmony_ci * | | | (SHOULD) 43962306a36Sopenharmony_ci * | | and EXCLUSIVE4 | or EXCLUSIVE4 44062306a36Sopenharmony_ci * | | | (SHOULD NOT) 44162306a36Sopenharmony_ci * no | yes | EXCLUSIVE4_1 | EXCLUSIVE4_1 44262306a36Sopenharmony_ci * yes | no | GUARDED4 | GUARDED4 44362306a36Sopenharmony_ci * yes | yes | GUARDED4 | GUARDED4 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci current->fs->umask = open->op_umask; 44762306a36Sopenharmony_ci status = nfsd4_create_file(rqstp, current_fh, *resfh, open); 44862306a36Sopenharmony_ci current->fs->umask = 0; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * Following rfc 3530 14.2.16, and rfc 5661 18.16.4 45262306a36Sopenharmony_ci * use the returned bitmask to indicate which attributes 45362306a36Sopenharmony_ci * we used to store the verifier: 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci if (nfsd4_create_is_exclusive(open->op_createmode) && status == 0) 45662306a36Sopenharmony_ci open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS | 45762306a36Sopenharmony_ci FATTR4_WORD1_TIME_MODIFY); 45862306a36Sopenharmony_ci } else { 45962306a36Sopenharmony_ci status = nfsd_lookup(rqstp, current_fh, 46062306a36Sopenharmony_ci open->op_fname, open->op_fnamelen, *resfh); 46162306a36Sopenharmony_ci if (status == nfs_ok) 46262306a36Sopenharmony_ci /* NFSv4 protocol requires change attributes even though 46362306a36Sopenharmony_ci * no change happened. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci status = fh_fill_both_attrs(current_fh); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci if (status) 46862306a36Sopenharmony_ci goto out; 46962306a36Sopenharmony_ci status = nfsd_check_obj_isreg(*resfh); 47062306a36Sopenharmony_ci if (status) 47162306a36Sopenharmony_ci goto out; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci nfsd4_set_open_owner_reply_cache(cstate, open, *resfh); 47462306a36Sopenharmony_ci accmode = NFSD_MAY_NOP; 47562306a36Sopenharmony_ci if (open->op_created || 47662306a36Sopenharmony_ci open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) 47762306a36Sopenharmony_ci accmode |= NFSD_MAY_OWNER_OVERRIDE; 47862306a36Sopenharmony_ci status = do_open_permission(rqstp, *resfh, open, accmode); 47962306a36Sopenharmony_ci set_change_info(&open->op_cinfo, current_fh); 48062306a36Sopenharmony_ciout: 48162306a36Sopenharmony_ci return status; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic __be32 48562306a36Sopenharmony_cido_open_fhandle(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_open *open) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct svc_fh *current_fh = &cstate->current_fh; 48862306a36Sopenharmony_ci int accmode = 0; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* We don't know the target directory, and therefore can not 49162306a36Sopenharmony_ci * set the change info 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info)); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci nfsd4_set_open_owner_reply_cache(cstate, open, current_fh); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) && 49962306a36Sopenharmony_ci (open->op_iattr.ia_size == 0); 50062306a36Sopenharmony_ci /* 50162306a36Sopenharmony_ci * In the delegation case, the client is telling us about an 50262306a36Sopenharmony_ci * open that it *already* performed locally, some time ago. We 50362306a36Sopenharmony_ci * should let it succeed now if possible. 50462306a36Sopenharmony_ci * 50562306a36Sopenharmony_ci * In the case of a CLAIM_FH open, on the other hand, the client 50662306a36Sopenharmony_ci * may be counting on us to enforce permissions (the Linux 4.1 50762306a36Sopenharmony_ci * client uses this for normal opens, for example). 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEG_CUR_FH) 51062306a36Sopenharmony_ci accmode = NFSD_MAY_OWNER_OVERRIDE; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return do_open_permission(rqstp, current_fh, open, accmode); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void 51662306a36Sopenharmony_cicopy_clientid(clientid_t *clid, struct nfsd4_session *session) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct nfsd4_sessionid *sid = 51962306a36Sopenharmony_ci (struct nfsd4_sessionid *)session->se_sessionid.data; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci clid->cl_boot = sid->clientid.cl_boot; 52262306a36Sopenharmony_ci clid->cl_id = sid->clientid.cl_id; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic __be32 52662306a36Sopenharmony_cinfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 52762306a36Sopenharmony_ci union nfsd4_op_u *u) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct nfsd4_open *open = &u->open; 53062306a36Sopenharmony_ci __be32 status; 53162306a36Sopenharmony_ci struct svc_fh *resfh = NULL; 53262306a36Sopenharmony_ci struct net *net = SVC_NET(rqstp); 53362306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(net, nfsd_net_id); 53462306a36Sopenharmony_ci bool reclaim = false; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci dprintk("NFSD: nfsd4_open filename %.*s op_openowner %p\n", 53762306a36Sopenharmony_ci (int)open->op_fnamelen, open->op_fname, 53862306a36Sopenharmony_ci open->op_openowner); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci open->op_filp = NULL; 54162306a36Sopenharmony_ci open->op_rqstp = rqstp; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* This check required by spec. */ 54462306a36Sopenharmony_ci if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) 54562306a36Sopenharmony_ci return nfserr_inval; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci open->op_created = false; 54862306a36Sopenharmony_ci /* 54962306a36Sopenharmony_ci * RFC5661 18.51.3 55062306a36Sopenharmony_ci * Before RECLAIM_COMPLETE done, server should deny new lock 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci if (nfsd4_has_session(cstate) && 55362306a36Sopenharmony_ci !test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, &cstate->clp->cl_flags) && 55462306a36Sopenharmony_ci open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) 55562306a36Sopenharmony_ci return nfserr_grace; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (nfsd4_has_session(cstate)) 55862306a36Sopenharmony_ci copy_clientid(&open->op_clientid, cstate->session); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* check seqid for replay. set nfs4_owner */ 56162306a36Sopenharmony_ci status = nfsd4_process_open1(cstate, open, nn); 56262306a36Sopenharmony_ci if (status == nfserr_replay_me) { 56362306a36Sopenharmony_ci struct nfs4_replay *rp = &open->op_openowner->oo_owner.so_replay; 56462306a36Sopenharmony_ci fh_put(&cstate->current_fh); 56562306a36Sopenharmony_ci fh_copy_shallow(&cstate->current_fh.fh_handle, 56662306a36Sopenharmony_ci &rp->rp_openfh); 56762306a36Sopenharmony_ci status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); 56862306a36Sopenharmony_ci if (status) 56962306a36Sopenharmony_ci dprintk("nfsd4_open: replay failed" 57062306a36Sopenharmony_ci " restoring previous filehandle\n"); 57162306a36Sopenharmony_ci else 57262306a36Sopenharmony_ci status = nfserr_replay_me; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci if (status) 57562306a36Sopenharmony_ci goto out; 57662306a36Sopenharmony_ci if (open->op_xdr_error) { 57762306a36Sopenharmony_ci status = open->op_xdr_error; 57862306a36Sopenharmony_ci goto out; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci status = nfsd4_check_open_attributes(rqstp, cstate, open); 58262306a36Sopenharmony_ci if (status) 58362306a36Sopenharmony_ci goto out; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Openowner is now set, so sequence id will get bumped. Now we need 58662306a36Sopenharmony_ci * these checks before we do any creates: */ 58762306a36Sopenharmony_ci status = nfserr_grace; 58862306a36Sopenharmony_ci if (opens_in_grace(net) && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) 58962306a36Sopenharmony_ci goto out; 59062306a36Sopenharmony_ci status = nfserr_no_grace; 59162306a36Sopenharmony_ci if (!opens_in_grace(net) && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) 59262306a36Sopenharmony_ci goto out; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci switch (open->op_claim_type) { 59562306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_DELEGATE_CUR: 59662306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_NULL: 59762306a36Sopenharmony_ci status = do_open_lookup(rqstp, cstate, open, &resfh); 59862306a36Sopenharmony_ci if (status) 59962306a36Sopenharmony_ci goto out; 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_PREVIOUS: 60262306a36Sopenharmony_ci status = nfs4_check_open_reclaim(cstate->clp); 60362306a36Sopenharmony_ci if (status) 60462306a36Sopenharmony_ci goto out; 60562306a36Sopenharmony_ci open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; 60662306a36Sopenharmony_ci reclaim = true; 60762306a36Sopenharmony_ci fallthrough; 60862306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_FH: 60962306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_DELEG_CUR_FH: 61062306a36Sopenharmony_ci status = do_open_fhandle(rqstp, cstate, open); 61162306a36Sopenharmony_ci if (status) 61262306a36Sopenharmony_ci goto out; 61362306a36Sopenharmony_ci resfh = &cstate->current_fh; 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_DELEG_PREV_FH: 61662306a36Sopenharmony_ci case NFS4_OPEN_CLAIM_DELEGATE_PREV: 61762306a36Sopenharmony_ci status = nfserr_notsupp; 61862306a36Sopenharmony_ci goto out; 61962306a36Sopenharmony_ci default: 62062306a36Sopenharmony_ci status = nfserr_inval; 62162306a36Sopenharmony_ci goto out; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci status = nfsd4_process_open2(rqstp, resfh, open); 62562306a36Sopenharmony_ci if (status && open->op_created) 62662306a36Sopenharmony_ci pr_warn("nfsd4_process_open2 failed to open newly-created file: status=%u\n", 62762306a36Sopenharmony_ci be32_to_cpu(status)); 62862306a36Sopenharmony_ci if (reclaim && !status) 62962306a36Sopenharmony_ci nn->somebody_reclaimed = true; 63062306a36Sopenharmony_ciout: 63162306a36Sopenharmony_ci if (open->op_filp) { 63262306a36Sopenharmony_ci fput(open->op_filp); 63362306a36Sopenharmony_ci open->op_filp = NULL; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci if (resfh && resfh != &cstate->current_fh) { 63662306a36Sopenharmony_ci fh_dup2(&cstate->current_fh, resfh); 63762306a36Sopenharmony_ci fh_put(resfh); 63862306a36Sopenharmony_ci kfree(resfh); 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci nfsd4_cleanup_open_state(cstate, open); 64162306a36Sopenharmony_ci nfsd4_bump_seqid(cstate, status); 64262306a36Sopenharmony_ci return status; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/* 64662306a36Sopenharmony_ci * OPEN is the only seqid-mutating operation whose decoding can fail 64762306a36Sopenharmony_ci * with a seqid-mutating error (specifically, decoding of user names in 64862306a36Sopenharmony_ci * the attributes). Therefore we have to do some processing to look up 64962306a36Sopenharmony_ci * the stateowner so that we can bump the seqid. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_cistatic __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_op *op) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct nfsd4_open *open = &op->u.open; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (!seqid_mutating_err(ntohl(op->status))) 65662306a36Sopenharmony_ci return op->status; 65762306a36Sopenharmony_ci if (nfsd4_has_session(cstate)) 65862306a36Sopenharmony_ci return op->status; 65962306a36Sopenharmony_ci open->op_xdr_error = op->status; 66062306a36Sopenharmony_ci return nfsd4_open(rqstp, cstate, &op->u); 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/* 66462306a36Sopenharmony_ci * filehandle-manipulating ops. 66562306a36Sopenharmony_ci */ 66662306a36Sopenharmony_cistatic __be32 66762306a36Sopenharmony_cinfsd4_getfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 66862306a36Sopenharmony_ci union nfsd4_op_u *u) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci u->getfh = &cstate->current_fh; 67162306a36Sopenharmony_ci return nfs_ok; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic __be32 67562306a36Sopenharmony_cinfsd4_putfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 67662306a36Sopenharmony_ci union nfsd4_op_u *u) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct nfsd4_putfh *putfh = &u->putfh; 67962306a36Sopenharmony_ci __be32 ret; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci fh_put(&cstate->current_fh); 68262306a36Sopenharmony_ci cstate->current_fh.fh_handle.fh_size = putfh->pf_fhlen; 68362306a36Sopenharmony_ci memcpy(&cstate->current_fh.fh_handle.fh_raw, putfh->pf_fhval, 68462306a36Sopenharmony_ci putfh->pf_fhlen); 68562306a36Sopenharmony_ci ret = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_BYPASS_GSS); 68662306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_2_INTER_SSC 68762306a36Sopenharmony_ci if (ret == nfserr_stale && putfh->no_verify) { 68862306a36Sopenharmony_ci SET_FH_FLAG(&cstate->current_fh, NFSD4_FH_FOREIGN); 68962306a36Sopenharmony_ci ret = 0; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci#endif 69262306a36Sopenharmony_ci return ret; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic __be32 69662306a36Sopenharmony_cinfsd4_putrootfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 69762306a36Sopenharmony_ci union nfsd4_op_u *u) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci fh_put(&cstate->current_fh); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return exp_pseudoroot(rqstp, &cstate->current_fh); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic __be32 70562306a36Sopenharmony_cinfsd4_restorefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 70662306a36Sopenharmony_ci union nfsd4_op_u *u) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci if (!cstate->save_fh.fh_dentry) 70962306a36Sopenharmony_ci return nfserr_restorefh; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci fh_dup2(&cstate->current_fh, &cstate->save_fh); 71262306a36Sopenharmony_ci if (HAS_CSTATE_FLAG(cstate, SAVED_STATE_ID_FLAG)) { 71362306a36Sopenharmony_ci memcpy(&cstate->current_stateid, &cstate->save_stateid, sizeof(stateid_t)); 71462306a36Sopenharmony_ci SET_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci return nfs_ok; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic __be32 72062306a36Sopenharmony_cinfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 72162306a36Sopenharmony_ci union nfsd4_op_u *u) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci fh_dup2(&cstate->save_fh, &cstate->current_fh); 72462306a36Sopenharmony_ci if (HAS_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG)) { 72562306a36Sopenharmony_ci memcpy(&cstate->save_stateid, &cstate->current_stateid, sizeof(stateid_t)); 72662306a36Sopenharmony_ci SET_CSTATE_FLAG(cstate, SAVED_STATE_ID_FLAG); 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci return nfs_ok; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/* 73262306a36Sopenharmony_ci * misc nfsv4 ops 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_cistatic __be32 73562306a36Sopenharmony_cinfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 73662306a36Sopenharmony_ci union nfsd4_op_u *u) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct nfsd4_access *access = &u->access; 73962306a36Sopenharmony_ci u32 access_full; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci access_full = NFS3_ACCESS_FULL; 74262306a36Sopenharmony_ci if (cstate->minorversion >= 2) 74362306a36Sopenharmony_ci access_full |= NFS4_ACCESS_XALIST | NFS4_ACCESS_XAREAD | 74462306a36Sopenharmony_ci NFS4_ACCESS_XAWRITE; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (access->ac_req_access & ~access_full) 74762306a36Sopenharmony_ci return nfserr_inval; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci access->ac_resp_access = access->ac_req_access; 75062306a36Sopenharmony_ci return nfsd_access(rqstp, &cstate->current_fh, &access->ac_resp_access, 75162306a36Sopenharmony_ci &access->ac_supported); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic void gen_boot_verifier(nfs4_verifier *verifier, struct net *net) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci __be32 *verf = (__be32 *)verifier->data; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci BUILD_BUG_ON(2*sizeof(*verf) != sizeof(verifier->data)); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci nfsd_copy_write_verifier(verf, net_generic(net, nfsd_net_id)); 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic __be32 76462306a36Sopenharmony_cinfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 76562306a36Sopenharmony_ci union nfsd4_op_u *u) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct nfsd4_commit *commit = &u->commit; 76862306a36Sopenharmony_ci struct nfsd_file *nf; 76962306a36Sopenharmony_ci __be32 status; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci status = nfsd_file_acquire(rqstp, &cstate->current_fh, NFSD_MAY_WRITE | 77262306a36Sopenharmony_ci NFSD_MAY_NOT_BREAK_LEASE, &nf); 77362306a36Sopenharmony_ci if (status != nfs_ok) 77462306a36Sopenharmony_ci return status; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci status = nfsd_commit(rqstp, &cstate->current_fh, nf, commit->co_offset, 77762306a36Sopenharmony_ci commit->co_count, 77862306a36Sopenharmony_ci (__be32 *)commit->co_verf.data); 77962306a36Sopenharmony_ci nfsd_file_put(nf); 78062306a36Sopenharmony_ci return status; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic __be32 78462306a36Sopenharmony_cinfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 78562306a36Sopenharmony_ci union nfsd4_op_u *u) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct nfsd4_create *create = &u->create; 78862306a36Sopenharmony_ci struct nfsd_attrs attrs = { 78962306a36Sopenharmony_ci .na_iattr = &create->cr_iattr, 79062306a36Sopenharmony_ci .na_seclabel = &create->cr_label, 79162306a36Sopenharmony_ci }; 79262306a36Sopenharmony_ci struct svc_fh resfh; 79362306a36Sopenharmony_ci __be32 status; 79462306a36Sopenharmony_ci dev_t rdev; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci fh_init(&resfh, NFS4_FHSIZE); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, NFSD_MAY_NOP); 79962306a36Sopenharmony_ci if (status) 80062306a36Sopenharmony_ci return status; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci status = check_attr_support(rqstp, cstate, create->cr_bmval, 80362306a36Sopenharmony_ci nfsd_attrmask); 80462306a36Sopenharmony_ci if (status) 80562306a36Sopenharmony_ci return status; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs); 80862306a36Sopenharmony_ci current->fs->umask = create->cr_umask; 80962306a36Sopenharmony_ci switch (create->cr_type) { 81062306a36Sopenharmony_ci case NF4LNK: 81162306a36Sopenharmony_ci status = nfsd_symlink(rqstp, &cstate->current_fh, 81262306a36Sopenharmony_ci create->cr_name, create->cr_namelen, 81362306a36Sopenharmony_ci create->cr_data, &attrs, &resfh); 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci case NF4BLK: 81762306a36Sopenharmony_ci status = nfserr_inval; 81862306a36Sopenharmony_ci rdev = MKDEV(create->cr_specdata1, create->cr_specdata2); 81962306a36Sopenharmony_ci if (MAJOR(rdev) != create->cr_specdata1 || 82062306a36Sopenharmony_ci MINOR(rdev) != create->cr_specdata2) 82162306a36Sopenharmony_ci goto out_umask; 82262306a36Sopenharmony_ci status = nfsd_create(rqstp, &cstate->current_fh, 82362306a36Sopenharmony_ci create->cr_name, create->cr_namelen, 82462306a36Sopenharmony_ci &attrs, S_IFBLK, rdev, &resfh); 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci case NF4CHR: 82862306a36Sopenharmony_ci status = nfserr_inval; 82962306a36Sopenharmony_ci rdev = MKDEV(create->cr_specdata1, create->cr_specdata2); 83062306a36Sopenharmony_ci if (MAJOR(rdev) != create->cr_specdata1 || 83162306a36Sopenharmony_ci MINOR(rdev) != create->cr_specdata2) 83262306a36Sopenharmony_ci goto out_umask; 83362306a36Sopenharmony_ci status = nfsd_create(rqstp, &cstate->current_fh, 83462306a36Sopenharmony_ci create->cr_name, create->cr_namelen, 83562306a36Sopenharmony_ci &attrs, S_IFCHR, rdev, &resfh); 83662306a36Sopenharmony_ci break; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci case NF4SOCK: 83962306a36Sopenharmony_ci status = nfsd_create(rqstp, &cstate->current_fh, 84062306a36Sopenharmony_ci create->cr_name, create->cr_namelen, 84162306a36Sopenharmony_ci &attrs, S_IFSOCK, 0, &resfh); 84262306a36Sopenharmony_ci break; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci case NF4FIFO: 84562306a36Sopenharmony_ci status = nfsd_create(rqstp, &cstate->current_fh, 84662306a36Sopenharmony_ci create->cr_name, create->cr_namelen, 84762306a36Sopenharmony_ci &attrs, S_IFIFO, 0, &resfh); 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci case NF4DIR: 85162306a36Sopenharmony_ci create->cr_iattr.ia_valid &= ~ATTR_SIZE; 85262306a36Sopenharmony_ci status = nfsd_create(rqstp, &cstate->current_fh, 85362306a36Sopenharmony_ci create->cr_name, create->cr_namelen, 85462306a36Sopenharmony_ci &attrs, S_IFDIR, 0, &resfh); 85562306a36Sopenharmony_ci break; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci default: 85862306a36Sopenharmony_ci status = nfserr_badtype; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (status) 86262306a36Sopenharmony_ci goto out; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (attrs.na_labelerr) 86562306a36Sopenharmony_ci create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; 86662306a36Sopenharmony_ci if (attrs.na_aclerr) 86762306a36Sopenharmony_ci create->cr_bmval[0] &= ~FATTR4_WORD0_ACL; 86862306a36Sopenharmony_ci set_change_info(&create->cr_cinfo, &cstate->current_fh); 86962306a36Sopenharmony_ci fh_dup2(&cstate->current_fh, &resfh); 87062306a36Sopenharmony_ciout: 87162306a36Sopenharmony_ci fh_put(&resfh); 87262306a36Sopenharmony_ciout_umask: 87362306a36Sopenharmony_ci current->fs->umask = 0; 87462306a36Sopenharmony_ci nfsd_attrs_free(&attrs); 87562306a36Sopenharmony_ci return status; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic __be32 87962306a36Sopenharmony_cinfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 88062306a36Sopenharmony_ci union nfsd4_op_u *u) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct nfsd4_getattr *getattr = &u->getattr; 88362306a36Sopenharmony_ci __be32 status; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); 88662306a36Sopenharmony_ci if (status) 88762306a36Sopenharmony_ci return status; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (getattr->ga_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) 89062306a36Sopenharmony_ci return nfserr_inval; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci getattr->ga_bmval[0] &= nfsd_suppattrs[cstate->minorversion][0]; 89362306a36Sopenharmony_ci getattr->ga_bmval[1] &= nfsd_suppattrs[cstate->minorversion][1]; 89462306a36Sopenharmony_ci getattr->ga_bmval[2] &= nfsd_suppattrs[cstate->minorversion][2]; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci getattr->ga_fhp = &cstate->current_fh; 89762306a36Sopenharmony_ci return nfs_ok; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic __be32 90162306a36Sopenharmony_cinfsd4_link(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 90262306a36Sopenharmony_ci union nfsd4_op_u *u) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci struct nfsd4_link *link = &u->link; 90562306a36Sopenharmony_ci __be32 status; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci status = nfsd_link(rqstp, &cstate->current_fh, 90862306a36Sopenharmony_ci link->li_name, link->li_namelen, &cstate->save_fh); 90962306a36Sopenharmony_ci if (!status) 91062306a36Sopenharmony_ci set_change_info(&link->li_cinfo, &cstate->current_fh); 91162306a36Sopenharmony_ci return status; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic __be32 nfsd4_do_lookupp(struct svc_rqst *rqstp, struct svc_fh *fh) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct svc_fh tmp_fh; 91762306a36Sopenharmony_ci __be32 ret; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci fh_init(&tmp_fh, NFS4_FHSIZE); 92062306a36Sopenharmony_ci ret = exp_pseudoroot(rqstp, &tmp_fh); 92162306a36Sopenharmony_ci if (ret) 92262306a36Sopenharmony_ci return ret; 92362306a36Sopenharmony_ci if (tmp_fh.fh_dentry == fh->fh_dentry) { 92462306a36Sopenharmony_ci fh_put(&tmp_fh); 92562306a36Sopenharmony_ci return nfserr_noent; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci fh_put(&tmp_fh); 92862306a36Sopenharmony_ci return nfsd_lookup(rqstp, fh, "..", 2, fh); 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic __be32 93262306a36Sopenharmony_cinfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 93362306a36Sopenharmony_ci union nfsd4_op_u *u) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci return nfsd4_do_lookupp(rqstp, &cstate->current_fh); 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic __be32 93962306a36Sopenharmony_cinfsd4_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 94062306a36Sopenharmony_ci union nfsd4_op_u *u) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci return nfsd_lookup(rqstp, &cstate->current_fh, 94362306a36Sopenharmony_ci u->lookup.lo_name, u->lookup.lo_len, 94462306a36Sopenharmony_ci &cstate->current_fh); 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cistatic __be32 94862306a36Sopenharmony_cinfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 94962306a36Sopenharmony_ci union nfsd4_op_u *u) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct nfsd4_read *read = &u->read; 95262306a36Sopenharmony_ci __be32 status; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci read->rd_nf = NULL; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci trace_nfsd_read_start(rqstp, &cstate->current_fh, 95762306a36Sopenharmony_ci read->rd_offset, read->rd_length); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci read->rd_length = min_t(u32, read->rd_length, svc_max_payload(rqstp)); 96062306a36Sopenharmony_ci if (read->rd_offset > (u64)OFFSET_MAX) 96162306a36Sopenharmony_ci read->rd_offset = (u64)OFFSET_MAX; 96262306a36Sopenharmony_ci if (read->rd_offset + read->rd_length > (u64)OFFSET_MAX) 96362306a36Sopenharmony_ci read->rd_length = (u64)OFFSET_MAX - read->rd_offset; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci /* 96662306a36Sopenharmony_ci * If we do a zero copy read, then a client will see read data 96762306a36Sopenharmony_ci * that reflects the state of the file *after* performing the 96862306a36Sopenharmony_ci * following compound. 96962306a36Sopenharmony_ci * 97062306a36Sopenharmony_ci * To ensure proper ordering, we therefore turn off zero copy if 97162306a36Sopenharmony_ci * the client wants us to do more in this compound: 97262306a36Sopenharmony_ci */ 97362306a36Sopenharmony_ci if (!nfsd4_last_compound_op(rqstp)) 97462306a36Sopenharmony_ci clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* check stateid */ 97762306a36Sopenharmony_ci status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, 97862306a36Sopenharmony_ci &read->rd_stateid, RD_STATE, 97962306a36Sopenharmony_ci &read->rd_nf, NULL); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci read->rd_rqstp = rqstp; 98262306a36Sopenharmony_ci read->rd_fhp = &cstate->current_fh; 98362306a36Sopenharmony_ci return status; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic void 98862306a36Sopenharmony_cinfsd4_read_release(union nfsd4_op_u *u) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci if (u->read.rd_nf) 99162306a36Sopenharmony_ci nfsd_file_put(u->read.rd_nf); 99262306a36Sopenharmony_ci trace_nfsd_read_done(u->read.rd_rqstp, u->read.rd_fhp, 99362306a36Sopenharmony_ci u->read.rd_offset, u->read.rd_length); 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic __be32 99762306a36Sopenharmony_cinfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 99862306a36Sopenharmony_ci union nfsd4_op_u *u) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct nfsd4_readdir *readdir = &u->readdir; 100162306a36Sopenharmony_ci u64 cookie = readdir->rd_cookie; 100262306a36Sopenharmony_ci static const nfs4_verifier zeroverf; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci /* no need to check permission - this will be done in nfsd_readdir() */ 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) 100762306a36Sopenharmony_ci return nfserr_inval; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci readdir->rd_bmval[0] &= nfsd_suppattrs[cstate->minorversion][0]; 101062306a36Sopenharmony_ci readdir->rd_bmval[1] &= nfsd_suppattrs[cstate->minorversion][1]; 101162306a36Sopenharmony_ci readdir->rd_bmval[2] &= nfsd_suppattrs[cstate->minorversion][2]; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if ((cookie == 1) || (cookie == 2) || 101462306a36Sopenharmony_ci (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE))) 101562306a36Sopenharmony_ci return nfserr_bad_cookie; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci readdir->rd_rqstp = rqstp; 101862306a36Sopenharmony_ci readdir->rd_fhp = &cstate->current_fh; 101962306a36Sopenharmony_ci return nfs_ok; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic __be32 102362306a36Sopenharmony_cinfsd4_readlink(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 102462306a36Sopenharmony_ci union nfsd4_op_u *u) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci u->readlink.rl_rqstp = rqstp; 102762306a36Sopenharmony_ci u->readlink.rl_fhp = &cstate->current_fh; 102862306a36Sopenharmony_ci return nfs_ok; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cistatic __be32 103262306a36Sopenharmony_cinfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 103362306a36Sopenharmony_ci union nfsd4_op_u *u) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct nfsd4_remove *remove = &u->remove; 103662306a36Sopenharmony_ci __be32 status; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (opens_in_grace(SVC_NET(rqstp))) 103962306a36Sopenharmony_ci return nfserr_grace; 104062306a36Sopenharmony_ci status = nfsd_unlink(rqstp, &cstate->current_fh, 0, 104162306a36Sopenharmony_ci remove->rm_name, remove->rm_namelen); 104262306a36Sopenharmony_ci if (!status) 104362306a36Sopenharmony_ci set_change_info(&remove->rm_cinfo, &cstate->current_fh); 104462306a36Sopenharmony_ci return status; 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic __be32 104862306a36Sopenharmony_cinfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 104962306a36Sopenharmony_ci union nfsd4_op_u *u) 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci struct nfsd4_rename *rename = &u->rename; 105262306a36Sopenharmony_ci __be32 status; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (opens_in_grace(SVC_NET(rqstp))) 105562306a36Sopenharmony_ci return nfserr_grace; 105662306a36Sopenharmony_ci status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname, 105762306a36Sopenharmony_ci rename->rn_snamelen, &cstate->current_fh, 105862306a36Sopenharmony_ci rename->rn_tname, rename->rn_tnamelen); 105962306a36Sopenharmony_ci if (status) 106062306a36Sopenharmony_ci return status; 106162306a36Sopenharmony_ci set_change_info(&rename->rn_sinfo, &cstate->save_fh); 106262306a36Sopenharmony_ci set_change_info(&rename->rn_tinfo, &cstate->current_fh); 106362306a36Sopenharmony_ci return nfs_ok; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic __be32 106762306a36Sopenharmony_cinfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 106862306a36Sopenharmony_ci union nfsd4_op_u *u) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct nfsd4_secinfo *secinfo = &u->secinfo; 107162306a36Sopenharmony_ci struct svc_export *exp; 107262306a36Sopenharmony_ci struct dentry *dentry; 107362306a36Sopenharmony_ci __be32 err; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci err = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, NFSD_MAY_EXEC); 107662306a36Sopenharmony_ci if (err) 107762306a36Sopenharmony_ci return err; 107862306a36Sopenharmony_ci err = nfsd_lookup_dentry(rqstp, &cstate->current_fh, 107962306a36Sopenharmony_ci secinfo->si_name, secinfo->si_namelen, 108062306a36Sopenharmony_ci &exp, &dentry); 108162306a36Sopenharmony_ci if (err) 108262306a36Sopenharmony_ci return err; 108362306a36Sopenharmony_ci if (d_really_is_negative(dentry)) { 108462306a36Sopenharmony_ci exp_put(exp); 108562306a36Sopenharmony_ci err = nfserr_noent; 108662306a36Sopenharmony_ci } else 108762306a36Sopenharmony_ci secinfo->si_exp = exp; 108862306a36Sopenharmony_ci dput(dentry); 108962306a36Sopenharmony_ci if (cstate->minorversion) 109062306a36Sopenharmony_ci /* See rfc 5661 section 2.6.3.1.1.8 */ 109162306a36Sopenharmony_ci fh_put(&cstate->current_fh); 109262306a36Sopenharmony_ci return err; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic __be32 109662306a36Sopenharmony_cinfsd4_secinfo_no_name(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 109762306a36Sopenharmony_ci union nfsd4_op_u *u) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci __be32 err; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci switch (u->secinfo_no_name.sin_style) { 110262306a36Sopenharmony_ci case NFS4_SECINFO_STYLE4_CURRENT_FH: 110362306a36Sopenharmony_ci break; 110462306a36Sopenharmony_ci case NFS4_SECINFO_STYLE4_PARENT: 110562306a36Sopenharmony_ci err = nfsd4_do_lookupp(rqstp, &cstate->current_fh); 110662306a36Sopenharmony_ci if (err) 110762306a36Sopenharmony_ci return err; 110862306a36Sopenharmony_ci break; 110962306a36Sopenharmony_ci default: 111062306a36Sopenharmony_ci return nfserr_inval; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci u->secinfo_no_name.sin_exp = exp_get(cstate->current_fh.fh_export); 111462306a36Sopenharmony_ci fh_put(&cstate->current_fh); 111562306a36Sopenharmony_ci return nfs_ok; 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic void 111962306a36Sopenharmony_cinfsd4_secinfo_release(union nfsd4_op_u *u) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci if (u->secinfo.si_exp) 112262306a36Sopenharmony_ci exp_put(u->secinfo.si_exp); 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_cistatic void 112662306a36Sopenharmony_cinfsd4_secinfo_no_name_release(union nfsd4_op_u *u) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci if (u->secinfo_no_name.sin_exp) 112962306a36Sopenharmony_ci exp_put(u->secinfo_no_name.sin_exp); 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_cistatic __be32 113362306a36Sopenharmony_cinfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 113462306a36Sopenharmony_ci union nfsd4_op_u *u) 113562306a36Sopenharmony_ci{ 113662306a36Sopenharmony_ci struct nfsd4_setattr *setattr = &u->setattr; 113762306a36Sopenharmony_ci struct nfsd_attrs attrs = { 113862306a36Sopenharmony_ci .na_iattr = &setattr->sa_iattr, 113962306a36Sopenharmony_ci .na_seclabel = &setattr->sa_label, 114062306a36Sopenharmony_ci }; 114162306a36Sopenharmony_ci struct inode *inode; 114262306a36Sopenharmony_ci __be32 status = nfs_ok; 114362306a36Sopenharmony_ci int err; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { 114662306a36Sopenharmony_ci status = nfs4_preprocess_stateid_op(rqstp, cstate, 114762306a36Sopenharmony_ci &cstate->current_fh, &setattr->sa_stateid, 114862306a36Sopenharmony_ci WR_STATE, NULL, NULL); 114962306a36Sopenharmony_ci if (status) 115062306a36Sopenharmony_ci return status; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci err = fh_want_write(&cstate->current_fh); 115362306a36Sopenharmony_ci if (err) 115462306a36Sopenharmony_ci return nfserrno(err); 115562306a36Sopenharmony_ci status = nfs_ok; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci status = check_attr_support(rqstp, cstate, setattr->sa_bmval, 115862306a36Sopenharmony_ci nfsd_attrmask); 115962306a36Sopenharmony_ci if (status) 116062306a36Sopenharmony_ci goto out; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci inode = cstate->current_fh.fh_dentry->d_inode; 116362306a36Sopenharmony_ci status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG, 116462306a36Sopenharmony_ci setattr->sa_acl, &attrs); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (status) 116762306a36Sopenharmony_ci goto out; 116862306a36Sopenharmony_ci status = nfsd_setattr(rqstp, &cstate->current_fh, &attrs, 116962306a36Sopenharmony_ci 0, (time64_t)0); 117062306a36Sopenharmony_ci if (!status) 117162306a36Sopenharmony_ci status = nfserrno(attrs.na_labelerr); 117262306a36Sopenharmony_ci if (!status) 117362306a36Sopenharmony_ci status = nfserrno(attrs.na_aclerr); 117462306a36Sopenharmony_ciout: 117562306a36Sopenharmony_ci nfsd_attrs_free(&attrs); 117662306a36Sopenharmony_ci fh_drop_write(&cstate->current_fh); 117762306a36Sopenharmony_ci return status; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_cistatic __be32 118162306a36Sopenharmony_cinfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 118262306a36Sopenharmony_ci union nfsd4_op_u *u) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci struct nfsd4_write *write = &u->write; 118562306a36Sopenharmony_ci stateid_t *stateid = &write->wr_stateid; 118662306a36Sopenharmony_ci struct nfsd_file *nf = NULL; 118762306a36Sopenharmony_ci __be32 status = nfs_ok; 118862306a36Sopenharmony_ci unsigned long cnt; 118962306a36Sopenharmony_ci int nvecs; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci if (write->wr_offset > (u64)OFFSET_MAX || 119262306a36Sopenharmony_ci write->wr_offset + write->wr_buflen > (u64)OFFSET_MAX) 119362306a36Sopenharmony_ci return nfserr_fbig; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci cnt = write->wr_buflen; 119662306a36Sopenharmony_ci trace_nfsd_write_start(rqstp, &cstate->current_fh, 119762306a36Sopenharmony_ci write->wr_offset, cnt); 119862306a36Sopenharmony_ci status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, 119962306a36Sopenharmony_ci stateid, WR_STATE, &nf, NULL); 120062306a36Sopenharmony_ci if (status) 120162306a36Sopenharmony_ci return status; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci write->wr_how_written = write->wr_stable_how; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci nvecs = svc_fill_write_vector(rqstp, &write->wr_payload); 120662306a36Sopenharmony_ci WARN_ON_ONCE(nvecs > ARRAY_SIZE(rqstp->rq_vec)); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci status = nfsd_vfs_write(rqstp, &cstate->current_fh, nf, 120962306a36Sopenharmony_ci write->wr_offset, rqstp->rq_vec, nvecs, &cnt, 121062306a36Sopenharmony_ci write->wr_how_written, 121162306a36Sopenharmony_ci (__be32 *)write->wr_verifier.data); 121262306a36Sopenharmony_ci nfsd_file_put(nf); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci write->wr_bytes_written = cnt; 121562306a36Sopenharmony_ci trace_nfsd_write_done(rqstp, &cstate->current_fh, 121662306a36Sopenharmony_ci write->wr_offset, cnt); 121762306a36Sopenharmony_ci return status; 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_cistatic __be32 122162306a36Sopenharmony_cinfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 122262306a36Sopenharmony_ci stateid_t *src_stateid, struct nfsd_file **src, 122362306a36Sopenharmony_ci stateid_t *dst_stateid, struct nfsd_file **dst) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci __be32 status; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (!cstate->save_fh.fh_dentry) 122862306a36Sopenharmony_ci return nfserr_nofilehandle; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh, 123162306a36Sopenharmony_ci src_stateid, RD_STATE, src, NULL); 123262306a36Sopenharmony_ci if (status) 123362306a36Sopenharmony_ci goto out; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, 123662306a36Sopenharmony_ci dst_stateid, WR_STATE, dst, NULL); 123762306a36Sopenharmony_ci if (status) 123862306a36Sopenharmony_ci goto out_put_src; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* fix up for NFS-specific error code */ 124162306a36Sopenharmony_ci if (!S_ISREG(file_inode((*src)->nf_file)->i_mode) || 124262306a36Sopenharmony_ci !S_ISREG(file_inode((*dst)->nf_file)->i_mode)) { 124362306a36Sopenharmony_ci status = nfserr_wrong_type; 124462306a36Sopenharmony_ci goto out_put_dst; 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ciout: 124862306a36Sopenharmony_ci return status; 124962306a36Sopenharmony_ciout_put_dst: 125062306a36Sopenharmony_ci nfsd_file_put(*dst); 125162306a36Sopenharmony_ci *dst = NULL; 125262306a36Sopenharmony_ciout_put_src: 125362306a36Sopenharmony_ci nfsd_file_put(*src); 125462306a36Sopenharmony_ci *src = NULL; 125562306a36Sopenharmony_ci goto out; 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic __be32 125962306a36Sopenharmony_cinfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 126062306a36Sopenharmony_ci union nfsd4_op_u *u) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci struct nfsd4_clone *clone = &u->clone; 126362306a36Sopenharmony_ci struct nfsd_file *src, *dst; 126462306a36Sopenharmony_ci __be32 status; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci status = nfsd4_verify_copy(rqstp, cstate, &clone->cl_src_stateid, &src, 126762306a36Sopenharmony_ci &clone->cl_dst_stateid, &dst); 126862306a36Sopenharmony_ci if (status) 126962306a36Sopenharmony_ci goto out; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci status = nfsd4_clone_file_range(rqstp, src, clone->cl_src_pos, 127262306a36Sopenharmony_ci dst, clone->cl_dst_pos, clone->cl_count, 127362306a36Sopenharmony_ci EX_ISSYNC(cstate->current_fh.fh_export)); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci nfsd_file_put(dst); 127662306a36Sopenharmony_ci nfsd_file_put(src); 127762306a36Sopenharmony_ciout: 127862306a36Sopenharmony_ci return status; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic void nfs4_put_copy(struct nfsd4_copy *copy) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci if (!refcount_dec_and_test(©->refcount)) 128462306a36Sopenharmony_ci return; 128562306a36Sopenharmony_ci kfree(copy->cp_src); 128662306a36Sopenharmony_ci kfree(copy); 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic void nfsd4_stop_copy(struct nfsd4_copy *copy) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags)) 129262306a36Sopenharmony_ci kthread_stop(copy->copy_task); 129362306a36Sopenharmony_ci nfs4_put_copy(copy); 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic struct nfsd4_copy *nfsd4_get_copy(struct nfs4_client *clp) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct nfsd4_copy *copy = NULL; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci spin_lock(&clp->async_lock); 130162306a36Sopenharmony_ci if (!list_empty(&clp->async_copies)) { 130262306a36Sopenharmony_ci copy = list_first_entry(&clp->async_copies, struct nfsd4_copy, 130362306a36Sopenharmony_ci copies); 130462306a36Sopenharmony_ci refcount_inc(©->refcount); 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci spin_unlock(&clp->async_lock); 130762306a36Sopenharmony_ci return copy; 130862306a36Sopenharmony_ci} 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_civoid nfsd4_shutdown_copy(struct nfs4_client *clp) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci struct nfsd4_copy *copy; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci while ((copy = nfsd4_get_copy(clp)) != NULL) 131562306a36Sopenharmony_ci nfsd4_stop_copy(copy); 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_2_INTER_SSC 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ciextern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, 132062306a36Sopenharmony_ci struct nfs_fh *src_fh, 132162306a36Sopenharmony_ci nfs4_stateid *stateid); 132262306a36Sopenharmony_ciextern void nfs42_ssc_close(struct file *filep); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ciextern void nfs_sb_deactive(struct super_block *sb); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci#define NFSD42_INTERSSC_MOUNTOPS "vers=4.2,addr=%s,sec=sys" 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci/* 132962306a36Sopenharmony_ci * setup a work entry in the ssc delayed unmount list. 133062306a36Sopenharmony_ci */ 133162306a36Sopenharmony_cistatic __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr, 133262306a36Sopenharmony_ci struct nfsd4_ssc_umount_item **nsui) 133362306a36Sopenharmony_ci{ 133462306a36Sopenharmony_ci struct nfsd4_ssc_umount_item *ni = NULL; 133562306a36Sopenharmony_ci struct nfsd4_ssc_umount_item *work = NULL; 133662306a36Sopenharmony_ci struct nfsd4_ssc_umount_item *tmp; 133762306a36Sopenharmony_ci DEFINE_WAIT(wait); 133862306a36Sopenharmony_ci __be32 status = 0; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci *nsui = NULL; 134162306a36Sopenharmony_ci work = kzalloc(sizeof(*work), GFP_KERNEL); 134262306a36Sopenharmony_citry_again: 134362306a36Sopenharmony_ci spin_lock(&nn->nfsd_ssc_lock); 134462306a36Sopenharmony_ci list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) { 134562306a36Sopenharmony_ci if (strncmp(ni->nsui_ipaddr, ipaddr, sizeof(ni->nsui_ipaddr))) 134662306a36Sopenharmony_ci continue; 134762306a36Sopenharmony_ci /* found a match */ 134862306a36Sopenharmony_ci if (ni->nsui_busy) { 134962306a36Sopenharmony_ci /* wait - and try again */ 135062306a36Sopenharmony_ci prepare_to_wait(&nn->nfsd_ssc_waitq, &wait, TASK_IDLE); 135162306a36Sopenharmony_ci spin_unlock(&nn->nfsd_ssc_lock); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* allow 20secs for mount/unmount for now - revisit */ 135462306a36Sopenharmony_ci if (kthread_should_stop() || 135562306a36Sopenharmony_ci (schedule_timeout(20*HZ) == 0)) { 135662306a36Sopenharmony_ci finish_wait(&nn->nfsd_ssc_waitq, &wait); 135762306a36Sopenharmony_ci kfree(work); 135862306a36Sopenharmony_ci return nfserr_eagain; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci finish_wait(&nn->nfsd_ssc_waitq, &wait); 136162306a36Sopenharmony_ci goto try_again; 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci *nsui = ni; 136462306a36Sopenharmony_ci refcount_inc(&ni->nsui_refcnt); 136562306a36Sopenharmony_ci spin_unlock(&nn->nfsd_ssc_lock); 136662306a36Sopenharmony_ci kfree(work); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* return vfsmount in (*nsui)->nsui_vfsmount */ 136962306a36Sopenharmony_ci return 0; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci if (work) { 137262306a36Sopenharmony_ci strscpy(work->nsui_ipaddr, ipaddr, sizeof(work->nsui_ipaddr) - 1); 137362306a36Sopenharmony_ci refcount_set(&work->nsui_refcnt, 2); 137462306a36Sopenharmony_ci work->nsui_busy = true; 137562306a36Sopenharmony_ci list_add_tail(&work->nsui_list, &nn->nfsd_ssc_mount_list); 137662306a36Sopenharmony_ci *nsui = work; 137762306a36Sopenharmony_ci } else 137862306a36Sopenharmony_ci status = nfserr_resource; 137962306a36Sopenharmony_ci spin_unlock(&nn->nfsd_ssc_lock); 138062306a36Sopenharmony_ci return status; 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_cistatic void nfsd4_ssc_update_dul(struct nfsd_net *nn, 138462306a36Sopenharmony_ci struct nfsd4_ssc_umount_item *nsui, 138562306a36Sopenharmony_ci struct vfsmount *ss_mnt) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci spin_lock(&nn->nfsd_ssc_lock); 138862306a36Sopenharmony_ci nsui->nsui_vfsmount = ss_mnt; 138962306a36Sopenharmony_ci nsui->nsui_busy = false; 139062306a36Sopenharmony_ci wake_up_all(&nn->nfsd_ssc_waitq); 139162306a36Sopenharmony_ci spin_unlock(&nn->nfsd_ssc_lock); 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic void nfsd4_ssc_cancel_dul(struct nfsd_net *nn, 139562306a36Sopenharmony_ci struct nfsd4_ssc_umount_item *nsui) 139662306a36Sopenharmony_ci{ 139762306a36Sopenharmony_ci spin_lock(&nn->nfsd_ssc_lock); 139862306a36Sopenharmony_ci list_del(&nsui->nsui_list); 139962306a36Sopenharmony_ci wake_up_all(&nn->nfsd_ssc_waitq); 140062306a36Sopenharmony_ci spin_unlock(&nn->nfsd_ssc_lock); 140162306a36Sopenharmony_ci kfree(nsui); 140262306a36Sopenharmony_ci} 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci/* 140562306a36Sopenharmony_ci * Support one copy source server for now. 140662306a36Sopenharmony_ci */ 140762306a36Sopenharmony_cistatic __be32 140862306a36Sopenharmony_cinfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp, 140962306a36Sopenharmony_ci struct nfsd4_ssc_umount_item **nsui) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci struct file_system_type *type; 141262306a36Sopenharmony_ci struct vfsmount *ss_mnt; 141362306a36Sopenharmony_ci struct nfs42_netaddr *naddr; 141462306a36Sopenharmony_ci struct sockaddr_storage tmp_addr; 141562306a36Sopenharmony_ci size_t tmp_addrlen, match_netid_len = 3; 141662306a36Sopenharmony_ci char *startsep = "", *endsep = "", *match_netid = "tcp"; 141762306a36Sopenharmony_ci char *ipaddr, *dev_name, *raw_data; 141862306a36Sopenharmony_ci int len, raw_len; 141962306a36Sopenharmony_ci __be32 status = nfserr_inval; 142062306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci naddr = &nss->u.nl4_addr; 142362306a36Sopenharmony_ci tmp_addrlen = rpc_uaddr2sockaddr(SVC_NET(rqstp), naddr->addr, 142462306a36Sopenharmony_ci naddr->addr_len, 142562306a36Sopenharmony_ci (struct sockaddr *)&tmp_addr, 142662306a36Sopenharmony_ci sizeof(tmp_addr)); 142762306a36Sopenharmony_ci *nsui = NULL; 142862306a36Sopenharmony_ci if (tmp_addrlen == 0) 142962306a36Sopenharmony_ci goto out_err; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (tmp_addr.ss_family == AF_INET6) { 143262306a36Sopenharmony_ci startsep = "["; 143362306a36Sopenharmony_ci endsep = "]"; 143462306a36Sopenharmony_ci match_netid = "tcp6"; 143562306a36Sopenharmony_ci match_netid_len = 4; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (naddr->netid_len != match_netid_len || 143962306a36Sopenharmony_ci strncmp(naddr->netid, match_netid, naddr->netid_len)) 144062306a36Sopenharmony_ci goto out_err; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* Construct the raw data for the vfs_kern_mount call */ 144362306a36Sopenharmony_ci len = RPC_MAX_ADDRBUFLEN + 1; 144462306a36Sopenharmony_ci ipaddr = kzalloc(len, GFP_KERNEL); 144562306a36Sopenharmony_ci if (!ipaddr) 144662306a36Sopenharmony_ci goto out_err; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci rpc_ntop((struct sockaddr *)&tmp_addr, ipaddr, len); 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci /* 2 for ipv6 endsep and startsep. 3 for ":/" and trailing '/0'*/ 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci raw_len = strlen(NFSD42_INTERSSC_MOUNTOPS) + strlen(ipaddr); 145362306a36Sopenharmony_ci raw_data = kzalloc(raw_len, GFP_KERNEL); 145462306a36Sopenharmony_ci if (!raw_data) 145562306a36Sopenharmony_ci goto out_free_ipaddr; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci snprintf(raw_data, raw_len, NFSD42_INTERSSC_MOUNTOPS, ipaddr); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci status = nfserr_nodev; 146062306a36Sopenharmony_ci type = get_fs_type("nfs"); 146162306a36Sopenharmony_ci if (!type) 146262306a36Sopenharmony_ci goto out_free_rawdata; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci /* Set the server:<export> for the vfs_kern_mount call */ 146562306a36Sopenharmony_ci dev_name = kzalloc(len + 5, GFP_KERNEL); 146662306a36Sopenharmony_ci if (!dev_name) 146762306a36Sopenharmony_ci goto out_free_rawdata; 146862306a36Sopenharmony_ci snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci status = nfsd4_ssc_setup_dul(nn, ipaddr, nsui); 147162306a36Sopenharmony_ci if (status) 147262306a36Sopenharmony_ci goto out_free_devname; 147362306a36Sopenharmony_ci if ((*nsui)->nsui_vfsmount) 147462306a36Sopenharmony_ci goto out_done; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */ 147762306a36Sopenharmony_ci ss_mnt = vfs_kern_mount(type, SB_KERNMOUNT, dev_name, raw_data); 147862306a36Sopenharmony_ci module_put(type->owner); 147962306a36Sopenharmony_ci if (IS_ERR(ss_mnt)) { 148062306a36Sopenharmony_ci status = nfserr_nodev; 148162306a36Sopenharmony_ci nfsd4_ssc_cancel_dul(nn, *nsui); 148262306a36Sopenharmony_ci goto out_free_devname; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci nfsd4_ssc_update_dul(nn, *nsui, ss_mnt); 148562306a36Sopenharmony_ciout_done: 148662306a36Sopenharmony_ci status = 0; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ciout_free_devname: 148962306a36Sopenharmony_ci kfree(dev_name); 149062306a36Sopenharmony_ciout_free_rawdata: 149162306a36Sopenharmony_ci kfree(raw_data); 149262306a36Sopenharmony_ciout_free_ipaddr: 149362306a36Sopenharmony_ci kfree(ipaddr); 149462306a36Sopenharmony_ciout_err: 149562306a36Sopenharmony_ci return status; 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci/* 149962306a36Sopenharmony_ci * Verify COPY destination stateid. 150062306a36Sopenharmony_ci * 150162306a36Sopenharmony_ci * Connect to the source server with NFSv4.1. 150262306a36Sopenharmony_ci * Create the source struct file for nfsd_copy_range. 150362306a36Sopenharmony_ci * Called with COPY cstate: 150462306a36Sopenharmony_ci * SAVED_FH: source filehandle 150562306a36Sopenharmony_ci * CURRENT_FH: destination filehandle 150662306a36Sopenharmony_ci */ 150762306a36Sopenharmony_cistatic __be32 150862306a36Sopenharmony_cinfsd4_setup_inter_ssc(struct svc_rqst *rqstp, 150962306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, 151062306a36Sopenharmony_ci struct nfsd4_copy *copy) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci struct svc_fh *s_fh = NULL; 151362306a36Sopenharmony_ci stateid_t *s_stid = ©->cp_src_stateid; 151462306a36Sopenharmony_ci __be32 status = nfserr_inval; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci /* Verify the destination stateid and set dst struct file*/ 151762306a36Sopenharmony_ci status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, 151862306a36Sopenharmony_ci ©->cp_dst_stateid, 151962306a36Sopenharmony_ci WR_STATE, ©->nf_dst, NULL); 152062306a36Sopenharmony_ci if (status) 152162306a36Sopenharmony_ci goto out; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci status = nfsd4_interssc_connect(copy->cp_src, rqstp, ©->ss_nsui); 152462306a36Sopenharmony_ci if (status) 152562306a36Sopenharmony_ci goto out; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci s_fh = &cstate->save_fh; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci copy->c_fh.size = s_fh->fh_handle.fh_size; 153062306a36Sopenharmony_ci memcpy(copy->c_fh.data, &s_fh->fh_handle.fh_raw, copy->c_fh.size); 153162306a36Sopenharmony_ci copy->stateid.seqid = cpu_to_be32(s_stid->si_generation); 153262306a36Sopenharmony_ci memcpy(copy->stateid.other, (void *)&s_stid->si_opaque, 153362306a36Sopenharmony_ci sizeof(stateid_opaque_t)); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci status = 0; 153662306a36Sopenharmony_ciout: 153762306a36Sopenharmony_ci return status; 153862306a36Sopenharmony_ci} 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_cistatic void 154162306a36Sopenharmony_cinfsd4_cleanup_inter_ssc(struct nfsd4_ssc_umount_item *nsui, struct file *filp, 154262306a36Sopenharmony_ci struct nfsd_file *dst) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(dst->nf_net, nfsd_net_id); 154562306a36Sopenharmony_ci long timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci nfs42_ssc_close(filp); 154862306a36Sopenharmony_ci fput(filp); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci spin_lock(&nn->nfsd_ssc_lock); 155162306a36Sopenharmony_ci list_del(&nsui->nsui_list); 155262306a36Sopenharmony_ci /* 155362306a36Sopenharmony_ci * vfsmount can be shared by multiple exports, 155462306a36Sopenharmony_ci * decrement refcnt. If the count drops to 1 it 155562306a36Sopenharmony_ci * will be unmounted when nsui_expire expires. 155662306a36Sopenharmony_ci */ 155762306a36Sopenharmony_ci refcount_dec(&nsui->nsui_refcnt); 155862306a36Sopenharmony_ci nsui->nsui_expire = jiffies + timeout; 155962306a36Sopenharmony_ci list_add_tail(&nsui->nsui_list, &nn->nfsd_ssc_mount_list); 156062306a36Sopenharmony_ci spin_unlock(&nn->nfsd_ssc_lock); 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci#else /* CONFIG_NFSD_V4_2_INTER_SSC */ 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_cistatic __be32 156662306a36Sopenharmony_cinfsd4_setup_inter_ssc(struct svc_rqst *rqstp, 156762306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, 156862306a36Sopenharmony_ci struct nfsd4_copy *copy) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci return nfserr_inval; 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic void 157462306a36Sopenharmony_cinfsd4_cleanup_inter_ssc(struct nfsd4_ssc_umount_item *nsui, struct file *filp, 157562306a36Sopenharmony_ci struct nfsd_file *dst) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci} 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_cistatic struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, 158062306a36Sopenharmony_ci struct nfs_fh *src_fh, 158162306a36Sopenharmony_ci nfs4_stateid *stateid) 158262306a36Sopenharmony_ci{ 158362306a36Sopenharmony_ci return NULL; 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci#endif /* CONFIG_NFSD_V4_2_INTER_SSC */ 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic __be32 158862306a36Sopenharmony_cinfsd4_setup_intra_ssc(struct svc_rqst *rqstp, 158962306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, 159062306a36Sopenharmony_ci struct nfsd4_copy *copy) 159162306a36Sopenharmony_ci{ 159262306a36Sopenharmony_ci return nfsd4_verify_copy(rqstp, cstate, ©->cp_src_stateid, 159362306a36Sopenharmony_ci ©->nf_src, ©->cp_dst_stateid, 159462306a36Sopenharmony_ci ©->nf_dst); 159562306a36Sopenharmony_ci} 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_cistatic void nfsd4_cb_offload_release(struct nfsd4_callback *cb) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci struct nfsd4_cb_offload *cbo = 160062306a36Sopenharmony_ci container_of(cb, struct nfsd4_cb_offload, co_cb); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci kfree(cbo); 160362306a36Sopenharmony_ci} 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistatic int nfsd4_cb_offload_done(struct nfsd4_callback *cb, 160662306a36Sopenharmony_ci struct rpc_task *task) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci struct nfsd4_cb_offload *cbo = 160962306a36Sopenharmony_ci container_of(cb, struct nfsd4_cb_offload, co_cb); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci trace_nfsd_cb_offload_done(&cbo->co_res.cb_stateid, task); 161262306a36Sopenharmony_ci return 1; 161362306a36Sopenharmony_ci} 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_cistatic const struct nfsd4_callback_ops nfsd4_cb_offload_ops = { 161662306a36Sopenharmony_ci .release = nfsd4_cb_offload_release, 161762306a36Sopenharmony_ci .done = nfsd4_cb_offload_done 161862306a36Sopenharmony_ci}; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_cistatic void nfsd4_init_copy_res(struct nfsd4_copy *copy, bool sync) 162162306a36Sopenharmony_ci{ 162262306a36Sopenharmony_ci copy->cp_res.wr_stable_how = 162362306a36Sopenharmony_ci test_bit(NFSD4_COPY_F_COMMITTED, ©->cp_flags) ? 162462306a36Sopenharmony_ci NFS_FILE_SYNC : NFS_UNSTABLE; 162562306a36Sopenharmony_ci nfsd4_copy_set_sync(copy, sync); 162662306a36Sopenharmony_ci gen_boot_verifier(©->cp_res.wr_verifier, copy->cp_clp->net); 162762306a36Sopenharmony_ci} 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_cistatic ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy, 163062306a36Sopenharmony_ci struct file *dst, 163162306a36Sopenharmony_ci struct file *src) 163262306a36Sopenharmony_ci{ 163362306a36Sopenharmony_ci errseq_t since; 163462306a36Sopenharmony_ci ssize_t bytes_copied = 0; 163562306a36Sopenharmony_ci u64 bytes_total = copy->cp_count; 163662306a36Sopenharmony_ci u64 src_pos = copy->cp_src_pos; 163762306a36Sopenharmony_ci u64 dst_pos = copy->cp_dst_pos; 163862306a36Sopenharmony_ci int status; 163962306a36Sopenharmony_ci loff_t end; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci /* See RFC 7862 p.67: */ 164262306a36Sopenharmony_ci if (bytes_total == 0) 164362306a36Sopenharmony_ci bytes_total = ULLONG_MAX; 164462306a36Sopenharmony_ci do { 164562306a36Sopenharmony_ci if (kthread_should_stop()) 164662306a36Sopenharmony_ci break; 164762306a36Sopenharmony_ci bytes_copied = nfsd_copy_file_range(src, src_pos, dst, dst_pos, 164862306a36Sopenharmony_ci bytes_total); 164962306a36Sopenharmony_ci if (bytes_copied <= 0) 165062306a36Sopenharmony_ci break; 165162306a36Sopenharmony_ci bytes_total -= bytes_copied; 165262306a36Sopenharmony_ci copy->cp_res.wr_bytes_written += bytes_copied; 165362306a36Sopenharmony_ci src_pos += bytes_copied; 165462306a36Sopenharmony_ci dst_pos += bytes_copied; 165562306a36Sopenharmony_ci } while (bytes_total > 0 && nfsd4_copy_is_async(copy)); 165662306a36Sopenharmony_ci /* for a non-zero asynchronous copy do a commit of data */ 165762306a36Sopenharmony_ci if (nfsd4_copy_is_async(copy) && copy->cp_res.wr_bytes_written > 0) { 165862306a36Sopenharmony_ci since = READ_ONCE(dst->f_wb_err); 165962306a36Sopenharmony_ci end = copy->cp_dst_pos + copy->cp_res.wr_bytes_written - 1; 166062306a36Sopenharmony_ci status = vfs_fsync_range(dst, copy->cp_dst_pos, end, 0); 166162306a36Sopenharmony_ci if (!status) 166262306a36Sopenharmony_ci status = filemap_check_wb_err(dst->f_mapping, since); 166362306a36Sopenharmony_ci if (!status) 166462306a36Sopenharmony_ci set_bit(NFSD4_COPY_F_COMMITTED, ©->cp_flags); 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci return bytes_copied; 166762306a36Sopenharmony_ci} 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cistatic __be32 nfsd4_do_copy(struct nfsd4_copy *copy, 167062306a36Sopenharmony_ci struct file *src, struct file *dst, 167162306a36Sopenharmony_ci bool sync) 167262306a36Sopenharmony_ci{ 167362306a36Sopenharmony_ci __be32 status; 167462306a36Sopenharmony_ci ssize_t bytes; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci bytes = _nfsd_copy_file_range(copy, dst, src); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci /* for async copy, we ignore the error, client can always retry 167962306a36Sopenharmony_ci * to get the error 168062306a36Sopenharmony_ci */ 168162306a36Sopenharmony_ci if (bytes < 0 && !copy->cp_res.wr_bytes_written) 168262306a36Sopenharmony_ci status = nfserrno(bytes); 168362306a36Sopenharmony_ci else { 168462306a36Sopenharmony_ci nfsd4_init_copy_res(copy, sync); 168562306a36Sopenharmony_ci status = nfs_ok; 168662306a36Sopenharmony_ci } 168762306a36Sopenharmony_ci return status; 168862306a36Sopenharmony_ci} 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci dst->cp_src_pos = src->cp_src_pos; 169362306a36Sopenharmony_ci dst->cp_dst_pos = src->cp_dst_pos; 169462306a36Sopenharmony_ci dst->cp_count = src->cp_count; 169562306a36Sopenharmony_ci dst->cp_flags = src->cp_flags; 169662306a36Sopenharmony_ci memcpy(&dst->cp_res, &src->cp_res, sizeof(src->cp_res)); 169762306a36Sopenharmony_ci memcpy(&dst->fh, &src->fh, sizeof(src->fh)); 169862306a36Sopenharmony_ci dst->cp_clp = src->cp_clp; 169962306a36Sopenharmony_ci dst->nf_dst = nfsd_file_get(src->nf_dst); 170062306a36Sopenharmony_ci /* for inter, nf_src doesn't exist yet */ 170162306a36Sopenharmony_ci if (!nfsd4_ssc_is_inter(src)) 170262306a36Sopenharmony_ci dst->nf_src = nfsd_file_get(src->nf_src); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci memcpy(&dst->cp_stateid, &src->cp_stateid, sizeof(src->cp_stateid)); 170562306a36Sopenharmony_ci memcpy(dst->cp_src, src->cp_src, sizeof(struct nl4_server)); 170662306a36Sopenharmony_ci memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid)); 170762306a36Sopenharmony_ci memcpy(&dst->c_fh, &src->c_fh, sizeof(src->c_fh)); 170862306a36Sopenharmony_ci dst->ss_nsui = src->ss_nsui; 170962306a36Sopenharmony_ci} 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_cistatic void release_copy_files(struct nfsd4_copy *copy) 171262306a36Sopenharmony_ci{ 171362306a36Sopenharmony_ci if (copy->nf_src) 171462306a36Sopenharmony_ci nfsd_file_put(copy->nf_src); 171562306a36Sopenharmony_ci if (copy->nf_dst) 171662306a36Sopenharmony_ci nfsd_file_put(copy->nf_dst); 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistatic void cleanup_async_copy(struct nfsd4_copy *copy) 172062306a36Sopenharmony_ci{ 172162306a36Sopenharmony_ci nfs4_free_copy_state(copy); 172262306a36Sopenharmony_ci release_copy_files(copy); 172362306a36Sopenharmony_ci if (copy->cp_clp) { 172462306a36Sopenharmony_ci spin_lock(©->cp_clp->async_lock); 172562306a36Sopenharmony_ci if (!list_empty(©->copies)) 172662306a36Sopenharmony_ci list_del_init(©->copies); 172762306a36Sopenharmony_ci spin_unlock(©->cp_clp->async_lock); 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci nfs4_put_copy(copy); 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistatic void nfsd4_send_cb_offload(struct nfsd4_copy *copy, __be32 nfserr) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci struct nfsd4_cb_offload *cbo; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci cbo = kzalloc(sizeof(*cbo), GFP_KERNEL); 173762306a36Sopenharmony_ci if (!cbo) 173862306a36Sopenharmony_ci return; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci memcpy(&cbo->co_res, ©->cp_res, sizeof(copy->cp_res)); 174162306a36Sopenharmony_ci memcpy(&cbo->co_fh, ©->fh, sizeof(copy->fh)); 174262306a36Sopenharmony_ci cbo->co_nfserr = nfserr; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops, 174562306a36Sopenharmony_ci NFSPROC4_CLNT_CB_OFFLOAD); 174662306a36Sopenharmony_ci trace_nfsd_cb_offload(copy->cp_clp, &cbo->co_res.cb_stateid, 174762306a36Sopenharmony_ci &cbo->co_fh, copy->cp_count, nfserr); 174862306a36Sopenharmony_ci nfsd4_run_cb(&cbo->co_cb); 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci/** 175262306a36Sopenharmony_ci * nfsd4_do_async_copy - kthread function for background server-side COPY 175362306a36Sopenharmony_ci * @data: arguments for COPY operation 175462306a36Sopenharmony_ci * 175562306a36Sopenharmony_ci * Return values: 175662306a36Sopenharmony_ci * %0: Copy operation is done. 175762306a36Sopenharmony_ci */ 175862306a36Sopenharmony_cistatic int nfsd4_do_async_copy(void *data) 175962306a36Sopenharmony_ci{ 176062306a36Sopenharmony_ci struct nfsd4_copy *copy = (struct nfsd4_copy *)data; 176162306a36Sopenharmony_ci __be32 nfserr; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci if (nfsd4_ssc_is_inter(copy)) { 176462306a36Sopenharmony_ci struct file *filp; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci filp = nfs42_ssc_open(copy->ss_nsui->nsui_vfsmount, 176762306a36Sopenharmony_ci ©->c_fh, ©->stateid); 176862306a36Sopenharmony_ci if (IS_ERR(filp)) { 176962306a36Sopenharmony_ci switch (PTR_ERR(filp)) { 177062306a36Sopenharmony_ci case -EBADF: 177162306a36Sopenharmony_ci nfserr = nfserr_wrong_type; 177262306a36Sopenharmony_ci break; 177362306a36Sopenharmony_ci default: 177462306a36Sopenharmony_ci nfserr = nfserr_offload_denied; 177562306a36Sopenharmony_ci } 177662306a36Sopenharmony_ci /* ss_mnt will be unmounted by the laundromat */ 177762306a36Sopenharmony_ci goto do_callback; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file, 178062306a36Sopenharmony_ci false); 178162306a36Sopenharmony_ci nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst); 178262306a36Sopenharmony_ci } else { 178362306a36Sopenharmony_ci nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file, 178462306a36Sopenharmony_ci copy->nf_dst->nf_file, false); 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_cido_callback: 178862306a36Sopenharmony_ci nfsd4_send_cb_offload(copy, nfserr); 178962306a36Sopenharmony_ci cleanup_async_copy(copy); 179062306a36Sopenharmony_ci return 0; 179162306a36Sopenharmony_ci} 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_cistatic __be32 179462306a36Sopenharmony_cinfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 179562306a36Sopenharmony_ci union nfsd4_op_u *u) 179662306a36Sopenharmony_ci{ 179762306a36Sopenharmony_ci struct nfsd4_copy *copy = &u->copy; 179862306a36Sopenharmony_ci __be32 status; 179962306a36Sopenharmony_ci struct nfsd4_copy *async_copy = NULL; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (nfsd4_ssc_is_inter(copy)) { 180262306a36Sopenharmony_ci if (!inter_copy_offload_enable || nfsd4_copy_is_sync(copy)) { 180362306a36Sopenharmony_ci status = nfserr_notsupp; 180462306a36Sopenharmony_ci goto out; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci status = nfsd4_setup_inter_ssc(rqstp, cstate, copy); 180762306a36Sopenharmony_ci if (status) 180862306a36Sopenharmony_ci return nfserr_offload_denied; 180962306a36Sopenharmony_ci } else { 181062306a36Sopenharmony_ci status = nfsd4_setup_intra_ssc(rqstp, cstate, copy); 181162306a36Sopenharmony_ci if (status) 181262306a36Sopenharmony_ci return status; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci copy->cp_clp = cstate->clp; 181662306a36Sopenharmony_ci memcpy(©->fh, &cstate->current_fh.fh_handle, 181762306a36Sopenharmony_ci sizeof(struct knfsd_fh)); 181862306a36Sopenharmony_ci if (nfsd4_copy_is_async(copy)) { 181962306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci status = nfserrno(-ENOMEM); 182262306a36Sopenharmony_ci async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL); 182362306a36Sopenharmony_ci if (!async_copy) 182462306a36Sopenharmony_ci goto out_err; 182562306a36Sopenharmony_ci INIT_LIST_HEAD(&async_copy->copies); 182662306a36Sopenharmony_ci refcount_set(&async_copy->refcount, 1); 182762306a36Sopenharmony_ci async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL); 182862306a36Sopenharmony_ci if (!async_copy->cp_src) 182962306a36Sopenharmony_ci goto out_err; 183062306a36Sopenharmony_ci if (!nfs4_init_copy_state(nn, copy)) 183162306a36Sopenharmony_ci goto out_err; 183262306a36Sopenharmony_ci memcpy(©->cp_res.cb_stateid, ©->cp_stateid.cs_stid, 183362306a36Sopenharmony_ci sizeof(copy->cp_res.cb_stateid)); 183462306a36Sopenharmony_ci dup_copy_fields(copy, async_copy); 183562306a36Sopenharmony_ci async_copy->copy_task = kthread_create(nfsd4_do_async_copy, 183662306a36Sopenharmony_ci async_copy, "%s", "copy thread"); 183762306a36Sopenharmony_ci if (IS_ERR(async_copy->copy_task)) 183862306a36Sopenharmony_ci goto out_err; 183962306a36Sopenharmony_ci spin_lock(&async_copy->cp_clp->async_lock); 184062306a36Sopenharmony_ci list_add(&async_copy->copies, 184162306a36Sopenharmony_ci &async_copy->cp_clp->async_copies); 184262306a36Sopenharmony_ci spin_unlock(&async_copy->cp_clp->async_lock); 184362306a36Sopenharmony_ci wake_up_process(async_copy->copy_task); 184462306a36Sopenharmony_ci status = nfs_ok; 184562306a36Sopenharmony_ci } else { 184662306a36Sopenharmony_ci status = nfsd4_do_copy(copy, copy->nf_src->nf_file, 184762306a36Sopenharmony_ci copy->nf_dst->nf_file, true); 184862306a36Sopenharmony_ci } 184962306a36Sopenharmony_ciout: 185062306a36Sopenharmony_ci release_copy_files(copy); 185162306a36Sopenharmony_ci return status; 185262306a36Sopenharmony_ciout_err: 185362306a36Sopenharmony_ci if (nfsd4_ssc_is_inter(copy)) { 185462306a36Sopenharmony_ci /* 185562306a36Sopenharmony_ci * Source's vfsmount of inter-copy will be unmounted 185662306a36Sopenharmony_ci * by the laundromat. Use copy instead of async_copy 185762306a36Sopenharmony_ci * since async_copy->ss_nsui might not be set yet. 185862306a36Sopenharmony_ci */ 185962306a36Sopenharmony_ci refcount_dec(©->ss_nsui->nsui_refcnt); 186062306a36Sopenharmony_ci } 186162306a36Sopenharmony_ci if (async_copy) 186262306a36Sopenharmony_ci cleanup_async_copy(async_copy); 186362306a36Sopenharmony_ci status = nfserrno(-ENOMEM); 186462306a36Sopenharmony_ci goto out; 186562306a36Sopenharmony_ci} 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cistatic struct nfsd4_copy * 186862306a36Sopenharmony_cifind_async_copy_locked(struct nfs4_client *clp, stateid_t *stateid) 186962306a36Sopenharmony_ci{ 187062306a36Sopenharmony_ci struct nfsd4_copy *copy; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci lockdep_assert_held(&clp->async_lock); 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci list_for_each_entry(copy, &clp->async_copies, copies) { 187562306a36Sopenharmony_ci if (memcmp(©->cp_stateid.cs_stid, stateid, NFS4_STATEID_SIZE)) 187662306a36Sopenharmony_ci continue; 187762306a36Sopenharmony_ci return copy; 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci return NULL; 188062306a36Sopenharmony_ci} 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_cistatic struct nfsd4_copy * 188362306a36Sopenharmony_cifind_async_copy(struct nfs4_client *clp, stateid_t *stateid) 188462306a36Sopenharmony_ci{ 188562306a36Sopenharmony_ci struct nfsd4_copy *copy; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci spin_lock(&clp->async_lock); 188862306a36Sopenharmony_ci copy = find_async_copy_locked(clp, stateid); 188962306a36Sopenharmony_ci if (copy) 189062306a36Sopenharmony_ci refcount_inc(©->refcount); 189162306a36Sopenharmony_ci spin_unlock(&clp->async_lock); 189262306a36Sopenharmony_ci return copy; 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_cistatic __be32 189662306a36Sopenharmony_cinfsd4_offload_cancel(struct svc_rqst *rqstp, 189762306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, 189862306a36Sopenharmony_ci union nfsd4_op_u *u) 189962306a36Sopenharmony_ci{ 190062306a36Sopenharmony_ci struct nfsd4_offload_status *os = &u->offload_status; 190162306a36Sopenharmony_ci struct nfsd4_copy *copy; 190262306a36Sopenharmony_ci struct nfs4_client *clp = cstate->clp; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci copy = find_async_copy(clp, &os->stateid); 190562306a36Sopenharmony_ci if (!copy) { 190662306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci return manage_cpntf_state(nn, &os->stateid, clp, NULL); 190962306a36Sopenharmony_ci } else 191062306a36Sopenharmony_ci nfsd4_stop_copy(copy); 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci return nfs_ok; 191362306a36Sopenharmony_ci} 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_cistatic __be32 191662306a36Sopenharmony_cinfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 191762306a36Sopenharmony_ci union nfsd4_op_u *u) 191862306a36Sopenharmony_ci{ 191962306a36Sopenharmony_ci struct nfsd4_copy_notify *cn = &u->copy_notify; 192062306a36Sopenharmony_ci __be32 status; 192162306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 192262306a36Sopenharmony_ci struct nfs4_stid *stid; 192362306a36Sopenharmony_ci struct nfs4_cpntf_state *cps; 192462306a36Sopenharmony_ci struct nfs4_client *clp = cstate->clp; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, 192762306a36Sopenharmony_ci &cn->cpn_src_stateid, RD_STATE, NULL, 192862306a36Sopenharmony_ci &stid); 192962306a36Sopenharmony_ci if (status) 193062306a36Sopenharmony_ci return status; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci cn->cpn_sec = nn->nfsd4_lease; 193362306a36Sopenharmony_ci cn->cpn_nsec = 0; 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci status = nfserrno(-ENOMEM); 193662306a36Sopenharmony_ci cps = nfs4_alloc_init_cpntf_state(nn, stid); 193762306a36Sopenharmony_ci if (!cps) 193862306a36Sopenharmony_ci goto out; 193962306a36Sopenharmony_ci memcpy(&cn->cpn_cnr_stateid, &cps->cp_stateid.cs_stid, sizeof(stateid_t)); 194062306a36Sopenharmony_ci memcpy(&cps->cp_p_stateid, &stid->sc_stateid, sizeof(stateid_t)); 194162306a36Sopenharmony_ci memcpy(&cps->cp_p_clid, &clp->cl_clientid, sizeof(clientid_t)); 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci /* For now, only return one server address in cpn_src, the 194462306a36Sopenharmony_ci * address used by the client to connect to this server. 194562306a36Sopenharmony_ci */ 194662306a36Sopenharmony_ci cn->cpn_src->nl4_type = NL4_NETADDR; 194762306a36Sopenharmony_ci status = nfsd4_set_netaddr((struct sockaddr *)&rqstp->rq_daddr, 194862306a36Sopenharmony_ci &cn->cpn_src->u.nl4_addr); 194962306a36Sopenharmony_ci WARN_ON_ONCE(status); 195062306a36Sopenharmony_ci if (status) { 195162306a36Sopenharmony_ci nfs4_put_cpntf_state(nn, cps); 195262306a36Sopenharmony_ci goto out; 195362306a36Sopenharmony_ci } 195462306a36Sopenharmony_ciout: 195562306a36Sopenharmony_ci nfs4_put_stid(stid); 195662306a36Sopenharmony_ci return status; 195762306a36Sopenharmony_ci} 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_cistatic __be32 196062306a36Sopenharmony_cinfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 196162306a36Sopenharmony_ci struct nfsd4_fallocate *fallocate, int flags) 196262306a36Sopenharmony_ci{ 196362306a36Sopenharmony_ci __be32 status; 196462306a36Sopenharmony_ci struct nfsd_file *nf; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, 196762306a36Sopenharmony_ci &fallocate->falloc_stateid, 196862306a36Sopenharmony_ci WR_STATE, &nf, NULL); 196962306a36Sopenharmony_ci if (status != nfs_ok) 197062306a36Sopenharmony_ci return status; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci status = nfsd4_vfs_fallocate(rqstp, &cstate->current_fh, nf->nf_file, 197362306a36Sopenharmony_ci fallocate->falloc_offset, 197462306a36Sopenharmony_ci fallocate->falloc_length, 197562306a36Sopenharmony_ci flags); 197662306a36Sopenharmony_ci nfsd_file_put(nf); 197762306a36Sopenharmony_ci return status; 197862306a36Sopenharmony_ci} 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_cistatic __be32 198162306a36Sopenharmony_cinfsd4_offload_status(struct svc_rqst *rqstp, 198262306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, 198362306a36Sopenharmony_ci union nfsd4_op_u *u) 198462306a36Sopenharmony_ci{ 198562306a36Sopenharmony_ci struct nfsd4_offload_status *os = &u->offload_status; 198662306a36Sopenharmony_ci __be32 status = nfs_ok; 198762306a36Sopenharmony_ci struct nfsd4_copy *copy; 198862306a36Sopenharmony_ci struct nfs4_client *clp = cstate->clp; 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci spin_lock(&clp->async_lock); 199162306a36Sopenharmony_ci copy = find_async_copy_locked(clp, &os->stateid); 199262306a36Sopenharmony_ci if (copy) 199362306a36Sopenharmony_ci os->count = copy->cp_res.wr_bytes_written; 199462306a36Sopenharmony_ci else 199562306a36Sopenharmony_ci status = nfserr_bad_stateid; 199662306a36Sopenharmony_ci spin_unlock(&clp->async_lock); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci return status; 199962306a36Sopenharmony_ci} 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_cistatic __be32 200262306a36Sopenharmony_cinfsd4_allocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 200362306a36Sopenharmony_ci union nfsd4_op_u *u) 200462306a36Sopenharmony_ci{ 200562306a36Sopenharmony_ci return nfsd4_fallocate(rqstp, cstate, &u->allocate, 0); 200662306a36Sopenharmony_ci} 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_cistatic __be32 200962306a36Sopenharmony_cinfsd4_deallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 201062306a36Sopenharmony_ci union nfsd4_op_u *u) 201162306a36Sopenharmony_ci{ 201262306a36Sopenharmony_ci return nfsd4_fallocate(rqstp, cstate, &u->deallocate, 201362306a36Sopenharmony_ci FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE); 201462306a36Sopenharmony_ci} 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_cistatic __be32 201762306a36Sopenharmony_cinfsd4_seek(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 201862306a36Sopenharmony_ci union nfsd4_op_u *u) 201962306a36Sopenharmony_ci{ 202062306a36Sopenharmony_ci struct nfsd4_seek *seek = &u->seek; 202162306a36Sopenharmony_ci int whence; 202262306a36Sopenharmony_ci __be32 status; 202362306a36Sopenharmony_ci struct nfsd_file *nf; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, 202662306a36Sopenharmony_ci &seek->seek_stateid, 202762306a36Sopenharmony_ci RD_STATE, &nf, NULL); 202862306a36Sopenharmony_ci if (status) 202962306a36Sopenharmony_ci return status; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci switch (seek->seek_whence) { 203262306a36Sopenharmony_ci case NFS4_CONTENT_DATA: 203362306a36Sopenharmony_ci whence = SEEK_DATA; 203462306a36Sopenharmony_ci break; 203562306a36Sopenharmony_ci case NFS4_CONTENT_HOLE: 203662306a36Sopenharmony_ci whence = SEEK_HOLE; 203762306a36Sopenharmony_ci break; 203862306a36Sopenharmony_ci default: 203962306a36Sopenharmony_ci status = nfserr_union_notsupp; 204062306a36Sopenharmony_ci goto out; 204162306a36Sopenharmony_ci } 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci /* 204462306a36Sopenharmony_ci * Note: This call does change file->f_pos, but nothing in NFSD 204562306a36Sopenharmony_ci * should ever file->f_pos. 204662306a36Sopenharmony_ci */ 204762306a36Sopenharmony_ci seek->seek_pos = vfs_llseek(nf->nf_file, seek->seek_offset, whence); 204862306a36Sopenharmony_ci if (seek->seek_pos < 0) 204962306a36Sopenharmony_ci status = nfserrno(seek->seek_pos); 205062306a36Sopenharmony_ci else if (seek->seek_pos >= i_size_read(file_inode(nf->nf_file))) 205162306a36Sopenharmony_ci seek->seek_eof = true; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ciout: 205462306a36Sopenharmony_ci nfsd_file_put(nf); 205562306a36Sopenharmony_ci return status; 205662306a36Sopenharmony_ci} 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci/* This routine never returns NFS_OK! If there are no other errors, it 205962306a36Sopenharmony_ci * will return NFSERR_SAME or NFSERR_NOT_SAME depending on whether the 206062306a36Sopenharmony_ci * attributes matched. VERIFY is implemented by mapping NFSERR_SAME 206162306a36Sopenharmony_ci * to NFS_OK after the call; NVERIFY by mapping NFSERR_NOT_SAME to NFS_OK. 206262306a36Sopenharmony_ci */ 206362306a36Sopenharmony_cistatic __be32 206462306a36Sopenharmony_ci_nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 206562306a36Sopenharmony_ci struct nfsd4_verify *verify) 206662306a36Sopenharmony_ci{ 206762306a36Sopenharmony_ci __be32 *buf, *p; 206862306a36Sopenharmony_ci int count; 206962306a36Sopenharmony_ci __be32 status; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); 207262306a36Sopenharmony_ci if (status) 207362306a36Sopenharmony_ci return status; 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL); 207662306a36Sopenharmony_ci if (status) 207762306a36Sopenharmony_ci return status; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR) 208062306a36Sopenharmony_ci || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)) 208162306a36Sopenharmony_ci return nfserr_inval; 208262306a36Sopenharmony_ci if (verify->ve_attrlen & 3) 208362306a36Sopenharmony_ci return nfserr_inval; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci /* count in words: 208662306a36Sopenharmony_ci * bitmap_len(1) + bitmap(2) + attr_len(1) = 4 208762306a36Sopenharmony_ci */ 208862306a36Sopenharmony_ci count = 4 + (verify->ve_attrlen >> 2); 208962306a36Sopenharmony_ci buf = kmalloc(count << 2, GFP_KERNEL); 209062306a36Sopenharmony_ci if (!buf) 209162306a36Sopenharmony_ci return nfserr_jukebox; 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci p = buf; 209462306a36Sopenharmony_ci status = nfsd4_encode_fattr_to_buf(&p, count, &cstate->current_fh, 209562306a36Sopenharmony_ci cstate->current_fh.fh_export, 209662306a36Sopenharmony_ci cstate->current_fh.fh_dentry, 209762306a36Sopenharmony_ci verify->ve_bmval, 209862306a36Sopenharmony_ci rqstp, 0); 209962306a36Sopenharmony_ci /* 210062306a36Sopenharmony_ci * If nfsd4_encode_fattr() ran out of space, assume that's because 210162306a36Sopenharmony_ci * the attributes are longer (hence different) than those given: 210262306a36Sopenharmony_ci */ 210362306a36Sopenharmony_ci if (status == nfserr_resource) 210462306a36Sopenharmony_ci status = nfserr_not_same; 210562306a36Sopenharmony_ci if (status) 210662306a36Sopenharmony_ci goto out_kfree; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci /* skip bitmap */ 210962306a36Sopenharmony_ci p = buf + 1 + ntohl(buf[0]); 211062306a36Sopenharmony_ci status = nfserr_not_same; 211162306a36Sopenharmony_ci if (ntohl(*p++) != verify->ve_attrlen) 211262306a36Sopenharmony_ci goto out_kfree; 211362306a36Sopenharmony_ci if (!memcmp(p, verify->ve_attrval, verify->ve_attrlen)) 211462306a36Sopenharmony_ci status = nfserr_same; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ciout_kfree: 211762306a36Sopenharmony_ci kfree(buf); 211862306a36Sopenharmony_ci return status; 211962306a36Sopenharmony_ci} 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_cistatic __be32 212262306a36Sopenharmony_cinfsd4_nverify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 212362306a36Sopenharmony_ci union nfsd4_op_u *u) 212462306a36Sopenharmony_ci{ 212562306a36Sopenharmony_ci __be32 status; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci status = _nfsd4_verify(rqstp, cstate, &u->verify); 212862306a36Sopenharmony_ci return status == nfserr_not_same ? nfs_ok : status; 212962306a36Sopenharmony_ci} 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_cistatic __be32 213262306a36Sopenharmony_cinfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 213362306a36Sopenharmony_ci union nfsd4_op_u *u) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci __be32 status; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci status = _nfsd4_verify(rqstp, cstate, &u->nverify); 213862306a36Sopenharmony_ci return status == nfserr_same ? nfs_ok : status; 213962306a36Sopenharmony_ci} 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS 214262306a36Sopenharmony_cistatic const struct nfsd4_layout_ops * 214362306a36Sopenharmony_cinfsd4_layout_verify(struct svc_export *exp, unsigned int layout_type) 214462306a36Sopenharmony_ci{ 214562306a36Sopenharmony_ci if (!exp->ex_layout_types) { 214662306a36Sopenharmony_ci dprintk("%s: export does not support pNFS\n", __func__); 214762306a36Sopenharmony_ci return NULL; 214862306a36Sopenharmony_ci } 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci if (layout_type >= LAYOUT_TYPE_MAX || 215162306a36Sopenharmony_ci !(exp->ex_layout_types & (1 << layout_type))) { 215262306a36Sopenharmony_ci dprintk("%s: layout type %d not supported\n", 215362306a36Sopenharmony_ci __func__, layout_type); 215462306a36Sopenharmony_ci return NULL; 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci return nfsd4_layout_ops[layout_type]; 215862306a36Sopenharmony_ci} 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_cistatic __be32 216162306a36Sopenharmony_cinfsd4_getdeviceinfo(struct svc_rqst *rqstp, 216262306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, union nfsd4_op_u *u) 216362306a36Sopenharmony_ci{ 216462306a36Sopenharmony_ci struct nfsd4_getdeviceinfo *gdp = &u->getdeviceinfo; 216562306a36Sopenharmony_ci const struct nfsd4_layout_ops *ops; 216662306a36Sopenharmony_ci struct nfsd4_deviceid_map *map; 216762306a36Sopenharmony_ci struct svc_export *exp; 216862306a36Sopenharmony_ci __be32 nfserr; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci dprintk("%s: layout_type %u dev_id [0x%llx:0x%x] maxcnt %u\n", 217162306a36Sopenharmony_ci __func__, 217262306a36Sopenharmony_ci gdp->gd_layout_type, 217362306a36Sopenharmony_ci gdp->gd_devid.fsid_idx, gdp->gd_devid.generation, 217462306a36Sopenharmony_ci gdp->gd_maxcount); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci map = nfsd4_find_devid_map(gdp->gd_devid.fsid_idx); 217762306a36Sopenharmony_ci if (!map) { 217862306a36Sopenharmony_ci dprintk("%s: couldn't find device ID to export mapping!\n", 217962306a36Sopenharmony_ci __func__); 218062306a36Sopenharmony_ci return nfserr_noent; 218162306a36Sopenharmony_ci } 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci exp = rqst_exp_find(rqstp, map->fsid_type, map->fsid); 218462306a36Sopenharmony_ci if (IS_ERR(exp)) { 218562306a36Sopenharmony_ci dprintk("%s: could not find device id\n", __func__); 218662306a36Sopenharmony_ci return nfserr_noent; 218762306a36Sopenharmony_ci } 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci nfserr = nfserr_layoutunavailable; 219062306a36Sopenharmony_ci ops = nfsd4_layout_verify(exp, gdp->gd_layout_type); 219162306a36Sopenharmony_ci if (!ops) 219262306a36Sopenharmony_ci goto out; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci nfserr = nfs_ok; 219562306a36Sopenharmony_ci if (gdp->gd_maxcount != 0) { 219662306a36Sopenharmony_ci nfserr = ops->proc_getdeviceinfo(exp->ex_path.mnt->mnt_sb, 219762306a36Sopenharmony_ci rqstp, cstate->clp, gdp); 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci gdp->gd_notify_types &= ops->notify_types; 220162306a36Sopenharmony_ciout: 220262306a36Sopenharmony_ci exp_put(exp); 220362306a36Sopenharmony_ci return nfserr; 220462306a36Sopenharmony_ci} 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_cistatic void 220762306a36Sopenharmony_cinfsd4_getdeviceinfo_release(union nfsd4_op_u *u) 220862306a36Sopenharmony_ci{ 220962306a36Sopenharmony_ci kfree(u->getdeviceinfo.gd_device); 221062306a36Sopenharmony_ci} 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_cistatic __be32 221362306a36Sopenharmony_cinfsd4_layoutget(struct svc_rqst *rqstp, 221462306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, union nfsd4_op_u *u) 221562306a36Sopenharmony_ci{ 221662306a36Sopenharmony_ci struct nfsd4_layoutget *lgp = &u->layoutget; 221762306a36Sopenharmony_ci struct svc_fh *current_fh = &cstate->current_fh; 221862306a36Sopenharmony_ci const struct nfsd4_layout_ops *ops; 221962306a36Sopenharmony_ci struct nfs4_layout_stateid *ls; 222062306a36Sopenharmony_ci __be32 nfserr; 222162306a36Sopenharmony_ci int accmode = NFSD_MAY_READ_IF_EXEC; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci switch (lgp->lg_seg.iomode) { 222462306a36Sopenharmony_ci case IOMODE_READ: 222562306a36Sopenharmony_ci accmode |= NFSD_MAY_READ; 222662306a36Sopenharmony_ci break; 222762306a36Sopenharmony_ci case IOMODE_RW: 222862306a36Sopenharmony_ci accmode |= NFSD_MAY_READ | NFSD_MAY_WRITE; 222962306a36Sopenharmony_ci break; 223062306a36Sopenharmony_ci default: 223162306a36Sopenharmony_ci dprintk("%s: invalid iomode %d\n", 223262306a36Sopenharmony_ci __func__, lgp->lg_seg.iomode); 223362306a36Sopenharmony_ci nfserr = nfserr_badiomode; 223462306a36Sopenharmony_ci goto out; 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci nfserr = fh_verify(rqstp, current_fh, 0, accmode); 223862306a36Sopenharmony_ci if (nfserr) 223962306a36Sopenharmony_ci goto out; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci nfserr = nfserr_layoutunavailable; 224262306a36Sopenharmony_ci ops = nfsd4_layout_verify(current_fh->fh_export, lgp->lg_layout_type); 224362306a36Sopenharmony_ci if (!ops) 224462306a36Sopenharmony_ci goto out; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci /* 224762306a36Sopenharmony_ci * Verify minlength and range as per RFC5661: 224862306a36Sopenharmony_ci * o If loga_length is less than loga_minlength, 224962306a36Sopenharmony_ci * the metadata server MUST return NFS4ERR_INVAL. 225062306a36Sopenharmony_ci * o If the sum of loga_offset and loga_minlength exceeds 225162306a36Sopenharmony_ci * NFS4_UINT64_MAX, and loga_minlength is not 225262306a36Sopenharmony_ci * NFS4_UINT64_MAX, the error NFS4ERR_INVAL MUST result. 225362306a36Sopenharmony_ci * o If the sum of loga_offset and loga_length exceeds 225462306a36Sopenharmony_ci * NFS4_UINT64_MAX, and loga_length is not NFS4_UINT64_MAX, 225562306a36Sopenharmony_ci * the error NFS4ERR_INVAL MUST result. 225662306a36Sopenharmony_ci */ 225762306a36Sopenharmony_ci nfserr = nfserr_inval; 225862306a36Sopenharmony_ci if (lgp->lg_seg.length < lgp->lg_minlength || 225962306a36Sopenharmony_ci (lgp->lg_minlength != NFS4_MAX_UINT64 && 226062306a36Sopenharmony_ci lgp->lg_minlength > NFS4_MAX_UINT64 - lgp->lg_seg.offset) || 226162306a36Sopenharmony_ci (lgp->lg_seg.length != NFS4_MAX_UINT64 && 226262306a36Sopenharmony_ci lgp->lg_seg.length > NFS4_MAX_UINT64 - lgp->lg_seg.offset)) 226362306a36Sopenharmony_ci goto out; 226462306a36Sopenharmony_ci if (lgp->lg_seg.length == 0) 226562306a36Sopenharmony_ci goto out; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lgp->lg_sid, 226862306a36Sopenharmony_ci true, lgp->lg_layout_type, &ls); 226962306a36Sopenharmony_ci if (nfserr) { 227062306a36Sopenharmony_ci trace_nfsd_layout_get_lookup_fail(&lgp->lg_sid); 227162306a36Sopenharmony_ci goto out; 227262306a36Sopenharmony_ci } 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci nfserr = nfserr_recallconflict; 227562306a36Sopenharmony_ci if (atomic_read(&ls->ls_stid.sc_file->fi_lo_recalls)) 227662306a36Sopenharmony_ci goto out_put_stid; 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci nfserr = ops->proc_layoutget(d_inode(current_fh->fh_dentry), 227962306a36Sopenharmony_ci current_fh, lgp); 228062306a36Sopenharmony_ci if (nfserr) 228162306a36Sopenharmony_ci goto out_put_stid; 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci nfserr = nfsd4_insert_layout(lgp, ls); 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ciout_put_stid: 228662306a36Sopenharmony_ci mutex_unlock(&ls->ls_mutex); 228762306a36Sopenharmony_ci nfs4_put_stid(&ls->ls_stid); 228862306a36Sopenharmony_ciout: 228962306a36Sopenharmony_ci return nfserr; 229062306a36Sopenharmony_ci} 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_cistatic void 229362306a36Sopenharmony_cinfsd4_layoutget_release(union nfsd4_op_u *u) 229462306a36Sopenharmony_ci{ 229562306a36Sopenharmony_ci kfree(u->layoutget.lg_content); 229662306a36Sopenharmony_ci} 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_cistatic __be32 229962306a36Sopenharmony_cinfsd4_layoutcommit(struct svc_rqst *rqstp, 230062306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, union nfsd4_op_u *u) 230162306a36Sopenharmony_ci{ 230262306a36Sopenharmony_ci struct nfsd4_layoutcommit *lcp = &u->layoutcommit; 230362306a36Sopenharmony_ci const struct nfsd4_layout_seg *seg = &lcp->lc_seg; 230462306a36Sopenharmony_ci struct svc_fh *current_fh = &cstate->current_fh; 230562306a36Sopenharmony_ci const struct nfsd4_layout_ops *ops; 230662306a36Sopenharmony_ci loff_t new_size = lcp->lc_last_wr + 1; 230762306a36Sopenharmony_ci struct inode *inode; 230862306a36Sopenharmony_ci struct nfs4_layout_stateid *ls; 230962306a36Sopenharmony_ci __be32 nfserr; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci nfserr = fh_verify(rqstp, current_fh, 0, NFSD_MAY_WRITE); 231262306a36Sopenharmony_ci if (nfserr) 231362306a36Sopenharmony_ci goto out; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci nfserr = nfserr_layoutunavailable; 231662306a36Sopenharmony_ci ops = nfsd4_layout_verify(current_fh->fh_export, lcp->lc_layout_type); 231762306a36Sopenharmony_ci if (!ops) 231862306a36Sopenharmony_ci goto out; 231962306a36Sopenharmony_ci inode = d_inode(current_fh->fh_dentry); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci nfserr = nfserr_inval; 232262306a36Sopenharmony_ci if (new_size <= seg->offset) { 232362306a36Sopenharmony_ci dprintk("pnfsd: last write before layout segment\n"); 232462306a36Sopenharmony_ci goto out; 232562306a36Sopenharmony_ci } 232662306a36Sopenharmony_ci if (new_size > seg->offset + seg->length) { 232762306a36Sopenharmony_ci dprintk("pnfsd: last write beyond layout segment\n"); 232862306a36Sopenharmony_ci goto out; 232962306a36Sopenharmony_ci } 233062306a36Sopenharmony_ci if (!lcp->lc_newoffset && new_size > i_size_read(inode)) { 233162306a36Sopenharmony_ci dprintk("pnfsd: layoutcommit beyond EOF\n"); 233262306a36Sopenharmony_ci goto out; 233362306a36Sopenharmony_ci } 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lcp->lc_sid, 233662306a36Sopenharmony_ci false, lcp->lc_layout_type, 233762306a36Sopenharmony_ci &ls); 233862306a36Sopenharmony_ci if (nfserr) { 233962306a36Sopenharmony_ci trace_nfsd_layout_commit_lookup_fail(&lcp->lc_sid); 234062306a36Sopenharmony_ci /* fixup error code as per RFC5661 */ 234162306a36Sopenharmony_ci if (nfserr == nfserr_bad_stateid) 234262306a36Sopenharmony_ci nfserr = nfserr_badlayout; 234362306a36Sopenharmony_ci goto out; 234462306a36Sopenharmony_ci } 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci /* LAYOUTCOMMIT does not require any serialization */ 234762306a36Sopenharmony_ci mutex_unlock(&ls->ls_mutex); 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci if (new_size > i_size_read(inode)) { 235062306a36Sopenharmony_ci lcp->lc_size_chg = 1; 235162306a36Sopenharmony_ci lcp->lc_newsize = new_size; 235262306a36Sopenharmony_ci } else { 235362306a36Sopenharmony_ci lcp->lc_size_chg = 0; 235462306a36Sopenharmony_ci } 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci nfserr = ops->proc_layoutcommit(inode, lcp); 235762306a36Sopenharmony_ci nfs4_put_stid(&ls->ls_stid); 235862306a36Sopenharmony_ciout: 235962306a36Sopenharmony_ci return nfserr; 236062306a36Sopenharmony_ci} 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_cistatic __be32 236362306a36Sopenharmony_cinfsd4_layoutreturn(struct svc_rqst *rqstp, 236462306a36Sopenharmony_ci struct nfsd4_compound_state *cstate, union nfsd4_op_u *u) 236562306a36Sopenharmony_ci{ 236662306a36Sopenharmony_ci struct nfsd4_layoutreturn *lrp = &u->layoutreturn; 236762306a36Sopenharmony_ci struct svc_fh *current_fh = &cstate->current_fh; 236862306a36Sopenharmony_ci __be32 nfserr; 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci nfserr = fh_verify(rqstp, current_fh, 0, NFSD_MAY_NOP); 237162306a36Sopenharmony_ci if (nfserr) 237262306a36Sopenharmony_ci goto out; 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci nfserr = nfserr_layoutunavailable; 237562306a36Sopenharmony_ci if (!nfsd4_layout_verify(current_fh->fh_export, lrp->lr_layout_type)) 237662306a36Sopenharmony_ci goto out; 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci switch (lrp->lr_seg.iomode) { 237962306a36Sopenharmony_ci case IOMODE_READ: 238062306a36Sopenharmony_ci case IOMODE_RW: 238162306a36Sopenharmony_ci case IOMODE_ANY: 238262306a36Sopenharmony_ci break; 238362306a36Sopenharmony_ci default: 238462306a36Sopenharmony_ci dprintk("%s: invalid iomode %d\n", __func__, 238562306a36Sopenharmony_ci lrp->lr_seg.iomode); 238662306a36Sopenharmony_ci nfserr = nfserr_inval; 238762306a36Sopenharmony_ci goto out; 238862306a36Sopenharmony_ci } 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci switch (lrp->lr_return_type) { 239162306a36Sopenharmony_ci case RETURN_FILE: 239262306a36Sopenharmony_ci nfserr = nfsd4_return_file_layouts(rqstp, cstate, lrp); 239362306a36Sopenharmony_ci break; 239462306a36Sopenharmony_ci case RETURN_FSID: 239562306a36Sopenharmony_ci case RETURN_ALL: 239662306a36Sopenharmony_ci nfserr = nfsd4_return_client_layouts(rqstp, cstate, lrp); 239762306a36Sopenharmony_ci break; 239862306a36Sopenharmony_ci default: 239962306a36Sopenharmony_ci dprintk("%s: invalid return_type %d\n", __func__, 240062306a36Sopenharmony_ci lrp->lr_return_type); 240162306a36Sopenharmony_ci nfserr = nfserr_inval; 240262306a36Sopenharmony_ci break; 240362306a36Sopenharmony_ci } 240462306a36Sopenharmony_ciout: 240562306a36Sopenharmony_ci return nfserr; 240662306a36Sopenharmony_ci} 240762306a36Sopenharmony_ci#endif /* CONFIG_NFSD_PNFS */ 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_cistatic __be32 241062306a36Sopenharmony_cinfsd4_getxattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 241162306a36Sopenharmony_ci union nfsd4_op_u *u) 241262306a36Sopenharmony_ci{ 241362306a36Sopenharmony_ci struct nfsd4_getxattr *getxattr = &u->getxattr; 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci return nfsd_getxattr(rqstp, &cstate->current_fh, 241662306a36Sopenharmony_ci getxattr->getxa_name, &getxattr->getxa_buf, 241762306a36Sopenharmony_ci &getxattr->getxa_len); 241862306a36Sopenharmony_ci} 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_cistatic __be32 242162306a36Sopenharmony_cinfsd4_setxattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 242262306a36Sopenharmony_ci union nfsd4_op_u *u) 242362306a36Sopenharmony_ci{ 242462306a36Sopenharmony_ci struct nfsd4_setxattr *setxattr = &u->setxattr; 242562306a36Sopenharmony_ci __be32 ret; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci if (opens_in_grace(SVC_NET(rqstp))) 242862306a36Sopenharmony_ci return nfserr_grace; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci ret = nfsd_setxattr(rqstp, &cstate->current_fh, setxattr->setxa_name, 243162306a36Sopenharmony_ci setxattr->setxa_buf, setxattr->setxa_len, 243262306a36Sopenharmony_ci setxattr->setxa_flags); 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci if (!ret) 243562306a36Sopenharmony_ci set_change_info(&setxattr->setxa_cinfo, &cstate->current_fh); 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci return ret; 243862306a36Sopenharmony_ci} 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_cistatic __be32 244162306a36Sopenharmony_cinfsd4_listxattrs(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 244262306a36Sopenharmony_ci union nfsd4_op_u *u) 244362306a36Sopenharmony_ci{ 244462306a36Sopenharmony_ci /* 244562306a36Sopenharmony_ci * Get the entire list, then copy out only the user attributes 244662306a36Sopenharmony_ci * in the encode function. 244762306a36Sopenharmony_ci */ 244862306a36Sopenharmony_ci return nfsd_listxattr(rqstp, &cstate->current_fh, 244962306a36Sopenharmony_ci &u->listxattrs.lsxa_buf, &u->listxattrs.lsxa_len); 245062306a36Sopenharmony_ci} 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_cistatic __be32 245362306a36Sopenharmony_cinfsd4_removexattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 245462306a36Sopenharmony_ci union nfsd4_op_u *u) 245562306a36Sopenharmony_ci{ 245662306a36Sopenharmony_ci struct nfsd4_removexattr *removexattr = &u->removexattr; 245762306a36Sopenharmony_ci __be32 ret; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci if (opens_in_grace(SVC_NET(rqstp))) 246062306a36Sopenharmony_ci return nfserr_grace; 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci ret = nfsd_removexattr(rqstp, &cstate->current_fh, 246362306a36Sopenharmony_ci removexattr->rmxa_name); 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci if (!ret) 246662306a36Sopenharmony_ci set_change_info(&removexattr->rmxa_cinfo, &cstate->current_fh); 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci return ret; 246962306a36Sopenharmony_ci} 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci/* 247262306a36Sopenharmony_ci * NULL call. 247362306a36Sopenharmony_ci */ 247462306a36Sopenharmony_cistatic __be32 247562306a36Sopenharmony_cinfsd4_proc_null(struct svc_rqst *rqstp) 247662306a36Sopenharmony_ci{ 247762306a36Sopenharmony_ci return rpc_success; 247862306a36Sopenharmony_ci} 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_cistatic inline void nfsd4_increment_op_stats(u32 opnum) 248162306a36Sopenharmony_ci{ 248262306a36Sopenharmony_ci if (opnum >= FIRST_NFS4_OP && opnum <= LAST_NFS4_OP) 248362306a36Sopenharmony_ci percpu_counter_inc(&nfsdstats.counter[NFSD_STATS_NFS4_OP(opnum)]); 248462306a36Sopenharmony_ci} 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_cistatic const struct nfsd4_operation nfsd4_ops[]; 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_cistatic const char *nfsd4_op_name(unsigned opnum); 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci/* 249162306a36Sopenharmony_ci * Enforce NFSv4.1 COMPOUND ordering rules: 249262306a36Sopenharmony_ci * 249362306a36Sopenharmony_ci * Also note, enforced elsewhere: 249462306a36Sopenharmony_ci * - SEQUENCE other than as first op results in 249562306a36Sopenharmony_ci * NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().) 249662306a36Sopenharmony_ci * - BIND_CONN_TO_SESSION must be the only op in its compound. 249762306a36Sopenharmony_ci * (Enforced in nfsd4_bind_conn_to_session().) 249862306a36Sopenharmony_ci * - DESTROY_SESSION must be the final operation in a compound, if 249962306a36Sopenharmony_ci * sessionid's in SEQUENCE and DESTROY_SESSION are the same. 250062306a36Sopenharmony_ci * (Enforced in nfsd4_destroy_session().) 250162306a36Sopenharmony_ci */ 250262306a36Sopenharmony_cistatic __be32 nfs41_check_op_ordering(struct nfsd4_compoundargs *args) 250362306a36Sopenharmony_ci{ 250462306a36Sopenharmony_ci struct nfsd4_op *first_op = &args->ops[0]; 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci /* These ordering requirements don't apply to NFSv4.0: */ 250762306a36Sopenharmony_ci if (args->minorversion == 0) 250862306a36Sopenharmony_ci return nfs_ok; 250962306a36Sopenharmony_ci /* This is weird, but OK, not our problem: */ 251062306a36Sopenharmony_ci if (args->opcnt == 0) 251162306a36Sopenharmony_ci return nfs_ok; 251262306a36Sopenharmony_ci if (first_op->status == nfserr_op_illegal) 251362306a36Sopenharmony_ci return nfs_ok; 251462306a36Sopenharmony_ci if (!(nfsd4_ops[first_op->opnum].op_flags & ALLOWED_AS_FIRST_OP)) 251562306a36Sopenharmony_ci return nfserr_op_not_in_session; 251662306a36Sopenharmony_ci if (first_op->opnum == OP_SEQUENCE) 251762306a36Sopenharmony_ci return nfs_ok; 251862306a36Sopenharmony_ci /* 251962306a36Sopenharmony_ci * So first_op is something allowed outside a session, like 252062306a36Sopenharmony_ci * EXCHANGE_ID; but then it has to be the only op in the 252162306a36Sopenharmony_ci * compound: 252262306a36Sopenharmony_ci */ 252362306a36Sopenharmony_ci if (args->opcnt != 1) 252462306a36Sopenharmony_ci return nfserr_not_only_op; 252562306a36Sopenharmony_ci return nfs_ok; 252662306a36Sopenharmony_ci} 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ciconst struct nfsd4_operation *OPDESC(struct nfsd4_op *op) 252962306a36Sopenharmony_ci{ 253062306a36Sopenharmony_ci return &nfsd4_ops[op->opnum]; 253162306a36Sopenharmony_ci} 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_cibool nfsd4_cache_this_op(struct nfsd4_op *op) 253462306a36Sopenharmony_ci{ 253562306a36Sopenharmony_ci if (op->opnum == OP_ILLEGAL) 253662306a36Sopenharmony_ci return false; 253762306a36Sopenharmony_ci return OPDESC(op)->op_flags & OP_CACHEME; 253862306a36Sopenharmony_ci} 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_cistatic bool need_wrongsec_check(struct svc_rqst *rqstp) 254162306a36Sopenharmony_ci{ 254262306a36Sopenharmony_ci struct nfsd4_compoundres *resp = rqstp->rq_resp; 254362306a36Sopenharmony_ci struct nfsd4_compoundargs *argp = rqstp->rq_argp; 254462306a36Sopenharmony_ci struct nfsd4_op *this = &argp->ops[resp->opcnt - 1]; 254562306a36Sopenharmony_ci struct nfsd4_op *next = &argp->ops[resp->opcnt]; 254662306a36Sopenharmony_ci const struct nfsd4_operation *thisd = OPDESC(this); 254762306a36Sopenharmony_ci const struct nfsd4_operation *nextd; 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci /* 255062306a36Sopenharmony_ci * Most ops check wronsec on our own; only the putfh-like ops 255162306a36Sopenharmony_ci * have special rules. 255262306a36Sopenharmony_ci */ 255362306a36Sopenharmony_ci if (!(thisd->op_flags & OP_IS_PUTFH_LIKE)) 255462306a36Sopenharmony_ci return false; 255562306a36Sopenharmony_ci /* 255662306a36Sopenharmony_ci * rfc 5661 2.6.3.1.1.6: don't bother erroring out a 255762306a36Sopenharmony_ci * put-filehandle operation if we're not going to use the 255862306a36Sopenharmony_ci * result: 255962306a36Sopenharmony_ci */ 256062306a36Sopenharmony_ci if (argp->opcnt == resp->opcnt) 256162306a36Sopenharmony_ci return false; 256262306a36Sopenharmony_ci if (next->opnum == OP_ILLEGAL) 256362306a36Sopenharmony_ci return false; 256462306a36Sopenharmony_ci nextd = OPDESC(next); 256562306a36Sopenharmony_ci /* 256662306a36Sopenharmony_ci * Rest of 2.6.3.1.1: certain operations will return WRONGSEC 256762306a36Sopenharmony_ci * errors themselves as necessary; others should check for them 256862306a36Sopenharmony_ci * now: 256962306a36Sopenharmony_ci */ 257062306a36Sopenharmony_ci return !(nextd->op_flags & OP_HANDLES_WRONGSEC); 257162306a36Sopenharmony_ci} 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci#ifdef CONFIG_NFSD_V4_2_INTER_SSC 257462306a36Sopenharmony_cistatic void 257562306a36Sopenharmony_cicheck_if_stalefh_allowed(struct nfsd4_compoundargs *args) 257662306a36Sopenharmony_ci{ 257762306a36Sopenharmony_ci struct nfsd4_op *op, *current_op = NULL, *saved_op = NULL; 257862306a36Sopenharmony_ci struct nfsd4_copy *copy; 257962306a36Sopenharmony_ci struct nfsd4_putfh *putfh; 258062306a36Sopenharmony_ci int i; 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci /* traverse all operation and if it's a COPY compound, mark the 258362306a36Sopenharmony_ci * source filehandle to skip verification 258462306a36Sopenharmony_ci */ 258562306a36Sopenharmony_ci for (i = 0; i < args->opcnt; i++) { 258662306a36Sopenharmony_ci op = &args->ops[i]; 258762306a36Sopenharmony_ci if (op->opnum == OP_PUTFH) 258862306a36Sopenharmony_ci current_op = op; 258962306a36Sopenharmony_ci else if (op->opnum == OP_SAVEFH) 259062306a36Sopenharmony_ci saved_op = current_op; 259162306a36Sopenharmony_ci else if (op->opnum == OP_RESTOREFH) 259262306a36Sopenharmony_ci current_op = saved_op; 259362306a36Sopenharmony_ci else if (op->opnum == OP_COPY) { 259462306a36Sopenharmony_ci copy = (struct nfsd4_copy *)&op->u; 259562306a36Sopenharmony_ci if (!saved_op) { 259662306a36Sopenharmony_ci op->status = nfserr_nofilehandle; 259762306a36Sopenharmony_ci return; 259862306a36Sopenharmony_ci } 259962306a36Sopenharmony_ci putfh = (struct nfsd4_putfh *)&saved_op->u; 260062306a36Sopenharmony_ci if (nfsd4_ssc_is_inter(copy)) 260162306a36Sopenharmony_ci putfh->no_verify = true; 260262306a36Sopenharmony_ci } 260362306a36Sopenharmony_ci } 260462306a36Sopenharmony_ci} 260562306a36Sopenharmony_ci#else 260662306a36Sopenharmony_cistatic void 260762306a36Sopenharmony_cicheck_if_stalefh_allowed(struct nfsd4_compoundargs *args) 260862306a36Sopenharmony_ci{ 260962306a36Sopenharmony_ci} 261062306a36Sopenharmony_ci#endif 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci/* 261362306a36Sopenharmony_ci * COMPOUND call. 261462306a36Sopenharmony_ci */ 261562306a36Sopenharmony_cistatic __be32 261662306a36Sopenharmony_cinfsd4_proc_compound(struct svc_rqst *rqstp) 261762306a36Sopenharmony_ci{ 261862306a36Sopenharmony_ci struct nfsd4_compoundargs *args = rqstp->rq_argp; 261962306a36Sopenharmony_ci struct nfsd4_compoundres *resp = rqstp->rq_resp; 262062306a36Sopenharmony_ci struct nfsd4_op *op; 262162306a36Sopenharmony_ci struct nfsd4_compound_state *cstate = &resp->cstate; 262262306a36Sopenharmony_ci struct svc_fh *current_fh = &cstate->current_fh; 262362306a36Sopenharmony_ci struct svc_fh *save_fh = &cstate->save_fh; 262462306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 262562306a36Sopenharmony_ci __be32 status; 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci resp->xdr = &rqstp->rq_res_stream; 262862306a36Sopenharmony_ci resp->statusp = resp->xdr->p; 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_ci /* reserve space for: NFS status code */ 263162306a36Sopenharmony_ci xdr_reserve_space(resp->xdr, XDR_UNIT); 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_ci /* reserve space for: taglen, tag, and opcnt */ 263462306a36Sopenharmony_ci xdr_reserve_space(resp->xdr, XDR_UNIT * 2 + args->taglen); 263562306a36Sopenharmony_ci resp->taglen = args->taglen; 263662306a36Sopenharmony_ci resp->tag = args->tag; 263762306a36Sopenharmony_ci resp->rqstp = rqstp; 263862306a36Sopenharmony_ci cstate->minorversion = args->minorversion; 263962306a36Sopenharmony_ci fh_init(current_fh, NFS4_FHSIZE); 264062306a36Sopenharmony_ci fh_init(save_fh, NFS4_FHSIZE); 264162306a36Sopenharmony_ci /* 264262306a36Sopenharmony_ci * Don't use the deferral mechanism for NFSv4; compounds make it 264362306a36Sopenharmony_ci * too hard to avoid non-idempotency problems. 264462306a36Sopenharmony_ci */ 264562306a36Sopenharmony_ci clear_bit(RQ_USEDEFERRAL, &rqstp->rq_flags); 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_ci /* 264862306a36Sopenharmony_ci * According to RFC3010, this takes precedence over all other errors. 264962306a36Sopenharmony_ci */ 265062306a36Sopenharmony_ci status = nfserr_minor_vers_mismatch; 265162306a36Sopenharmony_ci if (nfsd_minorversion(nn, args->minorversion, NFSD_TEST) <= 0) 265262306a36Sopenharmony_ci goto out; 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci status = nfs41_check_op_ordering(args); 265562306a36Sopenharmony_ci if (status) { 265662306a36Sopenharmony_ci op = &args->ops[0]; 265762306a36Sopenharmony_ci op->status = status; 265862306a36Sopenharmony_ci resp->opcnt = 1; 265962306a36Sopenharmony_ci goto encode_op; 266062306a36Sopenharmony_ci } 266162306a36Sopenharmony_ci check_if_stalefh_allowed(args); 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci rqstp->rq_lease_breaker = (void **)&cstate->clp; 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci trace_nfsd_compound(rqstp, args->tag, args->taglen, args->client_opcnt); 266662306a36Sopenharmony_ci while (!status && resp->opcnt < args->opcnt) { 266762306a36Sopenharmony_ci op = &args->ops[resp->opcnt++]; 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci if (unlikely(resp->opcnt == NFSD_MAX_OPS_PER_COMPOUND)) { 267062306a36Sopenharmony_ci /* If there are still more operations to process, 267162306a36Sopenharmony_ci * stop here and report NFS4ERR_RESOURCE. */ 267262306a36Sopenharmony_ci if (cstate->minorversion == 0 && 267362306a36Sopenharmony_ci args->client_opcnt > resp->opcnt) { 267462306a36Sopenharmony_ci op->status = nfserr_resource; 267562306a36Sopenharmony_ci goto encode_op; 267662306a36Sopenharmony_ci } 267762306a36Sopenharmony_ci } 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci /* 268062306a36Sopenharmony_ci * The XDR decode routines may have pre-set op->status; 268162306a36Sopenharmony_ci * for example, if there is a miscellaneous XDR error 268262306a36Sopenharmony_ci * it will be set to nfserr_bad_xdr. 268362306a36Sopenharmony_ci */ 268462306a36Sopenharmony_ci if (op->status) { 268562306a36Sopenharmony_ci if (op->opnum == OP_OPEN) 268662306a36Sopenharmony_ci op->status = nfsd4_open_omfg(rqstp, cstate, op); 268762306a36Sopenharmony_ci goto encode_op; 268862306a36Sopenharmony_ci } 268962306a36Sopenharmony_ci if (!current_fh->fh_dentry && 269062306a36Sopenharmony_ci !HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) { 269162306a36Sopenharmony_ci if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) { 269262306a36Sopenharmony_ci op->status = nfserr_nofilehandle; 269362306a36Sopenharmony_ci goto encode_op; 269462306a36Sopenharmony_ci } 269562306a36Sopenharmony_ci } else if (current_fh->fh_export && 269662306a36Sopenharmony_ci current_fh->fh_export->ex_fslocs.migrated && 269762306a36Sopenharmony_ci !(op->opdesc->op_flags & ALLOWED_ON_ABSENT_FS)) { 269862306a36Sopenharmony_ci op->status = nfserr_moved; 269962306a36Sopenharmony_ci goto encode_op; 270062306a36Sopenharmony_ci } 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci fh_clear_pre_post_attrs(current_fh); 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci /* If op is non-idempotent */ 270562306a36Sopenharmony_ci if (op->opdesc->op_flags & OP_MODIFIES_SOMETHING) { 270662306a36Sopenharmony_ci /* 270762306a36Sopenharmony_ci * Don't execute this op if we couldn't encode a 270862306a36Sopenharmony_ci * successful reply: 270962306a36Sopenharmony_ci */ 271062306a36Sopenharmony_ci u32 plen = op->opdesc->op_rsize_bop(rqstp, op); 271162306a36Sopenharmony_ci /* 271262306a36Sopenharmony_ci * Plus if there's another operation, make sure 271362306a36Sopenharmony_ci * we'll have space to at least encode an error: 271462306a36Sopenharmony_ci */ 271562306a36Sopenharmony_ci if (resp->opcnt < args->opcnt) 271662306a36Sopenharmony_ci plen += COMPOUND_ERR_SLACK_SPACE; 271762306a36Sopenharmony_ci op->status = nfsd4_check_resp_size(resp, plen); 271862306a36Sopenharmony_ci } 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci if (op->status) 272162306a36Sopenharmony_ci goto encode_op; 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci if (op->opdesc->op_get_currentstateid) 272462306a36Sopenharmony_ci op->opdesc->op_get_currentstateid(cstate, &op->u); 272562306a36Sopenharmony_ci op->status = op->opdesc->op_func(rqstp, cstate, &op->u); 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci /* Only from SEQUENCE */ 272862306a36Sopenharmony_ci if (cstate->status == nfserr_replay_cache) { 272962306a36Sopenharmony_ci dprintk("%s NFS4.1 replay from cache\n", __func__); 273062306a36Sopenharmony_ci status = op->status; 273162306a36Sopenharmony_ci goto out; 273262306a36Sopenharmony_ci } 273362306a36Sopenharmony_ci if (!op->status) { 273462306a36Sopenharmony_ci if (op->opdesc->op_set_currentstateid) 273562306a36Sopenharmony_ci op->opdesc->op_set_currentstateid(cstate, &op->u); 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci if (op->opdesc->op_flags & OP_CLEAR_STATEID) 273862306a36Sopenharmony_ci clear_current_stateid(cstate); 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci if (current_fh->fh_export && 274162306a36Sopenharmony_ci need_wrongsec_check(rqstp)) 274262306a36Sopenharmony_ci op->status = check_nfsd_access(current_fh->fh_export, rqstp); 274362306a36Sopenharmony_ci } 274462306a36Sopenharmony_ciencode_op: 274562306a36Sopenharmony_ci if (op->status == nfserr_replay_me) { 274662306a36Sopenharmony_ci op->replay = &cstate->replay_owner->so_replay; 274762306a36Sopenharmony_ci nfsd4_encode_replay(resp->xdr, op); 274862306a36Sopenharmony_ci status = op->status = op->replay->rp_status; 274962306a36Sopenharmony_ci } else { 275062306a36Sopenharmony_ci nfsd4_encode_operation(resp, op); 275162306a36Sopenharmony_ci status = op->status; 275262306a36Sopenharmony_ci } 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci trace_nfsd_compound_status(args->client_opcnt, resp->opcnt, 275562306a36Sopenharmony_ci status, nfsd4_op_name(op->opnum)); 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci nfsd4_cstate_clear_replay(cstate); 275862306a36Sopenharmony_ci nfsd4_increment_op_stats(op->opnum); 275962306a36Sopenharmony_ci } 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_ci fh_put(current_fh); 276262306a36Sopenharmony_ci fh_put(save_fh); 276362306a36Sopenharmony_ci BUG_ON(cstate->replay_owner); 276462306a36Sopenharmony_ciout: 276562306a36Sopenharmony_ci cstate->status = status; 276662306a36Sopenharmony_ci /* Reset deferral mechanism for RPC deferrals */ 276762306a36Sopenharmony_ci set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags); 276862306a36Sopenharmony_ci return rpc_success; 276962306a36Sopenharmony_ci} 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci#define op_encode_hdr_size (2) 277262306a36Sopenharmony_ci#define op_encode_stateid_maxsz (XDR_QUADLEN(NFS4_STATEID_SIZE)) 277362306a36Sopenharmony_ci#define op_encode_verifier_maxsz (XDR_QUADLEN(NFS4_VERIFIER_SIZE)) 277462306a36Sopenharmony_ci#define op_encode_change_info_maxsz (5) 277562306a36Sopenharmony_ci#define nfs4_fattr_bitmap_maxsz (4) 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci/* We'll fall back on returning no lockowner if run out of space: */ 277862306a36Sopenharmony_ci#define op_encode_lockowner_maxsz (0) 277962306a36Sopenharmony_ci#define op_encode_lock_denied_maxsz (8 + op_encode_lockowner_maxsz) 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci#define op_encode_ace_maxsz (3 + nfs4_owner_maxsz) 278462306a36Sopenharmony_ci#define op_encode_delegation_maxsz (1 + op_encode_stateid_maxsz + 1 + \ 278562306a36Sopenharmony_ci op_encode_ace_maxsz) 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci#define op_encode_channel_attrs_maxsz (6 + 1 + 1) 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci/* 279062306a36Sopenharmony_ci * The _rsize() helpers are invoked by the NFSv4 COMPOUND decoder, which 279162306a36Sopenharmony_ci * is called before sunrpc sets rq_res.buflen. Thus we have to compute 279262306a36Sopenharmony_ci * the maximum payload size here, based on transport limits and the size 279362306a36Sopenharmony_ci * of the remaining space in the rq_pages array. 279462306a36Sopenharmony_ci */ 279562306a36Sopenharmony_cistatic u32 nfsd4_max_payload(const struct svc_rqst *rqstp) 279662306a36Sopenharmony_ci{ 279762306a36Sopenharmony_ci u32 buflen; 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci buflen = (rqstp->rq_page_end - rqstp->rq_next_page) * PAGE_SIZE; 280062306a36Sopenharmony_ci buflen -= rqstp->rq_auth_slack; 280162306a36Sopenharmony_ci buflen -= rqstp->rq_res.head[0].iov_len; 280262306a36Sopenharmony_ci return min_t(u32, buflen, svc_max_payload(rqstp)); 280362306a36Sopenharmony_ci} 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_cistatic u32 nfsd4_only_status_rsize(const struct svc_rqst *rqstp, 280662306a36Sopenharmony_ci const struct nfsd4_op *op) 280762306a36Sopenharmony_ci{ 280862306a36Sopenharmony_ci return (op_encode_hdr_size) * sizeof(__be32); 280962306a36Sopenharmony_ci} 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_cistatic u32 nfsd4_status_stateid_rsize(const struct svc_rqst *rqstp, 281262306a36Sopenharmony_ci const struct nfsd4_op *op) 281362306a36Sopenharmony_ci{ 281462306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_stateid_maxsz)* sizeof(__be32); 281562306a36Sopenharmony_ci} 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_cistatic u32 nfsd4_access_rsize(const struct svc_rqst *rqstp, 281862306a36Sopenharmony_ci const struct nfsd4_op *op) 281962306a36Sopenharmony_ci{ 282062306a36Sopenharmony_ci /* ac_supported, ac_resp_access */ 282162306a36Sopenharmony_ci return (op_encode_hdr_size + 2)* sizeof(__be32); 282262306a36Sopenharmony_ci} 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_cistatic u32 nfsd4_commit_rsize(const struct svc_rqst *rqstp, 282562306a36Sopenharmony_ci const struct nfsd4_op *op) 282662306a36Sopenharmony_ci{ 282762306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_verifier_maxsz) * sizeof(__be32); 282862306a36Sopenharmony_ci} 282962306a36Sopenharmony_ci 283062306a36Sopenharmony_cistatic u32 nfsd4_create_rsize(const struct svc_rqst *rqstp, 283162306a36Sopenharmony_ci const struct nfsd4_op *op) 283262306a36Sopenharmony_ci{ 283362306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_change_info_maxsz 283462306a36Sopenharmony_ci + nfs4_fattr_bitmap_maxsz) * sizeof(__be32); 283562306a36Sopenharmony_ci} 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci/* 283862306a36Sopenharmony_ci * Note since this is an idempotent operation we won't insist on failing 283962306a36Sopenharmony_ci * the op prematurely if the estimate is too large. We may turn off splice 284062306a36Sopenharmony_ci * reads unnecessarily. 284162306a36Sopenharmony_ci */ 284262306a36Sopenharmony_cistatic u32 nfsd4_getattr_rsize(const struct svc_rqst *rqstp, 284362306a36Sopenharmony_ci const struct nfsd4_op *op) 284462306a36Sopenharmony_ci{ 284562306a36Sopenharmony_ci const u32 *bmap = op->u.getattr.ga_bmval; 284662306a36Sopenharmony_ci u32 bmap0 = bmap[0], bmap1 = bmap[1], bmap2 = bmap[2]; 284762306a36Sopenharmony_ci u32 ret = 0; 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci if (bmap0 & FATTR4_WORD0_ACL) 285062306a36Sopenharmony_ci return nfsd4_max_payload(rqstp); 285162306a36Sopenharmony_ci if (bmap0 & FATTR4_WORD0_FS_LOCATIONS) 285262306a36Sopenharmony_ci return nfsd4_max_payload(rqstp); 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci if (bmap1 & FATTR4_WORD1_OWNER) { 285562306a36Sopenharmony_ci ret += IDMAP_NAMESZ + 4; 285662306a36Sopenharmony_ci bmap1 &= ~FATTR4_WORD1_OWNER; 285762306a36Sopenharmony_ci } 285862306a36Sopenharmony_ci if (bmap1 & FATTR4_WORD1_OWNER_GROUP) { 285962306a36Sopenharmony_ci ret += IDMAP_NAMESZ + 4; 286062306a36Sopenharmony_ci bmap1 &= ~FATTR4_WORD1_OWNER_GROUP; 286162306a36Sopenharmony_ci } 286262306a36Sopenharmony_ci if (bmap0 & FATTR4_WORD0_FILEHANDLE) { 286362306a36Sopenharmony_ci ret += NFS4_FHSIZE + 4; 286462306a36Sopenharmony_ci bmap0 &= ~FATTR4_WORD0_FILEHANDLE; 286562306a36Sopenharmony_ci } 286662306a36Sopenharmony_ci if (bmap2 & FATTR4_WORD2_SECURITY_LABEL) { 286762306a36Sopenharmony_ci ret += NFS4_MAXLABELLEN + 12; 286862306a36Sopenharmony_ci bmap2 &= ~FATTR4_WORD2_SECURITY_LABEL; 286962306a36Sopenharmony_ci } 287062306a36Sopenharmony_ci /* 287162306a36Sopenharmony_ci * Largest of remaining attributes are 16 bytes (e.g., 287262306a36Sopenharmony_ci * supported_attributes) 287362306a36Sopenharmony_ci */ 287462306a36Sopenharmony_ci ret += 16 * (hweight32(bmap0) + hweight32(bmap1) + hweight32(bmap2)); 287562306a36Sopenharmony_ci /* bitmask, length */ 287662306a36Sopenharmony_ci ret += 20; 287762306a36Sopenharmony_ci return ret; 287862306a36Sopenharmony_ci} 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_cistatic u32 nfsd4_getfh_rsize(const struct svc_rqst *rqstp, 288162306a36Sopenharmony_ci const struct nfsd4_op *op) 288262306a36Sopenharmony_ci{ 288362306a36Sopenharmony_ci return (op_encode_hdr_size + 1) * sizeof(__be32) + NFS4_FHSIZE; 288462306a36Sopenharmony_ci} 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_cistatic u32 nfsd4_link_rsize(const struct svc_rqst *rqstp, 288762306a36Sopenharmony_ci const struct nfsd4_op *op) 288862306a36Sopenharmony_ci{ 288962306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_change_info_maxsz) 289062306a36Sopenharmony_ci * sizeof(__be32); 289162306a36Sopenharmony_ci} 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_cistatic u32 nfsd4_lock_rsize(const struct svc_rqst *rqstp, 289462306a36Sopenharmony_ci const struct nfsd4_op *op) 289562306a36Sopenharmony_ci{ 289662306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_lock_denied_maxsz) 289762306a36Sopenharmony_ci * sizeof(__be32); 289862306a36Sopenharmony_ci} 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_cistatic u32 nfsd4_open_rsize(const struct svc_rqst *rqstp, 290162306a36Sopenharmony_ci const struct nfsd4_op *op) 290262306a36Sopenharmony_ci{ 290362306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_stateid_maxsz 290462306a36Sopenharmony_ci + op_encode_change_info_maxsz + 1 290562306a36Sopenharmony_ci + nfs4_fattr_bitmap_maxsz 290662306a36Sopenharmony_ci + op_encode_delegation_maxsz) * sizeof(__be32); 290762306a36Sopenharmony_ci} 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_cistatic u32 nfsd4_read_rsize(const struct svc_rqst *rqstp, 291062306a36Sopenharmony_ci const struct nfsd4_op *op) 291162306a36Sopenharmony_ci{ 291262306a36Sopenharmony_ci u32 rlen = min(op->u.read.rd_length, nfsd4_max_payload(rqstp)); 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci return (op_encode_hdr_size + 2 + XDR_QUADLEN(rlen)) * sizeof(__be32); 291562306a36Sopenharmony_ci} 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_cistatic u32 nfsd4_read_plus_rsize(const struct svc_rqst *rqstp, 291862306a36Sopenharmony_ci const struct nfsd4_op *op) 291962306a36Sopenharmony_ci{ 292062306a36Sopenharmony_ci u32 rlen = min(op->u.read.rd_length, nfsd4_max_payload(rqstp)); 292162306a36Sopenharmony_ci /* 292262306a36Sopenharmony_ci * If we detect that the file changed during hole encoding, then we 292362306a36Sopenharmony_ci * recover by encoding the remaining reply as data. This means we need 292462306a36Sopenharmony_ci * to set aside enough room to encode two data segments. 292562306a36Sopenharmony_ci */ 292662306a36Sopenharmony_ci u32 seg_len = 2 * (1 + 2 + 1); 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci return (op_encode_hdr_size + 2 + seg_len + XDR_QUADLEN(rlen)) * sizeof(__be32); 292962306a36Sopenharmony_ci} 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_cistatic u32 nfsd4_readdir_rsize(const struct svc_rqst *rqstp, 293262306a36Sopenharmony_ci const struct nfsd4_op *op) 293362306a36Sopenharmony_ci{ 293462306a36Sopenharmony_ci u32 rlen = min(op->u.readdir.rd_maxcount, nfsd4_max_payload(rqstp)); 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_verifier_maxsz + 293762306a36Sopenharmony_ci XDR_QUADLEN(rlen)) * sizeof(__be32); 293862306a36Sopenharmony_ci} 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_cistatic u32 nfsd4_readlink_rsize(const struct svc_rqst *rqstp, 294162306a36Sopenharmony_ci const struct nfsd4_op *op) 294262306a36Sopenharmony_ci{ 294362306a36Sopenharmony_ci return (op_encode_hdr_size + 1) * sizeof(__be32) + PAGE_SIZE; 294462306a36Sopenharmony_ci} 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_cistatic u32 nfsd4_remove_rsize(const struct svc_rqst *rqstp, 294762306a36Sopenharmony_ci const struct nfsd4_op *op) 294862306a36Sopenharmony_ci{ 294962306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_change_info_maxsz) 295062306a36Sopenharmony_ci * sizeof(__be32); 295162306a36Sopenharmony_ci} 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_cistatic u32 nfsd4_rename_rsize(const struct svc_rqst *rqstp, 295462306a36Sopenharmony_ci const struct nfsd4_op *op) 295562306a36Sopenharmony_ci{ 295662306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_change_info_maxsz 295762306a36Sopenharmony_ci + op_encode_change_info_maxsz) * sizeof(__be32); 295862306a36Sopenharmony_ci} 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_cistatic u32 nfsd4_sequence_rsize(const struct svc_rqst *rqstp, 296162306a36Sopenharmony_ci const struct nfsd4_op *op) 296262306a36Sopenharmony_ci{ 296362306a36Sopenharmony_ci return (op_encode_hdr_size 296462306a36Sopenharmony_ci + XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5) * sizeof(__be32); 296562306a36Sopenharmony_ci} 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_cistatic u32 nfsd4_test_stateid_rsize(const struct svc_rqst *rqstp, 296862306a36Sopenharmony_ci const struct nfsd4_op *op) 296962306a36Sopenharmony_ci{ 297062306a36Sopenharmony_ci return (op_encode_hdr_size + 1 + op->u.test_stateid.ts_num_ids) 297162306a36Sopenharmony_ci * sizeof(__be32); 297262306a36Sopenharmony_ci} 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_cistatic u32 nfsd4_setattr_rsize(const struct svc_rqst *rqstp, 297562306a36Sopenharmony_ci const struct nfsd4_op *op) 297662306a36Sopenharmony_ci{ 297762306a36Sopenharmony_ci return (op_encode_hdr_size + nfs4_fattr_bitmap_maxsz) * sizeof(__be32); 297862306a36Sopenharmony_ci} 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_cistatic u32 nfsd4_secinfo_rsize(const struct svc_rqst *rqstp, 298162306a36Sopenharmony_ci const struct nfsd4_op *op) 298262306a36Sopenharmony_ci{ 298362306a36Sopenharmony_ci return (op_encode_hdr_size + RPC_AUTH_MAXFLAVOR * 298462306a36Sopenharmony_ci (4 + XDR_QUADLEN(GSS_OID_MAX_LEN))) * sizeof(__be32); 298562306a36Sopenharmony_ci} 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_cistatic u32 nfsd4_setclientid_rsize(const struct svc_rqst *rqstp, 298862306a36Sopenharmony_ci const struct nfsd4_op *op) 298962306a36Sopenharmony_ci{ 299062306a36Sopenharmony_ci return (op_encode_hdr_size + 2 + XDR_QUADLEN(NFS4_VERIFIER_SIZE)) * 299162306a36Sopenharmony_ci sizeof(__be32); 299262306a36Sopenharmony_ci} 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_cistatic u32 nfsd4_write_rsize(const struct svc_rqst *rqstp, 299562306a36Sopenharmony_ci const struct nfsd4_op *op) 299662306a36Sopenharmony_ci{ 299762306a36Sopenharmony_ci return (op_encode_hdr_size + 2 + op_encode_verifier_maxsz) * sizeof(__be32); 299862306a36Sopenharmony_ci} 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_cistatic u32 nfsd4_exchange_id_rsize(const struct svc_rqst *rqstp, 300162306a36Sopenharmony_ci const struct nfsd4_op *op) 300262306a36Sopenharmony_ci{ 300362306a36Sopenharmony_ci return (op_encode_hdr_size + 2 + 1 + /* eir_clientid, eir_sequenceid */\ 300462306a36Sopenharmony_ci 1 + 1 + /* eir_flags, spr_how */\ 300562306a36Sopenharmony_ci 4 + /* spo_must_enforce & _allow with bitmap */\ 300662306a36Sopenharmony_ci 2 + /*eir_server_owner.so_minor_id */\ 300762306a36Sopenharmony_ci /* eir_server_owner.so_major_id<> */\ 300862306a36Sopenharmony_ci XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 +\ 300962306a36Sopenharmony_ci /* eir_server_scope<> */\ 301062306a36Sopenharmony_ci XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 +\ 301162306a36Sopenharmony_ci 1 + /* eir_server_impl_id array length */\ 301262306a36Sopenharmony_ci 0 /* ignored eir_server_impl_id contents */) * sizeof(__be32); 301362306a36Sopenharmony_ci} 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_cistatic u32 nfsd4_bind_conn_to_session_rsize(const struct svc_rqst *rqstp, 301662306a36Sopenharmony_ci const struct nfsd4_op *op) 301762306a36Sopenharmony_ci{ 301862306a36Sopenharmony_ci return (op_encode_hdr_size + \ 301962306a36Sopenharmony_ci XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + /* bctsr_sessid */\ 302062306a36Sopenharmony_ci 2 /* bctsr_dir, use_conn_in_rdma_mode */) * sizeof(__be32); 302162306a36Sopenharmony_ci} 302262306a36Sopenharmony_ci 302362306a36Sopenharmony_cistatic u32 nfsd4_create_session_rsize(const struct svc_rqst *rqstp, 302462306a36Sopenharmony_ci const struct nfsd4_op *op) 302562306a36Sopenharmony_ci{ 302662306a36Sopenharmony_ci return (op_encode_hdr_size + \ 302762306a36Sopenharmony_ci XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + /* sessionid */\ 302862306a36Sopenharmony_ci 2 + /* csr_sequence, csr_flags */\ 302962306a36Sopenharmony_ci op_encode_channel_attrs_maxsz + \ 303062306a36Sopenharmony_ci op_encode_channel_attrs_maxsz) * sizeof(__be32); 303162306a36Sopenharmony_ci} 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_cistatic u32 nfsd4_copy_rsize(const struct svc_rqst *rqstp, 303462306a36Sopenharmony_ci const struct nfsd4_op *op) 303562306a36Sopenharmony_ci{ 303662306a36Sopenharmony_ci return (op_encode_hdr_size + 303762306a36Sopenharmony_ci 1 /* wr_callback */ + 303862306a36Sopenharmony_ci op_encode_stateid_maxsz /* wr_callback */ + 303962306a36Sopenharmony_ci 2 /* wr_count */ + 304062306a36Sopenharmony_ci 1 /* wr_committed */ + 304162306a36Sopenharmony_ci op_encode_verifier_maxsz + 304262306a36Sopenharmony_ci 1 /* cr_consecutive */ + 304362306a36Sopenharmony_ci 1 /* cr_synchronous */) * sizeof(__be32); 304462306a36Sopenharmony_ci} 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_cistatic u32 nfsd4_offload_status_rsize(const struct svc_rqst *rqstp, 304762306a36Sopenharmony_ci const struct nfsd4_op *op) 304862306a36Sopenharmony_ci{ 304962306a36Sopenharmony_ci return (op_encode_hdr_size + 305062306a36Sopenharmony_ci 2 /* osr_count */ + 305162306a36Sopenharmony_ci 1 /* osr_complete<1> optional 0 for now */) * sizeof(__be32); 305262306a36Sopenharmony_ci} 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_cistatic u32 nfsd4_copy_notify_rsize(const struct svc_rqst *rqstp, 305562306a36Sopenharmony_ci const struct nfsd4_op *op) 305662306a36Sopenharmony_ci{ 305762306a36Sopenharmony_ci return (op_encode_hdr_size + 305862306a36Sopenharmony_ci 3 /* cnr_lease_time */ + 305962306a36Sopenharmony_ci 1 /* We support one cnr_source_server */ + 306062306a36Sopenharmony_ci 1 /* cnr_stateid seq */ + 306162306a36Sopenharmony_ci op_encode_stateid_maxsz /* cnr_stateid */ + 306262306a36Sopenharmony_ci 1 /* num cnr_source_server*/ + 306362306a36Sopenharmony_ci 1 /* nl4_type */ + 306462306a36Sopenharmony_ci 1 /* nl4 size */ + 306562306a36Sopenharmony_ci XDR_QUADLEN(NFS4_OPAQUE_LIMIT) /*nl4_loc + nl4_loc_sz */) 306662306a36Sopenharmony_ci * sizeof(__be32); 306762306a36Sopenharmony_ci} 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS 307062306a36Sopenharmony_cistatic u32 nfsd4_getdeviceinfo_rsize(const struct svc_rqst *rqstp, 307162306a36Sopenharmony_ci const struct nfsd4_op *op) 307262306a36Sopenharmony_ci{ 307362306a36Sopenharmony_ci u32 rlen = min(op->u.getdeviceinfo.gd_maxcount, nfsd4_max_payload(rqstp)); 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci return (op_encode_hdr_size + 307662306a36Sopenharmony_ci 1 /* gd_layout_type*/ + 307762306a36Sopenharmony_ci XDR_QUADLEN(rlen) + 307862306a36Sopenharmony_ci 2 /* gd_notify_types */) * sizeof(__be32); 307962306a36Sopenharmony_ci} 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci/* 308262306a36Sopenharmony_ci * At this stage we don't really know what layout driver will handle the request, 308362306a36Sopenharmony_ci * so we need to define an arbitrary upper bound here. 308462306a36Sopenharmony_ci */ 308562306a36Sopenharmony_ci#define MAX_LAYOUT_SIZE 128 308662306a36Sopenharmony_cistatic u32 nfsd4_layoutget_rsize(const struct svc_rqst *rqstp, 308762306a36Sopenharmony_ci const struct nfsd4_op *op) 308862306a36Sopenharmony_ci{ 308962306a36Sopenharmony_ci return (op_encode_hdr_size + 309062306a36Sopenharmony_ci 1 /* logr_return_on_close */ + 309162306a36Sopenharmony_ci op_encode_stateid_maxsz + 309262306a36Sopenharmony_ci 1 /* nr of layouts */ + 309362306a36Sopenharmony_ci MAX_LAYOUT_SIZE) * sizeof(__be32); 309462306a36Sopenharmony_ci} 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_cistatic u32 nfsd4_layoutcommit_rsize(const struct svc_rqst *rqstp, 309762306a36Sopenharmony_ci const struct nfsd4_op *op) 309862306a36Sopenharmony_ci{ 309962306a36Sopenharmony_ci return (op_encode_hdr_size + 310062306a36Sopenharmony_ci 1 /* locr_newsize */ + 310162306a36Sopenharmony_ci 2 /* ns_size */) * sizeof(__be32); 310262306a36Sopenharmony_ci} 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_cistatic u32 nfsd4_layoutreturn_rsize(const struct svc_rqst *rqstp, 310562306a36Sopenharmony_ci const struct nfsd4_op *op) 310662306a36Sopenharmony_ci{ 310762306a36Sopenharmony_ci return (op_encode_hdr_size + 310862306a36Sopenharmony_ci 1 /* lrs_stateid */ + 310962306a36Sopenharmony_ci op_encode_stateid_maxsz) * sizeof(__be32); 311062306a36Sopenharmony_ci} 311162306a36Sopenharmony_ci#endif /* CONFIG_NFSD_PNFS */ 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_cistatic u32 nfsd4_seek_rsize(const struct svc_rqst *rqstp, 311562306a36Sopenharmony_ci const struct nfsd4_op *op) 311662306a36Sopenharmony_ci{ 311762306a36Sopenharmony_ci return (op_encode_hdr_size + 3) * sizeof(__be32); 311862306a36Sopenharmony_ci} 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_cistatic u32 nfsd4_getxattr_rsize(const struct svc_rqst *rqstp, 312162306a36Sopenharmony_ci const struct nfsd4_op *op) 312262306a36Sopenharmony_ci{ 312362306a36Sopenharmony_ci u32 rlen = min_t(u32, XATTR_SIZE_MAX, nfsd4_max_payload(rqstp)); 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci return (op_encode_hdr_size + 1 + XDR_QUADLEN(rlen)) * sizeof(__be32); 312662306a36Sopenharmony_ci} 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_cistatic u32 nfsd4_setxattr_rsize(const struct svc_rqst *rqstp, 312962306a36Sopenharmony_ci const struct nfsd4_op *op) 313062306a36Sopenharmony_ci{ 313162306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_change_info_maxsz) 313262306a36Sopenharmony_ci * sizeof(__be32); 313362306a36Sopenharmony_ci} 313462306a36Sopenharmony_cistatic u32 nfsd4_listxattrs_rsize(const struct svc_rqst *rqstp, 313562306a36Sopenharmony_ci const struct nfsd4_op *op) 313662306a36Sopenharmony_ci{ 313762306a36Sopenharmony_ci u32 rlen = min(op->u.listxattrs.lsxa_maxcount, nfsd4_max_payload(rqstp)); 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci return (op_encode_hdr_size + 4 + XDR_QUADLEN(rlen)) * sizeof(__be32); 314062306a36Sopenharmony_ci} 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_cistatic u32 nfsd4_removexattr_rsize(const struct svc_rqst *rqstp, 314362306a36Sopenharmony_ci const struct nfsd4_op *op) 314462306a36Sopenharmony_ci{ 314562306a36Sopenharmony_ci return (op_encode_hdr_size + op_encode_change_info_maxsz) 314662306a36Sopenharmony_ci * sizeof(__be32); 314762306a36Sopenharmony_ci} 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_cistatic const struct nfsd4_operation nfsd4_ops[] = { 315162306a36Sopenharmony_ci [OP_ACCESS] = { 315262306a36Sopenharmony_ci .op_func = nfsd4_access, 315362306a36Sopenharmony_ci .op_name = "OP_ACCESS", 315462306a36Sopenharmony_ci .op_rsize_bop = nfsd4_access_rsize, 315562306a36Sopenharmony_ci }, 315662306a36Sopenharmony_ci [OP_CLOSE] = { 315762306a36Sopenharmony_ci .op_func = nfsd4_close, 315862306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 315962306a36Sopenharmony_ci .op_name = "OP_CLOSE", 316062306a36Sopenharmony_ci .op_rsize_bop = nfsd4_status_stateid_rsize, 316162306a36Sopenharmony_ci .op_get_currentstateid = nfsd4_get_closestateid, 316262306a36Sopenharmony_ci .op_set_currentstateid = nfsd4_set_closestateid, 316362306a36Sopenharmony_ci }, 316462306a36Sopenharmony_ci [OP_COMMIT] = { 316562306a36Sopenharmony_ci .op_func = nfsd4_commit, 316662306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 316762306a36Sopenharmony_ci .op_name = "OP_COMMIT", 316862306a36Sopenharmony_ci .op_rsize_bop = nfsd4_commit_rsize, 316962306a36Sopenharmony_ci }, 317062306a36Sopenharmony_ci [OP_CREATE] = { 317162306a36Sopenharmony_ci .op_func = nfsd4_create, 317262306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME | OP_CLEAR_STATEID, 317362306a36Sopenharmony_ci .op_name = "OP_CREATE", 317462306a36Sopenharmony_ci .op_rsize_bop = nfsd4_create_rsize, 317562306a36Sopenharmony_ci }, 317662306a36Sopenharmony_ci [OP_DELEGRETURN] = { 317762306a36Sopenharmony_ci .op_func = nfsd4_delegreturn, 317862306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 317962306a36Sopenharmony_ci .op_name = "OP_DELEGRETURN", 318062306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 318162306a36Sopenharmony_ci .op_get_currentstateid = nfsd4_get_delegreturnstateid, 318262306a36Sopenharmony_ci }, 318362306a36Sopenharmony_ci [OP_GETATTR] = { 318462306a36Sopenharmony_ci .op_func = nfsd4_getattr, 318562306a36Sopenharmony_ci .op_flags = ALLOWED_ON_ABSENT_FS, 318662306a36Sopenharmony_ci .op_rsize_bop = nfsd4_getattr_rsize, 318762306a36Sopenharmony_ci .op_name = "OP_GETATTR", 318862306a36Sopenharmony_ci }, 318962306a36Sopenharmony_ci [OP_GETFH] = { 319062306a36Sopenharmony_ci .op_func = nfsd4_getfh, 319162306a36Sopenharmony_ci .op_name = "OP_GETFH", 319262306a36Sopenharmony_ci .op_rsize_bop = nfsd4_getfh_rsize, 319362306a36Sopenharmony_ci }, 319462306a36Sopenharmony_ci [OP_LINK] = { 319562306a36Sopenharmony_ci .op_func = nfsd4_link, 319662306a36Sopenharmony_ci .op_flags = ALLOWED_ON_ABSENT_FS | OP_MODIFIES_SOMETHING 319762306a36Sopenharmony_ci | OP_CACHEME, 319862306a36Sopenharmony_ci .op_name = "OP_LINK", 319962306a36Sopenharmony_ci .op_rsize_bop = nfsd4_link_rsize, 320062306a36Sopenharmony_ci }, 320162306a36Sopenharmony_ci [OP_LOCK] = { 320262306a36Sopenharmony_ci .op_func = nfsd4_lock, 320362306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING | 320462306a36Sopenharmony_ci OP_NONTRIVIAL_ERROR_ENCODE, 320562306a36Sopenharmony_ci .op_name = "OP_LOCK", 320662306a36Sopenharmony_ci .op_rsize_bop = nfsd4_lock_rsize, 320762306a36Sopenharmony_ci .op_set_currentstateid = nfsd4_set_lockstateid, 320862306a36Sopenharmony_ci }, 320962306a36Sopenharmony_ci [OP_LOCKT] = { 321062306a36Sopenharmony_ci .op_func = nfsd4_lockt, 321162306a36Sopenharmony_ci .op_flags = OP_NONTRIVIAL_ERROR_ENCODE, 321262306a36Sopenharmony_ci .op_name = "OP_LOCKT", 321362306a36Sopenharmony_ci .op_rsize_bop = nfsd4_lock_rsize, 321462306a36Sopenharmony_ci }, 321562306a36Sopenharmony_ci [OP_LOCKU] = { 321662306a36Sopenharmony_ci .op_func = nfsd4_locku, 321762306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 321862306a36Sopenharmony_ci .op_name = "OP_LOCKU", 321962306a36Sopenharmony_ci .op_rsize_bop = nfsd4_status_stateid_rsize, 322062306a36Sopenharmony_ci .op_get_currentstateid = nfsd4_get_lockustateid, 322162306a36Sopenharmony_ci }, 322262306a36Sopenharmony_ci [OP_LOOKUP] = { 322362306a36Sopenharmony_ci .op_func = nfsd4_lookup, 322462306a36Sopenharmony_ci .op_flags = OP_HANDLES_WRONGSEC | OP_CLEAR_STATEID, 322562306a36Sopenharmony_ci .op_name = "OP_LOOKUP", 322662306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 322762306a36Sopenharmony_ci }, 322862306a36Sopenharmony_ci [OP_LOOKUPP] = { 322962306a36Sopenharmony_ci .op_func = nfsd4_lookupp, 323062306a36Sopenharmony_ci .op_flags = OP_HANDLES_WRONGSEC | OP_CLEAR_STATEID, 323162306a36Sopenharmony_ci .op_name = "OP_LOOKUPP", 323262306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 323362306a36Sopenharmony_ci }, 323462306a36Sopenharmony_ci [OP_NVERIFY] = { 323562306a36Sopenharmony_ci .op_func = nfsd4_nverify, 323662306a36Sopenharmony_ci .op_name = "OP_NVERIFY", 323762306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 323862306a36Sopenharmony_ci }, 323962306a36Sopenharmony_ci [OP_OPEN] = { 324062306a36Sopenharmony_ci .op_func = nfsd4_open, 324162306a36Sopenharmony_ci .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING, 324262306a36Sopenharmony_ci .op_name = "OP_OPEN", 324362306a36Sopenharmony_ci .op_rsize_bop = nfsd4_open_rsize, 324462306a36Sopenharmony_ci .op_set_currentstateid = nfsd4_set_openstateid, 324562306a36Sopenharmony_ci }, 324662306a36Sopenharmony_ci [OP_OPEN_CONFIRM] = { 324762306a36Sopenharmony_ci .op_func = nfsd4_open_confirm, 324862306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 324962306a36Sopenharmony_ci .op_name = "OP_OPEN_CONFIRM", 325062306a36Sopenharmony_ci .op_rsize_bop = nfsd4_status_stateid_rsize, 325162306a36Sopenharmony_ci }, 325262306a36Sopenharmony_ci [OP_OPEN_DOWNGRADE] = { 325362306a36Sopenharmony_ci .op_func = nfsd4_open_downgrade, 325462306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 325562306a36Sopenharmony_ci .op_name = "OP_OPEN_DOWNGRADE", 325662306a36Sopenharmony_ci .op_rsize_bop = nfsd4_status_stateid_rsize, 325762306a36Sopenharmony_ci .op_get_currentstateid = nfsd4_get_opendowngradestateid, 325862306a36Sopenharmony_ci .op_set_currentstateid = nfsd4_set_opendowngradestateid, 325962306a36Sopenharmony_ci }, 326062306a36Sopenharmony_ci [OP_PUTFH] = { 326162306a36Sopenharmony_ci .op_func = nfsd4_putfh, 326262306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 326362306a36Sopenharmony_ci | OP_IS_PUTFH_LIKE | OP_CLEAR_STATEID, 326462306a36Sopenharmony_ci .op_name = "OP_PUTFH", 326562306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 326662306a36Sopenharmony_ci }, 326762306a36Sopenharmony_ci [OP_PUTPUBFH] = { 326862306a36Sopenharmony_ci .op_func = nfsd4_putrootfh, 326962306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 327062306a36Sopenharmony_ci | OP_IS_PUTFH_LIKE | OP_CLEAR_STATEID, 327162306a36Sopenharmony_ci .op_name = "OP_PUTPUBFH", 327262306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 327362306a36Sopenharmony_ci }, 327462306a36Sopenharmony_ci [OP_PUTROOTFH] = { 327562306a36Sopenharmony_ci .op_func = nfsd4_putrootfh, 327662306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 327762306a36Sopenharmony_ci | OP_IS_PUTFH_LIKE | OP_CLEAR_STATEID, 327862306a36Sopenharmony_ci .op_name = "OP_PUTROOTFH", 327962306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 328062306a36Sopenharmony_ci }, 328162306a36Sopenharmony_ci [OP_READ] = { 328262306a36Sopenharmony_ci .op_func = nfsd4_read, 328362306a36Sopenharmony_ci .op_release = nfsd4_read_release, 328462306a36Sopenharmony_ci .op_name = "OP_READ", 328562306a36Sopenharmony_ci .op_rsize_bop = nfsd4_read_rsize, 328662306a36Sopenharmony_ci .op_get_currentstateid = nfsd4_get_readstateid, 328762306a36Sopenharmony_ci }, 328862306a36Sopenharmony_ci [OP_READDIR] = { 328962306a36Sopenharmony_ci .op_func = nfsd4_readdir, 329062306a36Sopenharmony_ci .op_name = "OP_READDIR", 329162306a36Sopenharmony_ci .op_rsize_bop = nfsd4_readdir_rsize, 329262306a36Sopenharmony_ci }, 329362306a36Sopenharmony_ci [OP_READLINK] = { 329462306a36Sopenharmony_ci .op_func = nfsd4_readlink, 329562306a36Sopenharmony_ci .op_name = "OP_READLINK", 329662306a36Sopenharmony_ci .op_rsize_bop = nfsd4_readlink_rsize, 329762306a36Sopenharmony_ci }, 329862306a36Sopenharmony_ci [OP_REMOVE] = { 329962306a36Sopenharmony_ci .op_func = nfsd4_remove, 330062306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, 330162306a36Sopenharmony_ci .op_name = "OP_REMOVE", 330262306a36Sopenharmony_ci .op_rsize_bop = nfsd4_remove_rsize, 330362306a36Sopenharmony_ci }, 330462306a36Sopenharmony_ci [OP_RENAME] = { 330562306a36Sopenharmony_ci .op_func = nfsd4_rename, 330662306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, 330762306a36Sopenharmony_ci .op_name = "OP_RENAME", 330862306a36Sopenharmony_ci .op_rsize_bop = nfsd4_rename_rsize, 330962306a36Sopenharmony_ci }, 331062306a36Sopenharmony_ci [OP_RENEW] = { 331162306a36Sopenharmony_ci .op_func = nfsd4_renew, 331262306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 331362306a36Sopenharmony_ci | OP_MODIFIES_SOMETHING, 331462306a36Sopenharmony_ci .op_name = "OP_RENEW", 331562306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci }, 331862306a36Sopenharmony_ci [OP_RESTOREFH] = { 331962306a36Sopenharmony_ci .op_func = nfsd4_restorefh, 332062306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 332162306a36Sopenharmony_ci | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING, 332262306a36Sopenharmony_ci .op_name = "OP_RESTOREFH", 332362306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 332462306a36Sopenharmony_ci }, 332562306a36Sopenharmony_ci [OP_SAVEFH] = { 332662306a36Sopenharmony_ci .op_func = nfsd4_savefh, 332762306a36Sopenharmony_ci .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING, 332862306a36Sopenharmony_ci .op_name = "OP_SAVEFH", 332962306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 333062306a36Sopenharmony_ci }, 333162306a36Sopenharmony_ci [OP_SECINFO] = { 333262306a36Sopenharmony_ci .op_func = nfsd4_secinfo, 333362306a36Sopenharmony_ci .op_release = nfsd4_secinfo_release, 333462306a36Sopenharmony_ci .op_flags = OP_HANDLES_WRONGSEC, 333562306a36Sopenharmony_ci .op_name = "OP_SECINFO", 333662306a36Sopenharmony_ci .op_rsize_bop = nfsd4_secinfo_rsize, 333762306a36Sopenharmony_ci }, 333862306a36Sopenharmony_ci [OP_SETATTR] = { 333962306a36Sopenharmony_ci .op_func = nfsd4_setattr, 334062306a36Sopenharmony_ci .op_name = "OP_SETATTR", 334162306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME 334262306a36Sopenharmony_ci | OP_NONTRIVIAL_ERROR_ENCODE, 334362306a36Sopenharmony_ci .op_rsize_bop = nfsd4_setattr_rsize, 334462306a36Sopenharmony_ci .op_get_currentstateid = nfsd4_get_setattrstateid, 334562306a36Sopenharmony_ci }, 334662306a36Sopenharmony_ci [OP_SETCLIENTID] = { 334762306a36Sopenharmony_ci .op_func = nfsd4_setclientid, 334862306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 334962306a36Sopenharmony_ci | OP_MODIFIES_SOMETHING | OP_CACHEME 335062306a36Sopenharmony_ci | OP_NONTRIVIAL_ERROR_ENCODE, 335162306a36Sopenharmony_ci .op_name = "OP_SETCLIENTID", 335262306a36Sopenharmony_ci .op_rsize_bop = nfsd4_setclientid_rsize, 335362306a36Sopenharmony_ci }, 335462306a36Sopenharmony_ci [OP_SETCLIENTID_CONFIRM] = { 335562306a36Sopenharmony_ci .op_func = nfsd4_setclientid_confirm, 335662306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 335762306a36Sopenharmony_ci | OP_MODIFIES_SOMETHING | OP_CACHEME, 335862306a36Sopenharmony_ci .op_name = "OP_SETCLIENTID_CONFIRM", 335962306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 336062306a36Sopenharmony_ci }, 336162306a36Sopenharmony_ci [OP_VERIFY] = { 336262306a36Sopenharmony_ci .op_func = nfsd4_verify, 336362306a36Sopenharmony_ci .op_name = "OP_VERIFY", 336462306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 336562306a36Sopenharmony_ci }, 336662306a36Sopenharmony_ci [OP_WRITE] = { 336762306a36Sopenharmony_ci .op_func = nfsd4_write, 336862306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, 336962306a36Sopenharmony_ci .op_name = "OP_WRITE", 337062306a36Sopenharmony_ci .op_rsize_bop = nfsd4_write_rsize, 337162306a36Sopenharmony_ci .op_get_currentstateid = nfsd4_get_writestateid, 337262306a36Sopenharmony_ci }, 337362306a36Sopenharmony_ci [OP_RELEASE_LOCKOWNER] = { 337462306a36Sopenharmony_ci .op_func = nfsd4_release_lockowner, 337562306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS 337662306a36Sopenharmony_ci | OP_MODIFIES_SOMETHING, 337762306a36Sopenharmony_ci .op_name = "OP_RELEASE_LOCKOWNER", 337862306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 337962306a36Sopenharmony_ci }, 338062306a36Sopenharmony_ci 338162306a36Sopenharmony_ci /* NFSv4.1 operations */ 338262306a36Sopenharmony_ci [OP_EXCHANGE_ID] = { 338362306a36Sopenharmony_ci .op_func = nfsd4_exchange_id, 338462306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP 338562306a36Sopenharmony_ci | OP_MODIFIES_SOMETHING, 338662306a36Sopenharmony_ci .op_name = "OP_EXCHANGE_ID", 338762306a36Sopenharmony_ci .op_rsize_bop = nfsd4_exchange_id_rsize, 338862306a36Sopenharmony_ci }, 338962306a36Sopenharmony_ci [OP_BACKCHANNEL_CTL] = { 339062306a36Sopenharmony_ci .op_func = nfsd4_backchannel_ctl, 339162306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, 339262306a36Sopenharmony_ci .op_name = "OP_BACKCHANNEL_CTL", 339362306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 339462306a36Sopenharmony_ci }, 339562306a36Sopenharmony_ci [OP_BIND_CONN_TO_SESSION] = { 339662306a36Sopenharmony_ci .op_func = nfsd4_bind_conn_to_session, 339762306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP 339862306a36Sopenharmony_ci | OP_MODIFIES_SOMETHING, 339962306a36Sopenharmony_ci .op_name = "OP_BIND_CONN_TO_SESSION", 340062306a36Sopenharmony_ci .op_rsize_bop = nfsd4_bind_conn_to_session_rsize, 340162306a36Sopenharmony_ci }, 340262306a36Sopenharmony_ci [OP_CREATE_SESSION] = { 340362306a36Sopenharmony_ci .op_func = nfsd4_create_session, 340462306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP 340562306a36Sopenharmony_ci | OP_MODIFIES_SOMETHING, 340662306a36Sopenharmony_ci .op_name = "OP_CREATE_SESSION", 340762306a36Sopenharmony_ci .op_rsize_bop = nfsd4_create_session_rsize, 340862306a36Sopenharmony_ci }, 340962306a36Sopenharmony_ci [OP_DESTROY_SESSION] = { 341062306a36Sopenharmony_ci .op_func = nfsd4_destroy_session, 341162306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP 341262306a36Sopenharmony_ci | OP_MODIFIES_SOMETHING, 341362306a36Sopenharmony_ci .op_name = "OP_DESTROY_SESSION", 341462306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 341562306a36Sopenharmony_ci }, 341662306a36Sopenharmony_ci [OP_SEQUENCE] = { 341762306a36Sopenharmony_ci .op_func = nfsd4_sequence, 341862306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, 341962306a36Sopenharmony_ci .op_name = "OP_SEQUENCE", 342062306a36Sopenharmony_ci .op_rsize_bop = nfsd4_sequence_rsize, 342162306a36Sopenharmony_ci }, 342262306a36Sopenharmony_ci [OP_DESTROY_CLIENTID] = { 342362306a36Sopenharmony_ci .op_func = nfsd4_destroy_clientid, 342462306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP 342562306a36Sopenharmony_ci | OP_MODIFIES_SOMETHING, 342662306a36Sopenharmony_ci .op_name = "OP_DESTROY_CLIENTID", 342762306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 342862306a36Sopenharmony_ci }, 342962306a36Sopenharmony_ci [OP_RECLAIM_COMPLETE] = { 343062306a36Sopenharmony_ci .op_func = nfsd4_reclaim_complete, 343162306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, 343262306a36Sopenharmony_ci .op_name = "OP_RECLAIM_COMPLETE", 343362306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 343462306a36Sopenharmony_ci }, 343562306a36Sopenharmony_ci [OP_SECINFO_NO_NAME] = { 343662306a36Sopenharmony_ci .op_func = nfsd4_secinfo_no_name, 343762306a36Sopenharmony_ci .op_release = nfsd4_secinfo_no_name_release, 343862306a36Sopenharmony_ci .op_flags = OP_HANDLES_WRONGSEC, 343962306a36Sopenharmony_ci .op_name = "OP_SECINFO_NO_NAME", 344062306a36Sopenharmony_ci .op_rsize_bop = nfsd4_secinfo_rsize, 344162306a36Sopenharmony_ci }, 344262306a36Sopenharmony_ci [OP_TEST_STATEID] = { 344362306a36Sopenharmony_ci .op_func = nfsd4_test_stateid, 344462306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH, 344562306a36Sopenharmony_ci .op_name = "OP_TEST_STATEID", 344662306a36Sopenharmony_ci .op_rsize_bop = nfsd4_test_stateid_rsize, 344762306a36Sopenharmony_ci }, 344862306a36Sopenharmony_ci [OP_FREE_STATEID] = { 344962306a36Sopenharmony_ci .op_func = nfsd4_free_stateid, 345062306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, 345162306a36Sopenharmony_ci .op_name = "OP_FREE_STATEID", 345262306a36Sopenharmony_ci .op_get_currentstateid = nfsd4_get_freestateid, 345362306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 345462306a36Sopenharmony_ci }, 345562306a36Sopenharmony_ci#ifdef CONFIG_NFSD_PNFS 345662306a36Sopenharmony_ci [OP_GETDEVICEINFO] = { 345762306a36Sopenharmony_ci .op_func = nfsd4_getdeviceinfo, 345862306a36Sopenharmony_ci .op_release = nfsd4_getdeviceinfo_release, 345962306a36Sopenharmony_ci .op_flags = ALLOWED_WITHOUT_FH, 346062306a36Sopenharmony_ci .op_name = "OP_GETDEVICEINFO", 346162306a36Sopenharmony_ci .op_rsize_bop = nfsd4_getdeviceinfo_rsize, 346262306a36Sopenharmony_ci }, 346362306a36Sopenharmony_ci [OP_LAYOUTGET] = { 346462306a36Sopenharmony_ci .op_func = nfsd4_layoutget, 346562306a36Sopenharmony_ci .op_release = nfsd4_layoutget_release, 346662306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 346762306a36Sopenharmony_ci .op_name = "OP_LAYOUTGET", 346862306a36Sopenharmony_ci .op_rsize_bop = nfsd4_layoutget_rsize, 346962306a36Sopenharmony_ci }, 347062306a36Sopenharmony_ci [OP_LAYOUTCOMMIT] = { 347162306a36Sopenharmony_ci .op_func = nfsd4_layoutcommit, 347262306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 347362306a36Sopenharmony_ci .op_name = "OP_LAYOUTCOMMIT", 347462306a36Sopenharmony_ci .op_rsize_bop = nfsd4_layoutcommit_rsize, 347562306a36Sopenharmony_ci }, 347662306a36Sopenharmony_ci [OP_LAYOUTRETURN] = { 347762306a36Sopenharmony_ci .op_func = nfsd4_layoutreturn, 347862306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 347962306a36Sopenharmony_ci .op_name = "OP_LAYOUTRETURN", 348062306a36Sopenharmony_ci .op_rsize_bop = nfsd4_layoutreturn_rsize, 348162306a36Sopenharmony_ci }, 348262306a36Sopenharmony_ci#endif /* CONFIG_NFSD_PNFS */ 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci /* NFSv4.2 operations */ 348562306a36Sopenharmony_ci [OP_ALLOCATE] = { 348662306a36Sopenharmony_ci .op_func = nfsd4_allocate, 348762306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 348862306a36Sopenharmony_ci .op_name = "OP_ALLOCATE", 348962306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 349062306a36Sopenharmony_ci }, 349162306a36Sopenharmony_ci [OP_DEALLOCATE] = { 349262306a36Sopenharmony_ci .op_func = nfsd4_deallocate, 349362306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 349462306a36Sopenharmony_ci .op_name = "OP_DEALLOCATE", 349562306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 349662306a36Sopenharmony_ci }, 349762306a36Sopenharmony_ci [OP_CLONE] = { 349862306a36Sopenharmony_ci .op_func = nfsd4_clone, 349962306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 350062306a36Sopenharmony_ci .op_name = "OP_CLONE", 350162306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 350262306a36Sopenharmony_ci }, 350362306a36Sopenharmony_ci [OP_COPY] = { 350462306a36Sopenharmony_ci .op_func = nfsd4_copy, 350562306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 350662306a36Sopenharmony_ci .op_name = "OP_COPY", 350762306a36Sopenharmony_ci .op_rsize_bop = nfsd4_copy_rsize, 350862306a36Sopenharmony_ci }, 350962306a36Sopenharmony_ci [OP_READ_PLUS] = { 351062306a36Sopenharmony_ci .op_func = nfsd4_read, 351162306a36Sopenharmony_ci .op_release = nfsd4_read_release, 351262306a36Sopenharmony_ci .op_name = "OP_READ_PLUS", 351362306a36Sopenharmony_ci .op_rsize_bop = nfsd4_read_plus_rsize, 351462306a36Sopenharmony_ci .op_get_currentstateid = nfsd4_get_readstateid, 351562306a36Sopenharmony_ci }, 351662306a36Sopenharmony_ci [OP_SEEK] = { 351762306a36Sopenharmony_ci .op_func = nfsd4_seek, 351862306a36Sopenharmony_ci .op_name = "OP_SEEK", 351962306a36Sopenharmony_ci .op_rsize_bop = nfsd4_seek_rsize, 352062306a36Sopenharmony_ci }, 352162306a36Sopenharmony_ci [OP_OFFLOAD_STATUS] = { 352262306a36Sopenharmony_ci .op_func = nfsd4_offload_status, 352362306a36Sopenharmony_ci .op_name = "OP_OFFLOAD_STATUS", 352462306a36Sopenharmony_ci .op_rsize_bop = nfsd4_offload_status_rsize, 352562306a36Sopenharmony_ci }, 352662306a36Sopenharmony_ci [OP_OFFLOAD_CANCEL] = { 352762306a36Sopenharmony_ci .op_func = nfsd4_offload_cancel, 352862306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 352962306a36Sopenharmony_ci .op_name = "OP_OFFLOAD_CANCEL", 353062306a36Sopenharmony_ci .op_rsize_bop = nfsd4_only_status_rsize, 353162306a36Sopenharmony_ci }, 353262306a36Sopenharmony_ci [OP_COPY_NOTIFY] = { 353362306a36Sopenharmony_ci .op_func = nfsd4_copy_notify, 353462306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING, 353562306a36Sopenharmony_ci .op_name = "OP_COPY_NOTIFY", 353662306a36Sopenharmony_ci .op_rsize_bop = nfsd4_copy_notify_rsize, 353762306a36Sopenharmony_ci }, 353862306a36Sopenharmony_ci [OP_GETXATTR] = { 353962306a36Sopenharmony_ci .op_func = nfsd4_getxattr, 354062306a36Sopenharmony_ci .op_name = "OP_GETXATTR", 354162306a36Sopenharmony_ci .op_rsize_bop = nfsd4_getxattr_rsize, 354262306a36Sopenharmony_ci }, 354362306a36Sopenharmony_ci [OP_SETXATTR] = { 354462306a36Sopenharmony_ci .op_func = nfsd4_setxattr, 354562306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, 354662306a36Sopenharmony_ci .op_name = "OP_SETXATTR", 354762306a36Sopenharmony_ci .op_rsize_bop = nfsd4_setxattr_rsize, 354862306a36Sopenharmony_ci }, 354962306a36Sopenharmony_ci [OP_LISTXATTRS] = { 355062306a36Sopenharmony_ci .op_func = nfsd4_listxattrs, 355162306a36Sopenharmony_ci .op_name = "OP_LISTXATTRS", 355262306a36Sopenharmony_ci .op_rsize_bop = nfsd4_listxattrs_rsize, 355362306a36Sopenharmony_ci }, 355462306a36Sopenharmony_ci [OP_REMOVEXATTR] = { 355562306a36Sopenharmony_ci .op_func = nfsd4_removexattr, 355662306a36Sopenharmony_ci .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME, 355762306a36Sopenharmony_ci .op_name = "OP_REMOVEXATTR", 355862306a36Sopenharmony_ci .op_rsize_bop = nfsd4_removexattr_rsize, 355962306a36Sopenharmony_ci }, 356062306a36Sopenharmony_ci}; 356162306a36Sopenharmony_ci 356262306a36Sopenharmony_ci/** 356362306a36Sopenharmony_ci * nfsd4_spo_must_allow - Determine if the compound op contains an 356462306a36Sopenharmony_ci * operation that is allowed to be sent with machine credentials 356562306a36Sopenharmony_ci * 356662306a36Sopenharmony_ci * @rqstp: a pointer to the struct svc_rqst 356762306a36Sopenharmony_ci * 356862306a36Sopenharmony_ci * Checks to see if the compound contains a spo_must_allow op 356962306a36Sopenharmony_ci * and confirms that it was sent with the proper machine creds. 357062306a36Sopenharmony_ci */ 357162306a36Sopenharmony_ci 357262306a36Sopenharmony_cibool nfsd4_spo_must_allow(struct svc_rqst *rqstp) 357362306a36Sopenharmony_ci{ 357462306a36Sopenharmony_ci struct nfsd4_compoundres *resp = rqstp->rq_resp; 357562306a36Sopenharmony_ci struct nfsd4_compoundargs *argp = rqstp->rq_argp; 357662306a36Sopenharmony_ci struct nfsd4_op *this; 357762306a36Sopenharmony_ci struct nfsd4_compound_state *cstate = &resp->cstate; 357862306a36Sopenharmony_ci struct nfs4_op_map *allow = &cstate->clp->cl_spo_must_allow; 357962306a36Sopenharmony_ci u32 opiter; 358062306a36Sopenharmony_ci 358162306a36Sopenharmony_ci if (!cstate->minorversion) 358262306a36Sopenharmony_ci return false; 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ci if (cstate->spo_must_allowed) 358562306a36Sopenharmony_ci return true; 358662306a36Sopenharmony_ci 358762306a36Sopenharmony_ci opiter = resp->opcnt; 358862306a36Sopenharmony_ci while (opiter < argp->opcnt) { 358962306a36Sopenharmony_ci this = &argp->ops[opiter++]; 359062306a36Sopenharmony_ci if (test_bit(this->opnum, allow->u.longs) && 359162306a36Sopenharmony_ci cstate->clp->cl_mach_cred && 359262306a36Sopenharmony_ci nfsd4_mach_creds_match(cstate->clp, rqstp)) { 359362306a36Sopenharmony_ci cstate->spo_must_allowed = true; 359462306a36Sopenharmony_ci return true; 359562306a36Sopenharmony_ci } 359662306a36Sopenharmony_ci } 359762306a36Sopenharmony_ci cstate->spo_must_allowed = false; 359862306a36Sopenharmony_ci return false; 359962306a36Sopenharmony_ci} 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ciint nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op) 360262306a36Sopenharmony_ci{ 360362306a36Sopenharmony_ci if (op->opnum == OP_ILLEGAL || op->status == nfserr_notsupp) 360462306a36Sopenharmony_ci return op_encode_hdr_size * sizeof(__be32); 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci BUG_ON(OPDESC(op)->op_rsize_bop == NULL); 360762306a36Sopenharmony_ci return OPDESC(op)->op_rsize_bop(rqstp, op); 360862306a36Sopenharmony_ci} 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_civoid warn_on_nonidempotent_op(struct nfsd4_op *op) 361162306a36Sopenharmony_ci{ 361262306a36Sopenharmony_ci if (OPDESC(op)->op_flags & OP_MODIFIES_SOMETHING) { 361362306a36Sopenharmony_ci pr_err("unable to encode reply to nonidempotent op %u (%s)\n", 361462306a36Sopenharmony_ci op->opnum, nfsd4_op_name(op->opnum)); 361562306a36Sopenharmony_ci WARN_ON_ONCE(1); 361662306a36Sopenharmony_ci } 361762306a36Sopenharmony_ci} 361862306a36Sopenharmony_ci 361962306a36Sopenharmony_cistatic const char *nfsd4_op_name(unsigned opnum) 362062306a36Sopenharmony_ci{ 362162306a36Sopenharmony_ci if (opnum < ARRAY_SIZE(nfsd4_ops)) 362262306a36Sopenharmony_ci return nfsd4_ops[opnum].op_name; 362362306a36Sopenharmony_ci return "unknown_operation"; 362462306a36Sopenharmony_ci} 362562306a36Sopenharmony_ci 362662306a36Sopenharmony_cistatic const struct svc_procedure nfsd_procedures4[2] = { 362762306a36Sopenharmony_ci [NFSPROC4_NULL] = { 362862306a36Sopenharmony_ci .pc_func = nfsd4_proc_null, 362962306a36Sopenharmony_ci .pc_decode = nfssvc_decode_voidarg, 363062306a36Sopenharmony_ci .pc_encode = nfssvc_encode_voidres, 363162306a36Sopenharmony_ci .pc_argsize = sizeof(struct nfsd_voidargs), 363262306a36Sopenharmony_ci .pc_argzero = sizeof(struct nfsd_voidargs), 363362306a36Sopenharmony_ci .pc_ressize = sizeof(struct nfsd_voidres), 363462306a36Sopenharmony_ci .pc_cachetype = RC_NOCACHE, 363562306a36Sopenharmony_ci .pc_xdrressize = 1, 363662306a36Sopenharmony_ci .pc_name = "NULL", 363762306a36Sopenharmony_ci }, 363862306a36Sopenharmony_ci [NFSPROC4_COMPOUND] = { 363962306a36Sopenharmony_ci .pc_func = nfsd4_proc_compound, 364062306a36Sopenharmony_ci .pc_decode = nfs4svc_decode_compoundargs, 364162306a36Sopenharmony_ci .pc_encode = nfs4svc_encode_compoundres, 364262306a36Sopenharmony_ci .pc_argsize = sizeof(struct nfsd4_compoundargs), 364362306a36Sopenharmony_ci .pc_argzero = offsetof(struct nfsd4_compoundargs, iops), 364462306a36Sopenharmony_ci .pc_ressize = sizeof(struct nfsd4_compoundres), 364562306a36Sopenharmony_ci .pc_release = nfsd4_release_compoundargs, 364662306a36Sopenharmony_ci .pc_cachetype = RC_NOCACHE, 364762306a36Sopenharmony_ci .pc_xdrressize = NFSD_BUFSIZE/4, 364862306a36Sopenharmony_ci .pc_name = "COMPOUND", 364962306a36Sopenharmony_ci }, 365062306a36Sopenharmony_ci}; 365162306a36Sopenharmony_ci 365262306a36Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(unsigned long, 365362306a36Sopenharmony_ci nfsd_count4[ARRAY_SIZE(nfsd_procedures4)]); 365462306a36Sopenharmony_ciconst struct svc_version nfsd_version4 = { 365562306a36Sopenharmony_ci .vs_vers = 4, 365662306a36Sopenharmony_ci .vs_nproc = ARRAY_SIZE(nfsd_procedures4), 365762306a36Sopenharmony_ci .vs_proc = nfsd_procedures4, 365862306a36Sopenharmony_ci .vs_count = nfsd_count4, 365962306a36Sopenharmony_ci .vs_dispatch = nfsd_dispatch, 366062306a36Sopenharmony_ci .vs_xdrsize = NFS4_SVC_XDRSIZE, 366162306a36Sopenharmony_ci .vs_rpcb_optnl = true, 366262306a36Sopenharmony_ci .vs_need_cong_ctrl = true, 366362306a36Sopenharmony_ci}; 3664