]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
* grub-core/fs/zfs/zfs.c: Check for feature compatibility.
authorMassimo Maggi <me@massimo-maggi.eu>
Sun, 14 Jul 2013 12:10:42 +0000 (14:10 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 14 Jul 2013 12:10:42 +0000 (14:10 +0200)
ChangeLog
grub-core/fs/zfs/zfs.c
include/grub/zfs/dmu.h
include/grub/zfs/zfs.h

index a36426a719a5567c811b1415bbddd776af770d59..0e118b973b5ac849893c61b0ee7752beddf32c25 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2013-07-14  Massimo Maggi <me@massimo-maggi.eu>
+
+       * grub-core/fs/zfs/zfs.c: Check for feature compatibility.
+
 2013-07-14  Massimo Maggi <me@massimo-maggi.eu>
 
        * grub-core/fs/zfs/zfs.c (uberblock_verify): Accept version 5000.
index beea55509e2551a4851a764dfb73eab4540c306b..e9c2fe999f77583387a7e1d86a9d47f65e886dad 100644 (file)
@@ -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)),
index 8fc6dc5b504cf48079180b3a9899cb69a166a9cb..4ad616c74bcb0a58a5c3585044da4e143d189a7c 100644 (file)
 
 #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 */
index 42942482016a6320f20cce62bc0bf74efcad8341..8bf09197429056f6d194eb87783f15750640fc5d 100644 (file)
@@ -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