All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] zfs: Support datto encryption and fix endianness handling
@ 2025-08-01 14:30 Vladimir Serbinenko
  2025-08-01 14:30 ` [PATCH 1/2] zfs: Rewrite " Vladimir Serbinenko
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Vladimir Serbinenko @ 2025-08-01 14:30 UTC (permalink / raw)
  To: grub-devel

With these patches I'm able to read my ZFS partition with encrypted datasets
and a big-endian volume even with mixed endianness contents


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] zfs: Rewrite endianness handling
  2025-08-01 14:30 [PATCH 0/2] zfs: Support datto encryption and fix endianness handling Vladimir Serbinenko
@ 2025-08-01 14:30 ` Vladimir Serbinenko
  2025-08-01 14:30 ` [PATCH 2/2] zfs: Support datto encryption Vladimir Serbinenko
  2025-08-11 13:23 ` [PATCH 0/2] zfs: Support datto encryption and fix endianness handling Neal Gompa
  2 siblings, 0 replies; 8+ messages in thread
From: Vladimir Serbinenko @ 2025-08-01 14:30 UTC (permalink / raw)
  To: grub-devel; +Cc: Vladimir Serbinenko

Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
---
 grub-core/fs/zfs/zfs.c          | 917 ++++++++++++++++++--------------
 grub-core/fs/zfs/zfs_fletcher.c |  18 +-
 grub-core/fs/zfs/zfs_sha256.c   |  14 +-
 grub-core/fs/zfs/zfscrypt.c     |   2 +-
 include/grub/zfs/sa_impl.h      |   1 +
 include/grub/zfs/spa.h          |   9 +
 include/grub/zfs/zfs.h          |  10 +-
 7 files changed, 551 insertions(+), 420 deletions(-)

diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index afe821f9b..b88a2b032 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -193,12 +193,6 @@ typedef struct zio_checksum_info {
   const char		*ci_name;	/* descriptive name */
 } zio_checksum_info_t;
 
-typedef struct dnode_end
-{
-  dnode_phys_t dn;
-  grub_zfs_endian_t endian;
-} dnode_end_t;
-
 struct grub_zfs_device_desc
 {
   enum { DEVICE_LEAF, DEVICE_MIRROR, DEVICE_RAIDZ } type;
@@ -223,7 +217,7 @@ struct grub_zfs_device_desc
 
 struct subvolume
 {
-  dnode_end_t mdn;
+  dnode_phys_t mdn;
   grub_uint64_t obj;
   grub_uint64_t case_insensitive;
   grub_size_t nkeys;
@@ -247,10 +241,9 @@ struct grub_zfs_data
   dnode_phys_t *dnode_mdn;
   grub_uint64_t dnode_start;
   grub_uint64_t dnode_end;
-  grub_zfs_endian_t dnode_endian;
 
-  dnode_end_t mos;
-  dnode_end_t dnode;
+  dnode_phys_t mos;
+  dnode_phys_t dnode;
   struct subvolume subvol;
 
   struct grub_zfs_device_desc *devices_attached;
@@ -273,7 +266,7 @@ struct grub_zfs_dir_ctx
 
 grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
 				grub_uint64_t algo,
-				void *nonce,
+				const void *nonce,
 				char *buf, grub_size_t size,
 				const grub_uint32_t *expected_mac,
 				grub_zfs_endian_t endian) = NULL;
@@ -302,7 +295,7 @@ static const char *spa_feature_names[] = {
 static int
 check_feature(const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx);
 static grub_err_t
-check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct grub_zfs_data* data );
+check_mos_features(dnode_phys_t *mosmdn_phys, struct grub_zfs_data* data );
 
 static grub_err_t
 zlib_decompress (void *s, void *d,
@@ -404,7 +397,7 @@ static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = {
   {"zstd", zstd_decompress},    /* ZIO_COMPRESS_ZSTD   */
 };
 
-static grub_err_t zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian,
+static grub_err_t zio_read_data (const blkptr_t * bp,
 				 void *buf, struct grub_zfs_data *data);
 
 /*
@@ -449,6 +442,245 @@ static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
   {zio_checksum_SHA256, 1, 0, "SHA256+MAC"},
 };
 
+static inline void
+grub_zfs_byteswap_u64(grub_uint64_t *val)
+{
+  *val = grub_swap_bytes64(*val);
+}
+
+static inline void
+grub_zfs_byteswap_u32(grub_uint32_t *val)
+{
+  *val = grub_swap_bytes32(*val);
+}
+
+static inline void
+grub_zfs_byteswap_u16(grub_uint16_t *val)
+{
+  *val = grub_swap_bytes16(*val);
+}
+
+static void
+grub_zfs_byteswap_checksum(zio_cksum_t *csum)
+{
+  for (int i = 0; i < 4; i++)
+    grub_zfs_byteswap_u64(&csum->zc_word[i]);
+}
+
+static void
+grub_zfs_byteswap_blkptr(blkptr_t *bp)
+{
+  for (int i = 0; i < SPA_DVAS_PER_BP; i++)
+    {
+      grub_zfs_byteswap_u64(&bp->blk_dva[i].dva_word[0]);
+      grub_zfs_byteswap_u64(&bp->blk_dva[i].dva_word[1]);
+    }
+  grub_zfs_byteswap_u64(&bp->blk_prop);
+  grub_zfs_byteswap_u64(&bp->blk_pad[0]);
+  grub_zfs_byteswap_u64(&bp->blk_pad[1]);
+  grub_zfs_byteswap_u64(&bp->blk_phys_birth);
+  grub_zfs_byteswap_u64(&bp->blk_birth);
+  grub_zfs_byteswap_u64(&bp->blk_fill);
+
+  grub_zfs_byteswap_checksum(&bp->blk_cksum);
+}
+
+static void
+grub_zfs_byteswap_uberblock(uberblock_t *ub)
+{
+  grub_zfs_byteswap_u64(&ub->ub_magic);
+  grub_zfs_byteswap_u64(&ub->ub_version);
+  grub_zfs_byteswap_u64(&ub->ub_txg);
+  grub_zfs_byteswap_u64(&ub->ub_guid_sum);
+  grub_zfs_byteswap_u64(&ub->ub_timestamp);
+  grub_zfs_byteswap_blkptr(&ub->ub_rootbp);
+}
+
+static void
+grub_zfs_byteswap_type(void *buf, grub_size_t len, grub_uint8_t type);
+
+static void
+grub_zfs_byteswap_dnode_proper(dnode_phys_t *dn)
+{
+  grub_zfs_byteswap_u16(&dn->dn_datablkszsec);
+  grub_zfs_byteswap_u16(&dn->dn_bonuslen);
+  grub_zfs_byteswap_u64(&dn->dn_maxblkid);
+  grub_zfs_byteswap_u64(&dn->dn_used);
+  grub_zfs_byteswap_u64(&dn->dn_pad3[0]);
+  grub_zfs_byteswap_u64(&dn->dn_pad3[1]);
+  grub_zfs_byteswap_u64(&dn->dn_pad3[2]);
+  grub_zfs_byteswap_u64(&dn->dn_pad3[3]);
+
+  for (unsigned i = 0; i < dn->dn_nblkptr; i++)
+    grub_zfs_byteswap_blkptr(&dn->dn_blkptr[i]);
+
+  if (dn->dn_flags & DNODE_FLAG_SPILL_BLKPTR)
+    grub_zfs_byteswap_blkptr(&dn->dn_spill);
+}
+
+static void
+grub_zfs_byteswap_dnode(dnode_phys_t *dn)
+{
+  grub_zfs_byteswap_dnode_proper(dn);
+  grub_zfs_byteswap_type(dn->dn_bonus, dn->dn_bonuslen, dn->dn_bonustype);
+}
+
+static void
+grub_zfs_byteswap_objset(objset_phys_t *os)
+{
+  grub_zfs_byteswap_dnode(&os->os_meta_dnode);
+  /* We ignore ZIL header.  */
+  grub_zfs_byteswap_u64(&os->os_type);
+  grub_zfs_byteswap_u64(&os->os_flags);
+  grub_zfs_byteswap_dnode(&os->os_userused_dnode);
+  grub_zfs_byteswap_dnode(&os->os_groupused_dnode);
+}
+
+static void
+grub_zfs_byteswap_u64_array(grub_uint64_t *arr, grub_size_t len)
+{
+  for (unsigned i = 0; i < len / 8; i++)
+    grub_zfs_byteswap_u64(arr + i);
+}
+
+static void
+grub_zfs_byteswap_mzap(mzap_phys_t *mz, grub_size_t objsize)
+{
+  grub_zfs_byteswap_u64(&mz->mz_block_type);
+  grub_zfs_byteswap_u64(&mz->mz_salt);
+  for (unsigned i = 0; i < 6; i++)
+    grub_zfs_byteswap_u64(&mz->mz_pad[i]);
+  for (unsigned i = 0; i < objsize / MZAP_ENT_LEN - 1; i++)
+    {
+      grub_zfs_byteswap_u64(&mz->mz_chunk[i].mze_value);
+      grub_zfs_byteswap_u32(&mz->mz_chunk[i].mze_cd);
+      grub_zfs_byteswap_u16(&mz->mz_chunk[i].mze_pad);
+    }
+}
+
+static void
+grub_zfs_byteswap_zap_leaf(zap_leaf_phys_t *lf, grub_size_t len)
+{
+  int blksft = zfs_log2 (len);
+
+  grub_zfs_byteswap_u64(&lf->l_hdr.lh_block_type);
+  grub_zfs_byteswap_u64(&lf->l_hdr.lh_prefix);
+  grub_zfs_byteswap_u32(&lf->l_hdr.lh_magic);
+  grub_zfs_byteswap_u16(&lf->l_hdr.lh_nfree);
+  grub_zfs_byteswap_u16(&lf->l_hdr.lh_nentries);
+  grub_zfs_byteswap_u16(&lf->l_hdr.lh_prefix_len);
+  grub_zfs_byteswap_u16(&lf->l_hdr.lh_freelist);
+
+  for (int i = 0; i < ZAP_LEAF_HASH_NUMENTRIES(blksft); i++)
+    grub_zfs_byteswap_u16(&lf->l_hash[i]);
+
+  for (int i = 0; i < ZAP_LEAF_NUMCHUNKS (blksft); i++) {
+    zap_leaf_chunk_t *lc = ZAP_LEAF_CHUNK(lf, blksft, i);
+    struct zap_leaf_entry *le;
+
+    switch (lc->l_free.lf_type) {
+    case ZAP_CHUNK_ENTRY:
+      le = &lc->l_entry;
+
+      grub_zfs_byteswap_u16(&le->le_next);
+      grub_zfs_byteswap_u16(&le->le_name_chunk);
+      grub_zfs_byteswap_u16(&le->le_name_length);
+      grub_zfs_byteswap_u16(&le->le_value_chunk);
+      grub_zfs_byteswap_u16(&le->le_value_length);
+      grub_zfs_byteswap_u32(&le->le_cd);
+      grub_zfs_byteswap_u64(&le->le_hash);
+      break;
+    case ZAP_CHUNK_FREE:
+      grub_zfs_byteswap_u16(&lc->l_free.lf_next);
+      break;
+    case ZAP_CHUNK_ARRAY:
+      grub_zfs_byteswap_u16(&lc->l_array.la_next);
+      break;
+    default:
+      grub_dprintf("zfs", "Unknown leaf type %d\n", lc->l_free.lf_type);
+    }
+  }
+}
+
+static void
+grub_zfs_byteswap_zap(void *zp, grub_size_t len)
+{
+  grub_uint64_t block_type = *(grub_uint64_t *) zp;
+  if (block_type == grub_swap_bytes64_compile_time(ZBT_MICRO))
+    grub_zfs_byteswap_mzap(zp, len);
+  else if (block_type == grub_swap_bytes64_compile_time(ZBT_HEADER))
+    grub_zfs_byteswap_u64_array(zp, len);
+  else if (block_type == grub_swap_bytes64_compile_time(ZBT_LEAF))
+    grub_zfs_byteswap_zap_leaf(zp, len);
+  else
+    grub_dprintf ("zfs", "unknown ZAP type");
+}
+
+static const dmu_object_byteswap_t byteswap_table[] = {
+  DMU_BSWAP_UINT8,  DMU_BSWAP_ZAP,    DMU_BSWAP_UINT64, DMU_BSWAP_UINT8,
+  DMU_BSWAP_UINT64, DMU_BSWAP_UINT64, DMU_BSWAP_UINT64, DMU_BSWAP_UINT64,
+  DMU_BSWAP_UINT64, DMU_BSWAP_UINT64, DMU_BSWAP_DNODE,  DMU_BSWAP_OBJSET,
+  DMU_BSWAP_UINT64, DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,
+
+  DMU_BSWAP_UINT64, DMU_BSWAP_ZNODE,  DMU_BSWAP_OLDACL, DMU_BSWAP_UINT8,
+  DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,    DMU_BSWAP_UINT8,
+  DMU_BSWAP_ZAP,    DMU_BSWAP_UINT8,  DMU_BSWAP_UINT64, DMU_BSWAP_ZAP,
+  DMU_BSWAP_ZAP,    DMU_BSWAP_UINT8,  DMU_BSWAP_UINT64, DMU_BSWAP_ZAP,
+
+  DMU_BSWAP_ZAP,    DMU_BSWAP_ACL,    DMU_BSWAP_UINT8,  DMU_BSWAP_UINT8,
+  DMU_BSWAP_UINT64, DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,
+  DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,
+  DMU_BSWAP_UINT8,  DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,    DMU_BSWAP_ZAP,
+
+  DMU_BSWAP_ZAP,    DMU_BSWAP_UINT8,  DMU_BSWAP_ZAP,    DMU_BSWAP_UINT64,
+  DMU_BSWAP_ZAP,    DMU_BSWAP_UINT64,
+};
+
+static dmu_object_byteswap_t
+get_byteswap_type(grub_uint8_t type)
+{
+  if (type & 0x80)
+    return type & DMU_OT_BYTESWAP_MASK;
+
+  if (type < ARRAY_SIZE (byteswap_table))
+    return byteswap_table[type];
+  grub_fatal("Unknown type %d", type);
+}
+
+static void
+grub_zfs_byteswap_type(void *buf, grub_size_t len, grub_uint8_t type)
+{
+  /*
+	DMU_BSWAP_UINT16,
+	DMU_BSWAP_UINT32,
+	DMU_BSWAP_ZNODE,
+	DMU_BSWAP_OLDACL,
+	DMU_BSWAP_ACL,
+
+   */
+  switch (get_byteswap_type(type))
+    {
+    case DMU_BSWAP_UINT8:
+      break;
+    case DMU_BSWAP_DNODE:
+      for (unsigned i = 0; i < len / sizeof(dnode_phys_t); i++)
+	grub_zfs_byteswap_dnode((dnode_phys_t *)buf + i);
+      break;
+    case DMU_BSWAP_OBJSET:
+      for (unsigned i = 0; i < len / sizeof(objset_phys_t); i++)
+	grub_zfs_byteswap_objset((objset_phys_t *)buf + i);
+      break;
+    case DMU_BSWAP_ZAP:
+      grub_zfs_byteswap_zap(buf, len);
+      break;
+    case DMU_BSWAP_UINT64:
+      grub_zfs_byteswap_u64_array(buf, len);
+      break;
+    default:
+      grub_fatal("Unknown type %d", type);
+    }
+}
+
 /*
  * zio_checksum_verify: Provides support for checksum verification.
  *
@@ -482,8 +714,12 @@ zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
   else
     ci->ci_func (buf, size, endian, &actual_cksum);
 
-  if (grub_memcmp (&actual_cksum, &zc,
-		   checksum != ZIO_CHECKSUM_SHA256_MAC ? 32 : 20) != 0)
+  int cksumlen = checksum != ZIO_CHECKSUM_SHA256_MAC ? 32 : 20;
+
+  if (ci->ci_eck && !GRUB_ZFS_IS_NATIVE_BYTEORDER(endian))
+    grub_zfs_byteswap_checksum(&actual_cksum);
+
+  if (grub_memcmp (&actual_cksum, &zc, cksumlen) != 0)
     {
       grub_dprintf ("zfs", "checksum %s verification failed\n", ci->ci_name);
       grub_dprintf ("zfs", "actual checksum %016llx %016llx %016llx %016llx\n",
@@ -515,30 +751,14 @@ zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
 static int
 vdev_uberblock_compare (uberblock_t * ub1, uberblock_t * ub2)
 {
-  grub_zfs_endian_t ub1_endian, ub2_endian;
-  if (grub_zfs_to_cpu64 (ub1->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
-      == UBERBLOCK_MAGIC)
-    ub1_endian = GRUB_ZFS_LITTLE_ENDIAN;
-  else
-    ub1_endian = GRUB_ZFS_BIG_ENDIAN;
-  if (grub_zfs_to_cpu64 (ub2->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
-      == UBERBLOCK_MAGIC)
-    ub2_endian = GRUB_ZFS_LITTLE_ENDIAN;
-  else
-    ub2_endian = GRUB_ZFS_BIG_ENDIAN;
-
-  if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian)
-      < grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian))
+  if (ub1->ub_txg < ub2->ub_txg)
     return -1;
-  if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian)
-      > grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian))
+  if (ub1->ub_txg > ub2->ub_txg)
     return 1;
 
-  if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian)
-      < grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian))
+  if (ub1->ub_timestamp < ub2->ub_timestamp)
     return -1;
-  if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian)
-      > grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian))
+  if (ub1->ub_timestamp > ub2->ub_timestamp)
     return 1;
 
   return 0;
@@ -615,34 +835,27 @@ find_bestub (uberblock_phys_t * ub_array,
 	  grub_errno = GRUB_ERR_NONE;
 	  continue;
 	}
+
+      if (ubptr->ubp_uberblock.ub_magic == grub_swap_bytes64_compile_time(UBERBLOCK_MAGIC))
+	grub_zfs_byteswap_uberblock(&ubptr->ubp_uberblock);
+
       if (ubbest == NULL
 	  || vdev_uberblock_compare (&(ubptr->ubp_uberblock),
 				     &(ubbest->ubp_uberblock)) > 0)
 	ubbest = ubptr;
     }
-  if (!ubbest)
+  if (!ubbest) {
     grub_errno = err;
+    grub_dprintf("zfs", "no valid ub found. vdev_pys_sector=%lld\n",
+		 (long long)desc->vdev_phys_sector);
+  } else {
+    grub_dprintf("zfs", "valid ub found. vdev_pys_sector=%lld\n",
+		 (long long)desc->vdev_phys_sector);
+  }
 
   return ubbest;
 }
 
-static inline grub_size_t
-get_psize (blkptr_t * bp, grub_zfs_endian_t endian)
-{
-  return ((((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) >> 16) & 0xffff) + 1)
-	  << SPA_MINBLOCKSHIFT);
-}
-
-static grub_uint64_t
-dva_get_offset (const dva_t *dva, grub_zfs_endian_t endian)
-{
-  grub_dprintf ("zfs", "dva=%llx, %llx\n",
-		(unsigned long long) dva->dva_word[0],
-		(unsigned long long) dva->dva_word[1]);
-  return grub_zfs_to_cpu64 ((dva)->dva_word[1],
-			    endian) << SPA_MINBLOCKSHIFT;
-}
-
 static grub_err_t
 zfs_fetch_nvlist (struct grub_zfs_device_desc *diskdesc, char **nvlist)
 {
@@ -1054,7 +1267,7 @@ check_pool_label (struct grub_zfs_data *data,
 			 "bad vdev_phys_t.vp_zbt.zec_magic number");
     }
   /* Now check the integrity of the vdev_phys_t structure though checksum.  */
-  ZIO_SET_CHECKSUM(&emptycksum, diskdesc->vdev_phys_sector << 9, 0, 0, 0);
+  ZIO_SET_CHECKSUM(&emptycksum, grub_cpu_to_zfs64(diskdesc->vdev_phys_sector << 9, endian), 0, 0, 0);
   err = zio_checksum_verify (emptycksum, ZIO_CHECKSUM_LABEL, endian,
 			     nvlist, VDEV_PHYS_SIZE);
   if (err) {
@@ -1743,14 +1956,14 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
 
 static grub_err_t
 read_dva (const dva_t *dva,
-	  grub_zfs_endian_t endian, struct grub_zfs_data *data,
+	  struct grub_zfs_data *data,
 	  void *buf, grub_size_t len)
 {
   grub_uint64_t offset;
   unsigned i;
   grub_err_t err = 0;
   int try = 0;
-  offset = dva_get_offset (dva, endian);
+  offset = DVA_GET_OFFSET(dva);
 
   for (try = 0; try < 2; try++)
     {
@@ -1780,7 +1993,7 @@ read_dva (const dva_t *dva,
  *
  */
 static grub_err_t
-zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf,
+zio_read_gang (const blkptr_t * bp, const dva_t * dva, void *buf,
 	       struct grub_zfs_data *data)
 {
   zio_gbh_phys_t *zio_gb;
@@ -1793,10 +2006,8 @@ zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf,
   zio_gb = grub_malloc (SPA_GANGBLOCKSIZE);
   if (!zio_gb)
     return grub_errno;
-  grub_dprintf ("zfs", endian == GRUB_ZFS_LITTLE_ENDIAN ? "little-endian gang\n"
-		:"big-endian gang\n");
 
-  err = read_dva (dva, endian, data, zio_gb, SPA_GANGBLOCKSIZE);
+  err = read_dva (dva, data, zio_gb, SPA_GANGBLOCKSIZE);
   if (err)
     {
       grub_free (zio_gb);
@@ -1806,8 +2017,8 @@ zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf,
   /* XXX */
   /* self checksuming the gang block header */
   ZIO_SET_CHECKSUM (&zc, DVA_GET_VDEV (dva),
-		    dva_get_offset (dva, endian), bp->blk_birth, 0);
-  err = zio_checksum_verify (zc, ZIO_CHECKSUM_GANG_HEADER, endian,
+		    DVA_GET_OFFSET (dva), bp->blk_birth, 0);
+  err = zio_checksum_verify (zc, ZIO_CHECKSUM_GANG_HEADER, BP_GET_BYTEORDER(bp),
 			     (char *) zio_gb, SPA_GANGBLOCKSIZE);
   if (err)
     {
@@ -1815,20 +2026,22 @@ zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf,
       return err;
     }
 
-  endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
+  if (!GRUB_ZFS_IS_NATIVE_BYTEORDER(BP_GET_BYTEORDER(bp)))
+    for (i = 0; i < SPA_GBH_NBLKPTRS; i++)
+      grub_zfs_byteswap_blkptr(&zio_gb->zg_blkptr[i]);
 
   for (i = 0; i < SPA_GBH_NBLKPTRS; i++)
     {
       if (BP_IS_HOLE(&zio_gb->zg_blkptr[i]))
 	continue;
 
-      err = zio_read_data (&zio_gb->zg_blkptr[i], endian, buf, data);
+      err = zio_read_data (&zio_gb->zg_blkptr[i], buf, data);
       if (err)
 	{
 	  grub_free (zio_gb);
 	  return err;
 	}
-      buf = (char *) buf + get_psize (&zio_gb->zg_blkptr[i], endian);
+      buf = (char *) buf + BP_GET_PSIZE(&zio_gb->zg_blkptr[i]);
     }
   grub_free (zio_gb);
   return GRUB_ERR_NONE;
@@ -1838,13 +2051,12 @@ zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf,
  * Read in a block of raw data to buf.
  */
 static grub_err_t
-zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf,
-	       struct grub_zfs_data *data)
+zio_read_data (const blkptr_t * bp, void *buf, struct grub_zfs_data *data)
 {
   int i, psize;
   grub_err_t err = GRUB_ERR_NONE;
 
-  psize = get_psize (bp, endian);
+  psize = BP_GET_PSIZE (bp);
 
   /* pick a good dva from the block pointer */
   for (i = 0; i < SPA_DVAS_PER_BP; i++)
@@ -1852,10 +2064,10 @@ zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf,
       if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0)
 	continue;
 
-      if ((grub_zfs_to_cpu64 (bp->blk_dva[i].dva_word[1], endian)>>63) & 1)
-	err = zio_read_gang (bp, endian, &bp->blk_dva[i], buf, data);
+      if (DVA_GET_GANG(&bp->blk_dva[i]))
+	err = zio_read_gang (bp, &bp->blk_dva[i], buf, data);
       else
-	err = read_dva (&bp->blk_dva[i], endian, data, buf, psize);
+	err = read_dva (&bp->blk_dva[i], data, buf, psize);
       if (!err)
 	return GRUB_ERR_NONE;
       grub_errno = GRUB_ERR_NONE;
@@ -1906,7 +2118,7 @@ decode_embedded_bp_compressed(const blkptr_t *bp, void *buf)
  * and put the uncompressed data in buf.
  */
 static grub_err_t
-zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
+zio_read (const blkptr_t *bp, void **buf,
 	  grub_size_t *size, struct grub_zfs_data *data)
 {
   grub_size_t lsize, psize;
@@ -1918,9 +2130,9 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
 
   *buf = NULL;
 
-  checksum = (grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff;
-  comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0x7f;
-  encrypted = ((grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 60) & 3);
+  checksum = BP_GET_CHECKSUM(bp);
+  comp = BP_GET_COMPRESS(bp);
+  encrypted = BP_GET_PROP_BIT_61(bp);
   if (BP_IS_EMBEDDED(bp))
     {
       if (BPE_GET_ETYPE(bp) != BP_EMBEDDED_TYPE_DATA)
@@ -1929,14 +2141,12 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
 			   PRIuGRUB_UINT64_T ")\n",
 			   BPE_GET_ETYPE (bp));
       lsize = BPE_GET_LSIZE(bp);
-      psize = BF64_GET_SB(grub_zfs_to_cpu64 ((bp)->blk_prop, endian), 25, 7, 0, 1);
+      psize = BPE_GET_PSIZE(bp);
     }
   else
     {
-      lsize = (BP_IS_HOLE(bp) ? 0 :
-	       (((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) & 0xffff) + 1)
-	        << SPA_MINBLOCKSHIFT));
-      psize = get_psize (bp, endian);
+      lsize = (BP_IS_HOLE(bp) ? 0 : BP_GET_LSIZE(bp));
+      psize = BP_GET_PSIZE(bp);
     }
   grub_dprintf("zfs", "zio_read: E %d: size %" PRIdGRUB_SSIZE "/%"
 	       PRIdGRUB_SSIZE "\n", (int)BP_IS_EMBEDDED(bp), lsize, psize);
@@ -1960,12 +2170,11 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
   if (! compbuf)
     return grub_errno;
 
-  grub_dprintf ("zfs", "endian = %d\n", endian);
   if (BP_IS_EMBEDDED(bp))
     err = decode_embedded_bp_compressed(bp, compbuf);
   else
     {
-      err = zio_read_data (bp, endian, compbuf, data);
+      err = zio_read_data (bp, compbuf, data);
       /* FIXME is it really necessary? */
       if (comp != ZIO_COMPRESS_OFF)
 	grub_memset (compbuf + psize, 0, ALIGN_UP (psize, 16) - psize);
@@ -1979,7 +2188,7 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
 
   if (!BP_IS_EMBEDDED(bp))
     {
-      err = zio_checksum_verify (zc, checksum, endian,
+      err = zio_checksum_verify (zc, checksum, BP_GET_BYTEORDER(bp),
 			         compbuf, psize);
       if (err)
         {
@@ -2001,8 +2210,7 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
 	  unsigned i, besti = 0;
 	  grub_uint64_t bestval = 0;
 	  for (i = 0; i < data->subvol.nkeys; i++)
-	    if (data->subvol.keyring[i].txg <= grub_zfs_to_cpu64 (bp->blk_birth,
-								  endian)
+	    if (data->subvol.keyring[i].txg <= bp->blk_birth
 		&& data->subvol.keyring[i].txg > bestval)
 	      {
 		besti = i;
@@ -2013,21 +2221,19 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
 	      grub_free (compbuf);
 	      *buf = NULL;
 	      grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
-			    grub_zfs_to_cpu64 (bp->blk_birth,
-					       endian));
+			    bp->blk_birth);
 	      return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
 	    }
 	  grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T
 			", %p) for txg %" PRIxGRUB_UINT64_T "\n",
 			besti, data->subvol.keyring[besti].txg,
 			data->subvol.keyring[besti].cipher,
-			grub_zfs_to_cpu64 (bp->blk_birth,
-					   endian));
+			bp->blk_birth);
 	  err = grub_zfs_decrypt (data->subvol.keyring[besti].cipher,
 				  data->subvol.keyring[besti].algo,
-				  &(bp)->blk_dva[encrypted],
+				  &(bp)->blk_dva[2],
 				  compbuf, psize, zc.zc_mac,
-				  endian);
+				  BP_GET_BYTEORDER(bp));
 	}
       if (err)
 	{
@@ -2056,6 +2262,17 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
 	}
     }
 
+  if (!GRUB_ZFS_IS_NATIVE_BYTEORDER(BP_GET_BYTEORDER(bp)))
+    {
+      if (BP_GET_LEVEL(bp) > 0)
+	{
+	  for (unsigned i = 0; i < lsize / sizeof(blkptr_t); i++)
+	    grub_zfs_byteswap_blkptr((blkptr_t *)*buf + i);
+	}
+      else
+	grub_zfs_byteswap_type(*buf, lsize, BP_GET_TYPE(bp));
+    }
+
   return GRUB_ERR_NONE;
 }
 
@@ -2065,29 +2282,28 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
  *
  */
 static grub_err_t
-dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf,
-	  grub_zfs_endian_t *endian_out, struct grub_zfs_data *data)
+dmu_read (dnode_phys_t * dn, grub_uint64_t blkid, void **buf,
+	  struct grub_zfs_data *data)
 {
   int level;
   grub_off_t idx;
-  blkptr_t *bp_array = dn->dn.dn_blkptr;
-  int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT;
+  blkptr_t *bp_array = dn->dn_blkptr;
+  int epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
   blkptr_t *bp;
   void *tmpbuf = 0;
-  grub_zfs_endian_t endian;
   grub_err_t err = GRUB_ERR_NONE;
 
   bp = grub_malloc (sizeof (blkptr_t));
   if (!bp)
     return grub_errno;
 
-  endian = dn->endian;
-  for (level = dn->dn.dn_nlevels - 1; level >= 0; level--)
+  *buf = NULL;
+
+  for (level = dn->dn_nlevels - 1; level >= 0; level--)
     {
-      grub_dprintf ("zfs", "endian = %d\n", endian);
       idx = (blkid >> (epbs * level)) & ((1 << epbs) - 1);
       *bp = bp_array[idx];
-      if (bp_array != dn->dn.dn_blkptr)
+      if (bp_array != dn->dn_blkptr)
 	{
 	  grub_free (bp_array);
 	  bp_array = 0;
@@ -2095,8 +2311,7 @@ dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf,
 
       if (BP_IS_HOLE (bp))
 	{
-	  grub_size_t size = grub_zfs_to_cpu16 (dn->dn.dn_datablkszsec,
-						dn->endian)
+	  grub_size_t size = dn->dn_datablkszsec
 	    << SPA_MINBLOCKSHIFT;
 	  *buf = grub_malloc (size);
 	  if (!*buf)
@@ -2105,27 +2320,20 @@ dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf,
 	      break;
 	    }
 	  grub_memset (*buf, 0, size);
-	  endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
 	  break;
 	}
       if (level == 0)
 	{
-	  grub_dprintf ("zfs", "endian = %d\n", endian);
-	  err = zio_read (bp, endian, buf, 0, data);
-	  endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
+	  err = zio_read (bp, buf, 0, data);
 	  break;
 	}
-      grub_dprintf ("zfs", "endian = %d\n", endian);
-      err = zio_read (bp, endian, &tmpbuf, 0, data);
-      endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
+      err = zio_read (bp, &tmpbuf, 0, data);
       if (err)
 	break;
       bp_array = tmpbuf;
     }
-  if (bp_array != dn->dn.dn_blkptr)
+  if (bp_array != dn->dn_blkptr)
     grub_free (bp_array);
-  if (endian_out)
-    *endian_out = endian;
 
   grub_free (bp);
   return err;
@@ -2136,7 +2344,7 @@ dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf,
  * in "value".
  */
 static grub_err_t
-mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian,
+mzap_lookup (mzap_phys_t * zapobj,
 	     grub_uint32_t objsize, const char *name, grub_uint64_t * value,
 	     int case_insensitive)
 {
@@ -2151,7 +2359,7 @@ mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian,
       if (case_insensitive ? (grub_strcasecmp (mzap_ent[i].mze_name, name) == 0)
 	  : (grub_strcmp (mzap_ent[i].mze_name, name) == 0))
 	{
-	  *value = grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian);
+	  *value = mzap_ent[i].mze_value;
 	  return GRUB_ERR_NONE;
 	}
     }
@@ -2160,7 +2368,7 @@ mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian,
 }
 
 static int
-mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize,
+mzap_iterate (mzap_phys_t * zapobj, int objsize,
 	      int (*hook) (const char *name, grub_uint64_t val,
 			   struct grub_zfs_dir_ctx *ctx),
 	      struct grub_zfs_dir_ctx *ctx)
@@ -2174,8 +2382,7 @@ mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize,
       grub_dprintf ("zfs", "zap: name = %s, value = %llx, cd = %x\n",
 		    mzap_ent[i].mze_name, (long long)mzap_ent[i].mze_value,
 		    (int)mzap_ent[i].mze_cd);
-      if (hook (mzap_ent[i].mze_name,
-		grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian), ctx))
+      if (hook (mzap_ent[i].mze_name, mzap_ent[i].mze_value, ctx))
 	return 1;
     }
 
