freebsd-nq/module/spl/spl-xdr.c
Brian Behlendorf 8d9a23e82c Retire legacy debugging infrastructure
When the SPL was originally written Linux tracepoints were still
in their infancy.  Therefore, an entire debugging subsystem was
added to facilite tracing which served us well for many years.

Now that Linux tracepoints have matured they provide all the
functionality of the previous tracing subsystem.  Rather than
maintain parallel functionality it makes sense to fully adopt
tracepoints.  Therefore, this patch retires the legacy debugging
infrastructure.

See zfsonlinux/zfs@bc9f413 for the tracepoint changes.

Signed-off-by: Ned Bass <bass6@llnl.gov>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #408
2014-11-19 10:35:07 -08:00

515 lines
13 KiB
C

/*****************************************************************************\
* Copyright (c) 2008-2010 Sun Microsystems, Inc.
* Written by Ricardo Correia <Ricardo.M.Correia@Sun.COM>
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting Layer (SPL) XDR Implementation.
\*****************************************************************************/
#include <linux/string.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/types.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
/*
* SPL's XDR mem implementation.
*
* This is used by libnvpair to serialize/deserialize the name-value pair data
* structures into byte arrays in a well-defined and portable manner.
*
* These data structures are used by the DMU/ZFS to flexibly manipulate various
* information in memory and later serialize it/deserialize it to disk.
* Examples of usages include the pool configuration, lists of pool and dataset
* properties, etc.
*
* Reference documentation for the XDR representation and XDR operations can be
* found in RFC 1832 and xdr(3), respectively.
*
* === Implementation shortcomings ===
*
* It is assumed that the following C types have the following sizes:
*
* char/unsigned char: 1 byte
* short/unsigned short: 2 bytes
* int/unsigned int: 4 bytes
* longlong_t/u_longlong_t: 8 bytes
*
* The C standard allows these types to be larger (and in the case of ints,
* shorter), so if that is the case on some compiler/architecture, the build
* will fail (on purpose).
*
* If someone wants to fix the code to work properly on such environments, then:
*
* 1) Preconditions should be added to xdrmem_enc functions to make sure the
* caller doesn't pass arguments which exceed the expected range.
* 2) Functions which take signed integers should be changed to properly do
* sign extension.
* 3) For ints with less than 32 bits, well.. I suspect you'll have bigger
* problems than this implementation.
*
* It is also assumed that:
*
* 1) Chars have 8 bits.
* 2) We can always do 32-bit-aligned int memory accesses and byte-aligned
* memcpy, memset and memcmp.
* 3) Arrays passed to xdr_array() are packed and the compiler/architecture
* supports element-sized-aligned memory accesses.
* 4) Negative integers are natively stored in two's complement binary
* representation.
*
* No checks are done for the 4 assumptions above, though.
*
* === Caller expectations ===
*
* Existing documentation does not describe the semantics of XDR operations very
* well. Therefore, some assumptions about failure semantics will be made and
* will be described below:
*
* 1) If any encoding operation fails (e.g., due to lack of buffer space), the
* the stream should be considered valid only up to the encoding operation
* previous to the one that first failed. However, the stream size as returned
* by xdr_control() cannot be considered to be strictly correct (it may be
* bigger).
*
* Putting it another way, if there is an encoding failure it's undefined
* whether anything is added to the stream in that operation and therefore
* neither xdr_control() nor future encoding operations on the same stream can
* be relied upon to produce correct results.
*
* 2) If a decoding operation fails, it's undefined whether anything will be
* decoded into passed buffers/pointers during that operation, or what the
* values on those buffers will look like.
*
* Future decoding operations on the same stream will also have similar
* undefined behavior.
*
* 3) When the first decoding operation fails it is OK to trust the results of
* previous decoding operations on the same stream, as long as the caller
* expects a failure to be possible (e.g. due to end-of-stream).
*
* However, this is highly discouraged because the caller should know the
* stream size and should be coded to expect any decoding failure to be data
* corruption due to hardware, accidental or even malicious causes, which should
* be handled gracefully in all cases.
*
* In very rare situations where there are strong reasons to believe the data
* can be trusted to be valid and non-tampered with, then the caller may assume
* a decoding failure to be a bug (e.g. due to mismatched data types) and may
* fail non-gracefully.
*
* 4) Non-zero padding bytes will cause the decoding operation to fail.
*
* 5) Zero bytes on string types will also cause the decoding operation to fail.
*
* 6) It is assumed that either the pointer to the stream buffer given by the
* caller is 32-bit aligned or the architecture supports non-32-bit-aligned int
* memory accesses.
*
* 7) The stream buffer and encoding/decoding buffers/ptrs should not overlap.
*
* 8) If a caller passes pointers to non-kernel memory (e.g., pointers to user
* space or MMIO space), the computer may explode.
*/
static struct xdr_ops xdrmem_encode_ops;
static struct xdr_ops xdrmem_decode_ops;
void
xdrmem_create(XDR *xdrs, const caddr_t addr, const uint_t size,
const enum xdr_op op)
{
switch (op) {
case XDR_ENCODE:
xdrs->x_ops = &xdrmem_encode_ops;
break;
case XDR_DECODE:
xdrs->x_ops = &xdrmem_decode_ops;
break;
default:
xdrs->x_ops = NULL; /* Let the caller know we failed */
return;
}
xdrs->x_op = op;
xdrs->x_addr = addr;
xdrs->x_addr_end = addr + size;
if (xdrs->x_addr_end < xdrs->x_addr) {
xdrs->x_ops = NULL;
}
}
EXPORT_SYMBOL(xdrmem_create);
static bool_t
xdrmem_control(XDR *xdrs, int req, void *info)
{
struct xdr_bytesrec *rec = (struct xdr_bytesrec *) info;
if (req != XDR_GET_BYTES_AVAIL)
return FALSE;
rec->xc_is_last_record = TRUE; /* always TRUE in xdrmem streams */
rec->xc_num_avail = xdrs->x_addr_end - xdrs->x_addr;
return TRUE;
}
static bool_t
xdrmem_enc_bytes(XDR *xdrs, caddr_t cp, const uint_t cnt)
{
uint_t size = roundup(cnt, 4);
uint_t pad;
if (size < cnt)
return FALSE; /* Integer overflow */
if (xdrs->x_addr > xdrs->x_addr_end)
return FALSE;
if (xdrs->x_addr_end - xdrs->x_addr < size)
return FALSE;
memcpy(xdrs->x_addr, cp, cnt);
xdrs->x_addr += cnt;
pad = size - cnt;
if (pad > 0) {
memset(xdrs->x_addr, 0, pad);
xdrs->x_addr += pad;
}
return TRUE;
}
static bool_t
xdrmem_dec_bytes(XDR *xdrs, caddr_t cp, const uint_t cnt)
{
static uint32_t zero = 0;
uint_t size = roundup(cnt, 4);
uint_t pad;
if (size < cnt)
return FALSE; /* Integer overflow */
if (xdrs->x_addr > xdrs->x_addr_end)
return FALSE;
if (xdrs->x_addr_end - xdrs->x_addr < size)
return FALSE;
memcpy(cp, xdrs->x_addr, cnt);
xdrs->x_addr += cnt;
pad = size - cnt;
if (pad > 0) {
/* An inverted memchr() would be useful here... */
if (memcmp(&zero, xdrs->x_addr, pad) != 0)
return FALSE;
xdrs->x_addr += pad;
}
return TRUE;
}
static bool_t
xdrmem_enc_uint32(XDR *xdrs, uint32_t val)
{
if (xdrs->x_addr + sizeof(uint32_t) > xdrs->x_addr_end)
return FALSE;
*((uint32_t *) xdrs->x_addr) = cpu_to_be32(val);
xdrs->x_addr += sizeof(uint32_t);
return TRUE;
}
static bool_t
xdrmem_dec_uint32(XDR *xdrs, uint32_t *val)
{
if (xdrs->x_addr + sizeof(uint32_t) > xdrs->x_addr_end)
return FALSE;
*val = be32_to_cpu(*((uint32_t *) xdrs->x_addr));
xdrs->x_addr += sizeof(uint32_t);
return TRUE;
}
static bool_t
xdrmem_enc_char(XDR *xdrs, char *cp)
{
uint32_t val;
BUILD_BUG_ON(sizeof(char) != 1);
val = *((unsigned char *) cp);
return xdrmem_enc_uint32(xdrs, val);
}
static bool_t
xdrmem_dec_char(XDR *xdrs, char *cp)
{
uint32_t val;
BUILD_BUG_ON(sizeof(char) != 1);
if (!xdrmem_dec_uint32(xdrs, &val))
return FALSE;
/*
* If any of the 3 other bytes are non-zero then val will be greater
* than 0xff and we fail because according to the RFC, this block does
* not have a char encoded in it.
*/
if (val > 0xff)
return FALSE;
*((unsigned char *) cp) = val;
return TRUE;
}
static bool_t
xdrmem_enc_ushort(XDR *xdrs, unsigned short *usp)
{
BUILD_BUG_ON(sizeof(unsigned short) != 2);
return xdrmem_enc_uint32(xdrs, *usp);
}
static bool_t
xdrmem_dec_ushort(XDR *xdrs, unsigned short *usp)
{
uint32_t val;
BUILD_BUG_ON(sizeof(unsigned short) != 2);
if (!xdrmem_dec_uint32(xdrs, &val))
return FALSE;
/*
* Short ints are not in the RFC, but we assume similar logic as in
* xdrmem_dec_char().
*/
if (val > 0xffff)
return FALSE;
*usp = val;
return TRUE;
}
static bool_t
xdrmem_enc_uint(XDR *xdrs, unsigned *up)
{
BUILD_BUG_ON(sizeof(unsigned) != 4);
return xdrmem_enc_uint32(xdrs, *up);
}
static bool_t
xdrmem_dec_uint(XDR *xdrs, unsigned *up)
{
BUILD_BUG_ON(sizeof(unsigned) != 4);
return xdrmem_dec_uint32(xdrs, (uint32_t *) up);
}
static bool_t
xdrmem_enc_ulonglong(XDR *xdrs, u_longlong_t *ullp)
{
BUILD_BUG_ON(sizeof(u_longlong_t) != 8);
if (!xdrmem_enc_uint32(xdrs, *ullp >> 32))
return FALSE;
return xdrmem_enc_uint32(xdrs, *ullp & 0xffffffff);
}
static bool_t
xdrmem_dec_ulonglong(XDR *xdrs, u_longlong_t *ullp)
{
uint32_t low, high;
BUILD_BUG_ON(sizeof(u_longlong_t) != 8);
if (!xdrmem_dec_uint32(xdrs, &high))
return FALSE;
if (!xdrmem_dec_uint32(xdrs, &low))
return FALSE;
*ullp = ((u_longlong_t) high << 32) | low;
return TRUE;
}
static bool_t
xdr_enc_array(XDR *xdrs, caddr_t *arrp, uint_t *sizep, const uint_t maxsize,
const uint_t elsize, const xdrproc_t elproc)
{
uint_t i;
caddr_t addr = *arrp;
if (*sizep > maxsize || *sizep > UINT_MAX / elsize)
return FALSE;
if (!xdrmem_enc_uint(xdrs, sizep))
return FALSE;
for (i = 0; i < *sizep; i++) {
if (!elproc(xdrs, addr))
return FALSE;
addr += elsize;
}
return TRUE;
}
static bool_t
xdr_dec_array(XDR *xdrs, caddr_t *arrp, uint_t *sizep, const uint_t maxsize,
const uint_t elsize, const xdrproc_t elproc)
{
uint_t i, size;
bool_t alloc = FALSE;
caddr_t addr;
if (!xdrmem_dec_uint(xdrs, sizep))
return FALSE;
size = *sizep;
if (size > maxsize || size > UINT_MAX / elsize)
return FALSE;
/*
* The Solaris man page says: "If *arrp is NULL when decoding,
* xdr_array() allocates memory and *arrp points to it".
*/
if (*arrp == NULL) {
BUILD_BUG_ON(sizeof(uint_t) > sizeof(size_t));
*arrp = kmem_alloc(size * elsize, KM_NOSLEEP);
if (*arrp == NULL)
return FALSE;
alloc = TRUE;
}
addr = *arrp;
for (i = 0; i < size; i++) {
if (!elproc(xdrs, addr)) {
if (alloc)
kmem_free(*arrp, size * elsize);
return FALSE;
}
addr += elsize;
}
return TRUE;
}
static bool_t
xdr_enc_string(XDR *xdrs, char **sp, const uint_t maxsize)
{
size_t slen = strlen(*sp);
uint_t len;
if (slen > maxsize)
return FALSE;
len = slen;
if (!xdrmem_enc_uint(xdrs, &len))
return FALSE;
return xdrmem_enc_bytes(xdrs, *sp, len);
}
static bool_t
xdr_dec_string(XDR *xdrs, char **sp, const uint_t maxsize)
{
uint_t size;
bool_t alloc = FALSE;
if (!xdrmem_dec_uint(xdrs, &size))
return FALSE;
if (size > maxsize || size > UINT_MAX - 1)
return FALSE;
/*
* Solaris man page: "If *sp is NULL when decoding, xdr_string()
* allocates memory and *sp points to it".
*/
if (*sp == NULL) {
BUILD_BUG_ON(sizeof(uint_t) > sizeof(size_t));
*sp = kmem_alloc(size + 1, KM_NOSLEEP);
if (*sp == NULL)
return FALSE;
alloc = TRUE;
}
if (!xdrmem_dec_bytes(xdrs, *sp, size))
goto fail;
if (memchr(*sp, 0, size) != NULL)
goto fail;
(*sp)[size] = '\0';
return TRUE;
fail:
if (alloc)
kmem_free(*sp, size + 1);
return FALSE;
}
static struct xdr_ops xdrmem_encode_ops = {
.xdr_control = xdrmem_control,
.xdr_char = xdrmem_enc_char,
.xdr_u_short = xdrmem_enc_ushort,
.xdr_u_int = xdrmem_enc_uint,
.xdr_u_longlong_t = xdrmem_enc_ulonglong,
.xdr_opaque = xdrmem_enc_bytes,
.xdr_string = xdr_enc_string,
.xdr_array = xdr_enc_array
};
static struct xdr_ops xdrmem_decode_ops = {
.xdr_control = xdrmem_control,
.xdr_char = xdrmem_dec_char,
.xdr_u_short = xdrmem_dec_ushort,
.xdr_u_int = xdrmem_dec_uint,
.xdr_u_longlong_t = xdrmem_dec_ulonglong,
.xdr_opaque = xdrmem_dec_bytes,
.xdr_string = xdr_dec_string,
.xdr_array = xdr_dec_array
};