/* * Server-side XDR 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.
*/
/* * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing * directory in order to indicate to the client that a filesystem boundary is present * We use a fixed fsid for a referral
*/ #define NFS4_REFERRAL_FSID_MAJOR 0x8000000ULL #define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL
static __be32
check_filename(char *str, int len)
{ int i;
if (len == 0) return nfserr_inval; if (len > NFS4_MAXNAMLEN) return nfserr_nametoolong; if (isdotent(str, len)) return nfserr_badname; for (i = 0; i < len; i++) if (str[i] == '/') return nfserr_badname; return 0;
}
/** * svcxdr_tmpalloc - allocate memory to be freed after compound processing * @argp: NFSv4 compound argument structure * @len: length of buffer to allocate * * Allocates a buffer of size @len to be freed when processing the compound * operation described in @argp finishes.
*/ staticvoid *
svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, size_t len)
{ struct svcxdr_tmpbuf *tb;
/* * For xdr strings that need to be passed to other kernel api's * as null-terminated strings. * * Note null-terminating in place usually isn't safe since the * buffer might end on a page boundary.
*/ staticchar *
svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, size_t len)
{ char *p = svcxdr_tmpalloc(argp, size_add(len, 1));
/* * The location of the decoded data item is stable, * so @p is OK to use. This is the common case.
*/ if (p != argp->xdr->scratch.iov_base) return p;
/* * This helper handles variable-length opaques which belong to protocol * elements that this implementation does not support.
*/ static __be32
nfsd4_decode_ignored_string(struct nfsd4_compoundargs *argp, u32 maxlen)
{
u32 len;
if (xdr_stream_decode_u32(argp->xdr, &len) < 0) return nfserr_bad_xdr; if (maxlen && len > maxlen) return nfserr_bad_xdr; if (!xdr_inline_decode(argp->xdr, len)) return nfserr_bad_xdr;
p = xdr_inline_decode(argp->xdr, NFS4_VERIFIER_SIZE); if (!p) return nfserr_bad_xdr;
memcpy(verf->data, p, sizeof(verf->data)); return nfs_ok;
}
/** * nfsd4_decode_bitmap4 - Decode an NFSv4 bitmap4 * @argp: NFSv4 compound argument structure * @bmval: pointer to an array of u32's to decode into * @bmlen: size of the @bmval array * * The server needs to return nfs_ok rather than nfserr_bad_xdr when * encountering bitmaps containing bits it does not recognize. This * includes bits in bitmap words past WORDn, where WORDn is the last * bitmap WORD the implementation currently supports. Thus we are * careful here to simply ignore bits in bitmap words that this * implementation has yet to support explicitly. * * Return values: * %nfs_ok: @bmval populated successfully * %nfserr_bad_xdr: the encoded bitmap was invalid
*/ static __be32
nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
{
ssize_t status;
status = xdr_stream_decode_uint32_array(argp->xdr, bmval, bmlen); return status == -EBADMSG ? nfserr_bad_xdr : nfs_ok;
}
if (xdr_stream_decode_u32(argp->xdr, &count) < 0) return nfserr_bad_xdr;
if (count > xdr_stream_remaining(argp->xdr) / 20) /* * Even with 4-byte names there wouldn't be * space for that many aces; something fishy is * going on:
*/ return nfserr_fbig;
*acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(count)); if (*acl == NULL) return nfserr_jukebox;
(*acl)->naces = count; for (ace = (*acl)->aces; ace < (*acl)->aces + count; ace++) {
status = nfsd4_decode_nfsace4(argp, ace); if (status) return status;
}
if (xdr_stream_decode_u32(argp->xdr, &length) < 0) return nfserr_bad_xdr;
p = xdr_inline_decode(argp->xdr, length); if (!p) return nfserr_bad_xdr;
status = nfsd_map_name_to_uid(argp->rqstp, (char *)p, length,
&iattr->ia_uid); if (status) return status;
iattr->ia_valid |= ATTR_UID;
} if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
u32 length;
if (xdr_stream_decode_u32(argp->xdr, &length) < 0) return nfserr_bad_xdr;
p = xdr_inline_decode(argp->xdr, length); if (!p) return nfserr_bad_xdr;
status = nfsd_map_name_to_gid(argp->rqstp, (char *)p, length,
&iattr->ia_gid); if (status) return status;
iattr->ia_valid |= ATTR_GID;
} if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
u32 set_it;
if (xdr_stream_decode_u32(argp->xdr, &set_it) < 0) return nfserr_bad_xdr; switch (set_it) { case NFS4_SET_TO_CLIENT_TIME:
status = nfsd4_decode_nfstime4(argp, &iattr->ia_atime); if (status) return status;
iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); break; case NFS4_SET_TO_SERVER_TIME:
iattr->ia_valid |= ATTR_ATIME; break; default: return nfserr_bad_xdr;
}
} if (bmval[1] & FATTR4_WORD1_TIME_CREATE) { struct timespec64 ts;
/* No Linux filesystem supports setting this attribute. */
bmval[1] &= ~FATTR4_WORD1_TIME_CREATE;
status = nfsd4_decode_nfstime4(argp, &ts); if (status) return status;
} if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
u32 set_it;
if (xdr_stream_decode_u32(argp->xdr, &set_it) < 0) return nfserr_bad_xdr; switch (set_it) { case NFS4_SET_TO_CLIENT_TIME:
status = nfsd4_decode_nfstime4(argp, &iattr->ia_mtime); if (status) return status;
iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); break; case NFS4_SET_TO_SERVER_TIME:
iattr->ia_valid |= ATTR_MTIME; break; default: return nfserr_bad_xdr;
}
}
label->len = 0; if (IS_ENABLED(CONFIG_NFSD_V4_SECURITY_LABEL) &&
bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
status = nfsd4_decode_security_label(argp, label); if (status) return status;
} if (bmval[2] & FATTR4_WORD2_MODE_UMASK) {
u32 mode, mask;
/* request sanity: did attrlist4 contain the expected number of words? */ if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos) return nfserr_bad_xdr;
dprintk("RPC_AUTH_GSS callback secflavor not supported!\n");
if (xdr_stream_decode_u32(argp->xdr, &service) < 0) return nfserr_bad_xdr; if (service < RPC_GSS_SVC_NONE || service > RPC_GSS_SVC_PRIVACY) return nfserr_bad_xdr; /* gcbp_handle_from_server */
status = nfsd4_decode_ignored_string(argp, 0); if (status) return status; /* gcbp_handle_from_client */
status = nfsd4_decode_ignored_string(argp, 0); if (status) return status;
return nfs_ok;
}
/* a counted array of callback_sec_parms4 items */ static __be32
nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs)
{
u32 i, secflavor, nr_secflavs;
__be32 status;
/* callback_sec_params4 */ if (xdr_stream_decode_u32(argp->xdr, &nr_secflavs) < 0) return nfserr_bad_xdr; if (nr_secflavs)
cbs->flavor = (u32)(-1); else /* Is this legal? Be generous, take it to mean AUTH_NONE: */
cbs->flavor = 0;
for (i = 0; i < nr_secflavs; ++i) { if (xdr_stream_decode_u32(argp->xdr, &secflavor) < 0) return nfserr_bad_xdr; switch (secflavor) { case RPC_AUTH_NULL: /* void */ if (cbs->flavor == (u32)(-1))
cbs->flavor = RPC_AUTH_NULL; break; case RPC_AUTH_UNIX:
status = nfsd4_decode_authsys_parms(argp, cbs); if (status) return status; break; case RPC_AUTH_GSS:
status = nfsd4_decode_gss_cb_handles4(argp, cbs); if (status) return status; break; default: return nfserr_inval;
}
}
status = nfsd4_decode_stateid4(argp, &lock->lk_old_lock_stateid); if (status) return status; if (xdr_stream_decode_u32(argp->xdr, &lock->lk_old_lock_seqid) < 0) return nfserr_bad_xdr;
if (xdr_stream_decode_u32(argp->xdr, &w) < 0) return nfserr_bad_xdr;
*share_access = w & NFS4_SHARE_ACCESS_MASK;
*deleg_want = w & NFS4_SHARE_WANT_MASK; if (deleg_when)
*deleg_when = w & NFS4_SHARE_WHEN_MASK;
switch (w & NFS4_SHARE_ACCESS_MASK) { case NFS4_SHARE_ACCESS_READ: case NFS4_SHARE_ACCESS_WRITE: case NFS4_SHARE_ACCESS_BOTH: break; default: return nfserr_bad_xdr;
}
w &= ~NFS4_SHARE_ACCESS_MASK; if (!w) return nfs_ok; if (!argp->minorversion) return nfserr_bad_xdr; switch (w & NFS4_SHARE_WANT_TYPE_MASK) { case OPEN4_SHARE_ACCESS_WANT_NO_PREFERENCE: case OPEN4_SHARE_ACCESS_WANT_READ_DELEG: case OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG: case OPEN4_SHARE_ACCESS_WANT_ANY_DELEG: case OPEN4_SHARE_ACCESS_WANT_NO_DELEG: case OPEN4_SHARE_ACCESS_WANT_CANCEL: break; default: return nfserr_bad_xdr;
}
w &= ~NFS4_SHARE_WANT_MASK; if (!w) return nfs_ok;
if (!deleg_when) /* open_downgrade */ return nfserr_inval; switch (w) { case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL: case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED: case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL |
NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED): return nfs_ok;
} return nfserr_bad_xdr;
}
static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x)
{ if (xdr_stream_decode_u32(argp->xdr, x) < 0) return nfserr_bad_xdr; /* Note: unlike access bits, deny bits may be zero. */ if (*x & ~NFS4_SHARE_DENY_BOTH) return nfserr_bad_xdr;
if (xdr_stream_decode_u32(argp->xdr, &open->op_claim_type) < 0) return nfserr_bad_xdr; switch (open->op_claim_type) { case NFS4_OPEN_CLAIM_NULL: case NFS4_OPEN_CLAIM_DELEGATE_PREV:
status = nfsd4_decode_component4(argp, &open->op_fname,
&open->op_fnamelen); if (status) return status; break; case NFS4_OPEN_CLAIM_PREVIOUS: if (xdr_stream_decode_u32(argp->xdr, &open->op_delegate_type) < 0) return nfserr_bad_xdr; break; case NFS4_OPEN_CLAIM_DELEGATE_CUR:
status = nfsd4_decode_stateid4(argp, &open->op_delegate_stateid); if (status) return status;
status = nfsd4_decode_component4(argp, &open->op_fname,
&open->op_fnamelen); if (status) return status; break; case NFS4_OPEN_CLAIM_FH: case NFS4_OPEN_CLAIM_DELEG_PREV_FH: if (argp->minorversion < 1) return nfserr_bad_xdr; /* void */ break; case NFS4_OPEN_CLAIM_DELEG_CUR_FH: if (argp->minorversion < 1) return nfserr_bad_xdr;
status = nfsd4_decode_stateid4(argp, &open->op_delegate_stateid); if (status) return status; break; default: return nfserr_bad_xdr;
}
if (argp->minorversion >= 1) return nfserr_notsupp;
status = nfsd4_decode_stateid4(argp, &open_conf->oc_req_stateid); if (status) return status; if (xdr_stream_decode_u32(argp->xdr, &open_conf->oc_seqid) < 0) return nfserr_bad_xdr;
status = nfsd4_decode_bitmap4(argp, exid->spo_must_enforce,
ARRAY_SIZE(exid->spo_must_enforce)); if (status) return nfserr_bad_xdr;
status = nfsd4_decode_bitmap4(argp, exid->spo_must_allow,
ARRAY_SIZE(exid->spo_must_allow)); if (status) return nfserr_bad_xdr;
return nfs_ok;
}
/* * This implementation currently does not support SP4_SSV. * This decoder simply skips over these arguments.
*/ static noinline __be32
nfsd4_decode_ssv_sp_parms(struct nfsd4_compoundargs *argp, struct nfsd4_exchange_id *exid)
{
u32 count, window, num_gss_handles;
__be32 status;
/* ssp_ops */
status = nfsd4_decode_state_protect_ops(argp, exid); if (status) return status;
/* ssp_hash_algs<> */ if (xdr_stream_decode_u32(argp->xdr, &count) < 0) return nfserr_bad_xdr; while (count--) {
status = nfsd4_decode_ignored_string(argp, 0); if (status) return status;
}
/* ssp_encr_algs<> */ if (xdr_stream_decode_u32(argp->xdr, &count) < 0) return nfserr_bad_xdr; while (count--) {
status = nfsd4_decode_ignored_string(argp, 0); if (status) return status;
}
if (xdr_stream_decode_u32(argp->xdr, &window) < 0) return nfserr_bad_xdr; if (xdr_stream_decode_u32(argp->xdr, &num_gss_handles) < 0) return nfserr_bad_xdr;
if (xdr_stream_decode_u32(argp->xdr, &exid->spa_how) < 0) return nfserr_bad_xdr; switch (exid->spa_how) { case SP4_NONE: break; case SP4_MACH_CRED:
status = nfsd4_decode_state_protect_ops(argp, exid); if (status) return status; break; case SP4_SSV:
status = nfsd4_decode_ssv_sp_parms(argp, exid); if (status) return status; break; default: return nfserr_bad_xdr;
}
if (xdr_stream_decode_u32(argp->xdr, &count) < 0) return nfserr_bad_xdr; switch (count) { case 0: break; case 1: /* Note that RFC 8881 places no length limit on * nii_domain, but this implementation permits no
* more than NFS4_OPAQUE_LIMIT bytes */
status = nfsd4_decode_opaque(argp, &exid->nii_domain); if (status) return status; /* Note that RFC 8881 places no length limit on * nii_name, but this implementation permits no
* more than NFS4_OPAQUE_LIMIT bytes */
status = nfsd4_decode_opaque(argp, &exid->nii_name); if (status) return status;
status = nfsd4_decode_nfstime4(argp, &exid->nii_time); if (status) return status; break; default: return nfserr_bad_xdr;
}
memset(test_stateid, 0, sizeof(*test_stateid)); if (xdr_stream_decode_u32(argp->xdr, &test_stateid->ts_num_ids) < 0) return nfserr_bad_xdr;
INIT_LIST_HEAD(&test_stateid->ts_stateid_list); for (i = 0; i < test_stateid->ts_num_ids; i++) {
stateid = svcxdr_tmpalloc(argp, sizeof(*stateid)); if (!stateid) return nfserr_jukebox;
INIT_LIST_HEAD(&stateid->ts_id_list);
list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list);
status = nfsd4_decode_stateid4(argp, &stateid->ts_id_stateid); if (status) return status;
}
status = nfsd4_decode_stateid4(argp, &clone->cl_src_stateid); if (status) return status;
status = nfsd4_decode_stateid4(argp, &clone->cl_dst_stateid); if (status) return status; if (xdr_stream_decode_u64(argp->xdr, &clone->cl_src_pos) < 0) return nfserr_bad_xdr; if (xdr_stream_decode_u64(argp->xdr, &clone->cl_dst_pos) < 0) return nfserr_bad_xdr; if (xdr_stream_decode_u64(argp->xdr, &clone->cl_count) < 0) return nfserr_bad_xdr;
return nfs_ok;
}
/* * XDR data that is more than PAGE_SIZE in size is normally part of a * read or write. However, the size of extended attributes is limited * by the maximum request size, and then further limited by the underlying * filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX * is 64k). Since there is no kvec- or page-based interface to xattrs, * and we're not dealing with contiguous pages, we need to do some copying.
*/
if (buflen <= head->iov_len) { /* * We're in luck, the head has enough space. Just return * the head, no need for copying.
*/
*bufp = head->iov_base; return 0;
}
tmp = svcxdr_tmpalloc(argp, buflen); if (tmp == NULL) return nfserr_jukebox;
while (buflen > 0) {
len = min_t(u32, buflen, PAGE_SIZE);
memcpy(dp, page_address(*pages), len);
buflen -= len;
dp += len;
pages++;
}
*bufp = tmp; return 0;
}
/* * Get a user extended attribute name from the XDR buffer. * It will not have the "user." prefix, so prepend it. * Lastly, check for nul characters in the name.
*/ static __be32
nfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep)
{ char *name, *sp, *dp;
u32 namelen, cnt;
__be32 *p;
if (xdr_stream_decode_u32(argp->xdr, &namelen) < 0) return nfserr_bad_xdr; if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) return nfserr_nametoolong; if (namelen == 0) return nfserr_bad_xdr;
p = xdr_inline_decode(argp->xdr, namelen); if (!p) return nfserr_bad_xdr;
name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1); if (!name) return nfserr_jukebox;
memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
/* * Copy the extended attribute name over while checking for 0 * characters.
*/
sp = (char *)p;
dp = name + XATTR_USER_PREFIX_LEN;
cnt = namelen;
while (cnt-- > 0) { if (*sp == '\0') return nfserr_bad_xdr;
*dp++ = *sp++;
}
*dp = '\0';
*namep = name;
return nfs_ok;
}
/* * A GETXATTR op request comes without a length specifier. We just set the * maximum length for the reply based on XATTR_SIZE_MAX and the maximum * channel reply size. nfsd_getxattr will probe the length of the xattr, * check it against getxa_len, and allocate + return the value.
*/ static __be32
nfsd4_decode_getxattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
{ struct nfsd4_getxattr *getxattr = &u->getxattr;
__be32 status;
u32 maxcount;
memset(getxattr, 0, sizeof(*getxattr));
status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name); if (status) return status;
if (xdr_stream_decode_u64(argp->xdr, &listxattrs->lsxa_cookie) < 0) return nfserr_bad_xdr;
/* * If the cookie is too large to have even one user.x attribute * plus trailing '\0' left in a maximum size buffer, it's invalid.
*/ if (listxattrs->lsxa_cookie >=
(XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2))) return nfserr_badcookie;
if (xdr_stream_decode_u32(argp->xdr, &maxcount) < 0) return nfserr_bad_xdr; if (maxcount < 8) /* Always need at least 2 words (length and one character) */ return nfserr_inval;
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.