Print this page
grub patch

*** 1,9 **** --- 1,10 ---- /* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011 Free Software Foundation, Inc. * Copyright 2010 Sun Microsystems, Inc. + * Copyright 2012 Daniil Lunev * * GRUB is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version.
*** 222,231 **** --- 223,246 ---- grub_uint64_t txg; grub_uint64_t algo; } *keyring; }; + static const char * feature_list[] = { + "localhost:unknown_feature", + NULL, + }; + + typedef enum zfs_feature_id { + ZFS_FEATURE_UNKNOWN, + } zfs_feature_id_t; + + struct enabled_feature_list { + struct enabled_feature_list * next; + zfs_feature_id_t id; + }; + struct grub_zfs_data { /* cache for a file block of the currently zfs_open()-ed file */ char *file_buf; grub_uint64_t file_start;
*** 247,256 **** --- 262,273 ---- unsigned n_devices_allocated; struct grub_zfs_device_desc *device_original; uberblock_t current_uberblock; + struct enabled_feature_list * feature_list; + int mounted; grub_uint64_t guid; }; grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
*** 480,498 **** grub_zfs_endian_t endian = GRUB_ZFS_UNKNOWN_ENDIAN; zio_cksum_t zc; if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC ! && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN) > 0 ! && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN) ! <= SPA_VERSION) endian = GRUB_ZFS_LITTLE_ENDIAN; if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_BIG_ENDIAN) == UBERBLOCK_MAGIC ! && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN) > 0 ! && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN) ! <= SPA_VERSION) endian = GRUB_ZFS_BIG_ENDIAN; if (endian == GRUB_ZFS_UNKNOWN_ENDIAN) return grub_error (GRUB_ERR_BAD_FS, "invalid uberblock magic"); --- 497,511 ---- grub_zfs_endian_t endian = GRUB_ZFS_UNKNOWN_ENDIAN; zio_cksum_t zc; if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC ! && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN) > 0) endian = GRUB_ZFS_LITTLE_ENDIAN; if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_BIG_ENDIAN) == UBERBLOCK_MAGIC ! && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN) > 0) endian = GRUB_ZFS_BIG_ENDIAN; if (endian == GRUB_ZFS_UNKNOWN_ENDIAN) return grub_error (GRUB_ERR_BAD_FS, "invalid uberblock magic");
*** 834,852 **** if (! grub_errno) grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found"); return grub_errno; } grub_dprintf ("zfs", "check 8 passed\n"); - - if (version > SPA_VERSION) - { - grub_free (nvlist); - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "too new version %llu > %llu", - (unsigned long long) version, - (unsigned long long) SPA_VERSION); - } grub_dprintf ("zfs", "check 9 passed\n"); found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID, &(diskdesc->guid)); if (! found) --- 847,856 ----
*** 2668,2727 **** } grub_free (path_buf); return err; } ! #if 0 /* * Get the default 'bootfs' property value from the rootpool. * */ static grub_err_t ! get_default_bootfsobj (dnode_phys_t * mosmdn, grub_uint64_t * obj, struct grub_zfs_data *data) { grub_uint64_t objnum = 0; ! dnode_phys_t *dn; ! if (!dn) ! return grub_errno; if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT, ! DMU_OT_OBJECT_DIRECTORY, dn, data))) { - grub_free (dn); return (grub_errno); } - /* * find the object number for 'pool_props', and get the dnode * of the 'pool_props'. */ ! if (zap_lookup (dn, DMU_POOL_PROPS, &objnum, data)) { - grub_free (dn); return (GRUB_ERR_BAD_FS); } ! if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, dn, data))) { - grub_free (dn); return (grub_errno); } ! if (zap_lookup (dn, ZPOOL_PROP_BOOTFS, &objnum, data)) { - grub_free (dn); return (GRUB_ERR_BAD_FS); } if (!objnum) { - grub_free (dn); return (GRUB_ERR_BAD_FS); } *obj = objnum; return (0); } #endif /* * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname), * e.g. pool/rootfs, or a given object number (obj), e.g. the object number * of pool/rootfs. --- 2672,2724 ---- } grub_free (path_buf); return err; } ! #if 1 /* * Get the default 'bootfs' property value from the rootpool. * */ static grub_err_t ! get_default_bootfsobj (dnode_end_t * mosmdn, grub_uint64_t * obj, struct grub_zfs_data *data) { grub_uint64_t objnum = 0; ! dnode_end_t dn; if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT, ! DMU_OT_OBJECT_DIRECTORY, &dn, data))) { return (grub_errno); } /* * find the object number for 'pool_props', and get the dnode * of the 'pool_props'. */ ! if (zap_lookup (&dn, DMU_POOL_PROPS, &objnum, data, 0)) { return (GRUB_ERR_BAD_FS); } ! if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, &dn, data))) { return (grub_errno); } ! if (zap_lookup (&dn, ZPOOL_PROP_BOOTFS, &objnum, data,0)) { return (GRUB_ERR_BAD_FS); } if (!objnum) { return (GRUB_ERR_BAD_FS); } *obj = objnum; return (0); } + #endif /* * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname), * e.g. pool/rootfs, or a given object number (obj), e.g. the object number * of pool/rootfs.
*** 3318,3330 **** --- 3315,3435 ---- grub_free (data->dnode_mdn); grub_free (data->file_buf); for (i = 0; i < data->subvol.nkeys; i++) grub_crypto_cipher_close (data->subvol.keyring[i].cipher); grub_free (data->subvol.keyring); + while (data->feature_list) { + struct enabled_feature_list * tmp = data->feature_list; + data->feature_list = data->feature_list->next; + grub_free(tmp); + } grub_free (data); } + static int + add_feature (struct grub_zfs_data * data, zfs_feature_id_t id) + { + struct enabled_feature_list * list = data->feature_list; + + while (list->next) { + if (list->next->id == id) + return id; + list = list->next; + } + + list->next = (struct enabled_feature_list *) grub_zalloc (sizeof (struct enabled_feature_list)); + + if (! list->next) + return (-1); + + list->next->id = id; + list->next->next = NULL; + + return 0; + } + + static zfs_feature_id_t + check_feature (const char * feature) + { + int i = 0; + for ( ; i < ZFS_FEATURE_UNKNOWN; ++i) + if (! grub_strcmp (feature, feature_list[i])) + return i; + + return ZFS_FEATURE_UNKNOWN; + } + + /* + * zfs_get_features_list() get feature list and check compatability + */ + static grub_err_t + zfs_get_features_list (struct grub_zfs_data * data) + { + dnode_end_t dn; + dnode_end_t * mos = &(data->mos); + grub_err_t err; + grub_uint64_t objnum; + int ret; + + int NESTED_FUNC_ATTR feature_hook (const char * cname, grub_uint64_t val) + { + grub_err_t iret = 0; + + zfs_feature_id_t id; + + if (! (*cname)) + goto out; + + // retrieve feature number + id = check_feature (cname); + + // if grub doesn't support such feature, return error + if (id == ZFS_FEATURE_UNKNOWN) { + if (val == 0) { + grub_error(GRUB_ERR_BAD_FS, + "Unsupported feature %s was enabled," + " but haven't been activated yet. You will not be able to boot" + " if this feature are activated.", cname); + } else { + grub_error(GRUB_ERR_BAD_FS, + "Unsupported feature %s is activated. Booting failed",cname); + iret = 1; + } + goto out; + } + + // add feature to list + iret = add_feature(data, id); + + out: + return iret; + } + + // get object directory + err = dnode_get (mos, DMU_POOL_DIRECTORY_OBJECT, + DMU_OT_OBJECT_DIRECTORY, &dn, data); + if (err) + return err; + + // retrieve pool properties object numer + err = zap_lookup (&dn, DMU_POOL_FEATURES_FOR_READ, &objnum, data, 0); + if (err) + return err; + + // get "features for read" zap dnode + err = dnode_get (mos, objnum, DMU_OTN_ZAP_METADATA, &dn, data); + if (err) + return err; + + // itterate zap to fetch feature list + ret = zap_iterate_u64 (&dn, feature_hook, data); + if (ret) + return grub_error(GRUB_ERR_BAD_FS, "There are enabled unsupported features"); + + return GRUB_ERR_NONE; + } + /* * zfs_mount() locates a valid uberblock of the root pool and read in its MOS * to the memory address MOS. * */
*** 3352,3361 **** --- 3457,3467 ---- /* if it's our first time here, zero the best uberblock out */ if (data->best_drive == 0 && data->best_part == 0 && find_best_root) grub_memset (&current_uberblock, 0, sizeof (uberblock_t)); #endif + data->feature_list = NULL; data->n_devices_allocated = 16; data->devices_attached = grub_malloc (sizeof (data->devices_attached[0]) * data->n_devices_allocated); data->n_devices_attached = 0; err = scan_disk (dev, data, 1, &inserted);
*** 3393,3405 **** --- 3499,3615 ---- ub_endian) >> 63) & 1; grub_free (osp); data->mounted = 1; + if (grub_zfs_to_cpu64(ub->ub_version, ub_endian) == SPA_FEATURE_VERSION) { + data->feature_list = (struct enabled_feature_list*) grub_zalloc ( + sizeof (struct enabled_feature_list)); + data->feature_list->next = NULL; + err = zfs_get_features_list(data); + + if (err) { + data->mounted = 0; + zfs_unmount (data); + return NULL; + } + } + return data; } + static grub_err_t + find_default_dataset_path(char * path, grub_uint64_t * mdnobj, struct grub_zfs_data *data) + { + grub_uint64_t obj, pobj, zobj; + grub_err_t err; + dnode_end_t mdn, *mosmdn; + char buf[512]; + + int NESTED_FUNC_ATTR hook (const char * name, grub_uint64_t val) { + if (val == obj) { + grub_strcpy(buf, name); + return 1; + } + return 0; + } + + obj = *mdnobj; + mosmdn = &(data->mos); + buf[0] = '\0'; + path[0] = '\0'; + + // get object's data dir + err = dnode_get(mosmdn, obj, DMU_OT_DSL_DATASET, &mdn, data); + if (err) + return err; + obj = grub_zfs_to_cpu64((((dsl_dataset_phys_t*)DN_BONUS (&mdn.dn)))->ds_dir_obj, mdn.endian); + + for (;;) { + // get object dnode + err = dnode_get(mosmdn, obj, DMU_OT_DSL_DIR, &mdn, data); + if (err) + return err; + + // find out its parent's objnum + pobj = grub_zfs_to_cpu64((((dsl_dir_phys_t*)DN_BONUS (&mdn.dn)))->dd_parent_obj, mdn.endian); + if (obj == pobj) + break; + + // if pobj is 0 then we have reached top dataset + if (! pobj) + break; + + // get object's parent dnode + err = dnode_get(mosmdn, pobj, DMU_OT_DSL_DIR, &mdn, data); + if (err) + return err; + + // find out parent's zap objnum + zobj = grub_zfs_to_cpu64((((dsl_dir_phys_t*)DN_BONUS (&mdn.dn)))->dd_child_dir_zapobj, mdn.endian); + + // get zap's dnode + err = dnode_get(mosmdn, zobj, DMU_OT_DSL_DIR_CHILD_MAP, &mdn, data); + if (err) + return err; + + // lookup zap to get name + err = zap_iterate_u64(&mdn, hook, data); + if (err == 0) + return err; + + // pobj becomes obj now + obj = pobj; + + // append new intermediate dnode to path + grub_strcat(buf, path); + grub_strcpy(path, "/"); + grub_strcat(path, buf); + } + + return GRUB_ERR_NONE; + } + + grub_err_t + get_default_bootfs_obj(grub_device_t dev, char * path, grub_uint64_t * mdnobj) + { + struct grub_zfs_data * data; + grub_err_t err = 0; + data = zfs_mount(dev); + if (data) { + err = get_default_bootfsobj(&(data->mos), mdnobj, data); + if (err) + return err; + err = find_default_dataset_path(path, mdnobj, data); + zfs_unmount(data); + return err; + } else { + return grub_errno; + } + return 0; + } + grub_err_t grub_zfs_fetch_nvlist (grub_device_t dev, char **nvlist) { struct grub_zfs_data *zfs; grub_err_t err;