[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#645305: getroot for ZFS without libzfs?



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
     {


Reply to: