Print this page
grub patch

Split Close
Expand all
Collapse all
          --- old/grub/grub-core/fs/zfs/zfs.c
          +++ new/grub/grub-core/fs/zfs/zfs.c
   1    1  /*
   2    2   *  GRUB  --  GRand Unified Bootloader
   3    3   *  Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011  Free Software Foundation, Inc.
   4    4   *  Copyright 2010  Sun Microsystems, Inc.
        5 + *  Copyright 2012  Daniil Lunev
   5    6   *
   6    7   *  GRUB is free software; you can redistribute it and/or modify
   7    8   *  it under the terms of the GNU General Public License as published by
   8    9   *  the Free Software Foundation; either version 3 of the License, or
   9   10   *  (at your option) any later version.
  10   11   *
  11   12   *  GRUB is distributed in the hope that it will be useful,
  12   13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13   14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14   15   *  GNU General Public License for more details.
↓ open down ↓ 202 lines elided ↑ open up ↑
 217  218    grub_uint64_t case_insensitive;
 218  219    grub_size_t nkeys;
 219  220    struct
 220  221    {
 221  222      grub_crypto_cipher_handle_t cipher;
 222  223      grub_uint64_t txg;
 223  224      grub_uint64_t algo;
 224  225    } *keyring;
 225  226  };
 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 +
 227  242  struct grub_zfs_data
 228  243  {
 229  244    /* cache for a file block of the currently zfs_open()-ed file */
 230  245    char *file_buf;
 231  246    grub_uint64_t file_start;
 232  247    grub_uint64_t file_end;
 233  248  
 234  249    /* cache for a dnode block */
 235  250    dnode_phys_t *dnode_buf;
 236  251    dnode_phys_t *dnode_mdn;
↓ open down ↓ 5 lines elided ↑ open up ↑
 242  257    dnode_end_t dnode;
 243  258    struct subvolume subvol;
 244  259  
 245  260    struct grub_zfs_device_desc *devices_attached;
 246  261    unsigned n_devices_attached;
 247  262    unsigned n_devices_allocated;
 248  263    struct grub_zfs_device_desc *device_original;
 249  264  
 250  265    uberblock_t current_uberblock;
 251  266  
      267 +  struct enabled_feature_list * feature_list;
      268 +  
 252  269    int mounted;
 253  270    grub_uint64_t guid;
 254  271  };
 255  272  
 256  273  grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
 257  274                                  grub_uint64_t algo,
 258  275                                  void *nonce,
 259  276                                  char *buf, grub_size_t size,
 260  277                                  const grub_uint32_t *expected_mac,
 261  278                                  grub_zfs_endian_t endian) = NULL;
↓ open down ↓ 213 lines elided ↑ open up ↑
 475  492  uberblock_verify (uberblock_phys_t * ub, grub_uint64_t offset,
 476  493                    grub_size_t s)
 477  494  {
 478  495    uberblock_t *uber = &ub->ubp_uberblock;
 479  496    grub_err_t err;
 480  497    grub_zfs_endian_t endian = GRUB_ZFS_UNKNOWN_ENDIAN;
 481  498    zio_cksum_t zc;
 482  499  
 483  500    if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
 484  501        == 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)
      502 +      && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN) > 0)
 488  503      endian = GRUB_ZFS_LITTLE_ENDIAN;
 489  504  
 490  505    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)
      506 +      && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN) > 0)
 494  507      endian = GRUB_ZFS_BIG_ENDIAN;
 495  508  
 496  509    if (endian == GRUB_ZFS_UNKNOWN_ENDIAN)
 497  510      return grub_error (GRUB_ERR_BAD_FS, "invalid uberblock magic");
 498  511  
 499  512    grub_memset (&zc, 0, sizeof (zc));
 500  513  
 501  514    zc.zc_word[0] = grub_cpu_to_zfs64 (offset, endian);
 502  515    err = zio_checksum_verify (zc, ZIO_CHECKSUM_LABEL, endian,
 503  516                               (char *) ub, s);
