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,10 +223,24 @@
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,10 +262,12 @@
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,19 +497,15 @@
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)
+ && 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
- && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN)
- <= SPA_VERSION)
+ && 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,19 +847,10 @@
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)
@@ -2668,60 +2672,53 @@
}
grub_free (path_buf);
return err;
}
-#if 0
+#if 1
/*
* Get the default 'bootfs' property value from the rootpool.
*
*/
static grub_err_t
-get_default_bootfsobj (dnode_phys_t * mosmdn, grub_uint64_t * obj,
+get_default_bootfsobj (dnode_end_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;
+ dnode_end_t dn;
if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT,
- DMU_OT_OBJECT_DIRECTORY, dn, data)))
+ 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))
+ if (zap_lookup (&dn, DMU_POOL_PROPS, &objnum, data, 0))
{
- grub_free (dn);
return (GRUB_ERR_BAD_FS);
}
- if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, dn, data)))
+ 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))
+ if (zap_lookup (&dn, ZPOOL_PROP_BOOTFS, &objnum, data,0))
{
- 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.
@@ -3318,13 +3315,121 @@
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,10 +3457,11 @@
/* 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 (¤t_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,13 +3499,117 @@
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;