diff --git a/sys/boot/zfs/libzfs.h b/sys/boot/zfs/libzfs.h index 58bfd266685b..0ce0a9c7f69b 100644 --- a/sys/boot/zfs/libzfs.h +++ b/sys/boot/zfs/libzfs.h @@ -65,7 +65,7 @@ int zfs_probe_dev(const char *devname, uint64_t *pool_guid); int zfs_list(const char *name); void init_zfs_bootenv(char *currdev); int zfs_bootenv(const char *name); -int zfs_belist_add(const char *name); +int zfs_belist_add(const char *name, uint64_t __unused); int zfs_set_env(void); extern struct devsw zfs_dev; diff --git a/sys/boot/zfs/zfs.c b/sys/boot/zfs/zfs.c index 229bcac3c3a4..2552e09cdf20 100644 --- a/sys/boot/zfs/zfs.c +++ b/sys/boot/zfs/zfs.c @@ -801,7 +801,7 @@ zfs_bootenv(const char *name) } int -zfs_belist_add(const char *name) +zfs_belist_add(const char *name, uint64_t value __unused) { /* Skip special datasets that start with a $ character */ diff --git a/sys/boot/zfs/zfsimpl.c b/sys/boot/zfs/zfsimpl.c index aa1a789b6c33..73fe0b64b8f5 100644 --- a/sys/boot/zfs/zfsimpl.c +++ b/sys/boot/zfs/zfsimpl.c @@ -1473,12 +1473,12 @@ zap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name, uint64 * the directory contents. */ static int -mzap_list(const dnode_phys_t *dnode, int (*callback)(const char *)) +mzap_list(const dnode_phys_t *dnode, int (*callback)(const char *, uint64_t)) { const mzap_phys_t *mz; const mzap_ent_phys_t *mze; size_t size; - int chunks, i; + int chunks, i, rc; /* * Microzap objects use exactly one block. Read the whole @@ -1490,9 +1490,11 @@ mzap_list(const dnode_phys_t *dnode, int (*callback)(const char *)) for (i = 0; i < chunks; i++) { mze = &mz->mz_chunk[i]; - if (mze->mze_name[0]) - //printf("%-32s 0x%jx\n", mze->mze_name, (uintmax_t)mze->mze_value); - callback(mze->mze_name); + if (mze->mze_name[0]) { + rc = callback(mze->mze_name, mze->mze_value); + if (rc != 0) + return (rc); + } } return (0); @@ -1503,12 +1505,12 @@ mzap_list(const dnode_phys_t *dnode, int (*callback)(const char *)) * the directory header. */ static int -fzap_list(const spa_t *spa, const dnode_phys_t *dnode, int (*callback)(const char *)) +fzap_list(const spa_t *spa, const dnode_phys_t *dnode, int (*callback)(const char *, uint64_t)) { int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT; zap_phys_t zh = *(zap_phys_t *) zap_scratch; fat_zap_t z; - int i, j; + int i, j, rc; if (zh.zap_magic != ZAP_MAGIC) return (EIO); @@ -1566,14 +1568,16 @@ fzap_list(const spa_t *spa, const dnode_phys_t *dnode, int (*callback)(const cha value = fzap_leaf_value(&zl, zc); //printf("%s 0x%jx\n", name, (uintmax_t)value); - callback((const char *)name); + rc = callback((const char *)name, value); + if (rc != 0) + return (rc); } } return (0); } -static int zfs_printf(const char *name) +static int zfs_printf(const char *name, uint64_t value __unused) { printf("%s\n", name); @@ -1868,7 +1872,7 @@ zfs_list_dataset(const spa_t *spa, uint64_t objnum/*, int pos, char *entry*/) } int -zfs_callback_dataset(const spa_t *spa, uint64_t objnum, int (*callback)(const char *name)) +zfs_callback_dataset(const spa_t *spa, uint64_t objnum, int (*callback)(const char *, uint64_t)) { uint64_t dir_obj, child_dir_zapobj, zap_type; dnode_phys_t child_dir_zap, dir, dataset; @@ -2008,9 +2012,67 @@ zfs_mount(const spa_t *spa, uint64_t rootobj, struct zfsmount *mount) return (0); } +/* + * callback function for feature name checks. + */ +static int +check_feature(const char *name, uint64_t value) +{ + int i; + + if (value == 0) + return (0); + if (name[0] == '\0') + return (0); + + for (i = 0; features_for_read[i] != NULL; i++) { + if (strcmp(name, features_for_read[i]) == 0) + return (0); + } + printf("ZFS: unsupported feature: %s\n", name); + return (EIO); +} + +/* + * Checks whether the MOS features that are active are supported. + */ +static int +check_mos_features(const spa_t *spa) +{ + dnode_phys_t dir; + uint64_t objnum, zap_type; + size_t size; + int rc; + + if ((rc = objset_get_dnode(spa, &spa->spa_mos, DMU_OT_OBJECT_DIRECTORY, + &dir)) != 0) + return (rc); + if ((rc = zap_lookup(spa, &dir, DMU_POOL_FEATURES_FOR_READ, &objnum)) != 0) + return (rc); + + if ((rc = objset_get_dnode(spa, &spa->spa_mos, objnum, &dir)) != 0) + return (rc); + + if (dir.dn_type != DMU_OTN_ZAP_METADATA) + return (EIO); + + size = dir.dn_datablkszsec * 512; + if (dnode_read(spa, &dir, 0, zap_scratch, size)) + return (EIO); + + zap_type = *(uint64_t *) zap_scratch; + if (zap_type == ZBT_MICRO) + rc = mzap_list(&dir, check_feature); + else + rc = fzap_list(spa, &dir, check_feature); + + return (rc); +} + static int zfs_spa_init(spa_t *spa) { + int rc; if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) { printf("ZFS: can't read MOS of pool %s\n", spa->spa_name); @@ -2020,7 +2082,13 @@ zfs_spa_init(spa_t *spa) printf("ZFS: corrupted MOS of pool %s\n", spa->spa_name); return (EIO); } - return (0); + + rc = check_mos_features(spa); + if (rc != 0) { + printf("ZFS: pool %s is not supported\n", spa->spa_name); + } + + return (rc); } static int diff --git a/sys/cddl/boot/zfs/zfsimpl.h b/sys/cddl/boot/zfs/zfsimpl.h index 730cdf49a0dd..a18c2b0bbffa 100644 --- a/sys/cddl/boot/zfs/zfsimpl.h +++ b/sys/cddl/boot/zfs/zfsimpl.h @@ -63,6 +63,8 @@ #define _NOTE(s) +typedef enum { B_FALSE, B_TRUE } boolean_t; + /* CRC64 table */ #define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */ @@ -899,6 +901,41 @@ typedef struct dnode_phys { blkptr_t dn_spill; } dnode_phys_t; +typedef enum dmu_object_byteswap { + DMU_BSWAP_UINT8, + DMU_BSWAP_UINT16, + DMU_BSWAP_UINT32, + DMU_BSWAP_UINT64, + DMU_BSWAP_ZAP, + DMU_BSWAP_DNODE, + DMU_BSWAP_OBJSET, + DMU_BSWAP_ZNODE, + DMU_BSWAP_OLDACL, + DMU_BSWAP_ACL, + /* + * Allocating a new byteswap type number makes the on-disk format + * incompatible with any other format that uses the same number. + * + * Data can usually be structured to work with one of the + * DMU_BSWAP_UINT* or DMU_BSWAP_ZAP types. + */ + DMU_BSWAP_NUMFUNCS +} dmu_object_byteswap_t; + +#define DMU_OT_NEWTYPE 0x80 +#define DMU_OT_METADATA 0x40 +#define DMU_OT_BYTESWAP_MASK 0x3f + +/* + * Defines a uint8_t object type. Object types specify if the data + * in the object is metadata (boolean) and how to byteswap the data + * (dmu_object_byteswap_t). + */ +#define DMU_OT(byteswap, metadata) \ + (DMU_OT_NEWTYPE | \ + ((metadata) ? DMU_OT_METADATA : 0) | \ + ((byteswap) & DMU_OT_BYTESWAP_MASK)) + typedef enum dmu_object_type { DMU_OT_NONE, /* general: */ @@ -959,7 +996,21 @@ typedef enum dmu_object_type { DMU_OT_SA_ATTR_LAYOUTS, /* ZAP */ DMU_OT_SCAN_XLATE, /* ZAP */ DMU_OT_DEDUP, /* fake dedup BP from ddt_bp_create() */ - DMU_OT_NUMTYPES + DMU_OT_NUMTYPES, + + /* + * Names for valid types declared with DMU_OT(). + */ + DMU_OTN_UINT8_DATA = DMU_OT(DMU_BSWAP_UINT8, B_FALSE), + DMU_OTN_UINT8_METADATA = DMU_OT(DMU_BSWAP_UINT8, B_TRUE), + DMU_OTN_UINT16_DATA = DMU_OT(DMU_BSWAP_UINT16, B_FALSE), + DMU_OTN_UINT16_METADATA = DMU_OT(DMU_BSWAP_UINT16, B_TRUE), + DMU_OTN_UINT32_DATA = DMU_OT(DMU_BSWAP_UINT32, B_FALSE), + DMU_OTN_UINT32_METADATA = DMU_OT(DMU_BSWAP_UINT32, B_TRUE), + DMU_OTN_UINT64_DATA = DMU_OT(DMU_BSWAP_UINT64, B_FALSE), + DMU_OTN_UINT64_METADATA = DMU_OT(DMU_BSWAP_UINT64, B_TRUE), + DMU_OTN_ZAP_DATA = DMU_OT(DMU_BSWAP_ZAP, B_FALSE), + DMU_OTN_ZAP_METADATA = DMU_OT(DMU_BSWAP_ZAP, B_TRUE) } dmu_object_type_t; typedef enum dmu_objset_type { @@ -1097,6 +1148,7 @@ typedef struct dsl_dataset_phys { */ #define DMU_POOL_DIRECTORY_OBJECT 1 #define DMU_POOL_CONFIG "config" +#define DMU_POOL_FEATURES_FOR_READ "features_for_read" #define DMU_POOL_ROOT_DATASET "root_dataset" #define DMU_POOL_SYNC_BPLIST "sync_bplist" #define DMU_POOL_ERRLOG_SCRUB "errlog_scrub"