↓ open down ↓ 325 lines elided ↑ open up ↑
 829  842    found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_VERSION,
 830  843                                           &version);
 831  844    if (! found)
 832  845      {
 833  846        grub_free (nvlist);
 834  847        if (! grub_errno)
 835  848          grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found");
 836  849        return grub_errno;
 837  850      }
 838  851    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  852    grub_dprintf ("zfs", "check 9 passed\n");
 849  853  
 850  854    found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID,
 851  855                                           &(diskdesc->guid));
 852  856    if (! found)
 853  857      {
 854  858        grub_free (nvlist);
 855  859        if (! grub_errno)
 856  860          grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_GUID " not found");
 857  861        return grub_errno;
↓ open down ↓ 909 lines elided ↑ open up ↑
1767 1771               int objsize, const char *name, grub_uint64_t * value,
1768 1772               int case_insensitive)
1769 1773  {
1770 1774    int i, chunks;
1771 1775    mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk;
1772 1776  
1773 1777    chunks = objsize / MZAP_ENT_LEN - 1;
1774 1778    for (i = 0; i < chunks; i++)
1775 1779      {
1776 1780        if (case_insensitive ? (grub_strcasecmp (mzap_ent[i].mze_name, name) == 0)
1777      -          : (grub_strcmp (mzap_ent[i].mze_name, name) == 0))
1778      -        {
1779      -          *value = grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian);
1780      -          return GRUB_ERR_NONE;
1781      -        }
     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 +          }
1782 1786      }
1783 1787  
1784 1788    return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), name);
1785 1789  }
1786 1790  
1787 1791  static int
1788 1792  mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize, 
1789 1793                int NESTED_FUNC_ATTR (*hook) (const char *name, 
1790 1794                                              grub_uint64_t val))
1791 1795  {
↓ open down ↓ 871 lines elided ↑ open up ↑
2663 2667    while (dnode_path)
2664 2668      {
2665 2669        dn_new = dnode_path->next;
2666 2670        grub_free (dnode_path);
2667 2671        dnode_path = dn_new;
2668 2672      }
2669 2673    grub_free (path_buf);
2670 2674    return err;
2671 2675  }
2672 2676  
2673      -#if 0
     2677 +#if 1
2674 2678  /*
2675 2679   * Get the default 'bootfs' property value from the rootpool.
2676 2680   *
2677 2681   */
2678 2682  static grub_err_t
2679      -get_default_bootfsobj (dnode_phys_t * mosmdn, grub_uint64_t * obj,
     2683 +get_default_bootfsobj (dnode_end_t * mosmdn, grub_uint64_t * obj,
2680 2684                         struct grub_zfs_data *data)
2681 2685  {
2682 2686    grub_uint64_t objnum = 0;
2683      -  dnode_phys_t *dn;
2684      -  if (!dn)
2685      -    return grub_errno;
     2687 +  dnode_end_t dn;
2686 2688  
2687 2689    if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT,
2688      -                               DMU_OT_OBJECT_DIRECTORY, dn, data)))
     2690 +                               DMU_OT_OBJECT_DIRECTORY, &dn, data)))
2689 2691      {
2690      -      grub_free (dn);
2691 2692        return (grub_errno);
2692 2693      }
2693      -
2694 2694    /*
2695 2695     * find the object number for 'pool_props', and get the dnode
2696 2696     * of the 'pool_props'.
2697 2697     */
2698      -  if (zap_lookup (dn, DMU_POOL_PROPS, &objnum, data))
     2698 +  if (zap_lookup (&dn, DMU_POOL_PROPS, &objnum, data, 0))
2699 2699      {
2700      -      grub_free (dn);
2701 2700        return (GRUB_ERR_BAD_FS);
2702 2701      }
2703      -  if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, dn, data)))
     2702 +  if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, &dn, data)))
2704 2703      {
2705      -      grub_free (dn);
2706 2704        return (grub_errno);
2707 2705      }
2708      -  if (zap_lookup (dn, ZPOOL_PROP_BOOTFS, &objnum, data))
     2706 +  if (zap_lookup (&dn, ZPOOL_PROP_BOOTFS, &objnum, data,0))