@@ -2250,7 +2457,7 @@ name_cmp (const char *s1, const char *s2, grub_size_t n,
 
 /* XXX */
 static int
-zap_leaf_array_equal (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
+zap_leaf_array_equal (zap_leaf_phys_t * l,
 		      int blksft, int chunk, grub_size_t array_len,
 		      const char *buf, int case_insensitive)
 {
@@ -2270,7 +2477,7 @@ zap_leaf_array_equal (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
       if (name_cmp ((char *) la->la_array, buf + bseen, toread,
 		    case_insensitive) != 0)
 	break;
-      chunk = grub_zfs_to_cpu16 (la->la_next, endian);
+      chunk = la->la_next;
       bseen += toread;
     }
   return (bseen == array_len);
@@ -2278,7 +2485,7 @@ zap_leaf_array_equal (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
 
 /* XXX */
 static grub_err_t
-zap_leaf_array_get (zap_leaf_phys_t * l, grub_zfs_endian_t endian, int blksft,
+zap_leaf_array_get (zap_leaf_phys_t * l, int blksft,
 		    int chunk, grub_size_t array_len, char *buf)
 {
   grub_size_t bseen = 0;
@@ -2297,7 +2504,7 @@ zap_leaf_array_get (zap_leaf_phys_t * l, grub_zfs_endian_t endian, int blksft,
 
       la = &ZAP_LEAF_CHUNK (l, blksft, chunk)->l_array;
       grub_memcpy (buf + bseen,la->la_array,  toread);
-      chunk = grub_zfs_to_cpu16 (la->la_next, endian);
+      chunk = la->la_next;
       bseen += toread;
     }
   return GRUB_ERR_NONE;
@@ -2311,7 +2518,7 @@ zap_leaf_array_get (zap_leaf_phys_t * l, grub_zfs_endian_t endian, int blksft,
  */
 /* XXX */
 static grub_err_t
-zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
+zap_leaf_lookup (zap_leaf_phys_t * l,
 		 int blksft, grub_uint64_t h,
 		 const char *name, grub_uint64_t * value,
 		 int case_insensitive)
@@ -2320,13 +2527,13 @@ zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
   struct zap_leaf_entry *le;
 
   /* Verify if this is a valid leaf block */
-  if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF)
+  if (l->l_hdr.lh_block_type != ZBT_LEAF)
     return grub_error (GRUB_ERR_BAD_FS, "invalid leaf type");
-  if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC)
+  if (l->l_hdr.lh_magic != ZAP_LEAF_MAGIC)
     return grub_error (GRUB_ERR_BAD_FS, "invalid leaf magic");
 
-  for (chunk = grub_zfs_to_cpu16 (l->l_hash[LEAF_HASH (blksft, h, l)], endian);
-       chunk != CHAIN_END; chunk = grub_zfs_to_cpu16 (le->le_next, endian))
+  for (chunk = l->l_hash[LEAF_HASH (blksft, h, l)];
+       chunk != CHAIN_END; chunk = le->le_next)
     {
 
       if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft))
@@ -2338,20 +2545,19 @@ zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
       if (le->le_type != ZAP_CHUNK_ENTRY)
 	return grub_error (GRUB_ERR_BAD_FS, "invalid chunk entry");
 
-      if (grub_zfs_to_cpu64 (le->le_hash,endian) != h)
+      if (le->le_hash != h)
 	continue;
 
       grub_dprintf ("zfs", "fzap: length %d\n", (int) le->le_name_length);
 
-      if (zap_leaf_array_equal (l, endian, blksft,
-				grub_zfs_to_cpu16 (le->le_name_chunk,endian),
-				grub_zfs_to_cpu16 (le->le_name_length, endian),
+      if (zap_leaf_array_equal (l, blksft,
+				le->le_name_chunk,
+				le->le_name_length,
 				name, case_insensitive))
 	{
 	  struct zap_leaf_array *la;
 
-	  if (le->le_int_size != 8 || grub_zfs_to_cpu16 (le->le_value_length,
-							 endian) != 1)
+	  if (le->le_int_size != 8 || le->le_value_length != 1)
 	    return grub_error (GRUB_ERR_BAD_FS, "invalid leaf chunk entry");
 
 	  /* get the uint64_t property value */
@@ -2369,9 +2575,9 @@ zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
 
 /* Verify if this is a fat zap header block */
 static grub_err_t
-zap_verify (zap_phys_t *zap, grub_zfs_endian_t endian)
+zap_verify (zap_phys_t *zap)
 {
-  if (grub_zfs_to_cpu64 (zap->zap_magic, endian) != (grub_uint64_t) ZAP_MAGIC)
+  if (zap->zap_magic != (grub_uint64_t) ZAP_MAGIC)
     return grub_error (GRUB_ERR_BAD_FS, "bad ZAP magic");
 
   if (zap->zap_salt == 0)
@@ -2386,18 +2592,16 @@ zap_verify (zap_phys_t *zap, grub_zfs_endian_t endian)
  */
 /* XXX */
 static grub_err_t
-fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap,
+fzap_lookup (dnode_phys_t * zap_dnode, zap_phys_t * zap,
 	     const char *name, grub_uint64_t * value,
 	     struct grub_zfs_data *data, int case_insensitive)
 {
   void *l;
   grub_uint64_t hash, idx, blkid;
-  int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec,
-					    zap_dnode->endian) << DNODE_SHIFT);
+  int blksft = zfs_log2 (zap_dnode->dn_datablkszsec) + DNODE_SHIFT;
   grub_err_t err;
-  grub_zfs_endian_t leafendian;
 
-  err = zap_verify (zap, zap_dnode->endian);
+  err = zap_verify (zap);
   if (err)
     return err;
 
@@ -2408,16 +2612,16 @@ fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap,
     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 		       "external pointer tables not supported");
   idx = ZAP_HASH_IDX (hash, zap->zap_ptrtbl.zt_shift);
-  blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], zap_dnode->endian);
+  blkid = ((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))];
 
   /* Get the leaf block */
   if ((1U << blksft) < sizeof (zap_leaf_phys_t))
     return grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small");
-  err = dmu_read (zap_dnode, blkid, &l, &leafendian, data);
+  err = dmu_read (zap_dnode, blkid, &l, data);
   if (err)
     return err;
 
-  err = zap_leaf_lookup (l, leafendian, blksft, hash, name, value,
+  err = zap_leaf_lookup (l, blksft, hash, name, value,
 			 case_insensitive);
   grub_free (l);
   return err;
@@ -2425,7 +2629,7 @@ fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap,
 
 /* XXX */
 static int
-fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
+fzap_iterate (dnode_phys_t * zap_dnode, zap_phys_t * zap,
 	      grub_size_t name_elem_length,
 	      int (*hook) (const void *name, grub_size_t name_length,
 			   const void *val_in,
@@ -2437,13 +2641,11 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
   void *l_in;
   grub_uint64_t idx, idx2, blkid;
   grub_uint16_t chunk;
-  int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec,
-					    zap_dnode->endian) << DNODE_SHIFT);
+  int blksft = zfs_log2 (zap_dnode->dn_datablkszsec) + DNODE_SHIFT;
   grub_err_t err;
-  grub_zfs_endian_t endian;
   grub_size_t sz;
 
-  if (zap_verify (zap, zap_dnode->endian))
+  if (zap_verify (zap))
     return 0;
 
   /* get block id from index */
@@ -2461,17 +2663,15 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
     }
   for (idx = 0; idx < (1ULL << zap->zap_ptrtbl.zt_shift); idx++)
     {
-      blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))],
-				 zap_dnode->endian);
+      blkid = ((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))];
 
       for (idx2 = 0; idx2 < idx; idx2++)
