Index: sys/boot/zfs/zfsimpl.c =================================================================== --- sys/boot/zfs/zfsimpl.c (revision 236715) +++ sys/boot/zfs/zfsimpl.c (working copy) @@ -45,6 +45,13 @@ struct zfsmount { }; /* + * List of ZFS features supported for read + */ +static const char *features_for_read[] = { + NULL +}; + +/* * List of all vdevs, chained through v_alllink. */ static vdev_list_t zfs_vdevs; @@ -140,92 +147,22 @@ xdr_uint64_t(const unsigned char **xdr, uint64_t * } static int -nvlist_find(const unsigned char *nvlist, const char *name, int type, - int* elementsp, void *valuep) +check_feature(const char *feature, const int len) { - const unsigned char *p, *pair; - int junk; - int encoded_size, decoded_size; + int i; - p = nvlist; - xdr_int(&p, &junk); - xdr_int(&p, &junk); - - pair = p; - xdr_int(&p, &encoded_size); - xdr_int(&p, &decoded_size); - while (encoded_size && decoded_size) { - int namelen, pairtype, elements; - const char *pairname; - - xdr_int(&p, &namelen); - pairname = (const char*) p; - p += roundup(namelen, 4); - xdr_int(&p, &pairtype); - - if (!memcmp(name, pairname, namelen) && type == pairtype) { - xdr_int(&p, &elements); - if (elementsp) - *elementsp = elements; - if (type == DATA_TYPE_UINT64) { - xdr_uint64_t(&p, (uint64_t *) valuep); + if (len != 0) { + for (i = 0; features_for_read[i] != NULL; i++) { + if (!memcmp(feature,features_for_read[i], len)) return (0); - } else if (type == DATA_TYPE_STRING) { - int len; - xdr_int(&p, &len); - (*(const char**) valuep) = (const char*) p; - return (0); - } else if (type == DATA_TYPE_NVLIST - || type == DATA_TYPE_NVLIST_ARRAY) { - (*(const unsigned char**) valuep) = - (const unsigned char*) p; - return (0); - } else { - return (EIO); - } - } else { - /* - * Not the pair we are looking for, skip to the next one. - */ - p = pair + encoded_size; } + } else + return (0); - pair = p; - xdr_int(&p, &encoded_size); - xdr_int(&p, &decoded_size); - } - - return (EIO); + printf("Unsupported ZFS feature: %s\n", feature); + return (ENOENT); } -/* - * Return the next nvlist in an nvlist array. - */ -static const unsigned char * -nvlist_next(const unsigned char *nvlist) -{ - const unsigned char *p, *pair; - int junk; - int encoded_size, decoded_size; - - p = nvlist; - xdr_int(&p, &junk); - xdr_int(&p, &junk); - - pair = p; - xdr_int(&p, &encoded_size); - xdr_int(&p, &decoded_size); - while (encoded_size && decoded_size) { - p = pair + encoded_size; - - pair = p; - xdr_int(&p, &encoded_size); - xdr_int(&p, &decoded_size); - } - - return p; -} - #ifdef TEST static const unsigned char * @@ -335,7 +272,222 @@ nvlist_print(const unsigned char *nvlist, unsigned #endif +/* + * Return the next nvpair in an nvlist array. + * If nvpair is NULL, return the first nvpair. + * If nvlist is NULL or this is the end of the list, return NULL + */ +static const unsigned char * +nvlist_next_nvpair(const unsigned char *nvlist, const unsigned char *nvpair) +{ + const unsigned char *p; + int junk; + int encoded_size, decoded_size; + + if (nvlist == NULL) + return (NULL); + + if (nvpair == NULL) { + p = nvlist; + xdr_int(&p, &junk); + xdr_int(&p, &junk); + } else { + p = nvpair; + xdr_int(&p, &encoded_size); + xdr_int(&p, &decoded_size); + + if (encoded_size && decoded_size) + p = nvpair + encoded_size; + else + return (NULL); + } + + return p; +} + +/* + * Copy nvpair_name to buffer + * On success return 0, failure return 1 + */ static int +nvpair_name(const unsigned char *nvpair, char *buf) +{ + const unsigned char *p; + int encoded_size, decoded_size; + int rc; + + rc = 1; + p = nvpair; + xdr_int(&p, &encoded_size); + xdr_int(&p, &decoded_size); + + if (encoded_size && decoded_size) { + int namelen; + xdr_int(&p, &namelen); + if (namelen > 0) { + memcpy(buf,(char*) p, MAXNAMELEN); + rc = 0; + } + } + + return (rc); +} + +/* + * Retrieve the type of a nvpair + */ +static int +nvpair_type(const unsigned char *nvpair) +{ + const unsigned char *p; + int encoded_size, decoded_size; + int namelen, pairtype; + + p = nvpair; + xdr_int(&p, &encoded_size); + xdr_int(&p, &decoded_size); + + if (encoded_size && decoded_size) { + xdr_int(&p, &namelen); + p += roundup(namelen, 4); + xdr_int(&p, &pairtype); + return (pairtype); + } + + return (0); +} + +/* + * Retrieve the pointer to a nvpair value + */ +static int +nvpair_value(const unsigned char *nvpair, int type, int* elementsp, void *valuep) +{ + const unsigned char *p; + int encoded_size, decoded_size; + int namelen, pairtype; + + p = nvpair; + xdr_int(&p, &encoded_size); + xdr_int(&p, &decoded_size); + + if (encoded_size && decoded_size) { + int elements; + + xdr_int(&p, &namelen); + p += roundup(namelen, 4); + xdr_int(&p, &pairtype); + + if (type == pairtype) { + xdr_int(&p, &elements); + if (elementsp) + *elementsp = elements; + switch (type) { + case DATA_TYPE_BOOLEAN: { + return (0); + } + case DATA_TYPE_UINT64: { + xdr_uint64_t(&p, (uint64_t *) valuep); + return (0); + } + case DATA_TYPE_STRING: { + int len; + xdr_int(&p, &len); + (*(const char**) valuep) = (const char*) p; + return (0); + } + case DATA_TYPE_NVLIST: + case DATA_TYPE_NVLIST_ARRAY: { + (*(const unsigned char**) valuep) = + (const unsigned char*) p; + return (0); + } + default: { + return (EIO); + } + } + } + } + + return (ENOENT); +} + +static int +nvlist_find(const unsigned char *nvlist, const char *name, int type, + int* elementsp, void *valuep) +{ + const unsigned char *pair; + + pair = NULL; + + while ((pair = nvlist_next_nvpair(nvlist,pair)) != NULL) { + char pairname[MAXNAMELEN]; + int pairtype; + + nvpair_name(pair, pairname); + pairtype = nvpair_type(pair); + + if (!memcmp(name, pairname, strlen(pairname)) && + type == pairtype) + return (nvpair_value(pair, type, elementsp, valuep)); + } + + return (EIO); +} + +/* + * Return the next nvlist in an nvlist array. + */ +static const unsigned char * +nvlist_next(const unsigned char *nvlist) +{ + const unsigned char *p, *pair; + int junk; + int encoded_size, decoded_size; + + p = nvlist; + xdr_int(&p, &junk); + xdr_int(&p, &junk); + + pair = p; + xdr_int(&p, &encoded_size); + xdr_int(&p, &decoded_size); + while (encoded_size && decoded_size) { + p = pair + encoded_size; + + pair = p; + xdr_int(&p, &encoded_size); + xdr_int(&p, &decoded_size); + } + + return p; +} + +/* + * Check the ZFS features nvlist for unsupported features + */ +static int +nvlist_check_features(const unsigned char *nvlist) +{ + const unsigned char *pair; + int rc; + + pair = NULL; + + while ((pair = nvlist_next_nvpair(nvlist, pair)) != NULL) { + char pairname[MAXNAMELEN]; + + if (nvpair_name(pair, pairname) != 0) { + rc = check_feature(pairname, strlen(pairname)); + if (rc != 0) + return (rc); + } + } + + return (0); +} + +static int vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf, off_t offset, size_t size) { @@ -788,6 +940,7 @@ vdev_probe(vdev_phys_read_t *read, void *read_priv uint64_t is_log; const char *pool_name; const unsigned char *vdevs; + const unsigned char *features; int i, rc, is_newer; char *upbuf; const struct uberblock *up; @@ -822,12 +975,21 @@ vdev_probe(vdev_phys_read_t *read, void *read_priv return (EIO); } - if (val > SPA_VERSION) { + if (!SPA_VERSION_IS_SUPPORTED(val)) { printf("ZFS: unsupported ZFS version %u (should be %u)\n", (unsigned) val, (unsigned) SPA_VERSION); return (EIO); } + /* Check features for read */ + rc = nvlist_find(nvlist, ZPOOL_CONFIG_FEATURES_FOR_READ, + DATA_TYPE_NVLIST, 0, &features); + if (rc == 0) { + rc = nvlist_check_features(features); + if (rc != 0) + return (rc); + } + if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_STATE, DATA_TYPE_UINT64, 0, &val)) { Index: sys/cddl/boot/zfs/zfsimpl.h =================================================================== --- sys/cddl/boot/zfs/zfsimpl.h (revision 236715) +++ sys/cddl/boot/zfs/zfsimpl.h (working copy) @@ -53,6 +53,8 @@ * Use is subject to license terms. */ +#define MAXNAMELEN 256 + /* CRC64 table */ #define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */ @@ -508,6 +510,7 @@ typedef enum { #define SPA_VERSION_26 26ULL #define SPA_VERSION_27 27ULL #define SPA_VERSION_28 28ULL +#define SPA_VERSION_5000 5000ULL /* * When bumping up SPA_VERSION, make sure GRUB ZFS understands the on-disk @@ -515,8 +518,8 @@ typedef enum { * and do the appropriate changes. Also bump the version number in * usr/src/grub/capability. */ -#define SPA_VERSION SPA_VERSION_28 -#define SPA_VERSION_STRING "28" +#define SPA_VERSION SPA_VERSION_5000 +#define SPA_VERSION_STRING "5000" /* * Symbolic names for the changes that caused a SPA_VERSION switch. @@ -567,7 +570,13 @@ typedef enum { #define SPA_VERSION_DEADLISTS SPA_VERSION_26 #define SPA_VERSION_FAST_SNAP SPA_VERSION_27 #define SPA_VERSION_MULTI_REPLACE SPA_VERSION_28 +#define SPA_VERSION_BEFORE_FEATURES SPA_VERSION_28 +#define SPA_VERSION_FEATURES SPA_VERSION_5000 +#define SPA_VERSION_IS_SUPPORTED(v) \ + (((v) >= SPA_VERSION_INITIAL && (v) <= SPA_VERSION_BEFORE_FEATURES) || \ + ((v) >= SPA_VERSION_FEATURES && (v) <= SPA_VERSION)) + /* * The following are configuration names used in the nvlist describing a pool's * configuration. @@ -602,6 +611,7 @@ typedef enum { #define ZPOOL_CONFIG_HOSTNAME "hostname" #define ZPOOL_CONFIG_IS_LOG "is_log" #define ZPOOL_CONFIG_TIMESTAMP "timestamp" /* not stored on disk */ +#define ZPOOL_CONFIG_FEATURES_FOR_READ "features_for_read" /* * The persistent vdev state is stored as separate values rather than a single