int nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp) { if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN))) return 0; argp->state = ntohl(*p++); memcpy(&argp->priv.data, p, sizeof(argp->priv.data)); p += XDR_QUADLEN(SM_PRIV_SIZE); return xdr_argsize_check(rqstp, p); }
int nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p, struct nfsd_readres *resp) { p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode); *p++ = htonl(resp->count); p += XDR_QUADLEN(resp->count); return xdr_ressize_check(rqstp, p); }
static inline u32 * xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle) { /* Zero handle first to allow comparisons */ memset(fhandle, 0, sizeof(*fhandle)); /* NFSv2 handles have a fixed length */ fhandle->size = NFS2_FHSIZE; memcpy(fhandle->data, p, NFS2_FHSIZE); return p + XDR_QUADLEN(NFS2_FHSIZE); }
static __be32 *__xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) { __be32 *p = xdr->p; __be32 *q = p + XDR_QUADLEN(nbytes); if (q > xdr->end || q < p) return NULL; xdr->p = q; return p; }
u32 * xdr_encode_array(u32 *p, const char *array, unsigned int len) { int quadlen = XDR_QUADLEN(len); p[quadlen] = 0; *p++ = htonl(len); memcpy(p, array, len); return p + quadlen; }
/* * Generic encode routines from fs/nfs/nfs4xdr.c */ static inline __be32 * xdr_writemem(__be32 *p, const void *ptr, int nbytes) { int tmp = XDR_QUADLEN(nbytes); if (!tmp) return p; p[tmp-1] = 0; memcpy(p, ptr, nbytes); return p + tmp; }
static inline u32 * xdr_decode_string2(u32 *p, char **string, unsigned int *len, unsigned int maxlen) { *len = ntohl(*p++); if (*len > maxlen) return NULL; *string = (char *) p; return p + XDR_QUADLEN(*len); }
u32 * xdr_decode_string_inplace(u32 *p, char **sp, int *lenp, int maxlen) { unsigned int len; if ((len = ntohl(*p++)) > maxlen) return NULL; *lenp = len; *sp = (char *) p; return p + XDR_QUADLEN(len); }
/* READLINK */ int nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p, struct nfsd3_readlinkres *resp) { p = encode_post_op_attr(rqstp, p, &resp->fh); if (resp->status == 0) { *p++ = htonl(resp->len); p += XDR_QUADLEN(resp->len); } return xdr_ressize_check(rqstp, p); }
u32 * xdr_decode_netobj(u32 *p, struct xdr_netobj *obj) { unsigned int len; if ((len = ntohl(*p++)) > XDR_MAX_NETOBJ) return NULL; obj->len = len; obj->data = (u8 *) p; return p + XDR_QUADLEN(len); }
int nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_writeargs *args) { unsigned int len, hdr, dlen; int v; p = decode_fh(p, &args->fh); if (!p) return 0; p++; /* beginoffset */ args->offset = ntohl(*p++); /* offset */ p++; /* totalcount */ len = args->len = ntohl(*p++); /* * The protocol specifies a maximum of 8192 bytes. */ if (len > NFSSVC_MAXBLKSIZE_V2) return 0; /* * Check to make sure that we got the right number of * bytes. */ hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len - hdr; /* * Round the length of the data which was specified up to * the next multiple of XDR units and then compare that * against the length which was actually received. * Note that when RPCSEC/GSS (for example) is used, the * data buffer can be padded so dlen might be larger * than required. It must never be smaller. */ if (dlen < XDR_QUADLEN(len)*4) return 0; rqstp->rq_vec[0].iov_base = (void*)p; rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; v = 0; while (len > rqstp->rq_vec[v].iov_len) { len -= rqstp->rq_vec[v].iov_len; v++; rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]); rqstp->rq_vec[v].iov_len = PAGE_SIZE; } rqstp->rq_vec[v].iov_len = len; args->vlen = v + 1; return 1; }
static inline u32 * xdr_decode_fhandle(u32 *p, struct nfs_fh *fh) { /* * Zero all nonused bytes */ memset((u8 *)fh, 0, sizeof(*fh)); if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) { memcpy(fh->data, p, fh->size); return p + XDR_QUADLEN(fh->size); } return NULL; }
/* READDIR */ int nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p, struct nfsd3_readdirres *resp) { p = encode_post_op_attr(rqstp, p, &resp->fh); if (resp->status == 0) { /* stupid readdir cookie */ memcpy(p, resp->verf, 8); p += 2; p += XDR_QUADLEN(resp->count); } return xdr_ressize_check(rqstp, p); }
static __be32 * nlm4_decode_fh(__be32 *p, struct nfs_fh *f) { memset(f->data, 0, sizeof(f->data)); f->size = ntohl(*p++); if (f->size > NFS_MAXFHSIZE) { dprintk("lockd: bad fhandle size %d (should be <=%d)\n", f->size, NFS_MAXFHSIZE); return NULL; } memcpy(f->data, p, f->size); return p + XDR_QUADLEN(f->size); }
static inline u32 * decode_fh(u32 *p, struct svc_fh *fhp) { int size; fh_init(fhp, NFS3_FHSIZE); size = ntohl(*p++); if (size > NFS3_FHSIZE) return NULL; memcpy(&fhp->fh_handle.fh_base, p, size); fhp->fh_handle.fh_size = size; return p + XDR_QUADLEN(size); }
/* READ */ int nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p, struct nfsd3_readres *resp) { p = encode_post_op_attr(rqstp, p, &resp->fh); if (resp->status == 0) { *p++ = htonl(resp->count); *p++ = htonl(resp->eof); *p++ = htonl(resp->count); /* xdr opaque count */ p += XDR_QUADLEN(resp->count); } return xdr_ressize_check(rqstp, p); }
int nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_writeargs *args = rqstp->rq_argp; unsigned int len, hdr, dlen; u32 max_blocksize = svc_max_payload(rqstp); struct kvec *head = rqstp->rq_arg.head; struct kvec *tail = rqstp->rq_arg.tail; p = decode_fh(p, &args->fh); if (!p) return 0; p = xdr_decode_hyper(p, &args->offset); args->count = ntohl(*p++); args->stable = ntohl(*p++); len = args->len = ntohl(*p++); if ((void *)p > head->iov_base + head->iov_len) return 0; /* * The count must equal the amount of data passed. */ if (args->count != args->len) return 0; /* * Check to make sure that we got the right number of * bytes. */ hdr = (void*)p - head->iov_base; dlen = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len - hdr; /* * Round the length of the data which was specified up to * the next multiple of XDR units and then compare that * against the length which was actually received. * Note that when RPCSEC/GSS (for example) is used, the * data buffer can be padded so dlen might be larger * than required. It must never be smaller. */ if (dlen < XDR_QUADLEN(len)*4) return 0; if (args->count > max_blocksize) { args->count = max_blocksize; len = args->len = max_blocksize; } args->first.iov_base = (void *)p; args->first.iov_len = head->iov_len - hdr; return 1; }
/* * CB_SEQUENCE4resok * * struct CB_SEQUENCE4resok { * sessionid4 csr_sessionid; * sequenceid4 csr_sequenceid; * slotid4 csr_slotid; * slotid4 csr_highest_slotid; * slotid4 csr_target_highest_slotid; * }; * * union CB_SEQUENCE4res switch (nfsstat4 csr_status) { * case NFS4_OK: * CB_SEQUENCE4resok csr_resok4; * default: * void; * }; * * Our current back channel implmentation supports a single backchannel * with a single slot. */ static int decode_cb_sequence4resok(struct xdr_stream *xdr, struct nfsd4_callback *cb) { struct nfsd4_session *session = cb->cb_clp->cl_cb_session; struct nfs4_sessionid id; int status; __be32 *p; u32 dummy; status = -ESERVERFAULT; /* * If the server returns different values for sessionID, slotID or * sequence number, the server is looney tunes. */ p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN + 4 + 4 + 4 + 4); if (unlikely(p == NULL)) goto out_overflow; memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN); if (memcmp(id.data, session->se_sessionid.data, NFS4_MAX_SESSIONID_LEN) != 0) { dprintk("NFS: %s Invalid session id\n", __func__); goto out; } p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN); dummy = be32_to_cpup(p++); if (dummy != session->se_cb_seq_nr) { dprintk("NFS: %s Invalid sequence number\n", __func__); goto out; } dummy = be32_to_cpup(p++); if (dummy != 0) { dprintk("NFS: %s Invalid slotid\n", __func__); goto out; } /* * FIXME: process highest slotid and target highest slotid */ status = 0; out: if (status) nfsd4_mark_cb_fault(cb->cb_clp, status); return status; out_overflow: print_overflow_msg(__func__, xdr); return -EIO; }
/** * xdr_encode_opaque_fixed - Encode fixed length opaque data * @p - pointer to current position in XDR buffer. * @ptr - pointer to data to encode (or NULL) * @nbytes - size of data. * * Copy the array of data of length nbytes at ptr to the XDR buffer * at position p, then align to the next 32-bit boundary by padding * with zero bytes (see RFC1832). * Note: if ptr is NULL, only the padding is performed. * * Returns the updated current XDR buffer position * */ u32 *xdr_encode_opaque_fixed(u32 *p, const void *ptr, unsigned int nbytes) { if (likely(nbytes != 0)) { unsigned int quadlen = XDR_QUADLEN(nbytes); unsigned int padding = (quadlen << 2) - nbytes; if (ptr != NULL) memcpy(p, ptr, nbytes); if (padding != 0) memset((char *)p + nbytes, 0, padding); p += quadlen; } return p; }
static __be32 * nlm_decode_fh(__be32 *p, struct nfs_fh *f) { unsigned int len; if ((len = ntohl(*p++)) != NFS2_FHSIZE) { dprintk("lockd: bad fhandle size %d (should be %d)\n", len, NFS2_FHSIZE); return NULL; } f->size = NFS2_FHSIZE; memset(f->data, 0, sizeof(f->data)); memcpy(f->data, p, NFS2_FHSIZE); return p + XDR_QUADLEN(NFS2_FHSIZE); }
int nfssvc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, struct nfsd_writeargs *args) { if (!(p = decode_fh(p, &args->fh))) return 0; p++; /* beginoffset */ args->offset = ntohl(*p++); /* offset */ p++; /* totalcount */ args->len = ntohl(*p++); args->data = (char *) p; p += XDR_QUADLEN(args->len); return xdr_argsize_check(rqstp, p); }
int nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, struct nfsd3_writeargs *args) { if (!(p = decode_fh(p, &args->fh)) || !(p = xdr_decode_hyper(p, &args->offset))) return 0; args->count = ntohl(*p++); args->stable = ntohl(*p++); args->len = ntohl(*p++); args->data = (char *) p; p += XDR_QUADLEN(args->len); return xdr_argsize_check(rqstp, p); }
static void encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp, struct nfs4_cb_compound_hdr *hdr) { __be32 *p; int len = dp->dl_fh.fh_size; RESERVE_SPACE(4); WRITE32(OP_CB_RECALL); encode_stateid(xdr, &dp->dl_stateid); RESERVE_SPACE(8 + (XDR_QUADLEN(len) << 2)); WRITE32(0); /* truncate optimization not implemented */ WRITE32(len); WRITEMEM(&dp->dl_fh.fh_base, len); hdr->nops++; }
/* * Our current back channel implmentation supports a single backchannel * with a single slot. */ static int decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb, struct rpc_rqst *rqstp) { struct nfsd4_session *ses = cb->cb_clp->cl_cb_session; struct nfs4_sessionid id; int status; u32 dummy; __be32 *p; if (cb->cb_minorversion == 0) return 0; status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE); if (status) return status; /* * If the server returns different values for sessionID, slotID or * sequence number, the server is looney tunes. */ status = -ESERVERFAULT; READ_BUF(NFS4_MAX_SESSIONID_LEN + 16); memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN); p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN); if (memcmp(id.data, ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) { dprintk("%s Invalid session id\n", __func__); goto out; } READ32(dummy); if (dummy != ses->se_cb_seq_nr) { dprintk("%s Invalid sequence number\n", __func__); goto out; } READ32(dummy); /* slotid must be 0 */ if (dummy != 0) { dprintk("%s Invalid slotid\n", __func__); goto out; } /* FIXME: process highest slotid and target highest slotid */ status = 0; out: return status; }
int nfssvc_encode_entry(void *ccdv, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) { struct readdir_cd *ccd = ccdv; struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common); __be32 *p = cd->buffer; int buflen, slen; /* dprintk("nfsd: entry(%.*s off %ld ino %ld)\n", namlen, name, offset, ino); */ if (offset > ~((u32) 0)) { cd->common.err = nfserr_fbig; return -EINVAL; } if (cd->offset) *cd->offset = htonl(offset); /* truncate filename */ namlen = min(namlen, NFS2_MAXNAMLEN); slen = XDR_QUADLEN(namlen); if ((buflen = cd->buflen - slen - 4) < 0) { cd->common.err = nfserr_toosmall; return -EINVAL; } if (ino > ~((u32) 0)) { cd->common.err = nfserr_fbig; return -EINVAL; } *p++ = xdr_one; /* mark entry present */ *p++ = htonl((u32) ino); /* file id */ p = xdr_encode_array(p, name, namlen);/* name length & name */ cd->offset = p; /* remember pointer */ *p++ = htonl(~0U); /* offset of next entry */ cd->buflen = buflen; cd->buffer = p; cd->common.err = nfs_ok; return 0; }
__be32 * nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus) { __be32 *p; p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) goto out_overflow; if (!ntohl(*p++)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) goto out_overflow; if (!ntohl(*p++)) return ERR_PTR(-EAGAIN); entry->eof = 1; return ERR_PTR(-EBADCOOKIE); } p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) goto out_overflow; entry->ino = ntohl(*p++); entry->len = ntohl(*p++); p = xdr_inline_decode(xdr, entry->len + 4); if (unlikely(!p)) goto out_overflow; entry->name = (const char *) p; p += XDR_QUADLEN(entry->len); entry->prev_cookie = entry->cookie; entry->cookie = ntohl(*p++); p = xdr_inline_peek(xdr, 8); if (p != NULL) entry->eof = !p[0] && p[1]; else entry->eof = 0; return p; out_overflow: print_overflow_msg(__func__, xdr); return ERR_PTR(-EIO); }
/* * Decode the result of a readdir call. * We're not really decoding anymore, we just leave the buffer untouched * and only check that it is syntactically correct. * The real decoding happens in nfs_decode_entry below, called directly * from nfs_readdir for each entry. */ static int nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) { struct iovec *iov = req->rq_rvec; int status, nr; u32 *end, *entry, len; if ((status = ntohl(*p++))) return -nfs_stat_to_errno(status); if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) { /* Unexpected reply header size. Punt. */ printk(KERN_WARNING "NFS: Odd RPC header size in readdirres reply\n"); return -errno_NFSERR_IO; } /* Get start and end address of XDR data */ p = (u32 *) iov[1].iov_base; end = (u32 *) ((u8 *) p + iov[1].iov_len); /* Get start and end of dirent buffer */ if (res->buffer != p) { printk(KERN_ERR "NFS: Bad result buffer in readdir\n"); return -errno_NFSERR_IO; } for (nr = 0; *p++; nr++) { entry = p - 1; p++; /* fileid */ len = ntohl(*p++); p += XDR_QUADLEN(len) + 1; /* name plus cookie */ if (len > NFS2_MAXNAMLEN) { printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)!\n", len); return -errno_NFSERR_IO; } if (p + 2 > end) { printk(KERN_NOTICE "NFS: short packet in readdir reply!\n"); entry[0] = entry[1] = 0; break; } } return nr; }
u32 * xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen) { unsigned int len; char *string; if ((len = ntohl(*p++)) > maxlen) return NULL; if (lenp) *lenp = len; if ((len % 4) != 0) { string = (char *) p; } else { string = (char *) (p - 1); memmove(string, p, len); } string[len] = '\0'; *sp = string; return p + XDR_QUADLEN(len); }
/* * To avoid a separate RDMA READ just for a handful of zero bytes, * RFC 5666 section 3.7 allows the client to omit the XDR zero pad * in chunk lists. */ static void rdma_fix_xdr_pad(struct xdr_buf *buf) { unsigned int page_len = buf->page_len; unsigned int size = (XDR_QUADLEN(page_len) << 2) - page_len; unsigned int offset, pg_no; char *p; if (size == 0) return; pg_no = page_len >> PAGE_SHIFT; offset = page_len & ~PAGE_MASK; p = page_address(buf->pages[pg_no]); memset(p + offset, 0, size); buf->page_len += size; buf->buflen += size; buf->len += size; }
u32 * nfs_decode_dirent(u32 *p, struct nfs_entry *entry, int plus) { if (!*p++) { if (!*p) return ERR_PTR(-EAGAIN); entry->eof = 1; return ERR_PTR(-EBADCOOKIE); } entry->ino = ntohl(*p++); entry->len = ntohl(*p++); entry->name = (const char *) p; p += XDR_QUADLEN(entry->len); entry->prev_cookie = entry->cookie; entry->cookie = ntohl(*p++); entry->eof = !p[0] && p[1]; return p; }