-	if (blkid == grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))],
-					zap_dnode->endian))
+	if (blkid == ((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))])
 	  break;
       if (idx2 != idx)
 	continue;
 
-      err = dmu_read (zap_dnode, blkid, &l_in, &endian, data);
+      err = dmu_read (zap_dnode, blkid, &l_in, data);
       l = l_in;
       if (err)
 	{
@@ -2480,12 +2680,12 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
 	}
 
       /* Verify if this is a valid leaf block */
-      if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF)
+      if (l->l_hdr.lh_block_type != ZBT_LEAF)
 	{
 	  grub_free (l);
 	  continue;
 	}
-      if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC)
+      if (l->l_hdr.lh_magic != ZAP_LEAF_MAGIC)
 	{
 	  grub_free (l);
 	  continue;
@@ -2503,7 +2703,7 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
 	  if (le->le_type != ZAP_CHUNK_ENTRY)
 	    continue;
 
-	  if (grub_mul (grub_zfs_to_cpu16 (le->le_name_length, endian), name_elem_length, &sz) ||
+	  if (grub_mul (le->le_name_length, name_elem_length, &sz) ||
 	      grub_add (sz, 1, &sz))
 	    {
 	      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("buffer size overflow"));
@@ -2516,11 +2716,9 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
 	      grub_free (l);
 	      return grub_errno;
 	    }
-	  if (zap_leaf_array_get (l, endian, blksft,
-				  grub_zfs_to_cpu16 (le->le_name_chunk,
-						     endian),
-				  grub_zfs_to_cpu16 (le->le_name_length,
-						     endian)
+	  if (zap_leaf_array_get (l, blksft,
+				  le->le_name_chunk,
+				  le->le_name_length
 				  * name_elem_length, buf))
 	    {
 	      grub_free (buf);
@@ -2530,16 +2728,15 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
 
 	  val_length = ((int) le->le_value_length
 			* (int) le->le_int_size);
-	  val = grub_malloc (grub_zfs_to_cpu16 (val_length, endian));
+	  val = grub_malloc (val_length);
 	  if (!val)
 	    {
 	      grub_free (l);
 	      grub_free (buf);
 	      return grub_errno;
 	    }
-	  if (zap_leaf_array_get (l, endian, blksft,
-				  grub_zfs_to_cpu16 (le->le_value_chunk,
-						     endian),
+	  if (zap_leaf_array_get (l, blksft,
+				  le->le_value_chunk,
 				  val_length, val))
 	    {
 	      grub_free (buf);
@@ -2567,32 +2764,29 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
  *
  */
 static grub_err_t
-zap_lookup (dnode_end_t * zap_dnode, const char *name, grub_uint64_t *val,
+zap_lookup (dnode_phys_t * zap_dnode, const char *name, grub_uint64_t *val,
 	    struct grub_zfs_data *data, int case_insensitive)
 {
   grub_uint64_t block_type;
   grub_uint32_t size;
   void *zapbuf;
   grub_err_t err;
-  grub_zfs_endian_t endian;
 
   grub_dprintf ("zfs", "looking for '%s'\n", name);
 
   /* Read in the first block of the zap object data. */
-  size = (grub_uint32_t) grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec,
-			    zap_dnode->endian) << SPA_MINBLOCKSHIFT;
-  err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
+  size = (grub_uint32_t) zap_dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+  err = dmu_read (zap_dnode, 0, &zapbuf, data);
   if (err)
     return err;
-  block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
+  block_type = *((grub_uint64_t *) zapbuf);
 
   grub_dprintf ("zfs", "zap read\n");
 
   if (block_type == ZBT_MICRO)
     {
       grub_dprintf ("zfs", "micro zap\n");
-      err = mzap_lookup (zapbuf, endian, size, name, val,
-			 case_insensitive);
+      err = mzap_lookup (zapbuf, size, name, val, case_insensitive);
       grub_dprintf ("zfs", "returned %d\n", err);
       grub_free (zapbuf);
       return err;
@@ -2637,7 +2831,7 @@ zap_iterate_u64_transform (const void *name,
 }
 
 static int
-zap_iterate_u64 (dnode_end_t * zap_dnode,
+zap_iterate_u64 (dnode_phys_t * zap_dnode,
 		 int (*hook) (const char *name, grub_uint64_t val,
 			      struct grub_zfs_dir_ctx *ctx),
 		 struct grub_zfs_data *data, struct grub_zfs_dir_ctx *ctx)
@@ -2647,21 +2841,20 @@ zap_iterate_u64 (dnode_end_t * zap_dnode,
   void *zapbuf;
   grub_err_t err;
   int ret;
-  grub_zfs_endian_t endian;
 
   /* Read in the first block of the zap object data. */
-  size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT;
-  err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
+  size = zap_dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+  err = dmu_read (zap_dnode, 0, &zapbuf, data);
   if (err)
     return 0;
-  block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
+  block_type = *((grub_uint64_t *) zapbuf);
 
   grub_dprintf ("zfs", "zap iterate\n");
 
   if (block_type == ZBT_MICRO)
     {
       grub_dprintf ("zfs", "micro zap\n");
-      ret = mzap_iterate (zapbuf, endian, size, hook, ctx);
+      ret = mzap_iterate (zapbuf, size, hook, ctx);
       grub_free (zapbuf);
       return ret;
     }
@@ -2685,7 +2878,7 @@ zap_iterate_u64 (dnode_end_t * zap_dnode,
 }
 
 static int
-zap_iterate (dnode_end_t * zap_dnode,
+zap_iterate (dnode_phys_t * zap_dnode,
 	     grub_size_t nameelemlen,
 	     int (*hook) (const void *name, grub_size_t namelen,
 			  const void *val_in,
@@ -2697,13 +2890,12 @@ zap_iterate (dnode_end_t * zap_dnode,
   void *zapbuf;
   grub_err_t err;
   int ret;
-  grub_zfs_endian_t endian;
 
   /* Read in the first block of the zap object data. */
-  err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
+  err = dmu_read (zap_dnode, 0, &zapbuf, data);
   if (err)
     return 0;
-  block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
+  block_type = *((grub_uint64_t *) zapbuf);
 
   grub_dprintf ("zfs", "zap iterate\n");
 
@@ -2737,20 +2929,18 @@ zap_iterate (dnode_end_t * zap_dnode,
  *	buf - data buffer that holds the returning dnode
  */
 static grub_err_t
-dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
-	   dnode_end_t * buf, struct grub_zfs_data *data)
+dnode_get (dnode_phys_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
+	   dnode_phys_t * buf, struct grub_zfs_data *data)
 {
   grub_uint64_t blkid, blksz;	/* the block id this object dnode is in */
   int epbs;			/* shift of number of dnodes in a block */
   int idx;			/* index within a block */
   void *dnbuf;
   grub_err_t err;
-  grub_zfs_endian_t endian;
 
   objnum &= DNODE_NUM_MASK;
 
-  blksz = grub_zfs_to_cpu16 (mdn->dn.dn_datablkszsec,
-			     mdn->endian) << SPA_MINBLOCKSHIFT;
+  blksz = mdn->dn_datablkszsec << SPA_MINBLOCKSHIFT;
   epbs = zfs_log2 (blksz) - DNODE_SHIFT;
 
   /* While this should never happen, we should check that epbs is not negative. */
@@ -2760,22 +2950,23 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
   blkid = objnum >> epbs;
   idx = objnum & ((1 << epbs) - 1);
 
-  if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, &mdn->dn,
-					      sizeof (mdn->dn)) == 0
+  if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, mdn,
+					      sizeof (*mdn)) == 0
       && objnum >= data->dnode_start && objnum < data->dnode_end)
     {
-      grub_memmove (&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE);
-      buf->endian = data->dnode_endian;
-      if (type && buf->dn.dn_type != type)
+      *buf = (data->dnode_buf)[idx];
+      if (type && buf->dn_type != type)
 	return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type");
       return GRUB_ERR_NONE;
     }
 
-  grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian,
+  grub_dprintf ("zfs", "blkid=%llx\n",
 		(unsigned long long) blkid);
-  err = dmu_read (mdn, blkid, &dnbuf, &endian, data);
+  err = dmu_read (mdn, blkid, &dnbuf, data);
   if (err)
     return err;
+  if (dnbuf == NULL)
+    return grub_error(GRUB_ERR_BAD_FS, "invalid or unavailable dnode");
   grub_dprintf ("zfs", "alive\n");
 
   grub_free (data->dnode_buf);
@@ -2792,15 +2983,10 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
       data->dnode_buf = dnbuf;
       data->dnode_start = blkid << epbs;
       data->dnode_end = (blkid + 1) << epbs;
-      data->dnode_endian = endian;
     }
 
-  grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE);
-  if (data->dnode_buf == 0)
-	  /* dnbuf not used anymore if data->dnode_mdn malloc failed */
-	  grub_free (dnbuf);
-  buf->endian = endian;
-  if (type && buf->dn.dn_type != type)
+  *buf = ((dnode_phys_t *) dnbuf)[idx];
+  if (type && buf->dn_type != type)
     return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type");
 
   return GRUB_ERR_NONE;
@@ -2815,7 +3001,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
  *
  */
 static grub_err_t
-dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
+dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_phys_t *dn,
 		struct grub_zfs_data *data)
 {
   grub_uint64_t objnum, version;
@@ -2825,7 +3011,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
   struct dnode_chain
   {
     struct dnode_chain *next;
-    dnode_end_t dn;
+    dnode_phys_t dn;
   };
   struct dnode_chain *dnode_path = 0, *dn_new, *root;
 
@@ -2922,7 +3108,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
       ch = *path;
       *path = 0;		/* ensure null termination */
 
-      if (dnode_path->dn.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS)
+      if (dnode_path->dn.dn_type != DMU_OT_DIRECTORY_CONTENTS)
 	{
 	  err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
 	  break;
@@ -2947,25 +3133,23 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
 	break;
 
       *path = ch;
-      if (dnode_path->dn.dn.dn_bonustype == DMU_OT_ZNODE
-	  && ((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa)
+      if (dnode_path->dn.dn_bonustype == DMU_OT_ZNODE
+	  && (((((znode_phys_t *) DN_BONUS (&dnode_path->dn))->zp_mode) >> 12) & 0xf) == 0xa)
 	{
 	  char *sym_value;
 	  grub_size_t sz;
 	  grub_size_t sym_sz;
 	  int free_symval = 0;
 	  char *oldpath = path, *oldpathbuf = path_buf;
-	  sym_value = ((char *) DN_BONUS (&dnode_path->dn.dn) + sizeof (struct znode_phys));
+	  sym_value = ((char *) DN_BONUS (&dnode_path->dn) + sizeof (struct znode_phys));
 
-	  sym_sz = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_size, dnode_path->dn.endian);
+	  sym_sz = ((znode_phys_t *) DN_BONUS (&dnode_path->dn))->zp_size;
 
-	  if (dnode_path->dn.dn.dn_flags & 1)
+	  if (dnode_path->dn.dn_flags & 1)
 	    {
 	      grub_size_t block;
 	      grub_size_t blksz;
-	      blksz = (grub_zfs_to_cpu16 (dnode_path->dn.dn.dn_datablkszsec,
-					  dnode_path->dn.endian)
-		       << SPA_MINBLOCKSHIFT);
+	      blksz = dnode_path->dn.dn_datablkszsec << SPA_MINBLOCKSHIFT;
 
 	      if (blksz == 0)
                 {
@@ -2985,7 +3169,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
 		  void *t;
 		  grub_size_t movesize;
 
-		  err = dmu_read (&(dnode_path->dn), block, &t, 0, data);
+		  err = dmu_read (&(dnode_path->dn), block, &t, data);
 		  if (err)
 		    {
 		      grub_free (sym_value);
@@ -3047,22 +3231,22 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
 		 }
 	  dn_new = dnode_path;
 	}
-      if (dnode_path->dn.dn.dn_bonustype == DMU_OT_SA)
+      if (dnode_path->dn.dn_bonustype == DMU_OT_SA)
 	{
 	  void *sahdrp;
 	  int hdrsize;
 	  grub_size_t sz;
 	  bool free_sahdrp = false;
 
-	  if (dnode_path->dn.dn.dn_bonuslen != 0)
+	  if (dnode_path->dn.dn_bonuslen != 0)
 	    {
-	      sahdrp = DN_BONUS (&dnode_path->dn.dn);
+	      sahdrp = DN_BONUS (&dnode_path->dn);
 	    }
-	  else if (dnode_path->dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
+	  else if (dnode_path->dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
 	    {
-	      blkptr_t *bp = &dnode_path->dn.dn.dn_spill;
+	      blkptr_t *bp = &dnode_path->dn.dn_spill;
 
-	      err = zio_read (bp, dnode_path->dn.endian, &sahdrp, NULL, data);
+	      err = zio_read (bp, &sahdrp, NULL, data);
 	      if (err)
 	        break;
 	      free_sahdrp = true;
@@ -3075,17 +3259,14 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
 
 	  hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
 
-	  if (((grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp
-							 + hdrsize
-							 + SA_TYPE_OFFSET),
-				   dnode_path->dn.endian) >> 12) & 0xf) == 0xa)
+	  if (((grub_get_unaligned64 ((char *) sahdrp
+				      + hdrsize
+				      + SA_TYPE_OFFSET) >> 12) & 0xf) == 0xa)
 	    {
 	      char *sym_value = (char *) sahdrp + hdrsize + SA_SYMLINK_OFFSET;
-	      grub_size_t sym_sz =
-		grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp
+	      grub_size_t sym_sz = grub_get_unaligned64 ((char *) sahdrp
 							 + hdrsize
-							 + SA_SIZE_OFFSET),
-				   dnode_path->dn.endian);
+							 + SA_SIZE_OFFSET);
 	      char *oldpath = path, *oldpathbuf = path_buf;
 	      if (grub_add (sym_sz, grub_strlen (oldpath), &sz) ||
 		  grub_add (sz, 1, &sz))
@@ -3205,14 +3386,12 @@ get_default_bootfsobj (dnode_phys_t * mosmdn, grub_uint64_t * obj,
  *
  */
 static grub_err_t
-get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname,
-		      dnode_end_t * mdn, struct grub_zfs_data *data)
+get_filesystem_dnode (dnode_phys_t * mosmdn, char *fsname,
+		      dnode_phys_t * mdn, struct grub_zfs_data *data)
 {
   grub_uint64_t objnum;
   grub_err_t err;
 
-  grub_dprintf ("zfs", "endian = %d\n", mosmdn->endian);
-
   err = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT,
 		   DMU_OT_OBJECT_DIRECTORY, mdn, data);
   if (err)
@@ -3249,7 +3428,7 @@ get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname,
       ch = *fsname;
       *fsname = 0;
 
-      childobj = grub_zfs_to_cpu64 ((((dsl_dir_phys_t *) DN_BONUS (&mdn->dn)))->dd_child_dir_zapobj, mdn->endian);
+      childobj = (((dsl_dir_phys_t *) DN_BONUS (mdn)))->dd_child_dir_zapobj;
       err = dnode_get (mosmdn, childobj,
 		       DMU_OT_DSL_DIR_CHILD_MAP, mdn, data);
       if (err)
@@ -3269,17 +3448,15 @@ get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname,
 }
 
 static grub_err_t
-make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data)
+make_mdn (dnode_phys_t * mdn, struct grub_zfs_data *data)
 {
   objset_phys_t *osp;
   blkptr_t *bp;
   grub_size_t ospsize = 0;
   grub_err_t err;
 
-  grub_dprintf ("zfs", "endian = %d\n", mdn->endian);
-
-  bp = &(((dsl_dataset_phys_t *) DN_BONUS (&mdn->dn))->ds_bp);
-  err = zio_read (bp, mdn->endian, (void **) &osp, &ospsize, data);
+  bp = &(((dsl_dataset_phys_t *) DN_BONUS (mdn))->ds_bp);
+  err = zio_read (bp, (void **) &osp, &ospsize, data);
   if (err)
     return err;
   if (ospsize < OBJSET_PHYS_SIZE_V14)
@@ -3288,9 +3465,7 @@ make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data)
       return grub_error (GRUB_ERR_BAD_FS, "too small osp");
     }
 
-  mdn->endian = (grub_zfs_to_cpu64 (bp->blk_prop, mdn->endian)>>63) & 1;
-  grub_memmove ((char *) &(mdn->dn),
-		(char *) &(osp)->os_meta_dnode, DNODE_SIZE);
+  *mdn = (osp)->os_meta_dnode;
   grub_free (osp);
   return GRUB_ERR_NONE;
 }
@@ -3352,7 +3527,7 @@ load_zap_key (const void *name, grub_size_t namelen, const void *val_in,
 
 static grub_err_t
 dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
-		    dnode_end_t * dn, int *isfs,
+		    dnode_phys_t * dn, int *isfs,
 		    struct grub_zfs_data *data)
 {
   char *fsname, *snapname;
@@ -3412,9 +3587,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
 
   grub_dprintf ("zfs", "alive\n");
 
-  headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_head_dataset_obj, dn->endian);
-
-  grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
+  headobj = ((dsl_dir_phys_t *) DN_BONUS (dn))->dd_head_dataset_obj;
 
   err = dnode_get (&(data->mos), headobj, 0, &subvol->mdn, data);
   if (err)
@@ -3423,18 +3596,19 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
       grub_free (snapname);
       return err;
     }
-  grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
+  keychainobj = ((dsl_dir_phys_t *) DN_BONUS (dn))->keychain;
+
+  grub_dprintf("zfs", "keychain obj = %lld\n", (long long) keychainobj);
 
-  keychainobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->keychain, dn->endian);
-  if (grub_zfs_load_key && keychainobj)
+  if (grub_zfs_decrypt && keychainobj)
     {
       struct dnode_get_fullpath_ctx ctx = {
 	.subvol = subvol,
 	.keyn = 0
       };
-      dnode_end_t keychain_dn, props_dn;
+      dnode_phys_t keychain_dn, props_dn;
       grub_uint64_t propsobj;
-      propsobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_props_zapobj, dn->endian);
+      propsobj = ((dsl_dir_phys_t *) DN_BONUS (dn))->dd_props_zapobj;
 
       err = dnode_get (&(data->mos), propsobj, DMU_OT_DSL_PROPS,
 		       &props_dn, data);
@@ -3484,7 +3658,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
     {
       grub_uint64_t snapobj;
 
-      snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&subvol->mdn.dn))->ds_snapnames_zapobj, subvol->mdn.endian);
+      snapobj = ((dsl_dataset_phys_t *) DN_BONUS (&subvol->mdn))->ds_snapnames_zapobj;
 
       err = dnode_get (&(data->mos), snapobj,
 		       DMU_OT_DSL_DS_SNAP_MAP, &subvol->mdn, data);
@@ -3493,7 +3667,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
       if (!err)
 	err = dnode_get (&(data->mos), headobj, 0,
 			 &subvol->mdn, data);
-      if (!err && subvol->mdn.dn.dn_type != DMU_OT_DSL_DATASET && subvol->mdn.dn.dn_bonustype != DMU_OT_DSL_DATASET) {
+      if (!err && subvol->mdn.dn_type != DMU_OT_DSL_DATASET && subvol->mdn.dn_bonustype != DMU_OT_DSL_DATASET) {
 	grub_free (fsname);
 	grub_free (snapname);
 	return grub_error(GRUB_ERR_BAD_FS, "incorrect dataset dnode type");
@@ -3511,8 +3685,6 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
 
   make_mdn (&subvol->mdn, data);
 
-  grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
-
   if (*isfs)
     {
       grub_free (fsname);
@@ -3776,7 +3948,6 @@ zfs_mount (grub_device_t dev)
   grub_err_t err;
   objset_phys_t *osp = 0;
   grub_size_t ospsize;
-  grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
   uberblock_t *ub;
   int inserted;
 
@@ -3812,11 +3983,8 @@ zfs_mount (grub_device_t dev)
     }
 
   ub = &(data->current_uberblock);
-  ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic,
-				  GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC
-	       ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN);
 
-  err = zio_read (&ub->ub_rootbp, ub_endian,
+  err = zio_read (&ub->ub_rootbp,
 		  (void **) &osp, &ospsize, data);
   if (err)
     {
@@ -3833,7 +4001,7 @@ zfs_mount (grub_device_t dev)
     }
 
   if (ub->ub_version >= SPA_VERSION_FEATURES &&
-      check_mos_features(&osp->os_meta_dnode, ub_endian, data) != 0)
+      check_mos_features(&osp->os_meta_dnode, data) != 0)
     {
       grub_error (GRUB_ERR_BAD_FS, "Unsupported features in pool");
       grub_free (osp);
@@ -3842,9 +4010,7 @@ zfs_mount (grub_device_t dev)
     }
 
   /* Got the MOS. Save it at the memory addr MOS. */
-  grub_memmove (&(data->mos.dn), &osp->os_meta_dnode, DNODE_SIZE);
-  data->mos.endian = (grub_zfs_to_cpu64 (ub->ub_rootbp.blk_prop,
-					 ub_endian) >> 63) & 1;
+  grub_memmove (&(data->mos), &osp->os_meta_dnode, DNODE_SIZE);
   grub_free (osp);
 
   return data;
@@ -3910,8 +4076,6 @@ static grub_err_t
 zfs_mtime (grub_device_t device, grub_int64_t *mt)
 {
   struct grub_zfs_data *data;
-  grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
-  uberblock_t *ub;
 
   *mt = 0;
 
@@ -3919,16 +4083,48 @@ zfs_mtime (grub_device_t device, grub_int64_t *mt)
   if (! data)
     return grub_errno;
 
-  ub = &(data->current_uberblock);
-  ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic,
-				  GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC
-	       ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN);
-
-  *mt = grub_zfs_to_cpu64 (ub->ub_timestamp, ub_endian);
+  *mt = data->current_uberblock.ub_timestamp;
   zfs_unmount (data);
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+parse_sa(const dnode_phys_t *dn, grub_int64_t *mtime, grub_uint64_t *sz,
+	 struct grub_zfs_data *data)
+{
+  void *sahdrp;
+  int hdrsize;
+  grub_zfs_endian_t sa_endian;
+
+  if (dn->dn_bonuslen != 0)
+    sahdrp = (sa_hdr_phys_t *) DN_BONUS (dn);
+  else if (dn->dn_flags & DNODE_FLAG_SPILL_BLKPTR)
+    {
+      const blkptr_t *bp = &dn->dn_spill;
+
+      grub_err_t err = zio_read (bp, &sahdrp, NULL, data);
+      if (err)
+	return err;
+    }
+  else
+    return GRUB_ERR_NONE;
+
+  if (grub_zfs_to_cpu32(((sa_hdr_phys_t *) sahdrp)->sa_magic, GRUB_ZFS_LITTLE_ENDIAN) == SA_MAGIC)
+    sa_endian = GRUB_ZFS_LITTLE_ENDIAN;
+  else if (grub_zfs_to_cpu32(((sa_hdr_phys_t *) sahdrp)->sa_magic, GRUB_ZFS_BIG_ENDIAN) == SA_MAGIC)
+    sa_endian = GRUB_ZFS_BIG_ENDIAN;
+  else
+    return GRUB_ERR_NONE;
+
+  hdrsize = (grub_zfs_to_cpu16(((sa_hdr_phys_t *) sahdrp)->sa_layout_info, sa_endian) >> 10) << 3;
+  if (mtime)
+    *mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), sa_endian);
+  if (sz)
+    *sz = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), sa_endian);
+
+  return GRUB_ERR_NONE;
+}
+
 /*
  * zfs_open() locates a file in the rootpool by following the
  * MOS and places the dnode of the file in the memory address DNODE.
@@ -3959,7 +4155,7 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename)
     }
 
   /* We found the dnode for this file. Verify if it is a plain file. */
-  if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS)
+  if (data->dnode.dn_type != DMU_OT_PLAIN_FILE_CONTENTS)
     {
       zfs_unmount (data);
       return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
@@ -3972,38 +4168,17 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename)
    * attribute, which could be either in the bonus buffer
    * or the "spill" block.
    */
-  if (data->dnode.dn.dn_bonustype == DMU_OT_SA)
+  if (data->dnode.dn_bonustype == DMU_OT_SA)
     {
-      void *sahdrp;
-      int hdrsize;
-      bool free_sahdrp = false;
-
-      if (data->dnode.dn.dn_bonuslen != 0)
-	{
-	  sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn);
-	}
-      else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
-	{
-	  blkptr_t *bp = &data->dnode.dn.dn_spill;
-
-	  err = zio_read (bp, data->dnode.endian, &sahdrp, NULL, data);
-	  if (err)
-	    return err;
-	  free_sahdrp = true;
-	}
-      else
-	{
-	  return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
-	}
-
-      hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
-      file->size = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), data->dnode.endian);
-      if (free_sahdrp)
-	      grub_free(sahdrp);
+      grub_uint64_t fsize;
+      err = parse_sa(&data->dnode, NULL, &fsize, data);
+      if (err)
+	return err;
+      file->size = fsize;
     }
-  else if (data->dnode.dn.dn_bonustype == DMU_OT_ZNODE)
+  else if (data->dnode.dn_bonustype == DMU_OT_ZNODE)
     {
-      file->size = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&data->dnode.dn))->zp_size, data->dnode.endian);
+      file->size = ((znode_phys_t *) DN_BONUS (&data->dnode))->zp_size;
     }
   else
     return grub_error (GRUB_ERR_BAD_FS, "bad bonus type");
