Hi, Please test / comment on proof-of-concept attached patch, it gets rid of libzfs dependency in GRUB.
The approach is to implement a disk abstraction, like LVM does, but in this case a very simple one (passthrough). Then grub-probe et al can obtain their information from this abstraction layer like they currently do with LVM or mdRAID, but they don't need the device node list anymore (since it's filled with a full scan, as with LVM). -- Robert Millan
=== modified file 'configure.ac' --- configure.ac 2011-08-19 20:49:48 +0000 +++ configure.ac 2011-10-15 16:42:10 +0000 @@ -916,18 +916,6 @@ AC_CHECK_LIB([lzma], [lzma_code], [Define to 1 if you have the LZMA library.])],) AC_SUBST([LIBLZMA]) -AC_CHECK_LIB([zfs], [libzfs_init], - [LIBZFS="-lzfs" - AC_DEFINE([HAVE_LIBZFS], [1], - [Define to 1 if you have the ZFS library.])],) -AC_SUBST([LIBZFS]) - -AC_CHECK_LIB([nvpair], [nvlist_print], - [LIBNVPAIR="-lnvpair" - AC_DEFINE([HAVE_LIBNVPAIR], [1], - [Define to 1 if you have the NVPAIR library.])],) -AC_SUBST([LIBNVPAIR]) - LIBS="" pkglibrootdir='$(libdir)'/`echo $PACKAGE | sed "$program_transform_name"` === modified file 'grub-core/fs/zfs/zfs.c' --- grub-core/fs/zfs/zfs.c 2011-06-23 22:31:29 +0000 +++ grub-core/fs/zfs/zfs.c 2011-10-15 16:42:10 +0000 @@ -53,6 +53,10 @@ #include <grub/zfs/dsl_dataset.h> #include <grub/deflate.h> +#ifdef GRUB_UTIL +#include <grub/emu/misc.h> +#endif + GRUB_MOD_LICENSE ("GPLv3+"); #define ZPOOL_PROP_BOOTFS "bootfs" @@ -2179,6 +2183,130 @@ zfs_uuid (grub_device_t device, char **u return GRUB_ERR_NONE; } +struct grub_zfs_vdev +{ + const char *name; + struct grub_zfs_vdev *next; +}; + +struct grub_zfs_pool +{ + grub_uint64_t guid; + char *name; + struct grub_zfs_vdev *vdev_list; + struct grub_zfs_pool *next; +}; + +static struct grub_zfs_pool *zpool_list; + +static int +zfs_scan_device (const char *name) +{ + grub_device_t device; + struct grub_zfs_data *data; + char *nvlist; + grub_uint64_t guid; + char *label; + struct grub_zfs_pool *zpool; + struct grub_zfs_vdev *vdev; + +#ifdef GRUB_UTIL + grub_util_info ("scanning %s for ZFS", name); +#endif + + device = grub_device_open (name); + if (! device) + return 0; + + data = zfs_mount (device); + if (! data) + goto end1; + + if (zfs_fetch_nvlist (data, &nvlist)) + goto end2; + + if (! grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID, &guid)) + goto end3; + + label = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME); + if (! label) + goto end3; + + vdev = grub_zalloc (sizeof (*vdev)); + vdev->name = grub_strdup (name); + + struct grub_zfs_pool *i; + for (i = zpool_list; i; i = i->next) + { + if (guid == i->guid) + { + /* This vdev belongs to an already-registered pool. */ + vdev->next = i->vdev_list; + i->vdev_list = vdev; + return 0; + } + } + + /* Create a new ZFS pool with this vdev. */ + zpool = grub_zalloc (sizeof (*zpool)); + zpool->guid = guid; + zpool->name = grub_xasprintf ("zfs/%s", label); + zpool->vdev_list = vdev; + + /* Insert it to ZFS pool list. */ + zpool->next = zpool_list; + zpool_list = zpool; + + end3: + grub_free (nvlist); + end2: + zfs_unmount (data); + end1: + grub_device_close (device); + + return 0; +} + +static int +grub_zpool_iterate (int (*hook) (const char *name), + grub_disk_pull_t pull __attribute__ ((unused))) +{ + struct grub_zfs_pool *i; + for (i = zpool_list; i; i = i->next) + { + if (hook (i->name)) + return 1; + } + + return 0; +} + +static grub_err_t +grub_zpool_open (const char *name, grub_disk_t disk) +{ + struct grub_zfs_pool *i; + for (i = zpool_list; i; i = i->next) + { + if (! grub_strcmp (i->name, name)) + { + /* For now just pick the first vdev as lower layer. */ + grub_disk_t lower = grub_disk_open (i->vdev_list->name); + grub_memcpy (disk, lower, sizeof (*disk)); + return GRUB_ERR_NONE; + } + } + + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a ZFS pool"); +} + +static struct grub_disk_dev grub_zpool_dev = + { + .name = "zfs", + .id = GRUB_DISK_DEVICE_ZFS_ID, + .iterate = grub_zpool_iterate, + .open = grub_zpool_open, + }; + /* * zfs_open() locates a file in the rootpool by following the * MOS and places the dnode of the file in the memory address DNODE. @@ -2556,6 +2684,9 @@ static struct grub_fs grub_zfs_fs = { GRUB_MOD_INIT (zfs) { + grub_device_iterate (&zfs_scan_device); + grub_disk_dev_register (&grub_zpool_dev); + grub_fs_register (&grub_zfs_fs); #ifndef GRUB_UTIL my_mod = mod; @@ -2564,5 +2695,9 @@ GRUB_MOD_INIT (zfs) GRUB_MOD_FINI (zfs) { + grub_disk_dev_unregister (&grub_zpool_dev); + zpool_list = NULL; + /* FIXME: free the zpool list. */ + grub_fs_unregister (&grub_zfs_fs); } === modified file 'grub-core/kern/disk.c' --- grub-core/kern/disk.c 2011-08-13 13:00:48 +0000 +++ grub-core/kern/disk.c 2011-10-15 16:42:10 +0000 @@ -267,6 +267,7 @@ grub_disk_open (const char *name) for (dev = grub_disk_dev_list; dev; dev = dev->next) { + disk->dev = dev; if ((dev->open) (raw, disk) == GRUB_ERR_NONE) break; else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE) @@ -289,8 +290,6 @@ grub_disk_open (const char *name) goto fail; } - disk->dev = dev; - if (p) { disk->partition = grub_partition_probe (disk, p + 1); === modified file 'include/grub/disk.h' --- include/grub/disk.h 2011-08-13 13:00:48 +0000 +++ include/grub/disk.h 2011-10-15 16:42:10 +0000 @@ -44,6 +44,7 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_FILE_ID, GRUB_DISK_DEVICE_CRYPTODISK_ID, GRUB_DISK_DEVICE_ARCDISK_ID, + GRUB_DISK_DEVICE_ZFS_ID, }; struct grub_disk; === modified file 'include/grub/emu/getroot.h' --- include/grub/emu/getroot.h 2011-04-25 12:52:07 +0000 +++ include/grub/emu/getroot.h 2011-10-15 16:42:10 +0000 @@ -27,6 +27,7 @@ enum grub_dev_abstraction_types { GRUB_DEV_ABSTRACTION_RAID, GRUB_DEV_ABSTRACTION_LUKS, GRUB_DEV_ABSTRACTION_GELI, + GRUB_DEV_ABSTRACTION_ZFS, }; char *grub_find_device (const char *dir, dev_t dev); === modified file 'util/getroot.c' --- util/getroot.c 2011-10-15 16:37:55 +0000 +++ util/getroot.c 2011-10-15 16:44:12 +0000 @@ -243,69 +243,6 @@ grub_find_root_device_from_mountinfo (co #endif /* __linux__ */ -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) -static char * -find_root_device_from_libzfs (const char *dir) -{ - char *device = NULL; - char *poolname; - char *poolfs; - - grub_find_zpool_from_dir (dir, &poolname, &poolfs); - if (! poolname) - return NULL; - - { - zpool_handle_t *zpool; - libzfs_handle_t *libzfs; - nvlist_t *config, *vdev_tree; - nvlist_t **children, **path; - unsigned int nvlist_count; - unsigned int i; - - libzfs = grub_get_libzfs_handle (); - if (! libzfs) - return NULL; - - zpool = zpool_open (libzfs, poolname); - config = zpool_get_config (zpool, NULL); - - if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0) - error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")"); - - if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0) - error (1, errno, "nvlist_lookup_nvlist_array (\"children\")"); - assert (nvlist_count > 0); - - while (nvlist_lookup_nvlist_array (children[0], "children", - &children, &nvlist_count) == 0) - assert (nvlist_count > 0); - - for (i = 0; i < nvlist_count; i++) - { - if (nvlist_lookup_string (children[i], "path", &device) != 0) - error (1, errno, "nvlist_lookup_string (\"path\")"); - - struct stat st; - if (stat (device, &st) == 0) - { - device = xstrdup (device); - break; - } - - device = NULL; - } - - zpool_close (zpool); - } - - free (poolname); - if (poolfs) - free (poolfs); - - return device; -} -#endif #ifdef __MINGW32__ @@ -608,11 +545,6 @@ grub_guess_root_device (const char *dir) os_dev = grub_find_root_device_from_mountinfo (dir, NULL); #endif /* __linux__ */ -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) - if (!os_dev) - os_dev = find_root_device_from_libzfs (dir); -#endif - if (os_dev) { char *tmp = os_dev; @@ -635,6 +567,28 @@ grub_guess_root_device (const char *dir) free (os_dev); } + /* Check for ZFS. */ + if (!os_dev) + { + char *pool; + char *fs; + + grub_find_zpool_from_dir (dir, &pool, &fs); + + if (pool) + { + os_dev = xasprintf ("zfs/%s", pool); + free (pool); + } + + if (fs) + free (fs); + } + + if (grub_util_check_nodeless_device (os_dev)) + /* This kind of abstraction doesn't provide device nodes. */ + return os_dev; + if (stat (dir, &st) < 0) grub_util_error ("cannot stat `%s'", dir); @@ -884,7 +838,6 @@ grub_util_get_dev_abstraction (const cha #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) const char *abs; abs = grub_util_get_geom_abstraction (os_dev); - grub_util_info ("abstraction of %s is %s", os_dev, abs); if (abs && grub_strcasecmp (abs, "eli") == 0) return GRUB_DEV_ABSTRACTION_GELI; @@ -893,6 +846,9 @@ grub_util_get_dev_abstraction (const cha return GRUB_DEV_ABSTRACTION_LVM; #endif + if (!strncmp (os_dev, "zfs/", sizeof ("zfs/")-1)) + return GRUB_DEV_ABSTRACTION_ZFS; + /* No abstraction found. */ return GRUB_DEV_ABSTRACTION_NONE; } @@ -1107,6 +1063,9 @@ grub_util_pull_device (const char *os_de #endif return; + case GRUB_DEV_ABSTRACTION_ZFS: + return; + default: /* GRUB_DEV_ABSTRACTION_NONE */ grub_util_biosdisk_get_grub_dev (os_dev); return; @@ -1139,6 +1098,12 @@ grub_util_get_grub_dev (const char *os_d break; #endif + case GRUB_DEV_ABSTRACTION_ZFS: + { + grub_dev = xstrdup (os_dev); + } + break; + #ifdef __linux__ case GRUB_DEV_ABSTRACTION_LUKS: { @@ -1317,6 +1282,13 @@ grub_util_get_grub_dev (const char *os_d return grub_dev; } +const int +grub_util_check_nodeless_device (const char *dev) +{ + /* Only ZFS for now. Btrfs might fit here. */ + return ! strncmp (dev, "zfs/", sizeof ("zfs/")-1); +} + const char * grub_util_check_block_device (const char *blk_dev) { @@ -1366,32 +1338,6 @@ get_win32_path (const char *path) } #endif -#ifdef HAVE_LIBZFS -static libzfs_handle_t *__libzfs_handle; - -static void -fini_libzfs (void) -{ - libzfs_fini (__libzfs_handle); -} - -libzfs_handle_t * -grub_get_libzfs_handle (void) -{ - if (! __libzfs_handle) - { - __libzfs_handle = libzfs_init (); - - if (__libzfs_handle) - atexit (fini_libzfs); - } - - return __libzfs_handle; -} -#endif /* HAVE_LIBZFS */ - -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) -/* ZFS has similar problems to those of btrfs (see above). */ void grub_find_zpool_from_dir (const char *dir, char **poolname, char **poolfs) { @@ -1451,7 +1397,6 @@ grub_find_zpool_from_dir (const char *di else *poolfs = xstrdup (""); } -#endif /* This function never prints trailing slashes (so that its output can be appended a slash unconditionally). */ @@ -1463,23 +1408,18 @@ grub_make_system_path_relative_to_its_ro uintptr_t offset = 0; dev_t num; size_t len; - -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) char *poolfs = NULL; -#endif /* canonicalize. */ p = canonicalize_file_name (path); if (p == NULL) grub_util_error ("failed to get canonical path of %s", path); -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) /* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */ { char *dummy; grub_find_zpool_from_dir (p, &dummy, &poolfs); } -#endif len = strlen (p) + 1; buf = xstrdup (p); @@ -1531,10 +1471,8 @@ grub_make_system_path_relative_to_its_ro } #endif free (buf2); -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) if (poolfs) return xasprintf ("/%s/@", poolfs); -#endif return xstrdup (""); } else === modified file 'util/grub-probe.c' --- util/grub-probe.c 2011-08-15 22:30:11 +0000 +++ util/grub-probe.c 2011-10-15 16:42:10 +0000 @@ -144,6 +144,9 @@ probe_abstraction (grub_disk_t disk) if (disk->dev->id == GRUB_DISK_DEVICE_LVM_ID) printf ("lvm "); + if (disk->dev->id == GRUB_DISK_DEVICE_ZFS_ID) + printf ("zfs "); + if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) grub_util_cryptodisk_print_abstraction (disk); @@ -171,13 +174,16 @@ probe (const char *path, char *device_na if (path == NULL) { + if (! grub_util_check_nodeless_device (device_name)) + { #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) - if (! grub_util_check_char_device (device_name)) - grub_util_error ("%s is not a character device", device_name); + if (! grub_util_check_char_device (device_name)) + grub_util_error ("%s is not a character device", device_name); #else - if (! grub_util_check_block_device (device_name)) - grub_util_error ("%s is not a block device", device_name); + if (! grub_util_check_block_device (device_name)) + grub_util_error ("%s is not a block device", device_name); #endif + } } else {