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