@@ -4038,8 +4213,7 @@ grub_zfs_read (grub_file_t file, char *buf, grub_size_t len)
       return len;
     }
 
-  blksz = grub_zfs_to_cpu16 (data->dnode.dn.dn_datablkszsec,
-			     data->dnode.endian) << SPA_MINBLOCKSHIFT;
+  blksz = data->dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT;
 
   if (blksz == 0)
     {
@@ -4065,8 +4239,7 @@ grub_zfs_read (grub_file_t file, char *buf, grub_size_t len)
       grub_free (data->file_buf);
       data->file_buf = 0;
 
-      err = dmu_read (&(data->dnode), blkid, &t,
-		      0, data);
+      err = dmu_read (&(data->dnode), blkid, &t, data);
       data->file_buf = t;
       if (err)
 	{
@@ -4125,19 +4298,19 @@ grub_zfs_getmdnobj (grub_device_t dev, const char *fsfilename,
 
 /*
  * Note: fill_fs_info() uses functions such as make_mdn() that modify
- * the input dnode_end_t parameter. However, we should not allow it.
+ * the input dnode_phys_t parameter. However, we should not allow it.
  * Therefore, we are making mdn_in constant - fill_fs_info() makes
  * a local copy of it.
  */
 static grub_err_t
 fill_fs_info (struct grub_dirhook_info *info,
-	      const dnode_end_t *mdn_in, struct grub_zfs_data *data)
+	      const dnode_phys_t *mdn_in, struct grub_zfs_data *data)
 {
   grub_err_t err;
-  dnode_end_t dn;
+  dnode_phys_t dn;
   grub_uint64_t objnum;
   grub_uint64_t headobj;
-  dnode_end_t mdn;
+  dnode_phys_t mdn;
 
   grub_memcpy (&mdn, mdn_in, sizeof (*mdn_in));
 
@@ -4145,9 +4318,9 @@ fill_fs_info (struct grub_dirhook_info *info,
 
   info->dir = 1;
 
-  if (mdn.dn.dn_type == DMU_OT_DSL_DIR || mdn.dn.dn_bonustype == DMU_OT_DSL_DIR)
+  if (mdn.dn_type == DMU_OT_DSL_DIR || mdn.dn_bonustype == DMU_OT_DSL_DIR)
     {
-      headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&mdn.dn))->dd_head_dataset_obj, mdn.endian);
+      headobj = ((dsl_dir_phys_t *) DN_BONUS (&mdn))->dd_head_dataset_obj;
 
       err = dnode_get (&(data->mos), headobj, 0, &mdn, data);
       if (err)
@@ -4181,42 +4354,18 @@ fill_fs_info (struct grub_dirhook_info *info,
       return err;
     }
 
-  if (dn.dn.dn_bonustype == DMU_OT_SA)
+  if (dn.dn_bonustype == DMU_OT_SA)
     {
-      void *sahdrp;
-      int hdrsize;
-      bool free_sahdrp = false;
-
-      if (dn.dn.dn_bonuslen != 0)
-	{
-	  sahdrp = (sa_hdr_phys_t *) DN_BONUS (&dn.dn);
-	}
-      else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
-	{
-	  blkptr_t *bp = &dn.dn.dn_spill;
-
-	  err = zio_read (bp, dn.endian, &sahdrp, NULL, data);
-	  if (err)
-	    return err;
-	  free_sahdrp = true;
-	}
-      else
-	{
-	  grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
-	  return grub_errno;
-	}
-
-      hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
+      err = parse_sa(&dn, &info->mtime, NULL, data);
+      if (err)
+	return err;
       info->mtimeset = 1;
-      info->mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
-      if (free_sahdrp)
-        grub_free (sahdrp);
     }
 
-  if (dn.dn.dn_bonustype == DMU_OT_ZNODE)
+  if (dn.dn_bonustype == DMU_OT_ZNODE)
     {
       info->mtimeset = 1;
-      info->mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], dn.endian);
+      info->mtime = ((znode_phys_t *) DN_BONUS (&dn))->zp_mtime[0];
     }
   return 0;
 }
@@ -4228,7 +4377,7 @@ iterate_zap (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx)
   grub_err_t err;
   struct grub_dirhook_info info;
 
-  dnode_end_t dn;
+  dnode_phys_t dn;
   grub_memset (&info, 0, sizeof (info));
 
   err = dnode_get (&(ctx->data->subvol.mdn), val, 0, &dn, ctx->data);
@@ -4238,52 +4387,22 @@ iterate_zap (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx)
       return 0;
     }
 
-  if (dn.dn.dn_bonustype == DMU_OT_SA)
+  if (dn.dn_bonustype == DMU_OT_SA)
     {
-      void *sahdrp;
-      int hdrsize;
-      bool free_sahdrp = false;
-
-      if (dn.dn.dn_bonuslen != 0)
-	{
-	  sahdrp = (sa_hdr_phys_t *) DN_BONUS (&ctx->data->dnode.dn);
-	}
-      else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
-	{
-	  blkptr_t *bp = &dn.dn.dn_spill;
-
-	  err = zio_read (bp, dn.endian, &sahdrp, NULL, ctx->data);
-	  if (err)
-	    {
-	      grub_print_error ();
-	      return 0;
-	    }
-	  free_sahdrp = true;
-	}
-      else
-	{
-	  grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
-	  grub_print_error ();
-	  return 0;
-	}
-
-      hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
+      err = parse_sa(&dn, &info.mtime, NULL, ctx->data);
+      if (err)
+	return err;
       info.mtimeset = 1;
-      info.mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
-      info.case_insensitive = ctx->data->subvol.case_insensitive;
-      if (free_sahdrp)
-	grub_free (sahdrp);
     }
 
-  if (dn.dn.dn_bonustype == DMU_OT_ZNODE)
+  if (dn.dn_bonustype == DMU_OT_ZNODE)
     {
       info.mtimeset = 1;
-      info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0],
-				      dn.endian);
+      info.mtime = ((znode_phys_t *) DN_BONUS (&dn))->zp_mtime[0];
     }
-  info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS);
+  info.dir = (dn.dn_type == DMU_OT_DIRECTORY_CONTENTS);
   grub_dprintf ("zfs", "type=%d, name=%s\n",
-		(int)dn.dn.dn_type, (char *)name);
+		(int)dn.dn_type, (char *)name);
   return ctx->hook (name, &info, ctx->hook_data);
 }
 
@@ -4298,15 +4417,15 @@ iterate_zap_fs (const char *name, grub_uint64_t val,
   if (name[0] == 0 && val == 0)
     return 0;
 
-  dnode_end_t mdn;
+  dnode_phys_t mdn;
   err = dnode_get (&(ctx->data->mos), val, 0, &mdn, ctx->data);
   if (err)
     {
       grub_errno = 0;
       return 0;
     }
-  if (mdn.dn.dn_type != DMU_OT_DSL_DIR && mdn.dn.dn_bonustype != DMU_OT_DSL_DIR) {
-    grub_dprintf ("zfs", "type = 0x%x, val = 0x%llx\n", mdn.dn.dn_type, (long long)val);
+  if (mdn.dn_type != DMU_OT_DSL_DIR && mdn.dn_bonustype != DMU_OT_DSL_DIR) {
+    grub_dprintf ("zfs", "type = 0x%x, val = 0x%llx\n", mdn.dn_type, (long long)val);
     return 0;
   }
 
@@ -4330,7 +4449,7 @@ iterate_zap_snap (const char *name, grub_uint64_t val,
   int ret;
   grub_size_t sz;
 
-  dnode_end_t mdn;
+  dnode_phys_t mdn;
 
   err = dnode_get (&(ctx->data->mos), val, 0, &mdn, ctx->data);
   if (err)
@@ -4339,7 +4458,7 @@ iterate_zap_snap (const char *name, grub_uint64_t val,
       return 0;
     }
 
-  if (mdn.dn.dn_type != DMU_OT_DSL_DATASET && mdn.dn.dn_bonustype != DMU_OT_DSL_DATASET)
+  if (mdn.dn_type != DMU_OT_DSL_DATASET && mdn.dn_bonustype != DMU_OT_DSL_DATASET)
     return 0;
 
   err = fill_fs_info (&info, &mdn, ctx->data);
@@ -4390,7 +4509,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
     {
       grub_uint64_t childobj, headobj;
       grub_uint64_t snapobj;
-      dnode_end_t dn;
+      dnode_phys_t dn;
       struct grub_dirhook_info info;
 
       err = fill_fs_info (&info, &data->dnode, data);
@@ -4405,8 +4524,8 @@ grub_zfs_dir (grub_device_t device, const char *path,
 	  return GRUB_ERR_NONE;
 	}
 
-      childobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian);
-      headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian);
+      childobj = ((dsl_dir_phys_t *) DN_BONUS (&data->dnode))->dd_child_dir_zapobj;
+      headobj = ((dsl_dir_phys_t *) DN_BONUS (&data->dnode))->dd_head_dataset_obj;
       err = dnode_get (&(data->mos), childobj,
 		       DMU_OT_DSL_DIR_CHILD_MAP, &dn, data);
       if (err)
@@ -4418,7 +4537,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
       zap_iterate_u64 (&dn, iterate_zap_fs, data, &ctx);
 
       err = dnode_get (&(data->mos), headobj, 0, &dn, data);
-      if (!err && dn.dn.dn_type != DMU_OT_DSL_DATASET && dn.dn.dn_bonustype != DMU_OT_DSL_DATASET)
+      if (!err && dn.dn_type != DMU_OT_DSL_DATASET && dn.dn_bonustype != DMU_OT_DSL_DATASET)
 	return grub_error(GRUB_ERR_BAD_FS, "incorrect dataset dnode type");
 
       if (err)
@@ -4427,7 +4546,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
 	  return err;
 	}
 
-      snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&dn.dn))->ds_snapnames_zapobj, dn.endian);
+      snapobj = ((dsl_dataset_phys_t *) DN_BONUS (&dn))->ds_snapnames_zapobj;
 
       err = dnode_get (&(data->mos), snapobj,
 		       DMU_OT_DSL_DS_SNAP_MAP, &dn, data);
@@ -4441,7 +4560,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
     }
   else
     {
-      if (data->dnode.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS)
+      if (data->dnode.dn_type != DMU_OT_DIRECTORY_CONTENTS)
 	{
 	  zfs_unmount (data);
 	  return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
@@ -4477,16 +4596,14 @@ check_feature (const char *name, grub_uint64_t val,
  */
 
 static grub_err_t
-check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct grub_zfs_data* data )
+check_mos_features(dnode_phys_t *mosmdn_phys, struct grub_zfs_data* data )
 {
   grub_uint64_t objnum;
-  grub_err_t errnum = 0;
-  dnode_end_t dn,mosmdn;
+  grub_err_t errnum = 0, ret = 0;
+  dnode_phys_t dn,mosmdn;
   mzap_phys_t* mzp;
-  grub_zfs_endian_t endianzap;
-  int size, ret;
-  grub_memmove(&(mosmdn.dn),mosmdn_phys,sizeof(dnode_phys_t));
-  mosmdn.endian=endian;
+  int size;
+  grub_memmove(&mosmdn,mosmdn_phys,sizeof(dnode_phys_t));
   errnum = dnode_get(&mosmdn, DMU_POOL_DIRECTORY_OBJECT,
 		     DMU_OT_OBJECT_DIRECTORY, &dn,data);
   if (errnum != 0)
@@ -4505,12 +4622,12 @@ check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct gru
   if (errnum != 0)
     return errnum;
 
-  errnum = dmu_read(&dn, 0, (void**)&mzp, &endianzap,data);
+  errnum = dmu_read(&dn, 0, (void**)&mzp, data);
   if (errnum != 0)
     return errnum;
 
-  size = grub_zfs_to_cpu16 (dn.dn.dn_datablkszsec, dn.endian) << SPA_MINBLOCKSHIFT;
-  ret = mzap_iterate (mzp,endianzap, size, check_feature,NULL);
+  size = dn.dn_datablkszsec << SPA_MINBLOCKSHIFT;
+  ret = mzap_iterate (mzp, size, check_feature,NULL);
   grub_free(mzp);
   return ret;
 }
diff --git a/grub-core/fs/zfs/zfs_fletcher.c b/grub-core/fs/zfs/zfs_fletcher.c
index ad3be6705..deac0b45b 100644
--- a/grub-core/fs/zfs/zfs_fletcher.c
+++ b/grub-core/fs/zfs/zfs_fletcher.c
@@ -54,10 +54,10 @@ fletcher_2(const void *buf, grub_uint64_t size, grub_zfs_endian_t endian,
       b1 += a1;
     }
 
-  zcp->zc_word[0] = grub_cpu_to_zfs64 (a0, endian);
-  zcp->zc_word[1] = grub_cpu_to_zfs64 (a1, endian);
-  zcp->zc_word[2] = grub_cpu_to_zfs64 (b0, endian);
-  zcp->zc_word[3] = grub_cpu_to_zfs64 (b1, endian);
+  zcp->zc_word[0] = a0;
+  zcp->zc_word[1] = a1;
+  zcp->zc_word[2] = b0;
+  zcp->zc_word[3] = b1;
 }
 
 void
@@ -70,15 +70,15 @@ fletcher_4 (const void *buf, grub_uint64_t size, grub_zfs_endian_t endian,
 
   for (a = b = c = d = 0; ip < ipend; ip++)
     {
-      a += grub_zfs_to_cpu32 (ip[0], endian);;
+      a += grub_zfs_to_cpu32 (ip[0], endian);
       b += a;
       c += b;
       d += c;
     }
 
-  zcp->zc_word[0] = grub_cpu_to_zfs64 (a, endian);
-  zcp->zc_word[1] = grub_cpu_to_zfs64 (b, endian);
-  zcp->zc_word[2] = grub_cpu_to_zfs64 (c, endian);
-  zcp->zc_word[3] = grub_cpu_to_zfs64 (d, endian);
+  zcp->zc_word[0] = a;
+  zcp->zc_word[1] = b;
+  zcp->zc_word[2] = c;
+  zcp->zc_word[3] = d;
 }
 
diff --git a/grub-core/fs/zfs/zfs_sha256.c b/grub-core/fs/zfs/zfs_sha256.c
index f042fa61a..69f1bd9d9 100644
--- a/grub-core/fs/zfs/zfs_sha256.c
+++ b/grub-core/fs/zfs/zfs_sha256.c
@@ -109,7 +109,7 @@ SHA256Transform(grub_uint32_t *H, const grub_uint8_t *cp)
 
 void
 zio_checksum_SHA256(const void *buf, grub_uint64_t size,
-		    grub_zfs_endian_t endian, zio_cksum_t *zcp)
+		    grub_zfs_endian_t endian __attribute__((unused)), zio_cksum_t *zcp)
 {
   grub_uint32_t H[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
 			 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
@@ -132,12 +132,8 @@ zio_checksum_SHA256(const void *buf, grub_uint64_t size,
   for (i = 0; i < padsize && i <= 64; i += 64)
     SHA256Transform(H, pad + i);
 
-  zcp->zc_word[0] = grub_cpu_to_zfs64 ((grub_uint64_t)H[0] << 32 | H[1],
-				       endian);
-  zcp->zc_word[1] = grub_cpu_to_zfs64 ((grub_uint64_t)H[2] << 32 | H[3],
-				       endian);
-  zcp->zc_word[2] = grub_cpu_to_zfs64 ((grub_uint64_t)H[4] << 32 | H[5],
-				       endian);
-  zcp->zc_word[3] = grub_cpu_to_zfs64 ((grub_uint64_t)H[6] << 32 | H[7],
-				       endian);
+  zcp->zc_word[0] = (grub_uint64_t)H[0] << 32 | H[1];
+  zcp->zc_word[1] = (grub_uint64_t)H[2] << 32 | H[3];
+  zcp->zc_word[2] = (grub_uint64_t)H[4] << 32 | H[5];
+  zcp->zc_word[3] = (grub_uint64_t)H[6] << 32 | H[7];
 }
diff --git a/grub-core/fs/zfs/zfscrypt.c b/grub-core/fs/zfs/zfscrypt.c
index da30e9ab3..535fd4065 100644
--- a/grub-core/fs/zfs/zfscrypt.c
+++ b/grub-core/fs/zfs/zfscrypt.c
@@ -277,7 +277,7 @@ algo_decrypt (grub_crypto_cipher_handle_t cipher, grub_uint64_t algo,
 static grub_err_t
 grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher,
 		       grub_uint64_t algo,
-		       void *nonce,
+		       const void *nonce,
 		       char *buf, grub_size_t size,
 		       const grub_uint32_t *expected_mac,
 		       grub_zfs_endian_t endian)
diff --git a/include/grub/zfs/sa_impl.h b/include/grub/zfs/sa_impl.h
index 0845d1290..b92c458a4 100644
--- a/include/grub/zfs/sa_impl.h
+++ b/include/grub/zfs/sa_impl.h
@@ -29,6 +29,7 @@ typedef struct sa_hdr_phys {
 } sa_hdr_phys_t;
 
 #define	SA_HDR_SIZE(hdr)	BF32_GET_SB(hdr->sa_layout_info, 10, 16, 3, 0)
+#define	SA_MAGIC	0x2F505A
 #define	SA_TYPE_OFFSET	0x0
 #define	SA_SIZE_OFFSET	0x8
 #define	SA_MTIME_OFFSET	0x38
diff --git a/include/grub/zfs/spa.h b/include/grub/zfs/spa.h
index 5afbe4ecd..da44d1864 100644
--- a/include/grub/zfs/spa.h
+++ b/include/grub/zfs/spa.h
@@ -196,9 +196,18 @@ typedef struct blkptr {
 #define	DVA_GET_VDEV(dva)	BF64_GET((dva)->dva_word[0], 32, 32)
 #define	DVA_SET_VDEV(dva, x)	BF64_SET((dva)->dva_word[0], 32, 32, x)
 
+#define	DVA_GET_OFFSET(dva)	\
+	BF64_GET_SB((dva)->dva_word[1], 0, 63, SPA_MINBLOCKSHIFT, 0)
+#define	DVA_SET_OFFSET(dva, x)	\
+	BF64_SET_SB((dva)->dva_word[1], 0, 63, SPA_MINBLOCKSHIFT, 0, x)
+
 #define	DVA_GET_GANG(dva)	BF64_GET((dva)->dva_word[1], 63, 1)
 #define	DVA_SET_GANG(dva, x)	BF64_SET((dva)->dva_word[1], 63, 1, x)
 
+#define	BP_GET_PSIZE(bp)	\
+	BF64_GET_SB((bp)->blk_prop, 16, 16, SPA_MINBLOCKSHIFT, 1)
+#define	BP_SET_PSIZE(bp, x)	\
+	BF64_SET_SB((bp)->blk_prop, 16, 16, SPA_MINBLOCKSHIFT, 1, x)
 #define	BP_GET_LSIZE(bp)	\
 	BF64_GET_SB((bp)->blk_prop, 0, 16, SPA_MINBLOCKSHIFT, 1)
 #define	BP_SET_LSIZE(bp, x)	\
diff --git a/include/grub/zfs/zfs.h b/include/grub/zfs/zfs.h
index 4ee513887..0c5c40a73 100644
--- a/include/grub/zfs/zfs.h
+++ b/include/grub/zfs/zfs.h
@@ -33,6 +33,14 @@ typedef enum grub_zfs_endian
     GRUB_ZFS_BIG_ENDIAN = 0
   } grub_zfs_endian_t;
 
+#ifdef GRUB_CPU_WORDS_BIGENDIAN
+#define GRUB_ZFS_IS_NATIVE_BYTEORDER(x) ((x) == GRUB_ZFS_BIG_ENDIAN)
+#define GRUB_ZFS_NATIVE_ENDIAN GRUB_ZFS_BIG_ENDIAN
+#else
+#define GRUB_ZFS_IS_NATIVE_BYTEORDER(x) ((x) != GRUB_ZFS_BIG_ENDIAN)
+#define GRUB_ZFS_NATIVE_ENDIAN GRUB_ZFS_LITTLE_ENDIAN
+#endif
+
 /*
  * On-disk version number.
  */
@@ -141,7 +149,7 @@ grub_zfs_add_key (grub_uint8_t *key_in,
 
 extern grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
 				       grub_uint64_t algo,
-				       void *nonce,
+				       const void *nonce,
 				       char *buf, grub_size_t size,
 				       const grub_uint32_t *expected_mac,
 				       grub_zfs_endian_t endian);
-- 
2.49.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/2] zfs: Support datto encryption
  2025-08-01 14:30 [PATCH 0/2] zfs: Support datto encryption and fix endianness handling Vladimir Serbinenko
  2025-08-01 14:30 ` [PATCH 1/2] zfs: Rewrite " Vladimir Serbinenko
@ 2025-08-01 14:30 ` Vladimir Serbinenko
  2025-08-11 13:48   ` Neal Gompa
  2025-08-11 13:23 ` [PATCH 0/2] zfs: Support datto encryption and fix endianness handling Neal Gompa
  2 siblings, 1 reply; 8+ messages in thread
From: Vladimir Serbinenko @ 2025-08-01 14:30 UTC (permalink / raw)
  To: grub-devel; +Cc: Vladimir Serbinenko

Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
---
 grub-core/fs/zfs/zfs.c      | 502 ++++++++++++++++++++++++++++++++----
 grub-core/fs/zfs/zfscrypt.c | 335 +++++++++++++++++++++---
 include/grub/zfs/zfs.h      |  54 +++-
 3 files changed, 793 insertions(+), 98 deletions(-)

diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index b88a2b032..da1661680 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -227,6 +227,10 @@ struct subvolume
     grub_uint64_t txg;
     grub_uint64_t algo;
   } *keyring;
+
+  struct grub_zfs_datto_key key_datto;
+
+  int is_datto_encrypted;
 };
 
 struct grub_zfs_data
@@ -264,16 +268,19 @@ struct grub_zfs_dir_ctx
   struct grub_zfs_data *data;
 };
 
-grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
-				grub_uint64_t algo,
-				const void *nonce,
-				char *buf, grub_size_t size,
-				const grub_uint32_t *expected_mac,
-				grub_zfs_endian_t endian) = NULL;
-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;
+struct grub_zfs_decryptor *grub_zfs_decrypt = NULL;
+
+struct grub_zfs_crypt_datto_data
+{
+  grub_uint8_t *iv;
+  grub_size_t ivlen;
+  grub_uint8_t *mac;
+  grub_size_t maclen;
+  grub_uint8_t *master;
+  grub_size_t masterlen;
+  grub_uint8_t *hmac;
+  grub_size_t hmaclen;
+};
 /*
  * 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
@@ -289,6 +296,7 @@ static const char *spa_feature_names[] = {
   "com.klarasystems:vdev_zaps_v2",
   "com.delphix:head_errlog",
   "org.freebsd:zstd_compress",
+  "com.datto:encryption",
   NULL
 };
 
@@ -400,6 +408,47 @@ static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = {
 static grub_err_t zio_read_data (const blkptr_t * bp,
 				 void *buf, struct grub_zfs_data *data);
 
+static int
+fill_crypt_datto_data (const void *name,
+		    grub_size_t namelen __attribute__ ((unused)),
+		    const void *val_in,
+		    grub_size_t nelem,
+		    grub_size_t elemsize,
+		    void *data_in)
+{
+  struct grub_zfs_crypt_datto_data *data = data_in;
+  if (grub_strcmp(name, "DSL_CRYPTO_IV") == 0 && elemsize == 1)
+    {
+      data->ivlen = nelem;
+      data->iv = grub_malloc(nelem);
+      if (data->iv)
+	grub_memcpy(data->iv, val_in, nelem);
+    }
+  else if (grub_strcmp(name, "DSL_CRYPTO_MAC") == 0 && elemsize == 1)
+    {
+      data->maclen = nelem;
+      data->mac = grub_malloc(nelem);
+      if (data->mac)
+	grub_memcpy(data->mac, val_in, nelem);
+    }
+  else if (grub_strcmp(name, "DSL_CRYPTO_MASTER_KEY_1") == 0 && elemsize == 1)
+    {
+      data->masterlen = nelem;
+      data->master = grub_malloc(nelem);
+      if (data->master)
+	grub_memcpy(data->master, val_in, nelem);
+    }
+  else if (grub_strcmp(name, "DSL_CRYPTO_HMAC_KEY_1") == 0 && elemsize == 1)
+    {
+      data->hmaclen = nelem;
+      data->hmac = grub_malloc(nelem);
+      if (data->hmac)
+	grub_memcpy(data->hmac, val_in, nelem);
+    }
+
+  return 0;
+}
+
 /*
  * Our own version of log2().  Same thing as highbit()-1.
  */