2709 2707      {
2710      -      grub_free (dn);
2711 2708        return (GRUB_ERR_BAD_FS);
2712 2709      }
2713 2710  
2714 2711    if (!objnum)
2715 2712      {
2716      -      grub_free (dn);
2717 2713        return (GRUB_ERR_BAD_FS);
2718 2714      }
2719 2715  
2720 2716    *obj = objnum;
2721 2717    return (0);
2722 2718  }
     2719 +
2723 2720  #endif
2724 2721  /*
2725 2722   * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname),
2726 2723   * e.g. pool/rootfs, or a given object number (obj), e.g. the object number
2727 2724   * of pool/rootfs.
2728 2725   *
2729 2726   * If no fsname and no obj are given, return the DSL_DIR metadnode.
2730 2727   * If fsname is given, return its metadnode and its matching object number.
2731 2728   * If only obj is given, return the metadnode for this object number.
2732 2729   *
↓ open down ↓ 580 lines elided ↑ open up ↑
3313 3310    unsigned i;
3314 3311    for (i = 0; i < data->n_devices_attached; i++)
3315 3312      unmount_device (&data->devices_attached[i]);
3316 3313    grub_free (data->devices_attached);
3317 3314    grub_free (data->dnode_buf);
3318 3315    grub_free (data->dnode_mdn);
3319 3316    grub_free (data->file_buf);
3320 3317    for (i = 0; i < data->subvol.nkeys; i++)
3321 3318      grub_crypto_cipher_close (data->subvol.keyring[i].cipher);
3322 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 +  }
3323 3325    grub_free (data);
3324 3326  }
3325 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 +
3326 3431  /*
3327 3432   * zfs_mount() locates a valid uberblock of the root pool and read in its MOS
3328 3433   * to the memory address MOS.
3329 3434   *
3330 3435   */
3331 3436  static struct grub_zfs_data *
3332 3437  zfs_mount (grub_device_t dev)
3333 3438  {
3334 3439    struct grub_zfs_data *data = 0;
3335 3440    grub_err_t err;
↓ open down ↓ 11 lines elided ↑ open up ↑
3347 3452  
3348 3453    data = grub_zalloc (sizeof (*data));
3349 3454    if (!data)
3350 3455      return 0;
3351 3456  #if 0
3352 3457    /* if it's our first time here, zero the best uberblock out */
3353 3458    if (data->best_drive == 0 && data->best_part == 0 && find_best_root)
3354 3459      grub_memset (&current_uberblock, 0, sizeof (uberblock_t));
3355 3460  #endif
3356 3461  
     3462 +  data->feature_list = NULL;
3357 3463    data->n_devices_allocated = 16;
3358 3464    data->devices_attached = grub_malloc (sizeof (data->devices_attached[0])
3359 3465                                          * data->n_devices_allocated);
3360 3466    data->n_devices_attached = 0;
3361 3467    err = scan_disk (dev, data, 1, &inserted);
3362 3468    if (err)
3363 3469      {
3364 3470        zfs_unmount (data);
3365 3471        return NULL;
3366 3472      }
↓ open down ↓ 18 lines elided ↑ open up ↑
3385 3491        zfs_unmount (data);
3386 3492        return NULL;
3387 3493      }
3388 3494  
3389 3495    /* Got the MOS. Save it at the memory addr MOS. */
3390 3496    grub_memmove (&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode,
3391 3497                  DNODE_SIZE);
3392 3498    data->mos.endian = (grub_zfs_to_cpu64 (ub->ub_rootbp.blk_prop,
3393 3499                                           ub_endian) >> 63) & 1;
3394 3500    grub_free (osp);
3395      -
     3501 +  
3396 3502    data->mounted = 1;
3397 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 +
3398 3517    return data;
3399 3518  }
3400 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 +
3401 3611  grub_err_t
3402 3612  grub_zfs_fetch_nvlist (grub_device_t dev, char **nvlist)
3403 3613  {
3404 3614    struct grub_zfs_data *zfs;
3405 3615    grub_err_t err;
3406 3616  
3407 3617    zfs = zfs_mount (dev);
3408 3618    if (!zfs)
3409 3619      return grub_errno;
3410 3620    err = zfs_fetch_nvlist (zfs->device_original, nvlist);
↓ open down ↓ 564 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX