/* * Server-side procedures for NFSv4. * * Copyright (c) 2002 The Regents of the University of Michigan. * All rights reserved. * * Kendrick Smith <kmsmith@umich.edu> * Andy Adamson <andros@umich.edu> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ #include <linux/fs_struct.h> #include <linux/file.h> #include <linux/falloc.h> #include <linux/slab.h> #include <linux/kthread.h> #include <linux/namei.h>
staticbool inter_copy_offload_enable;
module_param(inter_copy_offload_enable, bool, 0644);
MODULE_PARM_DESC(inter_copy_offload_enable, "Enable inter server to server copy offload. Default: false");
/* * Solaris 7 gets confused (bugid 4218508) if these have * the high bit set, as do xfs filesystems without the * "bigtime" feature. So just clear the high bits. If this * is ever changed to use different attrs for storing the * verifier, then do_open_lookup() will also need to be * fixed accordingly.
*/
v_mtime = verifier[0] & 0x7fffffff;
v_atime = verifier[1] & 0x7fffffff;
}
if (d_really_is_positive(child)) { /* NFSv4 protocol requires change attributes even though * no change happened.
*/
status = fh_fill_both_attrs(fhp); if (status != nfs_ok) goto out;
switch (open->op_createmode) { case NFS4_CREATE_UNCHECKED: if (!d_is_reg(child)) break;
/* * In NFSv4, we don't want to truncate the file * now. This would be wrong if the OPEN fails for * some other reason. Furthermore, if the size is * nonzero, we should ignore it according to spec!
*/
open->op_truncate = (iap->ia_valid & ATTR_SIZE) &&
!iap->ia_size; break; case NFS4_CREATE_GUARDED:
status = nfserr_exist; break; case NFS4_CREATE_EXCLUSIVE: if (inode_get_mtime_sec(d_inode(child)) == v_mtime &&
inode_get_atime_sec(d_inode(child)) == v_atime &&
d_inode(child)->i_size == 0) {
open->op_created = true; break; /* subtle */
}
status = nfserr_exist; break; case NFS4_CREATE_EXCLUSIVE4_1: if (inode_get_mtime_sec(d_inode(child)) == v_mtime &&
inode_get_atime_sec(d_inode(child)) == v_atime &&
d_inode(child)->i_size == 0) {
open->op_created = true; goto set_attr; /* subtle */
}
status = nfserr_exist;
} goto out;
}
if (!IS_POSIXACL(inode))
iap->ia_mode &= ~current_umask();
status = fh_fill_pre_attrs(fhp); if (status != nfs_ok) goto out;
status = nfsd4_vfs_create(fhp, child, open); if (status != nfs_ok) goto out;
open->op_created = true;
fh_fill_post_attrs(fhp);
/* A newly created file already has a file size of zero. */ if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
iap->ia_valid &= ~ATTR_SIZE; if (nfsd4_create_is_exclusive(open->op_createmode)) {
iap->ia_valid = ATTR_MTIME | ATTR_ATIME |
ATTR_MTIME_SET|ATTR_ATIME_SET;
iap->ia_mtime.tv_sec = v_mtime;
iap->ia_atime.tv_sec = v_atime;
iap->ia_mtime.tv_nsec = 0;
iap->ia_atime.tv_nsec = 0;
}
set_attr:
status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs);
if (attrs.na_labelerr)
open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; if (attrs.na_aclerr)
open->op_bmval[0] &= ~FATTR4_WORD0_ACL;
out:
inode_unlock(inode);
nfsd_attrs_free(&attrs); if (child && !IS_ERR(child))
dput(child);
fh_drop_write(fhp); return status;
}
/** * set_change_info - set up the change_info4 for a reply * @cinfo: pointer to nfsd4_change_info to be populated * @fhp: pointer to svc_fh to use as source * * Many operations in NFSv4 require change_info4 in the reply. This function * populates that from the info that we (should!) have already collected. In * the event that we didn't get any pre-attrs, just zero out both.
*/ staticvoid
set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp)
{
cinfo->atomic = (u32)(fhp->fh_pre_saved && fhp->fh_post_saved && !fhp->fh_no_atomic_attr);
cinfo->before_change = fhp->fh_pre_change;
cinfo->after_change = fhp->fh_post_change;
/* * If fetching the pre-change attributes failed, then we should * have already failed the whole operation. We could have still * failed to fetch post-change attributes however. * * If we didn't get post-op attrs, just zero-out the after * field since we don't know what it should be. If the pre_saved * field isn't set for some reason, throw warning and just copy * whatever is in the after field.
*/ if (WARN_ON_ONCE(!fhp->fh_pre_saved))
cinfo->before_change = 0; if (!fhp->fh_post_saved)
cinfo->after_change = cinfo->before_change + 1;
}
/* * Following rfc 3530 14.2.16, and rfc 5661 18.16.4 * use the returned bitmask to indicate which attributes * we used to store the verifier:
*/ if (nfsd4_create_is_exclusive(open->op_createmode) && status == 0)
open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS |
FATTR4_WORD1_TIME_MODIFY);
} else {
status = nfsd_lookup(rqstp, current_fh,
open->op_fname, open->op_fnamelen, *resfh); if (status == nfs_ok) /* NFSv4 protocol requires change attributes even though * no change happened.
*/
status = fh_fill_both_attrs(current_fh);
} if (status) goto out;
status = nfsd_check_obj_isreg(*resfh, cstate->minorversion); if (status) goto out;
open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) &&
(open->op_iattr.ia_size == 0); /* * In the delegation case, the client is telling us about an * open that it *already* performed locally, some time ago. We * should let it succeed now if possible. * * In the case of a CLAIM_FH open, on the other hand, the client * may be counting on us to enforce permissions (the Linux 4.1 * client uses this for normal opens, for example).
*/ if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEG_CUR_FH)
accmode = NFSD_MAY_OWNER_OVERRIDE;
/* This check required by spec. */ if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) return nfserr_inval;
open->op_created = false; /* * RFC5661 18.51.3 * Before RECLAIM_COMPLETE done, server should deny new lock
*/ if (nfsd4_has_session(cstate) &&
!test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, &cstate->clp->cl_flags) &&
open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) return nfserr_grace;
if (nfsd4_has_session(cstate))
copy_clientid(&open->op_clientid, cstate->session);
/* check seqid for replay. set nfs4_owner */
status = nfsd4_process_open1(cstate, open, nn); if (status == nfserr_replay_me) { struct nfs4_replay *rp = &open->op_openowner->oo_owner.so_replay;
fh_put(&cstate->current_fh);
fh_copy_shallow(&cstate->current_fh.fh_handle,
&rp->rp_openfh);
status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); if (status)
dprintk("nfsd4_open: replay failed" " restoring previous filehandle\n"); else
status = nfserr_replay_me;
} if (status) goto out; if (open->op_xdr_error) {
status = open->op_xdr_error; goto out;
}
status = nfsd4_check_open_attributes(rqstp, cstate, open); if (status) goto out;
/* Openowner is now set, so sequence id will get bumped. Now we need
* these checks before we do any creates: */
status = nfserr_grace; if (opens_in_grace(net) && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) goto out;
status = nfserr_no_grace; if (!opens_in_grace(net) && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) goto out;
switch (open->op_claim_type) { case NFS4_OPEN_CLAIM_DELEGATE_CUR: case NFS4_OPEN_CLAIM_NULL:
status = do_open_lookup(rqstp, cstate, open, &resfh); if (status) goto out; break; case NFS4_OPEN_CLAIM_PREVIOUS:
status = nfs4_check_open_reclaim(cstate->clp); if (status) goto out;
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
reclaim = true;
fallthrough; case NFS4_OPEN_CLAIM_FH: case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
status = do_open_fhandle(rqstp, cstate, open); if (status) goto out;
resfh = &cstate->current_fh; break; case NFS4_OPEN_CLAIM_DELEG_PREV_FH: case NFS4_OPEN_CLAIM_DELEGATE_PREV:
status = nfserr_notsupp; goto out; default:
status = nfserr_inval; goto out;
}
status = nfsd4_process_open2(rqstp, resfh, open); if (status && open->op_created)
pr_warn("nfsd4_process_open2 failed to open newly-created file: status=%u\n",
be32_to_cpu(status)); if (reclaim && !status)
nn->somebody_reclaimed = true;
out: if (open->op_filp) {
fput(open->op_filp);
open->op_filp = NULL;
} if (resfh && resfh != &cstate->current_fh) {
fh_dup2(&cstate->current_fh, resfh);
fh_put(resfh);
kfree(resfh);
}
nfsd4_cleanup_open_state(cstate, open);
nfsd4_bump_seqid(cstate, status); return status;
}
/* * OPEN is the only seqid-mutating operation whose decoding can fail * with a seqid-mutating error (specifically, decoding of user names in * the attributes). Therefore we have to do some processing to look up * the stateowner so that we can bump the seqid.
*/ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_op *op)
{ struct nfsd4_open *open = &op->u.open;
if (!seqid_mutating_err(ntohl(op->status))) return op->status; if (nfsd4_has_session(cstate)) return op->status;
open->op_xdr_error = op->status; return nfsd4_open(rqstp, cstate, &op->u);
}
/* * If we do a zero copy read, then a client will see read data * that reflects the state of the file *after* performing the * following compound. * * To ensure proper ordering, we therefore turn off zero copy if * the client wants us to do more in this compound:
*/ if (!nfsd4_last_compound_op(rqstp)) { struct nfsd4_compoundargs *argp = rqstp->rq_argp;
staticvoid
nfsd4_secinfo_release(union nfsd4_op_u *u)
{ if (u->secinfo.si_exp)
exp_put(u->secinfo.si_exp);
}
staticvoid
nfsd4_secinfo_no_name_release(union nfsd4_op_u *u)
{ if (u->secinfo_no_name.sin_exp)
exp_put(u->secinfo_no_name.sin_exp);
}
/* * Validate that the requested timestamps are within the acceptable range. If * timestamp appears to be in the future, then it will be clamped to * current_time().
*/ staticvoid
vet_deleg_attrs(struct nfsd4_setattr *setattr, struct nfs4_delegation *dp)
{ struct timespec64 now = current_time(dp->dl_stid.sc_file->fi_inode); struct iattr *iattr = &setattr->sa_iattr;
if (deleg_attrs || (setattr->sa_iattr.ia_valid & ATTR_SIZE)) { int flags = WR_STATE;
if (setattr->sa_bmval[2] & FATTR4_WORD2_TIME_DELEG_ACCESS)
flags |= RD_STATE;
status = nfs4_preprocess_stateid_op(rqstp, cstate,
&cstate->current_fh, &setattr->sa_stateid,
flags, NULL, &st); if (status) return status;
}
if (deleg_attrs) {
status = nfserr_bad_stateid; if (st->sc_type & SC_TYPE_DELEG) { struct nfs4_delegation *dp = delegstateid(st);
/* Only for *_ATTRS_DELEG flavors */ if (deleg_attrs_deleg(dp->dl_type)) {
vet_deleg_attrs(setattr, dp);
status = nfs_ok;
}
}
} if (st)
nfs4_put_stid(st); if (status) return status;
err = fh_want_write(&cstate->current_fh); if (err) return nfserrno(err);
status = nfs_ok;
status = check_attr_support(rqstp, cstate, setattr->sa_bmval,
nfsd_attrmask); if (status) goto out;
if (!cstate->save_fh.fh_dentry) return nfserr_nofilehandle;
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
src_stateid, RD_STATE, src, NULL); if (status) goto out;
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
dst_stateid, WR_STATE, dst, NULL); if (status) goto out_put_src;
/* fix up for NFS-specific error code */ if (!S_ISREG(file_inode((*src)->nf_file)->i_mode) ||
!S_ISREG(file_inode((*dst)->nf_file)->i_mode)) {
status = nfserr_wrong_type; goto out_put_dst;
}
/** * nfsd4_has_active_async_copies - Check for ongoing copy operations * @clp: Client to be checked * * NFSD maintains state for async COPY operations after they complete, * and this state remains in the nfs4_client's async_copies list. * Ongoing copies should block the destruction of the nfs4_client, but * completed copies should not. * * Return values: * %true: At least one active async COPY is ongoing * %false: No active async COPY operations were found
*/ bool nfsd4_has_active_async_copies(struct nfs4_client *clp)
{ struct nfsd4_copy *copy; bool result = false;
/* * setup a work entry in the ssc delayed unmount list.
*/ static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr, struct nfsd4_ssc_umount_item **nsui, struct svc_rqst *rqstp)
{ struct nfsd4_ssc_umount_item *ni = NULL; struct nfsd4_ssc_umount_item *work = NULL; struct nfsd4_ssc_umount_item *tmp;
DEFINE_WAIT(wait);
__be32 status = 0;
*nsui = NULL;
work = kzalloc(sizeof(*work), GFP_KERNEL);
try_again:
spin_lock(&nn->nfsd_ssc_lock);
list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) { if (strncmp(ni->nsui_ipaddr, ipaddr, sizeof(ni->nsui_ipaddr))) continue; /* found a match */ if (ni->nsui_busy) { /* wait - and try again */
prepare_to_wait(&nn->nfsd_ssc_waitq, &wait, TASK_IDLE);
spin_unlock(&nn->nfsd_ssc_lock);
/* allow 20secs for mount/unmount for now - revisit */ if (svc_thread_should_stop(rqstp) ||
(schedule_timeout(20*HZ) == 0)) {
finish_wait(&nn->nfsd_ssc_waitq, &wait);
kfree(work); return nfserr_eagain;
}
finish_wait(&nn->nfsd_ssc_waitq, &wait); goto try_again;
}
*nsui = ni;
refcount_inc(&ni->nsui_refcnt);
spin_unlock(&nn->nfsd_ssc_lock);
kfree(work);
status = nfserr_nodev;
type = get_fs_type("nfs"); if (!type) goto out_free_rawdata;
/* Set the server:<export> for the vfs_kern_mount call */
dev_name = kzalloc(len + 5, GFP_KERNEL); if (!dev_name) goto out_free_rawdata;
snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep);
status = nfsd4_ssc_setup_dul(nn, ipaddr, nsui, rqstp); if (status) goto out_free_devname; if ((*nsui)->nsui_vfsmount) goto out_done;
/* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */
ss_mnt = vfs_kern_mount(type, SB_KERNMOUNT, dev_name, raw_data);
module_put(type->owner); if (IS_ERR(ss_mnt)) {
status = nfserr_nodev;
nfsd4_ssc_cancel_dul(nn, *nsui); goto out_free_devname;
}
nfsd4_ssc_update_dul(nn, *nsui, ss_mnt);
out_done:
status = 0;
spin_lock(&nn->nfsd_ssc_lock);
list_del(&nsui->nsui_list); /* * vfsmount can be shared by multiple exports, * decrement refcnt. If the count drops to 1 it * will be unmounted when nsui_expire expires.
*/
refcount_dec(&nsui->nsui_refcnt);
nsui->nsui_expire = jiffies + timeout;
list_add_tail(&nsui->nsui_list, &nn->nfsd_ssc_mount_list);
spin_unlock(&nn->nfsd_ssc_lock);
}
/* for async copy, we ignore the error, client can always retry * to get the error
*/ if (bytes < 0 && !copy->cp_res.wr_bytes_written)
status = nfserrno(bytes); else {
nfsd4_init_copy_res(copy, sync);
status = nfs_ok;
} return status;
}
status = nfserrno(-ENOMEM);
cps = nfs4_alloc_init_cpntf_state(nn, stid); if (!cps) goto out;
memcpy(&cn->cpn_cnr_stateid, &cps->cp_stateid.cs_stid, sizeof(stateid_t));
memcpy(&cps->cp_p_stateid, &stid->sc_stateid, sizeof(stateid_t));
memcpy(&cps->cp_p_clid, &clp->cl_clientid, sizeof(clientid_t));
/* For now, only return one server address in cpn_src, the * address used by the client to connect to this server.
*/
cn->cpn_src->nl4_type = NL4_NETADDR;
status = nfsd4_set_netaddr((struct sockaddr *)&rqstp->rq_daddr,
&cn->cpn_src->u.nl4_addr);
WARN_ON_ONCE(status); if (status) {
nfs4_put_cpntf_state(nn, cps); goto out;
}
out:
nfs4_put_stid(stid); return status;
}
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
&seek->seek_stateid,
RD_STATE, &nf, NULL); if (status) return status;
switch (seek->seek_whence) { case NFS4_CONTENT_DATA:
whence = SEEK_DATA; break; case NFS4_CONTENT_HOLE:
whence = SEEK_HOLE; break; default:
status = nfserr_union_notsupp; goto out;
}
/* * Note: This call does change file->f_pos, but nothing in NFSD * should ever file->f_pos.
*/
seek->seek_pos = vfs_llseek(nf->nf_file, seek->seek_offset, whence); if (seek->seek_pos < 0)
status = nfserrno(seek->seek_pos); elseif (seek->seek_pos >= i_size_read(file_inode(nf->nf_file)))
seek->seek_eof = true;
out:
nfsd_file_put(nf); return status;
}
/* This routine never returns NFS_OK! If there are no other errors, it * will return NFSERR_SAME or NFSERR_NOT_SAME depending on whether the * attributes matched. VERIFY is implemented by mapping NFSERR_SAME * to NFS_OK after the call; NVERIFY by mapping NFSERR_NOT_SAME to NFS_OK.
*/ static __be32
_nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_verify *verify)
{
__be32 *buf, *p; int count;
__be32 status;
status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); if (status) return status;
status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL); if (status) return status;
if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)
|| (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)) return nfserr_inval; if (verify->ve_attrlen & 3) return nfserr_inval;
p = buf;
status = nfsd4_encode_fattr_to_buf(&p, count, &cstate->current_fh,
cstate->current_fh.fh_export,
cstate->current_fh.fh_dentry,
verify->ve_bmval,
rqstp, 0); /* * If nfsd4_encode_fattr() ran out of space, assume that's because * the attributes are longer (hence different) than those given:
*/ if (status == nfserr_resource)
status = nfserr_not_same; if (status) goto out_kfree;
/* skip bitmap */
p = buf + 1 + ntohl(buf[0]);
status = nfserr_not_same; if (ntohl(*p++) != verify->ve_attrlen) goto out_kfree; if (!memcmp(p, verify->ve_attrval, verify->ve_attrlen))
status = nfserr_same;
/* * RFC 8881, section 18.39.3 says: * * "The server may refuse to grant the delegation. In that case, the * server will return NFS4ERR_DIRDELEG_UNAVAIL." * * This is sub-optimal, since it means that the server would need to * abort compound processing just because the delegation wasn't * available. RFC8881bis should change this to allow the server to * return NFS4_OK with a non-fatal status of GDD4_UNAVAIL in this * situation.
*/
gdd->gddrnf_status = GDD4_UNAVAIL; return nfs_ok;
}
#ifdef CONFIG_NFSD_PNFS staticconststruct nfsd4_layout_ops *
nfsd4_layout_verify(struct svc_export *exp, unsignedint layout_type)
{ if (!exp->ex_layout_types) {
dprintk("%s: export does not support pNFS\n", __func__); return NULL;
}
if (layout_type >= LAYOUT_TYPE_MAX ||
!(exp->ex_layout_types & (1 << layout_type))) {
dprintk("%s: layout type %d not supported\n",
__func__, layout_type); return NULL;
}
/* * Verify minlength and range as per RFC5661: * o If loga_length is less than loga_minlength, * the metadata server MUST return NFS4ERR_INVAL. * o If the sum of loga_offset and loga_minlength exceeds * NFS4_UINT64_MAX, and loga_minlength is not * NFS4_UINT64_MAX, the error NFS4ERR_INVAL MUST result. * o If the sum of loga_offset and loga_length exceeds * NFS4_UINT64_MAX, and loga_length is not NFS4_UINT64_MAX, * the error NFS4ERR_INVAL MUST result.
*/
nfserr = nfserr_inval; if (lgp->lg_seg.length < lgp->lg_minlength ||
(lgp->lg_minlength != NFS4_MAX_UINT64 &&
lgp->lg_minlength > NFS4_MAX_UINT64 - lgp->lg_seg.offset) ||
(lgp->lg_seg.length != NFS4_MAX_UINT64 &&
lgp->lg_seg.length > NFS4_MAX_UINT64 - lgp->lg_seg.offset)) goto out; if (lgp->lg_seg.length == 0) goto out;
if (opens_in_grace(SVC_NET(rqstp))) return nfserr_grace;
ret = nfsd_setxattr(rqstp, &cstate->current_fh, setxattr->setxa_name,
setxattr->setxa_buf, setxattr->setxa_len,
setxattr->setxa_flags);
if (!ret)
set_change_info(&setxattr->setxa_cinfo, &cstate->current_fh);
return ret;
}
static __be32
nfsd4_listxattrs(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, union nfsd4_op_u *u)
{ /* * Get the entire list, then copy out only the user attributes * in the encode function.
*/ return nfsd_listxattr(rqstp, &cstate->current_fh,
&u->listxattrs.lsxa_buf, &u->listxattrs.lsxa_len);
}
/* * Enforce NFSv4.1 COMPOUND ordering rules: * * Also note, enforced elsewhere: * - SEQUENCE other than as first op results in * NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().) * - BIND_CONN_TO_SESSION must be the only op in its compound. * (Enforced in nfsd4_bind_conn_to_session().) * - DESTROY_SESSION must be the final operation in a compound, if * sessionid's in SEQUENCE and DESTROY_SESSION are the same. * (Enforced in nfsd4_destroy_session().)
*/ static __be32 nfs41_check_op_ordering(struct nfsd4_compoundargs *args)
{ struct nfsd4_op *first_op = &args->ops[0];
/* These ordering requirements don't apply to NFSv4.0: */ if (args->minorversion == 0) return nfs_ok; /* This is weird, but OK, not our problem: */ if (args->opcnt == 0) return nfs_ok; if (first_op->status == nfserr_op_illegal) return nfs_ok; if (!(nfsd4_ops[first_op->opnum].op_flags & ALLOWED_AS_FIRST_OP)) return nfserr_op_not_in_session; if (first_op->opnum == OP_SEQUENCE) return nfs_ok; /* * So first_op is something allowed outside a session, like * EXCHANGE_ID; but then it has to be the only op in the * compound:
*/ if (args->opcnt != 1) return nfserr_not_only_op; return nfs_ok;
}
/* * Most ops check wronsec on our own; only the putfh-like ops * have special rules.
*/ if (!(thisd->op_flags & OP_IS_PUTFH_LIKE)) returnfalse; /* * rfc 5661 2.6.3.1.1.6: don't bother erroring out a * put-filehandle operation if we're not going to use the * result:
*/ if (argp->opcnt == resp->opcnt) returnfalse; if (next->opnum == OP_ILLEGAL) returnfalse;
nextd = OPDESC(next); /* * Rest of 2.6.3.1.1: certain operations will return WRONGSEC * errors themselves as necessary; others should check for them * now:
*/ return !(nextd->op_flags & OP_HANDLES_WRONGSEC);
}
/* reserve space for: NFS status code */
xdr_reserve_space(resp->xdr, XDR_UNIT);
/* reserve space for: taglen, tag, and opcnt */
xdr_reserve_space(resp->xdr, XDR_UNIT * 2 + args->taglen);
resp->taglen = args->taglen;
resp->tag = args->tag;
resp->rqstp = rqstp;
cstate->minorversion = args->minorversion;
fh_init(current_fh, NFS4_FHSIZE);
fh_init(save_fh, NFS4_FHSIZE); /* * Don't use the deferral mechanism for NFSv4; compounds make it * too hard to avoid non-idempotency problems.
*/
clear_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
/* * According to RFC3010, this takes precedence over all other errors.
*/
status = nfserr_minor_vers_mismatch; if (nfsd_minorversion(nn, args->minorversion, NFSD_TEST) <= 0) goto out;
status = nfs41_check_op_ordering(args); if (status) {
op = &args->ops[0];
op->status = status;
resp->opcnt = 1; goto encode_op;
}
check_if_stalefh_allowed(args);
rqstp->rq_lease_breaker = (void **)&cstate->clp;
trace_nfsd_compound(rqstp, args->tag, args->taglen, args->client_opcnt); while (!status && resp->opcnt < args->opcnt) {
op = &args->ops[resp->opcnt++];
if (unlikely(resp->opcnt == NFSD_MAX_OPS_PER_COMPOUND)) { /* If there are still more operations to process,
* stop here and report NFS4ERR_RESOURCE. */ if (cstate->minorversion == 0 &&
args->client_opcnt > resp->opcnt) {
op->status = nfserr_resource; goto encode_op;
}
}
/* * The XDR decode routines may have pre-set op->status; * for example, if there is a miscellaneous XDR error * it will be set to nfserr_bad_xdr.
*/ if (op->status) { if (op->opnum == OP_OPEN)
op->status = nfsd4_open_omfg(rqstp, cstate, op); goto encode_op;
} if (!current_fh->fh_dentry &&
!HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) { if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
op->status = nfserr_nofilehandle; goto encode_op;
}
} elseif (current_fh->fh_export &&
current_fh->fh_export->ex_fslocs.migrated &&
!(op->opdesc->op_flags & ALLOWED_ON_ABSENT_FS)) {
op->status = nfserr_moved; goto encode_op;
}
fh_clear_pre_post_attrs(current_fh);
/* If op is non-idempotent */ if (op->opdesc->op_flags & OP_MODIFIES_SOMETHING) { /* * Don't execute this op if we couldn't encode a * successful reply:
*/
u32 plen = op->opdesc->op_rsize_bop(rqstp, op); /* * Plus if there's another operation, make sure * we'll have space to at least encode an error:
*/ if (resp->opcnt < args->opcnt)
plen += COMPOUND_ERR_SLACK_SPACE;
op->status = nfsd4_check_resp_size(resp, plen);
}
/* Only from SEQUENCE */ if (cstate->status == nfserr_replay_cache) {
dprintk("%s NFS4.1 replay from cache\n", __func__);
status = op->status; goto out;
} if (!op->status) { if (op->opdesc->op_set_currentstateid)
op->opdesc->op_set_currentstateid(cstate, &op->u);
if (op->opdesc->op_flags & OP_CLEAR_STATEID)
clear_current_stateid(cstate);
if (current_fh->fh_export &&
need_wrongsec_check(rqstp))
op->status = check_nfsd_access(current_fh->fh_export, rqstp, false);
}
encode_op: if (op->status == nfserr_replay_me) {
op->replay = &cstate->replay_owner->so_replay;
nfsd4_encode_replay(resp->xdr, op);
status = op->status = op->replay->rp_status;
} else {
nfsd4_encode_operation(resp, op);
status = op->status;
}
/* We'll fall back on returning no lockowner if run out of space: */ #define op_encode_lockowner_maxsz (0) #define op_encode_lock_denied_maxsz (8 + op_encode_lockowner_maxsz)
/* * The _rsize() helpers are invoked by the NFSv4 COMPOUND decoder, which * is called before sunrpc sets rq_res.buflen. Thus we have to compute * the maximum payload size here, based on transport limits and the size * of the remaining space in the rq_pages array.
*/ static u32 nfsd4_max_payload(conststruct svc_rqst *rqstp)
{
u32 buflen;
/* * Note since this is an idempotent operation we won't insist on failing * the op prematurely if the estimate is too large. We may turn off splice * reads unnecessarily.
*/ static u32 nfsd4_getattr_rsize(conststruct svc_rqst *rqstp, conststruct nfsd4_op *op)
{ const u32 *bmap = op->u.getattr.ga_bmval;
u32 bmap0 = bmap[0], bmap1 = bmap[1], bmap2 = bmap[2];
u32 ret = 0;
if (bmap0 & FATTR4_WORD0_ACL) return nfsd4_max_payload(rqstp); if (bmap0 & FATTR4_WORD0_FS_LOCATIONS) return nfsd4_max_payload(rqstp);
if (bmap1 & FATTR4_WORD1_OWNER) {
ret += IDMAP_NAMESZ + 4;
bmap1 &= ~FATTR4_WORD1_OWNER;
} if (bmap1 & FATTR4_WORD1_OWNER_GROUP) {
ret += IDMAP_NAMESZ + 4;
bmap1 &= ~FATTR4_WORD1_OWNER_GROUP;
} if (bmap0 & FATTR4_WORD0_FILEHANDLE) {
ret += NFS4_FHSIZE + 4;
bmap0 &= ~FATTR4_WORD0_FILEHANDLE;
} if (bmap2 & FATTR4_WORD2_SECURITY_LABEL) {
ret += NFS4_MAXLABELLEN + 12;
bmap2 &= ~FATTR4_WORD2_SECURITY_LABEL;
} /* * Largest of remaining attributes are 16 bytes (e.g., * supported_attributes)
*/
ret += 16 * (hweight32(bmap0) + hweight32(bmap1) + hweight32(bmap2)); /* bitmask, length */
ret += 20; return ret;
}
static u32 nfsd4_read_plus_rsize(conststruct svc_rqst *rqstp, conststruct nfsd4_op *op)
{
u32 rlen = min(op->u.read.rd_length, nfsd4_max_payload(rqstp)); /* * If we detect that the file changed during hole encoding, then we * recover by encoding the remaining reply as data. This means we need * to set aside enough room to encode two data segments.
*/
u32 seg_len = 2 * (1 + 2 + 1);
/* * At this stage we don't really know what layout driver will handle the request, * so we need to define an arbitrary upper bound here.
*/ #define MAX_LAYOUT_SIZE 128 static u32 nfsd4_layoutget_rsize(conststruct svc_rqst *rqstp, conststruct nfsd4_op *op)
{ return (op_encode_hdr_size +
1 /* logr_return_on_close */ +
op_encode_stateid_maxsz +
1 /* nr of layouts */ +
MAX_LAYOUT_SIZE) * sizeof(__be32);
}
/** * nfsd4_spo_must_allow - Determine if the compound op contains an * operation that is allowed to be sent with machine credentials * * @rqstp: a pointer to the struct svc_rqst * * Checks to see if the compound contains a spo_must_allow op * and confirms that it was sent with the proper machine creds.
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.