@@ -690,7 +739,7 @@ grub_zfs_byteswap_type(void *buf, grub_size_t len, grub_uint8_t type)
 static grub_err_t
 zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
 		     grub_zfs_endian_t endian,
-		     char *buf, grub_size_t size)
+		     char *buf, grub_size_t size, int datto_crypt)
 {
   zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1;
   zio_checksum_info_t *ci = &zio_checksum_table[checksum];
@@ -715,6 +764,12 @@ zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
     ci->ci_func (buf, size, endian, &actual_cksum);
 
   int cksumlen = checksum != ZIO_CHECKSUM_SHA256_MAC ? 32 : 20;
+  if (datto_crypt)
+    {
+      actual_cksum.zc_word[0] ^= actual_cksum.zc_word[2];
+      actual_cksum.zc_word[1] ^= actual_cksum.zc_word[3];
+      cksumlen = 16;
+    }
 
   if (ci->ci_eck && !GRUB_ZFS_IS_NATIVE_BYTEORDER(endian))
     grub_zfs_byteswap_checksum(&actual_cksum);
@@ -796,7 +851,7 @@ uberblock_verify (uberblock_phys_t * ub, grub_uint64_t offset,
 
   zc.zc_word[0] = grub_cpu_to_zfs64 (offset, endian);
   err = zio_checksum_verify (zc, ZIO_CHECKSUM_LABEL, endian,
-			     (char *) ub, s);
+			     (char *) ub, s, 0);
 
   return err;
 }
@@ -1269,7 +1324,7 @@ check_pool_label (struct grub_zfs_data *data,
   /* Now check the integrity of the vdev_phys_t structure though checksum.  */
   ZIO_SET_CHECKSUM(&emptycksum, grub_cpu_to_zfs64(diskdesc->vdev_phys_sector << 9, endian), 0, 0, 0);
   err = zio_checksum_verify (emptycksum, ZIO_CHECKSUM_LABEL, endian,
-			     nvlist, VDEV_PHYS_SIZE);
+			     nvlist, VDEV_PHYS_SIZE, 0);
   if (err) {
     grub_free (nvlist);
     return err;
@@ -2019,7 +2074,7 @@ zio_read_gang (const blkptr_t * bp, const dva_t * dva, void *buf,
   ZIO_SET_CHECKSUM (&zc, DVA_GET_VDEV (dva),
 		    DVA_GET_OFFSET (dva), bp->blk_birth, 0);
   err = zio_checksum_verify (zc, ZIO_CHECKSUM_GANG_HEADER, BP_GET_BYTEORDER(bp),
-			     (char *) zio_gb, SPA_GANGBLOCKSIZE);
+			     (char *) zio_gb, SPA_GANGBLOCKSIZE, 0);
   if (err)
     {
       grub_free (zio_gb);
@@ -2113,6 +2168,57 @@ decode_embedded_bp_compressed(const blkptr_t *bp, void *buf)
   return GRUB_ERR_NONE;
 }
 
+static int
+datto_is_encrypted_type(grub_uint8_t dn_type)
+{
+  /* New structred types.  */
+  if (dn_type & 0x80)
+    return !!(dn_type & 0x20);
+  switch(dn_type) {
+  case DMU_OT_NONE ... DMU_OT_SPACE_MAP:
+  case DMU_OT_OBJSET ... DMU_OT_ZNODE:
+  case DMU_OT_MASTER_NODE:
+  case DMU_OT_ZVOL_PROP:
+  case DMU_OT_ZAP_OTHER ... DMU_OT_DSL_PERMS:
+  case DMU_OT_FUID_SIZE ... DMU_OT_SCRUB_QUEUE:
+  case DMU_OT_USERREFS ... DMU_OT_DDT_STATS:
+    return 0;
+  default:
+    return 1;
+  }
+}
+
+static void
+add_blkptr_to_aad (char *aad, grub_size_t *aad_offset, blkptr_t bp, grub_zfs_endian_t endian, int preswapped)
+{
+  if (!preswapped && !GRUB_ZFS_IS_NATIVE_BYTEORDER(endian))
+    grub_zfs_byteswap_blkptr(&bp);
+  
+  grub_uint64_t blk_prop = BP_IS_HOLE(&bp) ? 0 : bp.blk_prop;
+
+  if (BP_GET_LEVEL(&bp) != 0) {
+    blk_prop &= ~0x8000007fffff0000ULL;
+  }
+
+  blk_prop &= ~0x4000ff0000000000ULL;
+
+  grub_set_unaligned64(&aad[*aad_offset], grub_cpu_to_le64(blk_prop));
+  *aad_offset += 8;
+
+  if (!GRUB_ZFS_IS_NATIVE_BYTEORDER(endian))
+    {
+      grub_set_unaligned64(&aad[*aad_offset], grub_swap_bytes64(bp.blk_cksum.zc_word[2]));
+      grub_set_unaligned64(&aad[*aad_offset+8], grub_swap_bytes64(bp.blk_cksum.zc_word[3]));
+    }
+  else
+      grub_memcpy(&aad[*aad_offset], &bp.blk_cksum.zc_word[2], 16);
+      
+  *aad_offset += 16;
+
+  grub_memset(&aad[*aad_offset], 0, 8);
+  *aad_offset += 8;
+}
+
 /*
  * Read in a block of data, verify its checksum, decompress if needed,
  * and put the uncompressed data in buf.
@@ -2122,17 +2228,44 @@ zio_read (const blkptr_t *bp, void **buf,
 	  grub_size_t *size, struct grub_zfs_data *data)
 {
   grub_size_t lsize, psize;
-  unsigned int comp, encrypted;
+  unsigned int comp;
   char *compbuf = NULL;
   grub_err_t err;
   zio_cksum_t zc = bp->blk_cksum;
   grub_uint32_t checksum;
+  int datto_encrypted = 0, datto_authenticated = 0, datto_dnode_encryption = 0, oracle_encrypted = 0;
 
   *buf = NULL;
 
   checksum = BP_GET_CHECKSUM(bp);
   comp = BP_GET_COMPRESS(bp);
-  encrypted = BP_GET_PROP_BIT_61(bp);
+  if (BP_GET_PROP_BIT_61(bp))
+    {
+      if (data->subvol.is_datto_encrypted)
+	{
+	  grub_uint8_t type = BP_GET_TYPE(bp);
+	  if (BP_GET_LEVEL(bp) > 0)
+	    datto_authenticated = 1;
+	  else if (type == DMU_OT_DNODE)
+	    {
+	      datto_encrypted = 1;
+	      datto_authenticated = 0;
+	      datto_dnode_encryption = 1;
+	    }
+	  else if (type == DMU_OT_OBJSET)
+	    {
+	      /* Objset uses inner hmacs that we don't suport yet.
+		 Normal checksum is unaffected . */
+	    }
+	  else
+	    {
+	      datto_encrypted = datto_is_encrypted_type(type);
+	      datto_authenticated = !datto_encrypted;
+	    }
+	}
+      else
+	  oracle_encrypted = 1;
+    }
   if (BP_IS_EMBEDDED(bp))
     {
       if (BPE_GET_ETYPE(bp) != BP_EMBEDDED_TYPE_DATA)
@@ -2186,10 +2319,11 @@ zio_read (const blkptr_t *bp, void **buf,
       return err;
     }
 
-  if (!BP_IS_EMBEDDED(bp))
+  if (!BP_IS_EMBEDDED(bp) && !datto_encrypted)
     {
       err = zio_checksum_verify (zc, checksum, BP_GET_BYTEORDER(bp),
-			         compbuf, psize);
+			         compbuf, psize,
+				 datto_authenticated);
       if (err)
         {
           grub_dprintf ("zfs", "incorrect checksum\n");
@@ -2199,48 +2333,190 @@ zio_read (const blkptr_t *bp, void **buf,
         }
     }
 
-  if (encrypted)
+  if (!BP_IS_EMBEDDED(bp) && datto_authenticated && data->subvol.key_datto.hmac_key && BP_GET_LEVEL(bp) == 0)
     {
-      if (!grub_zfs_decrypt)
-	err = grub_error (GRUB_ERR_BAD_FS,
-			  N_("module `%s' isn't loaded"),
-			  "zfscrypt");
-      else
+      grub_uint64_t hmac[8];
+      gcry_error_t err_gcry;
+      err_gcry = grub_crypto_hmac_buffer (GRUB_MD_SHA512,
+					  data->subvol.key_datto.hmac_key, 64,
+					  compbuf, psize,
+					  hmac);
+      if (err_gcry)
 	{
-	  unsigned i, besti = 0;
-	  grub_uint64_t bestval = 0;
-	  for (i = 0; i < data->subvol.nkeys; i++)
-	    if (data->subvol.keyring[i].txg <= bp->blk_birth
-		&& data->subvol.keyring[i].txg > bestval)
-	      {
-		besti = i;
-		bestval = data->subvol.keyring[i].txg;
-	      }
-	  if (bestval == 0)
+	  grub_free (compbuf);
+          *buf = NULL;
+	  return grub_crypto_gcry_error (err_gcry);
+	}
+
+      if (!GRUB_ZFS_IS_NATIVE_BYTEORDER(BP_GET_BYTEORDER(bp)))
+	{
+	  grub_zfs_byteswap_u64(&hmac[0]);
+	  grub_zfs_byteswap_u64(&hmac[1]);
+	}      
+
+      if (grub_crypto_memcmp(&zc.zc_word[2], hmac, 8) != 0)
+	{
+	  grub_free (compbuf);
+          *buf = NULL;
+	  grub_dprintf ("zfs", "actual hmac "
+			"%016llx %016llx %016llx %016llx %016llx %016llx %016llx %016llx \n",
+			(unsigned long long) hmac[0],
+			(unsigned long long) hmac[1],
+			(unsigned long long) hmac[2],
+			(unsigned long long) hmac[3],
+			(unsigned long long) hmac[4],
+			(unsigned long long) hmac[5],
+			(unsigned long long) hmac[6],
+			(unsigned long long) hmac[7]);
+	  grub_dprintf ("zfs", "expected hmac %016llx %016llx %016llx %016llx\n",
+			(unsigned long long) zc.zc_word[0],
+			(unsigned long long) zc.zc_word[1],
+			(unsigned long long) zc.zc_word[2],
+			(unsigned long long) zc.zc_word[3]);
+	  return grub_error (GRUB_ERR_BAD_FS, N_("HMAC verification failed"));
+	}
+    }
+
+  if (datto_dnode_encryption && (!grub_zfs_decrypt || !data->subvol.key_datto.master_key))
+    {
+      grub_dprintf("zfs", "Skipping decrypt of bonus because of missing zfscrypt module or key\n");
+      datto_encrypted = 0;
+      datto_dnode_encryption = 0;
+    }
+  if ((oracle_encrypted || datto_encrypted) && !grub_zfs_decrypt)
+    err = grub_error (GRUB_ERR_BAD_FS,
+		      N_("module `%s' isn't loaded"),
+		      "zfscrypt");
+  else if (datto_encrypted)
+    {
+      grub_uint32_t iv[3];
+
+      if (!data->subvol.key_datto.master_key)
+	{
+	  grub_free (compbuf);
+	  *buf = NULL;
+	  grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
+			bp->blk_birth);
+	  return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
+	}
+
+      grub_set_unaligned64(iv, grub_cpu_to_zfs64((bp)->blk_dva[2].dva_word[1], BP_GET_BYTEORDER(bp)));
+      iv[2] = grub_cpu_to_zfs32((bp)->blk_fill >> 32, BP_GET_BYTEORDER(bp));
+
+      if (datto_dnode_encryption)
+	{
+	  grub_size_t offset = 0, crypt_offset = 0, aad_offset = 0;
+	  char *crypt = grub_malloc(psize), *aad = grub_malloc(psize);
+	  grub_zfs_endian_t endian = BP_GET_BYTEORDER(bp);
+	  if (!crypt || !aad)
 	    {
 	      grub_free (compbuf);
+	      grub_free (crypt);
+	      grub_free (aad);
 	      *buf = NULL;
-	      grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
-			    bp->blk_birth);
-	      return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
+	      return grub_errno;
 	    }
-	  grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T
-			", %p) for txg %" PRIxGRUB_UINT64_T "\n",
-			besti, data->subvol.keyring[besti].txg,
-			data->subvol.keyring[besti].cipher,
-			bp->blk_birth);
-	  err = grub_zfs_decrypt (data->subvol.keyring[besti].cipher,
-				  data->subvol.keyring[besti].algo,
-				  &(bp)->blk_dva[2],
-				  compbuf, psize, zc.zc_mac,
-				  BP_GET_BYTEORDER(bp));
+	  for (offset = 0; offset + sizeof (dnode_phys_t) <= psize; offset += sizeof(dnode_phys_t))
+	    {
+	      dnode_phys_t *dnp = (dnode_phys_t *) (void *) (compbuf + offset);
+	      dnode_phys_t *dno = (dnode_phys_t *) (aad + aad_offset);
+	      grub_memcpy(aad + aad_offset, dnp, 64);
+	      dno->dn_used = 0;
+	      dno->dn_flags &= DNODE_FLAG_SPILL_BLKPTR;
+	      int has_spill = dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR;
+	      unsigned i;
+
+	      aad_offset += 64;
+
+	      for (i = 0; i < dnp->dn_nblkptr; i++) {
+		add_blkptr_to_aad (aad, &aad_offset, dnp->dn_blkptr[i], endian, 0);
+	      }
+
+	      if (has_spill)
+		add_blkptr_to_aad (aad, &aad_offset, dnp->dn_spill, endian, 0);
+
+	      char *bonus = DN_BONUS(dnp);
+	      char *bonusmaxptr = (char *) (dnp + 1);
+	      if (has_spill)
+		bonusmaxptr -= sizeof(blkptr_t);
+	      grub_size_t bonusmaxlen = bonusmaxptr - bonus;
+	      if (datto_is_encrypted_type(dnp->dn_bonustype))
+		{
+		  grub_memcpy(crypt + crypt_offset, bonus, bonusmaxlen);
+		  crypt_offset += bonusmaxlen;
+		}
+	      else
+		{
+		  grub_memcpy(aad + aad_offset, bonus, bonusmaxlen);
+		  grub_zfs_byteswap_type((aad + aad_offset), bonusmaxlen, dnp->dn_bonustype);
+		  aad_offset += bonusmaxlen;
+		}
+	    }
+	  err = grub_zfs_decrypt->decrypt_datto (&data->subvol.key_datto,
+						 iv, (bp)->blk_dva[2].dva_word[0],
+						 crypt, crypt_offset, aad, aad_offset,
+						 &zc.zc_word[2],
+						 endian);
+	  grub_size_t out_offset = 0;
+	  for (offset = 0; offset + sizeof (dnode_phys_t) <= psize; offset += sizeof(dnode_phys_t))
+	    {
+	      dnode_phys_t *dnp = (dnode_phys_t *) (void *) (compbuf + offset);
+	      int has_spill = dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR;
+	      if (!datto_is_encrypted_type(dnp->dn_bonustype))
+		continue;
+	      char * bonus = DN_BONUS(dnp);
+	      char *bonusmaxptr = (char *) (dnp + 1);
+	      if (has_spill)
+		bonusmaxptr -= sizeof(blkptr_t);
+	      grub_size_t bonusmaxlen = bonusmaxptr - bonus;
+	      grub_memcpy(bonus, crypt + out_offset, bonusmaxlen);
+	      out_offset += bonusmaxlen;
+	    }
+
 	}
-      if (err)
+      else
+	err = grub_zfs_decrypt->decrypt_datto (&data->subvol.key_datto,
+					       iv, (bp)->blk_dva[2].dva_word[0],
+					       compbuf, psize, NULL, 0,
+					       &zc.zc_word[2],
+					       BP_GET_BYTEORDER(bp));
+    }
+  else if (oracle_encrypted)
+    {
+      unsigned i, besti = 0;
+      grub_uint64_t bestval = 0;
+      grub_dprintf("zfs", "Subvol has %d keys\n", (int) data->subvol.nkeys);
+      for (i = 0; i < data->subvol.nkeys; i++)
+	if (data->subvol.keyring[i].txg <= bp->blk_birth
+	    && data->subvol.keyring[i].txg > bestval)
+	  {
+	    besti = i;
+	    bestval = data->subvol.keyring[i].txg;
+	  }
+      if (bestval == 0)
 	{
 	  grub_free (compbuf);
 	  *buf = NULL;
-	  return err;
+	  grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
+			bp->blk_birth);
+	  return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
 	}
+      grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T
+		    ", %p) for txg %" PRIxGRUB_UINT64_T "\n",
+		    besti, data->subvol.keyring[besti].txg,
+		    data->subvol.keyring[besti].cipher,
+		    bp->blk_birth);
+      err = grub_zfs_decrypt->decrypt_oracle (data->subvol.keyring[besti].cipher,
+					      data->subvol.keyring[besti].algo,
+					      &(bp)->blk_dva[2],
+					      compbuf, psize, zc.zc_mac,
+					      BP_GET_BYTEORDER(bp));
+    }
+  if (err)
+    {
+      grub_free (compbuf);
+      *buf = NULL;
+      return err;
     }
 
   if (comp != ZIO_COMPRESS_OFF)
