diff --git a/include/sys/nvpair.h b/include/sys/nvpair.h index 66362a9dbd41..6bec9702eb16 100644 --- a/include/sys/nvpair.h +++ b/include/sys/nvpair.h @@ -1,420 +1,422 @@ // SPDX-License-Identifier: CDDL-1.0 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2018 by Delphix. All rights reserved. */ #ifndef _SYS_NVPAIR_H #define _SYS_NVPAIR_H extern __attribute__((visibility("default"))) #include #include #include #ifdef __cplusplus extern "C" { #endif typedef enum { DATA_TYPE_DONTCARE = -1, DATA_TYPE_UNKNOWN = 0, DATA_TYPE_BOOLEAN, DATA_TYPE_BYTE, DATA_TYPE_INT16, DATA_TYPE_UINT16, DATA_TYPE_INT32, DATA_TYPE_UINT32, DATA_TYPE_INT64, DATA_TYPE_UINT64, DATA_TYPE_STRING, DATA_TYPE_BYTE_ARRAY, DATA_TYPE_INT16_ARRAY, DATA_TYPE_UINT16_ARRAY, DATA_TYPE_INT32_ARRAY, DATA_TYPE_UINT32_ARRAY, DATA_TYPE_INT64_ARRAY, DATA_TYPE_UINT64_ARRAY, DATA_TYPE_STRING_ARRAY, DATA_TYPE_HRTIME, DATA_TYPE_NVLIST, DATA_TYPE_NVLIST_ARRAY, DATA_TYPE_BOOLEAN_VALUE, DATA_TYPE_INT8, DATA_TYPE_UINT8, DATA_TYPE_BOOLEAN_ARRAY, DATA_TYPE_INT8_ARRAY, #if !defined(_KERNEL) && !defined(_STANDALONE) DATA_TYPE_UINT8_ARRAY, DATA_TYPE_DOUBLE #else DATA_TYPE_UINT8_ARRAY #endif } data_type_t; typedef struct nvpair { int32_t nvp_size; /* size of this nvpair */ int16_t nvp_name_sz; /* length of name string */ int16_t nvp_reserve; /* not used */ int32_t nvp_value_elem; /* number of elements for array types */ data_type_t nvp_type; /* type of value */ char nvp_name[]; /* name string */ /* aligned ptr array for string arrays */ /* aligned array of data for value */ } nvpair_t; /* nvlist header */ typedef struct nvlist { int32_t nvl_version; uint32_t nvl_nvflag; /* persistent flags */ uint64_t nvl_priv; /* ptr to private data if not packed */ uint32_t nvl_flag; int32_t nvl_pad; /* currently not used, for alignment */ } nvlist_t; /* nvp implementation version */ #define NV_VERSION 0 /* nvlist pack encoding */ #define NV_ENCODE_NATIVE 0 #define NV_ENCODE_XDR 1 /* nvlist persistent unique name flags, stored in nvl_nvflags */ #define NV_UNIQUE_NAME 0x1 #define NV_UNIQUE_NAME_TYPE 0x2 /* nvlist lookup pairs related flags */ #define NV_FLAG_NOENTOK 0x1 /* convenience macros */ #define NV_ALIGN(x) (((ulong_t)(x) + 7ul) & ~7ul) #define NV_ALIGN4(x) (((x) + 3) & ~3) #define NVP_SIZE(nvp) ((nvp)->nvp_size) #define NVP_NAME(nvp) ((nvp)->nvp_name) #define NVP_TYPE(nvp) ((nvp)->nvp_type) #define NVP_NELEM(nvp) ((nvp)->nvp_value_elem) #define NVP_VALUE(nvp) ((char *)(nvp) + NV_ALIGN(sizeof (nvpair_t) \ + (nvp)->nvp_name_sz)) #define NVL_VERSION(nvl) ((nvl)->nvl_version) #define NVL_SIZE(nvl) ((nvl)->nvl_size) #define NVL_FLAG(nvl) ((nvl)->nvl_flag) /* NV allocator framework */ typedef struct nv_alloc_ops nv_alloc_ops_t; typedef struct nv_alloc { const nv_alloc_ops_t *nva_ops; void *nva_arg; } nv_alloc_t; struct nv_alloc_ops { int (*nv_ao_init)(nv_alloc_t *, va_list); void (*nv_ao_fini)(nv_alloc_t *); void *(*nv_ao_alloc)(nv_alloc_t *, size_t); void (*nv_ao_free)(nv_alloc_t *, void *, size_t); void (*nv_ao_reset)(nv_alloc_t *); }; _SYS_NVPAIR_H const nv_alloc_ops_t *const nv_fixed_ops; _SYS_NVPAIR_H nv_alloc_t *const nv_alloc_nosleep; #if defined(_KERNEL) _SYS_NVPAIR_H nv_alloc_t *const nv_alloc_sleep; _SYS_NVPAIR_H nv_alloc_t *const nv_alloc_pushpage; #endif _SYS_NVPAIR_H int nv_alloc_init(nv_alloc_t *, const nv_alloc_ops_t *, /* args */ ...); _SYS_NVPAIR_H void nv_alloc_reset(nv_alloc_t *); _SYS_NVPAIR_H void nv_alloc_fini(nv_alloc_t *); /* list management */ _SYS_NVPAIR_H int nvlist_alloc(nvlist_t **, uint_t, int); _SYS_NVPAIR_H void nvlist_free(nvlist_t *); _SYS_NVPAIR_H int nvlist_size(nvlist_t *, size_t *, int); _SYS_NVPAIR_H int nvlist_pack(nvlist_t *, char **, size_t *, int, int); _SYS_NVPAIR_H int nvlist_unpack(char *, size_t, nvlist_t **, int); _SYS_NVPAIR_H int nvlist_dup(const nvlist_t *, nvlist_t **, int); _SYS_NVPAIR_H int nvlist_merge(nvlist_t *, nvlist_t *, int); _SYS_NVPAIR_H uint_t nvlist_nvflag(nvlist_t *); _SYS_NVPAIR_H int nvlist_xalloc(nvlist_t **, uint_t, nv_alloc_t *); _SYS_NVPAIR_H int nvlist_xpack(nvlist_t *, char **, size_t *, int, nv_alloc_t *); _SYS_NVPAIR_H int nvlist_xunpack(char *, size_t, nvlist_t **, nv_alloc_t *); _SYS_NVPAIR_H int nvlist_xdup(const nvlist_t *, nvlist_t **, nv_alloc_t *); _SYS_NVPAIR_H nv_alloc_t *nvlist_lookup_nv_alloc(nvlist_t *); _SYS_NVPAIR_H int nvlist_add_nvpair(nvlist_t *, nvpair_t *); _SYS_NVPAIR_H int nvlist_add_boolean(nvlist_t *, const char *); _SYS_NVPAIR_H int nvlist_add_boolean_value(nvlist_t *, const char *, boolean_t); _SYS_NVPAIR_H int nvlist_add_byte(nvlist_t *, const char *, uchar_t); _SYS_NVPAIR_H int nvlist_add_int8(nvlist_t *, const char *, int8_t); _SYS_NVPAIR_H int nvlist_add_uint8(nvlist_t *, const char *, uint8_t); _SYS_NVPAIR_H int nvlist_add_int16(nvlist_t *, const char *, int16_t); _SYS_NVPAIR_H int nvlist_add_uint16(nvlist_t *, const char *, uint16_t); _SYS_NVPAIR_H int nvlist_add_int32(nvlist_t *, const char *, int32_t); _SYS_NVPAIR_H int nvlist_add_uint32(nvlist_t *, const char *, uint32_t); _SYS_NVPAIR_H int nvlist_add_int64(nvlist_t *, const char *, int64_t); _SYS_NVPAIR_H int nvlist_add_uint64(nvlist_t *, const char *, uint64_t); _SYS_NVPAIR_H int nvlist_add_string(nvlist_t *, const char *, const char *); _SYS_NVPAIR_H int nvlist_add_nvlist(nvlist_t *, const char *, const nvlist_t *); _SYS_NVPAIR_H int nvlist_add_boolean_array(nvlist_t *, const char *, const boolean_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_byte_array(nvlist_t *, const char *, const uchar_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_int8_array(nvlist_t *, const char *, const int8_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_uint8_array(nvlist_t *, const char *, const uint8_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_int16_array(nvlist_t *, const char *, const int16_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_uint16_array(nvlist_t *, const char *, const uint16_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_int32_array(nvlist_t *, const char *, const int32_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_uint32_array(nvlist_t *, const char *, const uint32_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_int64_array(nvlist_t *, const char *, const int64_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_uint64_array(nvlist_t *, const char *, const uint64_t *, uint_t); _SYS_NVPAIR_H int nvlist_add_string_array(nvlist_t *, const char *, const char * const *, uint_t); _SYS_NVPAIR_H int nvlist_add_nvlist_array(nvlist_t *, const char *, const nvlist_t * const *, uint_t); _SYS_NVPAIR_H int nvlist_add_hrtime(nvlist_t *, const char *, hrtime_t); #if !defined(_KERNEL) && !defined(_STANDALONE) _SYS_NVPAIR_H int nvlist_add_double(nvlist_t *, const char *, double); #endif _SYS_NVPAIR_H int nvlist_remove(nvlist_t *, const char *, data_type_t); _SYS_NVPAIR_H int nvlist_remove_all(nvlist_t *, const char *); _SYS_NVPAIR_H int nvlist_remove_nvpair(nvlist_t *, nvpair_t *); _SYS_NVPAIR_H int nvlist_lookup_boolean(const nvlist_t *, const char *); _SYS_NVPAIR_H int nvlist_lookup_boolean_value(const nvlist_t *, const char *, boolean_t *); _SYS_NVPAIR_H int nvlist_lookup_byte(const nvlist_t *, const char *, uchar_t *); _SYS_NVPAIR_H int nvlist_lookup_int8(const nvlist_t *, const char *, int8_t *); _SYS_NVPAIR_H int nvlist_lookup_uint8(const nvlist_t *, const char *, uint8_t *); _SYS_NVPAIR_H int nvlist_lookup_int16(const nvlist_t *, const char *, int16_t *); _SYS_NVPAIR_H int nvlist_lookup_uint16(const nvlist_t *, const char *, uint16_t *); _SYS_NVPAIR_H int nvlist_lookup_int32(const nvlist_t *, const char *, int32_t *); _SYS_NVPAIR_H int nvlist_lookup_uint32(const nvlist_t *, const char *, uint32_t *); _SYS_NVPAIR_H int nvlist_lookup_int64(const nvlist_t *, const char *, int64_t *); _SYS_NVPAIR_H int nvlist_lookup_uint64(const nvlist_t *, const char *, uint64_t *); _SYS_NVPAIR_H int nvlist_lookup_string(const nvlist_t *, const char *, const char **); _SYS_NVPAIR_H int nvlist_lookup_nvlist(nvlist_t *, const char *, nvlist_t **); _SYS_NVPAIR_H int nvlist_lookup_boolean_array(nvlist_t *, const char *, boolean_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_byte_array(nvlist_t *, const char *, uchar_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_int8_array(nvlist_t *, const char *, int8_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_uint8_array(nvlist_t *, const char *, uint8_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_int16_array(nvlist_t *, const char *, int16_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_uint16_array(nvlist_t *, const char *, uint16_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_int32_array(nvlist_t *, const char *, int32_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_uint32_array(nvlist_t *, const char *, uint32_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_int64_array(nvlist_t *, const char *, int64_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_uint64_array(nvlist_t *, const char *, uint64_t **, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_string_array(nvlist_t *, const char *, char ***, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_nvlist_array(nvlist_t *, const char *, nvlist_t ***, uint_t *); _SYS_NVPAIR_H int nvlist_lookup_hrtime(nvlist_t *, const char *, hrtime_t *); _SYS_NVPAIR_H int nvlist_lookup_pairs(nvlist_t *, int, ...); #if !defined(_KERNEL) && !defined(_STANDALONE) _SYS_NVPAIR_H int nvlist_lookup_double(const nvlist_t *, const char *, double *); #endif +_SYS_NVPAIR_H int nvlist_snprintf(char *, size_t, nvlist_t *, int); + _SYS_NVPAIR_H int nvlist_lookup_nvpair(nvlist_t *, const char *, nvpair_t **); _SYS_NVPAIR_H int nvlist_lookup_nvpair_embedded_index(nvlist_t *, const char *, nvpair_t **, int *, const char **); _SYS_NVPAIR_H boolean_t nvlist_exists(const nvlist_t *, const char *); _SYS_NVPAIR_H boolean_t nvlist_empty(const nvlist_t *); /* processing nvpair */ _SYS_NVPAIR_H nvpair_t *nvlist_next_nvpair(nvlist_t *, const nvpair_t *); _SYS_NVPAIR_H nvpair_t *nvlist_prev_nvpair(nvlist_t *, const nvpair_t *); _SYS_NVPAIR_H const char *nvpair_name(const nvpair_t *); _SYS_NVPAIR_H data_type_t nvpair_type(const nvpair_t *); _SYS_NVPAIR_H int nvpair_type_is_array(const nvpair_t *); _SYS_NVPAIR_H int nvpair_value_boolean_value(const nvpair_t *, boolean_t *); _SYS_NVPAIR_H int nvpair_value_byte(const nvpair_t *, uchar_t *); _SYS_NVPAIR_H int nvpair_value_int8(const nvpair_t *, int8_t *); _SYS_NVPAIR_H int nvpair_value_uint8(const nvpair_t *, uint8_t *); _SYS_NVPAIR_H int nvpair_value_int16(const nvpair_t *, int16_t *); _SYS_NVPAIR_H int nvpair_value_uint16(const nvpair_t *, uint16_t *); _SYS_NVPAIR_H int nvpair_value_int32(const nvpair_t *, int32_t *); _SYS_NVPAIR_H int nvpair_value_uint32(const nvpair_t *, uint32_t *); _SYS_NVPAIR_H int nvpair_value_int64(const nvpair_t *, int64_t *); _SYS_NVPAIR_H int nvpair_value_uint64(const nvpair_t *, uint64_t *); _SYS_NVPAIR_H int nvpair_value_string(const nvpair_t *, const char **); _SYS_NVPAIR_H int nvpair_value_nvlist(nvpair_t *, nvlist_t **); _SYS_NVPAIR_H int nvpair_value_boolean_array(nvpair_t *, boolean_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_byte_array(nvpair_t *, uchar_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_int8_array(nvpair_t *, int8_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_uint8_array(nvpair_t *, uint8_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_int16_array(nvpair_t *, int16_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_uint16_array(nvpair_t *, uint16_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_int32_array(nvpair_t *, int32_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_uint32_array(nvpair_t *, uint32_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_int64_array(nvpair_t *, int64_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_uint64_array(nvpair_t *, uint64_t **, uint_t *); _SYS_NVPAIR_H int nvpair_value_string_array(nvpair_t *, const char ***, uint_t *); _SYS_NVPAIR_H int nvpair_value_nvlist_array(nvpair_t *, nvlist_t ***, uint_t *); _SYS_NVPAIR_H int nvpair_value_hrtime(nvpair_t *, hrtime_t *); #if !defined(_KERNEL) && !defined(_STANDALONE) _SYS_NVPAIR_H int nvpair_value_double(const nvpair_t *, double *); #endif _SYS_NVPAIR_H nvlist_t *fnvlist_alloc(void); _SYS_NVPAIR_H void fnvlist_free(nvlist_t *); _SYS_NVPAIR_H size_t fnvlist_size(nvlist_t *); _SYS_NVPAIR_H char *fnvlist_pack(nvlist_t *, size_t *); _SYS_NVPAIR_H void fnvlist_pack_free(char *, size_t); _SYS_NVPAIR_H nvlist_t *fnvlist_unpack(char *, size_t); _SYS_NVPAIR_H nvlist_t *fnvlist_dup(const nvlist_t *); _SYS_NVPAIR_H void fnvlist_merge(nvlist_t *, nvlist_t *); _SYS_NVPAIR_H size_t fnvlist_num_pairs(nvlist_t *); _SYS_NVPAIR_H void fnvlist_add_boolean(nvlist_t *, const char *); _SYS_NVPAIR_H void fnvlist_add_boolean_value(nvlist_t *, const char *, boolean_t); _SYS_NVPAIR_H void fnvlist_add_byte(nvlist_t *, const char *, uchar_t); _SYS_NVPAIR_H void fnvlist_add_int8(nvlist_t *, const char *, int8_t); _SYS_NVPAIR_H void fnvlist_add_uint8(nvlist_t *, const char *, uint8_t); _SYS_NVPAIR_H void fnvlist_add_int16(nvlist_t *, const char *, int16_t); _SYS_NVPAIR_H void fnvlist_add_uint16(nvlist_t *, const char *, uint16_t); _SYS_NVPAIR_H void fnvlist_add_int32(nvlist_t *, const char *, int32_t); _SYS_NVPAIR_H void fnvlist_add_uint32(nvlist_t *, const char *, uint32_t); _SYS_NVPAIR_H void fnvlist_add_int64(nvlist_t *, const char *, int64_t); _SYS_NVPAIR_H void fnvlist_add_uint64(nvlist_t *, const char *, uint64_t); _SYS_NVPAIR_H void fnvlist_add_string(nvlist_t *, const char *, const char *); _SYS_NVPAIR_H void fnvlist_add_nvlist(nvlist_t *, const char *, nvlist_t *); _SYS_NVPAIR_H void fnvlist_add_nvpair(nvlist_t *, nvpair_t *); _SYS_NVPAIR_H void fnvlist_add_boolean_array(nvlist_t *, const char *, const boolean_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_byte_array(nvlist_t *, const char *, const uchar_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_int8_array(nvlist_t *, const char *, const int8_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_uint8_array(nvlist_t *, const char *, const uint8_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_int16_array(nvlist_t *, const char *, const int16_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_uint16_array(nvlist_t *, const char *, const uint16_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_int32_array(nvlist_t *, const char *, const int32_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_uint32_array(nvlist_t *, const char *, const uint32_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_int64_array(nvlist_t *, const char *, const int64_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_uint64_array(nvlist_t *, const char *, const uint64_t *, uint_t); _SYS_NVPAIR_H void fnvlist_add_string_array(nvlist_t *, const char *, const char * const *, uint_t); _SYS_NVPAIR_H void fnvlist_add_nvlist_array(nvlist_t *, const char *, const nvlist_t * const *, uint_t); _SYS_NVPAIR_H void fnvlist_remove(nvlist_t *, const char *); _SYS_NVPAIR_H void fnvlist_remove_nvpair(nvlist_t *, nvpair_t *); _SYS_NVPAIR_H nvpair_t *fnvlist_lookup_nvpair(nvlist_t *, const char *); _SYS_NVPAIR_H boolean_t fnvlist_lookup_boolean(const nvlist_t *, const char *); _SYS_NVPAIR_H boolean_t fnvlist_lookup_boolean_value(const nvlist_t *, const char *); _SYS_NVPAIR_H uchar_t fnvlist_lookup_byte(const nvlist_t *, const char *); _SYS_NVPAIR_H int8_t fnvlist_lookup_int8(const nvlist_t *, const char *); _SYS_NVPAIR_H int16_t fnvlist_lookup_int16(const nvlist_t *, const char *); _SYS_NVPAIR_H int32_t fnvlist_lookup_int32(const nvlist_t *, const char *); _SYS_NVPAIR_H int64_t fnvlist_lookup_int64(const nvlist_t *, const char *); _SYS_NVPAIR_H uint8_t fnvlist_lookup_uint8(const nvlist_t *, const char *); _SYS_NVPAIR_H uint16_t fnvlist_lookup_uint16(const nvlist_t *, const char *); _SYS_NVPAIR_H uint32_t fnvlist_lookup_uint32(const nvlist_t *, const char *); _SYS_NVPAIR_H uint64_t fnvlist_lookup_uint64(const nvlist_t *, const char *); _SYS_NVPAIR_H const char *fnvlist_lookup_string(const nvlist_t *, const char *); _SYS_NVPAIR_H nvlist_t *fnvlist_lookup_nvlist(nvlist_t *, const char *); _SYS_NVPAIR_H boolean_t *fnvlist_lookup_boolean_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H uchar_t *fnvlist_lookup_byte_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H int8_t *fnvlist_lookup_int8_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H uint8_t *fnvlist_lookup_uint8_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H int16_t *fnvlist_lookup_int16_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H uint16_t *fnvlist_lookup_uint16_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H int32_t *fnvlist_lookup_int32_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H uint32_t *fnvlist_lookup_uint32_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H int64_t *fnvlist_lookup_int64_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H uint64_t *fnvlist_lookup_uint64_array(nvlist_t *, const char *, uint_t *); _SYS_NVPAIR_H boolean_t fnvpair_value_boolean_value(const nvpair_t *nvp); _SYS_NVPAIR_H uchar_t fnvpair_value_byte(const nvpair_t *nvp); _SYS_NVPAIR_H int8_t fnvpair_value_int8(const nvpair_t *nvp); _SYS_NVPAIR_H int16_t fnvpair_value_int16(const nvpair_t *nvp); _SYS_NVPAIR_H int32_t fnvpair_value_int32(const nvpair_t *nvp); _SYS_NVPAIR_H int64_t fnvpair_value_int64(const nvpair_t *nvp); _SYS_NVPAIR_H uint8_t fnvpair_value_uint8(const nvpair_t *nvp); _SYS_NVPAIR_H uint16_t fnvpair_value_uint16(const nvpair_t *nvp); _SYS_NVPAIR_H uint32_t fnvpair_value_uint32(const nvpair_t *nvp); _SYS_NVPAIR_H uint64_t fnvpair_value_uint64(const nvpair_t *nvp); _SYS_NVPAIR_H const char *fnvpair_value_string(const nvpair_t *nvp); _SYS_NVPAIR_H nvlist_t *fnvpair_value_nvlist(nvpair_t *nvp); #ifdef __cplusplus } #endif #endif /* _SYS_NVPAIR_H */ diff --git a/include/sys/zfs_debug.h b/include/sys/zfs_debug.h index 871936da15f6..4d4cd4c39e97 100644 --- a/include/sys/zfs_debug.h +++ b/include/sys/zfs_debug.h @@ -1,116 +1,136 @@ // SPDX-License-Identifier: CDDL-1.0 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2019 by Delphix. All rights reserved. */ #ifndef _SYS_ZFS_DEBUG_H #define _SYS_ZFS_DEBUG_H #ifdef __cplusplus extern "C" { #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif +#include + extern int zfs_flags; extern int zfs_recover; extern int zfs_free_leak_on_eio; extern int zfs_dbgmsg_enable; #define ZFS_DEBUG_DPRINTF (1 << 0) #define ZFS_DEBUG_DBUF_VERIFY (1 << 1) #define ZFS_DEBUG_DNODE_VERIFY (1 << 2) #define ZFS_DEBUG_SNAPNAMES (1 << 3) #define ZFS_DEBUG_MODIFY (1 << 4) /* 1<<5 was previously used, try not to reuse */ #define ZFS_DEBUG_ZIO_FREE (1 << 6) #define ZFS_DEBUG_HISTOGRAM_VERIFY (1 << 7) #define ZFS_DEBUG_METASLAB_VERIFY (1 << 8) #define ZFS_DEBUG_SET_ERROR (1 << 9) #define ZFS_DEBUG_INDIRECT_REMAP (1 << 10) #define ZFS_DEBUG_TRIM (1 << 11) #define ZFS_DEBUG_LOG_SPACEMAP (1 << 12) #define ZFS_DEBUG_METASLAB_ALLOC (1 << 13) #define ZFS_DEBUG_BRT (1 << 14) #define ZFS_DEBUG_RAIDZ_RECONSTRUCT (1 << 15) #define ZFS_DEBUG_DDT (1 << 16) extern void __set_error(const char *file, const char *func, int line, int err); extern void __zfs_dbgmsg(char *buf); extern void __dprintf(boolean_t dprint, const char *file, const char *func, int line, const char *fmt, ...) __attribute__((format(printf, 5, 6))); /* * Some general principles for using zfs_dbgmsg(): * 1. We don't want to pollute the log with typically-irrelevant messages, * so don't print too many messages in the "normal" code path - O(1) * per txg. * 2. We want to know for sure what happened, so make the message specific * (e.g. *which* thing am I operating on). * 3. Do print a message when something unusual or unexpected happens * (e.g. error cases). * 4. Print a message when making user-initiated on-disk changes. * * Note that besides principle 1, another reason that we don't want to * use zfs_dbgmsg in high-frequency routines is the potential impact * that it can have on performance. */ #define zfs_dbgmsg(...) \ if (zfs_dbgmsg_enable) \ __dprintf(B_FALSE, __FILE__, __func__, __LINE__, __VA_ARGS__) #ifdef ZFS_DEBUG /* * To enable this: * * $ echo 1 >/sys/module/zfs/parameters/zfs_flags */ #define dprintf(...) \ if (zfs_flags & ZFS_DEBUG_DPRINTF) \ __dprintf(B_TRUE, __FILE__, __func__, __LINE__, __VA_ARGS__) #else #define dprintf(...) ((void)0) #endif /* ZFS_DEBUG */ extern void zfs_panic_recover(const char *fmt, ...); extern void zfs_dbgmsg_init(void); extern void zfs_dbgmsg_fini(void); +/* + * When printing an nvlist, print one beginning line with the file/func/line + * number and the text "nvlist :" followed by all the nvlist lines + * without the file/fun/line number. This makes the nvlist lines easy to read. + */ +#define zfs_dbgmsg_nvlist(nv) \ + if (zfs_dbgmsg_enable) { \ + zfs_dbgmsg("nvlist "#nv":"); \ + __zfs_dbgmsg_nvlist(nv); \ + } + +#define zfs_dbgmsg(...) \ + if (zfs_dbgmsg_enable) \ + __dprintf(B_FALSE, __FILE__, __func__, __LINE__, __VA_ARGS__) + + +extern void __zfs_dbgmsg_nvlist(nvlist_t *nv); + #ifndef _KERNEL extern int dprintf_find_string(const char *string); extern void zfs_dbgmsg_print(int fd, const char *tag); #endif #ifdef __cplusplus } #endif #endif /* _SYS_ZFS_DEBUG_H */ diff --git a/lib/libnvpair/libnvpair.abi b/lib/libnvpair/libnvpair.abi index e3eacb195463..a7f51f49487d 100644 --- a/lib/libnvpair/libnvpair.abi +++ b/lib/libnvpair/libnvpair.abi @@ -1,3437 +1,3443 @@ + + + + + + + + - - - - - - + + + + + - - - - - - - - - - - + + + + + + + + + + diff --git a/lib/libnvpair/libnvpair.c b/lib/libnvpair/libnvpair.c index fb26ed63845c..41be7c678d25 100644 --- a/lib/libnvpair/libnvpair.c +++ b/lib/libnvpair/libnvpair.c @@ -1,1293 +1,1166 @@ // SPDX-License-Identifier: CDDL-1.0 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #include #include #include #include #include #include "libnvpair.h" /* * libnvpair - A tools library for manipulating pairs. * * This library provides routines packing an unpacking nv pairs * for transporting data across process boundaries, transporting * between kernel and userland, and possibly saving onto disk files. */ /* * Print control structure. */ #define DEFINEOP(opname, vtype) \ struct { \ int (*op)(struct nvlist_prtctl *, void *, nvlist_t *, \ const char *, vtype); \ void *arg; \ } opname #define DEFINEARROP(opname, vtype) \ struct { \ int (*op)(struct nvlist_prtctl *, void *, nvlist_t *, \ const char *, vtype, uint_t); \ void *arg; \ } opname struct nvlist_printops { DEFINEOP(print_boolean, int); DEFINEOP(print_boolean_value, boolean_t); DEFINEOP(print_byte, uchar_t); DEFINEOP(print_int8, int8_t); DEFINEOP(print_uint8, uint8_t); DEFINEOP(print_int16, int16_t); DEFINEOP(print_uint16, uint16_t); DEFINEOP(print_int32, int32_t); DEFINEOP(print_uint32, uint32_t); DEFINEOP(print_int64, int64_t); DEFINEOP(print_uint64, uint64_t); DEFINEOP(print_double, double); DEFINEOP(print_string, const char *); DEFINEOP(print_hrtime, hrtime_t); DEFINEOP(print_nvlist, nvlist_t *); DEFINEARROP(print_boolean_array, boolean_t *); DEFINEARROP(print_byte_array, uchar_t *); DEFINEARROP(print_int8_array, int8_t *); DEFINEARROP(print_uint8_array, uint8_t *); DEFINEARROP(print_int16_array, int16_t *); DEFINEARROP(print_uint16_array, uint16_t *); DEFINEARROP(print_int32_array, int32_t *); DEFINEARROP(print_uint32_array, uint32_t *); DEFINEARROP(print_int64_array, int64_t *); DEFINEARROP(print_uint64_array, uint64_t *); DEFINEARROP(print_string_array, const char **); DEFINEARROP(print_nvlist_array, nvlist_t **); }; struct nvlist_prtctl { FILE *nvprt_fp; /* output destination */ enum nvlist_indent_mode nvprt_indent_mode; /* see above */ int nvprt_indent; /* absolute indent, or tab depth */ int nvprt_indentinc; /* indent or tab increment */ const char *nvprt_nmfmt; /* member name format, max one %s */ const char *nvprt_eomfmt; /* after member format, e.g. "\n" */ const char *nvprt_btwnarrfmt; /* between array members */ int nvprt_btwnarrfmt_nl; /* nvprt_eoamfmt includes newline? */ struct nvlist_printops *nvprt_dfltops; struct nvlist_printops *nvprt_custops; }; #define DFLTPRTOP(pctl, type) \ ((pctl)->nvprt_dfltops->print_##type.op) #define DFLTPRTOPARG(pctl, type) \ ((pctl)->nvprt_dfltops->print_##type.arg) #define CUSTPRTOP(pctl, type) \ ((pctl)->nvprt_custops->print_##type.op) #define CUSTPRTOPARG(pctl, type) \ ((pctl)->nvprt_custops->print_##type.arg) #define RENDER(pctl, type, nvl, name, val) \ { \ int done = 0; \ if ((pctl)->nvprt_custops && CUSTPRTOP(pctl, type)) { \ done = CUSTPRTOP(pctl, type)(pctl, \ CUSTPRTOPARG(pctl, type), nvl, name, val); \ } \ if (!done) { \ (void) DFLTPRTOP(pctl, type)(pctl, \ DFLTPRTOPARG(pctl, type), nvl, name, val); \ } \ (void) fprintf(pctl->nvprt_fp, "%s", pctl->nvprt_eomfmt); \ } #define ARENDER(pctl, type, nvl, name, arrp, count) \ { \ int done = 0; \ if ((pctl)->nvprt_custops && CUSTPRTOP(pctl, type)) { \ done = CUSTPRTOP(pctl, type)(pctl, \ CUSTPRTOPARG(pctl, type), nvl, name, arrp, count); \ } \ if (!done) { \ (void) DFLTPRTOP(pctl, type)(pctl, \ DFLTPRTOPARG(pctl, type), nvl, name, arrp, count); \ } \ (void) fprintf(pctl->nvprt_fp, "%s", pctl->nvprt_eomfmt); \ } static void nvlist_print_with_indent(nvlist_t *, nvlist_prtctl_t); /* * ====================================================================== * | | * | Indentation | * | | * ====================================================================== */ static void indent(nvlist_prtctl_t pctl, int onemore) { int depth; switch (pctl->nvprt_indent_mode) { case NVLIST_INDENT_ABS: (void) fprintf(pctl->nvprt_fp, "%*s", pctl->nvprt_indent + onemore * pctl->nvprt_indentinc, ""); break; case NVLIST_INDENT_TABBED: depth = pctl->nvprt_indent + onemore; while (depth-- > 0) (void) fprintf(pctl->nvprt_fp, "\t"); } } /* * ====================================================================== * | | * | Default nvlist member rendering functions. | * | | * ====================================================================== */ /* * Generate functions to print single-valued nvlist members. * * type_and_variant - suffix to form function name * vtype - C type for the member value * ptype - C type to cast value to for printing * vfmt - format string for pair value, e.g "%d" or "0x%llx" */ #define NVLIST_PRTFUNC(type_and_variant, vtype, ptype, vfmt) \ static int \ nvprint_##type_and_variant(nvlist_prtctl_t pctl, void *private, \ nvlist_t *nvl, const char *name, vtype value) \ { \ (void) private; \ (void) nvl; \ FILE *fp = pctl->nvprt_fp; \ indent(pctl, 1); \ (void) fprintf(fp, pctl->nvprt_nmfmt, name); \ (void) fprintf(fp, vfmt, (ptype)value); \ return (1); \ } /* * Workaround for GCC 12+ with UBSan enabled deficencies. * * GCC 12+ invoked with -fsanitize=undefined incorrectly reports the code * below as violating -Wformat-overflow. */ #if defined(__GNUC__) && !defined(__clang__) && \ defined(ZFS_UBSAN_ENABLED) && defined(HAVE_FORMAT_OVERFLOW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-overflow" #endif NVLIST_PRTFUNC(boolean, int, int, "%d") NVLIST_PRTFUNC(boolean_value, boolean_t, int, "%d") NVLIST_PRTFUNC(byte, uchar_t, uchar_t, "0x%2.2x") NVLIST_PRTFUNC(int8, int8_t, int, "%d") NVLIST_PRTFUNC(uint8, uint8_t, uint8_t, "0x%x") NVLIST_PRTFUNC(int16, int16_t, int16_t, "%d") NVLIST_PRTFUNC(uint16, uint16_t, uint16_t, "0x%x") NVLIST_PRTFUNC(int32, int32_t, int32_t, "%d") NVLIST_PRTFUNC(uint32, uint32_t, uint32_t, "0x%x") NVLIST_PRTFUNC(int64, int64_t, longlong_t, "%lld") NVLIST_PRTFUNC(uint64, uint64_t, u_longlong_t, "0x%llx") NVLIST_PRTFUNC(double, double, double, "0x%f") NVLIST_PRTFUNC(string, const char *, const char *, "%s") NVLIST_PRTFUNC(hrtime, hrtime_t, hrtime_t, "0x%llx") #if defined(__GNUC__) && !defined(__clang__) && \ defined(ZFS_UBSAN_ENABLED) && defined(HAVE_FORMAT_OVERFLOW) #pragma GCC diagnostic pop #endif /* * Generate functions to print array-valued nvlist members. */ #define NVLIST_ARRPRTFUNC(type_and_variant, vtype, ptype, vfmt) \ static int \ nvaprint_##type_and_variant(nvlist_prtctl_t pctl, void *private, \ nvlist_t *nvl, const char *name, vtype *valuep, uint_t count) \ { \ (void) private; \ (void) nvl; \ FILE *fp = pctl->nvprt_fp; \ uint_t i; \ for (i = 0; i < count; i++) { \ if (i == 0 || pctl->nvprt_btwnarrfmt_nl) { \ indent(pctl, 1); \ (void) fprintf(fp, pctl->nvprt_nmfmt, name); \ if (pctl->nvprt_btwnarrfmt_nl) \ (void) fprintf(fp, "[%d]: ", i); \ } \ if (i != 0) \ (void) fprintf(fp, "%s", pctl->nvprt_btwnarrfmt); \ (void) fprintf(fp, vfmt, (ptype)valuep[i]); \ } \ return (1); \ } NVLIST_ARRPRTFUNC(boolean_array, boolean_t, boolean_t, "%d") NVLIST_ARRPRTFUNC(byte_array, uchar_t, uchar_t, "0x%2.2x") NVLIST_ARRPRTFUNC(int8_array, int8_t, int8_t, "%d") NVLIST_ARRPRTFUNC(uint8_array, uint8_t, uint8_t, "0x%x") NVLIST_ARRPRTFUNC(int16_array, int16_t, int16_t, "%d") NVLIST_ARRPRTFUNC(uint16_array, uint16_t, uint16_t, "0x%x") NVLIST_ARRPRTFUNC(int32_array, int32_t, int32_t, "%d") NVLIST_ARRPRTFUNC(uint32_array, uint32_t, uint32_t, "0x%x") NVLIST_ARRPRTFUNC(int64_array, int64_t, longlong_t, "%lld") NVLIST_ARRPRTFUNC(uint64_array, uint64_t, u_longlong_t, "0x%llx") NVLIST_ARRPRTFUNC(string_array, const char *, const char *, "%s") static int nvprint_nvlist(nvlist_prtctl_t pctl, void *private, nvlist_t *nvl, const char *name, nvlist_t *value) { (void) private, (void) nvl; FILE *fp = pctl->nvprt_fp; indent(pctl, 1); (void) fprintf(fp, "%s = (embedded nvlist)\n", name); pctl->nvprt_indent += pctl->nvprt_indentinc; nvlist_print_with_indent(value, pctl); pctl->nvprt_indent -= pctl->nvprt_indentinc; indent(pctl, 1); (void) fprintf(fp, "(end %s)\n", name); return (1); } static int nvaprint_nvlist_array(nvlist_prtctl_t pctl, void *private, nvlist_t *nvl, const char *name, nvlist_t **valuep, uint_t count) { (void) private, (void) nvl; FILE *fp = pctl->nvprt_fp; uint_t i; indent(pctl, 1); (void) fprintf(fp, "%s = (array of embedded nvlists)\n", name); for (i = 0; i < count; i++) { indent(pctl, 1); (void) fprintf(fp, "(start %s[%d])\n", name, i); pctl->nvprt_indent += pctl->nvprt_indentinc; nvlist_print_with_indent(valuep[i], pctl); pctl->nvprt_indent -= pctl->nvprt_indentinc; indent(pctl, 1); (void) fprintf(fp, "(end %s[%d])\n", name, i); } return (1); } /* * ====================================================================== * | | * | Interfaces that allow control over formatting. | * | | * ====================================================================== */ void nvlist_prtctl_setdest(nvlist_prtctl_t pctl, FILE *fp) { pctl->nvprt_fp = fp; } FILE * nvlist_prtctl_getdest(nvlist_prtctl_t pctl) { return (pctl->nvprt_fp); } void nvlist_prtctl_setindent(nvlist_prtctl_t pctl, enum nvlist_indent_mode mode, int start, int inc) { if (mode < NVLIST_INDENT_ABS || mode > NVLIST_INDENT_TABBED) mode = NVLIST_INDENT_TABBED; if (start < 0) start = 0; if (inc < 0) inc = 1; pctl->nvprt_indent_mode = mode; pctl->nvprt_indent = start; pctl->nvprt_indentinc = inc; } void nvlist_prtctl_doindent(nvlist_prtctl_t pctl, int onemore) { indent(pctl, onemore); } void nvlist_prtctl_setfmt(nvlist_prtctl_t pctl, enum nvlist_prtctl_fmt which, const char *fmt) { switch (which) { case NVLIST_FMT_MEMBER_NAME: if (fmt == NULL) fmt = "%s = "; pctl->nvprt_nmfmt = fmt; break; case NVLIST_FMT_MEMBER_POSTAMBLE: if (fmt == NULL) fmt = "\n"; pctl->nvprt_eomfmt = fmt; break; case NVLIST_FMT_BTWN_ARRAY: if (fmt == NULL) { pctl->nvprt_btwnarrfmt = " "; pctl->nvprt_btwnarrfmt_nl = 0; } else { pctl->nvprt_btwnarrfmt = fmt; pctl->nvprt_btwnarrfmt_nl = (strchr(fmt, '\n') != NULL); } break; default: break; } } void nvlist_prtctl_dofmt(nvlist_prtctl_t pctl, enum nvlist_prtctl_fmt which, ...) { FILE *fp = pctl->nvprt_fp; va_list ap; const char *name; va_start(ap, which); switch (which) { case NVLIST_FMT_MEMBER_NAME: name = va_arg(ap, const char *); (void) fprintf(fp, pctl->nvprt_nmfmt, name); break; case NVLIST_FMT_MEMBER_POSTAMBLE: (void) fprintf(fp, "%s", pctl->nvprt_eomfmt); break; case NVLIST_FMT_BTWN_ARRAY: (void) fprintf(fp, "%s", pctl->nvprt_btwnarrfmt); break; default: break; } va_end(ap); } /* * ====================================================================== * | | * | Interfaces to allow appointment of replacement rendering functions.| * | | * ====================================================================== */ #define NVLIST_PRINTCTL_REPLACE(type, vtype) \ void \ nvlist_prtctlop_##type(nvlist_prtctl_t pctl, \ int (*func)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype), \ void *private) \ { \ CUSTPRTOP(pctl, type) = func; \ CUSTPRTOPARG(pctl, type) = private; \ } NVLIST_PRINTCTL_REPLACE(boolean, int) NVLIST_PRINTCTL_REPLACE(boolean_value, boolean_t) NVLIST_PRINTCTL_REPLACE(byte, uchar_t) NVLIST_PRINTCTL_REPLACE(int8, int8_t) NVLIST_PRINTCTL_REPLACE(uint8, uint8_t) NVLIST_PRINTCTL_REPLACE(int16, int16_t) NVLIST_PRINTCTL_REPLACE(uint16, uint16_t) NVLIST_PRINTCTL_REPLACE(int32, int32_t) NVLIST_PRINTCTL_REPLACE(uint32, uint32_t) NVLIST_PRINTCTL_REPLACE(int64, int64_t) NVLIST_PRINTCTL_REPLACE(uint64, uint64_t) NVLIST_PRINTCTL_REPLACE(double, double) NVLIST_PRINTCTL_REPLACE(string, const char *) NVLIST_PRINTCTL_REPLACE(hrtime, hrtime_t) NVLIST_PRINTCTL_REPLACE(nvlist, nvlist_t *) #define NVLIST_PRINTCTL_AREPLACE(type, vtype) \ void \ nvlist_prtctlop_##type(nvlist_prtctl_t pctl, \ int (*func)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype, \ uint_t), void *private) \ { \ CUSTPRTOP(pctl, type) = func; \ CUSTPRTOPARG(pctl, type) = private; \ } NVLIST_PRINTCTL_AREPLACE(boolean_array, boolean_t *) NVLIST_PRINTCTL_AREPLACE(byte_array, uchar_t *) NVLIST_PRINTCTL_AREPLACE(int8_array, int8_t *) NVLIST_PRINTCTL_AREPLACE(uint8_array, uint8_t *) NVLIST_PRINTCTL_AREPLACE(int16_array, int16_t *) NVLIST_PRINTCTL_AREPLACE(uint16_array, uint16_t *) NVLIST_PRINTCTL_AREPLACE(int32_array, int32_t *) NVLIST_PRINTCTL_AREPLACE(uint32_array, uint32_t *) NVLIST_PRINTCTL_AREPLACE(int64_array, int64_t *) NVLIST_PRINTCTL_AREPLACE(uint64_array, uint64_t *) NVLIST_PRINTCTL_AREPLACE(string_array, const char **) NVLIST_PRINTCTL_AREPLACE(nvlist_array, nvlist_t **) /* * ====================================================================== * | | * | Interfaces to manage nvlist_prtctl_t cookies. | * | | * ====================================================================== */ static const struct nvlist_printops defprtops = { { nvprint_boolean, NULL }, { nvprint_boolean_value, NULL }, { nvprint_byte, NULL }, { nvprint_int8, NULL }, { nvprint_uint8, NULL }, { nvprint_int16, NULL }, { nvprint_uint16, NULL }, { nvprint_int32, NULL }, { nvprint_uint32, NULL }, { nvprint_int64, NULL }, { nvprint_uint64, NULL }, { nvprint_double, NULL }, { nvprint_string, NULL }, { nvprint_hrtime, NULL }, { nvprint_nvlist, NULL }, { nvaprint_boolean_array, NULL }, { nvaprint_byte_array, NULL }, { nvaprint_int8_array, NULL }, { nvaprint_uint8_array, NULL }, { nvaprint_int16_array, NULL }, { nvaprint_uint16_array, NULL }, { nvaprint_int32_array, NULL }, { nvaprint_uint32_array, NULL }, { nvaprint_int64_array, NULL }, { nvaprint_uint64_array, NULL }, { nvaprint_string_array, NULL }, { nvaprint_nvlist_array, NULL }, }; static void prtctl_defaults(FILE *fp, struct nvlist_prtctl *pctl, struct nvlist_printops *ops) { pctl->nvprt_fp = fp; pctl->nvprt_indent_mode = NVLIST_INDENT_TABBED; pctl->nvprt_indent = 0; pctl->nvprt_indentinc = 1; pctl->nvprt_nmfmt = "%s = "; pctl->nvprt_eomfmt = "\n"; pctl->nvprt_btwnarrfmt = " "; pctl->nvprt_btwnarrfmt_nl = 0; pctl->nvprt_dfltops = (struct nvlist_printops *)&defprtops; pctl->nvprt_custops = ops; } nvlist_prtctl_t nvlist_prtctl_alloc(void) { struct nvlist_prtctl *pctl; struct nvlist_printops *ops; if ((pctl = malloc(sizeof (*pctl))) == NULL) return (NULL); if ((ops = calloc(1, sizeof (*ops))) == NULL) { free(pctl); return (NULL); } prtctl_defaults(stdout, pctl, ops); return (pctl); } void nvlist_prtctl_free(nvlist_prtctl_t pctl) { if (pctl != NULL) { free(pctl->nvprt_custops); free(pctl); } } /* * ====================================================================== * | | * | Top-level print request interfaces. | * | | * ====================================================================== */ /* * nvlist_print - Prints elements in an event buffer */ static void nvlist_print_with_indent(nvlist_t *nvl, nvlist_prtctl_t pctl) { FILE *fp = pctl->nvprt_fp; const char *name; uint_t nelem; nvpair_t *nvp; if (nvl == NULL) return; indent(pctl, 0); (void) fprintf(fp, "nvlist version: %d\n", NVL_VERSION(nvl)); nvp = nvlist_next_nvpair(nvl, NULL); while (nvp) { data_type_t type = nvpair_type(nvp); name = nvpair_name(nvp); nelem = 0; switch (type) { case DATA_TYPE_BOOLEAN: { RENDER(pctl, boolean, nvl, name, 1); break; } case DATA_TYPE_BOOLEAN_VALUE: { boolean_t val; (void) nvpair_value_boolean_value(nvp, &val); RENDER(pctl, boolean_value, nvl, name, val); break; } case DATA_TYPE_BYTE: { uchar_t val; (void) nvpair_value_byte(nvp, &val); RENDER(pctl, byte, nvl, name, val); break; } case DATA_TYPE_INT8: { int8_t val; (void) nvpair_value_int8(nvp, &val); RENDER(pctl, int8, nvl, name, val); break; } case DATA_TYPE_UINT8: { uint8_t val; (void) nvpair_value_uint8(nvp, &val); RENDER(pctl, uint8, nvl, name, val); break; } case DATA_TYPE_INT16: { int16_t val; (void) nvpair_value_int16(nvp, &val); RENDER(pctl, int16, nvl, name, val); break; } case DATA_TYPE_UINT16: { uint16_t val; (void) nvpair_value_uint16(nvp, &val); RENDER(pctl, uint16, nvl, name, val); break; } case DATA_TYPE_INT32: { int32_t val; (void) nvpair_value_int32(nvp, &val); RENDER(pctl, int32, nvl, name, val); break; } case DATA_TYPE_UINT32: { uint32_t val; (void) nvpair_value_uint32(nvp, &val); RENDER(pctl, uint32, nvl, name, val); break; } case DATA_TYPE_INT64: { int64_t val; (void) nvpair_value_int64(nvp, &val); RENDER(pctl, int64, nvl, name, val); break; } case DATA_TYPE_UINT64: { uint64_t val; (void) nvpair_value_uint64(nvp, &val); RENDER(pctl, uint64, nvl, name, val); break; } case DATA_TYPE_DOUBLE: { double val; (void) nvpair_value_double(nvp, &val); RENDER(pctl, double, nvl, name, val); break; } case DATA_TYPE_STRING: { const char *val; (void) nvpair_value_string(nvp, &val); RENDER(pctl, string, nvl, name, val); break; } case DATA_TYPE_BOOLEAN_ARRAY: { boolean_t *val; (void) nvpair_value_boolean_array(nvp, &val, &nelem); ARENDER(pctl, boolean_array, nvl, name, val, nelem); break; } case DATA_TYPE_BYTE_ARRAY: { uchar_t *val; (void) nvpair_value_byte_array(nvp, &val, &nelem); ARENDER(pctl, byte_array, nvl, name, val, nelem); break; } case DATA_TYPE_INT8_ARRAY: { int8_t *val; (void) nvpair_value_int8_array(nvp, &val, &nelem); ARENDER(pctl, int8_array, nvl, name, val, nelem); break; } case DATA_TYPE_UINT8_ARRAY: { uint8_t *val; (void) nvpair_value_uint8_array(nvp, &val, &nelem); ARENDER(pctl, uint8_array, nvl, name, val, nelem); break; } case DATA_TYPE_INT16_ARRAY: { int16_t *val; (void) nvpair_value_int16_array(nvp, &val, &nelem); ARENDER(pctl, int16_array, nvl, name, val, nelem); break; } case DATA_TYPE_UINT16_ARRAY: { uint16_t *val; (void) nvpair_value_uint16_array(nvp, &val, &nelem); ARENDER(pctl, uint16_array, nvl, name, val, nelem); break; } case DATA_TYPE_INT32_ARRAY: { int32_t *val; (void) nvpair_value_int32_array(nvp, &val, &nelem); ARENDER(pctl, int32_array, nvl, name, val, nelem); break; } case DATA_TYPE_UINT32_ARRAY: { uint32_t *val; (void) nvpair_value_uint32_array(nvp, &val, &nelem); ARENDER(pctl, uint32_array, nvl, name, val, nelem); break; } case DATA_TYPE_INT64_ARRAY: { int64_t *val; (void) nvpair_value_int64_array(nvp, &val, &nelem); ARENDER(pctl, int64_array, nvl, name, val, nelem); break; } case DATA_TYPE_UINT64_ARRAY: { uint64_t *val; (void) nvpair_value_uint64_array(nvp, &val, &nelem); ARENDER(pctl, uint64_array, nvl, name, val, nelem); break; } case DATA_TYPE_STRING_ARRAY: { const char **val; (void) nvpair_value_string_array(nvp, &val, &nelem); ARENDER(pctl, string_array, nvl, name, val, nelem); break; } case DATA_TYPE_HRTIME: { hrtime_t val; (void) nvpair_value_hrtime(nvp, &val); RENDER(pctl, hrtime, nvl, name, val); break; } case DATA_TYPE_NVLIST: { nvlist_t *val; (void) nvpair_value_nvlist(nvp, &val); RENDER(pctl, nvlist, nvl, name, val); break; } case DATA_TYPE_NVLIST_ARRAY: { nvlist_t **val; (void) nvpair_value_nvlist_array(nvp, &val, &nelem); ARENDER(pctl, nvlist_array, nvl, name, val, nelem); break; } default: (void) fprintf(fp, " unknown data type (%d)", type); break; } nvp = nvlist_next_nvpair(nvl, nvp); } } void nvlist_print(FILE *fp, nvlist_t *nvl) { struct nvlist_prtctl pc; prtctl_defaults(fp, &pc, NULL); nvlist_print_with_indent(nvl, &pc); } void nvlist_prt(nvlist_t *nvl, nvlist_prtctl_t pctl) { nvlist_print_with_indent(nvl, pctl); } -#define NVP(elem, type, vtype, ptype, format) { \ - vtype value; \ -\ - (void) nvpair_value_##type(elem, &value); \ - (void) printf("%*s%s: " format "\n", indent, "", \ - nvpair_name(elem), (ptype)value); \ -} - -#define NVPA(elem, type, vtype, ptype, format) { \ - uint_t i, count; \ - vtype *value; \ -\ - (void) nvpair_value_##type(elem, &value, &count); \ - for (i = 0; i < count; i++) { \ - (void) printf("%*s%s[%d]: " format "\n", indent, "", \ - nvpair_name(elem), i, (ptype)value[i]); \ - } \ -} - -/* - * Similar to nvlist_print() but handles arrays slightly differently. - */ -void -dump_nvlist(nvlist_t *list, int indent) -{ - nvpair_t *elem = NULL; - boolean_t bool_value; - nvlist_t *nvlist_value; - nvlist_t **nvlist_array_value; - uint_t i, count; - - if (list == NULL) { - return; - } - - while ((elem = nvlist_next_nvpair(list, elem)) != NULL) { - switch (nvpair_type(elem)) { - case DATA_TYPE_BOOLEAN: - (void) printf("%*s%s\n", indent, "", nvpair_name(elem)); - break; - - case DATA_TYPE_BOOLEAN_VALUE: - (void) nvpair_value_boolean_value(elem, &bool_value); - (void) printf("%*s%s: %s\n", indent, "", - nvpair_name(elem), bool_value ? "true" : "false"); - break; - - case DATA_TYPE_BYTE: - NVP(elem, byte, uchar_t, int, "%u"); - break; - - case DATA_TYPE_INT8: - NVP(elem, int8, int8_t, int, "%d"); - break; - - case DATA_TYPE_UINT8: - NVP(elem, uint8, uint8_t, int, "%u"); - break; - - case DATA_TYPE_INT16: - NVP(elem, int16, int16_t, int, "%d"); - break; - - case DATA_TYPE_UINT16: - NVP(elem, uint16, uint16_t, int, "%u"); - break; - - case DATA_TYPE_INT32: - NVP(elem, int32, int32_t, long, "%ld"); - break; - - case DATA_TYPE_UINT32: - NVP(elem, uint32, uint32_t, ulong_t, "%lu"); - break; - - case DATA_TYPE_INT64: - NVP(elem, int64, int64_t, longlong_t, "%lld"); - break; - - case DATA_TYPE_UINT64: - NVP(elem, uint64, uint64_t, u_longlong_t, "%llu"); - break; - - case DATA_TYPE_STRING: - NVP(elem, string, const char *, const char *, "'%s'"); - break; - - case DATA_TYPE_BYTE_ARRAY: - NVPA(elem, byte_array, uchar_t, int, "%u"); - break; - - case DATA_TYPE_INT8_ARRAY: - NVPA(elem, int8_array, int8_t, int, "%d"); - break; - - case DATA_TYPE_UINT8_ARRAY: - NVPA(elem, uint8_array, uint8_t, int, "%u"); - break; - - case DATA_TYPE_INT16_ARRAY: - NVPA(elem, int16_array, int16_t, int, "%d"); - break; - - case DATA_TYPE_UINT16_ARRAY: - NVPA(elem, uint16_array, uint16_t, int, "%u"); - break; - - case DATA_TYPE_INT32_ARRAY: - NVPA(elem, int32_array, int32_t, long, "%ld"); - break; - - case DATA_TYPE_UINT32_ARRAY: - NVPA(elem, uint32_array, uint32_t, ulong_t, "%lu"); - break; - - case DATA_TYPE_INT64_ARRAY: - NVPA(elem, int64_array, int64_t, longlong_t, "%lld"); - break; - - case DATA_TYPE_UINT64_ARRAY: - NVPA(elem, uint64_array, uint64_t, u_longlong_t, - "%llu"); - break; - - case DATA_TYPE_STRING_ARRAY: - NVPA(elem, string_array, const char *, const char *, - "'%s'"); - break; - - case DATA_TYPE_NVLIST: - (void) nvpair_value_nvlist(elem, &nvlist_value); - (void) printf("%*s%s:\n", indent, "", - nvpair_name(elem)); - dump_nvlist(nvlist_value, indent + 4); - break; - - case DATA_TYPE_NVLIST_ARRAY: - (void) nvpair_value_nvlist_array(elem, - &nvlist_array_value, &count); - for (i = 0; i < count; i++) { - (void) printf("%*s%s[%u]:\n", indent, "", - nvpair_name(elem), i); - dump_nvlist(nvlist_array_value[i], indent + 4); - } - break; - - default: - (void) printf(dgettext(TEXT_DOMAIN, "bad config type " - "%d for %s\n"), nvpair_type(elem), - nvpair_name(elem)); - } - } -} - /* * ====================================================================== * | | * | Misc private interface. | * | | * ====================================================================== */ /* * Determine if string 'value' matches 'nvp' value. The 'value' string is * converted, depending on the type of 'nvp', prior to match. For numeric * types, a radix independent sscanf conversion of 'value' is used. If 'nvp' * is an array type, 'ai' is the index into the array against which we are * checking for match. If nvp is of DATA_TYPE_STRING*, the caller can pass * in a regex_t compilation of value in 'value_regex' to trigger regular * expression string match instead of simple strcmp(). * * Return 1 on match, 0 on no-match, and -1 on error. If the error is * related to value syntax error and 'ep' is non-NULL, *ep will point into * the 'value' string at the location where the error exists. * * NOTE: It may be possible to move the non-regex_t version of this into * common code used by library/kernel/boot. */ int nvpair_value_match_regex(nvpair_t *nvp, int ai, const char *value, regex_t *value_regex, const char **ep) { const char *evalue; uint_t a_len; int sr; if (ep) *ep = NULL; if ((nvp == NULL) || (value == NULL)) return (-1); /* error fail match - invalid args */ /* make sure array and index combination make sense */ if ((nvpair_type_is_array(nvp) && (ai < 0)) || (!nvpair_type_is_array(nvp) && (ai >= 0))) return (-1); /* error fail match - bad index */ /* non-string values should be single 'chunk' */ if ((nvpair_type(nvp) != DATA_TYPE_STRING) && (nvpair_type(nvp) != DATA_TYPE_STRING_ARRAY)) { value += strspn(value, " \t"); evalue = value + strcspn(value, " \t"); if (*evalue) { if (ep) *ep = evalue; return (-1); /* error fail match - syntax */ } } sr = EOF; switch (nvpair_type(nvp)) { case DATA_TYPE_STRING: { const char *val; /* check string value for match */ if (nvpair_value_string(nvp, &val) == 0) { if (value_regex) { if (regexec(value_regex, val, (size_t)0, NULL, 0) == 0) return (1); /* match */ } else { if (strcmp(value, val) == 0) return (1); /* match */ } } break; } case DATA_TYPE_STRING_ARRAY: { const char **val_array; /* check indexed string value of array for match */ if ((nvpair_value_string_array(nvp, &val_array, &a_len) == 0) && (ai < a_len)) { if (value_regex) { if (regexec(value_regex, val_array[ai], (size_t)0, NULL, 0) == 0) return (1); } else { if (strcmp(value, val_array[ai]) == 0) return (1); } } break; } case DATA_TYPE_BYTE: { uchar_t val, val_arg; /* scanf uchar_t from value and check for match */ sr = sscanf(value, "%c", &val_arg); if ((sr == 1) && (nvpair_value_byte(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_BYTE_ARRAY: { uchar_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%c", &val_arg); if ((sr == 1) && (nvpair_value_byte_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_INT8: { int8_t val, val_arg; /* scanf int8_t from value and check for match */ sr = sscanf(value, "%"SCNi8, &val_arg); if ((sr == 1) && (nvpair_value_int8(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_INT8_ARRAY: { int8_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi8, &val_arg); if ((sr == 1) && (nvpair_value_int8_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_UINT8: { uint8_t val, val_arg; /* scanf uint8_t from value and check for match */ sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint8(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_UINT8_ARRAY: { uint8_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint8_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_INT16: { int16_t val, val_arg; /* scanf int16_t from value and check for match */ sr = sscanf(value, "%"SCNi16, &val_arg); if ((sr == 1) && (nvpair_value_int16(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_INT16_ARRAY: { int16_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi16, &val_arg); if ((sr == 1) && (nvpair_value_int16_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_UINT16: { uint16_t val, val_arg; /* scanf uint16_t from value and check for match */ sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint16(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_UINT16_ARRAY: { uint16_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint16_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_INT32: { int32_t val, val_arg; /* scanf int32_t from value and check for match */ sr = sscanf(value, "%"SCNi32, &val_arg); if ((sr == 1) && (nvpair_value_int32(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_INT32_ARRAY: { int32_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi32, &val_arg); if ((sr == 1) && (nvpair_value_int32_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_UINT32: { uint32_t val, val_arg; /* scanf uint32_t from value and check for match */ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint32(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_UINT32_ARRAY: { uint32_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint32_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_INT64: { int64_t val, val_arg; /* scanf int64_t from value and check for match */ sr = sscanf(value, "%"SCNi64, &val_arg); if ((sr == 1) && (nvpair_value_int64(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_INT64_ARRAY: { int64_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi64, &val_arg); if ((sr == 1) && (nvpair_value_int64_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_UINT64: { uint64_t val_arg, val; /* scanf uint64_t from value and check for match */ sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint64(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_UINT64_ARRAY: { uint64_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint64_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_BOOLEAN_VALUE: { int32_t val_arg; boolean_t val; /* scanf boolean_t from value and check for match */ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg); if ((sr == 1) && (nvpair_value_boolean_value(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_BOOLEAN_ARRAY: { boolean_t *val_array; int32_t val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg); if ((sr == 1) && (nvpair_value_boolean_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_HRTIME: case DATA_TYPE_NVLIST: case DATA_TYPE_NVLIST_ARRAY: case DATA_TYPE_BOOLEAN: case DATA_TYPE_DOUBLE: case DATA_TYPE_UNKNOWN: default: /* * unknown/unsupported data type */ return (-1); /* error fail match */ } /* * check to see if sscanf failed conversion, return approximate * pointer to problem */ if (sr != 1) { if (ep) *ep = value; return (-1); /* error fail match - syntax */ } return (0); /* fail match */ } int nvpair_value_match(nvpair_t *nvp, int ai, const char *value, const char **ep) { return (nvpair_value_match_regex(nvp, ai, value, NULL, ep)); } + +/* + * Similar to nvlist_print() but handles arrays slightly differently. + */ +void +dump_nvlist(nvlist_t *list, int indent) +{ + int len; + char *buf; + + len = nvlist_snprintf(NULL, 0, list, indent); + len++; /* Add null terminator */ + + buf = malloc(len); + if (buf == NULL) + return; + + (void) nvlist_snprintf(buf, len, list, indent); + + /* + * fputs does not have limitations on the size of the buffer being + * printed (unlike printf). + */ + fputs(buf, stdout); + + free(buf); +} diff --git a/lib/libuutil/libuutil.abi b/lib/libuutil/libuutil.abi index 7cb92ac9f3f8..0052f0d47a7f 100644 --- a/lib/libuutil/libuutil.abi +++ b/lib/libuutil/libuutil.abi @@ -1,2374 +1,2341 @@ + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi index 7e6a9211b08d..26ae5d2f1396 100644 --- a/lib/libzfs/libzfs.abi +++ b/lib/libzfs/libzfs.abi @@ -1,10136 +1,10116 @@ - + + + + + + + + + + + + + - + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - + + + + + + + + - + - - - - - - - - - - + + + + + - - + + diff --git a/lib/libzfs_core/libzfs_core.abi b/lib/libzfs_core/libzfs_core.abi index 6a9c20a2bb88..f6aa9a742bff 100644 --- a/lib/libzfs_core/libzfs_core.abi +++ b/lib/libzfs_core/libzfs_core.abi @@ -1,3223 +1,3196 @@ + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/lib/libzfsbootenv/libzfsbootenv.abi b/lib/libzfsbootenv/libzfsbootenv.abi index 5903d5dcbe21..bf866b0fa61b 100644 --- a/lib/libzfsbootenv/libzfsbootenv.abi +++ b/lib/libzfsbootenv/libzfsbootenv.abi @@ -1,584 +1,584 @@ - + diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am index 8875393dcb22..829d626d4866 100644 --- a/lib/libzpool/Makefile.am +++ b/lib/libzpool/Makefile.am @@ -1,220 +1,221 @@ include $(srcdir)/%D%/include/Makefile.am libzpool_la_CFLAGS = $(AM_CFLAGS) $(KERNEL_CFLAGS) $(LIBRARY_CFLAGS) libzpool_la_CFLAGS += $(ZLIB_CFLAGS) libzpool_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBZPOOL_CPPFLAGS) libzpool_la_CPPFLAGS += -I$(srcdir)/include/os/@ac_system_l@/zfs libzpool_la_CPPFLAGS += -DLIB_ZPOOL_BUILD lib_LTLIBRARIES += libzpool.la CPPCHECKTARGETS += libzpool.la dist_libzpool_la_SOURCES = \ %D%/abd_os.c \ %D%/arc_os.c \ %D%/kernel.c \ %D%/taskq.c \ %D%/util.c \ %D%/vdev_label_os.c \ %D%/zfs_racct.c \ %D%/zfs_debug.c nodist_libzpool_la_SOURCES = \ module/lua/lapi.c \ module/lua/lauxlib.c \ module/lua/lbaselib.c \ module/lua/lcode.c \ module/lua/lcompat.c \ module/lua/lcorolib.c \ module/lua/lctype.c \ module/lua/ldebug.c \ module/lua/ldo.c \ module/lua/lfunc.c \ module/lua/lgc.c \ module/lua/llex.c \ module/lua/lmem.c \ module/lua/lobject.c \ module/lua/lopcodes.c \ module/lua/lparser.c \ module/lua/lstate.c \ module/lua/lstring.c \ module/lua/lstrlib.c \ module/lua/ltable.c \ module/lua/ltablib.c \ module/lua/ltm.c \ module/lua/lvm.c \ module/lua/lzio.c \ \ module/os/linux/zfs/zio_crypt.c \ \ module/zcommon/cityhash.c \ module/zcommon/simd_stat.c \ module/zcommon/zfeature_common.c \ module/zcommon/zfs_comutil.c \ module/zcommon/zfs_deleg.c \ module/zcommon/zfs_fletcher.c \ module/zcommon/zfs_fletcher_aarch64_neon.c \ module/zcommon/zfs_fletcher_avx512.c \ module/zcommon/zfs_fletcher_intel.c \ module/zcommon/zfs_fletcher_sse.c \ module/zcommon/zfs_fletcher_superscalar.c \ module/zcommon/zfs_fletcher_superscalar4.c \ module/zcommon/zfs_namecheck.c \ module/zcommon/zfs_prop.c \ module/zcommon/zfs_valstr.c \ module/zcommon/zpool_prop.c \ module/zcommon/zprop_common.c \ \ module/zfs/abd.c \ module/zfs/aggsum.c \ module/zfs/arc.c \ module/zfs/blake3_zfs.c \ module/zfs/blkptr.c \ module/zfs/bplist.c \ module/zfs/bpobj.c \ module/zfs/bptree.c \ module/zfs/bqueue.c \ module/zfs/btree.c \ module/zfs/brt.c \ module/zfs/dbuf.c \ module/zfs/dbuf_stats.c \ module/zfs/ddt.c \ module/zfs/ddt_log.c \ module/zfs/ddt_stats.c \ module/zfs/ddt_zap.c \ module/zfs/dmu.c \ module/zfs/dmu_diff.c \ module/zfs/dmu_direct.c \ module/zfs/dmu_object.c \ module/zfs/dmu_objset.c \ module/zfs/dmu_recv.c \ module/zfs/dmu_redact.c \ module/zfs/dmu_send.c \ module/zfs/dmu_traverse.c \ module/zfs/dmu_tx.c \ module/zfs/dmu_zfetch.c \ module/zfs/dnode.c \ module/zfs/dnode_sync.c \ module/zfs/dsl_bookmark.c \ module/zfs/dsl_crypt.c \ module/zfs/dsl_dataset.c \ module/zfs/dsl_deadlist.c \ module/zfs/dsl_deleg.c \ module/zfs/dsl_destroy.c \ module/zfs/dsl_dir.c \ module/zfs/dsl_pool.c \ module/zfs/dsl_prop.c \ module/zfs/dsl_scan.c \ module/zfs/dsl_synctask.c \ module/zfs/dsl_userhold.c \ module/zfs/edonr_zfs.c \ module/zfs/fm.c \ module/zfs/gzip.c \ module/zfs/hkdf.c \ module/zfs/lz4.c \ module/zfs/lz4_zfs.c \ module/zfs/lzjb.c \ module/zfs/metaslab.c \ module/zfs/mmp.c \ module/zfs/multilist.c \ module/zfs/objlist.c \ module/zfs/pathname.c \ module/zfs/range_tree.c \ module/zfs/refcount.c \ module/zfs/rrwlock.c \ module/zfs/sa.c \ module/zfs/sha2_zfs.c \ module/zfs/skein_zfs.c \ module/zfs/spa.c \ module/zfs/spa_checkpoint.c \ module/zfs/spa_config.c \ module/zfs/spa_errlog.c \ module/zfs/spa_history.c \ module/zfs/spa_log_spacemap.c \ module/zfs/spa_misc.c \ module/zfs/spa_stats.c \ module/zfs/space_map.c \ module/zfs/space_reftree.c \ module/zfs/txg.c \ module/zfs/uberblock.c \ module/zfs/unique.c \ module/zfs/vdev.c \ module/zfs/vdev_draid.c \ module/zfs/vdev_draid_rand.c \ module/zfs/vdev_file.c \ module/zfs/vdev_indirect.c \ module/zfs/vdev_indirect_births.c \ module/zfs/vdev_indirect_mapping.c \ module/zfs/vdev_initialize.c \ module/zfs/vdev_label.c \ module/zfs/vdev_mirror.c \ module/zfs/vdev_missing.c \ module/zfs/vdev_queue.c \ module/zfs/vdev_raidz.c \ module/zfs/vdev_raidz_math.c \ module/zfs/vdev_raidz_math_aarch64_neon.c \ module/zfs/vdev_raidz_math_aarch64_neonx2.c \ module/zfs/vdev_raidz_math_avx2.c \ module/zfs/vdev_raidz_math_avx512bw.c \ module/zfs/vdev_raidz_math_avx512f.c \ module/zfs/vdev_raidz_math_powerpc_altivec.c \ module/zfs/vdev_raidz_math_scalar.c \ module/zfs/vdev_raidz_math_sse2.c \ module/zfs/vdev_raidz_math_ssse3.c \ module/zfs/vdev_rebuild.c \ module/zfs/vdev_removal.c \ module/zfs/vdev_root.c \ module/zfs/vdev_trim.c \ module/zfs/zap.c \ module/zfs/zap_leaf.c \ module/zfs/zap_micro.c \ module/zfs/zcp.c \ module/zfs/zcp_get.c \ module/zfs/zcp_global.c \ module/zfs/zcp_iter.c \ module/zfs/zcp_set.c \ module/zfs/zcp_synctask.c \ module/zfs/zfeature.c \ module/zfs/zfs_byteswap.c \ module/zfs/zfs_chksum.c \ + module/zfs/zfs_debug_common.c \ module/zfs/zfs_fm.c \ module/zfs/zfs_fuid.c \ module/zfs/zfs_ratelimit.c \ module/zfs/zfs_rlock.c \ module/zfs/zfs_sa.c \ module/zfs/zfs_znode.c \ module/zfs/zil.c \ module/zfs/zio.c \ module/zfs/zio_checksum.c \ module/zfs/zio_compress.c \ module/zfs/zio_inject.c \ module/zfs/zle.c \ module/zfs/zrlock.c \ module/zfs/zthr.c libzpool_la_LIBADD = \ libicp.la \ libunicode.la \ libnvpair.la \ libzstd.la \ libzutil.la libzpool_la_LIBADD += $(LIBCLOCK_GETTIME) $(ZLIB_LIBS) -ldl -lm libzpool_la_LDFLAGS = -pthread if !ASAN_ENABLED libzpool_la_LDFLAGS += -Wl,-z,defs endif if BUILD_FREEBSD libzpool_la_LIBADD += -lgeom endif libzpool_la_LDFLAGS += -version-info 6:0:0 if TARGET_CPU_POWERPC module/zfs/libzpool_la-vdev_raidz_math_powerpc_altivec.$(OBJEXT) : CFLAGS += -maltivec module/zfs/libzpool_la-vdev_raidz_math_powerpc_altivec.l$(OBJEXT): CFLAGS += -maltivec endif diff --git a/module/Kbuild.in b/module/Kbuild.in index 569c3a869015..edb475c7d3e0 100644 --- a/module/Kbuild.in +++ b/module/Kbuild.in @@ -1,508 +1,509 @@ # When integrated in to a monolithic kernel the spl module must appear # first. This ensures its module initialization function is run before # any of the other module initialization functions which depend on it. ZFS_MODULE_CFLAGS += -std=gnu99 -Wno-declaration-after-statement ZFS_MODULE_CFLAGS += -Wmissing-prototypes ZFS_MODULE_CFLAGS += @KERNEL_DEBUG_CFLAGS@ @NO_FORMAT_ZERO_LENGTH@ ifneq ($(KBUILD_EXTMOD),) zfs_include = @abs_top_srcdir@/include icp_include = @abs_srcdir@/icp/include zstd_include = @abs_srcdir@/zstd/include ZFS_MODULE_CFLAGS += -include @abs_top_builddir@/zfs_config.h ZFS_MODULE_CFLAGS += -I@abs_top_builddir@/include src = @abs_srcdir@ obj = @abs_builddir@ else zfs_include = $(srctree)/include/zfs icp_include = $(src)/icp/include zstd_include = $(src)/zstd/include ZFS_MODULE_CFLAGS += -include $(zfs_include)/zfs_config.h endif ZFS_MODULE_CFLAGS += -I$(zfs_include)/os/linux/kernel ZFS_MODULE_CFLAGS += -I$(zfs_include)/os/linux/spl ZFS_MODULE_CFLAGS += -I$(zfs_include)/os/linux/zfs ZFS_MODULE_CFLAGS += -I$(zfs_include) ZFS_MODULE_CPPFLAGS += -D_KERNEL ZFS_MODULE_CPPFLAGS += @KERNEL_DEBUG_CPPFLAGS@ # KASAN enables -Werror=frame-larger-than=1024, which # breaks oh so many parts of our build. ifeq ($(CONFIG_KASAN),y) ZFS_MODULE_CFLAGS += -Wno-error=frame-larger-than= endif # Generated binary search code is particularly bad with this optimization. # Oddly, range_tree.c is not affected when unrolling is not done and dsl_scan.c # is not affected when unrolling is done. # Disable it until the following upstream issue is resolved: # https://github.com/llvm/llvm-project/issues/62790 ifeq ($(CONFIG_X86),y) ifeq ($(CONFIG_CC_IS_CLANG),y) CFLAGS_zfs/dsl_scan.o += -mllvm -x86-cmov-converter=false CFLAGS_zfs/metaslab.o += -mllvm -x86-cmov-converter=false CFLAGS_zfs/range_tree.o += -mllvm -x86-cmov-converter=false CFLAGS_zfs/zap_micro.o += -mllvm -x86-cmov-converter=false endif endif ifneq ($(KBUILD_EXTMOD),) @CONFIG_QAT_TRUE@ZFS_MODULE_CFLAGS += -I@QAT_SRC@/include @CONFIG_QAT_TRUE@KBUILD_EXTRA_SYMBOLS += @QAT_SYMBOLS@ endif asflags-y := $(ZFS_MODULE_CFLAGS) $(ZFS_MODULE_CPPFLAGS) ccflags-y := $(ZFS_MODULE_CFLAGS) $(ZFS_MODULE_CPPFLAGS) ifeq ($(CONFIG_ARM64),y) CFLAGS_REMOVE_zcommon/zfs_fletcher_aarch64_neon.o += -mgeneral-regs-only CFLAGS_REMOVE_zfs/vdev_raidz_math_aarch64_neon.o += -mgeneral-regs-only CFLAGS_REMOVE_zfs/vdev_raidz_math_aarch64_neonx2.o += -mgeneral-regs-only endif # Suppress unused-value warnings in sparc64 architecture headers ccflags-$(CONFIG_SPARC64) += -Wno-unused-value obj-$(CONFIG_ZFS) := spl.o zfs.o SPL_OBJS := \ spl-atomic.o \ spl-condvar.o \ spl-cred.o \ spl-err.o \ spl-generic.o \ spl-kmem-cache.o \ spl-kmem.o \ spl-kstat.o \ spl-proc.o \ spl-procfs-list.o \ spl-shrinker.o \ spl-taskq.o \ spl-thread.o \ spl-trace.o \ spl-tsd.o \ spl-vmem.o \ spl-xdr.o \ spl-zlib.o \ spl-zone.o spl-objs += $(addprefix os/linux/spl/,$(SPL_OBJS)) zfs-objs += avl/avl.o ICP_OBJS := \ algs/aes/aes_impl.o \ algs/aes/aes_impl_generic.o \ algs/aes/aes_modes.o \ algs/blake3/blake3.o \ algs/blake3/blake3_generic.o \ algs/blake3/blake3_impl.o \ algs/edonr/edonr.o \ algs/modes/ccm.o \ algs/modes/gcm.o \ algs/modes/gcm_generic.o \ algs/modes/modes.o \ algs/sha2/sha2_generic.o \ algs/sha2/sha256_impl.o \ algs/sha2/sha512_impl.o \ algs/skein/skein.o \ algs/skein/skein_block.o \ algs/skein/skein_iv.o \ api/kcf_cipher.o \ api/kcf_ctxops.o \ api/kcf_mac.o \ core/kcf_callprov.o \ core/kcf_mech_tabs.o \ core/kcf_prov_lib.o \ core/kcf_prov_tabs.o \ core/kcf_sched.o \ illumos-crypto.o \ io/aes.o \ io/sha2_mod.o \ spi/kcf_spi.o ICP_OBJS_X86_64 := \ asm-x86_64/aes/aes_aesni.o \ asm-x86_64/aes/aes_amd64.o \ asm-x86_64/aes/aeskey.o \ asm-x86_64/blake3/blake3_avx2.o \ asm-x86_64/blake3/blake3_avx512.o \ asm-x86_64/blake3/blake3_sse2.o \ asm-x86_64/blake3/blake3_sse41.o \ asm-x86_64/sha2/sha256-x86_64.o \ asm-x86_64/sha2/sha512-x86_64.o \ asm-x86_64/modes/aesni-gcm-x86_64.o \ asm-x86_64/modes/gcm_pclmulqdq.o \ asm-x86_64/modes/ghash-x86_64.o ICP_OBJS_X86 := \ algs/aes/aes_impl_aesni.o \ algs/aes/aes_impl_x86-64.o \ algs/modes/gcm_pclmulqdq.o ICP_OBJS_ARM := \ asm-arm/sha2/sha256-armv7.o \ asm-arm/sha2/sha512-armv7.o ICP_OBJS_ARM64 := \ asm-aarch64/blake3/b3_aarch64_sse2.o \ asm-aarch64/blake3/b3_aarch64_sse41.o \ asm-aarch64/sha2/sha256-armv8.o \ asm-aarch64/sha2/sha512-armv8.o ICP_OBJS_PPC_PPC64 := \ asm-ppc64/blake3/b3_ppc64le_sse2.o \ asm-ppc64/blake3/b3_ppc64le_sse41.o \ asm-ppc64/sha2/sha256-p8.o \ asm-ppc64/sha2/sha512-p8.o \ asm-ppc64/sha2/sha256-ppc.o \ asm-ppc64/sha2/sha512-ppc.o zfs-objs += $(addprefix icp/,$(ICP_OBJS)) zfs-$(CONFIG_X86) += $(addprefix icp/,$(ICP_OBJS_X86)) zfs-$(CONFIG_UML_X86)+= $(addprefix icp/,$(ICP_OBJS_X86)) zfs-$(CONFIG_X86_64) += $(addprefix icp/,$(ICP_OBJS_X86_64)) zfs-$(CONFIG_ARM) += $(addprefix icp/,$(ICP_OBJS_ARM)) zfs-$(CONFIG_ARM64) += $(addprefix icp/,$(ICP_OBJS_ARM64)) zfs-$(CONFIG_PPC) += $(addprefix icp/,$(ICP_OBJS_PPC_PPC64)) zfs-$(CONFIG_PPC64) += $(addprefix icp/,$(ICP_OBJS_PPC_PPC64)) $(addprefix $(obj)/icp/,$(ICP_OBJS) $(ICP_OBJS_X86) $(ICP_OBJS_X86_64) \ $(ICP_OBJS_ARM64) $(ICP_OBJS_PPC_PPC64)) : asflags-y += -I$(icp_include) -I$(zfs_include)/os/linux/spl -I$(zfs_include) $(addprefix $(obj)/icp/,$(ICP_OBJS) $(ICP_OBJS_X86) $(ICP_OBJS_X86_64) \ $(ICP_OBJS_ARM64) $(ICP_OBJS_PPC_PPC64)) : ccflags-y += -I$(icp_include) -I$(zfs_include)/os/linux/spl -I$(zfs_include) # Suppress objtool "return with modified stack frame" warnings. OBJECT_FILES_NON_STANDARD_aesni-gcm-x86_64.o := y # Suppress objtool "unsupported stack pointer realignment" warnings. # See #6950 for the reasoning. OBJECT_FILES_NON_STANDARD_sha256-x86_64.o := y OBJECT_FILES_NON_STANDARD_sha512-x86_64.o := y LUA_OBJS := \ lapi.o \ lauxlib.o \ lbaselib.o \ lcode.o \ lcompat.o \ lcorolib.o \ lctype.o \ ldebug.o \ ldo.o \ lfunc.o \ lgc.o \ llex.o \ lmem.o \ lobject.o \ lopcodes.o \ lparser.o \ lstate.o \ lstring.o \ lstrlib.o \ ltable.o \ ltablib.o \ ltm.o \ lvm.o \ lzio.o \ setjmp/setjmp.o zfs-objs += $(addprefix lua/,$(LUA_OBJS)) NVPAIR_OBJS := \ fnvpair.o \ nvpair.o \ nvpair_alloc_fixed.o \ nvpair_alloc_spl.o zfs-objs += $(addprefix nvpair/,$(NVPAIR_OBJS)) UNICODE_OBJS := \ u8_textprep.o zfs-objs += $(addprefix unicode/,$(UNICODE_OBJS)) ZCOMMON_OBJS := \ cityhash.o \ simd_stat.o \ zfeature_common.o \ zfs_comutil.o \ zfs_deleg.o \ zfs_fletcher.o \ zfs_fletcher_superscalar.o \ zfs_fletcher_superscalar4.o \ zfs_namecheck.o \ zfs_prop.o \ zfs_valstr.o \ zpool_prop.o \ zprop_common.o ZCOMMON_OBJS_X86 := \ zfs_fletcher_avx512.o \ zfs_fletcher_intel.o \ zfs_fletcher_sse.o ZCOMMON_OBJS_ARM64 := \ zfs_fletcher_aarch64_neon.o zfs-objs += $(addprefix zcommon/,$(ZCOMMON_OBJS)) zfs-$(CONFIG_X86) += $(addprefix zcommon/,$(ZCOMMON_OBJS_X86)) zfs-$(CONFIG_UML_X86)+= $(addprefix zcommon/,$(ZCOMMON_OBJS_X86)) zfs-$(CONFIG_ARM64) += $(addprefix zcommon/,$(ZCOMMON_OBJS_ARM64)) # Zstd uses -O3 by default, so we should follow ZFS_ZSTD_FLAGS := -O3 # -fno-tree-vectorize gets set for gcc in zstd/common/compiler.h # Set it for other compilers, too. ZFS_ZSTD_FLAGS += -fno-tree-vectorize # SSE register return with SSE disabled if -march=znverX is passed ZFS_ZSTD_FLAGS += -U__BMI__ # Quiet warnings about frame size due to unused code in unmodified zstd lib ZFS_ZSTD_FLAGS += -Wframe-larger-than=20480 ZSTD_OBJS := \ zfs_zstd.o \ zstd_sparc.o ZSTD_UPSTREAM_OBJS := \ lib/common/entropy_common.o \ lib/common/error_private.o \ lib/common/fse_decompress.o \ lib/common/pool.o \ lib/common/zstd_common.o \ lib/compress/fse_compress.o \ lib/compress/hist.o \ lib/compress/huf_compress.o \ lib/compress/zstd_compress.o \ lib/compress/zstd_compress_literals.o \ lib/compress/zstd_compress_sequences.o \ lib/compress/zstd_compress_superblock.o \ lib/compress/zstd_double_fast.o \ lib/compress/zstd_fast.o \ lib/compress/zstd_lazy.o \ lib/compress/zstd_ldm.o \ lib/compress/zstd_opt.o \ lib/decompress/huf_decompress.o \ lib/decompress/zstd_ddict.o \ lib/decompress/zstd_decompress.o \ lib/decompress/zstd_decompress_block.o zfs-objs += $(addprefix zstd/,$(ZSTD_OBJS) $(ZSTD_UPSTREAM_OBJS)) # Disable aarch64 neon SIMD instructions for kernel mode $(addprefix $(obj)/zstd/,$(ZSTD_OBJS) $(ZSTD_UPSTREAM_OBJS)) : ccflags-y += -I$(zstd_include) $(ZFS_ZSTD_FLAGS) $(addprefix $(obj)/zstd/,$(ZSTD_OBJS) $(ZSTD_UPSTREAM_OBJS)) : asflags-y += -I$(zstd_include) $(addprefix $(obj)/zstd/,$(ZSTD_UPSTREAM_OBJS)) : ccflags-y += -include $(zstd_include)/aarch64_compat.h -include $(zstd_include)/zstd_compat_wrapper.h -Wp,-w $(obj)/zstd/zfs_zstd.o : ccflags-y += -include $(zstd_include)/zstd_compat_wrapper.h ZFS_OBJS := \ abd.o \ aggsum.o \ arc.o \ blake3_zfs.o \ blkptr.o \ bplist.o \ bpobj.o \ bptree.o \ bqueue.o \ brt.o \ btree.o \ dataset_kstats.o \ dbuf.o \ dbuf_stats.o \ ddt.o \ ddt_log.o \ ddt_stats.o \ ddt_zap.o \ dmu.o \ dmu_direct.o \ dmu_diff.o \ dmu_object.o \ dmu_objset.o \ dmu_recv.o \ dmu_redact.o \ dmu_send.o \ dmu_traverse.o \ dmu_tx.o \ dmu_zfetch.o \ dnode.o \ dnode_sync.o \ dsl_bookmark.o \ dsl_crypt.o \ dsl_dataset.o \ dsl_deadlist.o \ dsl_deleg.o \ dsl_destroy.o \ dsl_dir.o \ dsl_pool.o \ dsl_prop.o \ dsl_scan.o \ dsl_synctask.o \ dsl_userhold.o \ edonr_zfs.o \ fm.o \ gzip.o \ hkdf.o \ lz4.o \ lz4_zfs.o \ lzjb.o \ metaslab.o \ mmp.o \ multilist.o \ objlist.o \ pathname.o \ range_tree.o \ refcount.o \ rrwlock.o \ sa.o \ sha2_zfs.o \ skein_zfs.o \ spa.o \ spa_checkpoint.o \ spa_config.o \ spa_errlog.o \ spa_history.o \ spa_log_spacemap.o \ spa_misc.o \ spa_stats.o \ space_map.o \ space_reftree.o \ txg.o \ uberblock.o \ unique.o \ vdev.o \ vdev_draid.o \ vdev_draid_rand.o \ vdev_file.o \ vdev_indirect.o \ vdev_indirect_births.o \ vdev_indirect_mapping.o \ vdev_initialize.o \ vdev_label.o \ vdev_mirror.o \ vdev_missing.o \ vdev_queue.o \ vdev_raidz.o \ vdev_raidz_math.o \ vdev_raidz_math_scalar.o \ vdev_rebuild.o \ vdev_removal.o \ vdev_root.o \ vdev_trim.o \ zap.o \ zap_leaf.o \ zap_micro.o \ zcp.o \ zcp_get.o \ zcp_global.o \ zcp_iter.o \ zcp_set.o \ zcp_synctask.o \ zfeature.o \ zfs_byteswap.o \ zfs_chksum.o \ + zfs_debug_common.o \ zfs_fm.o \ zfs_fuid.o \ zfs_impl.o \ zfs_ioctl.o \ zfs_log.o \ zfs_onexit.o \ zfs_quota.o \ zfs_ratelimit.o \ zfs_replay.o \ zfs_rlock.o \ zfs_sa.o \ zfs_vnops.o \ zfs_znode.o \ zil.o \ zio.o \ zio_checksum.o \ zio_compress.o \ zio_inject.o \ zle.o \ zrlock.o \ zthr.o \ zvol.o ZFS_OBJS_OS := \ abd_os.o \ arc_os.o \ mmp_os.o \ policy.o \ qat.o \ qat_compress.o \ qat_crypt.o \ spa_misc_os.o \ trace.o \ vdev_disk.o \ vdev_raidz.o \ vdev_label_os.o \ zfs_acl.o \ zfs_ctldir.o \ zfs_debug.o \ zfs_dir.o \ zfs_file_os.o \ zfs_ioctl_os.o \ zfs_racct.o \ zfs_sysfs.o \ zfs_uio.o \ zfs_vfsops.o \ zfs_vnops_os.o \ zfs_znode_os.o \ zio_crypt.o \ zpl_ctldir.o \ zpl_export.o \ zpl_file.o \ zpl_file_range.o \ zpl_inode.o \ zpl_super.o \ zpl_xattr.o \ zvol_os.o ZFS_OBJS_X86 := \ vdev_raidz_math_avx2.o \ vdev_raidz_math_avx512bw.o \ vdev_raidz_math_avx512f.o \ vdev_raidz_math_sse2.o \ vdev_raidz_math_ssse3.o ZFS_OBJS_ARM64 := \ vdev_raidz_math_aarch64_neon.o \ vdev_raidz_math_aarch64_neonx2.o ZFS_OBJS_PPC_PPC64 := \ vdev_raidz_math_powerpc_altivec.o zfs-objs += $(addprefix zfs/,$(ZFS_OBJS)) $(addprefix os/linux/zfs/,$(ZFS_OBJS_OS)) zfs-$(CONFIG_X86) += $(addprefix zfs/,$(ZFS_OBJS_X86)) zfs-$(CONFIG_UML_X86)+= $(addprefix zfs/,$(ZFS_OBJS_X86)) zfs-$(CONFIG_ARM64) += $(addprefix zfs/,$(ZFS_OBJS_ARM64)) zfs-$(CONFIG_PPC) += $(addprefix zfs/,$(ZFS_OBJS_PPC_PPC64)) zfs-$(CONFIG_PPC64) += $(addprefix zfs/,$(ZFS_OBJS_PPC_PPC64)) UBSAN_SANITIZE_zap_leaf.o := n UBSAN_SANITIZE_zap_micro.o := n UBSAN_SANITIZE_sa.o := n UBSAN_SANITIZE_zfs/zap_micro.o := n UBSAN_SANITIZE_zfs/sa.o := n # Suppress incorrect warnings from versions of objtool which are not # aware of x86 EVEX prefix instructions used for AVX512. OBJECT_FILES_NON_STANDARD_vdev_raidz_math_avx512bw.o := y OBJECT_FILES_NON_STANDARD_vdev_raidz_math_avx512f.o := y ifeq ($(CONFIG_ALTIVEC),y) $(obj)/zfs/vdev_raidz_math_powerpc_altivec.o : c_flags += -maltivec endif diff --git a/module/nvpair/nvpair.c b/module/nvpair/nvpair.c index db1b595a6be7..811cfc87d7a4 100644 --- a/module/nvpair/nvpair.c +++ b/module/nvpair/nvpair.c @@ -1,3796 +1,4032 @@ // SPDX-License-Identifier: CDDL-1.0 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017 by Delphix. All rights reserved. * Copyright 2018 RackTop Systems. */ /* * Links to Illumos.org for more information on Interface Libraries: * [1] https://illumos.org/man/3lib/libnvpair * [2] https://illumos.org/man/3nvpair/nvlist_alloc * [3] https://illumos.org/man/9f/nvlist_alloc * [4] https://illumos.org/man/9f/nvlist_next_nvpair * [5] https://illumos.org/man/9f/nvpair_value_byte */ #include #include #include #include #include #include #include #include #include #include #if defined(_KERNEL) #include #include #else #include #include #include #endif #define skip_whitespace(p) while ((*(p) == ' ') || (*(p) == '\t')) (p)++ /* * nvpair.c - Provides kernel & userland interfaces for manipulating * name-value pairs. * * Overview Diagram * * +--------------+ * | nvlist_t | * |--------------| * | nvl_version | * | nvl_nvflag | * | nvl_priv -+-+ * | nvl_flag | | * | nvl_pad | | * +--------------+ | * V * +--------------+ last i_nvp in list * | nvpriv_t | +---------------------> * |--------------| | * +--+- nvp_list | | +------------+ * | | nvp_last -+--+ + nv_alloc_t | * | | nvp_curr | |------------| * | | nvp_nva -+----> | nva_ops | * | | nvp_stat | | nva_arg | * | +--------------+ +------------+ * | * +-------+ * V * +---------------------+ +-------------------+ * | i_nvp_t | +-->| i_nvp_t | +--> * |---------------------| | |-------------------| | * | nvi_next -+--+ | nvi_next -+--+ * | nvi_prev (NULL) | <----+ nvi_prev | * | . . . . . . . . . . | | . . . . . . . . . | * | nvp (nvpair_t) | | nvp (nvpair_t) | * | - nvp_size | | - nvp_size | * | - nvp_name_sz | | - nvp_name_sz | * | - nvp_value_elem | | - nvp_value_elem | * | - nvp_type | | - nvp_type | * | - data ... | | - data ... | * +---------------------+ +-------------------+ * * * * +---------------------+ +---------------------+ * | i_nvp_t | +--> +-->| i_nvp_t (last) | * |---------------------| | | |---------------------| * | nvi_next -+--+ ... --+ | nvi_next (NULL) | * <-+- nvi_prev |<-- ... <----+ nvi_prev | * | . . . . . . . . . | | . . . . . . . . . | * | nvp (nvpair_t) | | nvp (nvpair_t) | * | - nvp_size | | - nvp_size | * | - nvp_name_sz | | - nvp_name_sz | * | - nvp_value_elem | | - nvp_value_elem | * | - DATA_TYPE_NVLIST | | - nvp_type | * | - data (embedded) | | - data ... | * | nvlist name | +---------------------+ * | +--------------+ | * | | nvlist_t | | * | |--------------| | * | | nvl_version | | * | | nvl_nvflag | | * | | nvl_priv --+---+----> * | | nvl_flag | | * | | nvl_pad | | * | +--------------+ | * +---------------------+ * * * N.B. nvpair_t may be aligned on 4 byte boundary, so +4 will * allow value to be aligned on 8 byte boundary * * name_len is the length of the name string including the null terminator * so it must be >= 1 */ #define NVP_SIZE_CALC(name_len, data_len) \ (NV_ALIGN((sizeof (nvpair_t)) + name_len) + NV_ALIGN(data_len)) static int i_get_value_size(data_type_t type, const void *data, uint_t nelem); static int nvlist_add_common(nvlist_t *nvl, const char *name, data_type_t type, uint_t nelem, const void *data); #define NV_STAT_EMBEDDED 0x1 #define EMBEDDED_NVL(nvp) ((nvlist_t *)(void *)NVP_VALUE(nvp)) #define EMBEDDED_NVL_ARRAY(nvp) ((nvlist_t **)(void *)NVP_VALUE(nvp)) #define NVP_VALOFF(nvp) (NV_ALIGN(sizeof (nvpair_t) + (nvp)->nvp_name_sz)) #define NVPAIR2I_NVP(nvp) \ ((i_nvp_t *)((size_t)(nvp) - offsetof(i_nvp_t, nvi_nvp))) #ifdef _KERNEL static const int nvpair_max_recursion = 20; #else static const int nvpair_max_recursion = 100; #endif static const uint64_t nvlist_hashtable_init_size = (1 << 4); int nv_alloc_init(nv_alloc_t *nva, const nv_alloc_ops_t *nvo, /* args */ ...) { va_list valist; int err = 0; nva->nva_ops = nvo; nva->nva_arg = NULL; va_start(valist, nvo); if (nva->nva_ops->nv_ao_init != NULL) err = nva->nva_ops->nv_ao_init(nva, valist); va_end(valist); return (err); } void nv_alloc_reset(nv_alloc_t *nva) { if (nva->nva_ops->nv_ao_reset != NULL) nva->nva_ops->nv_ao_reset(nva); } void nv_alloc_fini(nv_alloc_t *nva) { if (nva->nva_ops->nv_ao_fini != NULL) nva->nva_ops->nv_ao_fini(nva); } nv_alloc_t * nvlist_lookup_nv_alloc(nvlist_t *nvl) { nvpriv_t *priv; if (nvl == NULL || (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) return (NULL); return (priv->nvp_nva); } static void * nv_mem_zalloc(nvpriv_t *nvp, size_t size) { nv_alloc_t *nva = nvp->nvp_nva; void *buf; if ((buf = nva->nva_ops->nv_ao_alloc(nva, size)) != NULL) memset(buf, 0, size); return (buf); } static void nv_mem_free(nvpriv_t *nvp, void *buf, size_t size) { nv_alloc_t *nva = nvp->nvp_nva; nva->nva_ops->nv_ao_free(nva, buf, size); } static void nv_priv_init(nvpriv_t *priv, nv_alloc_t *nva, uint32_t stat) { memset(priv, 0, sizeof (nvpriv_t)); priv->nvp_nva = nva; priv->nvp_stat = stat; } static nvpriv_t * nv_priv_alloc(nv_alloc_t *nva) { nvpriv_t *priv; /* * nv_mem_alloc() cannot called here because it needs the priv * argument. */ if ((priv = nva->nva_ops->nv_ao_alloc(nva, sizeof (nvpriv_t))) == NULL) return (NULL); nv_priv_init(priv, nva, 0); return (priv); } /* * Embedded lists need their own nvpriv_t's. We create a new * nvpriv_t using the parameters and allocator from the parent * list's nvpriv_t. */ static nvpriv_t * nv_priv_alloc_embedded(nvpriv_t *priv) { nvpriv_t *emb_priv; if ((emb_priv = nv_mem_zalloc(priv, sizeof (nvpriv_t))) == NULL) return (NULL); nv_priv_init(emb_priv, priv->nvp_nva, NV_STAT_EMBEDDED); return (emb_priv); } static int nvt_tab_alloc(nvpriv_t *priv, uint64_t buckets) { ASSERT3P(priv->nvp_hashtable, ==, NULL); ASSERT0(priv->nvp_nbuckets); ASSERT0(priv->nvp_nentries); i_nvp_t **tab = nv_mem_zalloc(priv, buckets * sizeof (i_nvp_t *)); if (tab == NULL) return (ENOMEM); priv->nvp_hashtable = tab; priv->nvp_nbuckets = buckets; return (0); } static void nvt_tab_free(nvpriv_t *priv) { i_nvp_t **tab = priv->nvp_hashtable; if (tab == NULL) { ASSERT0(priv->nvp_nbuckets); ASSERT0(priv->nvp_nentries); return; } nv_mem_free(priv, tab, priv->nvp_nbuckets * sizeof (i_nvp_t *)); priv->nvp_hashtable = NULL; priv->nvp_nbuckets = 0; priv->nvp_nentries = 0; } static uint32_t nvt_hash(const char *p) { uint32_t g, hval = 0; while (*p) { hval = (hval << 4) + *p++; if ((g = (hval & 0xf0000000)) != 0) hval ^= g >> 24; hval &= ~g; } return (hval); } static boolean_t nvt_nvpair_match(const nvpair_t *nvp1, const nvpair_t *nvp2, uint32_t nvflag) { boolean_t match = B_FALSE; if (nvflag & NV_UNIQUE_NAME_TYPE) { if (strcmp(NVP_NAME(nvp1), NVP_NAME(nvp2)) == 0 && NVP_TYPE(nvp1) == NVP_TYPE(nvp2)) match = B_TRUE; } else { ASSERT(nvflag == 0 || nvflag & NV_UNIQUE_NAME); if (strcmp(NVP_NAME(nvp1), NVP_NAME(nvp2)) == 0) match = B_TRUE; } return (match); } static nvpair_t * nvt_lookup_name_type(const nvlist_t *nvl, const char *name, data_type_t type) { const nvpriv_t *priv = (const nvpriv_t *)(uintptr_t)nvl->nvl_priv; ASSERT(priv != NULL); i_nvp_t **tab = priv->nvp_hashtable; if (tab == NULL) { ASSERT3P(priv->nvp_list, ==, NULL); ASSERT0(priv->nvp_nbuckets); ASSERT0(priv->nvp_nentries); return (NULL); } else { ASSERT(priv->nvp_nbuckets != 0); } uint64_t hash = nvt_hash(name); uint64_t index = hash & (priv->nvp_nbuckets - 1); ASSERT3U(index, <, priv->nvp_nbuckets); i_nvp_t *entry = tab[index]; for (i_nvp_t *e = entry; e != NULL; e = e->nvi_hashtable_next) { if (strcmp(NVP_NAME(&e->nvi_nvp), name) == 0 && (type == DATA_TYPE_DONTCARE || NVP_TYPE(&e->nvi_nvp) == type)) return (&e->nvi_nvp); } return (NULL); } static nvpair_t * nvt_lookup_name(const nvlist_t *nvl, const char *name) { return (nvt_lookup_name_type(nvl, name, DATA_TYPE_DONTCARE)); } static int nvt_resize(nvpriv_t *priv, uint32_t new_size) { i_nvp_t **tab = priv->nvp_hashtable; /* * Migrate all the entries from the current table * to a newly-allocated table with the new size by * re-adjusting the pointers of their entries. */ uint32_t size = priv->nvp_nbuckets; uint32_t new_mask = new_size - 1; ASSERT(ISP2(new_size)); i_nvp_t **new_tab = nv_mem_zalloc(priv, new_size * sizeof (i_nvp_t *)); if (new_tab == NULL) return (ENOMEM); uint32_t nentries = 0; for (uint32_t i = 0; i < size; i++) { i_nvp_t *next, *e = tab[i]; while (e != NULL) { next = e->nvi_hashtable_next; uint32_t hash = nvt_hash(NVP_NAME(&e->nvi_nvp)); uint32_t index = hash & new_mask; e->nvi_hashtable_next = new_tab[index]; new_tab[index] = e; nentries++; e = next; } tab[i] = NULL; } ASSERT3U(nentries, ==, priv->nvp_nentries); nvt_tab_free(priv); priv->nvp_hashtable = new_tab; priv->nvp_nbuckets = new_size; priv->nvp_nentries = nentries; return (0); } static boolean_t nvt_needs_togrow(nvpriv_t *priv) { /* * Grow only when we have more elements than buckets * and the # of buckets doesn't overflow. */ return (priv->nvp_nentries > priv->nvp_nbuckets && (UINT32_MAX >> 1) >= priv->nvp_nbuckets); } /* * Allocate a new table that's twice the size of the old one, * and migrate all the entries from the old one to the new * one by re-adjusting their pointers. */ static int nvt_grow(nvpriv_t *priv) { uint32_t current_size = priv->nvp_nbuckets; /* ensure we won't overflow */ ASSERT3U(UINT32_MAX >> 1, >=, current_size); return (nvt_resize(priv, current_size << 1)); } static boolean_t nvt_needs_toshrink(nvpriv_t *priv) { /* * Shrink only when the # of elements is less than or * equal to 1/4 the # of buckets. Never shrink less than * nvlist_hashtable_init_size. */ ASSERT3U(priv->nvp_nbuckets, >=, nvlist_hashtable_init_size); if (priv->nvp_nbuckets == nvlist_hashtable_init_size) return (B_FALSE); return (priv->nvp_nentries <= (priv->nvp_nbuckets >> 2)); } /* * Allocate a new table that's half the size of the old one, * and migrate all the entries from the old one to the new * one by re-adjusting their pointers. */ static int nvt_shrink(nvpriv_t *priv) { uint32_t current_size = priv->nvp_nbuckets; /* ensure we won't overflow */ ASSERT3U(current_size, >=, nvlist_hashtable_init_size); return (nvt_resize(priv, current_size >> 1)); } static int nvt_remove_nvpair(nvlist_t *nvl, const nvpair_t *nvp) { nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; if (nvt_needs_toshrink(priv)) { int err = nvt_shrink(priv); if (err != 0) return (err); } i_nvp_t **tab = priv->nvp_hashtable; const char *name = NVP_NAME(nvp); uint64_t hash = nvt_hash(name); uint64_t index = hash & (priv->nvp_nbuckets - 1); ASSERT3U(index, <, priv->nvp_nbuckets); i_nvp_t *bucket = tab[index]; for (i_nvp_t *prev = NULL, *e = bucket; e != NULL; prev = e, e = e->nvi_hashtable_next) { if (nvt_nvpair_match(&e->nvi_nvp, nvp, nvl->nvl_nvflag)) { if (prev != NULL) { prev->nvi_hashtable_next = e->nvi_hashtable_next; } else { ASSERT3P(e, ==, bucket); tab[index] = e->nvi_hashtable_next; } e->nvi_hashtable_next = NULL; priv->nvp_nentries--; break; } } return (0); } static int nvt_add_nvpair(nvlist_t *nvl, nvpair_t *nvp) { nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; /* initialize nvpair table now if it doesn't exist. */ if (priv->nvp_hashtable == NULL) { int err = nvt_tab_alloc(priv, nvlist_hashtable_init_size); if (err != 0) return (err); } /* * if we don't allow duplicate entries, make sure to * unlink any existing entries from the table. */ if (nvl->nvl_nvflag != 0) { int err = nvt_remove_nvpair(nvl, nvp); if (err != 0) return (err); } if (nvt_needs_togrow(priv)) { int err = nvt_grow(priv); if (err != 0) return (err); } i_nvp_t **tab = priv->nvp_hashtable; const char *name = NVP_NAME(nvp); uint64_t hash = nvt_hash(name); uint64_t index = hash & (priv->nvp_nbuckets - 1); ASSERT3U(index, <, priv->nvp_nbuckets); // cppcheck-suppress nullPointerRedundantCheck i_nvp_t *bucket = tab[index]; /* insert link at the beginning of the bucket */ i_nvp_t *new_entry = NVPAIR2I_NVP(nvp); ASSERT3P(new_entry->nvi_hashtable_next, ==, NULL); new_entry->nvi_hashtable_next = bucket; // cppcheck-suppress nullPointerRedundantCheck tab[index] = new_entry; priv->nvp_nentries++; return (0); } static void nvlist_init(nvlist_t *nvl, uint32_t nvflag, nvpriv_t *priv) { nvl->nvl_version = NV_VERSION; nvl->nvl_nvflag = nvflag & (NV_UNIQUE_NAME|NV_UNIQUE_NAME_TYPE); nvl->nvl_priv = (uint64_t)(uintptr_t)priv; nvl->nvl_flag = 0; nvl->nvl_pad = 0; } uint_t nvlist_nvflag(nvlist_t *nvl) { return (nvl->nvl_nvflag); } static nv_alloc_t * nvlist_nv_alloc(int kmflag) { #if defined(_KERNEL) switch (kmflag) { case KM_SLEEP: return (nv_alloc_sleep); case KM_NOSLEEP: return (nv_alloc_nosleep); default: return (nv_alloc_pushpage); } #else (void) kmflag; return (nv_alloc_nosleep); #endif /* _KERNEL */ } /* * nvlist_alloc - Allocate nvlist. */ int nvlist_alloc(nvlist_t **nvlp, uint_t nvflag, int kmflag) { return (nvlist_xalloc(nvlp, nvflag, nvlist_nv_alloc(kmflag))); } int nvlist_xalloc(nvlist_t **nvlp, uint_t nvflag, nv_alloc_t *nva) { nvpriv_t *priv; if (nvlp == NULL || nva == NULL) return (EINVAL); if ((priv = nv_priv_alloc(nva)) == NULL) return (ENOMEM); if ((*nvlp = nv_mem_zalloc(priv, NV_ALIGN(sizeof (nvlist_t)))) == NULL) { nv_mem_free(priv, priv, sizeof (nvpriv_t)); return (ENOMEM); } nvlist_init(*nvlp, nvflag, priv); return (0); } /* * nvp_buf_alloc - Allocate i_nvp_t for storing a new nv pair. */ static nvpair_t * nvp_buf_alloc(nvlist_t *nvl, size_t len) { nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; i_nvp_t *buf; nvpair_t *nvp; size_t nvsize; /* * Allocate the buffer */ nvsize = len + offsetof(i_nvp_t, nvi_nvp); if ((buf = nv_mem_zalloc(priv, nvsize)) == NULL) return (NULL); nvp = &buf->nvi_nvp; nvp->nvp_size = len; return (nvp); } /* * nvp_buf_free - de-Allocate an i_nvp_t. */ static void nvp_buf_free(nvlist_t *nvl, nvpair_t *nvp) { nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; size_t nvsize = nvp->nvp_size + offsetof(i_nvp_t, nvi_nvp); nv_mem_free(priv, NVPAIR2I_NVP(nvp), nvsize); } /* * nvp_buf_link - link a new nv pair into the nvlist. */ static void nvp_buf_link(nvlist_t *nvl, nvpair_t *nvp) { nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; i_nvp_t *curr = NVPAIR2I_NVP(nvp); /* Put element at end of nvlist */ if (priv->nvp_list == NULL) { priv->nvp_list = priv->nvp_last = curr; } else { curr->nvi_prev = priv->nvp_last; priv->nvp_last->nvi_next = curr; priv->nvp_last = curr; } } /* * nvp_buf_unlink - unlink an removed nvpair out of the nvlist. */ static void nvp_buf_unlink(nvlist_t *nvl, nvpair_t *nvp) { nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; i_nvp_t *curr = NVPAIR2I_NVP(nvp); /* * protect nvlist_next_nvpair() against walking on freed memory. */ if (priv->nvp_curr == curr) priv->nvp_curr = curr->nvi_next; if (curr == priv->nvp_list) priv->nvp_list = curr->nvi_next; else curr->nvi_prev->nvi_next = curr->nvi_next; if (curr == priv->nvp_last) priv->nvp_last = curr->nvi_prev; else curr->nvi_next->nvi_prev = curr->nvi_prev; } /* * take a nvpair type and number of elements and make sure the are valid */ static int i_validate_type_nelem(data_type_t type, uint_t nelem) { switch (type) { case DATA_TYPE_BOOLEAN: if (nelem != 0) return (EINVAL); break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: case DATA_TYPE_INT16: case DATA_TYPE_UINT16: case DATA_TYPE_INT32: case DATA_TYPE_UINT32: case DATA_TYPE_INT64: case DATA_TYPE_UINT64: case DATA_TYPE_STRING: case DATA_TYPE_HRTIME: case DATA_TYPE_NVLIST: #if !defined(_KERNEL) case DATA_TYPE_DOUBLE: #endif if (nelem != 1) return (EINVAL); break; case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_BYTE_ARRAY: case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: case DATA_TYPE_INT16_ARRAY: case DATA_TYPE_UINT16_ARRAY: case DATA_TYPE_INT32_ARRAY: case DATA_TYPE_UINT32_ARRAY: case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: case DATA_TYPE_STRING_ARRAY: case DATA_TYPE_NVLIST_ARRAY: /* we allow arrays with 0 elements */ break; default: return (EINVAL); } return (0); } /* * Verify nvp_name_sz and check the name string length. */ static int i_validate_nvpair_name(nvpair_t *nvp) { if ((nvp->nvp_name_sz <= 0) || (nvp->nvp_size < NVP_SIZE_CALC(nvp->nvp_name_sz, 0))) return (EFAULT); /* verify the name string, make sure its terminated */ if (NVP_NAME(nvp)[nvp->nvp_name_sz - 1] != '\0') return (EFAULT); return (strlen(NVP_NAME(nvp)) == nvp->nvp_name_sz - 1 ? 0 : EFAULT); } static int i_validate_nvpair_value(data_type_t type, uint_t nelem, const void *data) { switch (type) { case DATA_TYPE_BOOLEAN_VALUE: if (*(boolean_t *)data != B_TRUE && *(boolean_t *)data != B_FALSE) return (EINVAL); break; case DATA_TYPE_BOOLEAN_ARRAY: { int i; for (i = 0; i < nelem; i++) if (((boolean_t *)data)[i] != B_TRUE && ((boolean_t *)data)[i] != B_FALSE) return (EINVAL); break; } default: break; } return (0); } /* * This function takes a pointer to what should be a nvpair and it's size * and then verifies that all the nvpair fields make sense and can be * trusted. This function is used when decoding packed nvpairs. */ static int i_validate_nvpair(nvpair_t *nvp) { data_type_t type = NVP_TYPE(nvp); int size1, size2; /* verify nvp_name_sz, check the name string length */ if (i_validate_nvpair_name(nvp) != 0) return (EFAULT); if (i_validate_nvpair_value(type, NVP_NELEM(nvp), NVP_VALUE(nvp)) != 0) return (EFAULT); /* * verify nvp_type, nvp_value_elem, and also possibly * verify string values and get the value size. */ size2 = i_get_value_size(type, NVP_VALUE(nvp), NVP_NELEM(nvp)); size1 = nvp->nvp_size - NVP_VALOFF(nvp); if (size2 < 0 || size1 != NV_ALIGN(size2)) return (EFAULT); return (0); } static int nvlist_copy_pairs(const nvlist_t *snvl, nvlist_t *dnvl) { const nvpriv_t *priv; const i_nvp_t *curr; if ((priv = (const nvpriv_t *)(uintptr_t)snvl->nvl_priv) == NULL) return (EINVAL); for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { const nvpair_t *nvp = &curr->nvi_nvp; int err; if ((err = nvlist_add_common(dnvl, NVP_NAME(nvp), NVP_TYPE(nvp), NVP_NELEM(nvp), NVP_VALUE(nvp))) != 0) return (err); } return (0); } /* * Frees all memory allocated for an nvpair (like embedded lists) with * the exception of the nvpair buffer itself. */ static void nvpair_free(nvpair_t *nvp) { switch (NVP_TYPE(nvp)) { case DATA_TYPE_NVLIST: nvlist_free(EMBEDDED_NVL(nvp)); break; case DATA_TYPE_NVLIST_ARRAY: { nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp); int i; for (i = 0; i < NVP_NELEM(nvp); i++) if (nvlp[i] != NULL) nvlist_free(nvlp[i]); break; } default: break; } } /* * nvlist_free - free an unpacked nvlist */ void nvlist_free(nvlist_t *nvl) { nvpriv_t *priv; i_nvp_t *curr; if (nvl == NULL || (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) return; /* * Unpacked nvlist are linked through i_nvp_t */ curr = priv->nvp_list; while (curr != NULL) { nvpair_t *nvp = &curr->nvi_nvp; curr = curr->nvi_next; nvpair_free(nvp); nvp_buf_free(nvl, nvp); } if (!(priv->nvp_stat & NV_STAT_EMBEDDED)) nv_mem_free(priv, nvl, NV_ALIGN(sizeof (nvlist_t))); else nvl->nvl_priv = 0; nvt_tab_free(priv); nv_mem_free(priv, priv, sizeof (nvpriv_t)); } static int nvlist_contains_nvp(const nvlist_t *nvl, const nvpair_t *nvp) { const nvpriv_t *priv = (const nvpriv_t *)(uintptr_t)nvl->nvl_priv; const i_nvp_t *curr; if (nvp == NULL) return (0); for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) if (&curr->nvi_nvp == nvp) return (1); return (0); } /* * Make a copy of nvlist */ int nvlist_dup(const nvlist_t *nvl, nvlist_t **nvlp, int kmflag) { return (nvlist_xdup(nvl, nvlp, nvlist_nv_alloc(kmflag))); } int nvlist_xdup(const nvlist_t *nvl, nvlist_t **nvlp, nv_alloc_t *nva) { int err; nvlist_t *ret; if (nvl == NULL || nvlp == NULL) return (EINVAL); if ((err = nvlist_xalloc(&ret, nvl->nvl_nvflag, nva)) != 0) return (err); if ((err = nvlist_copy_pairs(nvl, ret)) != 0) nvlist_free(ret); else *nvlp = ret; return (err); } /* * Remove all with matching name */ int nvlist_remove_all(nvlist_t *nvl, const char *name) { int error = ENOENT; if (nvl == NULL || name == NULL || nvl->nvl_priv == 0) return (EINVAL); nvpair_t *nvp; while ((nvp = nvt_lookup_name(nvl, name)) != NULL) { VERIFY0(nvlist_remove_nvpair(nvl, nvp)); error = 0; } return (error); } /* * Remove first one with matching name and type */ int nvlist_remove(nvlist_t *nvl, const char *name, data_type_t type) { if (nvl == NULL || name == NULL || nvl->nvl_priv == 0) return (EINVAL); nvpair_t *nvp = nvt_lookup_name_type(nvl, name, type); if (nvp == NULL) return (ENOENT); return (nvlist_remove_nvpair(nvl, nvp)); } int nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp) { if (nvl == NULL || nvp == NULL) return (EINVAL); int err = nvt_remove_nvpair(nvl, nvp); if (err != 0) return (err); nvp_buf_unlink(nvl, nvp); nvpair_free(nvp); nvp_buf_free(nvl, nvp); return (0); } /* * This function calculates the size of an nvpair value. * * The data argument controls the behavior in case of the data types * DATA_TYPE_STRING and * DATA_TYPE_STRING_ARRAY * Is data == NULL then the size of the string(s) is excluded. */ static int i_get_value_size(data_type_t type, const void *data, uint_t nelem) { uint64_t value_sz; if (i_validate_type_nelem(type, nelem) != 0) return (-1); /* Calculate required size for holding value */ switch (type) { case DATA_TYPE_BOOLEAN: value_sz = 0; break; case DATA_TYPE_BOOLEAN_VALUE: value_sz = sizeof (boolean_t); break; case DATA_TYPE_BYTE: value_sz = sizeof (uchar_t); break; case DATA_TYPE_INT8: value_sz = sizeof (int8_t); break; case DATA_TYPE_UINT8: value_sz = sizeof (uint8_t); break; case DATA_TYPE_INT16: value_sz = sizeof (int16_t); break; case DATA_TYPE_UINT16: value_sz = sizeof (uint16_t); break; case DATA_TYPE_INT32: value_sz = sizeof (int32_t); break; case DATA_TYPE_UINT32: value_sz = sizeof (uint32_t); break; case DATA_TYPE_INT64: value_sz = sizeof (int64_t); break; case DATA_TYPE_UINT64: value_sz = sizeof (uint64_t); break; #if !defined(_KERNEL) case DATA_TYPE_DOUBLE: value_sz = sizeof (double); break; #endif case DATA_TYPE_STRING: if (data == NULL) value_sz = 0; else value_sz = strlen(data) + 1; break; case DATA_TYPE_BOOLEAN_ARRAY: value_sz = (uint64_t)nelem * sizeof (boolean_t); break; case DATA_TYPE_BYTE_ARRAY: value_sz = (uint64_t)nelem * sizeof (uchar_t); break; case DATA_TYPE_INT8_ARRAY: value_sz = (uint64_t)nelem * sizeof (int8_t); break; case DATA_TYPE_UINT8_ARRAY: value_sz = (uint64_t)nelem * sizeof (uint8_t); break; case DATA_TYPE_INT16_ARRAY: value_sz = (uint64_t)nelem * sizeof (int16_t); break; case DATA_TYPE_UINT16_ARRAY: value_sz = (uint64_t)nelem * sizeof (uint16_t); break; case DATA_TYPE_INT32_ARRAY: value_sz = (uint64_t)nelem * sizeof (int32_t); break; case DATA_TYPE_UINT32_ARRAY: value_sz = (uint64_t)nelem * sizeof (uint32_t); break; case DATA_TYPE_INT64_ARRAY: value_sz = (uint64_t)nelem * sizeof (int64_t); break; case DATA_TYPE_UINT64_ARRAY: value_sz = (uint64_t)nelem * sizeof (uint64_t); break; case DATA_TYPE_STRING_ARRAY: value_sz = (uint64_t)nelem * sizeof (uint64_t); if (data != NULL) { char *const *strs = data; uint_t i; /* no alignment requirement for strings */ for (i = 0; i < nelem; i++) { if (strs[i] == NULL) return (-1); value_sz += strlen(strs[i]) + 1; } } break; case DATA_TYPE_HRTIME: value_sz = sizeof (hrtime_t); break; case DATA_TYPE_NVLIST: value_sz = NV_ALIGN(sizeof (nvlist_t)); break; case DATA_TYPE_NVLIST_ARRAY: value_sz = (uint64_t)nelem * sizeof (uint64_t) + (uint64_t)nelem * NV_ALIGN(sizeof (nvlist_t)); break; default: return (-1); } return (value_sz > INT32_MAX ? -1 : (int)value_sz); } static int nvlist_copy_embedded(nvlist_t *nvl, nvlist_t *onvl, nvlist_t *emb_nvl) { nvpriv_t *priv; int err; if ((priv = nv_priv_alloc_embedded((nvpriv_t *)(uintptr_t) nvl->nvl_priv)) == NULL) return (ENOMEM); nvlist_init(emb_nvl, onvl->nvl_nvflag, priv); if ((err = nvlist_copy_pairs(onvl, emb_nvl)) != 0) { nvlist_free(emb_nvl); emb_nvl->nvl_priv = 0; } return (err); } /* * nvlist_add_common - Add new pair to nvlist */ static int nvlist_add_common(nvlist_t *nvl, const char *name, data_type_t type, uint_t nelem, const void *data) { nvpair_t *nvp; uint_t i; int nvp_sz, name_sz, value_sz; int err = 0; if (name == NULL || nvl == NULL || nvl->nvl_priv == 0) return (EINVAL); if (nelem != 0 && data == NULL) return (EINVAL); /* * Verify type and nelem and get the value size. * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY * is the size of the string(s) included. */ if ((value_sz = i_get_value_size(type, data, nelem)) < 0) return (EINVAL); if (i_validate_nvpair_value(type, nelem, data) != 0) return (EINVAL); /* * If we're adding an nvlist or nvlist array, ensure that we are not * adding the input nvlist to itself, which would cause recursion, * and ensure that no NULL nvlist pointers are present. */ switch (type) { case DATA_TYPE_NVLIST: if (data == nvl || data == NULL) return (EINVAL); break; case DATA_TYPE_NVLIST_ARRAY: { nvlist_t **onvlp = (nvlist_t **)data; for (i = 0; i < nelem; i++) { if (onvlp[i] == nvl || onvlp[i] == NULL) return (EINVAL); } break; } default: break; } /* calculate sizes of the nvpair elements and the nvpair itself */ name_sz = strlen(name) + 1; if (name_sz >= 1ULL << (sizeof (nvp->nvp_name_sz) * NBBY - 1)) return (EINVAL); nvp_sz = NVP_SIZE_CALC(name_sz, value_sz); if ((nvp = nvp_buf_alloc(nvl, nvp_sz)) == NULL) return (ENOMEM); ASSERT(nvp->nvp_size == nvp_sz); nvp->nvp_name_sz = name_sz; nvp->nvp_value_elem = nelem; nvp->nvp_type = type; memcpy(NVP_NAME(nvp), name, name_sz); switch (type) { case DATA_TYPE_BOOLEAN: break; case DATA_TYPE_STRING_ARRAY: { char *const *strs = data; char *buf = NVP_VALUE(nvp); char **cstrs = (void *)buf; /* skip pre-allocated space for pointer array */ buf += nelem * sizeof (uint64_t); for (i = 0; i < nelem; i++) { int slen = strlen(strs[i]) + 1; memcpy(buf, strs[i], slen); cstrs[i] = buf; buf += slen; } break; } case DATA_TYPE_NVLIST: { nvlist_t *nnvl = EMBEDDED_NVL(nvp); nvlist_t *onvl = (nvlist_t *)data; if ((err = nvlist_copy_embedded(nvl, onvl, nnvl)) != 0) { nvp_buf_free(nvl, nvp); return (err); } break; } case DATA_TYPE_NVLIST_ARRAY: { nvlist_t **onvlp = (nvlist_t **)data; nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp); nvlist_t *embedded = (nvlist_t *) ((uintptr_t)nvlp + nelem * sizeof (uint64_t)); for (i = 0; i < nelem; i++) { if ((err = nvlist_copy_embedded(nvl, onvlp[i], embedded)) != 0) { /* * Free any successfully created lists */ nvpair_free(nvp); nvp_buf_free(nvl, nvp); return (err); } nvlp[i] = embedded++; } break; } default: memcpy(NVP_VALUE(nvp), data, value_sz); } /* if unique name, remove before add */ if (nvl->nvl_nvflag & NV_UNIQUE_NAME) (void) nvlist_remove_all(nvl, name); else if (nvl->nvl_nvflag & NV_UNIQUE_NAME_TYPE) (void) nvlist_remove(nvl, name, type); err = nvt_add_nvpair(nvl, nvp); if (err != 0) { nvpair_free(nvp); nvp_buf_free(nvl, nvp); return (err); } nvp_buf_link(nvl, nvp); return (0); } int nvlist_add_boolean(nvlist_t *nvl, const char *name) { return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN, 0, NULL)); } int nvlist_add_boolean_value(nvlist_t *nvl, const char *name, boolean_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_VALUE, 1, &val)); } int nvlist_add_byte(nvlist_t *nvl, const char *name, uchar_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE, 1, &val)); } int nvlist_add_int8(nvlist_t *nvl, const char *name, int8_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT8, 1, &val)); } int nvlist_add_uint8(nvlist_t *nvl, const char *name, uint8_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8, 1, &val)); } int nvlist_add_int16(nvlist_t *nvl, const char *name, int16_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT16, 1, &val)); } int nvlist_add_uint16(nvlist_t *nvl, const char *name, uint16_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16, 1, &val)); } int nvlist_add_int32(nvlist_t *nvl, const char *name, int32_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT32, 1, &val)); } int nvlist_add_uint32(nvlist_t *nvl, const char *name, uint32_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32, 1, &val)); } int nvlist_add_int64(nvlist_t *nvl, const char *name, int64_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT64, 1, &val)); } int nvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64, 1, &val)); } #if !defined(_KERNEL) int nvlist_add_double(nvlist_t *nvl, const char *name, double val) { return (nvlist_add_common(nvl, name, DATA_TYPE_DOUBLE, 1, &val)); } #endif int nvlist_add_string(nvlist_t *nvl, const char *name, const char *val) { return (nvlist_add_common(nvl, name, DATA_TYPE_STRING, 1, (void *)val)); } int nvlist_add_boolean_array(nvlist_t *nvl, const char *name, const boolean_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_ARRAY, n, a)); } int nvlist_add_byte_array(nvlist_t *nvl, const char *name, const uchar_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a)); } int nvlist_add_int8_array(nvlist_t *nvl, const char *name, const int8_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a)); } int nvlist_add_uint8_array(nvlist_t *nvl, const char *name, const uint8_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a)); } int nvlist_add_int16_array(nvlist_t *nvl, const char *name, const int16_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a)); } int nvlist_add_uint16_array(nvlist_t *nvl, const char *name, const uint16_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a)); } int nvlist_add_int32_array(nvlist_t *nvl, const char *name, const int32_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a)); } int nvlist_add_uint32_array(nvlist_t *nvl, const char *name, const uint32_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a)); } int nvlist_add_int64_array(nvlist_t *nvl, const char *name, const int64_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a)); } int nvlist_add_uint64_array(nvlist_t *nvl, const char *name, const uint64_t *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a)); } int nvlist_add_string_array(nvlist_t *nvl, const char *name, const char *const *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a)); } int nvlist_add_hrtime(nvlist_t *nvl, const char *name, hrtime_t val) { return (nvlist_add_common(nvl, name, DATA_TYPE_HRTIME, 1, &val)); } int nvlist_add_nvlist(nvlist_t *nvl, const char *name, const nvlist_t *val) { return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST, 1, val)); } int nvlist_add_nvlist_array(nvlist_t *nvl, const char *name, const nvlist_t * const *a, uint_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a)); } /* reading name-value pairs */ nvpair_t * nvlist_next_nvpair(nvlist_t *nvl, const nvpair_t *nvp) { nvpriv_t *priv; i_nvp_t *curr; if (nvl == NULL || (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) return (NULL); curr = NVPAIR2I_NVP(nvp); /* * Ensure that nvp is a valid nvpair on this nvlist. * NB: nvp_curr is used only as a hint so that we don't always * have to walk the list to determine if nvp is still on the list. */ if (nvp == NULL) curr = priv->nvp_list; else if (priv->nvp_curr == curr || nvlist_contains_nvp(nvl, nvp)) curr = curr->nvi_next; else curr = NULL; priv->nvp_curr = curr; return (curr != NULL ? &curr->nvi_nvp : NULL); } nvpair_t * nvlist_prev_nvpair(nvlist_t *nvl, const nvpair_t *nvp) { nvpriv_t *priv; i_nvp_t *curr; if (nvl == NULL || (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) return (NULL); curr = NVPAIR2I_NVP(nvp); if (nvp == NULL) curr = priv->nvp_last; else if (priv->nvp_curr == curr || nvlist_contains_nvp(nvl, nvp)) curr = curr->nvi_prev; else curr = NULL; priv->nvp_curr = curr; return (curr != NULL ? &curr->nvi_nvp : NULL); } boolean_t nvlist_empty(const nvlist_t *nvl) { const nvpriv_t *priv; if (nvl == NULL || (priv = (const nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) return (B_TRUE); return (priv->nvp_list == NULL); } const char * nvpair_name(const nvpair_t *nvp) { return (NVP_NAME(nvp)); } data_type_t nvpair_type(const nvpair_t *nvp) { return (NVP_TYPE(nvp)); } int nvpair_type_is_array(const nvpair_t *nvp) { data_type_t type = NVP_TYPE(nvp); if ((type == DATA_TYPE_BYTE_ARRAY) || (type == DATA_TYPE_INT8_ARRAY) || (type == DATA_TYPE_UINT8_ARRAY) || (type == DATA_TYPE_INT16_ARRAY) || (type == DATA_TYPE_UINT16_ARRAY) || (type == DATA_TYPE_INT32_ARRAY) || (type == DATA_TYPE_UINT32_ARRAY) || (type == DATA_TYPE_INT64_ARRAY) || (type == DATA_TYPE_UINT64_ARRAY) || (type == DATA_TYPE_BOOLEAN_ARRAY) || (type == DATA_TYPE_STRING_ARRAY) || (type == DATA_TYPE_NVLIST_ARRAY)) return (1); return (0); } static int nvpair_value_common(const nvpair_t *nvp, data_type_t type, uint_t *nelem, void *data) { int value_sz; if (nvp == NULL || nvpair_type(nvp) != type) return (EINVAL); /* * For non-array types, we copy the data. * For array types (including string), we set a pointer. */ switch (type) { case DATA_TYPE_BOOLEAN: if (nelem != NULL) *nelem = 0; break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: case DATA_TYPE_INT16: case DATA_TYPE_UINT16: case DATA_TYPE_INT32: case DATA_TYPE_UINT32: case DATA_TYPE_INT64: case DATA_TYPE_UINT64: case DATA_TYPE_HRTIME: #if !defined(_KERNEL) case DATA_TYPE_DOUBLE: #endif if (data == NULL) return (EINVAL); if ((value_sz = i_get_value_size(type, NULL, 1)) < 0) return (EINVAL); memcpy(data, NVP_VALUE(nvp), (size_t)value_sz); if (nelem != NULL) *nelem = 1; break; case DATA_TYPE_NVLIST: case DATA_TYPE_STRING: if (data == NULL) return (EINVAL); /* * This discards the const from nvp, so all callers for these * types must not accept const nvpairs. */ *(void **)data = (void *)NVP_VALUE(nvp); if (nelem != NULL) *nelem = 1; break; case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_BYTE_ARRAY: case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: case DATA_TYPE_INT16_ARRAY: case DATA_TYPE_UINT16_ARRAY: case DATA_TYPE_INT32_ARRAY: case DATA_TYPE_UINT32_ARRAY: case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: case DATA_TYPE_STRING_ARRAY: case DATA_TYPE_NVLIST_ARRAY: if (nelem == NULL || data == NULL) return (EINVAL); /* * This discards the const from nvp, so all callers for these * types must not accept const nvpairs. */ if ((*nelem = NVP_NELEM(nvp)) != 0) *(void **)data = (void *)NVP_VALUE(nvp); else *(void **)data = NULL; break; default: return (ENOTSUP); } return (0); } static int nvlist_lookup_common(const nvlist_t *nvl, const char *name, data_type_t type, uint_t *nelem, void *data) { if (name == NULL || nvl == NULL || nvl->nvl_priv == 0) return (EINVAL); if (!(nvl->nvl_nvflag & (NV_UNIQUE_NAME | NV_UNIQUE_NAME_TYPE))) return (ENOTSUP); nvpair_t *nvp = nvt_lookup_name_type(nvl, name, type); if (nvp == NULL) return (ENOENT); return (nvpair_value_common(nvp, type, nelem, data)); } int nvlist_lookup_boolean(const nvlist_t *nvl, const char *name) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_BOOLEAN, NULL, NULL)); } int nvlist_lookup_boolean_value(const nvlist_t *nvl, const char *name, boolean_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_BOOLEAN_VALUE, NULL, val)); } int nvlist_lookup_byte(const nvlist_t *nvl, const char *name, uchar_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_BYTE, NULL, val)); } int nvlist_lookup_int8(const nvlist_t *nvl, const char *name, int8_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT8, NULL, val)); } int nvlist_lookup_uint8(const nvlist_t *nvl, const char *name, uint8_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT8, NULL, val)); } int nvlist_lookup_int16(const nvlist_t *nvl, const char *name, int16_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT16, NULL, val)); } int nvlist_lookup_uint16(const nvlist_t *nvl, const char *name, uint16_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT16, NULL, val)); } int nvlist_lookup_int32(const nvlist_t *nvl, const char *name, int32_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT32, NULL, val)); } int nvlist_lookup_uint32(const nvlist_t *nvl, const char *name, uint32_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT32, NULL, val)); } int nvlist_lookup_int64(const nvlist_t *nvl, const char *name, int64_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT64, NULL, val)); } int nvlist_lookup_uint64(const nvlist_t *nvl, const char *name, uint64_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64, NULL, val)); } #if !defined(_KERNEL) int nvlist_lookup_double(const nvlist_t *nvl, const char *name, double *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_DOUBLE, NULL, val)); } #endif int nvlist_lookup_string(const nvlist_t *nvl, const char *name, const char **val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_STRING, NULL, val)); } int nvlist_lookup_nvlist(nvlist_t *nvl, const char *name, nvlist_t **val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_NVLIST, NULL, val)); } int nvlist_lookup_boolean_array(nvlist_t *nvl, const char *name, boolean_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_BOOLEAN_ARRAY, n, a)); } int nvlist_lookup_byte_array(nvlist_t *nvl, const char *name, uchar_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a)); } int nvlist_lookup_int8_array(nvlist_t *nvl, const char *name, int8_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a)); } int nvlist_lookup_uint8_array(nvlist_t *nvl, const char *name, uint8_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a)); } int nvlist_lookup_int16_array(nvlist_t *nvl, const char *name, int16_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a)); } int nvlist_lookup_uint16_array(nvlist_t *nvl, const char *name, uint16_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a)); } int nvlist_lookup_int32_array(nvlist_t *nvl, const char *name, int32_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a)); } int nvlist_lookup_uint32_array(nvlist_t *nvl, const char *name, uint32_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a)); } int nvlist_lookup_int64_array(nvlist_t *nvl, const char *name, int64_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a)); } int nvlist_lookup_uint64_array(nvlist_t *nvl, const char *name, uint64_t **a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a)); } int nvlist_lookup_string_array(nvlist_t *nvl, const char *name, char ***a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a)); } int nvlist_lookup_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t ***a, uint_t *n) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a)); } int nvlist_lookup_hrtime(nvlist_t *nvl, const char *name, hrtime_t *val) { return (nvlist_lookup_common(nvl, name, DATA_TYPE_HRTIME, NULL, val)); } int nvlist_lookup_pairs(nvlist_t *nvl, int flag, ...) { va_list ap; char *name; int noentok = (flag & NV_FLAG_NOENTOK ? 1 : 0); int ret = 0; va_start(ap, flag); while (ret == 0 && (name = va_arg(ap, char *)) != NULL) { data_type_t type; void *val; uint_t *nelem; switch (type = va_arg(ap, data_type_t)) { case DATA_TYPE_BOOLEAN: ret = nvlist_lookup_common(nvl, name, type, NULL, NULL); break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: case DATA_TYPE_INT16: case DATA_TYPE_UINT16: case DATA_TYPE_INT32: case DATA_TYPE_UINT32: case DATA_TYPE_INT64: case DATA_TYPE_UINT64: case DATA_TYPE_HRTIME: case DATA_TYPE_STRING: case DATA_TYPE_NVLIST: #if !defined(_KERNEL) case DATA_TYPE_DOUBLE: #endif val = va_arg(ap, void *); ret = nvlist_lookup_common(nvl, name, type, NULL, val); break; case DATA_TYPE_BYTE_ARRAY: case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: case DATA_TYPE_INT16_ARRAY: case DATA_TYPE_UINT16_ARRAY: case DATA_TYPE_INT32_ARRAY: case DATA_TYPE_UINT32_ARRAY: case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: case DATA_TYPE_STRING_ARRAY: case DATA_TYPE_NVLIST_ARRAY: val = va_arg(ap, void *); nelem = va_arg(ap, uint_t *); ret = nvlist_lookup_common(nvl, name, type, nelem, val); break; default: ret = EINVAL; } if (ret == ENOENT && noentok) ret = 0; } va_end(ap); return (ret); } /* * Find the 'name'ed nvpair in the nvlist 'nvl'. If 'name' found, the function * returns zero and a pointer to the matching nvpair is returned in '*ret' * (given 'ret' is non-NULL). If 'sep' is specified then 'name' will penitrate * multiple levels of embedded nvlists, with 'sep' as the separator. As an * example, if sep is '.', name might look like: "a" or "a.b" or "a.c[3]" or * "a.d[3].e[1]". This matches the C syntax for array embed (for convenience, * code also supports "a.d[3]e[1]" syntax). * * If 'ip' is non-NULL and the last name component is an array, return the * value of the "...[index]" array index in *ip. For an array reference that * is not indexed, *ip will be returned as -1. If there is a syntax error in * 'name', and 'ep' is non-NULL then *ep will be set to point to the location * inside the 'name' string where the syntax error was detected. */ static int nvlist_lookup_nvpair_ei_sep(nvlist_t *nvl, const char *name, const char sep, nvpair_t **ret, int *ip, const char **ep) { nvpair_t *nvp; const char *np; char *sepp = NULL; char *idxp, *idxep; nvlist_t **nva; long idx = 0; int n; if (ip) *ip = -1; /* not indexed */ if (ep) *ep = NULL; if ((nvl == NULL) || (name == NULL)) return (EINVAL); sepp = NULL; idx = 0; /* step through components of name */ for (np = name; np && *np; np = sepp) { /* ensure unique names */ if (!(nvl->nvl_nvflag & NV_UNIQUE_NAME)) return (ENOTSUP); /* skip white space */ skip_whitespace(np); if (*np == 0) break; /* set 'sepp' to end of current component 'np' */ if (sep) sepp = strchr(np, sep); else sepp = NULL; /* find start of next "[ index ]..." */ idxp = strchr(np, '['); /* if sepp comes first, set idxp to NULL */ if (sepp && idxp && (sepp < idxp)) idxp = NULL; /* * At this point 'idxp' is set if there is an index * expected for the current component. */ if (idxp) { /* set 'n' to length of current 'np' name component */ n = idxp++ - np; /* keep sepp up to date for *ep use as we advance */ skip_whitespace(idxp); sepp = idxp; /* determine the index value */ #if defined(_KERNEL) if (ddi_strtol(idxp, &idxep, 0, &idx)) goto fail; #else idx = strtol(idxp, &idxep, 0); #endif if (idxep == idxp) goto fail; /* keep sepp up to date for *ep use as we advance */ sepp = idxep; /* skip white space index value and check for ']' */ skip_whitespace(sepp); if (*sepp++ != ']') goto fail; /* for embedded arrays, support C syntax: "a[1].b" */ skip_whitespace(sepp); if (sep && (*sepp == sep)) sepp++; } else if (sepp) { n = sepp++ - np; } else { n = strlen(np); } /* trim trailing whitespace by reducing length of 'np' */ if (n == 0) goto fail; for (n--; (np[n] == ' ') || (np[n] == '\t'); n--) ; n++; /* skip whitespace, and set sepp to NULL if complete */ if (sepp) { skip_whitespace(sepp); if (*sepp == 0) sepp = NULL; } /* * At this point: * o 'n' is the length of current 'np' component. * o 'idxp' is set if there was an index, and value 'idx'. * o 'sepp' is set to the beginning of the next component, * and set to NULL if we have no more components. * * Search for nvpair with matching component name. */ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) { /* continue if no match on name */ if (strncmp(np, nvpair_name(nvp), n) || (strlen(nvpair_name(nvp)) != n)) continue; /* if indexed, verify type is array oriented */ if (idxp && !nvpair_type_is_array(nvp)) goto fail; /* * Full match found, return nvp and idx if this * was the last component. */ if (sepp == NULL) { if (ret) *ret = nvp; if (ip && idxp) *ip = (int)idx; /* return index */ return (0); /* found */ } /* * More components: current match must be * of DATA_TYPE_NVLIST or DATA_TYPE_NVLIST_ARRAY * to support going deeper. */ if (nvpair_type(nvp) == DATA_TYPE_NVLIST) { nvl = EMBEDDED_NVL(nvp); break; } else if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY) { if (nvpair_value_nvlist_array(nvp, &nva, (uint_t *)&n) != 0) goto fail; if (nva == NULL) goto fail; if ((n < 0) || (idx >= n)) goto fail; nvl = nva[idx]; break; } /* type does not support more levels */ goto fail; } if (nvp == NULL) goto fail; /* 'name' not found */ /* search for match of next component in embedded 'nvl' list */ } fail: if (ep && sepp) *ep = sepp; return (EINVAL); } /* * Return pointer to nvpair with specified 'name'. */ int nvlist_lookup_nvpair(nvlist_t *nvl, const char *name, nvpair_t **ret) { return (nvlist_lookup_nvpair_ei_sep(nvl, name, 0, ret, NULL, NULL)); } /* * Determine if named nvpair exists in nvlist (use embedded separator of '.' * and return array index). See nvlist_lookup_nvpair_ei_sep for more detailed * description. */ int nvlist_lookup_nvpair_embedded_index(nvlist_t *nvl, const char *name, nvpair_t **ret, int *ip, const char **ep) { return (nvlist_lookup_nvpair_ei_sep(nvl, name, '.', ret, ip, ep)); } boolean_t nvlist_exists(const nvlist_t *nvl, const char *name) { nvpriv_t *priv; nvpair_t *nvp; i_nvp_t *curr; if (name == NULL || nvl == NULL || (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) return (B_FALSE); for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { nvp = &curr->nvi_nvp; if (strcmp(name, NVP_NAME(nvp)) == 0) return (B_TRUE); } return (B_FALSE); } int nvpair_value_boolean_value(const nvpair_t *nvp, boolean_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_BOOLEAN_VALUE, NULL, val)); } int nvpair_value_byte(const nvpair_t *nvp, uchar_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_BYTE, NULL, val)); } int nvpair_value_int8(const nvpair_t *nvp, int8_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_INT8, NULL, val)); } int nvpair_value_uint8(const nvpair_t *nvp, uint8_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_UINT8, NULL, val)); } int nvpair_value_int16(const nvpair_t *nvp, int16_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_INT16, NULL, val)); } int nvpair_value_uint16(const nvpair_t *nvp, uint16_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_UINT16, NULL, val)); } int nvpair_value_int32(const nvpair_t *nvp, int32_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_INT32, NULL, val)); } int nvpair_value_uint32(const nvpair_t *nvp, uint32_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_UINT32, NULL, val)); } int nvpair_value_int64(const nvpair_t *nvp, int64_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_INT64, NULL, val)); } int nvpair_value_uint64(const nvpair_t *nvp, uint64_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_UINT64, NULL, val)); } #if !defined(_KERNEL) int nvpair_value_double(const nvpair_t *nvp, double *val) { return (nvpair_value_common(nvp, DATA_TYPE_DOUBLE, NULL, val)); } #endif int nvpair_value_string(const nvpair_t *nvp, const char **val) { return (nvpair_value_common(nvp, DATA_TYPE_STRING, NULL, val)); } int nvpair_value_nvlist(nvpair_t *nvp, nvlist_t **val) { return (nvpair_value_common(nvp, DATA_TYPE_NVLIST, NULL, val)); } int nvpair_value_boolean_array(nvpair_t *nvp, boolean_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_BOOLEAN_ARRAY, nelem, val)); } int nvpair_value_byte_array(nvpair_t *nvp, uchar_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_BYTE_ARRAY, nelem, val)); } int nvpair_value_int8_array(nvpair_t *nvp, int8_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_INT8_ARRAY, nelem, val)); } int nvpair_value_uint8_array(nvpair_t *nvp, uint8_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_UINT8_ARRAY, nelem, val)); } int nvpair_value_int16_array(nvpair_t *nvp, int16_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_INT16_ARRAY, nelem, val)); } int nvpair_value_uint16_array(nvpair_t *nvp, uint16_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_UINT16_ARRAY, nelem, val)); } int nvpair_value_int32_array(nvpair_t *nvp, int32_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_INT32_ARRAY, nelem, val)); } int nvpair_value_uint32_array(nvpair_t *nvp, uint32_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_UINT32_ARRAY, nelem, val)); } int nvpair_value_int64_array(nvpair_t *nvp, int64_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_INT64_ARRAY, nelem, val)); } int nvpair_value_uint64_array(nvpair_t *nvp, uint64_t **val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_UINT64_ARRAY, nelem, val)); } int nvpair_value_string_array(nvpair_t *nvp, const char ***val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_STRING_ARRAY, nelem, val)); } int nvpair_value_nvlist_array(nvpair_t *nvp, nvlist_t ***val, uint_t *nelem) { return (nvpair_value_common(nvp, DATA_TYPE_NVLIST_ARRAY, nelem, val)); } int nvpair_value_hrtime(nvpair_t *nvp, hrtime_t *val) { return (nvpair_value_common(nvp, DATA_TYPE_HRTIME, NULL, val)); } /* * Add specified pair to the list. */ int nvlist_add_nvpair(nvlist_t *nvl, nvpair_t *nvp) { if (nvl == NULL || nvp == NULL) return (EINVAL); return (nvlist_add_common(nvl, NVP_NAME(nvp), NVP_TYPE(nvp), NVP_NELEM(nvp), NVP_VALUE(nvp))); } /* * Merge the supplied nvlists and put the result in dst. * The merged list will contain all names specified in both lists, * the values are taken from nvl in the case of duplicates. * Return 0 on success. */ int nvlist_merge(nvlist_t *dst, nvlist_t *nvl, int flag) { (void) flag; if (nvl == NULL || dst == NULL) return (EINVAL); if (dst != nvl) return (nvlist_copy_pairs(nvl, dst)); return (0); } /* * Encoding related routines */ #define NVS_OP_ENCODE 0 #define NVS_OP_DECODE 1 #define NVS_OP_GETSIZE 2 typedef struct nvs_ops nvs_ops_t; typedef struct { int nvs_op; const nvs_ops_t *nvs_ops; void *nvs_private; nvpriv_t *nvs_priv; int nvs_recursion; } nvstream_t; /* * nvs operations are: * - nvs_nvlist * encoding / decoding of an nvlist header (nvlist_t) * calculates the size used for header and end detection * * - nvs_nvpair * responsible for the first part of encoding / decoding of an nvpair * calculates the decoded size of an nvpair * * - nvs_nvp_op * second part of encoding / decoding of an nvpair * * - nvs_nvp_size * calculates the encoding size of an nvpair * * - nvs_nvl_fini * encodes the end detection mark (zeros). */ struct nvs_ops { int (*nvs_nvlist)(nvstream_t *, nvlist_t *, size_t *); int (*nvs_nvpair)(nvstream_t *, nvpair_t *, size_t *); int (*nvs_nvp_op)(nvstream_t *, nvpair_t *); int (*nvs_nvp_size)(nvstream_t *, nvpair_t *, size_t *); int (*nvs_nvl_fini)(nvstream_t *); }; typedef struct { char nvh_encoding; /* nvs encoding method */ char nvh_endian; /* nvs endian */ char nvh_reserved1; /* reserved for future use */ char nvh_reserved2; /* reserved for future use */ } nvs_header_t; static int nvs_encode_pairs(nvstream_t *nvs, nvlist_t *nvl) { nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; i_nvp_t *curr; /* * Walk nvpair in list and encode each nvpair */ for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) if (nvs->nvs_ops->nvs_nvpair(nvs, &curr->nvi_nvp, NULL) != 0) return (EFAULT); return (nvs->nvs_ops->nvs_nvl_fini(nvs)); } static int nvs_decode_pairs(nvstream_t *nvs, nvlist_t *nvl) { nvpair_t *nvp; size_t nvsize; int err; /* * Get decoded size of next pair in stream, alloc * memory for nvpair_t, then decode the nvpair */ while ((err = nvs->nvs_ops->nvs_nvpair(nvs, NULL, &nvsize)) == 0) { if (nvsize == 0) /* end of list */ break; /* make sure len makes sense */ if (nvsize < NVP_SIZE_CALC(1, 0)) return (EFAULT); if ((nvp = nvp_buf_alloc(nvl, nvsize)) == NULL) return (ENOMEM); if ((err = nvs->nvs_ops->nvs_nvp_op(nvs, nvp)) != 0) { nvp_buf_free(nvl, nvp); return (err); } if (i_validate_nvpair(nvp) != 0) { nvpair_free(nvp); nvp_buf_free(nvl, nvp); return (EFAULT); } err = nvt_add_nvpair(nvl, nvp); if (err != 0) { nvpair_free(nvp); nvp_buf_free(nvl, nvp); return (err); } nvp_buf_link(nvl, nvp); } return (err); } static int nvs_getsize_pairs(nvstream_t *nvs, nvlist_t *nvl, size_t *buflen) { nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; i_nvp_t *curr; uint64_t nvsize = *buflen; size_t size; /* * Get encoded size of nvpairs in nvlist */ for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { if (nvs->nvs_ops->nvs_nvp_size(nvs, &curr->nvi_nvp, &size) != 0) return (EINVAL); if ((nvsize += size) > INT32_MAX) return (EINVAL); } *buflen = nvsize; return (0); } static int nvs_operation(nvstream_t *nvs, nvlist_t *nvl, size_t *buflen) { int err; if (nvl->nvl_priv == 0) return (EFAULT); /* * Perform the operation, starting with header, then each nvpair */ if ((err = nvs->nvs_ops->nvs_nvlist(nvs, nvl, buflen)) != 0) return (err); switch (nvs->nvs_op) { case NVS_OP_ENCODE: err = nvs_encode_pairs(nvs, nvl); break; case NVS_OP_DECODE: err = nvs_decode_pairs(nvs, nvl); break; case NVS_OP_GETSIZE: err = nvs_getsize_pairs(nvs, nvl, buflen); break; default: err = EINVAL; } return (err); } static int nvs_embedded(nvstream_t *nvs, nvlist_t *embedded) { switch (nvs->nvs_op) { case NVS_OP_ENCODE: { int err; if (nvs->nvs_recursion >= nvpair_max_recursion) return (EINVAL); nvs->nvs_recursion++; err = nvs_operation(nvs, embedded, NULL); nvs->nvs_recursion--; return (err); } case NVS_OP_DECODE: { nvpriv_t *priv; int err; if (embedded->nvl_version != NV_VERSION) return (ENOTSUP); if ((priv = nv_priv_alloc_embedded(nvs->nvs_priv)) == NULL) return (ENOMEM); nvlist_init(embedded, embedded->nvl_nvflag, priv); if (nvs->nvs_recursion >= nvpair_max_recursion) { nvlist_free(embedded); return (EINVAL); } nvs->nvs_recursion++; if ((err = nvs_operation(nvs, embedded, NULL)) != 0) nvlist_free(embedded); nvs->nvs_recursion--; return (err); } default: break; } return (EINVAL); } static int nvs_embedded_nvl_array(nvstream_t *nvs, nvpair_t *nvp, size_t *size) { size_t nelem = NVP_NELEM(nvp); nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp); int i; switch (nvs->nvs_op) { case NVS_OP_ENCODE: for (i = 0; i < nelem; i++) if (nvs_embedded(nvs, nvlp[i]) != 0) return (EFAULT); break; case NVS_OP_DECODE: { size_t len = nelem * sizeof (uint64_t); nvlist_t *embedded = (nvlist_t *)((uintptr_t)nvlp + len); memset(nvlp, 0, len); /* don't trust packed data */ for (i = 0; i < nelem; i++) { if (nvs_embedded(nvs, embedded) != 0) { nvpair_free(nvp); return (EFAULT); } nvlp[i] = embedded++; } break; } case NVS_OP_GETSIZE: { uint64_t nvsize = 0; for (i = 0; i < nelem; i++) { size_t nvp_sz = 0; if (nvs_operation(nvs, nvlp[i], &nvp_sz) != 0) return (EINVAL); if ((nvsize += nvp_sz) > INT32_MAX) return (EINVAL); } *size = nvsize; break; } default: return (EINVAL); } return (0); } static int nvs_native(nvstream_t *, nvlist_t *, char *, size_t *); static int nvs_xdr(nvstream_t *, nvlist_t *, char *, size_t *); /* * Common routine for nvlist operations: * encode, decode, getsize (encoded size). */ static int nvlist_common(nvlist_t *nvl, char *buf, size_t *buflen, int encoding, int nvs_op) { int err = 0; nvstream_t nvs; int nvl_endian; #if defined(_ZFS_LITTLE_ENDIAN) int host_endian = 1; #elif defined(_ZFS_BIG_ENDIAN) int host_endian = 0; #else #error "No endian defined!" #endif /* _ZFS_LITTLE_ENDIAN */ nvs_header_t *nvh; if (buflen == NULL || nvl == NULL || (nvs.nvs_priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) return (EINVAL); nvs.nvs_op = nvs_op; nvs.nvs_recursion = 0; /* * For NVS_OP_ENCODE and NVS_OP_DECODE make sure an nvlist and * a buffer is allocated. The first 4 bytes in the buffer are * used for encoding method and host endian. */ switch (nvs_op) { case NVS_OP_ENCODE: if (buf == NULL || *buflen < sizeof (nvs_header_t)) return (EINVAL); nvh = (void *)buf; nvh->nvh_encoding = encoding; nvh->nvh_endian = nvl_endian = host_endian; nvh->nvh_reserved1 = 0; nvh->nvh_reserved2 = 0; break; case NVS_OP_DECODE: if (buf == NULL || *buflen < sizeof (nvs_header_t)) return (EINVAL); /* get method of encoding from first byte */ nvh = (void *)buf; encoding = nvh->nvh_encoding; nvl_endian = nvh->nvh_endian; break; case NVS_OP_GETSIZE: nvl_endian = host_endian; /* * add the size for encoding */ *buflen = sizeof (nvs_header_t); break; default: return (ENOTSUP); } /* * Create an nvstream with proper encoding method */ switch (encoding) { case NV_ENCODE_NATIVE: /* * check endianness, in case we are unpacking * from a file */ if (nvl_endian != host_endian) return (ENOTSUP); err = nvs_native(&nvs, nvl, buf, buflen); break; case NV_ENCODE_XDR: err = nvs_xdr(&nvs, nvl, buf, buflen); break; default: err = ENOTSUP; break; } return (err); } int nvlist_size(nvlist_t *nvl, size_t *size, int encoding) { return (nvlist_common(nvl, NULL, size, encoding, NVS_OP_GETSIZE)); } /* * Pack nvlist into contiguous memory */ int nvlist_pack(nvlist_t *nvl, char **bufp, size_t *buflen, int encoding, int kmflag) { return (nvlist_xpack(nvl, bufp, buflen, encoding, nvlist_nv_alloc(kmflag))); } int nvlist_xpack(nvlist_t *nvl, char **bufp, size_t *buflen, int encoding, nv_alloc_t *nva) { nvpriv_t nvpriv; size_t alloc_size; char *buf; int err; if (nva == NULL || nvl == NULL || bufp == NULL || buflen == NULL) return (EINVAL); if (*bufp != NULL) return (nvlist_common(nvl, *bufp, buflen, encoding, NVS_OP_ENCODE)); /* * Here is a difficult situation: * 1. The nvlist has fixed allocator properties. * All other nvlist routines (like nvlist_add_*, ...) use * these properties. * 2. When using nvlist_pack() the user can specify their own * allocator properties (e.g. by using KM_NOSLEEP). * * We use the user specified properties (2). A clearer solution * will be to remove the kmflag from nvlist_pack(), but we will * not change the interface. */ nv_priv_init(&nvpriv, nva, 0); if ((err = nvlist_size(nvl, &alloc_size, encoding))) return (err); if ((buf = nv_mem_zalloc(&nvpriv, alloc_size)) == NULL) return (ENOMEM); if ((err = nvlist_common(nvl, buf, &alloc_size, encoding, NVS_OP_ENCODE)) != 0) { nv_mem_free(&nvpriv, buf, alloc_size); } else { *buflen = alloc_size; *bufp = buf; } return (err); } /* * Unpack buf into an nvlist_t */ int nvlist_unpack(char *buf, size_t buflen, nvlist_t **nvlp, int kmflag) { return (nvlist_xunpack(buf, buflen, nvlp, nvlist_nv_alloc(kmflag))); } int nvlist_xunpack(char *buf, size_t buflen, nvlist_t **nvlp, nv_alloc_t *nva) { nvlist_t *nvl; int err; if (nvlp == NULL) return (EINVAL); if ((err = nvlist_xalloc(&nvl, 0, nva)) != 0) return (err); if ((err = nvlist_common(nvl, buf, &buflen, NV_ENCODE_NATIVE, NVS_OP_DECODE)) != 0) nvlist_free(nvl); else *nvlp = nvl; return (err); } /* * Native encoding functions */ typedef struct { /* * This structure is used when decoding a packed nvpair in * the native format. n_base points to a buffer containing the * packed nvpair. n_end is a pointer to the end of the buffer. * (n_end actually points to the first byte past the end of the * buffer.) n_curr is a pointer that lies between n_base and n_end. * It points to the current data that we are decoding. * The amount of data left in the buffer is equal to n_end - n_curr. * n_flag is used to recognize a packed embedded list. */ caddr_t n_base; caddr_t n_end; caddr_t n_curr; uint_t n_flag; } nvs_native_t; static int nvs_native_create(nvstream_t *nvs, nvs_native_t *native, char *buf, size_t buflen) { switch (nvs->nvs_op) { case NVS_OP_ENCODE: case NVS_OP_DECODE: nvs->nvs_private = native; native->n_curr = native->n_base = buf; native->n_end = buf + buflen; native->n_flag = 0; return (0); case NVS_OP_GETSIZE: nvs->nvs_private = native; native->n_curr = native->n_base = native->n_end = NULL; native->n_flag = 0; return (0); default: return (EINVAL); } } static void nvs_native_destroy(nvstream_t *nvs) { nvs->nvs_private = NULL; } static int native_cp(nvstream_t *nvs, void *buf, size_t size) { nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; if (native->n_curr + size > native->n_end) return (EFAULT); /* * The memcpy() below eliminates alignment requirement * on the buffer (stream) and is preferred over direct access. */ switch (nvs->nvs_op) { case NVS_OP_ENCODE: memcpy(native->n_curr, buf, size); break; case NVS_OP_DECODE: memcpy(buf, native->n_curr, size); break; default: return (EINVAL); } native->n_curr += size; return (0); } /* * operate on nvlist_t header */ static int nvs_native_nvlist(nvstream_t *nvs, nvlist_t *nvl, size_t *size) { nvs_native_t *native = nvs->nvs_private; switch (nvs->nvs_op) { case NVS_OP_ENCODE: case NVS_OP_DECODE: if (native->n_flag) return (0); /* packed embedded list */ native->n_flag = 1; /* copy version and nvflag of the nvlist_t */ if (native_cp(nvs, &nvl->nvl_version, sizeof (int32_t)) != 0 || native_cp(nvs, &nvl->nvl_nvflag, sizeof (int32_t)) != 0) return (EFAULT); return (0); case NVS_OP_GETSIZE: /* * if calculate for packed embedded list * 4 for end of the embedded list * else * 2 * sizeof (int32_t) for nvl_version and nvl_nvflag * and 4 for end of the entire list */ if (native->n_flag) { *size += 4; } else { native->n_flag = 1; *size += 2 * sizeof (int32_t) + 4; } return (0); default: return (EINVAL); } } static int nvs_native_nvl_fini(nvstream_t *nvs) { if (nvs->nvs_op == NVS_OP_ENCODE) { nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; /* * Add 4 zero bytes at end of nvlist. They are used * for end detection by the decode routine. */ if (native->n_curr + sizeof (int) > native->n_end) return (EFAULT); memset(native->n_curr, 0, sizeof (int)); native->n_curr += sizeof (int); } return (0); } static int nvpair_native_embedded(nvstream_t *nvs, nvpair_t *nvp) { if (nvs->nvs_op == NVS_OP_ENCODE) { nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; nvlist_t *packed = (void *) (native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp)); /* * Null out the pointer that is meaningless in the packed * structure. The address may not be aligned, so we have * to use memset. */ memset((char *)packed + offsetof(nvlist_t, nvl_priv), 0, sizeof (uint64_t)); } return (nvs_embedded(nvs, EMBEDDED_NVL(nvp))); } static int nvpair_native_embedded_array(nvstream_t *nvs, nvpair_t *nvp) { if (nvs->nvs_op == NVS_OP_ENCODE) { nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; char *value = native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp); size_t len = NVP_NELEM(nvp) * sizeof (uint64_t); nvlist_t *packed = (nvlist_t *)((uintptr_t)value + len); int i; /* * Null out pointers that are meaningless in the packed * structure. The addresses may not be aligned, so we have * to use memset. */ memset(value, 0, len); for (i = 0; i < NVP_NELEM(nvp); i++, packed++) /* * Null out the pointer that is meaningless in the * packed structure. The address may not be aligned, * so we have to use memset. */ memset((char *)packed + offsetof(nvlist_t, nvl_priv), 0, sizeof (uint64_t)); } return (nvs_embedded_nvl_array(nvs, nvp, NULL)); } static void nvpair_native_string_array(nvstream_t *nvs, nvpair_t *nvp) { switch (nvs->nvs_op) { case NVS_OP_ENCODE: { nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; uint64_t *strp = (void *) (native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp)); /* * Null out pointers that are meaningless in the packed * structure. The addresses may not be aligned, so we have * to use memset. */ memset(strp, 0, NVP_NELEM(nvp) * sizeof (uint64_t)); break; } case NVS_OP_DECODE: { char **strp = (void *)NVP_VALUE(nvp); char *buf = ((char *)strp + NVP_NELEM(nvp) * sizeof (uint64_t)); int i; for (i = 0; i < NVP_NELEM(nvp); i++) { strp[i] = buf; buf += strlen(buf) + 1; } break; } } } static int nvs_native_nvp_op(nvstream_t *nvs, nvpair_t *nvp) { data_type_t type; int value_sz; int ret = 0; /* * We do the initial memcpy of the data before we look at * the nvpair type, because when we're decoding, we won't * have the correct values for the pair until we do the memcpy. */ switch (nvs->nvs_op) { case NVS_OP_ENCODE: case NVS_OP_DECODE: if (native_cp(nvs, nvp, nvp->nvp_size) != 0) return (EFAULT); break; default: return (EINVAL); } /* verify nvp_name_sz, check the name string length */ if (i_validate_nvpair_name(nvp) != 0) return (EFAULT); type = NVP_TYPE(nvp); /* * Verify type and nelem and get the value size. * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY * is the size of the string(s) excluded. */ if ((value_sz = i_get_value_size(type, NULL, NVP_NELEM(nvp))) < 0) return (EFAULT); if (NVP_SIZE_CALC(nvp->nvp_name_sz, value_sz) > nvp->nvp_size) return (EFAULT); switch (type) { case DATA_TYPE_NVLIST: ret = nvpair_native_embedded(nvs, nvp); break; case DATA_TYPE_NVLIST_ARRAY: ret = nvpair_native_embedded_array(nvs, nvp); break; case DATA_TYPE_STRING_ARRAY: nvpair_native_string_array(nvs, nvp); break; default: break; } return (ret); } static int nvs_native_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size) { uint64_t nvp_sz = nvp->nvp_size; switch (NVP_TYPE(nvp)) { case DATA_TYPE_NVLIST: { size_t nvsize = 0; if (nvs_operation(nvs, EMBEDDED_NVL(nvp), &nvsize) != 0) return (EINVAL); nvp_sz += nvsize; break; } case DATA_TYPE_NVLIST_ARRAY: { size_t nvsize; if (nvs_embedded_nvl_array(nvs, nvp, &nvsize) != 0) return (EINVAL); nvp_sz += nvsize; break; } default: break; } if (nvp_sz > INT32_MAX) return (EINVAL); *size = nvp_sz; return (0); } static int nvs_native_nvpair(nvstream_t *nvs, nvpair_t *nvp, size_t *size) { switch (nvs->nvs_op) { case NVS_OP_ENCODE: return (nvs_native_nvp_op(nvs, nvp)); case NVS_OP_DECODE: { nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; int32_t decode_len; /* try to read the size value from the stream */ if (native->n_curr + sizeof (int32_t) > native->n_end) return (EFAULT); memcpy(&decode_len, native->n_curr, sizeof (int32_t)); /* sanity check the size value */ if (decode_len < 0 || decode_len > native->n_end - native->n_curr) return (EFAULT); *size = decode_len; /* * If at the end of the stream then move the cursor * forward, otherwise nvpair_native_op() will read * the entire nvpair at the same cursor position. */ if (*size == 0) native->n_curr += sizeof (int32_t); break; } default: return (EINVAL); } return (0); } static const nvs_ops_t nvs_native_ops = { .nvs_nvlist = nvs_native_nvlist, .nvs_nvpair = nvs_native_nvpair, .nvs_nvp_op = nvs_native_nvp_op, .nvs_nvp_size = nvs_native_nvp_size, .nvs_nvl_fini = nvs_native_nvl_fini }; static int nvs_native(nvstream_t *nvs, nvlist_t *nvl, char *buf, size_t *buflen) { nvs_native_t native; int err; nvs->nvs_ops = &nvs_native_ops; if ((err = nvs_native_create(nvs, &native, buf + sizeof (nvs_header_t), *buflen - sizeof (nvs_header_t))) != 0) return (err); err = nvs_operation(nvs, nvl, buflen); nvs_native_destroy(nvs); return (err); } /* * XDR encoding functions * * An xdr packed nvlist is encoded as: * * - encoding method and host endian (4 bytes) * - nvl_version (4 bytes) * - nvl_nvflag (4 bytes) * * - encoded nvpairs, the format of one xdr encoded nvpair is: * - encoded size of the nvpair (4 bytes) * - decoded size of the nvpair (4 bytes) * - name string, (4 + sizeof(NV_ALIGN4(string)) * a string is coded as size (4 bytes) and data * - data type (4 bytes) * - number of elements in the nvpair (4 bytes) * - data * * - 2 zero's for end of the entire list (8 bytes) */ static int nvs_xdr_create(nvstream_t *nvs, XDR *xdr, char *buf, size_t buflen) { /* xdr data must be 4 byte aligned */ if ((ulong_t)buf % 4 != 0) return (EFAULT); switch (nvs->nvs_op) { case NVS_OP_ENCODE: xdrmem_create(xdr, buf, (uint_t)buflen, XDR_ENCODE); nvs->nvs_private = xdr; return (0); case NVS_OP_DECODE: xdrmem_create(xdr, buf, (uint_t)buflen, XDR_DECODE); nvs->nvs_private = xdr; return (0); case NVS_OP_GETSIZE: nvs->nvs_private = NULL; return (0); default: return (EINVAL); } } static void nvs_xdr_destroy(nvstream_t *nvs) { switch (nvs->nvs_op) { case NVS_OP_ENCODE: case NVS_OP_DECODE: nvs->nvs_private = NULL; break; default: break; } } static int nvs_xdr_nvlist(nvstream_t *nvs, nvlist_t *nvl, size_t *size) { switch (nvs->nvs_op) { case NVS_OP_ENCODE: case NVS_OP_DECODE: { XDR *xdr = nvs->nvs_private; if (!xdr_int(xdr, &nvl->nvl_version) || !xdr_u_int(xdr, &nvl->nvl_nvflag)) return (EFAULT); break; } case NVS_OP_GETSIZE: { /* * 2 * 4 for nvl_version + nvl_nvflag * and 8 for end of the entire list */ *size += 2 * 4 + 8; break; } default: return (EINVAL); } return (0); } static int nvs_xdr_nvl_fini(nvstream_t *nvs) { if (nvs->nvs_op == NVS_OP_ENCODE) { XDR *xdr = nvs->nvs_private; int zero = 0; if (!xdr_int(xdr, &zero) || !xdr_int(xdr, &zero)) return (EFAULT); } return (0); } /* * xdrproc_t-compatible callbacks for xdr_array() */ #if defined(_KERNEL) && defined(__linux__) /* Linux kernel */ #define NVS_BUILD_XDRPROC_T(type) \ static bool_t \ nvs_xdr_nvp_##type(XDR *xdrs, void *ptr) \ { \ return (xdr_##type(xdrs, ptr)); \ } #elif !defined(_KERNEL) && defined(XDR_CONTROL) /* tirpc */ #define NVS_BUILD_XDRPROC_T(type) \ static bool_t \ nvs_xdr_nvp_##type(XDR *xdrs, ...) \ { \ va_list args; \ void *ptr; \ \ va_start(args, xdrs); \ ptr = va_arg(args, void *); \ va_end(args); \ \ return (xdr_##type(xdrs, ptr)); \ } #else /* FreeBSD, sunrpc */ #define NVS_BUILD_XDRPROC_T(type) \ static bool_t \ nvs_xdr_nvp_##type(XDR *xdrs, void *ptr, ...) \ { \ return (xdr_##type(xdrs, ptr)); \ } #endif NVS_BUILD_XDRPROC_T(char); NVS_BUILD_XDRPROC_T(short); NVS_BUILD_XDRPROC_T(u_short); NVS_BUILD_XDRPROC_T(int); NVS_BUILD_XDRPROC_T(u_int); NVS_BUILD_XDRPROC_T(longlong_t); NVS_BUILD_XDRPROC_T(u_longlong_t); /* * The format of xdr encoded nvpair is: * encode_size, decode_size, name string, data type, nelem, data */ static int nvs_xdr_nvp_op(nvstream_t *nvs, nvpair_t *nvp) { ASSERT(nvs != NULL && nvp != NULL); data_type_t type; char *buf; char *buf_end = (char *)nvp + nvp->nvp_size; int value_sz; uint_t nelem, buflen; bool_t ret = FALSE; XDR *xdr = nvs->nvs_private; ASSERT(xdr != NULL); /* name string */ if ((buf = NVP_NAME(nvp)) >= buf_end) return (EFAULT); buflen = buf_end - buf; if (!xdr_string(xdr, &buf, buflen - 1)) return (EFAULT); nvp->nvp_name_sz = strlen(buf) + 1; /* type and nelem */ if (!xdr_int(xdr, (int *)&nvp->nvp_type) || !xdr_int(xdr, &nvp->nvp_value_elem)) return (EFAULT); type = NVP_TYPE(nvp); nelem = nvp->nvp_value_elem; /* * Verify type and nelem and get the value size. * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY * is the size of the string(s) excluded. */ if ((value_sz = i_get_value_size(type, NULL, nelem)) < 0) return (EFAULT); /* if there is no data to extract then return */ if (nelem == 0) return (0); /* value */ if ((buf = NVP_VALUE(nvp)) >= buf_end) return (EFAULT); buflen = buf_end - buf; if (buflen < value_sz) return (EFAULT); switch (type) { case DATA_TYPE_NVLIST: if (nvs_embedded(nvs, (void *)buf) == 0) return (0); break; case DATA_TYPE_NVLIST_ARRAY: if (nvs_embedded_nvl_array(nvs, nvp, NULL) == 0) return (0); break; case DATA_TYPE_BOOLEAN: ret = TRUE; break; case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: ret = xdr_char(xdr, buf); break; case DATA_TYPE_INT16: ret = xdr_short(xdr, (void *)buf); break; case DATA_TYPE_UINT16: ret = xdr_u_short(xdr, (void *)buf); break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_INT32: ret = xdr_int(xdr, (void *)buf); break; case DATA_TYPE_UINT32: ret = xdr_u_int(xdr, (void *)buf); break; case DATA_TYPE_INT64: ret = xdr_longlong_t(xdr, (void *)buf); break; case DATA_TYPE_UINT64: ret = xdr_u_longlong_t(xdr, (void *)buf); break; case DATA_TYPE_HRTIME: /* * NOTE: must expose the definition of hrtime_t here */ ret = xdr_longlong_t(xdr, (void *)buf); break; #if !defined(_KERNEL) case DATA_TYPE_DOUBLE: ret = xdr_double(xdr, (void *)buf); break; #endif case DATA_TYPE_STRING: ret = xdr_string(xdr, &buf, buflen - 1); break; case DATA_TYPE_BYTE_ARRAY: ret = xdr_opaque(xdr, buf, nelem); break; case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: ret = xdr_array(xdr, &buf, &nelem, buflen, sizeof (int8_t), nvs_xdr_nvp_char); break; case DATA_TYPE_INT16_ARRAY: ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int16_t), sizeof (int16_t), nvs_xdr_nvp_short); break; case DATA_TYPE_UINT16_ARRAY: ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint16_t), sizeof (uint16_t), nvs_xdr_nvp_u_short); break; case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_INT32_ARRAY: ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int32_t), sizeof (int32_t), nvs_xdr_nvp_int); break; case DATA_TYPE_UINT32_ARRAY: ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint32_t), sizeof (uint32_t), nvs_xdr_nvp_u_int); break; case DATA_TYPE_INT64_ARRAY: ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int64_t), sizeof (int64_t), nvs_xdr_nvp_longlong_t); break; case DATA_TYPE_UINT64_ARRAY: ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint64_t), sizeof (uint64_t), nvs_xdr_nvp_u_longlong_t); break; case DATA_TYPE_STRING_ARRAY: { size_t len = nelem * sizeof (uint64_t); char **strp = (void *)buf; int i; if (nvs->nvs_op == NVS_OP_DECODE) memset(buf, 0, len); /* don't trust packed data */ for (i = 0; i < nelem; i++) { if (buflen <= len) return (EFAULT); buf += len; buflen -= len; if (xdr_string(xdr, &buf, buflen - 1) != TRUE) return (EFAULT); if (nvs->nvs_op == NVS_OP_DECODE) strp[i] = buf; len = strlen(buf) + 1; } ret = TRUE; break; } default: break; } return (ret == TRUE ? 0 : EFAULT); } static int nvs_xdr_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size) { data_type_t type = NVP_TYPE(nvp); /* * encode_size + decode_size + name string size + data type + nelem * where name string size = 4 + NV_ALIGN4(strlen(NVP_NAME(nvp))) */ uint64_t nvp_sz = 4 + 4 + 4 + NV_ALIGN4(strlen(NVP_NAME(nvp))) + 4 + 4; switch (type) { case DATA_TYPE_BOOLEAN: break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: case DATA_TYPE_INT16: case DATA_TYPE_UINT16: case DATA_TYPE_INT32: case DATA_TYPE_UINT32: nvp_sz += 4; /* 4 is the minimum xdr unit */ break; case DATA_TYPE_INT64: case DATA_TYPE_UINT64: case DATA_TYPE_HRTIME: #if !defined(_KERNEL) case DATA_TYPE_DOUBLE: #endif nvp_sz += 8; break; case DATA_TYPE_STRING: nvp_sz += 4 + NV_ALIGN4(strlen((char *)NVP_VALUE(nvp))); break; case DATA_TYPE_BYTE_ARRAY: nvp_sz += NV_ALIGN4(NVP_NELEM(nvp)); break; case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: case DATA_TYPE_INT16_ARRAY: case DATA_TYPE_UINT16_ARRAY: case DATA_TYPE_INT32_ARRAY: case DATA_TYPE_UINT32_ARRAY: nvp_sz += 4 + 4 * (uint64_t)NVP_NELEM(nvp); break; case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: nvp_sz += 4 + 8 * (uint64_t)NVP_NELEM(nvp); break; case DATA_TYPE_STRING_ARRAY: { int i; char **strs = (void *)NVP_VALUE(nvp); for (i = 0; i < NVP_NELEM(nvp); i++) nvp_sz += 4 + NV_ALIGN4(strlen(strs[i])); break; } case DATA_TYPE_NVLIST: case DATA_TYPE_NVLIST_ARRAY: { size_t nvsize = 0; int old_nvs_op = nvs->nvs_op; int err; nvs->nvs_op = NVS_OP_GETSIZE; if (type == DATA_TYPE_NVLIST) err = nvs_operation(nvs, EMBEDDED_NVL(nvp), &nvsize); else err = nvs_embedded_nvl_array(nvs, nvp, &nvsize); nvs->nvs_op = old_nvs_op; if (err != 0) return (EINVAL); nvp_sz += nvsize; break; } default: return (EINVAL); } if (nvp_sz > INT32_MAX) return (EINVAL); *size = nvp_sz; return (0); } /* * The NVS_XDR_MAX_LEN macro takes a packed xdr buffer of size x and estimates * the largest nvpair that could be encoded in the buffer. * * See comments above nvpair_xdr_op() for the format of xdr encoding. * The size of a xdr packed nvpair without any data is 5 words. * * Using the size of the data directly as an estimate would be ok * in all cases except one. If the data type is of DATA_TYPE_STRING_ARRAY * then the actual nvpair has space for an array of pointers to index * the strings. These pointers are not encoded into the packed xdr buffer. * * If the data is of type DATA_TYPE_STRING_ARRAY and all the strings are * of length 0, then each string is encoded in xdr format as a single word. * Therefore when expanded to an nvpair there will be 2.25 word used for * each string. (a int64_t allocated for pointer usage, and a single char * for the null termination.) * * This is the calculation performed by the NVS_XDR_MAX_LEN macro. */ #define NVS_XDR_HDR_LEN ((size_t)(5 * 4)) #define NVS_XDR_DATA_LEN(y) (((size_t)(y) <= NVS_XDR_HDR_LEN) ? \ 0 : ((size_t)(y) - NVS_XDR_HDR_LEN)) #define NVS_XDR_MAX_LEN(x) (NVP_SIZE_CALC(1, 0) + \ (NVS_XDR_DATA_LEN(x) * 2) + \ NV_ALIGN4((NVS_XDR_DATA_LEN(x) / 4))) static int nvs_xdr_nvpair(nvstream_t *nvs, nvpair_t *nvp, size_t *size) { XDR *xdr = nvs->nvs_private; int32_t encode_len, decode_len; switch (nvs->nvs_op) { case NVS_OP_ENCODE: { size_t nvsize; if (nvs_xdr_nvp_size(nvs, nvp, &nvsize) != 0) return (EFAULT); decode_len = nvp->nvp_size; encode_len = nvsize; if (!xdr_int(xdr, &encode_len) || !xdr_int(xdr, &decode_len)) return (EFAULT); return (nvs_xdr_nvp_op(nvs, nvp)); } case NVS_OP_DECODE: { struct xdr_bytesrec bytesrec; /* get the encode and decode size */ if (!xdr_int(xdr, &encode_len) || !xdr_int(xdr, &decode_len)) return (EFAULT); *size = decode_len; /* are we at the end of the stream? */ if (*size == 0) return (0); /* sanity check the size parameter */ if (!xdr_control(xdr, XDR_GET_BYTES_AVAIL, &bytesrec)) return (EFAULT); if (*size > NVS_XDR_MAX_LEN(bytesrec.xc_num_avail)) return (EFAULT); break; } default: return (EINVAL); } return (0); } static const struct nvs_ops nvs_xdr_ops = { .nvs_nvlist = nvs_xdr_nvlist, .nvs_nvpair = nvs_xdr_nvpair, .nvs_nvp_op = nvs_xdr_nvp_op, .nvs_nvp_size = nvs_xdr_nvp_size, .nvs_nvl_fini = nvs_xdr_nvl_fini }; static int nvs_xdr(nvstream_t *nvs, nvlist_t *nvl, char *buf, size_t *buflen) { XDR xdr; int err; nvs->nvs_ops = &nvs_xdr_ops; if ((err = nvs_xdr_create(nvs, &xdr, buf + sizeof (nvs_header_t), *buflen - sizeof (nvs_header_t))) != 0) return (err); err = nvs_operation(nvs, nvl, buflen); nvs_xdr_destroy(nvs); return (err); } +#define NVP(buf, size, len, buf_end, elem, type, vtype, ptype, format) { \ + vtype value; \ + int rc; \ +\ + (void) nvpair_value_##type(elem, &value); \ + rc = snprintf(buf, size, "%*s%s: " format "\n", indent, "", \ + nvpair_name(elem), (ptype)value); \ + if (rc < 0) \ + return (rc); \ + size = MAX((int)size - rc, 0); \ + buf = size == 0 ? NULL : buf_end - size; \ + len += rc; \ +} + +#define NVPA(buf, size, len, buf_end, elem, type, vtype, ptype, format) \ +{ \ + uint_t i, count; \ + vtype *value; \ + int rc; \ +\ + (void) nvpair_value_##type(elem, &value, &count); \ + for (i = 0; i < count; i++) { \ + rc = snprintf(buf, size, "%*s%s[%d]: " format "\n", indent, \ + "", nvpair_name(elem), i, (ptype)value[i]); \ + if (rc < 0) \ + return (rc); \ + size = MAX((int)size - rc, 0); \ + buf = size == 0 ? NULL : buf_end - size; \ + len += rc; \ + } \ +} + +/* + * snprintf() version of dump_nvlist() + * + * Works just like snprintf(), but with an nvlist and indent count as args. + * + * Output is similar to nvlist_print() but handles arrays slightly differently. + * + * Return value matches C99 snprintf() return value conventions. + */ +int +nvlist_snprintf(char *buf, size_t size, nvlist_t *list, int indent) +{ + nvpair_t *elem = NULL; + boolean_t bool_value; + nvlist_t *nvlist_value; + nvlist_t **nvlist_array_value; + uint_t i, count; + int len = 0; + int rc; + char *buf_end = &buf[size]; + + if (list == NULL) + return (0); + + while ((elem = nvlist_next_nvpair(list, elem)) != NULL) { + switch (nvpair_type(elem)) { + case DATA_TYPE_BOOLEAN: + rc = snprintf(buf, size, "%*s%s\n", indent, "", + nvpair_name(elem)); + if (rc < 0) + return (rc); + size = MAX((int)size - rc, 0); + buf = size == 0 ? NULL : buf_end - size; + len += rc; + break; + + case DATA_TYPE_BOOLEAN_VALUE: + (void) nvpair_value_boolean_value(elem, &bool_value); + rc = snprintf(buf, size, "%*s%s: %s\n", indent, "", + nvpair_name(elem), bool_value ? "true" : "false"); + if (rc < 0) + return (rc); + size = MAX((int)size - rc, 0); + buf = size == 0 ? NULL : buf_end - size; + len += rc; + break; + + case DATA_TYPE_BYTE: + NVP(buf, size, len, buf_end, elem, byte, uchar_t, int, + "%u"); + break; + + case DATA_TYPE_INT8: + NVP(buf, size, len, buf_end, elem, int8, int8_t, int, + "%d"); + break; + + case DATA_TYPE_UINT8: + NVP(buf, size, len, buf_end, elem, uint8, uint8_t, int, + "%u"); + break; + + case DATA_TYPE_INT16: + NVP(buf, size, len, buf_end, elem, int16, int16_t, int, + "%d"); + break; + + case DATA_TYPE_UINT16: + NVP(buf, size, len, buf_end, elem, uint16, uint16_t, + int, "%u"); + break; + + case DATA_TYPE_INT32: + NVP(buf, size, len, buf_end, elem, int32, int32_t, + long, "%ld"); + break; + + case DATA_TYPE_UINT32: + NVP(buf, size, len, buf_end, elem, uint32, uint32_t, + ulong_t, "%lu"); + break; + + case DATA_TYPE_INT64: + NVP(buf, size, len, buf_end, elem, int64, int64_t, + longlong_t, "%lld"); + break; + + case DATA_TYPE_UINT64: + NVP(buf, size, len, buf_end, elem, uint64, uint64_t, + u_longlong_t, "%llu"); + break; + + case DATA_TYPE_STRING: + NVP(buf, size, len, buf_end, elem, string, const char *, + const char *, "'%s'"); + break; + + case DATA_TYPE_BYTE_ARRAY: + NVPA(buf, size, len, buf_end, elem, byte_array, uchar_t, + int, "%u"); + break; + + case DATA_TYPE_INT8_ARRAY: + NVPA(buf, size, len, buf_end, elem, int8_array, int8_t, + int, "%d"); + break; + + case DATA_TYPE_UINT8_ARRAY: + NVPA(buf, size, len, buf_end, elem, uint8_array, + uint8_t, int, "%u"); + break; + + case DATA_TYPE_INT16_ARRAY: + NVPA(buf, size, len, buf_end, elem, int16_array, + int16_t, int, "%d"); + break; + + case DATA_TYPE_UINT16_ARRAY: + NVPA(buf, size, len, buf_end, elem, uint16_array, + uint16_t, int, "%u"); + break; + + case DATA_TYPE_INT32_ARRAY: + NVPA(buf, size, len, buf_end, elem, int32_array, + int32_t, long, "%ld"); + break; + + case DATA_TYPE_UINT32_ARRAY: + NVPA(buf, size, len, buf_end, elem, uint32_array, + uint32_t, ulong_t, "%lu"); + break; + + case DATA_TYPE_INT64_ARRAY: + NVPA(buf, size, len, buf_end, elem, int64_array, + int64_t, longlong_t, "%lld"); + break; + + case DATA_TYPE_UINT64_ARRAY: + NVPA(buf, size, len, buf_end, elem, uint64_array, + uint64_t, u_longlong_t, "%llu"); + break; + + case DATA_TYPE_STRING_ARRAY: + NVPA(buf, size, len, buf_end, elem, string_array, + const char *, const char *, "'%s'"); + break; + + case DATA_TYPE_NVLIST: + (void) nvpair_value_nvlist(elem, &nvlist_value); + + rc = snprintf(buf, size, "%*s%s:\n", indent, "", + nvpair_name(elem)); + if (rc < 0) + return (rc); + size = MAX((int)size - rc, 0); + buf = size == 0 ? NULL : buf_end - size; + len += rc; + + rc = nvlist_snprintf(buf, size, nvlist_value, + indent + 4); + if (rc < 0) + return (rc); + size = MAX((int)size - rc, 0); + buf = size == 0 ? NULL : buf_end - size; + len += rc; + break; + + case DATA_TYPE_NVLIST_ARRAY: + (void) nvpair_value_nvlist_array(elem, + &nvlist_array_value, &count); + for (i = 0; i < count; i++) { + rc = snprintf(buf, size, "%*s%s[%u]:\n", + indent, "", nvpair_name(elem), i); + if (rc < 0) + return (rc); + size = MAX((int)size - rc, 0); + buf = size == 0 ? NULL : buf_end - size; + len += rc; + + rc = nvlist_snprintf(buf, size, + nvlist_array_value[i], indent + 4); + if (rc < 0) + return (rc); + size = MAX((int)size - rc, 0); + buf = size == 0 ? NULL : buf_end - size; + len += rc; + } + break; + + default: + rc = snprintf(buf, size, "bad config type %d for %s\n", + nvpair_type(elem), nvpair_name(elem)); + if (rc < 0) + return (rc); + size = MAX((int)size - rc, 0); + buf = size == 0 ? NULL : buf_end - size; + len += rc; + } + } + return (len); +} + EXPORT_SYMBOL(nv_alloc_init); EXPORT_SYMBOL(nv_alloc_reset); EXPORT_SYMBOL(nv_alloc_fini); /* list management */ EXPORT_SYMBOL(nvlist_alloc); EXPORT_SYMBOL(nvlist_free); EXPORT_SYMBOL(nvlist_size); EXPORT_SYMBOL(nvlist_pack); EXPORT_SYMBOL(nvlist_unpack); EXPORT_SYMBOL(nvlist_dup); EXPORT_SYMBOL(nvlist_merge); EXPORT_SYMBOL(nvlist_xalloc); EXPORT_SYMBOL(nvlist_xpack); EXPORT_SYMBOL(nvlist_xunpack); EXPORT_SYMBOL(nvlist_xdup); EXPORT_SYMBOL(nvlist_lookup_nv_alloc); EXPORT_SYMBOL(nvlist_add_nvpair); EXPORT_SYMBOL(nvlist_add_boolean); EXPORT_SYMBOL(nvlist_add_boolean_value); EXPORT_SYMBOL(nvlist_add_byte); EXPORT_SYMBOL(nvlist_add_int8); EXPORT_SYMBOL(nvlist_add_uint8); EXPORT_SYMBOL(nvlist_add_int16); EXPORT_SYMBOL(nvlist_add_uint16); EXPORT_SYMBOL(nvlist_add_int32); EXPORT_SYMBOL(nvlist_add_uint32); EXPORT_SYMBOL(nvlist_add_int64); EXPORT_SYMBOL(nvlist_add_uint64); EXPORT_SYMBOL(nvlist_add_string); EXPORT_SYMBOL(nvlist_add_nvlist); EXPORT_SYMBOL(nvlist_add_boolean_array); EXPORT_SYMBOL(nvlist_add_byte_array); EXPORT_SYMBOL(nvlist_add_int8_array); EXPORT_SYMBOL(nvlist_add_uint8_array); EXPORT_SYMBOL(nvlist_add_int16_array); EXPORT_SYMBOL(nvlist_add_uint16_array); EXPORT_SYMBOL(nvlist_add_int32_array); EXPORT_SYMBOL(nvlist_add_uint32_array); EXPORT_SYMBOL(nvlist_add_int64_array); EXPORT_SYMBOL(nvlist_add_uint64_array); EXPORT_SYMBOL(nvlist_add_string_array); EXPORT_SYMBOL(nvlist_add_nvlist_array); EXPORT_SYMBOL(nvlist_next_nvpair); EXPORT_SYMBOL(nvlist_prev_nvpair); EXPORT_SYMBOL(nvlist_empty); EXPORT_SYMBOL(nvlist_add_hrtime); EXPORT_SYMBOL(nvlist_remove); EXPORT_SYMBOL(nvlist_remove_nvpair); EXPORT_SYMBOL(nvlist_remove_all); EXPORT_SYMBOL(nvlist_lookup_boolean); EXPORT_SYMBOL(nvlist_lookup_boolean_value); EXPORT_SYMBOL(nvlist_lookup_byte); EXPORT_SYMBOL(nvlist_lookup_int8); EXPORT_SYMBOL(nvlist_lookup_uint8); EXPORT_SYMBOL(nvlist_lookup_int16); EXPORT_SYMBOL(nvlist_lookup_uint16); EXPORT_SYMBOL(nvlist_lookup_int32); EXPORT_SYMBOL(nvlist_lookup_uint32); EXPORT_SYMBOL(nvlist_lookup_int64); EXPORT_SYMBOL(nvlist_lookup_uint64); EXPORT_SYMBOL(nvlist_lookup_string); EXPORT_SYMBOL(nvlist_lookup_nvlist); EXPORT_SYMBOL(nvlist_lookup_boolean_array); EXPORT_SYMBOL(nvlist_lookup_byte_array); EXPORT_SYMBOL(nvlist_lookup_int8_array); EXPORT_SYMBOL(nvlist_lookup_uint8_array); EXPORT_SYMBOL(nvlist_lookup_int16_array); EXPORT_SYMBOL(nvlist_lookup_uint16_array); EXPORT_SYMBOL(nvlist_lookup_int32_array); EXPORT_SYMBOL(nvlist_lookup_uint32_array); EXPORT_SYMBOL(nvlist_lookup_int64_array); EXPORT_SYMBOL(nvlist_lookup_uint64_array); EXPORT_SYMBOL(nvlist_lookup_string_array); EXPORT_SYMBOL(nvlist_lookup_nvlist_array); EXPORT_SYMBOL(nvlist_lookup_hrtime); EXPORT_SYMBOL(nvlist_lookup_pairs); EXPORT_SYMBOL(nvlist_lookup_nvpair); EXPORT_SYMBOL(nvlist_exists); +EXPORT_SYMBOL(nvlist_snprintf); + /* processing nvpair */ EXPORT_SYMBOL(nvpair_name); EXPORT_SYMBOL(nvpair_type); EXPORT_SYMBOL(nvpair_value_boolean_value); EXPORT_SYMBOL(nvpair_value_byte); EXPORT_SYMBOL(nvpair_value_int8); EXPORT_SYMBOL(nvpair_value_uint8); EXPORT_SYMBOL(nvpair_value_int16); EXPORT_SYMBOL(nvpair_value_uint16); EXPORT_SYMBOL(nvpair_value_int32); EXPORT_SYMBOL(nvpair_value_uint32); EXPORT_SYMBOL(nvpair_value_int64); EXPORT_SYMBOL(nvpair_value_uint64); EXPORT_SYMBOL(nvpair_value_string); EXPORT_SYMBOL(nvpair_value_nvlist); EXPORT_SYMBOL(nvpair_value_boolean_array); EXPORT_SYMBOL(nvpair_value_byte_array); EXPORT_SYMBOL(nvpair_value_int8_array); EXPORT_SYMBOL(nvpair_value_uint8_array); EXPORT_SYMBOL(nvpair_value_int16_array); EXPORT_SYMBOL(nvpair_value_uint16_array); EXPORT_SYMBOL(nvpair_value_int32_array); EXPORT_SYMBOL(nvpair_value_uint32_array); EXPORT_SYMBOL(nvpair_value_int64_array); EXPORT_SYMBOL(nvpair_value_uint64_array); EXPORT_SYMBOL(nvpair_value_string_array); EXPORT_SYMBOL(nvpair_value_nvlist_array); EXPORT_SYMBOL(nvpair_value_hrtime); diff --git a/module/zfs/zfs_debug_common.c b/module/zfs/zfs_debug_common.c new file mode 100644 index 000000000000..cff0dc01c39c --- /dev/null +++ b/module/zfs/zfs_debug_common.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: CDDL-1.0 +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2025 by Lawrence Livermore National Security, LLC. + */ +/* + * This file contains zfs_dbgmsg() specific functions that are not OS or + * userspace specific. + */ +#if !defined(_KERNEL) +#include +#endif + +#include +#include +#include + +/* + * Given a multi-line string, print out one of the lines and return a pointer + * to the next line. Lines are demarcated by '\n'. Note: this modifies the + * input string (buf[]). + * + * This function is meant to be used in a loop like: + * while (buf != NULL) + * buf = kernel_print_one_line(buf); + * + * This function is useful for printing large, multi-line text buffers. + * + * Returns the pointer to the beginning of the next line in buf[], or NULL + * if it's the last line, or nothing more to print. + */ +static char * +zfs_dbgmsg_one_line(char *buf) +{ + char *nl; + if (!buf) + return (NULL); + + nl = strchr(buf, '\n'); + if (nl == NULL) { + __zfs_dbgmsg(buf); + return (NULL); /* done */ + } + *nl = '\0'; + __zfs_dbgmsg(buf); + + return (nl + 1); +} + +/* + * Dump an nvlist tree to dbgmsg. + * + * This is the zfs_dbgmsg version of userspace's dump_nvlist() from libnvpair. + */ +void +__zfs_dbgmsg_nvlist(nvlist_t *nv) +{ + int len; + char *buf; + + len = nvlist_snprintf(NULL, 0, nv, 4); + len++; /* Add null terminator */ + + buf = vmem_alloc(len, KM_SLEEP); + if (buf == NULL) + return; + + (void) nvlist_snprintf(buf, len, nv, 4); + + while (buf != NULL) + buf = zfs_dbgmsg_one_line(buf); + + vmem_free(buf, len); +} + +#ifdef _KERNEL +EXPORT_SYMBOL(__zfs_dbgmsg_nvlist); +#endif