1 /*
   2  *  GRUB  --  GRand Unified Bootloader
   3  *  Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011  Free Software Foundation, Inc.
   4  *  Copyright 2010  Sun Microsystems, Inc.
   5  *
   6  *  GRUB is free software; you can redistribute it and/or modify
   7  *  it under the terms of the GNU General Public License as published by
   8  *  the Free Software Foundation; either version 3 of the License, or
   9  *  (at your option) any later version.
  10  *
  11  *  GRUB is distributed in the hope that it will be useful,
  12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14  *  GNU General Public License for more details.
  15  *
  16  *  You should have received a copy of the GNU General Public License
  17  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  18  */
  19 /*
  20  * The zfs plug-in routines for GRUB are:
  21  *
  22  * zfs_mount() - locates a valid uberblock of the root pool and reads
  23  *              in its MOS at the memory address MOS.
  24  *
  25  * zfs_open() - locates a plain file object by following the MOS
  26  *              and places its dnode at the memory address DNODE.
  27  *
  28  * zfs_read() - read in the data blocks pointed by the DNODE.
  29  *
  30  */
  31 
  32 #include <grub/err.h>
  33 #include <grub/file.h>
  34 #include <grub/mm.h>
  35 #include <grub/misc.h>
  36 #include <grub/disk.h>
  37 #include <grub/partition.h>
  38 #include <grub/dl.h>
  39 #include <grub/types.h>
  40 #include <grub/zfs/zfs.h>
  41 #include <grub/zfs/zio.h>
  42 #include <grub/zfs/dnode.h>
  43 #include <grub/zfs/uberblock_impl.h>
  44 #include <grub/zfs/vdev_impl.h>
  45 #include <grub/zfs/zio_checksum.h>
  46 #include <grub/zfs/zap_impl.h>
  47 #include <grub/zfs/zap_leaf.h>
  48 #include <grub/zfs/zfs_znode.h>
  49 #include <grub/zfs/dmu.h>
  50 #include <grub/zfs/dmu_objset.h>
  51 #include <grub/zfs/sa_impl.h>
  52 #include <grub/zfs/dsl_dir.h>
  53 #include <grub/zfs/dsl_dataset.h>
  54 #include <grub/deflate.h>
  55 #include <grub/crypto.h>
  56 #include <grub/i18n.h>
  57 
  58 GRUB_MOD_LICENSE ("GPLv3+");
  59 
  60 #define ZPOOL_PROP_BOOTFS               "bootfs"
  61 
  62 /*
  63  * For nvlist manipulation. (from nvpair.h)
  64  */
  65 #define NV_ENCODE_NATIVE        0
  66 #define NV_ENCODE_XDR           1
  67 #define NV_BIG_ENDIAN           0
  68 #define NV_LITTLE_ENDIAN        1
  69 #define DATA_TYPE_UINT64        8
  70 #define DATA_TYPE_STRING        9
  71 #define DATA_TYPE_NVLIST        19
  72 #define DATA_TYPE_NVLIST_ARRAY  20
  73 
  74 #ifndef GRUB_UTIL
  75 static grub_dl_t my_mod;
  76 #endif
  77 
  78 #define P2PHASE(x, align)               ((x) & ((align) - 1))
  79 
  80 static inline grub_disk_addr_t
  81 DVA_OFFSET_TO_PHYS_SECTOR (grub_disk_addr_t offset)
  82 {
  83   return ((offset + VDEV_LABEL_START_SIZE) >> SPA_MINBLOCKSHIFT);
  84 }
  85 
  86 /*
  87  * FAT ZAP data structures
  88  */
  89 #define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL    /* ECMA-182, reflected form */
  90 static inline grub_uint64_t
  91 ZAP_HASH_IDX (grub_uint64_t hash, grub_uint64_t n)
  92 {
  93   return (((n) == 0) ? 0 : ((hash) >> (64 - (n))));
  94 }
  95 
  96 #define CHAIN_END       0xffff  /* end of the chunk chain */
  97 
  98 /*
  99  * The amount of space within the chunk available for the array is:
 100  * chunk size - space for type (1) - space for next pointer (2)
 101  */
 102 #define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3)
 103 
 104 static inline int
 105 ZAP_LEAF_HASH_SHIFT (int bs)
 106 {
 107   return bs - 5;
 108 }
 109 
 110 static inline int
 111 ZAP_LEAF_HASH_NUMENTRIES (int bs)
 112 {
 113   return 1 << ZAP_LEAF_HASH_SHIFT(bs);
 114 }
 115 
 116 static inline grub_size_t
 117 LEAF_HASH (int bs, grub_uint64_t h, zap_leaf_phys_t *l)
 118 {
 119   return ((ZAP_LEAF_HASH_NUMENTRIES (bs)-1)
 120           & ((h) >> (64 - ZAP_LEAF_HASH_SHIFT (bs) - l->l_hdr.lh_prefix_len)));
 121 }
 122 
 123 /*
 124  * The amount of space available for chunks is:
 125  * block size shift - hash entry size (2) * number of hash
 126  * entries - header space (2*chunksize)
 127  */
 128 static inline int
 129 ZAP_LEAF_NUMCHUNKS (int bs)
 130 {
 131   return (((1 << bs) - 2 * ZAP_LEAF_HASH_NUMENTRIES (bs)) /
 132           ZAP_LEAF_CHUNKSIZE - 2);
 133 }
 134 
 135 /*
 136  * The chunks start immediately after the hash table.  The end of the
 137  * hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a
 138  * chunk_t.
 139  */
 140 static inline zap_leaf_chunk_t *
 141 ZAP_LEAF_CHUNK (zap_leaf_phys_t *l, int bs, int idx)
 142 {
 143   return &((zap_leaf_chunk_t *) (l->l_entries 
 144                                  + (ZAP_LEAF_HASH_NUMENTRIES(bs) * 2)
 145                                  / sizeof (grub_properly_aligned_t)))[idx];
 146 }
 147 
 148 static inline struct zap_leaf_entry *
 149 ZAP_LEAF_ENTRY(zap_leaf_phys_t *l, int bs, int idx)
 150 {
 151   return &ZAP_LEAF_CHUNK(l, bs, idx)->l_entry;
 152 }
 153 
 154 
 155 /*
 156  * Decompression Entry - lzjb
 157  */
 158 
 159 extern grub_err_t lzjb_decompress (void *, void *, grub_size_t, grub_size_t);
 160 
 161 typedef grub_err_t zfs_decomp_func_t (void *s_start, void *d_start,
 162                                       grub_size_t s_len, grub_size_t d_len);
 163 typedef struct decomp_entry
 164 {
 165   const char *name;
 166   zfs_decomp_func_t *decomp_func;
 167 } decomp_entry_t;
 168 
 169 /*
 170  * Signature for checksum functions.
 171  */
 172 typedef void zio_checksum_t(const void *data, grub_uint64_t size, 
 173                             grub_zfs_endian_t endian, zio_cksum_t *zcp);
 174 
 175 /*
 176  * Information about each checksum function.
 177  */
 178 typedef struct zio_checksum_info {
 179         zio_checksum_t  *ci_func; /* checksum function for each byteorder */
 180         int             ci_correctable; /* number of correctable bits   */
 181         int             ci_eck;         /* uses zio embedded checksum? */
 182         const char              *ci_name;       /* descriptive name */
 183 } zio_checksum_info_t;
 184 
 185 typedef struct dnode_end
 186 {
 187   dnode_phys_t dn;
 188   grub_zfs_endian_t endian;
 189 } dnode_end_t;
 190 
 191 struct grub_zfs_device_desc
 192 {
 193   enum { DEVICE_LEAF, DEVICE_MIRROR, DEVICE_RAIDZ } type;
 194   grub_uint64_t id;
 195   grub_uint64_t guid;
 196   unsigned ashift;
 197   unsigned max_children_ashift;
 198 
 199   /* Valid only for non-leafs.  */
 200   unsigned n_children;
 201   struct grub_zfs_device_desc *children;
 202 
 203   /* Valid only for RAIDZ.  */
 204   unsigned nparity;
 205 
 206   /* Valid only for leaf devices.  */
 207   grub_device_t dev;
 208   grub_disk_addr_t vdev_phys_sector;
 209   uberblock_t current_uberblock;
 210   int original;
 211 };
 212 
 213 struct subvolume
 214 {
 215   dnode_end_t mdn;
 216   grub_uint64_t obj;
 217   grub_uint64_t case_insensitive;
 218   grub_size_t nkeys;
 219   struct
 220   {
 221     grub_crypto_cipher_handle_t cipher;
 222     grub_uint64_t txg;
 223     grub_uint64_t algo;
 224   } *keyring;
 225 };
 226 
 227 struct grub_zfs_data
 228 {
 229   /* cache for a file block of the currently zfs_open()-ed file */
 230   char *file_buf;
 231   grub_uint64_t file_start;
 232   grub_uint64_t file_end;
 233 
 234   /* cache for a dnode block */
 235   dnode_phys_t *dnode_buf;
 236   dnode_phys_t *dnode_mdn;
 237   grub_uint64_t dnode_start;
 238   grub_uint64_t dnode_end;
 239   grub_zfs_endian_t dnode_endian;
 240 
 241   dnode_end_t mos;
 242   dnode_end_t dnode;
 243   struct subvolume subvol;
 244 
 245   struct grub_zfs_device_desc *devices_attached;
 246   unsigned n_devices_attached;
 247   unsigned n_devices_allocated;
 248   struct grub_zfs_device_desc *device_original;
 249 
 250   uberblock_t current_uberblock;
 251 
 252   int mounted;
 253   grub_uint64_t guid;
 254 };
 255 
 256 grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
 257                                 grub_uint64_t algo,
 258                                 void *nonce,
 259                                 char *buf, grub_size_t size,
 260                                 const grub_uint32_t *expected_mac,
 261                                 grub_zfs_endian_t endian) = NULL;
 262 grub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct grub_zfs_key *key,
 263                                                   grub_size_t keysize,
 264                                                   grub_uint64_t salt,
 265                                                   grub_uint64_t algo) = NULL;
 266 
 267 static grub_err_t 
 268 zlib_decompress (void *s, void *d,
 269                  grub_size_t slen, grub_size_t dlen)
 270 {
 271   if (grub_zlib_decompress (s, slen, 0, d, dlen) < 0)
 272     return grub_errno;
 273   return GRUB_ERR_NONE;
 274 }
 275 
 276 static grub_err_t 
 277 zle_decompress (void *s, void *d,
 278                 grub_size_t slen, grub_size_t dlen)
 279 {
 280   grub_uint8_t *iptr, *optr;
 281   grub_size_t clen;
 282   for (iptr = s, optr = d; iptr < (grub_uint8_t *) s + slen
 283          && optr < (grub_uint8_t *) d + dlen;)
 284     {
 285       if (*iptr & 0x80)
 286         clen = ((*iptr) & 0x7f) + 0x41;
 287       else
 288         clen = ((*iptr) & 0x3f) + 1;
 289       if ((grub_ssize_t) clen > (grub_uint8_t *) d + dlen - optr)
 290         clen = (grub_uint8_t *) d + dlen - optr;
 291       if (*iptr & 0x40 || *iptr & 0x80)
 292         {
 293           grub_memset (optr, 0, clen);
 294           iptr++;
 295           optr += clen;
 296           continue;
 297         }
 298       if ((grub_ssize_t) clen > (grub_uint8_t *) s + slen - iptr - 1)
 299         clen = (grub_uint8_t *) s + slen - iptr - 1;
 300       grub_memcpy (optr, iptr + 1, clen);
 301       optr += clen;
 302       iptr += clen + 1;
 303     }
 304   if (optr < (grub_uint8_t *) d + dlen)
 305     grub_memset (optr, 0, (grub_uint8_t *) d + dlen - optr);
 306   return GRUB_ERR_NONE;
 307 }
 308 
 309 static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = {
 310   {"inherit", NULL},            /* ZIO_COMPRESS_INHERIT */
 311   {"on", lzjb_decompress},      /* ZIO_COMPRESS_ON */
 312   {"off", NULL},                /* ZIO_COMPRESS_OFF */
 313   {"lzjb", lzjb_decompress},    /* ZIO_COMPRESS_LZJB */
 314   {"empty", NULL},              /* ZIO_COMPRESS_EMPTY */
 315   {"gzip-1", zlib_decompress},  /* ZIO_COMPRESS_GZIP1 */
 316   {"gzip-2", zlib_decompress},  /* ZIO_COMPRESS_GZIP2 */
 317   {"gzip-3", zlib_decompress},  /* ZIO_COMPRESS_GZIP3 */
 318   {"gzip-4", zlib_decompress},  /* ZIO_COMPRESS_GZIP4 */
 319   {"gzip-5", zlib_decompress},  /* ZIO_COMPRESS_GZIP5 */
 320   {"gzip-6", zlib_decompress},  /* ZIO_COMPRESS_GZIP6 */
 321   {"gzip-7", zlib_decompress},  /* ZIO_COMPRESS_GZIP7 */
 322   {"gzip-8", zlib_decompress},  /* ZIO_COMPRESS_GZIP8 */
 323   {"gzip-9", zlib_decompress},  /* ZIO_COMPRESS_GZIP9 */
 324   {"zle", zle_decompress},      /* ZIO_COMPRESS_ZLE   */
 325 };
 326 
 327 static grub_err_t zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian,
 328                                  void *buf, struct grub_zfs_data *data);
 329 
 330 /*
 331  * Our own version of log2().  Same thing as highbit()-1.
 332  */
 333 static int
 334 zfs_log2 (grub_uint64_t num)
 335 {
 336   int i = 0;
 337 
 338   while (num > 1)
 339     {
 340       i++;
 341       num = num >> 1;
 342     }
 343 
 344   return (i);
 345 }
 346 
 347 /* Checksum Functions */
 348 static void
 349 zio_checksum_off (const void *buf __attribute__ ((unused)),
 350                   grub_uint64_t size __attribute__ ((unused)),
 351                   grub_zfs_endian_t endian __attribute__ ((unused)),
 352                   zio_cksum_t * zcp)
 353 {
 354   ZIO_SET_CHECKSUM (zcp, 0, 0, 0, 0);
 355 }
 356 
 357 /* Checksum Table and Values */
 358 static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
 359   {NULL, 0, 0, "inherit"},
 360   {NULL, 0, 0, "on"},
 361   {zio_checksum_off, 0, 0, "off"},
 362   {zio_checksum_SHA256, 1, 1, "label"},
 363   {zio_checksum_SHA256, 1, 1, "gang_header"},
 364   {NULL, 0, 0, "zilog"},
 365   {fletcher_2, 0, 0, "fletcher2"},
 366   {fletcher_4, 1, 0, "fletcher4"},
 367   {zio_checksum_SHA256, 1, 0, "SHA256"},
 368   {NULL, 0, 0, "zilog2"},
 369   {zio_checksum_SHA256, 1, 0, "SHA256+MAC"},
 370 };
 371 
 372 /*
 373  * zio_checksum_verify: Provides support for checksum verification.
 374  *
 375  * Fletcher2, Fletcher4, and SHA256 are supported.
 376  *
 377  */
 378 static grub_err_t
 379 zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
 380                      grub_zfs_endian_t endian, 
 381                      char *buf, grub_size_t size)
 382 {
 383   zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1;
 384   zio_checksum_info_t *ci = &zio_checksum_table[checksum];
 385   zio_cksum_t actual_cksum, expected_cksum;
 386 
 387   if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func == NULL)
 388     {
 389       grub_dprintf ("zfs", "unknown checksum function %d\n", checksum);
 390       return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, 
 391                  "unknown checksum function %d", checksum);
 392     }
 393 
 394   if (ci->ci_eck)
 395     {
 396       expected_cksum = zec->zec_cksum;  
 397       zec->zec_cksum = zc;  
 398       ci->ci_func (buf, size, endian, &actual_cksum);
 399       zec->zec_cksum = expected_cksum;
 400       zc = expected_cksum;
 401     }
 402   else
 403     ci->ci_func (buf, size, endian, &actual_cksum);
 404 
 405   if (grub_memcmp (&actual_cksum, &zc,
 406                    checksum != ZIO_CHECKSUM_SHA256_MAC ? 32 : 20) != 0)
 407     {
 408       grub_dprintf ("zfs", "checksum %s verification failed\n", ci->ci_name);
 409       grub_dprintf ("zfs", "actual checksum %016llx %016llx %016llx %016llx\n",
 410                     (unsigned long long) actual_cksum.zc_word[0], 
 411                     (unsigned long long) actual_cksum.zc_word[1],
 412                     (unsigned long long) actual_cksum.zc_word[2], 
 413                     (unsigned long long) actual_cksum.zc_word[3]);
 414       grub_dprintf ("zfs", "expected checksum %016llx %016llx %016llx %016llx\n",
 415                     (unsigned long long) zc.zc_word[0], 
 416                     (unsigned long long) zc.zc_word[1],
 417                     (unsigned long long) zc.zc_word[2], 
 418                     (unsigned long long) zc.zc_word[3]);
 419       return grub_error (GRUB_ERR_BAD_FS, N_("checksum verification failed"));
 420     }
 421 
 422   return GRUB_ERR_NONE;
 423 }
 424 
 425 /*
 426  * vdev_uberblock_compare takes two uberblock structures and returns an integer
 427  * indicating the more recent of the two.
 428  *      Return Value = 1 if ub2 is more recent
 429  *      Return Value = -1 if ub1 is more recent
 430  * The most recent uberblock is determined using its transaction number and
 431  * timestamp.  The uberblock with the highest transaction number is
 432  * considered "newer".  If the transaction numbers of the two blocks match, the
 433  * timestamps are compared to determine the "newer" of the two.
 434  */
 435 static int
 436 vdev_uberblock_compare (uberblock_t * ub1, uberblock_t * ub2)
 437 {
 438   grub_zfs_endian_t ub1_endian, ub2_endian;
 439   if (grub_zfs_to_cpu64 (ub1->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
 440       == UBERBLOCK_MAGIC)
 441     ub1_endian = GRUB_ZFS_LITTLE_ENDIAN;
 442   else
 443     ub1_endian = GRUB_ZFS_BIG_ENDIAN;
 444   if (grub_zfs_to_cpu64 (ub2->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
 445       == UBERBLOCK_MAGIC)
 446     ub2_endian = GRUB_ZFS_LITTLE_ENDIAN;
 447   else
 448     ub2_endian = GRUB_ZFS_BIG_ENDIAN;
 449 
 450   if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) 
 451       < grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian))
 452     return (-1);
 453   if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) 
 454       > grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian))
 455     return (1);
 456 
 457   if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) 
 458       < grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian))
 459     return (-1);
 460   if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) 
 461       > grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian))
 462     return (1);
 463 
 464   return (0);
 465 }
 466 
 467 /*
 468  * Three pieces of information are needed to verify an uberblock: the magic
 469  * number, the version number, and the checksum.
 470  *
 471  * Currently Implemented: version number, magic number, checksum
 472  *
 473  */
 474 static grub_err_t
 475 uberblock_verify (uberblock_phys_t * ub, grub_uint64_t offset,
 476                   grub_size_t s)
 477 {
 478   uberblock_t *uber = &ub->ubp_uberblock;
 479   grub_err_t err;
 480   grub_zfs_endian_t endian = GRUB_ZFS_UNKNOWN_ENDIAN;
 481   zio_cksum_t zc;
 482 
 483   if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
 484       == UBERBLOCK_MAGIC
 485       && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN) > 0 
 486       && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN)
 487       <= SPA_VERSION)
 488     endian = GRUB_ZFS_LITTLE_ENDIAN;
 489 
 490   if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_BIG_ENDIAN) == UBERBLOCK_MAGIC
 491       && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN) > 0 
 492       && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN)
 493       <= SPA_VERSION)
 494     endian = GRUB_ZFS_BIG_ENDIAN;
 495 
 496   if (endian == GRUB_ZFS_UNKNOWN_ENDIAN)
 497     return grub_error (GRUB_ERR_BAD_FS, "invalid uberblock magic");
 498 
 499   grub_memset (&zc, 0, sizeof (zc));
 500 
 501   zc.zc_word[0] = grub_cpu_to_zfs64 (offset, endian);
 502   err = zio_checksum_verify (zc, ZIO_CHECKSUM_LABEL, endian,
 503                              (char *) ub, s);
 504 
 505   return err;
 506 }
 507 
 508 /*
 509  * Find the best uberblock.
 510  * Return:
 511  *    Success - Pointer to the best uberblock.
 512  *    Failure - NULL
 513  */
 514 static uberblock_phys_t *
 515 find_bestub (uberblock_phys_t * ub_array,
 516              const struct grub_zfs_device_desc *desc)
 517 {
 518   uberblock_phys_t *ubbest = NULL, *ubptr;
 519   int i;
 520   grub_disk_addr_t offset;
 521   grub_err_t err = GRUB_ERR_NONE;
 522   int ub_shift;
 523 
 524   ub_shift = desc->ashift;
 525   if (ub_shift < VDEV_UBERBLOCK_SHIFT)
 526     ub_shift = VDEV_UBERBLOCK_SHIFT;
 527 
 528   for (i = 0; i < (VDEV_UBERBLOCK_RING >> ub_shift); i++)
 529     {
 530       offset = (desc->vdev_phys_sector << SPA_MINBLOCKSHIFT) + VDEV_PHYS_SIZE
 531         + (i << ub_shift);
 532 
 533       ubptr = (uberblock_phys_t *) ((grub_properly_aligned_t *) ub_array
 534                                     + ((i << ub_shift)
 535                                        / sizeof (grub_properly_aligned_t)));
 536       err = uberblock_verify (ubptr, offset, 1 << ub_shift);
 537       if (err)
 538         {
 539           grub_errno = GRUB_ERR_NONE;
 540           continue;
 541         }
 542       if (ubbest == NULL 
 543           || vdev_uberblock_compare (&(ubptr->ubp_uberblock),
 544                                      &(ubbest->ubp_uberblock)) > 0)
 545         ubbest = ubptr;
 546     }
 547   if (!ubbest)
 548     grub_errno = err;
 549 
 550   return (ubbest);
 551 }
 552 
 553 static inline grub_size_t
 554 get_psize (blkptr_t * bp, grub_zfs_endian_t endian)
 555 {
 556   return ((((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) >> 16) & 0xffff) + 1)
 557           << SPA_MINBLOCKSHIFT);
 558 }
 559 
 560 static grub_uint64_t
 561 dva_get_offset (const dva_t *dva, grub_zfs_endian_t endian)
 562 {
 563   grub_dprintf ("zfs", "dva=%llx, %llx\n", 
 564                 (unsigned long long) dva->dva_word[0], 
 565                 (unsigned long long) dva->dva_word[1]);
 566   return grub_zfs_to_cpu64 ((dva)->dva_word[1], 
 567                             endian) << SPA_MINBLOCKSHIFT;
 568 }
 569 
 570 static grub_err_t
 571 zfs_fetch_nvlist (struct grub_zfs_device_desc *diskdesc, char **nvlist)
 572 {
 573   grub_err_t err;
 574 
 575   *nvlist = 0;
 576 
 577   if (!diskdesc->dev)
 578     return grub_error (GRUB_ERR_BUG, "member drive unknown");
 579 
 580   *nvlist = grub_malloc (VDEV_PHYS_SIZE);
 581 
 582   /* Read in the vdev name-value pair list (112K). */
 583   err = grub_disk_read (diskdesc->dev->disk, diskdesc->vdev_phys_sector, 0,
 584                         VDEV_PHYS_SIZE, *nvlist);
 585   if (err)
 586     {
 587       grub_free (*nvlist);
 588       *nvlist = 0;
 589       return err;
 590     }
 591   return GRUB_ERR_NONE;
 592 }
 593 
 594 static grub_err_t
 595 fill_vdev_info_real (struct grub_zfs_data *data,
 596                      const char *nvlist,
 597                      struct grub_zfs_device_desc *fill,
 598                      struct grub_zfs_device_desc *insert,
 599                      int *inserted,
 600                      unsigned ashift)
 601 {
 602   char *type;
 603 
 604   type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE);
 605 
 606   if (!type)
 607     return grub_errno;
 608 
 609   if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &(fill->id)))
 610     {
 611       grub_free (type);
 612       return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id");
 613     }
 614 
 615   if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "guid", &(fill->guid)))
 616     {
 617       grub_free (type);
 618       return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id");
 619     }
 620 
 621   {
 622     grub_uint64_t par;
 623     if (grub_zfs_nvlist_lookup_uint64 (nvlist, "ashift", &par))
 624       fill->ashift = par;
 625     else if (ashift != 0xffffffff)
 626       fill->ashift = ashift;
 627     else
 628       {
 629         grub_free (type);
 630         return grub_error (GRUB_ERR_BAD_FS, "couldn't find ashift");
 631       }
 632   }
 633 
 634   fill->max_children_ashift = 0;
 635 
 636   if (grub_strcmp (type, VDEV_TYPE_DISK) == 0
 637       || grub_strcmp (type, VDEV_TYPE_FILE) == 0)
 638     {
 639       fill->type = DEVICE_LEAF;
 640 
 641       if (!fill->dev && fill->guid == insert->guid)
 642         {
 643           fill->dev = insert->dev;
 644           fill->vdev_phys_sector = insert->vdev_phys_sector;
 645           fill->current_uberblock = insert->current_uberblock;
 646           fill->original = insert->original;
 647           if (!data->device_original)
 648             data->device_original = fill;
 649           insert->ashift = fill->ashift;
 650           *inserted = 1;
 651         }
 652 
 653       grub_free (type);
 654 
 655       return GRUB_ERR_NONE;
 656     }
 657 
 658   if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0
 659       || grub_strcmp (type, VDEV_TYPE_RAIDZ) == 0)
 660     {
 661       int nelm, i;
 662 
 663       if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0)
 664         fill->type = DEVICE_MIRROR;
 665       else
 666         {
 667           grub_uint64_t par;
 668           fill->type = DEVICE_RAIDZ;
 669           if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "nparity", &par))
 670             {
 671               grub_free (type);
 672               return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz parity");
 673             }
 674           fill->nparity = par;
 675         }
 676 
 677       nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm (nvlist,
 678                                                            ZPOOL_CONFIG_CHILDREN);
 679 
 680       if (nelm <= 0)
 681         {
 682           grub_free (type);
 683           return grub_error (GRUB_ERR_BAD_FS, "incorrect mirror VDEV");
 684         }
 685 
 686       if (!fill->children)
 687         {
 688           fill->n_children = nelm;
 689           
 690           fill->children = grub_zalloc (fill->n_children
 691                                         * sizeof (fill->children[0]));
 692         }
 693 
 694       for (i = 0; i < nelm; i++)
 695         {
 696           char *child;
 697           grub_err_t err;
 698 
 699           child = grub_zfs_nvlist_lookup_nvlist_array
 700             (nvlist, ZPOOL_CONFIG_CHILDREN, i);
 701 
 702           err = fill_vdev_info_real (data, child, &fill->children[i], insert,
 703                                      inserted, fill->ashift);
 704 
 705           grub_free (child);
 706 
 707           if (err)
 708             {
 709               grub_free (type);
 710               return err;
 711             }
 712           if (fill->children[i].ashift > fill->max_children_ashift)
 713             fill->max_children_ashift = fill->children[i].ashift;
 714         }
 715       grub_free (type);
 716       return GRUB_ERR_NONE;
 717     }
 718 
 719   grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "vdev %s isn't supported", type);
 720   grub_free (type);
 721   return grub_errno;
 722 }
 723 
 724 static grub_err_t
 725 fill_vdev_info (struct grub_zfs_data *data,
 726                 char *nvlist, struct grub_zfs_device_desc *diskdesc,
 727                 int *inserted)
 728 {
 729   grub_uint64_t id;
 730   unsigned i;
 731 
 732   *inserted = 0;
 733 
 734   if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &id))
 735     return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id");
 736 
 737   for (i = 0; i < data->n_devices_attached; i++)
 738     if (data->devices_attached[i].id == id)
 739       return fill_vdev_info_real (data, nvlist, &data->devices_attached[i],
 740                                   diskdesc, inserted, 0xffffffff);
 741 
 742   data->n_devices_attached++;
 743   if (data->n_devices_attached > data->n_devices_allocated)
 744     {
 745       void *tmp;
 746       data->n_devices_allocated = 2 * data->n_devices_attached + 1;
 747       data->devices_attached
 748         = grub_realloc (tmp = data->devices_attached,
 749                         data->n_devices_allocated
 750                         * sizeof (data->devices_attached[0]));
 751       if (!data->devices_attached)
 752         {
 753           data->devices_attached = tmp;
 754           return grub_errno;
 755         }
 756     }
 757 
 758   grub_memset (&data->devices_attached[data->n_devices_attached - 1],
 759                0, sizeof (data->devices_attached[data->n_devices_attached - 1]));
 760 
 761   return fill_vdev_info_real (data, nvlist,
 762                               &data->devices_attached[data->n_devices_attached - 1],
 763                               diskdesc, inserted, 0xffffffff);
 764 }
 765 
 766 /*
 767  * Check the disk label information and retrieve needed vdev name-value pairs.
 768  *
 769  */
 770 static grub_err_t
 771 check_pool_label (struct grub_zfs_data *data,
 772                   struct grub_zfs_device_desc *diskdesc,
 773                   int *inserted)
 774 {
 775   grub_uint64_t pool_state, txg = 0;
 776   char *nvlist;
 777 #if 0
 778   char *nv;
 779 #endif
 780   grub_uint64_t poolguid;
 781   grub_uint64_t version;
 782   int found;
 783   grub_err_t err;
 784 
 785   *inserted = 0;
 786 
 787   err = zfs_fetch_nvlist (diskdesc, &nvlist);
 788   if (err)
 789     return err;
 790 
 791   grub_dprintf ("zfs", "check 2 passed\n");
 792 
 793   found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE,
 794                                          &pool_state);
 795   if (! found)
 796     {
 797       grub_free (nvlist);
 798       if (! grub_errno)
 799         grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_STATE " not found");
 800       return grub_errno;
 801     }
 802   grub_dprintf ("zfs", "check 3 passed\n");
 803 
 804   if (pool_state == POOL_STATE_DESTROYED)
 805     {
 806       grub_free (nvlist);
 807       return grub_error (GRUB_ERR_BAD_FS, "zpool is marked as destroyed");
 808     }
 809   grub_dprintf ("zfs", "check 4 passed\n");
 810 
 811   found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_TXG, &txg);
 812   if (!found)
 813     {
 814       grub_free (nvlist);
 815       if (! grub_errno)
 816         grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_TXG " not found");
 817       return grub_errno;
 818     }
 819   grub_dprintf ("zfs", "check 6 passed\n");
 820 
 821   /* not an active device */
 822   if (txg == 0)
 823     {
 824       grub_free (nvlist);
 825       return grub_error (GRUB_ERR_BAD_FS, "zpool isn't active");
 826     }
 827   grub_dprintf ("zfs", "check 7 passed\n");
 828 
 829   found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_VERSION,
 830                                          &version);
 831   if (! found)
 832     {
 833       grub_free (nvlist);
 834       if (! grub_errno)
 835         grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found");
 836       return grub_errno;
 837     }
 838   grub_dprintf ("zfs", "check 8 passed\n");
 839 
 840   if (version > SPA_VERSION)
 841     {
 842       grub_free (nvlist);
 843       return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 844                          "too new version %llu > %llu",
 845                          (unsigned long long) version,
 846                          (unsigned long long) SPA_VERSION);
 847     }
 848   grub_dprintf ("zfs", "check 9 passed\n");
 849 
 850   found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID,
 851                                          &(diskdesc->guid));
 852   if (! found)
 853     {
 854       grub_free (nvlist);
 855       if (! grub_errno)
 856         grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_GUID " not found");
 857       return grub_errno;
 858     }
 859 
 860   found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID,
 861                                          &poolguid);
 862   if (! found)
 863     {
 864       grub_free (nvlist);
 865       if (! grub_errno)
 866         grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_GUID " not found");
 867       return grub_errno;
 868     }
 869 
 870   grub_dprintf ("zfs", "check 11 passed\n");
 871 
 872   if (data->mounted && data->guid != poolguid)
 873     return grub_error (GRUB_ERR_BAD_FS, "another zpool");
 874   else
 875     data->guid = poolguid;
 876 
 877   {
 878     char *nv;
 879     nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE);
 880 
 881     if (!nv)
 882       {
 883         grub_free (nvlist);
 884         return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev tree");
 885       }
 886     err = fill_vdev_info (data, nv, diskdesc, inserted);
 887     if (err)
 888       {
 889         grub_free (nv);
 890         grub_free (nvlist);
 891         return err;
 892       }
 893     grub_free (nv);
 894   }
 895   grub_dprintf ("zfs", "check 10 passed\n");
 896 
 897   grub_free (nvlist);
 898 
 899   return GRUB_ERR_NONE;
 900 }
 901 
 902 static grub_err_t
 903 scan_disk (grub_device_t dev, struct grub_zfs_data *data,
 904            int original, int *inserted)
 905 {
 906   int label = 0;
 907   uberblock_phys_t *ub_array, *ubbest = NULL;
 908   vdev_boot_header_t *bh;
 909   grub_err_t err;
 910   int vdevnum;
 911   struct grub_zfs_device_desc desc;
 912 
 913   ub_array = grub_malloc (VDEV_UBERBLOCK_RING);
 914   if (!ub_array)
 915     return grub_errno;
 916 
 917   bh = grub_malloc (VDEV_BOOT_HEADER_SIZE);
 918   if (!bh)
 919     {
 920       grub_free (ub_array);
 921       return grub_errno;
 922     }
 923 
 924   vdevnum = VDEV_LABELS;
 925 
 926   desc.dev = dev;
 927   desc.original = original;
 928 
 929   /* Don't check back labels on CDROM.  */
 930   if (grub_disk_get_size (dev->disk) == GRUB_DISK_SIZE_UNKNOWN)
 931     vdevnum = VDEV_LABELS / 2;
 932 
 933   for (label = 0; ubbest == NULL && label < vdevnum; label++)
 934     {
 935       desc.vdev_phys_sector
 936         = label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)
 937         + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT)
 938         + (label < VDEV_LABELS / 2 ? 0 : 
 939            ALIGN_DOWN (grub_disk_get_size (dev->disk), sizeof (vdev_label_t))
 940            - VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT));
 941 
 942       /* Read in the uberblock ring (128K). */
 943       err = grub_disk_read (dev->disk, desc.vdev_phys_sector
 944                             + (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT),
 945                             0, VDEV_UBERBLOCK_RING, (char *) ub_array);
 946       if (err)
 947         {
 948           grub_errno = GRUB_ERR_NONE;
 949           continue;
 950         }
 951       grub_dprintf ("zfs", "label ok %d\n", label);
 952 
 953       err = check_pool_label (data, &desc, inserted);
 954       if (err || !*inserted)
 955         {
 956           grub_errno = GRUB_ERR_NONE;
 957           continue;
 958         }
 959 
 960       ubbest = find_bestub (ub_array, &desc);
 961       if (!ubbest)
 962         {
 963           grub_dprintf ("zfs", "No uberblock found\n");
 964           grub_errno = GRUB_ERR_NONE;
 965           continue;
 966         }
 967 
 968       grub_memmove (&(desc.current_uberblock),
 969                     &ubbest->ubp_uberblock, sizeof (uberblock_t));
 970       if (original)
 971         grub_memmove (&(data->current_uberblock),
 972                       &ubbest->ubp_uberblock, sizeof (uberblock_t));
 973 
 974 #if 0
 975       if (find_best_root &&
 976           vdev_uberblock_compare (&ubbest->ubp_uberblock,
 977                                   &(current_uberblock)) <= 0)
 978         continue;
 979 #endif
 980       grub_free (ub_array);
 981       grub_free (bh);
 982       return GRUB_ERR_NONE;
 983     }
 984   
 985   grub_free (ub_array);
 986   grub_free (bh);
 987 
 988   return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid label");
 989 }
 990 
 991 static grub_err_t
 992 scan_devices (struct grub_zfs_data *data)
 993 {
 994   auto int hook (const char *name);
 995   int hook (const char *name)
 996   {
 997     grub_device_t dev;
 998     grub_err_t err;
 999     int inserted;
1000     dev = grub_device_open (name);
1001     if (!dev)
1002       return 0;
1003     if (!dev->disk)
1004       {
1005         grub_device_close (dev);
1006         return 0;
1007       }
1008     err = scan_disk (dev, data, 0, &inserted);
1009     if (err == GRUB_ERR_BAD_FS)
1010       {
1011         grub_device_close (dev);
1012         grub_errno = GRUB_ERR_NONE;
1013         return 0;
1014       }
1015     if (err)
1016       {
1017         grub_device_close (dev);
1018         grub_print_error ();
1019         return 0;
1020       }
1021 
1022     if (!inserted)
1023       grub_device_close (dev);
1024     
1025     return 0;
1026   }
1027   grub_device_iterate (hook);
1028   return GRUB_ERR_NONE;
1029 }
1030 
1031 /* x**y.  */
1032 static grub_uint8_t powx[255 * 2];
1033 /* Such an s that x**s = y */
1034 static int powx_inv[256];
1035 static const grub_uint8_t poly = 0x1d;
1036 
1037 /* perform the operation a ^= b * (x ** (known_idx * recovery_pow) ) */
1038 static inline void
1039 xor_out (grub_uint8_t *a, const grub_uint8_t *b, grub_size_t s,
1040          int known_idx, int recovery_pow)
1041 {
1042   int add;
1043 
1044   /* Simple xor.  */
1045   if (known_idx == 0 || recovery_pow == 0)
1046     {
1047       grub_crypto_xor (a, a, b, s);
1048       return;
1049     }
1050   add = (known_idx * recovery_pow) % 255;
1051   for (;s--; b++, a++)
1052     if (*b)
1053       *a ^= powx[powx_inv[*b] + add];
1054 }
1055 
1056 static inline grub_uint8_t
1057 gf_mul (grub_uint8_t a, grub_uint8_t b)
1058 {
1059   if (a == 0 || b == 0)
1060     return 0;
1061   return powx[powx_inv[a] + powx_inv[b]];
1062 }
1063 
1064 static inline grub_err_t
1065 recovery (grub_uint8_t *bufs[4], grub_size_t s, const int nbufs,
1066           const unsigned *powers,
1067           const int *idx)
1068 {
1069   grub_dprintf ("zfs", "recovering %u buffers\n", nbufs);
1070   /* Now we have */
1071   /* b_i = sum (r_j* (x ** (powers[i] * idx[j])))*/
1072   /* Let's invert the matrix in question. */
1073   switch (nbufs)
1074     {
1075       /* Easy: r_0 = bufs[0] / (x << (powers[i] * idx[j])).  */
1076     case 1:
1077       {
1078         int add;
1079         grub_uint8_t *a;
1080         if (powers[0] == 0 || idx[0] == 0)
1081           return GRUB_ERR_NONE;
1082         add = 255 - ((powers[0] * idx[0]) % 255);
1083         for (a = bufs[0]; s--; a++)
1084           if (*a)
1085             *a = powx[powx_inv[*a] + add];
1086         return GRUB_ERR_NONE;
1087       }
1088       /* Case 2x2: Let's use the determinant formula.  */
1089     case 2:
1090       {
1091         grub_uint8_t det, det_inv;
1092         grub_uint8_t matrixinv[2][2];
1093         unsigned i;
1094         /* The determinant is: */
1095         det = (powx[(powers[0] * idx[0] + powers[1] * idx[1]) % 255]
1096                ^ powx[(powers[0] * idx[1] + powers[1] * idx[0]) % 255]);
1097         if (det == 0)
1098           return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix");
1099         det_inv = powx[255 - powx_inv[det]];
1100         matrixinv[0][0] = gf_mul (powx[(powers[1] * idx[1]) % 255], det_inv);
1101         matrixinv[1][1] = gf_mul (powx[(powers[0] * idx[0]) % 255], det_inv);
1102         matrixinv[0][1] = gf_mul (powx[(powers[0] * idx[1]) % 255], det_inv);
1103         matrixinv[1][0] = gf_mul (powx[(powers[1] * idx[0]) % 255], det_inv);
1104         for (i = 0; i < s; i++)
1105           {
1106             grub_uint8_t b0, b1;
1107             b0 = bufs[0][i];
1108             b1 = bufs[1][i];
1109 
1110             bufs[0][i] = (gf_mul (b0, matrixinv[0][0])
1111                           ^ gf_mul (b1, matrixinv[0][1]));
1112             bufs[1][i] = (gf_mul (b0, matrixinv[1][0])
1113                           ^ gf_mul (b1, matrixinv[1][1]));
1114           }
1115         return GRUB_ERR_NONE;
1116       }
1117       /* Otherwise use Gauss.  */
1118     default:
1119       {
1120         grub_uint8_t matrix1[nbufs][nbufs], matrix2[nbufs][nbufs];
1121         int i, j, k;
1122 
1123         for (i = 0; i < nbufs; i++)
1124           for (j = 0; j < nbufs; j++)
1125             matrix1[i][j] = powx[(powers[i] * idx[j]) % 255];
1126         for (i = 0; i < nbufs; i++)
1127           for (j = 0; j < nbufs; j++)
1128             matrix2[i][j] = 0;
1129         for (i = 0; i < nbufs; i++)
1130             matrix2[i][i] = 1;
1131 
1132         for (i = 0; i < nbufs; i++)
1133           {
1134             grub_uint8_t mul;
1135             for (j = i; j < nbufs; j++)          
1136               if (matrix1[i][j])
1137                 break;
1138             if (j == nbufs)
1139               return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix");
1140             if (j != i)
1141               {
1142                 int xchng;
1143                 xchng = j;
1144                 for (j = 0; j < nbufs; j++)
1145                   {
1146                     grub_uint8_t t;
1147                     t = matrix1[xchng][j];
1148                     matrix1[xchng][j] = matrix1[i][j];
1149                     matrix1[i][j] = t;
1150                   }
1151                 for (j = 0; j < nbufs; j++)
1152                   {
1153                     grub_uint8_t t;
1154                     t = matrix2[xchng][j];
1155                     matrix2[xchng][j] = matrix2[i][j];
1156                     matrix2[i][j] = t;
1157                   }
1158               }
1159             mul = powx[255 - powx_inv[matrix1[i][i]]];
1160             for (j = 0; j < nbufs; j++)
1161               matrix1[i][j] = gf_mul (matrix1[i][j], mul);
1162             for (j = 0; j < nbufs; j++)
1163               matrix2[i][j] = gf_mul (matrix2[i][j], mul);
1164             for (j = i + 1; j < nbufs; j++)
1165               {
1166                 mul = matrix1[j][i];
1167                 for (k = 0; k < nbufs; k++)
1168                   matrix1[j][k] ^= gf_mul (matrix1[i][k], mul);
1169                 for (k = 0; k < nbufs; k++)
1170                   matrix2[j][k] ^= gf_mul (matrix2[i][k], mul);
1171               }
1172           }
1173         for (i = nbufs - 1; i >= 0; i--)
1174           {
1175             for (j = 0; j < i; j++)
1176               {
1177                 grub_uint8_t mul;
1178                 mul = matrix1[j][i];
1179                 for (k = 0; k < nbufs; k++)
1180                   matrix1[j][k] ^= gf_mul (matrix1[i][k], mul);
1181                 for (k = 0; k < nbufs; k++)
1182                   matrix2[j][k] ^= gf_mul (matrix2[i][k], mul);
1183               }
1184           }
1185 
1186         for (i = 0; i < (int) s; i++)
1187           {
1188             grub_uint8_t b[nbufs];
1189             for (j = 0; j < nbufs; j++)
1190               b[j] = bufs[j][i];
1191             for (j = 0; j < nbufs; j++)
1192               {
1193                 bufs[j][i] = 0;
1194                 for (k = 0; k < nbufs; k++)
1195                   bufs[j][i] ^= gf_mul (matrix2[j][k], b[k]);
1196               }
1197           }
1198         return GRUB_ERR_NONE;
1199       }
1200     }      
1201 }
1202 
1203 static grub_err_t
1204 read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
1205              grub_size_t len, void *buf)
1206 {
1207   switch (desc->type)
1208     {
1209     case DEVICE_LEAF:
1210       {
1211         grub_uint64_t sector;
1212         sector = DVA_OFFSET_TO_PHYS_SECTOR (offset);
1213         if (!desc->dev)
1214           {
1215             return grub_error (GRUB_ERR_BAD_FS,
1216                                N_("couldn't find a necessary member device "
1217                                   "of multi-device filesystem"));
1218           }
1219         /* read in a data block */
1220         return grub_disk_read (desc->dev->disk, sector, 0, len, buf);
1221       }
1222     case DEVICE_MIRROR:
1223       {
1224         grub_err_t err = GRUB_ERR_NONE;
1225         unsigned i;
1226         if (desc->n_children <= 0)
1227           return grub_error (GRUB_ERR_BAD_FS,
1228                              "non-positive number of mirror children");
1229         for (i = 0; i < desc->n_children; i++)
1230           {
1231             err = read_device (offset, &desc->children[i],
1232                                len, buf);
1233             if (!err)
1234               break;
1235             grub_errno = GRUB_ERR_NONE;
1236           }
1237         return (grub_errno = err);
1238       }
1239     case DEVICE_RAIDZ:
1240       {
1241         unsigned c = 0;
1242         grub_uint64_t high;
1243         grub_uint64_t devn;
1244         grub_uint64_t m;
1245         grub_uint32_t s, orig_s;
1246         void *orig_buf = buf;
1247         grub_size_t orig_len = len;
1248         grub_uint8_t *recovery_buf[4];
1249         grub_size_t recovery_len[4];
1250         int recovery_idx[4];
1251         unsigned failed_devices = 0;
1252         int idx, orig_idx;
1253 
1254         if (desc->nparity < 1 || desc->nparity > 3)
1255           return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, 
1256                              "raidz%d is not supported", desc->nparity);
1257 
1258         orig_s = (((len + (1 << desc->ashift) - 1) >> desc->ashift)
1259                   + (desc->n_children - desc->nparity) - 1);
1260         s = orig_s;
1261 
1262         high = grub_divmod64 ((offset >> desc->ashift),
1263                               desc->n_children, &m);
1264         if (desc->nparity == 2)
1265           c = 2;
1266         if (desc->nparity == 3)
1267           c = 3;
1268         if (((len + (1 << desc->ashift) - 1) >> desc->ashift)
1269             >= (desc->n_children - desc->nparity))
1270           idx = (desc->n_children - desc->nparity - 1);
1271         else
1272           idx = ((len + (1 << desc->ashift) - 1) >> desc->ashift) - 1;
1273         orig_idx = idx;
1274         while (len > 0)
1275           {
1276             grub_size_t csize;
1277             grub_uint32_t bsize;
1278             grub_err_t err;
1279             bsize = s / (desc->n_children - desc->nparity);
1280 
1281             if (desc->nparity == 1
1282                 && ((offset >> (desc->ashift + 20 - desc->max_children_ashift))
1283                     & 1) == c)
1284               c++;
1285 
1286             high = grub_divmod64 ((offset >> desc->ashift) + c,
1287                                   desc->n_children, &devn);
1288             csize = bsize << desc->ashift;
1289             if (csize > len)
1290               csize = len;
1291 
1292             grub_dprintf ("zfs", "RAIDZ mapping 0x%" PRIxGRUB_UINT64_T
1293                           "+%u (%" PRIxGRUB_SIZE ", %" PRIxGRUB_UINT32_T
1294                           ") -> (0x%" PRIxGRUB_UINT64_T ", 0x%"
1295                           PRIxGRUB_UINT64_T ")\n",
1296                           offset >> desc->ashift, c, len, bsize, high,
1297                           devn);
1298             err = read_device ((high << desc->ashift)
1299                                | (offset & ((1 << desc->ashift) - 1)),
1300                                &desc->children[devn],
1301                                csize, buf);
1302             if (err && failed_devices < desc->nparity)
1303               {
1304                 recovery_buf[failed_devices] = buf;
1305                 recovery_len[failed_devices] = csize;
1306                 recovery_idx[failed_devices] = idx;
1307                 failed_devices++;
1308                 grub_errno = err = 0;
1309               }
1310             if (err)
1311               return err;
1312 
1313             c++;
1314             idx--;
1315             s--;
1316             buf = (char *) buf + csize;
1317             len -= csize;
1318           }
1319         if (failed_devices)
1320           {
1321             unsigned redundancy_pow[4];
1322             unsigned cur_redundancy_pow = 0;
1323             unsigned n_redundancy = 0;
1324             unsigned i, j;
1325             grub_err_t err;
1326 
1327             /* Compute mul. x**s has a period of 255.  */
1328             if (powx[0] == 0)
1329               {
1330                 grub_uint8_t cur = 1;
1331                 for (i = 0; i < 255; i++)
1332                   {
1333                     powx[i] = cur;
1334                     powx[i + 255] = cur;
1335                     powx_inv[cur] = i;
1336                     if (cur & 0x80)
1337                       cur = (cur << 1) ^ poly;
1338                     else
1339                       cur <<= 1;
1340                   }
1341               }
1342 
1343             /* Read redundancy data.  */
1344             for (n_redundancy = 0, cur_redundancy_pow = 0;
1345                  n_redundancy < failed_devices;
1346                  cur_redundancy_pow++)
1347               {
1348                 high = grub_divmod64 ((offset >> desc->ashift)
1349                                       + cur_redundancy_pow
1350                                       + ((desc->nparity == 1)
1351                                          && ((offset >> (desc->ashift + 20
1352                                                          - desc->max_children_ashift))
1353                                              & 1)),
1354                                       desc->n_children, &devn);
1355                 err = read_device ((high << desc->ashift)
1356                                    | (offset & ((1 << desc->ashift) - 1)),
1357                                    &desc->children[devn],
1358                                    recovery_len[n_redundancy],
1359                                    recovery_buf[n_redundancy]);
1360                 /* Ignore error if we may still have enough devices.  */
1361                 if (err && n_redundancy + desc->nparity - cur_redundancy_pow - 1
1362                     >= failed_devices)
1363                   {
1364                     grub_errno = GRUB_ERR_NONE;
1365                     continue;
1366                   }
1367                 if (err)
1368                   return err;
1369                 redundancy_pow[n_redundancy] = cur_redundancy_pow;
1370                 n_redundancy++;
1371               }
1372             /* Now xor-our the parts we already know.  */
1373             buf = orig_buf;
1374             len = orig_len;
1375             s = orig_s;
1376             idx = orig_idx;
1377 
1378             while (len > 0)
1379               {
1380                 grub_size_t csize;
1381                 csize = ((s / (desc->n_children - desc->nparity))
1382                          << desc->ashift);
1383                 if (csize > len)
1384                   csize = len;
1385 
1386                 for (j = 0; j < failed_devices; j++)
1387                   if (buf == recovery_buf[j])
1388                     break;
1389 
1390                 if (j == failed_devices)
1391                   for (j = 0; j < failed_devices; j++)
1392                     xor_out (recovery_buf[j], buf,
1393                              csize < recovery_len[j] ? csize : recovery_len[j],
1394                              idx, redundancy_pow[j]);
1395 
1396                 s--;
1397                 buf = (char *) buf + csize;
1398                 len -= csize;
1399                 idx--;
1400               }
1401             for (i = 0; i < failed_devices 
1402                    && recovery_len[i] == recovery_len[0];
1403                  i++);
1404             /* Since the chunks have variable length handle the last block
1405                separately.  */
1406             if (i != failed_devices)
1407               {
1408                 grub_uint8_t *tmp_recovery_buf[4];
1409                 for (j = 0; j < i; j++)
1410                   tmp_recovery_buf[j] = recovery_buf[j] + recovery_len[failed_devices - 1];
1411                 err = recovery (tmp_recovery_buf, recovery_len[0] - recovery_len[failed_devices - 1], i, redundancy_pow,
1412                                 recovery_idx);
1413                 if (err)
1414                   return err;
1415               }
1416             err = recovery (recovery_buf, recovery_len[failed_devices - 1],
1417                             failed_devices, redundancy_pow, recovery_idx);
1418             if (err)
1419               return err;
1420           }
1421         return GRUB_ERR_NONE;
1422       }
1423     }
1424   return grub_error (GRUB_ERR_BAD_FS, "unsupported device type");
1425 }
1426 
1427 static grub_err_t
1428 read_dva (const dva_t *dva,
1429           grub_zfs_endian_t endian, struct grub_zfs_data *data,
1430           void *buf, grub_size_t len)
1431 {
1432   grub_uint64_t offset;
1433   unsigned i;
1434   grub_err_t err = 0;
1435   int try = 0;
1436   offset = dva_get_offset (dva, endian);
1437 
1438   for (try = 0; try < 2; try++)
1439     {
1440       for (i = 0; i < data->n_devices_attached; i++)
1441         if (data->devices_attached[i].id == DVA_GET_VDEV (dva))
1442           {
1443             err = read_device (offset, &data->devices_attached[i], len, buf);
1444             if (!err)
1445               return GRUB_ERR_NONE;
1446             break;
1447           }
1448       if (try == 1)
1449         break;
1450       err = scan_devices (data);
1451       if (err)
1452         return err;
1453     }
1454   if (!err)
1455     return grub_error (GRUB_ERR_BAD_FS, "unknown device %d",
1456                        (int) DVA_GET_VDEV (dva));
1457   return err;
1458 }
1459 
1460 /*
1461  * Read a block of data based on the gang block address dva,
1462  * and put its data in buf.
1463  *
1464  */
1465 static grub_err_t
1466 zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf,
1467                struct grub_zfs_data *data)
1468 {
1469   zio_gbh_phys_t *zio_gb;
1470   unsigned i;
1471   grub_err_t err;
1472   zio_cksum_t zc;
1473 
1474   grub_memset (&zc, 0, sizeof (zc));
1475 
1476   zio_gb = grub_malloc (SPA_GANGBLOCKSIZE);
1477   if (!zio_gb)
1478     return grub_errno;
1479   grub_dprintf ("zfs", endian == GRUB_ZFS_LITTLE_ENDIAN ? "little-endian gang\n"
1480                 :"big-endian gang\n");
1481 
1482   err = read_dva (dva, endian, data, zio_gb, SPA_GANGBLOCKSIZE);
1483   if (err)
1484     {
1485       grub_free (zio_gb);
1486       return err;
1487     }
1488 
1489   /* XXX */
1490   /* self checksuming the gang block header */
1491   ZIO_SET_CHECKSUM (&zc, DVA_GET_VDEV (dva),
1492                     dva_get_offset (dva, endian), bp->blk_birth, 0);
1493   err = zio_checksum_verify (zc, ZIO_CHECKSUM_GANG_HEADER, endian,
1494                              (char *) zio_gb, SPA_GANGBLOCKSIZE);
1495   if (err)
1496     {
1497       grub_free (zio_gb);
1498       return err;
1499     }
1500 
1501   endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
1502 
1503   for (i = 0; i < SPA_GBH_NBLKPTRS; i++)
1504     {
1505       if (zio_gb->zg_blkptr[i].blk_birth == 0)
1506         continue;
1507 
1508       err = zio_read_data (&zio_gb->zg_blkptr[i], endian, buf, data);
1509       if (err)
1510         {
1511           grub_free (zio_gb);
1512           return err;
1513         }
1514       buf = (char *) buf + get_psize (&zio_gb->zg_blkptr[i], endian);
1515     }
1516   grub_free (zio_gb);
1517   return GRUB_ERR_NONE;
1518 }
1519 
1520 /*
1521  * Read in a block of raw data to buf.
1522  */
1523 static grub_err_t
1524 zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf, 
1525                struct grub_zfs_data *data)
1526 {
1527   int i, psize;
1528   grub_err_t err = GRUB_ERR_NONE;
1529 
1530   psize = get_psize (bp, endian);
1531 
1532   /* pick a good dva from the block pointer */
1533   for (i = 0; i < SPA_DVAS_PER_BP; i++)
1534     {
1535       if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0)
1536         continue;
1537 
1538       if ((grub_zfs_to_cpu64 (bp->blk_dva[i].dva_word[1], endian)>>63) & 1)
1539         err = zio_read_gang (bp, endian, &bp->blk_dva[i], buf, data);
1540       else
1541         err = read_dva (&bp->blk_dva[i], endian, data, buf, psize);
1542       if (!err)
1543         return GRUB_ERR_NONE;
1544       grub_errno = GRUB_ERR_NONE;
1545     }
1546 
1547   if (!err)
1548     err = grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid DVA");
1549   grub_errno = err;
1550 
1551   return err;
1552 }
1553 
1554 /*
1555  * Read in a block of data, verify its checksum, decompress if needed,
1556  * and put the uncompressed data in buf.
1557  */
1558 static grub_err_t
1559 zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, 
1560           grub_size_t *size, struct grub_zfs_data *data)
1561 {
1562   grub_size_t lsize, psize;
1563   unsigned int comp, encrypted;
1564   char *compbuf = NULL;
1565   grub_err_t err;
1566   zio_cksum_t zc = bp->blk_cksum;
1567   grub_uint32_t checksum;
1568 
1569   *buf = NULL;
1570 
1571   checksum = (grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff;
1572   comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0xff;
1573   encrypted = ((grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 60) & 3);
1574   lsize = (BP_IS_HOLE(bp) ? 0 :
1575            (((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) & 0xffff) + 1)
1576             << SPA_MINBLOCKSHIFT));
1577   psize = get_psize (bp, endian);
1578 
1579   if (size)
1580     *size = lsize;
1581 
1582   if (comp >= ZIO_COMPRESS_FUNCTIONS)
1583     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1584                        "compression algorithm %u not supported\n", (unsigned int) comp);
1585 
1586   if (comp != ZIO_COMPRESS_OFF && decomp_table[comp].decomp_func == NULL)
1587     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1588                        "compression algorithm %s not supported\n", decomp_table[comp].name);
1589 
1590   if (comp != ZIO_COMPRESS_OFF)
1591     {
1592       /* It's not really necessary to align to 16, just for safety.  */
1593       compbuf = grub_malloc (ALIGN_UP (psize, 16));
1594       if (! compbuf)
1595         return grub_errno;
1596     }
1597   else
1598     compbuf = *buf = grub_malloc (lsize);
1599 
1600   grub_dprintf ("zfs", "endian = %d\n", endian);
1601   err = zio_read_data (bp, endian, compbuf, data);
1602   if (err)
1603     {
1604       grub_free (compbuf);
1605       *buf = NULL;
1606       return err;
1607     }
1608   grub_memset (compbuf, 0, ALIGN_UP (psize, 16) - psize);
1609 
1610   err = zio_checksum_verify (zc, checksum, endian,
1611                              compbuf, psize);
1612   if (err)
1613     {
1614       grub_dprintf ("zfs", "incorrect checksum\n");
1615       grub_free (compbuf);
1616       *buf = NULL;
1617       return err;
1618     }
1619 
1620   if (encrypted)
1621     {
1622       if (!grub_zfs_decrypt)
1623         err = grub_error (GRUB_ERR_BAD_FS, 
1624                           N_("module `%s' isn't loaded"),
1625                           "zfscrypt");
1626       else
1627         {
1628           unsigned i, besti = 0;
1629           grub_uint64_t bestval = 0;
1630           for (i = 0; i < data->subvol.nkeys; i++)
1631             if (data->subvol.keyring[i].txg <= grub_zfs_to_cpu64 (bp->blk_birth,
1632                                                                   endian)
1633                 && data->subvol.keyring[i].txg > bestval)
1634               {
1635                 besti = i;
1636                 bestval = data->subvol.keyring[i].txg;
1637               }
1638           if (bestval == 0)
1639             {
1640               grub_free (compbuf);
1641               *buf = NULL;
1642               grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
1643                             grub_zfs_to_cpu64 (bp->blk_birth,
1644                                                endian));
1645               return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
1646             }
1647           grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T 
1648                         ", %p) for txg %" PRIxGRUB_UINT64_T "\n",
1649                         besti, data->subvol.keyring[besti].txg,
1650                         data->subvol.keyring[besti].cipher,
1651                         grub_zfs_to_cpu64 (bp->blk_birth,
1652                                            endian));
1653           err = grub_zfs_decrypt (data->subvol.keyring[besti].cipher,
1654                                   data->subvol.keyring[besti].algo,
1655                                   &(bp)->blk_dva[encrypted],
1656                                   compbuf, psize, ((grub_uint32_t *) &zc + 5),
1657                                   endian);
1658         }
1659       if (err)
1660         {
1661           grub_free (compbuf);
1662           *buf = NULL;
1663           return err;
1664         }
1665     }
1666 
1667   if (comp != ZIO_COMPRESS_OFF)
1668     {
1669       *buf = grub_malloc (lsize);
1670       if (!*buf)
1671         {
1672           grub_free (compbuf);
1673           return grub_errno;
1674         }
1675 
1676       err = decomp_table[comp].decomp_func (compbuf, *buf, psize, lsize);
1677       grub_free (compbuf);
1678       if (err)
1679         {
1680           grub_free (*buf);
1681           *buf = NULL;
1682           return err;
1683         }
1684     }
1685 
1686   return GRUB_ERR_NONE;
1687 }
1688 
1689 /*
1690  * Get the block from a block id.
1691  * push the block onto the stack.
1692  *
1693  */
1694 static grub_err_t
1695 dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf, 
1696           grub_zfs_endian_t *endian_out, struct grub_zfs_data *data)
1697 {
1698   int level;
1699   grub_off_t idx;
1700   blkptr_t *bp_array = dn->dn.dn_blkptr;
1701   int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT;
1702   blkptr_t *bp;
1703   void *tmpbuf = 0;
1704   grub_zfs_endian_t endian;
1705   grub_err_t err = GRUB_ERR_NONE;
1706 
1707   bp = grub_malloc (sizeof (blkptr_t));
1708   if (!bp)
1709     return grub_errno;
1710 
1711   endian = dn->endian;
1712   for (level = dn->dn.dn_nlevels - 1; level >= 0; level--)
1713     {
1714       grub_dprintf ("zfs", "endian = %d\n", endian);
1715       idx = (blkid >> (epbs * level)) & ((1 << epbs) - 1);
1716       *bp = bp_array[idx];
1717       if (bp_array != dn->dn.dn_blkptr)
1718         {
1719           grub_free (bp_array);
1720           bp_array = 0;
1721         }
1722 
1723       if (BP_IS_HOLE (bp))
1724         {
1725           grub_size_t size = grub_zfs_to_cpu16 (dn->dn.dn_datablkszsec, 
1726                                                 dn->endian) 
1727             << SPA_MINBLOCKSHIFT;
1728           *buf = grub_malloc (size);
1729           if (*buf)
1730             {
1731               err = grub_errno;
1732               break;
1733             }
1734           grub_memset (*buf, 0, size);
1735           endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
1736           break;
1737         }
1738       if (level == 0)
1739         {
1740           grub_dprintf ("zfs", "endian = %d\n", endian);
1741           err = zio_read (bp, endian, buf, 0, data);
1742           endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
1743           break;
1744         }
1745       grub_dprintf ("zfs", "endian = %d\n", endian);
1746       err = zio_read (bp, endian, &tmpbuf, 0, data);
1747       endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
1748       if (err)
1749         break;
1750       bp_array = tmpbuf;
1751     }
1752   if (bp_array != dn->dn.dn_blkptr)
1753     grub_free (bp_array);
1754   if (endian_out)
1755     *endian_out = endian;
1756 
1757   grub_free (bp);
1758   return err;
1759 }
1760 
1761 /*
1762  * mzap_lookup: Looks up property described by "name" and returns the value
1763  * in "value".
1764  */
1765 static grub_err_t
1766 mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian,
1767              int objsize, const char *name, grub_uint64_t * value,
1768              int case_insensitive)
1769 {
1770   int i, chunks;
1771   mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk;
1772 
1773   chunks = objsize / MZAP_ENT_LEN - 1;
1774   for (i = 0; i < chunks; i++)
1775     {
1776       if (case_insensitive ? (grub_strcasecmp (mzap_ent[i].mze_name, name) == 0)
1777           : (grub_strcmp (mzap_ent[i].mze_name, name) == 0))
1778         {
1779           *value = grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian);
1780           return GRUB_ERR_NONE;
1781         }
1782     }
1783 
1784   return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), name);
1785 }
1786 
1787 static int
1788 mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize, 
1789               int NESTED_FUNC_ATTR (*hook) (const char *name, 
1790                                             grub_uint64_t val))
1791 {
1792   int i, chunks;
1793   mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk;
1794 
1795   chunks = objsize / MZAP_ENT_LEN - 1;
1796   for (i = 0; i < chunks; i++)
1797     {
1798       grub_dprintf ("zfs", "zap: name = %s, value = %llx, cd = %x\n",
1799                     mzap_ent[i].mze_name, (long long)mzap_ent[i].mze_value,
1800                     (int)mzap_ent[i].mze_cd);
1801       if (hook (mzap_ent[i].mze_name, 
1802                 grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian)))
1803         return 1;
1804     }
1805 
1806   return 0;
1807 }
1808 
1809 static grub_uint64_t
1810 zap_hash (grub_uint64_t salt, const char *name,
1811           int case_insensitive)
1812 {
1813   static grub_uint64_t table[256];
1814   const grub_uint8_t *cp;
1815   grub_uint8_t c;
1816   grub_uint64_t crc = salt;
1817 
1818   if (table[128] == 0)
1819     {
1820       grub_uint64_t *ct;
1821       int i, j;
1822       for (i = 0; i < 256; i++)
1823         {
1824           for (ct = table + i, *ct = i, j = 8; j > 0; j--)
1825             *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY);
1826         }
1827     }
1828 
1829   if (case_insensitive)
1830     for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++)
1831       crc = (crc >> 8) ^ table[(crc ^ grub_toupper (c)) & 0xFF];
1832   else
1833     for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++)
1834       crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF];
1835 
1836   /*
1837    * Only use 28 bits, since we need 4 bits in the cookie for the
1838    * collision differentiator.  We MUST use the high bits, since
1839    * those are the onces that we first pay attention to when
1840    * chosing the bucket.
1841    */
1842   crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1);
1843 
1844   return (crc);
1845 }
1846 
1847 /*
1848  * Only to be used on 8-bit arrays.
1849  * array_len is actual len in bytes (not encoded le_value_length).
1850  * buf is null-terminated.
1851  */
1852 
1853 static inline int
1854 name_cmp (const char *s1, const char *s2, grub_size_t n,
1855           int case_insensitive)
1856 {
1857   const char *t1 = (const char *) s1;
1858   const char *t2 = (const char *) s2;
1859 
1860   if (!case_insensitive)
1861     return grub_memcmp (t1, t2, n);
1862       
1863   while (n--)
1864     {
1865       if (grub_toupper (*t1) != grub_toupper (*t2))
1866           return (int) grub_toupper (*t1) - (int) grub_toupper (*t2);
1867           
1868       t1++;
1869       t2++;
1870     }
1871 
1872   return 0;
1873 }
1874 
1875 /* XXX */
1876 static int
1877 zap_leaf_array_equal (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
1878                       int blksft, int chunk, grub_size_t array_len,
1879                       const char *buf, int case_insensitive)
1880 {
1881   grub_size_t bseen = 0;
1882 
1883   while (bseen < array_len)
1884     {
1885       struct zap_leaf_array *la = &ZAP_LEAF_CHUNK (l, blksft, chunk)->l_array;
1886       grub_size_t toread = array_len - bseen;
1887 
1888       if (toread > ZAP_LEAF_ARRAY_BYTES)
1889         toread = ZAP_LEAF_ARRAY_BYTES;
1890 
1891       if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft))
1892         return 0;
1893 
1894       if (name_cmp ((char *) la->la_array, buf + bseen, toread,
1895                     case_insensitive) != 0)
1896         break;
1897       chunk = grub_zfs_to_cpu16 (la->la_next, endian);
1898       bseen += toread;
1899     }
1900   return (bseen == array_len);
1901 }
1902 
1903 /* XXX */
1904 static grub_err_t
1905 zap_leaf_array_get (zap_leaf_phys_t * l, grub_zfs_endian_t endian, int blksft, 
1906                     int chunk, grub_size_t array_len, char *buf)
1907 {
1908   grub_size_t bseen = 0;
1909 
1910   while (bseen < array_len)
1911     {
1912       struct zap_leaf_array *la = &ZAP_LEAF_CHUNK (l, blksft, chunk)->l_array;
1913       grub_size_t toread = array_len - bseen;
1914 
1915       if (toread > ZAP_LEAF_ARRAY_BYTES)
1916         toread = ZAP_LEAF_ARRAY_BYTES;
1917 
1918       if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft))
1919         /* Don't use grub_error because this error is to be ignored.  */
1920         return GRUB_ERR_BAD_FS;
1921 
1922       grub_memcpy (buf + bseen,la->la_array,  toread);
1923       chunk = grub_zfs_to_cpu16 (la->la_next, endian);
1924       bseen += toread;
1925     }
1926   return GRUB_ERR_NONE;
1927 }
1928 
1929 
1930 /*
1931  * Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the
1932  * value for the property "name".
1933  *
1934  */
1935 /* XXX */
1936 static grub_err_t
1937 zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
1938                  int blksft, grub_uint64_t h,
1939                  const char *name, grub_uint64_t * value,
1940                  int case_insensitive)
1941 {
1942   grub_uint16_t chunk;
1943   struct zap_leaf_entry *le;
1944 
1945   /* Verify if this is a valid leaf block */
1946   if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF)
1947     return grub_error (GRUB_ERR_BAD_FS, "invalid leaf type");
1948   if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC)
1949     return grub_error (GRUB_ERR_BAD_FS, "invalid leaf magic");
1950 
1951   for (chunk = grub_zfs_to_cpu16 (l->l_hash[LEAF_HASH (blksft, h, l)], endian);
1952        chunk != CHAIN_END; chunk = grub_zfs_to_cpu16 (le->le_next, endian))
1953     {
1954 
1955       if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft))
1956         return grub_error (GRUB_ERR_BAD_FS, "invalid chunk number");
1957 
1958       le = ZAP_LEAF_ENTRY (l, blksft, chunk);
1959 
1960       /* Verify the chunk entry */
1961       if (le->le_type != ZAP_CHUNK_ENTRY)
1962         return grub_error (GRUB_ERR_BAD_FS, "invalid chunk entry");
1963 
1964       if (grub_zfs_to_cpu64 (le->le_hash,endian) != h)
1965         continue;
1966 
1967       grub_dprintf ("zfs", "fzap: length %d\n", (int) le->le_name_length);
1968 
1969       if (zap_leaf_array_equal (l, endian, blksft, 
1970                                 grub_zfs_to_cpu16 (le->le_name_chunk,endian),
1971                                 grub_zfs_to_cpu16 (le->le_name_length, endian),
1972                                 name, case_insensitive))
1973         {
1974           struct zap_leaf_array *la;
1975 
1976           if (le->le_int_size != 8 || grub_zfs_to_cpu16 (le->le_value_length,
1977                                                          endian) != 1)
1978             return grub_error (GRUB_ERR_BAD_FS, "invalid leaf chunk entry");
1979 
1980           /* get the uint64_t property value */
1981           la = &ZAP_LEAF_CHUNK (l, blksft, le->le_value_chunk)->l_array;
1982 
1983           *value = grub_be_to_cpu64 (la->la_array64);
1984 
1985           return GRUB_ERR_NONE;
1986         }
1987     }
1988 
1989   return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), name);
1990 }
1991 
1992 
1993 /* Verify if this is a fat zap header block */
1994 static grub_err_t
1995 zap_verify (zap_phys_t *zap, grub_zfs_endian_t endian)
1996 {
1997   if (grub_zfs_to_cpu64 (zap->zap_magic, endian) != (grub_uint64_t) ZAP_MAGIC)
1998     return grub_error (GRUB_ERR_BAD_FS, "bad ZAP magic");
1999 
2000   if (zap->zap_salt == 0)
2001     return grub_error (GRUB_ERR_BAD_FS, "bad ZAP salt");
2002 
2003   return GRUB_ERR_NONE;
2004 }
2005 
2006 /*
2007  * Fat ZAP lookup
2008  *
2009  */
2010 /* XXX */
2011 static grub_err_t
2012 fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap,
2013              const char *name, grub_uint64_t * value,
2014              struct grub_zfs_data *data, int case_insensitive)
2015 {
2016   void *l;
2017   grub_uint64_t hash, idx, blkid;
2018   int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, 
2019                                             zap_dnode->endian) << DNODE_SHIFT);
2020   grub_err_t err;
2021   grub_zfs_endian_t leafendian;
2022 
2023   err = zap_verify (zap, zap_dnode->endian);
2024   if (err)
2025     return err;
2026 
2027   hash = zap_hash (zap->zap_salt, name, case_insensitive);
2028 
2029   /* get block id from index */
2030   if (zap->zap_ptrtbl.zt_numblks != 0)
2031     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, 
2032                        "external pointer tables not supported");
2033   idx = ZAP_HASH_IDX (hash, zap->zap_ptrtbl.zt_shift);
2034   blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], zap_dnode->endian);
2035 
2036   /* Get the leaf block */
2037   if ((1U << blksft) < sizeof (zap_leaf_phys_t))
2038     return grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small");
2039   err = dmu_read (zap_dnode, blkid, &l, &leafendian, data);
2040   if (err)
2041     return err;
2042 
2043   err = zap_leaf_lookup (l, leafendian, blksft, hash, name, value,
2044                          case_insensitive);
2045   grub_free (l);
2046   return err;
2047 }
2048 
2049 /* XXX */
2050 static int
2051 fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
2052               grub_size_t name_elem_length,
2053               int NESTED_FUNC_ATTR (*hook) (const void *name,
2054                                             grub_size_t name_length,
2055                                             const void *val_in,
2056                                             grub_size_t nelem,
2057                                             grub_size_t elemsize), 
2058               struct grub_zfs_data *data)
2059 {
2060   zap_leaf_phys_t *l;
2061   void *l_in;
2062   grub_uint64_t idx, idx2, blkid;
2063   grub_uint16_t chunk;
2064   int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, 
2065                                             zap_dnode->endian) << DNODE_SHIFT);
2066   grub_err_t err;
2067   grub_zfs_endian_t endian;
2068 
2069   if (zap_verify (zap, zap_dnode->endian))
2070     return 0;
2071 
2072   /* get block id from index */
2073   if (zap->zap_ptrtbl.zt_numblks != 0)
2074     {
2075       grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, 
2076                   "external pointer tables not supported");
2077       return 0;
2078     }
2079   /* Get the leaf block */
2080   if ((1U << blksft) < sizeof (zap_leaf_phys_t))
2081     {
2082       grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small");
2083       return 0;
2084     }
2085   for (idx = 0; idx < (1ULL << zap->zap_ptrtbl.zt_shift); idx++)
2086     {
2087       blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))],
2088                                          zap_dnode->endian);
2089 
2090       for (idx2 = 0; idx2 < idx; idx2++)
2091         if (blkid == grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))],
2092                                          zap_dnode->endian))
2093           break;
2094       if (idx2 != idx)
2095         continue;
2096 
2097       err = dmu_read (zap_dnode, blkid, &l_in, &endian, data);
2098       l = l_in;
2099       if (err)
2100         {
2101           grub_errno = GRUB_ERR_NONE;
2102           continue;
2103         }
2104 
2105       /* Verify if this is a valid leaf block */
2106       if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF)
2107         {
2108           grub_free (l);
2109           continue;
2110         }
2111       if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC)
2112         {
2113           grub_free (l);
2114           continue;
2115         }
2116 
2117       for (chunk = 0; chunk < ZAP_LEAF_NUMCHUNKS (blksft); chunk++)
2118           {
2119             char *buf;
2120             struct zap_leaf_entry *le;
2121             char *val;
2122             grub_size_t val_length;
2123             le = ZAP_LEAF_ENTRY (l, blksft, chunk);
2124 
2125             /* Verify the chunk entry */
2126             if (le->le_type != ZAP_CHUNK_ENTRY)
2127               continue;
2128 
2129             buf = grub_malloc (grub_zfs_to_cpu16 (le->le_name_length, endian)
2130                                * name_elem_length + 1);
2131             if (zap_leaf_array_get (l, endian, blksft,
2132                                     grub_zfs_to_cpu16 (le->le_name_chunk,
2133                                                        endian),
2134                                     grub_zfs_to_cpu16 (le->le_name_length,
2135                                                        endian)
2136                                     * name_elem_length, buf))
2137               {
2138                 grub_free (buf);
2139                 continue;
2140               }
2141             buf[le->le_name_length * name_elem_length] = 0;
2142 
2143             val_length = ((int) le->le_value_length
2144                           * (int) le->le_int_size);
2145             val = grub_malloc (grub_zfs_to_cpu16 (val_length, endian));
2146             if (zap_leaf_array_get (l, endian, blksft,
2147                                     grub_zfs_to_cpu16 (le->le_value_chunk,
2148                                                        endian),
2149                                     val_length, val))
2150               {
2151                 grub_free (buf);
2152                 grub_free (val);
2153                 continue;
2154               }
2155 
2156             if (hook (buf, le->le_name_length,
2157                       val, le->le_value_length, le->le_int_size))
2158               {
2159                 grub_free (l);
2160                 return 1;
2161               }
2162             grub_free (buf);
2163             grub_free (val);
2164           }
2165       grub_free (l);
2166     }
2167   return 0;
2168 }
2169 
2170 /*
2171  * Read in the data of a zap object and find the value for a matching
2172  * property name.
2173  *
2174  */
2175 static grub_err_t
2176 zap_lookup (dnode_end_t * zap_dnode, const char *name, grub_uint64_t *val,
2177             struct grub_zfs_data *data, int case_insensitive)
2178 {
2179   grub_uint64_t block_type;
2180   int size;
2181   void *zapbuf;
2182   grub_err_t err;
2183   grub_zfs_endian_t endian;
2184 
2185   grub_dprintf ("zfs", "looking for '%s'\n", name);
2186 
2187   /* Read in the first block of the zap object data. */
2188   size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, 
2189                             zap_dnode->endian) << SPA_MINBLOCKSHIFT;
2190   err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
2191   if (err)
2192     return err;
2193   block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
2194 
2195   grub_dprintf ("zfs", "zap read\n");
2196 
2197   if (block_type == ZBT_MICRO)
2198     {
2199       grub_dprintf ("zfs", "micro zap\n");
2200       err = mzap_lookup (zapbuf, endian, size, name, val,
2201                          case_insensitive);
2202       grub_dprintf ("zfs", "returned %d\n", err);      
2203       grub_free (zapbuf);
2204       return err;
2205     }
2206   else if (block_type == ZBT_HEADER)
2207     {
2208       grub_dprintf ("zfs", "fat zap\n");
2209       /* this is a fat zap */
2210       err = fzap_lookup (zap_dnode, zapbuf, name, val, data,
2211                          case_insensitive);
2212       grub_dprintf ("zfs", "returned %d\n", err);      
2213       grub_free (zapbuf);
2214       return err;
2215     }
2216 
2217   return grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type");
2218 }
2219 
2220 static int
2221 zap_iterate_u64 (dnode_end_t * zap_dnode, 
2222                  int NESTED_FUNC_ATTR (*hook) (const char *name,
2223                                                grub_uint64_t val),
2224                  struct grub_zfs_data *data)
2225 {
2226   grub_uint64_t block_type;
2227   int size;
2228   void *zapbuf;
2229   grub_err_t err;
2230   int ret;
2231   grub_zfs_endian_t endian;
2232 
2233   auto int NESTED_FUNC_ATTR transform (const void *name,
2234                                        grub_size_t namelen,
2235                                        const void *val_in,
2236                                        grub_size_t nelem,
2237                                        grub_size_t elemsize);
2238 
2239   int NESTED_FUNC_ATTR transform (const void *name,
2240                                   grub_size_t namelen __attribute__ ((unused)),
2241                                   const void *val_in,
2242                                   grub_size_t nelem,
2243                                   grub_size_t elemsize)
2244   {
2245     if (elemsize != sizeof (grub_uint64_t) || nelem != 1)
2246       return 0;
2247     return hook (name, grub_be_to_cpu64 (*(const grub_uint64_t *) val_in));
2248   }
2249 
2250   /* Read in the first block of the zap object data. */
2251   size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT;
2252   err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
2253   if (err)
2254     return 0;
2255   block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
2256 
2257   grub_dprintf ("zfs", "zap iterate\n");
2258 
2259   if (block_type == ZBT_MICRO)
2260     {
2261       grub_dprintf ("zfs", "micro zap\n");
2262       ret = mzap_iterate (zapbuf, endian, size, hook);
2263       grub_free (zapbuf);
2264       return ret;
2265     }
2266   else if (block_type == ZBT_HEADER)
2267     {
2268       grub_dprintf ("zfs", "fat zap\n");
2269       /* this is a fat zap */
2270       ret = fzap_iterate (zap_dnode, zapbuf, 1, transform, data);
2271       grub_free (zapbuf);
2272       return ret;
2273     }
2274   grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type");
2275   return 0;
2276 }
2277 
2278 static int
2279 zap_iterate (dnode_end_t * zap_dnode, 
2280              grub_size_t nameelemlen,
2281              int NESTED_FUNC_ATTR (*hook) (const void *name,
2282                                            grub_size_t namelen,
2283                                            const void *val_in,
2284                                            grub_size_t nelem,
2285                                            grub_size_t elemsize),
2286              struct grub_zfs_data *data)
2287 {
2288   grub_uint64_t block_type;
2289   void *zapbuf;
2290   grub_err_t err;
2291   int ret;
2292   grub_zfs_endian_t endian;
2293 
2294   /* Read in the first block of the zap object data. */
2295   err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
2296   if (err)
2297     return 0;
2298   block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
2299 
2300   grub_dprintf ("zfs", "zap iterate\n");
2301 
2302   if (block_type == ZBT_MICRO)
2303     {
2304       grub_error (GRUB_ERR_BAD_FS, "micro ZAP where FAT ZAP expected");
2305       return 0;
2306     }
2307   if (block_type == ZBT_HEADER)
2308     {
2309       grub_dprintf ("zfs", "fat zap\n");
2310       /* this is a fat zap */
2311       ret = fzap_iterate (zap_dnode, zapbuf, nameelemlen, hook, data);
2312       grub_free (zapbuf);
2313       return ret;
2314     }
2315   grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type");
2316   return 0;
2317 }
2318 
2319 
2320 /*
2321  * Get the dnode of an object number from the metadnode of an object set.
2322  *
2323  * Input
2324  *      mdn - metadnode to get the object dnode
2325  *      objnum - object number for the object dnode
2326  *      buf - data buffer that holds the returning dnode
2327  */
2328 static grub_err_t
2329 dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
2330            dnode_end_t * buf, struct grub_zfs_data *data)
2331 {
2332   grub_uint64_t blkid, blksz;   /* the block id this object dnode is in */
2333   int epbs;                     /* shift of number of dnodes in a block */
2334   int idx;                      /* index within a block */
2335   void *dnbuf;
2336   grub_err_t err;
2337   grub_zfs_endian_t endian;
2338 
2339   blksz = grub_zfs_to_cpu16 (mdn->dn.dn_datablkszsec, 
2340                              mdn->endian) << SPA_MINBLOCKSHIFT;
2341   epbs = zfs_log2 (blksz) - DNODE_SHIFT;
2342   blkid = objnum >> epbs;
2343   idx = objnum & ((1 << epbs) - 1);
2344 
2345   if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, mdn, 
2346                                               sizeof (*mdn)) == 0 
2347       && objnum >= data->dnode_start && objnum < data->dnode_end)
2348     {
2349       grub_memmove (&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE);
2350       buf->endian = data->dnode_endian;
2351       if (type && buf->dn.dn_type != type) 
2352         return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); 
2353       return GRUB_ERR_NONE;
2354     }
2355 
2356   grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian, 
2357                 (unsigned long long) blkid);
2358   err = dmu_read (mdn, blkid, &dnbuf, &endian, data);
2359   if (err)
2360     return err;
2361   grub_dprintf ("zfs", "alive\n");
2362 
2363   grub_free (data->dnode_buf);
2364   grub_free (data->dnode_mdn);
2365   data->dnode_mdn = grub_malloc (sizeof (*mdn));
2366   if (! data->dnode_mdn)
2367     {
2368       grub_errno = GRUB_ERR_NONE;
2369       data->dnode_buf = 0;
2370     }
2371   else
2372     {
2373       grub_memcpy (data->dnode_mdn, mdn, sizeof (*mdn));
2374       data->dnode_buf = dnbuf;
2375       data->dnode_start = blkid << epbs;
2376       data->dnode_end = (blkid + 1) << epbs;
2377       data->dnode_endian = endian;
2378     }
2379 
2380   grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE);
2381   buf->endian = endian;
2382   if (type && buf->dn.dn_type != type) 
2383     return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); 
2384 
2385   return GRUB_ERR_NONE;
2386 }
2387 
2388 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
2389 
2390 /*
2391  * Get the file dnode for a given file name where mdn is the meta dnode
2392  * for this ZFS object set. When found, place the file dnode in dn.
2393  * The 'path' argument will be mangled.
2394  *
2395  */
2396 static grub_err_t
2397 dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
2398                 struct grub_zfs_data *data)
2399 {
2400   grub_uint64_t objnum, version;
2401   char *cname, ch;
2402   grub_err_t err = GRUB_ERR_NONE;
2403   char *path, *path_buf;
2404   struct dnode_chain
2405   {
2406     struct dnode_chain *next;
2407     dnode_end_t dn; 
2408   };
2409   struct dnode_chain *dnode_path = 0, *dn_new, *root;
2410 
2411   dn_new = grub_malloc (sizeof (*dn_new));
2412   if (! dn_new)
2413     return grub_errno;
2414   dn_new->next = 0;
2415   dnode_path = root = dn_new;
2416 
2417   err = dnode_get (&subvol->mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, 
2418                    &(dnode_path->dn), data);
2419   if (err)
2420     {
2421       grub_free (dn_new);
2422       return err;
2423     }
2424 
2425   err = zap_lookup (&(dnode_path->dn), ZPL_VERSION_STR, &version,
2426                     data, 0);
2427   if (err)
2428     {
2429       grub_free (dn_new);
2430       return err;
2431     }
2432 
2433   if (version > ZPL_VERSION)
2434     {
2435       grub_free (dn_new);
2436       return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "too new ZPL version");
2437     }
2438 
2439   err = zap_lookup (&(dnode_path->dn), "casesensitivity",
2440                     &subvol->case_insensitive,
2441                     data, 0);
2442   if (err == GRUB_ERR_FILE_NOT_FOUND)
2443     {
2444       grub_errno = GRUB_ERR_NONE;
2445       subvol->case_insensitive = 0;
2446     }
2447 
2448   err = zap_lookup (&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data, 0);
2449   if (err)
2450     {
2451       grub_free (dn_new);
2452       return err;
2453     }
2454 
2455   err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data);
2456   if (err)
2457     {
2458       grub_free (dn_new);
2459       return err;
2460     }
2461 
2462   path = path_buf = grub_strdup (path_in);
2463   if (!path_buf)
2464     {
2465       grub_free (dn_new);
2466       return grub_errno;
2467     }
2468   
2469   while (1)
2470     {
2471       /* skip leading slashes */
2472       while (*path == '/')
2473         path++;
2474       if (!*path)
2475         break;
2476       /* get the next component name */
2477       cname = path;
2478       while (*path && *path != '/')
2479         path++;
2480       /* Skip dot.  */
2481       if (cname + 1 == path && cname[0] == '.')
2482         continue;
2483       /* Handle double dot.  */
2484       if (cname + 2 == path && cname[0] == '.' && cname[1] == '.')
2485         {
2486           if (dn_new->next)
2487             {
2488               dn_new = dnode_path;
2489               dnode_path = dn_new->next;
2490               grub_free (dn_new);
2491             }
2492           else
2493             {
2494               err = grub_error (GRUB_ERR_FILE_NOT_FOUND, 
2495                                 "can't resolve ..");
2496               break;
2497             }
2498           continue;
2499         }
2500 
2501       ch = *path;
2502       *path = 0;                /* ensure null termination */
2503 
2504       if (dnode_path->dn.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS)
2505         {
2506           grub_free (path_buf);
2507           return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
2508         }
2509       err = zap_lookup (&(dnode_path->dn), cname, &objnum,
2510                         data, subvol->case_insensitive);
2511       if (err)
2512         break;
2513 
2514       dn_new = grub_malloc (sizeof (*dn_new));
2515       if (! dn_new)
2516         {
2517           err = grub_errno;
2518           break;
2519         }
2520       dn_new->next = dnode_path;
2521       dnode_path = dn_new;
2522 
2523       objnum = ZFS_DIRENT_OBJ (objnum);
2524       err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data);
2525       if (err)
2526         break;
2527 
2528       *path = ch;
2529       if (dnode_path->dn.dn.dn_bonustype == DMU_OT_ZNODE
2530           && ((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa)
2531         {
2532           char *sym_value;
2533           grub_size_t sym_sz;
2534           int free_symval = 0;
2535           char *oldpath = path, *oldpathbuf = path_buf;
2536           sym_value = ((char *) DN_BONUS (&dnode_path->dn.dn) + sizeof (struct znode_phys));
2537 
2538           sym_sz = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_size, dnode_path->dn.endian);
2539 
2540           if (dnode_path->dn.dn.dn_flags & 1)
2541             {
2542               grub_size_t block;
2543               grub_size_t blksz;
2544               blksz = (grub_zfs_to_cpu16 (dnode_path->dn.dn.dn_datablkszsec, 
2545                                          dnode_path->dn.endian)
2546                        << SPA_MINBLOCKSHIFT);
2547 
2548               sym_value = grub_malloc (sym_sz);
2549               if (!sym_value)
2550                 return grub_errno;
2551               for (block = 0; block < (sym_sz + blksz - 1) / blksz; block++)
2552                 {
2553                   void *t;
2554                   grub_size_t movesize;
2555 
2556                   err = dmu_read (&(dnode_path->dn), block, &t, 0, data);
2557                   if (err)
2558                     return err;
2559 
2560                   movesize = sym_sz - block * blksz;
2561                   if (movesize > blksz)
2562                     movesize = blksz;
2563 
2564                   grub_memcpy (sym_value + block * blksz, t, movesize);
2565                   grub_free (t);
2566                 }
2567               free_symval = 1;
2568             }       
2569           path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1);
2570           if (!path_buf)
2571             {
2572               grub_free (oldpathbuf);
2573               return grub_errno;
2574             }
2575           grub_memcpy (path, sym_value, sym_sz);
2576           if (free_symval)
2577             grub_free (sym_value);
2578           path [sym_sz] = 0;
2579           grub_memcpy (path + grub_strlen (path), oldpath, 
2580                        grub_strlen (oldpath) + 1);
2581           
2582           grub_free (oldpathbuf);
2583           if (path[0] != '/')
2584             {
2585               dn_new = dnode_path;
2586               dnode_path = dn_new->next;
2587               grub_free (dn_new);
2588             }
2589           else while (dnode_path != root)
2590             {
2591               dn_new = dnode_path;
2592               dnode_path = dn_new->next;
2593               grub_free (dn_new);
2594             }
2595         }
2596       if (dnode_path->dn.dn.dn_bonustype == DMU_OT_SA)
2597         {
2598           void *sahdrp;
2599           int hdrsize;
2600           
2601           if (dnode_path->dn.dn.dn_bonuslen != 0)
2602             {
2603               sahdrp = DN_BONUS (&dnode_path->dn.dn);
2604             }
2605           else if (dnode_path->dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
2606             {
2607               blkptr_t *bp = &dnode_path->dn.dn.dn_spill;
2608               
2609               err = zio_read (bp, dnode_path->dn.endian, &sahdrp, NULL, data);
2610               if (err)
2611                 return err;
2612             }
2613           else
2614             {
2615               return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
2616             }
2617 
2618           hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
2619 
2620           if (((grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp
2621                                                          + hdrsize
2622                                                          + SA_TYPE_OFFSET),
2623                                    dnode_path->dn.endian) >> 12) & 0xf) == 0xa)
2624             {
2625               char *sym_value = (char *) sahdrp + hdrsize + SA_SYMLINK_OFFSET;
2626               grub_size_t sym_sz = 
2627                 grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp
2628                                                          + hdrsize
2629                                                          + SA_SIZE_OFFSET),
2630                                    dnode_path->dn.endian);
2631               char *oldpath = path, *oldpathbuf = path_buf;
2632               path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1);
2633               if (!path_buf)
2634                 {
2635                   grub_free (oldpathbuf);
2636                   return grub_errno;
2637                 }
2638               grub_memcpy (path, sym_value, sym_sz);
2639               path [sym_sz] = 0;
2640               grub_memcpy (path + grub_strlen (path), oldpath, 
2641                            grub_strlen (oldpath) + 1);
2642               
2643               grub_free (oldpathbuf);
2644               if (path[0] != '/')
2645                 {
2646                   dn_new = dnode_path;
2647                   dnode_path = dn_new->next;
2648                   grub_free (dn_new);
2649                 }
2650               else while (dnode_path != root)
2651                 {
2652                   dn_new = dnode_path;
2653                   dnode_path = dn_new->next;
2654                   grub_free (dn_new);
2655                 }
2656             }
2657         }
2658     }
2659 
2660   if (!err)
2661     grub_memcpy (dn, &(dnode_path->dn), sizeof (*dn));
2662 
2663   while (dnode_path)
2664     {
2665       dn_new = dnode_path->next;
2666       grub_free (dnode_path);
2667       dnode_path = dn_new;
2668     }
2669   grub_free (path_buf);
2670   return err;
2671 }
2672 
2673 #if 0
2674 /*
2675  * Get the default 'bootfs' property value from the rootpool.
2676  *
2677  */
2678 static grub_err_t
2679 get_default_bootfsobj (dnode_phys_t * mosmdn, grub_uint64_t * obj,
2680                        struct grub_zfs_data *data)
2681 {
2682   grub_uint64_t objnum = 0;
2683   dnode_phys_t *dn;
2684   if (!dn)
2685     return grub_errno;
2686 
2687   if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT,
2688                                DMU_OT_OBJECT_DIRECTORY, dn, data)))
2689     {
2690       grub_free (dn);
2691       return (grub_errno);
2692     }
2693 
2694   /*
2695    * find the object number for 'pool_props', and get the dnode
2696    * of the 'pool_props'.
2697    */
2698   if (zap_lookup (dn, DMU_POOL_PROPS, &objnum, data))
2699     {
2700       grub_free (dn);
2701       return (GRUB_ERR_BAD_FS);
2702     }
2703   if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, dn, data)))
2704     {
2705       grub_free (dn);
2706       return (grub_errno);
2707     }
2708   if (zap_lookup (dn, ZPOOL_PROP_BOOTFS, &objnum, data))
2709     {
2710       grub_free (dn);
2711       return (GRUB_ERR_BAD_FS);
2712     }
2713 
2714   if (!objnum)
2715     {
2716       grub_free (dn);
2717       return (GRUB_ERR_BAD_FS);
2718     }
2719 
2720   *obj = objnum;
2721   return (0);
2722 }
2723 #endif
2724 /*
2725  * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname),
2726  * e.g. pool/rootfs, or a given object number (obj), e.g. the object number
2727  * of pool/rootfs.
2728  *
2729  * If no fsname and no obj are given, return the DSL_DIR metadnode.
2730  * If fsname is given, return its metadnode and its matching object number.
2731  * If only obj is given, return the metadnode for this object number.
2732  *
2733  */
2734 static grub_err_t
2735 get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname,
2736                       dnode_end_t * mdn, struct grub_zfs_data *data)
2737 {
2738   grub_uint64_t objnum;
2739   grub_err_t err;
2740 
2741   grub_dprintf ("zfs", "endian = %d\n", mosmdn->endian);
2742 
2743   err = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT, 
2744                    DMU_OT_OBJECT_DIRECTORY, mdn, data);
2745   if (err)
2746     return err;
2747 
2748   grub_dprintf ("zfs", "alive\n");
2749 
2750   err = zap_lookup (mdn, DMU_POOL_ROOT_DATASET, &objnum, data, 0);
2751   if (err)
2752     return err;
2753 
2754   grub_dprintf ("zfs", "alive\n");
2755 
2756   err = dnode_get (mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data);
2757   if (err)
2758     return err;
2759 
2760   grub_dprintf ("zfs", "alive\n");
2761 
2762   while (*fsname)
2763     {
2764       grub_uint64_t childobj;
2765       char *cname, ch;
2766  
2767       while (*fsname == '/')
2768         fsname++;
2769 
2770       if (! *fsname || *fsname == '@')
2771         break;
2772 
2773       cname = fsname;
2774       while (*fsname && *fsname != '/')
2775         fsname++;
2776       ch = *fsname;
2777       *fsname = 0;
2778 
2779       childobj = grub_zfs_to_cpu64 ((((dsl_dir_phys_t *) DN_BONUS (&mdn->dn)))->dd_child_dir_zapobj, mdn->endian);
2780       err = dnode_get (mosmdn, childobj,
2781                        DMU_OT_DSL_DIR_CHILD_MAP, mdn, data);
2782       if (err)
2783         return err;
2784 
2785       err = zap_lookup (mdn, cname, &objnum, data, 0);
2786       if (err)
2787         return err;
2788 
2789       err = dnode_get (mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data);
2790       if (err)
2791         return err;
2792 
2793       *fsname = ch;
2794     }
2795   return GRUB_ERR_NONE;
2796 }
2797 
2798 static grub_err_t
2799 make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data)
2800 {
2801   void *osp;
2802   blkptr_t *bp;
2803   grub_size_t ospsize;
2804   grub_err_t err;
2805 
2806   grub_dprintf ("zfs", "endian = %d\n", mdn->endian);
2807 
2808   bp = &(((dsl_dataset_phys_t *) DN_BONUS (&mdn->dn))->ds_bp);
2809   err = zio_read (bp, mdn->endian, &osp, &ospsize, data);
2810   if (err)
2811     return err;
2812   if (ospsize < OBJSET_PHYS_SIZE_V14)
2813     {
2814       grub_free (osp);
2815       return grub_error (GRUB_ERR_BAD_FS, "too small osp");
2816     }
2817 
2818   mdn->endian = (grub_zfs_to_cpu64 (bp->blk_prop, mdn->endian)>>63) & 1;
2819   grub_memmove ((char *) &(mdn->dn),
2820                 (char *) &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE);
2821   grub_free (osp);
2822   return GRUB_ERR_NONE;
2823 }
2824 
2825 static grub_err_t
2826 dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
2827                     dnode_end_t * dn, int *isfs,
2828                     struct grub_zfs_data *data)
2829 {
2830   char *fsname, *snapname;
2831   const char *ptr_at, *filename;
2832   grub_uint64_t headobj;
2833   grub_uint64_t keychainobj;
2834   grub_uint64_t salt;
2835   grub_err_t err;
2836   int keyn = 0;
2837 
2838   auto int NESTED_FUNC_ATTR count_zap_keys (const void *name,
2839                                           grub_size_t namelen,
2840                                           const void *val_in,
2841                                           grub_size_t nelem,
2842                                           grub_size_t elemsize);
2843   int NESTED_FUNC_ATTR count_zap_keys (const void *name __attribute__ ((unused)),
2844                                        grub_size_t namelen __attribute__ ((unused)),
2845                                        const void *val_in __attribute__ ((unused)),
2846                                        grub_size_t nelem __attribute__ ((unused)),
2847                                        grub_size_t elemsize __attribute__ ((unused)))
2848   {
2849     subvol->nkeys++;
2850     return 0;
2851   }
2852 
2853   auto int NESTED_FUNC_ATTR load_zap_key (const void *name,
2854                                           grub_size_t namelen,
2855                                           const void *val_in,
2856                                           grub_size_t nelem,
2857                                           grub_size_t elemsize);
2858   int NESTED_FUNC_ATTR load_zap_key (const void *name,
2859                                      grub_size_t namelen,
2860                                      const void *val_in,
2861                                      grub_size_t nelem,
2862                                      grub_size_t elemsize)
2863   {
2864     if (namelen != 1)
2865       {
2866         grub_dprintf ("zfs", "Unexpected key index size %" PRIuGRUB_SIZE "\n",
2867                       namelen);
2868         return 0;
2869       }
2870 
2871     if (elemsize != 1)
2872       {
2873         grub_dprintf ("zfs", "Unexpected key element size %" PRIuGRUB_SIZE "\n",
2874                       elemsize);
2875         return 0;
2876       }
2877 
2878     subvol->keyring[keyn].txg = grub_be_to_cpu64 (*(grub_uint64_t *) name);
2879     subvol->keyring[keyn].algo = grub_le_to_cpu64 (*(grub_uint64_t *) val_in);
2880     subvol->keyring[keyn].cipher = grub_zfs_load_key (val_in, nelem, salt,
2881                                                       subvol->keyring[keyn].algo);
2882     keyn++;
2883     return 0;
2884   }
2885 
2886   ptr_at = grub_strchr (fullpath, '@');
2887   if (! ptr_at)
2888     {
2889       *isfs = 1;
2890       filename = 0;
2891       snapname = 0;
2892       fsname = grub_strdup (fullpath);
2893     }
2894   else
2895     {
2896       const char *ptr_slash = grub_strchr (ptr_at, '/');
2897 
2898       *isfs = 0;
2899       fsname = grub_malloc (ptr_at - fullpath + 1);
2900       if (!fsname)
2901         return grub_errno;
2902       grub_memcpy (fsname, fullpath, ptr_at - fullpath);
2903       fsname[ptr_at - fullpath] = 0;
2904       if (ptr_at[1] && ptr_at[1] != '/')
2905         {
2906           snapname = grub_malloc (ptr_slash - ptr_at);
2907           if (!snapname)
2908             {
2909               grub_free (fsname);
2910               return grub_errno;
2911             }
2912           grub_memcpy (snapname, ptr_at + 1, ptr_slash - ptr_at - 1);
2913           snapname[ptr_slash - ptr_at - 1] = 0;
2914         }
2915       else
2916         snapname = 0;
2917       if (ptr_slash)
2918         filename = ptr_slash;
2919       else
2920         filename = "/";
2921       grub_dprintf ("zfs", "fsname = '%s' snapname='%s' filename = '%s'\n", 
2922                     fsname, snapname, filename);
2923     }
2924   grub_dprintf ("zfs", "alive\n");
2925   err = get_filesystem_dnode (&(data->mos), fsname, dn, data);
2926   if (err)
2927     {
2928       grub_free (fsname);
2929       grub_free (snapname);
2930       return err;
2931     }
2932 
2933   grub_dprintf ("zfs", "alive\n");
2934 
2935   headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_head_dataset_obj, dn->endian);
2936 
2937   grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
2938 
2939   err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &subvol->mdn,
2940                    data);
2941   if (err)
2942     {
2943       grub_free (fsname);
2944       grub_free (snapname);
2945       return err;
2946     }
2947   grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
2948 
2949   keychainobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->keychain, dn->endian);
2950   if (grub_zfs_load_key && keychainobj)
2951     {
2952       dnode_end_t keychain_dn, props_dn;
2953       grub_uint64_t propsobj;
2954       propsobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_props_zapobj, dn->endian);
2955 
2956       err = dnode_get (&(data->mos), propsobj, DMU_OT_DSL_PROPS,
2957                        &props_dn, data);
2958       if (err)
2959         {
2960           grub_free (fsname);
2961           grub_free (snapname);
2962           return err;
2963         }
2964 
2965       err = zap_lookup (&props_dn, "salt", &salt, data, 0);
2966       if (err == GRUB_ERR_FILE_NOT_FOUND)
2967         {
2968           err = 0;
2969           grub_errno = 0;
2970           salt = 0;
2971         }
2972       if (err)
2973         {
2974           grub_dprintf ("zfs", "failed here\n");
2975           return err;
2976         }
2977 
2978       err = dnode_get (&(data->mos), keychainobj, DMU_OT_DSL_KEYCHAIN,
2979                        &keychain_dn, data);
2980       if (err)
2981         {
2982           grub_free (fsname);
2983           grub_free (snapname);
2984           return err;
2985         }
2986       subvol->nkeys = 0;
2987       zap_iterate (&keychain_dn, 8, count_zap_keys, data);
2988       subvol->keyring = grub_zalloc (subvol->nkeys * sizeof (subvol->keyring[0]));
2989       if (!subvol->keyring)
2990         {
2991           grub_free (fsname);
2992           grub_free (snapname);
2993           return err;
2994         }
2995       zap_iterate (&keychain_dn, 8, load_zap_key, data);
2996     }
2997 
2998   if (snapname)
2999     {
3000       grub_uint64_t snapobj;
3001 
3002       snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&subvol->mdn.dn))->ds_snapnames_zapobj, subvol->mdn.endian);
3003 
3004       err = dnode_get (&(data->mos), snapobj, 
3005                        DMU_OT_DSL_DS_SNAP_MAP, &subvol->mdn, data);
3006       if (!err)
3007         err = zap_lookup (&subvol->mdn, snapname, &headobj, data, 0);
3008       if (!err)
3009         err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET,
3010                          &subvol->mdn, data);
3011       if (err)
3012         {
3013           grub_free (fsname);
3014           grub_free (snapname);
3015           return err;
3016         }
3017     }
3018 
3019   subvol->obj = headobj;
3020 
3021   make_mdn (&subvol->mdn, data);
3022   
3023   grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
3024 
3025   if (*isfs)
3026     {
3027       grub_free (fsname);
3028       grub_free (snapname);      
3029       return GRUB_ERR_NONE;
3030     }
3031   err = dnode_get_path (subvol, filename, dn, data);
3032   grub_free (fsname);
3033   grub_free (snapname);
3034   return err;
3035 }
3036 
3037 /*
3038  * For a given XDR packed nvlist, verify the first 4 bytes and move on.
3039  *
3040  * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) :
3041  *
3042  *      encoding method/host endian     (4 bytes)
3043  *      nvl_version                     (4 bytes)
3044  *      nvl_nvflag                      (4 bytes)
3045  *      encoded nvpairs:
3046  *              encoded size of the nvpair      (4 bytes)
3047  *              decoded size of the nvpair      (4 bytes)
3048  *              name string size                (4 bytes)
3049  *              name string data                (sizeof(NV_ALIGN4(string))
3050  *              data type                       (4 bytes)
3051  *              # of elements in the nvpair     (4 bytes)
3052  *              data
3053  *      2 zero's for the last nvpair
3054  *              (end of the entire list)        (8 bytes)
3055  *
3056  */
3057 
3058 static int
3059 nvlist_find_value (const char *nvlist_in, const char *name,
3060                    int valtype, char **val,
3061                    grub_size_t *size_out, grub_size_t *nelm_out)
3062 {
3063   int name_len, type, encode_size;
3064   const char *nvpair, *nvp_name, *nvlist = nvlist_in;
3065 
3066   /* Verify if the 1st and 2nd byte in the nvlist are valid. */
3067   /* NOTE: independently of what endianness header announces all 
3068      subsequent values are big-endian.  */
3069   if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN 
3070                                      && nvlist[1] != NV_BIG_ENDIAN))
3071     {
3072       grub_dprintf ("zfs", "incorrect nvlist header\n");
3073       grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist");
3074       return 0;
3075     }
3076 
3077   /* skip the header, nvl_version, and nvl_nvflag */
3078   nvlist = nvlist + 4 * 3;
3079   /*
3080    * Loop thru the nvpair list
3081    * The XDR representation of an integer is in big-endian byte order.
3082    */
3083   while ((encode_size = grub_be_to_cpu32 (grub_get_unaligned32 (nvlist))))
3084     {
3085       int nelm;
3086 
3087       if (nvlist + 4 * 4 >= nvlist_in + VDEV_PHYS_SIZE)
3088         {
3089           grub_dprintf ("zfs", "nvlist overflow\n");
3090           grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist");
3091           return 0;
3092         }
3093 
3094       nvpair = nvlist + 4 * 2;  /* skip the encode/decode size */
3095 
3096       name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
3097       nvpair += 4;
3098 
3099       nvp_name = nvpair;
3100       nvpair = nvpair + ((name_len + 3) & ~3);      /* align */
3101 
3102       if (nvpair + 8 >= nvlist_in + VDEV_PHYS_SIZE
3103           || encode_size < 0
3104           || nvpair + 8 + encode_size > nvlist_in + VDEV_PHYS_SIZE)
3105         {
3106           grub_dprintf ("zfs", "nvlist overflow\n");
3107           grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist");
3108           return 0;
3109         }
3110 
3111       type = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
3112       nvpair += 4;
3113 
3114       nelm = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
3115       if (nelm < 1)
3116         {
3117           grub_error (GRUB_ERR_BAD_FS, "empty nvpair");
3118           return 0;
3119         }
3120 
3121       nvpair += 4;
3122 
3123       if ((grub_strncmp (nvp_name, name, name_len) == 0) && type == valtype)
3124         {
3125           *val = (char *) nvpair;
3126           *size_out = encode_size;
3127           if (nelm_out)
3128             *nelm_out = nelm;
3129           return 1;
3130         }
3131 
3132       nvlist += encode_size;    /* goto the next nvpair */
3133     }
3134   return 0;
3135 }
3136 
3137 int
3138 grub_zfs_nvlist_lookup_uint64 (const char *nvlist, const char *name,
3139                                grub_uint64_t * out)
3140 {
3141   char *nvpair;
3142   grub_size_t size;
3143   int found;
3144 
3145   found = nvlist_find_value (nvlist, name, DATA_TYPE_UINT64, &nvpair, &size, 0);
3146   if (!found)
3147     return 0;
3148   if (size < sizeof (grub_uint64_t))
3149     {
3150       grub_error (GRUB_ERR_BAD_FS, "invalid uint64");
3151       return 0;
3152     }
3153 
3154   *out = grub_be_to_cpu64 (grub_get_unaligned64 (nvpair));
3155   return 1;
3156 }
3157 
3158 char *
3159 grub_zfs_nvlist_lookup_string (const char *nvlist, const char *name)
3160 {
3161   char *nvpair;
3162   char *ret;
3163   grub_size_t slen;
3164   grub_size_t size;
3165   int found;
3166 
3167   found = nvlist_find_value (nvlist, name, DATA_TYPE_STRING, &nvpair, &size, 0);
3168   if (!found)
3169     return 0;
3170   if (size < 4)
3171     {
3172       grub_error (GRUB_ERR_BAD_FS, "invalid string");
3173       return 0;
3174     }
3175   slen = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
3176   if (slen > size - 4)
3177     slen = size - 4;
3178   ret = grub_malloc (slen + 1);
3179   if (!ret)
3180     return 0;
3181   grub_memcpy (ret, nvpair + 4, slen);
3182   ret[slen] = 0;
3183   return ret;
3184 }
3185 
3186 char *
3187 grub_zfs_nvlist_lookup_nvlist (const char *nvlist, const char *name)
3188 {
3189   char *nvpair;
3190   char *ret;
3191   grub_size_t size;
3192   int found;
3193 
3194   found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST, &nvpair,
3195                              &size, 0);
3196   if (!found)
3197     return 0;
3198   ret = grub_zalloc (size + 3 * sizeof (grub_uint32_t));
3199   if (!ret)
3200     return 0;
3201   grub_memcpy (ret, nvlist, sizeof (grub_uint32_t));
3202 
3203   grub_memcpy (ret + sizeof (grub_uint32_t), nvpair, size);
3204   return ret;
3205 }
3206 
3207 int
3208 grub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist,
3209                                               const char *name)
3210 {
3211   char *nvpair;
3212   grub_size_t nelm, size;
3213   int found;
3214 
3215   found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair,
3216                              &size, &nelm);
3217   if (! found)
3218     return -1;
3219   return nelm;
3220 }
3221 
3222 static int
3223 get_nvlist_size (const char *beg, const char *limit)
3224 {
3225   const char *ptr;
3226   grub_uint32_t encode_size;
3227   
3228   ptr = beg + 8;
3229 
3230   while (ptr < limit
3231          && (encode_size = grub_be_to_cpu32 (grub_get_unaligned32 (ptr))))
3232     ptr += encode_size; /* goto the next nvpair */
3233   ptr += 8;      
3234   return (ptr > limit) ? -1 : (ptr - beg);
3235 }
3236 
3237 char *
3238 grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, const char *name,
3239                                      grub_size_t index)
3240 {
3241   char *nvpair, *nvpairptr;
3242   int found;
3243   char *ret;
3244   grub_size_t size;
3245   unsigned i;
3246   grub_size_t nelm;
3247   int elemsize = 0;
3248 
3249   found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair,
3250                              &size, &nelm);
3251   if (!found)
3252     return 0;
3253   if (index >= nelm)
3254     {
3255       grub_error (GRUB_ERR_OUT_OF_RANGE, "trying to lookup past nvlist array");
3256       return 0;
3257     }
3258 
3259   nvpairptr = nvpair;
3260 
3261   for (i = 0; i < index; i++)
3262     {
3263       int r;
3264       r = get_nvlist_size (nvpairptr, nvpair + size);
3265 
3266       if (r < 0)
3267         {
3268           grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array");
3269           return NULL;
3270         }
3271       nvpairptr += r;
3272     }
3273 
3274   elemsize = get_nvlist_size (nvpairptr, nvpair + size);
3275 
3276   if (elemsize < 0)
3277     {
3278       grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array");
3279       return 0;
3280     }
3281 
3282   ret = grub_zalloc (elemsize + sizeof (grub_uint32_t));
3283   if (!ret)
3284     return 0;
3285   grub_memcpy (ret, nvlist, sizeof (grub_uint32_t));
3286 
3287   grub_memcpy (ret + sizeof (grub_uint32_t), nvpairptr, elemsize);
3288   return ret;
3289 }
3290 
3291 static void
3292 unmount_device (struct grub_zfs_device_desc *desc)
3293 {
3294   unsigned i;
3295   switch (desc->type)
3296     {
3297     case DEVICE_LEAF:
3298       if (!desc->original && desc->dev)
3299         grub_device_close (desc->dev);
3300       return;
3301     case DEVICE_RAIDZ:
3302     case DEVICE_MIRROR:
3303       for (i = 0; i < desc->n_children; i++)
3304         unmount_device (&desc->children[i]);
3305       grub_free (desc->children);
3306       return;
3307     }
3308 }
3309 
3310 static void
3311 zfs_unmount (struct grub_zfs_data *data)
3312 {
3313   unsigned i;
3314   for (i = 0; i < data->n_devices_attached; i++)
3315     unmount_device (&data->devices_attached[i]);
3316   grub_free (data->devices_attached);
3317   grub_free (data->dnode_buf);
3318   grub_free (data->dnode_mdn);
3319   grub_free (data->file_buf);
3320   for (i = 0; i < data->subvol.nkeys; i++)
3321     grub_crypto_cipher_close (data->subvol.keyring[i].cipher);
3322   grub_free (data->subvol.keyring);
3323   grub_free (data);
3324 }
3325 
3326 /*
3327  * zfs_mount() locates a valid uberblock of the root pool and read in its MOS
3328  * to the memory address MOS.
3329  *
3330  */
3331 static struct grub_zfs_data *
3332 zfs_mount (grub_device_t dev)
3333 {
3334   struct grub_zfs_data *data = 0;
3335   grub_err_t err;
3336   void *osp = 0;
3337   grub_size_t ospsize;
3338   grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
3339   uberblock_t *ub;
3340   int inserted;
3341 
3342   if (! dev->disk)
3343     {
3344       grub_error (GRUB_ERR_BAD_DEVICE, "not a disk");
3345       return 0;
3346     }
3347 
3348   data = grub_zalloc (sizeof (*data));
3349   if (!data)
3350     return 0;
3351 #if 0
3352   /* if it's our first time here, zero the best uberblock out */
3353   if (data->best_drive == 0 && data->best_part == 0 && find_best_root)
3354     grub_memset (&current_uberblock, 0, sizeof (uberblock_t));
3355 #endif
3356 
3357   data->n_devices_allocated = 16;
3358   data->devices_attached = grub_malloc (sizeof (data->devices_attached[0])
3359                                         * data->n_devices_allocated);
3360   data->n_devices_attached = 0;
3361   err = scan_disk (dev, data, 1, &inserted);
3362   if (err)
3363     {
3364       zfs_unmount (data);
3365       return NULL;
3366     }
3367 
3368   ub = &(data->current_uberblock);
3369   ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, 
3370                                   GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC 
3371                ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN);
3372 
3373   err = zio_read (&ub->ub_rootbp, ub_endian,
3374                   &osp, &ospsize, data);
3375   if (err)
3376     {
3377       zfs_unmount (data);
3378       return NULL;
3379     }
3380 
3381   if (ospsize < OBJSET_PHYS_SIZE_V14)
3382     {
3383       grub_error (GRUB_ERR_BAD_FS, "OSP too small");
3384       grub_free (osp);
3385       zfs_unmount (data);
3386       return NULL;
3387     }
3388 
3389   /* Got the MOS. Save it at the memory addr MOS. */
3390   grub_memmove (&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode,
3391                 DNODE_SIZE);
3392   data->mos.endian = (grub_zfs_to_cpu64 (ub->ub_rootbp.blk_prop,
3393                                          ub_endian) >> 63) & 1;
3394   grub_free (osp);
3395 
3396   data->mounted = 1;
3397 
3398   return data;
3399 }
3400 
3401 grub_err_t
3402 grub_zfs_fetch_nvlist (grub_device_t dev, char **nvlist)
3403 {
3404   struct grub_zfs_data *zfs;
3405   grub_err_t err;
3406 
3407   zfs = zfs_mount (dev);
3408   if (!zfs)
3409     return grub_errno;
3410   err = zfs_fetch_nvlist (zfs->device_original, nvlist);
3411   zfs_unmount (zfs);
3412   return err;
3413 }
3414 
3415 static grub_err_t 
3416 zfs_label (grub_device_t device, char **label)
3417 {
3418   char *nvlist;
3419   grub_err_t err;
3420   struct grub_zfs_data *data;
3421 
3422   data = zfs_mount (device);
3423   if (! data)
3424     return grub_errno;
3425 
3426   err = zfs_fetch_nvlist (data->device_original, &nvlist);
3427   if (err)      
3428     {
3429       zfs_unmount (data);
3430       return err;
3431     }
3432 
3433   *label = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME);
3434   grub_free (nvlist);
3435   zfs_unmount (data);
3436   return grub_errno;
3437 }
3438 
3439 static grub_err_t 
3440 zfs_uuid (grub_device_t device, char **uuid)
3441 {
3442   struct grub_zfs_data *data;
3443 
3444   *uuid = 0;
3445 
3446   data = zfs_mount (device);
3447   if (! data)
3448     return grub_errno;
3449 
3450   *uuid = grub_xasprintf ("%016llx", (long long unsigned) data->guid);
3451   zfs_unmount (data);
3452   if (! *uuid)
3453     return grub_errno;
3454   return GRUB_ERR_NONE;
3455 }
3456 
3457 static grub_err_t 
3458 zfs_mtime (grub_device_t device, grub_int32_t *mt)
3459 {
3460   struct grub_zfs_data *data;
3461   grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
3462   uberblock_t *ub;
3463 
3464   *mt = 0;
3465 
3466   data = zfs_mount (device);
3467   if (! data)
3468     return grub_errno;
3469 
3470   ub = &(data->current_uberblock);
3471   ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, 
3472                                   GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC 
3473                ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN);
3474 
3475   *mt = grub_zfs_to_cpu64 (ub->ub_timestamp, ub_endian);
3476   zfs_unmount (data);
3477   return GRUB_ERR_NONE;
3478 }
3479 
3480 /*
3481  * zfs_open() locates a file in the rootpool by following the
3482  * MOS and places the dnode of the file in the memory address DNODE.
3483  */
3484 static grub_err_t
3485 grub_zfs_open (struct grub_file *file, const char *fsfilename)
3486 {
3487   struct grub_zfs_data *data;
3488   grub_err_t err;
3489   int isfs;
3490 
3491   data = zfs_mount (file->device);
3492   if (! data)
3493     return grub_errno;
3494 
3495   err = dnode_get_fullpath (fsfilename, &(data->subvol),
3496                             &(data->dnode), &isfs, data);
3497   if (err)
3498     {
3499       zfs_unmount (data);
3500       return err;
3501     }
3502 
3503   if (isfs)
3504     {
3505       zfs_unmount (data);
3506       return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("missing `%c' symbol"), '@');
3507     }
3508 
3509   /* We found the dnode for this file. Verify if it is a plain file. */
3510   if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS) 
3511     {
3512       zfs_unmount (data);
3513       return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
3514     }
3515 
3516   /* get the file size and set the file position to 0 */
3517 
3518   /*
3519    * For DMU_OT_SA we will need to locate the SIZE attribute
3520    * attribute, which could be either in the bonus buffer
3521    * or the "spill" block.
3522    */
3523   if (data->dnode.dn.dn_bonustype == DMU_OT_SA)
3524     {
3525       void *sahdrp;
3526       int hdrsize;
3527 
3528       if (data->dnode.dn.dn_bonuslen != 0)
3529         {
3530           sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn);
3531         }
3532       else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
3533         {
3534           blkptr_t *bp = &data->dnode.dn.dn_spill;
3535 
3536           err = zio_read (bp, data->dnode.endian, &sahdrp, NULL, data);
3537           if (err)
3538             return err;
3539         }
3540       else
3541         {
3542           return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
3543         }
3544 
3545       hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
3546       file->size = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), data->dnode.endian);
3547     }
3548   else if (data->dnode.dn.dn_bonustype == DMU_OT_ZNODE)
3549     {
3550       file->size = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&data->dnode.dn))->zp_size, data->dnode.endian);
3551     }
3552   else
3553     return grub_error (GRUB_ERR_BAD_FS, "bad bonus type");
3554 
3555   file->data = data;
3556   file->offset = 0;
3557 
3558 #ifndef GRUB_UTIL
3559   grub_dl_ref (my_mod);
3560 #endif
3561 
3562   return GRUB_ERR_NONE;
3563 }
3564 
3565 static grub_ssize_t
3566 grub_zfs_read (grub_file_t file, char *buf, grub_size_t len)
3567 {
3568   struct grub_zfs_data *data = (struct grub_zfs_data *) file->data;
3569   grub_size_t blksz, movesize;
3570   grub_size_t length;
3571   grub_size_t read;
3572   grub_err_t err;
3573 
3574   /*
3575    * If offset is in memory, move it into the buffer provided and return.
3576    */
3577   if (file->offset >= data->file_start
3578       && file->offset + len <= data->file_end)
3579     {
3580       grub_memmove (buf, data->file_buf + file->offset - data->file_start,
3581                     len);
3582       return len;
3583     }
3584 
3585   blksz = grub_zfs_to_cpu16 (data->dnode.dn.dn_datablkszsec, 
3586                              data->dnode.endian) << SPA_MINBLOCKSHIFT;
3587 
3588   /*
3589    * Entire Dnode is too big to fit into the space available.  We
3590    * will need to read it in chunks.  This could be optimized to
3591    * read in as large a chunk as there is space available, but for
3592    * now, this only reads in one data block at a time.
3593    */
3594   length = len;
3595   read = 0;
3596   while (length)
3597     {
3598       void *t;
3599       /*
3600        * Find requested blkid and the offset within that block.
3601        */
3602       grub_uint64_t blkid = grub_divmod64 (file->offset + read, blksz, 0);
3603       grub_free (data->file_buf);
3604       data->file_buf = 0;
3605 
3606       err = dmu_read (&(data->dnode), blkid, &t,
3607                       0, data);
3608       data->file_buf = t;
3609       if (err)
3610         {
3611           data->file_buf = NULL;
3612           data->file_start = data->file_end = 0;
3613           return -1;
3614         }
3615 
3616       data->file_start = blkid * blksz;
3617       data->file_end = data->file_start + blksz;
3618 
3619       movesize = data->file_end - file->offset - read;
3620       if (movesize > length)
3621         movesize = length;
3622 
3623       grub_memmove (buf, data->file_buf + file->offset + read
3624                     - data->file_start, movesize);
3625       buf += movesize;
3626       length -= movesize;
3627       read += movesize;
3628     }
3629 
3630   return len;
3631 }
3632 
3633 static grub_err_t
3634 grub_zfs_close (grub_file_t file)
3635 {
3636   zfs_unmount ((struct grub_zfs_data *) file->data);
3637 
3638 #ifndef GRUB_UTIL
3639   grub_dl_unref (my_mod);
3640 #endif
3641 
3642   return GRUB_ERR_NONE;
3643 }
3644 
3645 grub_err_t
3646 grub_zfs_getmdnobj (grub_device_t dev, const char *fsfilename,
3647                     grub_uint64_t *mdnobj)
3648 {
3649   struct grub_zfs_data *data;
3650   grub_err_t err;
3651   int isfs;
3652 
3653   data = zfs_mount (dev);
3654   if (! data)
3655     return grub_errno;
3656 
3657   err = dnode_get_fullpath (fsfilename, &(data->subvol),
3658                             &(data->dnode), &isfs, data);
3659   *mdnobj = data->subvol.obj;
3660   zfs_unmount (data);
3661   return err;
3662 }
3663 
3664 static void
3665 fill_fs_info (struct grub_dirhook_info *info,
3666               dnode_end_t mdn, struct grub_zfs_data *data)
3667 {
3668   grub_err_t err;
3669   dnode_end_t dn;
3670   grub_uint64_t objnum;
3671   grub_uint64_t headobj;
3672   
3673   grub_memset (info, 0, sizeof (*info));
3674     
3675   info->dir = 1;
3676   
3677   if (mdn.dn.dn_type == DMU_OT_DSL_DIR)
3678     {
3679       headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&mdn.dn))->dd_head_dataset_obj, mdn.endian);
3680 
3681       err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &mdn, data);
3682       if (err)
3683         {
3684           grub_dprintf ("zfs", "failed here\n");
3685           return;
3686         }
3687     }
3688   make_mdn (&mdn, data);
3689   err = dnode_get (&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, 
3690                    &dn, data);
3691   if (err)
3692     {
3693       grub_dprintf ("zfs", "failed here\n");
3694       return;
3695     }
3696   
3697   err = zap_lookup (&dn, ZFS_ROOT_OBJ, &objnum, data, 0);
3698   if (err)
3699     {
3700       grub_dprintf ("zfs", "failed here\n");
3701       return;
3702     }
3703   
3704   err = dnode_get (&mdn, objnum, 0, &dn, data);
3705   if (err)
3706     {
3707       grub_dprintf ("zfs", "failed here\n");
3708       return;
3709     }
3710   
3711   if (dn.dn.dn_bonustype == DMU_OT_SA)
3712     {
3713       void *sahdrp;
3714       int hdrsize;
3715 
3716       if (dn.dn.dn_bonuslen != 0)
3717         {
3718           sahdrp = (sa_hdr_phys_t *) DN_BONUS (&dn.dn);
3719         }
3720       else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
3721         {
3722           blkptr_t *bp = &dn.dn.dn_spill;
3723 
3724           err = zio_read (bp, dn.endian, &sahdrp, NULL, data);
3725           if (err)
3726             return;
3727         }
3728       else
3729         {
3730           grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
3731           return;
3732         }
3733 
3734       hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
3735       info->mtimeset = 1;
3736       info->mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
3737     }
3738 
3739   if (dn.dn.dn_bonustype == DMU_OT_ZNODE)
3740     {
3741       info->mtimeset = 1;
3742       info->mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], dn.endian);
3743     }
3744   return;
3745 }
3746 
3747 static grub_err_t
3748 grub_zfs_dir (grub_device_t device, const char *path,
3749               int (*hook) (const char *, const struct grub_dirhook_info *))
3750 {
3751   struct grub_zfs_data *data;
3752   grub_err_t err;
3753   int isfs;
3754   auto int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val);
3755   auto int NESTED_FUNC_ATTR iterate_zap_fs (const char *name, 
3756                                             grub_uint64_t val);
3757   auto int NESTED_FUNC_ATTR iterate_zap_snap (const char *name, 
3758                                               grub_uint64_t val);
3759 
3760   int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val)
3761   {
3762     struct grub_dirhook_info info;
3763     dnode_end_t dn;
3764     grub_memset (&info, 0, sizeof (info));
3765 
3766     dnode_get (&(data->subvol.mdn), val, 0, &dn, data);
3767 
3768     if (dn.dn.dn_bonustype == DMU_OT_SA)
3769       {
3770         void *sahdrp;
3771         int hdrsize;
3772 
3773         if (dn.dn.dn_bonuslen != 0)
3774           {
3775             sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn);
3776           }
3777         else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
3778           {
3779             blkptr_t *bp = &dn.dn.dn_spill;
3780 
3781             err = zio_read (bp, dn.endian, &sahdrp, NULL, data);
3782             if (err)
3783               {
3784                 grub_print_error ();
3785                 return 0;
3786               }
3787           }
3788         else
3789           {
3790             grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
3791             grub_print_error ();
3792             return 0;
3793           }
3794 
3795         hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
3796         info.mtimeset = 1;
3797         info.mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
3798         info.case_insensitive = data->subvol.case_insensitive;
3799       }
3800     
3801     if (dn.dn.dn_bonustype == DMU_OT_ZNODE)
3802       { 
3803         info.mtimeset = 1;
3804         info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0],
3805                                         dn.endian);
3806       }
3807         info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS);
3808         grub_dprintf ("zfs", "type=%d, name=%s\n", 
3809                   (int)dn.dn.dn_type, (char *)name);
3810     return hook (name, &info);
3811   }
3812 
3813   int NESTED_FUNC_ATTR iterate_zap_fs (const char *name, grub_uint64_t val)
3814   {
3815     struct grub_dirhook_info info;
3816     dnode_end_t mdn;
3817     err = dnode_get (&(data->mos), val, 0, &mdn, data);
3818     if (err)
3819       return 0;
3820     if (mdn.dn.dn_type != DMU_OT_DSL_DIR)
3821       return 0;
3822 
3823     fill_fs_info (&info, mdn, data);
3824     return hook (name, &info);
3825   }
3826   int NESTED_FUNC_ATTR iterate_zap_snap (const char *name, grub_uint64_t val)
3827   {
3828     struct grub_dirhook_info info;
3829     char *name2;
3830     int ret;
3831     dnode_end_t mdn;
3832 
3833     err = dnode_get (&(data->mos), val, 0, &mdn, data);
3834     if (err)
3835       return 0;
3836 
3837     if (mdn.dn.dn_type != DMU_OT_DSL_DATASET)
3838       return 0;
3839 
3840     fill_fs_info (&info, mdn, data);
3841 
3842     name2 = grub_malloc (grub_strlen (name) + 2);
3843     name2[0] = '@';
3844     grub_memcpy (name2 + 1, name, grub_strlen (name) + 1);
3845     ret = hook (name2, &info);
3846     grub_free (name2);
3847     return ret;
3848   }
3849 
3850   data = zfs_mount (device);
3851   if (! data)
3852     return grub_errno;
3853   err = dnode_get_fullpath (path, &(data->subvol), &(data->dnode), &isfs, data);
3854   if (err)
3855     {
3856       zfs_unmount (data);
3857       return err;
3858     }
3859   if (isfs)
3860     {
3861       grub_uint64_t childobj, headobj; 
3862       grub_uint64_t snapobj;
3863       dnode_end_t dn;
3864       struct grub_dirhook_info info;
3865 
3866       fill_fs_info (&info, data->dnode, data);
3867       hook ("@", &info);
3868       
3869       childobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian);
3870       headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian);
3871       err = dnode_get (&(data->mos), childobj,
3872                        DMU_OT_DSL_DIR_CHILD_MAP, &dn, data);
3873       if (err)
3874         {
3875           zfs_unmount (data);
3876           return err;
3877         }
3878 
3879       zap_iterate_u64 (&dn, iterate_zap_fs, data);
3880       
3881       err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data);
3882       if (err)
3883         {
3884           zfs_unmount (data);
3885           return err;
3886         }
3887 
3888       snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&dn.dn))->ds_snapnames_zapobj, dn.endian);
3889 
3890       err = dnode_get (&(data->mos), snapobj,
3891                        DMU_OT_DSL_DS_SNAP_MAP, &dn, data);
3892       if (err)
3893         {
3894           zfs_unmount (data);
3895           return err;
3896         }
3897 
3898       zap_iterate_u64 (&dn, iterate_zap_snap, data);
3899     }
3900   else
3901     {
3902       if (data->dnode.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS)
3903         {
3904           zfs_unmount (data);
3905           return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
3906         }
3907       zap_iterate_u64 (&(data->dnode), iterate_zap, data);
3908     }
3909   zfs_unmount (data);
3910   return grub_errno;
3911 }
3912 
3913 #ifdef GRUB_UTIL
3914 static grub_err_t
3915 grub_zfs_embed (grub_device_t device __attribute__ ((unused)),
3916                 unsigned int *nsectors,
3917                 unsigned int max_nsectors,
3918                 grub_embed_type_t embed_type,
3919                 grub_disk_addr_t **sectors)
3920 {
3921   unsigned i;
3922 
3923   if (embed_type != GRUB_EMBED_PCBIOS)
3924     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
3925                        "ZFS currently supports only PC-BIOS embedding");
3926 
3927  if ((VDEV_BOOT_SIZE >> GRUB_DISK_SECTOR_BITS) < *nsectors)
3928     return grub_error (GRUB_ERR_OUT_OF_RANGE,
3929                        N_("your core.img is unusually large.  "
3930                           "It won't fit in the embedding area"));
3931 
3932   *nsectors = (VDEV_BOOT_SIZE >> GRUB_DISK_SECTOR_BITS);
3933   if (*nsectors > max_nsectors)
3934     *nsectors = max_nsectors;
3935   *sectors = grub_malloc (*nsectors * sizeof (**sectors));
3936   if (!*sectors)
3937     return grub_errno;
3938   for (i = 0; i < *nsectors; i++)
3939     (*sectors)[i] = i + (VDEV_BOOT_OFFSET >> GRUB_DISK_SECTOR_BITS);
3940 
3941   return GRUB_ERR_NONE;
3942 }
3943 #endif
3944 
3945 static struct grub_fs grub_zfs_fs = {
3946   .name = "zfs",
3947   .dir = grub_zfs_dir,
3948   .open = grub_zfs_open,
3949   .read = grub_zfs_read,
3950   .close = grub_zfs_close,
3951   .label = zfs_label,
3952   .uuid = zfs_uuid,
3953   .mtime = zfs_mtime,
3954 #ifdef GRUB_UTIL
3955   .embed = grub_zfs_embed,
3956   .reserved_first_sector = 1,
3957   .blocklist_install = 0,
3958 #endif
3959   .next = 0
3960 };
3961 
3962 GRUB_MOD_INIT (zfs)
3963 {
3964   COMPILE_TIME_ASSERT (sizeof (zap_leaf_chunk_t) == ZAP_LEAF_CHUNKSIZE);
3965   grub_fs_register (&grub_zfs_fs);
3966 #ifndef GRUB_UTIL
3967   my_mod = mod;
3968 #endif
3969 }
3970 
3971 GRUB_MOD_FINI (zfs)
3972 {
3973   grub_fs_unregister (&grub_zfs_fs);
3974 }