Print this page
grub patch
   1 /*
   2  *  GRUB  --  GRand Unified Bootloader
   3  *  Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011  Free Software Foundation, Inc.
   4  *  Copyright 2010  Sun Microsystems, Inc.

   5  *
   6  *  GRUB is free software; you can redistribute it and/or modify
   7  *  it under the terms of the GNU General Public License as published by
   8  *  the Free Software Foundation; either version 3 of the License, or
   9  *  (at your option) any later version.
  10  *
  11  *  GRUB is distributed in the hope that it will be useful,
  12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14  *  GNU General Public License for more details.
  15  *
  16  *  You should have received a copy of the GNU General Public License
  17  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  18  */
  19 /*
  20  * The zfs plug-in routines for GRUB are:
  21  *
  22  * zfs_mount() - locates a valid uberblock of the root pool and reads
  23  *              in its MOS at the memory address MOS.
  24  *


 207   grub_device_t dev;
 208   grub_disk_addr_t vdev_phys_sector;
 209   uberblock_t current_uberblock;
 210   int original;
 211 };
 212 
 213 struct subvolume
 214 {
 215   dnode_end_t mdn;
 216   grub_uint64_t obj;
 217   grub_uint64_t case_insensitive;
 218   grub_size_t nkeys;
 219   struct
 220   {
 221     grub_crypto_cipher_handle_t cipher;
 222     grub_uint64_t txg;
 223     grub_uint64_t algo;
 224   } *keyring;
 225 };
 226 














 227 struct grub_zfs_data
 228 {
 229   /* cache for a file block of the currently zfs_open()-ed file */
 230   char *file_buf;
 231   grub_uint64_t file_start;
 232   grub_uint64_t file_end;
 233 
 234   /* cache for a dnode block */
 235   dnode_phys_t *dnode_buf;
 236   dnode_phys_t *dnode_mdn;
 237   grub_uint64_t dnode_start;
 238   grub_uint64_t dnode_end;
 239   grub_zfs_endian_t dnode_endian;
 240 
 241   dnode_end_t mos;
 242   dnode_end_t dnode;
 243   struct subvolume subvol;
 244 
 245   struct grub_zfs_device_desc *devices_attached;
 246   unsigned n_devices_attached;
 247   unsigned n_devices_allocated;
 248   struct grub_zfs_device_desc *device_original;
 249 
 250   uberblock_t current_uberblock;
 251 


 252   int mounted;
 253   grub_uint64_t guid;
 254 };
 255 
 256 grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
 257                                 grub_uint64_t algo,
 258                                 void *nonce,
 259                                 char *buf, grub_size_t size,
 260                                 const grub_uint32_t *expected_mac,
 261                                 grub_zfs_endian_t endian) = NULL;
 262 grub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct grub_zfs_key *key,
 263                                                   grub_size_t keysize,
 264                                                   grub_uint64_t salt,
 265                                                   grub_uint64_t algo) = NULL;
 266 
 267 static grub_err_t 
 268 zlib_decompress (void *s, void *d,
 269                  grub_size_t slen, grub_size_t dlen)
 270 {
 271   if (grub_zlib_decompress (s, slen, 0, d, dlen) < 0)


 465 }
 466 
 467 /*
 468  * Three pieces of information are needed to verify an uberblock: the magic
 469  * number, the version number, and the checksum.
 470  *
 471  * Currently Implemented: version number, magic number, checksum
 472  *
 473  */
 474 static grub_err_t
 475 uberblock_verify (uberblock_phys_t * ub, grub_uint64_t offset,
 476                   grub_size_t s)
 477 {
 478   uberblock_t *uber = &ub->ubp_uberblock;
 479   grub_err_t err;
 480   grub_zfs_endian_t endian = GRUB_ZFS_UNKNOWN_ENDIAN;
 481   zio_cksum_t zc;
 482 
 483   if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
 484       == UBERBLOCK_MAGIC
 485       && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN) > 0 
 486       && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN)
 487       <= SPA_VERSION)
 488     endian = GRUB_ZFS_LITTLE_ENDIAN;
 489 
 490   if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_BIG_ENDIAN) == UBERBLOCK_MAGIC
 491       && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN) > 0 
 492       && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN)
 493       <= SPA_VERSION)
 494     endian = GRUB_ZFS_BIG_ENDIAN;
 495 
 496   if (endian == GRUB_ZFS_UNKNOWN_ENDIAN)
 497     return grub_error (GRUB_ERR_BAD_FS, "invalid uberblock magic");
 498 
 499   grub_memset (&zc, 0, sizeof (zc));
 500 
 501   zc.zc_word[0] = grub_cpu_to_zfs64 (offset, endian);
 502   err = zio_checksum_verify (zc, ZIO_CHECKSUM_LABEL, endian,
 503                              (char *) ub, s);
 504 
 505   return err;
 506 }
 507 
 508 /*
 509  * Find the best uberblock.
 510  * Return:
 511  *    Success - Pointer to the best uberblock.
 512  *    Failure - NULL
 513  */


 819   grub_dprintf ("zfs", "check 6 passed\n");
 820 
 821   /* not an active device */
 822   if (txg == 0)
 823     {
 824       grub_free (nvlist);
 825       return grub_error (GRUB_ERR_BAD_FS, "zpool isn't active");
 826     }
 827   grub_dprintf ("zfs", "check 7 passed\n");
 828 
 829   found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_VERSION,
 830                                          &version);
 831   if (! found)
 832     {
 833       grub_free (nvlist);
 834       if (! grub_errno)
 835         grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found");
 836       return grub_errno;
 837     }
 838   grub_dprintf ("zfs", "check 8 passed\n");
 839 
 840   if (version > SPA_VERSION)
 841     {
 842       grub_free (nvlist);
 843       return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 844                          "too new version %llu > %llu",
 845                          (unsigned long long) version,
 846                          (unsigned long long) SPA_VERSION);
 847     }
 848   grub_dprintf ("zfs", "check 9 passed\n");
 849 
 850   found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID,
 851                                          &(diskdesc->guid));
 852   if (! found)
 853     {
 854       grub_free (nvlist);
 855       if (! grub_errno)
 856         grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_GUID " not found");
 857       return grub_errno;
 858     }
 859 
 860   found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID,
 861                                          &poolguid);
 862   if (! found)
 863     {
 864       grub_free (nvlist);
 865       if (! grub_errno)
 866         grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_GUID " not found");
 867       return grub_errno;


2653                   dnode_path = dn_new->next;
2654                   grub_free (dn_new);
2655                 }
2656             }
2657         }
2658     }
2659 
2660   if (!err)
2661     grub_memcpy (dn, &(dnode_path->dn), sizeof (*dn));
2662 
2663   while (dnode_path)
2664     {
2665       dn_new = dnode_path->next;
2666       grub_free (dnode_path);
2667       dnode_path = dn_new;
2668     }
2669   grub_free (path_buf);
2670   return err;
2671 }
2672 
2673 #if 0
2674 /*
2675  * Get the default 'bootfs' property value from the rootpool.
2676  *
2677  */
2678 static grub_err_t
2679 get_default_bootfsobj (dnode_phys_t * mosmdn, grub_uint64_t * obj,
2680                        struct grub_zfs_data *data)
2681 {
2682   grub_uint64_t objnum = 0;
2683   dnode_phys_t *dn;
2684   if (!dn)
2685     return grub_errno;
2686 
2687   if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT,
2688                                DMU_OT_OBJECT_DIRECTORY, dn, data)))
2689     {
2690       grub_free (dn);
2691       return (grub_errno);
2692     }
2693 
2694   /*
2695    * find the object number for 'pool_props', and get the dnode
2696    * of the 'pool_props'.
2697    */
2698   if (zap_lookup (dn, DMU_POOL_PROPS, &objnum, data))
2699     {
2700       grub_free (dn);
2701       return (GRUB_ERR_BAD_FS);
2702     }
2703   if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, dn, data)))
2704     {
2705       grub_free (dn);
2706       return (grub_errno);
2707     }
2708   if (zap_lookup (dn, ZPOOL_PROP_BOOTFS, &objnum, data))
2709     {
2710       grub_free (dn);
2711       return (GRUB_ERR_BAD_FS);
2712     }
2713 
2714   if (!objnum)
2715     {
2716       grub_free (dn);
2717       return (GRUB_ERR_BAD_FS);
2718     }
2719 
2720   *obj = objnum;
2721   return (0);
2722 }

