Bug#540786: btrfs-probe branch

Colin Watson cjwatson at ubuntu.com
Tue May 18 12:17:24 UTC 2010


I've pushed sftp://bzr.sv.gnu.org/srv/bzr/grub/branches/btrfs-probe/
(world-readable: 'bzr get
http://bzr.savannah.gnu.org/r/grub/branches/btrfs-probe/') to handle
probing single-device btrfs filesystems.  I'm not going to pretend that
this is anything other than a hack, but it is reasonably well isolated
and it should be enough to handle the simple case where somebody has a
btrfs root filesystem and an ext[234] /boot.

Background and justification: since btrfs supports multiple-device
mounts, it always returns a virtual device (major number 0) in st_dev.
This means that grub-probe can't find the appropriate device node for a
given filesystem path just by statting the directory path and walking
/dev.  Instead, for the moment, I've taken the approach of looking
through /proc/self/mountinfo.  Note that this is pretty much exactly
what the now-widely-used udisks package does (src/mount-monitor.c), and
that's where I got the idea, although I implemented it independently to
avoid copyright assignment questions.

(Vladimir cited a rumour to the effect that libblkid may be able to
handle this, but I haven't been able to find any evidence of it knowing
how to deal with mount points at all.  We can always revisit this
later.)

This only deals with the single-device case for now.  Dealing with the
multiple-device case would (I think) require interface changes to
grub-probe, and I haven't thought about that much yet.

I've done basic testing, but would appreciate testing from others as
well.  For those who don't want to bother checking it out from revision
control, the patch follows.  It's against trunk, but should apply with
only a little bit of fuzz to 1.98; just note that in that case you'll
need to apply the patch for kern/emu/hostdisk.c to util/hostdisk.c
instead.

=== added file 'ChangeLog.btrfs-probe'
--- ChangeLog.btrfs-probe	1970-01-01 00:00:00 +0000
+++ ChangeLog.btrfs-probe	2010-05-18 12:01:59 +0000
@@ -0,0 +1,8 @@
+2010-05-18  Colin Watson  <cjwatson at ubuntu.com>
+
+	Add btrfs probing support, currently only in the single-device case.
+
+	* kern/emu/getroot.c (find_root_device_from_mountinfo): New
+	function.
+	(grub_guess_root_device): Call find_root_device_from_mountinfo
+	before looking in /dev.

=== modified file 'kern/emu/getroot.c'
--- kern/emu/getroot.c	2010-05-06 03:15:39 +0000
+++ kern/emu/getroot.c	2010-05-18 12:01:59 +0000
@@ -80,6 +80,84 @@ xgetcwd (void)
   return path;
 }
 
+#ifdef __linux__
+
+/* Statting something on a btrfs filesystem always returns a virtual device
+   major/minor pair rather than the real underlying device, because btrfs
+   can span multiple underlying devices (and even if it's currently only
+   using a single device it can be dynamically extended onto another).  We
+   can't deal with the multiple-device case yet, but in the meantime, we can
+   at least cope with the single-device case by scanning
+   /proc/self/mountinfo.  */
+static char *
+find_root_device_from_mountinfo (const char *dir)
+{
+  FILE *fp;
+  char buf[1024];	/* XXX */
+  char *ret = NULL;
+
+  fp = fopen ("/proc/self/mountinfo", "r");
+  if (! fp)
+    return NULL; /* fall through to other methods */
+
+  while (fgets (buf, sizeof (buf), fp))
+    {
+      int mnt_id, parent_mnt_id;
+      unsigned int major, minor;
+      char enc_root[PATH_MAX], enc_path[PATH_MAX];
+      int count;
+      size_t enc_path_len;
+      const char *sep;
+      char fstype[PATH_MAX], device[PATH_MAX];
+      struct stat st;
+
+      if (sscanf (buf, "%d %d %u:%u %s %s%n",
+		  &mnt_id, &parent_mnt_id, &major, &minor, enc_root, enc_path,
+		  &count) < 6)
+	continue;
+
+      if (strcmp (enc_root, "/") != 0)
+	continue; /* only a subtree is mounted */
+
+      enc_path_len = strlen (enc_path);
+      if (strncmp (dir, enc_path, enc_path_len) != 0 ||
+	  (dir[enc_path_len] && dir[enc_path_len] != '/'))
+	continue;
+
+      /* This is a parent of the requested directory.  /proc/self/mountinfo
+	 is in mount order, so it must be the closest parent we've
+	 encountered so far.  If it's virtual, return its device node;
+	 otherwise, carry on to try to find something closer.  */
+
+      free (ret);
+      ret = NULL;
+
+      if (major != 0)
+	continue; /* not a virtual device */
+
+      sep = strstr (buf + count, " - ");
+      if (!sep)
+	continue;
+
+      sep += strlen (" - ");
+      if (sscanf (sep, "%s %s", fstype, device) != 2)
+	continue;
+
+      if (stat (device, &st) < 0)
+	continue;
+
+      if (!S_ISBLK (st.st_mode))
+	continue; /* not a block device */
+
+      ret = strdup (device);
+    }
+
+  fclose (fp);
+  return ret;
+}
+
+#endif /* __linux__ */
+
 #ifdef __MINGW32__
 
 static char *
@@ -355,6 +433,12 @@ grub_guess_root_device (const char *dir)
 #else /* !__GNU__ */
   struct stat st;
 
+#ifdef __linux__
+  os_dev = find_root_device_from_mountinfo (dir);
+  if (os_dev)
+    return os_dev;
+#endif /* __linux__ */
+
   if (stat (dir, &st) < 0)
     grub_util_error ("cannot stat `%s'", dir);
 

-- 
Colin Watson                                       [cjwatson at ubuntu.com]





More information about the Pkg-grub-devel mailing list