From 776bdce291293ff1e124648819360ab07acc897e Mon Sep 17 00:00:00 2001 From: Massimo Maggi Date: Sun, 14 Jul 2013 14:10:42 +0200 Subject: [PATCH] * grub-core/fs/zfs/zfs.c: Check for feature compatibility. --- ChangeLog | 4 ++ grub-core/fs/zfs/zfs.c | 109 ++++++++++++++++++++++++++++++++++++++++- include/grub/zfs/dmu.h | 46 ++++++++++++++++- include/grub/zfs/zfs.h | 1 + 4 files changed, 157 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index a36426a71..0e118b973 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-07-14 Massimo Maggi + + * grub-core/fs/zfs/zfs.c: Check for feature compatibility. + 2013-07-14 Massimo Maggi * grub-core/fs/zfs/zfs.c (uberblock_verify): Accept version 5000. diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index beea55509..e9c2fe999 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -271,6 +271,20 @@ grub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct grub_zfs_key *key grub_size_t keysize, grub_uint64_t salt, grub_uint64_t algo) = NULL; +/* + * List of pool features that the grub implementation of ZFS supports for + * read. Note that features that are only required for write do not need + * to be listed here since grub opens pools in read-only mode. + */ +#define MAX_SUPPORTED_FEATURE_STRLEN 50 +static const char *spa_feature_names[] = { + "max.test:feat1",NULL +}; + +static int +check_feature(const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx); +static int +check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct grub_zfs_data* data ); static grub_err_t zlib_decompress (void *s, void *d, @@ -777,7 +791,7 @@ check_pool_label (struct grub_zfs_data *data, int *inserted) { grub_uint64_t pool_state, txg = 0; - char *nvlist; + char *nvlist,*features; #if 0 char *nv; #endif @@ -897,7 +911,31 @@ check_pool_label (struct grub_zfs_data *data, grub_free (nv); } grub_dprintf ("zfs", "check 10 passed\n"); - + features = grub_zfs_nvlist_lookup_nvlist(nvlist, + ZPOOL_CONFIG_FEATURES_FOR_READ); + if (features) + { + const char *nvp=NULL; + char name[MAX_SUPPORTED_FEATURE_STRLEN + 1]; + char *nameptr; + int namelen; + while ((nvp = nvlist_next_nvpair(features, nvp)) != NULL) + { + nvpair_name(nvp, &nameptr,&namelen); + if(namelen > MAX_SUPPORTED_FEATURE_STRLEN) + namelen = MAX_SUPPORTED_FEATURE_STRLEN; + grub_strncpy(name,nameptr,namelen); + name[namelen]=0; + grub_dprintf("zfs","namelen=%u str=%s\n",namelen,name); + if (check_feature(name,1, NULL) != 0) + { + grub_dprintf("zfs","feature missing in check_pool_label:%s\n",name); + err= grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET," check_pool_label missing feature '%s' for read",name); + return err; + } + } + } + grub_dprintf ("zfs", "check 12 passed (feature flags)\n"); grub_free (nvlist); return GRUB_ERR_NONE; @@ -3416,6 +3454,11 @@ zfs_mount (grub_device_t dev) return NULL; } + if (ub->ub_version >= SPA_VERSION_FEATURES && + check_mos_features(&((objset_phys_t *) osp)->os_meta_dnode,ub_endian, + data) != 0) + return NULL; + /* Got the MOS. Save it at the memory addr MOS. */ grub_memmove (&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE); @@ -3983,6 +4026,68 @@ grub_zfs_dir (grub_device_t device, const char *path, return grub_errno; } +static int +check_feature(const char *name, grub_uint64_t val,__attribute__((unused)) struct grub_zfs_dir_ctx *ctx) +{ + int i; + if(val ==0) return 0; + if(*name==0) return 0; + for (i = 0; spa_feature_names[i] != NULL; i++) + { + if (grub_strcmp(name, spa_feature_names[i]) == 0) + return 0; + } + grub_printf("missing feature for read '%s'\n",name); + return 1; +} + +/* + * Checks whether the MOS features that are active are supported by this + * (GRUB's) implementation of ZFS. + * + * Return: + * 0: Success. + * errnum: Failure. + */ + +static int +check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct grub_zfs_data* data ) +{ + grub_uint64_t objnum; + grub_uint8_t errnum = 0; + dnode_end_t dn,mosmdn; + mzap_phys_t* mzp; + grub_zfs_endian_t endianzap; + int size; + grub_memmove(&(mosmdn.dn),mosmdn_phys,sizeof(dnode_phys_t)); + mosmdn.endian=endian; + errnum = dnode_get(&mosmdn, DMU_POOL_DIRECTORY_OBJECT, + DMU_OT_OBJECT_DIRECTORY, &dn,data); + if (errnum != 0) + return errnum; + + /* + * Find the object number for 'features_for_read' and retrieve its + * corresponding dnode. Note that we don't check features_for_write + * because GRUB is not opening the pool for write. + */ + errnum = zap_lookup(&dn, DMU_POOL_FEATURES_FOR_READ, &objnum, data,0); + if (errnum != 0) + return errnum; + + errnum = dnode_get(&mosmdn, objnum, DMU_OTN_ZAP_METADATA, &dn, data); + if (errnum != 0) + return errnum; + + errnum = dmu_read(&dn, 0, (void**)&mzp, &endianzap,data); + if (errnum != 0) + return errnum; + + size = grub_zfs_to_cpu16 (dn.dn.dn_datablkszsec, dn.endian) << SPA_MINBLOCKSHIFT; + return mzap_iterate (mzp,endianzap, size, check_feature,NULL); +} + + #ifdef GRUB_UTIL static grub_err_t grub_zfs_embed (grub_device_t device __attribute__ ((unused)), diff --git a/include/grub/zfs/dmu.h b/include/grub/zfs/dmu.h index 8fc6dc5b5..4ad616c74 100644 --- a/include/grub/zfs/dmu.h +++ b/include/grub/zfs/dmu.h @@ -22,6 +22,39 @@ #ifndef _SYS_DMU_H #define _SYS_DMU_H +#define B_FALSE 0 +#define B_TRUE 1 + +#define DMU_OT_NEWTYPE 0x80 +#define DMU_OT_METADATA 0x40 +#define DMU_OT_BYTESWAP_MASK 0x3f + +#define DMU_OT(byteswap, metadata) \ + (DMU_OT_NEWTYPE | \ + ((metadata) ? DMU_OT_METADATA : 0) | \ + ((byteswap) & DMU_OT_BYTESWAP_MASK)) + +#define DMU_OT_IS_VALID(ot) (((ot) & DMU_OT_NEWTYPE) ? \ + ((ot) & DMU_OT_BYTESWAP_MASK) < DMU_BSWAP_NUMFUNCS : \ + (ot) < DMU_OT_NUMTYPES) + +#define DMU_OT_IS_METADATA(ot) (((ot) & DMU_OT_NEWTYPE) ? \ + ((ot) & DMU_OT_METADATA) : \ + dmu_ot[(ot)].ot_metadata) + +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, + DMU_BSWAP_NUMFUNCS +} dmu_object_byteswap_t; /* * This file describes the interface that the DMU provides for its @@ -89,7 +122,17 @@ typedef enum dmu_object_type { DMU_OT_SA_ATTR_REGISTRATION, /* ZAP */ DMU_OT_SA_ATTR_LAYOUTS, /* ZAP */ DMU_OT_DSL_KEYCHAIN = 54, - DMU_OT_NUMTYPES + DMU_OT_NUMTYPES, + 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 { @@ -116,5 +159,6 @@ typedef enum dmu_objset_type { #define DMU_POOL_HISTORY "history" #define DMU_POOL_PROPS "pool_props" #define DMU_POOL_L2CACHE "l2cache" +#define DMU_POOL_FEATURES_FOR_READ "features_for_read" #endif /* _SYS_DMU_H */ diff --git a/include/grub/zfs/zfs.h b/include/grub/zfs/zfs.h index 429424820..8bf091974 100644 --- a/include/grub/zfs/zfs.h +++ b/include/grub/zfs/zfs.h @@ -80,6 +80,7 @@ typedef enum grub_zfs_endian #define ZPOOL_CONFIG_DDT_HISTOGRAM "ddt_histogram" #define ZPOOL_CONFIG_DDT_OBJ_STATS "ddt_object_stats" #define ZPOOL_CONFIG_DDT_STATS "ddt_stats" +#define ZPOOL_CONFIG_FEATURES_FOR_READ "features_for_read" /* * The persistent vdev state is stored as separate values rather than a single * 'vdev_state' entry. This is because a device can be in multiple states, such