/* * 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;
}
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.