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 <cjwat...@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 [cjwat...@ubuntu.com] -- To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org