@@ -2254,6 +2530,7 @@ zio_read (const blkptr_t *bp, void **buf,
 
       err = decomp_table[comp].decomp_func (compbuf, *buf, psize, lsize);
       grub_free (compbuf);
+      compbuf = NULL;
       if (err)
 	{
 	  grub_free (*buf);
@@ -2273,6 +2550,51 @@ zio_read (const blkptr_t *bp, void **buf,
 	grub_zfs_byteswap_type(*buf, lsize, BP_GET_TYPE(bp));
     }
 
+  if (!BP_IS_EMBEDDED(bp) && datto_authenticated && BP_GET_LEVEL(bp) > 0)
+    {
+      grub_uint64_t hash[8];
+      char *aad = grub_malloc(lsize);
+      grub_size_t aad_offset = 0;
+      if (!aad)
+	{
+	  grub_free (*buf);
+	  *buf = NULL;
+	  return grub_errno;
+	}
+      for (unsigned i = 0; i < lsize / sizeof(blkptr_t); i++)
+	add_blkptr_to_aad (aad, &aad_offset, ((blkptr_t *)*buf)[i], BP_GET_BYTEORDER(bp), 1);
+      grub_crypto_hash(GRUB_MD_SHA512, hash, aad, aad_offset);
+      grub_free(aad);
+
+      if (!GRUB_ZFS_IS_NATIVE_BYTEORDER(BP_GET_BYTEORDER(bp)))
+	{
+	  grub_zfs_byteswap_u64(&hash[0]);
+	  grub_zfs_byteswap_u64(&hash[1]);
+	}      
+
+      if (grub_crypto_memcmp(&zc.zc_word[2], hash, 8) != 0)
+	{
+	  grub_free (*buf);
+          *buf = NULL;
+	  grub_dprintf ("zfs", "actual hash "
+			"%016llx %016llx %016llx %016llx %016llx %016llx %016llx %016llx \n",
+			(unsigned long long) hash[0],
+			(unsigned long long) hash[1],
+			(unsigned long long) hash[2],
+			(unsigned long long) hash[3],
+			(unsigned long long) hash[4],
+			(unsigned long long) hash[5],
+			(unsigned long long) hash[6],
+			(unsigned long long) hash[7]);
+	  grub_dprintf ("zfs", "expected hash %016llx %016llx %016llx %016llx\n",
+			(unsigned long long) zc.zc_word[0],
+			(unsigned long long) zc.zc_word[1],
+			(unsigned long long) zc.zc_word[2],
+			(unsigned long long) zc.zc_word[3]);
+	  return grub_error (GRUB_ERR_BAD_FS, N_("hash verification failed"));
+	}
+    }
+
   return GRUB_ERR_NONE;
 }
 
@@ -3519,8 +3841,8 @@ load_zap_key (const void *name, grub_size_t namelen, const void *val_in,
   ctx->subvol->keyring[ctx->keyn].algo =
     grub_le_to_cpu64 (*(grub_uint64_t *) val_in);
   ctx->subvol->keyring[ctx->keyn].cipher =
-    grub_zfs_load_key (val_in, nelem, ctx->salt,
-		       ctx->subvol->keyring[ctx->keyn].algo);
+    grub_zfs_decrypt->load_key_oracle (val_in, nelem, ctx->salt,
+				       ctx->subvol->keyring[ctx->keyn].algo);
   ctx->keyn++;
   return 0;
 }
@@ -3587,6 +3909,82 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
 
   grub_dprintf ("zfs", "alive\n");
 
+  grub_uint64_t crypt_obj;
+
+  err = zap_lookup (dn, "com.datto:crypto_key_obj", &crypt_obj, data, 0);
+  if (err)
+    {
+      grub_errno = GRUB_ERR_NONE;
+      crypt_obj = 0;
+    }
+
+  grub_dprintf("zfs", "crypt obj = %lld\n", (long long) crypt_obj);
+  subvol->is_datto_encrypted = crypt_obj != 0;
+
+  if (grub_zfs_decrypt && crypt_obj)
+    {
+      dnode_phys_t crypt_dn;
+      err = dnode_get (&(data->mos), crypt_obj, 0xc4,
+		       &crypt_dn, data);
+      if (err)
+	{
+	  grub_free (fsname);
+	  grub_free (snapname);
+	  return err;
+	}
+
+      err = dnode_get (&(data->mos), crypt_obj, 0 /* ?? */,
+		       &crypt_dn, data);
+      struct grub_zfs_crypt_datto_data crypt_datto_data = { 0 };
+      grub_uint64_t pbkdf2iters = 0, pbkdf2salt = 0, guid = 0, algo = 0, version = 0;
+      zap_iterate (&crypt_dn, 1, fill_crypt_datto_data, &crypt_datto_data, data);
+
+      err = zap_lookup (&crypt_dn, "pbkdf2iters", &pbkdf2iters, data, 0);
+      if (err)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  pbkdf2iters = 0;
+	}
+
+      err = zap_lookup (&crypt_dn, "DSL_CRYPTO_GUID", &guid, data, 0);
+      if (err)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  guid = 0;
+	}
+
+      err = zap_lookup (&crypt_dn, "DSL_CRYPTO_SUITE", &algo, data, 0);
+      if (err)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  algo = 0;
+	}
+
+      err = zap_lookup (&crypt_dn, "DSL_CRYPTO_VERSION", &version, data, 0);
+      if (err)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  version = 0;
+	}
+
+      err = zap_lookup (&crypt_dn, "pbkdf2salt", &pbkdf2salt, data, 0);
+      if (err)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  pbkdf2salt = 0;
+	}
+
+      pbkdf2salt = grub_cpu_to_le64(pbkdf2salt);
+
+      subvol->key_datto = grub_zfs_decrypt->load_key_datto(crypt_datto_data.iv, crypt_datto_data.ivlen,
+							   crypt_datto_data.mac, crypt_datto_data.maclen,
+							   crypt_datto_data.master, crypt_datto_data.masterlen,
+							   crypt_datto_data.hmac, crypt_datto_data.hmaclen,
+							   (const grub_uint8_t *) &pbkdf2salt, sizeof (pbkdf2salt),
+							   pbkdf2iters, guid, algo, version);
+    }
+
+
   headobj = ((dsl_dir_phys_t *) DN_BONUS (dn))->dd_head_dataset_obj;
 
   err = dnode_get (&(data->mos), headobj, 0, &subvol->mdn, data);
@@ -3600,7 +3998,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
 
   grub_dprintf("zfs", "keychain obj = %lld\n", (long long) keychainobj);
 
-  if (grub_zfs_decrypt && keychainobj)
+  if (grub_zfs_decrypt && !subvol->is_datto_encrypted && keychainobj)
     {
       struct dnode_get_fullpath_ctx ctx = {
 	.subvol = subvol,
diff --git a/grub-core/fs/zfs/zfscrypt.c b/grub-core/fs/zfs/zfscrypt.c
index 535fd4065..e202f4265 100644
--- a/grub-core/fs/zfs/zfscrypt.c
+++ b/grub-core/fs/zfs/zfscrypt.c
@@ -46,8 +46,9 @@
 GRUB_MOD_LICENSE ("GPLv3+");
 
 /*
-  Mostly based on following article:
+  Oracle part is mostly based on following article:
   https://blogs.oracle.com/darren/entry/zfs_encryption_what_is_on
+  Datto part is based on comments in OpenZFS. No actal code has been used
  */
 
 enum grub_zfs_algo
@@ -56,7 +57,26 @@ enum grub_zfs_algo
     GRUB_ZFS_ALGO_GCM,
   };
 
-struct grub_zfs_key
+struct datto_algo {
+  grub_size_t wrapkeylen;
+  grub_size_t cryptkeylen;
+  grub_size_t masterkeylen;
+  enum grub_zfs_algo used_algo;
+} datto_algos[] = {
+  { 0 },
+  { 0 },
+  { 0 },
+  { 32, 16, 16, GRUB_ZFS_ALGO_CCM },
+  { 32, 24, 24, GRUB_ZFS_ALGO_CCM },
+  { 32, 32, 32, GRUB_ZFS_ALGO_CCM },
+  { 32, 16, 16, GRUB_ZFS_ALGO_GCM },
+  { 32, 24, 24, GRUB_ZFS_ALGO_GCM },
+  { 32, 32, 32, GRUB_ZFS_ALGO_GCM },
+};
+#define MAX_WRAPCIPHERTEXTLEN 96
+#define MAX_MACLEN 16
+
+struct grub_zfs_key_oracle
 {
   grub_uint64_t algo;
   grub_uint8_t enc_nonce[13];
@@ -102,26 +122,81 @@ grub_zfs_add_key (grub_uint8_t *key_in,
 
 static gcry_err_code_t
 grub_ccm_decrypt (grub_crypto_cipher_handle_t cipher,
-		  grub_uint8_t *out, const grub_uint8_t *in,
-		  grub_size_t psize,
+		  grub_uint8_t *out,
+		  const grub_uint8_t *in, grub_size_t psize,
+		  const grub_uint8_t *aad, grub_size_t aadsize,
 		  void *mac_out, const void *nonce,
-		  unsigned l, unsigned m)
+		  unsigned noncelen, unsigned m)
 {
   grub_uint8_t iv[16];
   grub_uint8_t mul[16];
   grub_uint32_t mac[4];
-  unsigned i, j;
+  unsigned i, j, l = 15 - noncelen, aprefixlen = 0;
   gcry_err_code_t err;
+  grub_uint8_t aprefix[16] = { 0 };
 
-  grub_memcpy (iv + 1, nonce, 15 - l);
+  grub_memcpy (iv + 1, nonce, noncelen);
 
-  iv[0] = (l - 1) | (((m-2) / 2) << 3);
+  iv[0] = (l - 1) | (((m-2) / 2) << 3) | ((aadsize != 0) << 6);
   for (j = 0; j < l; j++)
     iv[15 - j] = psize >> (8 * j);
   err = grub_crypto_ecb_encrypt (cipher, mac, iv, 16);
   if (err)
     return err;
 
+  if (aadsize == 0)
+    aprefixlen = 0;
+  else if (aadsize <= 0xFEFF)
+    {
+      aprefixlen = 2;
+      aprefix[0] = aadsize >> 8;
+      aprefix[1] = aadsize;
+    }
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+  else if ((aadsize >> 32) == 0)
+#endif
+    {
+      aprefixlen = 6;
+      aprefix[0] = 0xff;
+      aprefix[1] = 0xfe;
+      grub_set_unaligned32(aprefix + 2, grub_cpu_to_be32(aadsize));
+    }
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+  else
+    {
+      aprefixlen = 10;
+      aprefix[0] = 0xff;
+      aprefix[1] = 0xff;
+      grub_set_unaligned64(aprefix + 2, grub_cpu_to_be64(aadsize));
+    }
+#endif
+
+  if (aadsize != 0)
+    {
+      grub_size_t ablocks = (aadsize + aprefixlen + 15) / 16;
+      grub_size_t first_block_datalen = 16 - aprefixlen;
+      if (first_block_datalen > aadsize)
+	first_block_datalen = aadsize;
+      grub_memcpy (aprefix + aprefixlen, aad, first_block_datalen);
+
+      grub_crypto_xor (mac, mac, aprefix, 16);
+      err = grub_crypto_ecb_encrypt (cipher, mac, mac, 16);
+      if (err)
+	return err;
+
+      for (i = 1; i < ablocks; i++)
+	{
+	  grub_size_t csize, instart = (i - 1) * 16 + first_block_datalen;
+	  csize = 16;
+	  if (csize > aadsize - instart)
+	    csize = aadsize - instart;
+	  grub_crypto_xor (mac, mac, aad + instart, csize);
+	  err = grub_crypto_ecb_encrypt (cipher, mac, mac, 16);
+	  if (err)
+	    return err;
+	}
+    }
+
   iv[0] = l - 1;
 
   for (i = 0; i < (psize + 15) / 16; i++)
@@ -187,6 +262,8 @@ static gcry_err_code_t
 grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher,
 		  grub_uint8_t *out, const grub_uint8_t *in,
 		  grub_size_t psize,
+		  const grub_uint8_t *aad,
+		  grub_size_t aadsize,
 		  void *mac_out, const void *nonce,
 		  unsigned nonce_len, unsigned m)
 {
@@ -213,7 +290,7 @@ grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher,
   else
     {
       grub_memset (iv, 0, sizeof (iv));
-      grub_memcpy (iv, nonce, nonce_len);
+      grub_memcpy (iv, nonce, nonce_len > sizeof (iv) ? sizeof (iv) : nonce_len);
       grub_gcm_mul (iv, h);
       iv[15] ^= nonce_len * 8;
       grub_gcm_mul (iv, h);
@@ -223,6 +300,16 @@ grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher,
   if (err)
     return err;
 
+  for (i = 0; i < (aadsize + 15) / 16; i++)
+    {
+      grub_size_t csize;
+      csize = 16;
+      if (csize > aadsize - 16 * i)
+	csize = aadsize - 16 * i;
+      grub_crypto_xor (mac, mac, aad + 16 * i, csize);
+      grub_gcm_mul (mac, h);
+    }
+
   for (i = 0; i < (psize + 15) / 16; i++)
     {
       grub_size_t csize;
@@ -244,6 +331,8 @@ grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher,
     }
   for (j = 0; j < 8; j++)
     mac[15 - j] ^= ((((grub_uint64_t) psize) * 8) >> (8 * j));
+  for (j = 0; j < 8; j++)
+    mac[7 - j] ^= ((((grub_uint64_t) aadsize) * 8) >> (8 * j));
   grub_gcm_mul (mac, h);
 
   if (mac_out)
@@ -257,30 +346,33 @@ static gcry_err_code_t
 algo_decrypt (grub_crypto_cipher_handle_t cipher, grub_uint64_t algo,
 	      grub_uint8_t *out, const grub_uint8_t *in,
 	      grub_size_t psize,
+	      const grub_uint8_t *aad,
+	      grub_size_t aadsize,
 	      void *mac_out, const void *nonce,
 	      unsigned l, unsigned m)
 {
   switch (algo)
     {
-    case 0:
+    case GRUB_ZFS_ALGO_CCM:
       return grub_ccm_decrypt (cipher, out, in, psize,
-			       mac_out, nonce, l, m);
-    case 1:
+			       aad, aadsize, mac_out, nonce,
+			       l <= 15 ? l : 0, m);
+    case GRUB_ZFS_ALGO_GCM:
       return grub_gcm_decrypt (cipher, out, in, psize,
-			       mac_out, nonce,
-			       15 - l, m);
+			       aad, aadsize, mac_out, nonce,
+			       l <= 15 ? l : 0, m);
     default:
       return GPG_ERR_CIPHER_ALGO;
     }
 }
 
 static grub_err_t
-grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher,
-		       grub_uint64_t algo,
-		       const void *nonce,
-		       char *buf, grub_size_t size,
-		       const grub_uint32_t *expected_mac,
-		       grub_zfs_endian_t endian)
+grub_zfs_decrypt_oracle (grub_crypto_cipher_handle_t cipher,
+			 grub_uint64_t algo,
+			 const void *nonce,
+			 char *buf, grub_size_t size,
+			 const grub_uint32_t *expected_mac,
+			 grub_zfs_endian_t endian)
 {
   grub_uint32_t mac[4];
   unsigned i;
@@ -298,8 +390,8 @@ grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher,
   err = algo_decrypt (cipher, algo,
 		      (grub_uint8_t *) buf,
 		      (grub_uint8_t *) buf,
-		      size, mac,
-		      sw + 1, 3, 12);
+		      size, NULL, 0, mac,
+		      sw, 12, 12);
   if (err)
     return grub_crypto_gcry_error (err);
 
@@ -310,11 +402,73 @@ grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher,
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+grub_zfs_decrypt_datto (const struct grub_zfs_datto_key *key,
+			const grub_uint32_t *nonce, grub_uint64_t salt,
+			char *buf, grub_size_t size,
+			const char *aadbuf, grub_size_t aadsize,
+			const grub_uint64_t *expected_mac,
+			grub_zfs_endian_t endian)
+{
+  grub_uint64_t mac[2];
+  unsigned i;
+  grub_uint8_t extractkey[64];
+  grub_uint8_t expandkey[64];
+  grub_uint8_t t[9];
+  gcry_err_code_t err;
+  grub_crypto_cipher_handle_t cipher;
+
+  if (!key)
+    return grub_error (GRUB_ERR_ACCESS_DENIED,
+		       N_("no decryption key available"));
+
+  /* A special case of HKDF with 512 > keylen.  */
+  err = grub_crypto_hmac_buffer (GRUB_MD_SHA512,
+				 "", 0,
+				 key->master_key, key->master_keylen,
+				 extractkey);
+  if (err)
+    return grub_crypto_gcry_error (err);
+
+  grub_set_unaligned64(t, grub_cpu_to_zfs64(salt, endian));
+  t[8] = 1;
+  err = grub_crypto_hmac_buffer (GRUB_MD_SHA512,
+				 extractkey, 64,
+				 t, 9, expandkey);
+  if (err)
+    return grub_crypto_gcry_error (err);
+
+  cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES);
+  if (!cipher)
+    return grub_crypto_gcry_error (err);
+  err = grub_crypto_cipher_set_key (cipher, expandkey, datto_algos[key->algo].cryptkeylen);
+  if (err)
+    {
+      grub_crypto_cipher_close (cipher);
+      return grub_crypto_gcry_error (err);
+    }
+
+  err = algo_decrypt (cipher, datto_algos[key->algo].used_algo,
+		      (grub_uint8_t *) buf,
+		      (grub_uint8_t *) buf,
+		      size, (grub_uint8_t *) aadbuf, aadsize, mac,
+		      nonce, 12, 16);
+  grub_crypto_cipher_close (cipher);
+  if (err)
+    return grub_crypto_gcry_error (err);
+
+  for (i = 0; i < 2; i++)
+    if (grub_zfs_to_cpu64 (expected_mac[i], endian)
+  	!= grub_le_to_cpu64 (mac[i]))
+      grub_dprintf("zfs", N_("MAC verification failed"));
+  return GRUB_ERR_NONE;
+}
+
 static grub_crypto_cipher_handle_t
-grub_zfs_load_key_real (const struct grub_zfs_key *key,
-			grub_size_t keysize,
-			grub_uint64_t salt,
-			grub_uint64_t algo)
+grub_zfs_load_key_oracle (const struct grub_zfs_key_oracle *key,
+			  grub_size_t keysize,
+			  grub_uint64_t salt,
+			  grub_uint64_t algo)
 {
   unsigned keylen;
   struct grub_zfs_wrap_key *wrap_key;
@@ -373,7 +527,7 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key,
 	}
 
       err = algo_decrypt (cipher, algo, decrypted, key->unknown_purpose_key, 32,
-			  mac, key->unknown_purpose_nonce, 2, 16);
+			  NULL, 0, mac, key->unknown_purpose_nonce, 13, 16);
       if (err || (grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16)
 		  != 0))
 	{
@@ -383,8 +537,8 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key,
 	  continue;
 	}
 
-      err = algo_decrypt (cipher, algo, decrypted, key->enc_key, keylen, mac,
-			  key->enc_nonce, 2, 16);
+      err = algo_decrypt (cipher, algo, decrypted, key->enc_key, keylen, NULL, 0,
+			  mac, key->enc_nonce, 13, 16);
       if (err || grub_crypto_memcmp (mac, key->enc_key + keylen, 16) != 0)
 	{
 	  grub_dprintf ("zfs", "key loading failed\n");
@@ -413,6 +567,119 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key,
   return NULL;
 }
 