2723 #endif
2724 /*
2725  * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname),
2726  * e.g. pool/rootfs, or a given object number (obj), e.g. the object number
2727  * of pool/rootfs.
2728  *
2729  * If no fsname and no obj are given, return the DSL_DIR metadnode.
2730  * If fsname is given, return its metadnode and its matching object number.
2731  * If only obj is given, return the metadnode for this object number.
2732  *
2733  */
2734 static grub_err_t
2735 get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname,
2736                       dnode_end_t * mdn, struct grub_zfs_data *data)
2737 {
2738   grub_uint64_t objnum;
2739   grub_err_t err;
2740 
2741   grub_dprintf ("zfs", "endian = %d\n", mosmdn->endian);
2742 


3303       for (i = 0; i < desc->n_children; i++)
3304         unmount_device (&desc->children[i]);
3305       grub_free (desc->children);
3306       return;
3307     }
3308 }
3309 
3310 static void
3311 zfs_unmount (struct grub_zfs_data *data)
3312 {
3313   unsigned i;
3314   for (i = 0; i < data->n_devices_attached; i++)
3315     unmount_device (&data->devices_attached[i]);
3316   grub_free (data->devices_attached);
3317   grub_free (data->dnode_buf);
3318   grub_free (data->dnode_mdn);
3319   grub_free (data->file_buf);
3320   for (i = 0; i < data->subvol.nkeys; i++)
3321     grub_crypto_cipher_close (data->subvol.keyring[i].cipher);
3322   grub_free (data->subvol.keyring);





3323   grub_free (data);
3324 }
3325 







































































































3326 /*
3327  * zfs_mount() locates a valid uberblock of the root pool and read in its MOS
3328  * to the memory address MOS.
3329  *
3330  */
3331 static struct grub_zfs_data *
3332 zfs_mount (grub_device_t dev)
3333 {
3334   struct grub_zfs_data *data = 0;
3335   grub_err_t err;
3336   void *osp = 0;
3337   grub_size_t ospsize;
3338   grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
3339   uberblock_t *ub;
3340   int inserted;
3341 
3342   if (! dev->disk)
3343     {
3344       grub_error (GRUB_ERR_BAD_DEVICE, "not a disk");
3345       return 0;
3346     }
3347 
3348   data = grub_zalloc (sizeof (*data));
3349   if (!data)
3350     return 0;
3351 #if 0
3352   /* if it's our first time here, zero the best uberblock out */
3353   if (data->best_drive == 0 && data->best_part == 0 && find_best_root)
3354     grub_memset (&current_uberblock, 0, sizeof (uberblock_t));
3355 #endif
3356 

3357   data->n_devices_allocated = 16;
3358   data->devices_attached = grub_malloc (sizeof (data->devices_attached[0])
3359                                         * data->n_devices_allocated);
3360   data->n_devices_attached = 0;
3361   err = scan_disk (dev, data, 1, &inserted);
3362   if (err)
3363     {
3364       zfs_unmount (data);
3365       return NULL;
3366     }
3367 
3368   ub = &(data->current_uberblock);
3369   ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, 
3370                                   GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC 
3371                ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN);
3372 
3373   err = zio_read (&ub->ub_rootbp, ub_endian,
3374                   &osp, &ospsize, data);
3375   if (err)
3376     {


3378       return NULL;
3379     }
3380 
3381   if (ospsize < OBJSET_PHYS_SIZE_V14)
3382     {
3383       grub_error (GRUB_ERR_BAD_FS, "OSP too small");
3384       grub_free (osp);
3385       zfs_unmount (data);
3386       return NULL;
3387     }
3388 
3389   /* Got the MOS. Save it at the memory addr MOS. */
3390   grub_memmove (&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode,
3391                 DNODE_SIZE);
3392   data->mos.endian = (grub_zfs_to_cpu64 (ub->ub_rootbp.blk_prop,
3393                                          ub_endian) >> 63) & 1;
3394   grub_free (osp);
3395 
3396   data->mounted = 1;
3397 













3398   return data;
3399 }
3400 



























































































3401 grub_err_t
3402 grub_zfs_fetch_nvlist (grub_device_t dev, char **nvlist)
3403 {
3404   struct grub_zfs_data *zfs;
3405   grub_err_t err;
3406 
3407   zfs = zfs_mount (dev);
3408   if (!zfs)
3409     return grub_errno;
3410   err = zfs_fetch_nvlist (zfs->device_original, nvlist);
3411   zfs_unmount (zfs);
3412   return err;
3413 }
3414 
3415 static grub_err_t 
3416 zfs_label (grub_device_t device, char **label)
3417 {
3418   char *nvlist;
3419   grub_err_t err;
3420   struct grub_zfs_data *data;


   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  *


 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)


 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  */


 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;


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 


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     {


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;