+static struct grub_zfs_datto_key
+grub_zfs_load_key_datto (const grub_uint8_t *iv, grub_size_t ivlen,
+			 const grub_uint8_t *mac_in, grub_size_t mac_inlen,
+			 const grub_uint8_t *master, grub_size_t masterlen,
+			 const grub_uint8_t *hmac, grub_size_t hmaclen,
+			 const grub_uint8_t *pbkdf2salt, grub_size_t pbkdf2saltlen,
+			 grub_uint64_t pbkdf2iters, grub_uint64_t guid, grub_uint64_t algo, grub_uint64_t version)
+{
+  struct grub_zfs_wrap_key *wrap_key;
+  struct grub_zfs_datto_key ret = { 0 };
+
+  if (algo <= 2 || algo >= ARRAY_SIZE(datto_algos))
+    {
+      grub_error(GRUB_ERR_BAD_FS, "unsupported crypto algo");
+      return ret;
+    }
+
+  /* Note: if last two ever become variable they need to be checkecked as matching algo. Especially mac_inlen.  */
+  if (masterlen < datto_algos[algo].masterkeylen || hmaclen != 64 || mac_inlen != 16)
+    {
+      grub_error(GRUB_ERR_BAD_FS, "crypto keys are invalid");
+      return ret;
+    }
+
+  masterlen = datto_algos[algo].masterkeylen;
+
+  grub_uint8_t ciphertext[MAX_WRAPCIPHERTEXTLEN];
+  grub_uint8_t plaintext[MAX_WRAPCIPHERTEXTLEN];
+  grub_uint8_t mac_computed[MAX_MACLEN];
+  struct {
+    grub_uint64_t guid;
+    grub_uint64_t algo;
+    grub_uint64_t version;
+  } aad = {
+    grub_cpu_to_le64(guid),
+    grub_cpu_to_le64(algo),
+    grub_cpu_to_le64(version)
+  };
+
+  grub_memcpy(ciphertext, master, masterlen);
+  grub_memcpy(ciphertext + masterlen, hmac, hmaclen);
+
+  for (wrap_key = zfs_wrap_keys; wrap_key; wrap_key = wrap_key->next)
+    {
+      grub_crypto_cipher_handle_t cipher;
+      grub_uint8_t wrap_key_real[32] = { 0 };
+      gcry_err_code_t err = 0;
+
+      cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES);
+      if (!cipher)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  return ret;
+	}
+      grub_memset (wrap_key_real, 0, sizeof (wrap_key_real));
+
+      if (!wrap_key->is_passphrase)
+	grub_memcpy(wrap_key_real, wrap_key->key,
+		    wrap_key->keylen < datto_algos[algo].wrapkeylen ? wrap_key->keylen : datto_algos[algo].wrapkeylen);
+      else
+	// TODO: PBKDF2
+	err = grub_crypto_pbkdf2 (GRUB_MD_SHA1,
+				  (const grub_uint8_t *) wrap_key->key,
+				  wrap_key->keylen,
+				  pbkdf2salt, pbkdf2saltlen,
+				  pbkdf2iters, wrap_key_real, datto_algos[algo].wrapkeylen);
+
+      if (err)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  grub_crypto_cipher_close (cipher);
+	  continue;
+	}
+
+      err = grub_crypto_cipher_set_key (cipher, wrap_key_real, datto_algos[algo].wrapkeylen);
+      if (err)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  grub_crypto_cipher_close (cipher);
+	  continue;
+	}
+
+      err = algo_decrypt (cipher, datto_algos[algo].used_algo,
+			  plaintext, ciphertext, masterlen + hmaclen,
+			  (grub_uint8_t *) &aad, sizeof(aad),
+			  mac_computed, iv, ivlen, mac_inlen);
+      if (err || (grub_crypto_memcmp (mac_computed, mac_in, mac_inlen) != 0))
+	{
+	  grub_dprintf ("zfs", "key loading failed\n");
+	  grub_errno = GRUB_ERR_NONE;
+	  grub_crypto_cipher_close (cipher);
+	  continue;
+	}
+
+      ret.master_keylen = masterlen;
+      ret.master_key = grub_malloc(ret.master_keylen);
+      if (!ret.master_key)
+	return ret;
+      grub_memcpy(ret.master_key, plaintext, masterlen);
+      ret.hmac_key = grub_malloc(hmaclen);
+      if (!ret.hmac_key)
+	{
+	  grub_free(ret.master_key);
+	  ret.master_key = NULL;
+	  return ret;
+	}
+      ret.algo = algo;
+      grub_memcpy(ret.hmac_key, plaintext + masterlen, hmaclen);
+      return ret;
+    }
+  return ret;
+}
+
 static const struct grub_arg_option options[] =
   {
     {"raw", 'r', 0, N_("Assume input is raw."), 0, 0},
@@ -471,12 +738,17 @@ grub_cmd_zfs_key (grub_extcmd_context_t ctxt, int argc, char **args)
 			       && !ctxt->state[1].set));
 }
 
+struct grub_zfs_decryptor grub_zfs_decrypt_real = {
+  .decrypt_oracle = grub_zfs_decrypt_oracle,
+  .decrypt_datto = grub_zfs_decrypt_datto,
+  .load_key_oracle = grub_zfs_load_key_oracle,
+  .load_key_datto = grub_zfs_load_key_datto,
+};
 static grub_extcmd_t cmd_key;
 
 GRUB_MOD_INIT(zfscrypt)
 {
-  grub_zfs_decrypt = grub_zfs_decrypt_real;
-  grub_zfs_load_key = grub_zfs_load_key_real;
+  grub_zfs_decrypt = &grub_zfs_decrypt_real;
   cmd_key = grub_register_extcmd ("zfskey", grub_cmd_zfs_key, 0,
 				  N_("[-h|-p|-r] [FILE]"),
 				  N_("Import ZFS wrapping key stored in FILE."),
@@ -486,6 +758,5 @@ GRUB_MOD_INIT(zfscrypt)
 GRUB_MOD_FINI(zfscrypt)
 {
   grub_zfs_decrypt = 0;
-  grub_zfs_load_key = 0;
   grub_unregister_extcmd (cmd_key);
 }
diff --git a/include/grub/zfs/zfs.h b/include/grub/zfs/zfs.h
index 0c5c40a73..fbac368b0 100644
--- a/include/grub/zfs/zfs.h
+++ b/include/grub/zfs/zfs.h
@@ -147,20 +147,46 @@ grub_zfs_add_key (grub_uint8_t *key_in,
 		  grub_size_t keylen,
 		  int passphrase);
 
-extern grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
-				       grub_uint64_t algo,
-				       const void *nonce,
-				       char *buf, grub_size_t size,
-				       const grub_uint32_t *expected_mac,
-				       grub_zfs_endian_t endian);
-
-struct grub_zfs_key;
-
-extern 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);
-
+struct grub_zfs_key_oracle;
+
+struct grub_zfs_datto_key
+{
+  grub_uint8_t *master_key;
+  grub_size_t master_keylen;
+  grub_uint8_t *hmac_key;
+  grub_uint64_t algo;
+};
+
+struct grub_zfs_decryptor
+{
+  grub_err_t (*decrypt_oracle) (grub_crypto_cipher_handle_t cipher,
+				grub_uint64_t algo,
+				const void *nonce,
+				char *buf, grub_size_t size,
+				const grub_uint32_t *expected_mac,
+				grub_zfs_endian_t endian);
+
+  grub_crypto_cipher_handle_t (*load_key_oracle) (const struct grub_zfs_key_oracle *key,
+						  grub_size_t keysize,
+						  grub_uint64_t salt,
+						  grub_uint64_t algo);
+
+  grub_err_t (*decrypt_datto) (const struct grub_zfs_datto_key *key,
+			       const grub_uint32_t *nonce, grub_uint64_t salt,
+			       char *buf, grub_size_t size,
+			       const char *aadbuf, grub_size_t aadsize,
+			       const grub_uint64_t *expected_mac,
+			       grub_zfs_endian_t endian);
+
+  struct grub_zfs_datto_key (*load_key_datto) (const grub_uint8_t *iv, grub_size_t ivlen,
+					       const grub_uint8_t *mac, grub_size_t maclen,
+					       const grub_uint8_t *master, grub_size_t masterlen,
+					       const grub_uint8_t *hmac, grub_size_t hmaclen,
+					       const grub_uint8_t *pbkdf2salt, grub_size_t pbkdf2saltlen,
+					       grub_uint64_t pbkdf2iters, grub_uint64_t guid, grub_uint64_t algo, grub_uint64_t version);
+};
+
+extern struct grub_zfs_decryptor *grub_zfs_decrypt;
 
 
 #endif	/* ! GRUB_ZFS_HEADER */
-- 
2.49.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/2] zfs: Support datto encryption and fix endianness handling
  2025-08-01 14:30 [PATCH 0/2] zfs: Support datto encryption and fix endianness handling Vladimir Serbinenko
  2025-08-01 14:30 ` [PATCH 1/2] zfs: Rewrite " Vladimir Serbinenko
  2025-08-01 14:30 ` [PATCH 2/2] zfs: Support datto encryption Vladimir Serbinenko
@ 2025-08-11 13:23 ` Neal Gompa
  2025-08-11 19:18   ` Toomas Soome via Grub-devel
  2 siblings, 1 reply; 8+ messages in thread
From: Neal Gompa @ 2025-08-11 13:23 UTC (permalink / raw)
  To: The development of GNU GRUB

On Fri, Aug 1, 2025 at 7:37 AM Vladimir Serbinenko <phcoder@gmail.com> wrote:
>
> With these patches I'm able to read my ZFS partition with encrypted datasets
> and a big-endian volume even with mixed endianness contents
>

Well, I certainly didn't expect to see this crop up all these years later. :)

(I used to work at Datto.)



-- 
真実はいつも一つ!/ Always, there's only one truth!

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] zfs: Support datto encryption
  2025-08-01 14:30 ` [PATCH 2/2] zfs: Support datto encryption Vladimir Serbinenko
@ 2025-08-11 13:48   ` Neal Gompa
  2025-08-11 14:13     ` Vladimir 'phcoder' Serbinenko
  0 siblings, 1 reply; 8+ messages in thread
From: Neal Gompa @ 2025-08-11 13:48 UTC (permalink / raw)
  To: The development of GNU GRUB; +Cc: Vladimir Serbinenko

On Fri, Aug 1, 2025 at 7:41 AM Vladimir Serbinenko <phcoder@gmail.com> wrote:
>
> Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
> ---
>  grub-core/fs/zfs/zfs.c      | 502 ++++++++++++++++++++++++++++++++----
>  grub-core/fs/zfs/zfscrypt.c | 335 +++++++++++++++++++++---
>  include/grub/zfs/zfs.h      |  54 +++-
>  3 files changed, 793 insertions(+), 98 deletions(-)
>
> diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
> index b88a2b032..da1661680 100644
> --- a/grub-core/fs/zfs/zfs.c
> +++ b/grub-core/fs/zfs/zfs.c
> @@ -227,6 +227,10 @@ struct subvolume
>      grub_uint64_t txg;
>      grub_uint64_t algo;
>    } *keyring;
> +
> +  struct grub_zfs_datto_key key_datto;
> +
> +  int is_datto_encrypted;
>  };

Do we really need to call this "datto ZFS encryption"? It's been a
while since I was engaged in OpenZFS development, but I'm pretty sure
it's still the standard ZFS native encryption for OpenZFS.


-- 
真実はいつも一つ!/ Always, there's only one truth!

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] zfs: Support datto encryption
  2025-08-11 13:48   ` Neal Gompa
@ 2025-08-11 14:13     ` Vladimir 'phcoder' Serbinenko
  2025-08-12 16:10       ` Neal Gompa
  0 siblings, 1 reply; 8+ messages in thread
From: Vladimir 'phcoder' Serbinenko @ 2025-08-11 14:13 UTC (permalink / raw)
  To: Neal Gompa; +Cc: The development of GNU GRUB


[-- Attachment #1.1: Type: text/plain, Size: 1408 bytes --]

Le lun. 11 août 2025, 16:49, Neal Gompa <ngompa13@gmail.com> a écrit :

> On Fri, Aug 1, 2025 at 7:41 AM Vladimir Serbinenko <phcoder@gmail.com>
> wrote:
> >
> > Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
> > ---
> >  grub-core/fs/zfs/zfs.c      | 502 ++++++++++++++++++++++++++++++++----
> >  grub-core/fs/zfs/zfscrypt.c | 335 +++++++++++++++++++++---
> >  include/grub/zfs/zfs.h      |  54 +++-
> >  3 files changed, 793 insertions(+), 98 deletions(-)
> >
> > diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
> > index b88a2b032..da1661680 100644
> > --- a/grub-core/fs/zfs/zfs.c
> > +++ b/grub-core/fs/zfs/zfs.c
> > @@ -227,6 +227,10 @@ struct subvolume
> >      grub_uint64_t txg;
> >      grub_uint64_t algo;
> >    } *keyring;
> > +
> > +  struct grub_zfs_datto_key key_datto;
> > +
> > +  int is_datto_encrypted;
> >  };
>
> Do we really need to call this "datto ZFS encryption"? It's been a
> while since I was engaged in OpenZFS development, but I'm pretty sure
> it's still the standard ZFS native encryption for OpenZFS.
>
We need to call it something. There are 2 ways of encrypting: Oracle and
Datto. And we need to distinguish then somehow. Given that "datto" is part
of its feature name I thought it's a good possibility for naming it in this
patch.

>
>
> --
> 真実はいつも一つ!/ Always, there's only one truth!
>

[-- Attachment #1.2: Type: text/html, Size: 2258 bytes --]

[-- Attachment #2: Type: text/plain, Size: 141 bytes --]

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/2] zfs: Support datto encryption and fix endianness handling
  2025-08-11 13:23 ` [PATCH 0/2] zfs: Support datto encryption and fix endianness handling Neal Gompa
@ 2025-08-11 19:18   ` Toomas Soome via Grub-devel
  0 siblings, 0 replies; 8+ messages in thread
From: Toomas Soome via Grub-devel @ 2025-08-11 19:18 UTC (permalink / raw)
  To: The development of GNU GRUB; +Cc: Toomas Soome


[-- Attachment #1.1: Type: text/plain, Size: 669 bytes --]



> On 11. Aug 2025, at 16:23, Neal Gompa <ngompa13@gmail.com> wrote:
> 
> On Fri, Aug 1, 2025 at 7:37 AM Vladimir Serbinenko <phcoder@gmail.com> wrote:
>> 
>> With these patches I'm able to read my ZFS partition with encrypted datasets
>> and a big-endian volume even with mixed endianness contents
>> 
> 
> Well, I certainly didn't expect to see this crop up all these years later. :)
> 
> (I used to work at Datto.)
> 

The feature is marked as "com.datto:encryption”, so in that sense its quite correct to use that name. Although technically it is OpenZFS versus Oracle ZFS (if anyone wants to make a distinction).

my 2 cents.

rgds,
toomas

[-- Attachment #1.2: Type: text/html, Size: 1637 bytes --]

[-- Attachment #2: Type: text/plain, Size: 141 bytes --]

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] zfs: Support datto encryption
  2025-08-11 14:13     ` Vladimir 'phcoder' Serbinenko
@ 2025-08-12 16:10       ` Neal Gompa
  0 siblings, 0 replies; 8+ messages in thread
From: Neal Gompa @ 2025-08-12 16:10 UTC (permalink / raw)
  To: Vladimir 'phcoder' Serbinenko; +Cc: The development of GNU GRUB

On Mon, Aug 11, 2025 at 7:13 AM Vladimir 'phcoder' Serbinenko
<phcoder@gmail.com> wrote:
>
>
>
> Le lun. 11 août 2025, 16:49, Neal Gompa <ngompa13@gmail.com> a écrit :
>>
>> On Fri, Aug 1, 2025 at 7:41 AM Vladimir Serbinenko <phcoder@gmail.com> wrote:
>> >
>> > Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
>> > ---
>> >  grub-core/fs/zfs/zfs.c      | 502 ++++++++++++++++++++++++++++++++----
>> >  grub-core/fs/zfs/zfscrypt.c | 335 +++++++++++++++++++++---
>> >  include/grub/zfs/zfs.h      |  54 +++-
>> >  3 files changed, 793 insertions(+), 98 deletions(-)
>> >
>> > diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
>> > index b88a2b032..da1661680 100644
>> > --- a/grub-core/fs/zfs/zfs.c
>> > +++ b/grub-core/fs/zfs/zfs.c
>> > @@ -227,6 +227,10 @@ struct subvolume
>> >      grub_uint64_t txg;
>> >      grub_uint64_t algo;
>> >    } *keyring;
>> > +
>> > +  struct grub_zfs_datto_key key_datto;
>> > +
>> > +  int is_datto_encrypted;
>> >  };
>>
>> Do we really need to call this "datto ZFS encryption"? It's been a
>> while since I was engaged in OpenZFS development, but I'm pretty sure
>> it's still the standard ZFS native encryption for OpenZFS.
>
> We need to call it something. There are 2 ways of encrypting: Oracle and Datto. And we need to distinguish then somehow. Given that "datto" is part of its feature name I thought it's a good possibility for naming it in this patch.

Yeah, this is probably fine, but it should be noted somehow that this
is the OpenZFS encryption feature.



-- 
真実はいつも一つ!/ Always, there's only one truth!

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-08-12 16:11 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-01 14:30 [PATCH 0/2] zfs: Support datto encryption and fix endianness handling Vladimir Serbinenko
2025-08-01 14:30 ` [PATCH 1/2] zfs: Rewrite " Vladimir Serbinenko
2025-08-01 14:30 ` [PATCH 2/2] zfs: Support datto encryption Vladimir Serbinenko
2025-08-11 13:48   ` Neal Gompa
2025-08-11 14:13     ` Vladimir 'phcoder' Serbinenko
2025-08-12 16:10       ` Neal Gompa
2025-08-11 13:23 ` [PATCH 0/2] zfs: Support datto encryption and fix endianness handling Neal Gompa
2025-08-11 19:18   ` Toomas Soome via Grub-devel

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.