Skip to contentSkip to conversation

UUID support

PATCH In review Robert Millan 2 participants 9 messages May 29, 2008 – May 30, 2008 VFS

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 UUID support

This patch implements UUID support in ext2, then in grub-probe and search command, and finally in update-grub scripts.

The result is that when UUID can be used (i.e. our /boot is ext2), update-grub puts the number in grub.cfg as a parameter to search command, and no longer relies on hardcoded values for the `root' variable.

This is very helpful specially when device.map only maps to broken drive names (which happens always on OFW and sometimes on BIOS).

Signature · 5 lines
Robert Millan

<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)
commands/search.c renamed from grub2/commands/search.c +56 -5
diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/commands/search.c ./commands/search.c
@@ -1,7 +1,7 @@
 /* search.c - search devices based on a file or a filesystem label */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2005,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2005,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -32,6 +32,7 @@ static const struct grub_arg_option opti
   {
     {"file", 'f', 0, "search devices by a file (default)", 0, 0},
     {"label", 'l', 0, "search devices by a filesystem label", 0, 0},
+    {"fs_uuid", 'u', 0, "search devices by a filesystem UUID", 0, 0},
     {"set", 's', GRUB_ARG_OPTION_OPTIONAL, "set a variable to the first device found", "VAR", ARG_TYPE_STRING},
     {0, 0, 0, 0, 0, 0}
   };
@@ -85,6 +86,54 @@ search_label (const char *key, const cha
 }
 
 static void
+search_fs_uuid (const char *key, const char *var)
+{
+  int count = 0;
+  auto int iterate_device (const char *name);
+
+  int iterate_device (const char *name)
+    {
+      grub_device_t dev;
+
+      dev = grub_device_open (name);
+      if (dev)
+	{
+	  grub_fs_t fs;
+	  
+	  fs = grub_fs_probe (dev);
+	  if (fs && fs->uuid)
+	    {
+	      char *uuid;
+	      
+	      (fs->uuid) (dev, &uuid);
+	      if (grub_errno == GRUB_ERR_NONE && uuid)
+		{
+		  if (grub_strcmp (uuid, key) == 0)
+		    {
+		      /* Found!  */
+		      grub_printf (" %s", name);
+		      if (count++ == 0 && var)
+			grub_env_set (var, name);
+		    }
+		  
+		  grub_free (uuid);
+		}
+	    }
+	  
+	  grub_device_close (dev);
+	}
+
+      grub_errno = GRUB_ERR_NONE;
+      return 0;
+    }
+  
+  grub_device_iterate (iterate_device);
+  
+  if (count == 0)
+    grub_error (GRUB_ERR_FILE_NOT_FOUND, "no such device: %s", key);
+}
+
+static void
 search_file (const char *key, const char *var)
 {
   int count = 0;
@@ -136,11 +185,13 @@ grub_cmd_search (struct grub_arg_list *s
   if (argc == 0)
     return grub_error (GRUB_ERR_INVALID_COMMAND, "no argument specified");
 
-  if (state[2].set)
-    var = state[2].arg ? : "root";
+  if (state[3].set)
+    var = state[3].arg ? state[3].arg : "root";
   
   if (state[1].set)
     search_label (args[0], var);
+  else if (state[2].set)
+    search_fs_uuid (args[0], var);
   else
     search_file (args[0], var);
 
@@ -151,8 +202,8 @@ GRUB_MOD_INIT(search)
 {
   (void) mod;			/* To stop warning. */
   grub_register_command ("search", grub_cmd_search, GRUB_COMMAND_FLAG_BOTH,
-			 "search [-f|-l|-s] NAME",
-			 "Search devices by a file or a filesystem label."
+			 "search [-f|-l|-u|-s] NAME",
+			 "Search devices by file, filesystem label or filesystem UUID."
 			 " If --set is specified, the first device found is"
 			 " set to a variable. If no variable name is"
 			 " specified, \"root\" is used.",
fs/ext2.c renamed from grub2/fs/ext2.c +35 -2
diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/fs/ext2.c ./fs/ext2.c
@@ -120,7 +120,7 @@ struct grub_ext2_sblock
   grub_uint32_t feature_compatibility;
   grub_uint32_t feature_incompat;
   grub_uint32_t feature_ro_compat;
-  grub_uint32_t unique_id[4];
+  grub_uint16_t uuid[8];
   char volume_name[16];
   char last_mounted_on[64];
   grub_uint32_t compression_info;
@@ -861,7 +861,39 @@ grub_ext2_label (grub_device_t device, c
   if (data)
     *label = grub_strndup (data->sblock.volume_name, 14);
   else
-    *label = 0;
+    *label = NULL;
+
+#ifndef GRUB_UTIL
+  grub_dl_unref (my_mod);
+#endif
+
+  grub_free (data);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_ext2_uuid (grub_device_t device, char **uuid)
+{
+  struct grub_ext2_data *data;
+  grub_disk_t disk = device->disk;
+
+#ifndef GRUB_UTIL
+  grub_dl_ref (my_mod);
+#endif
+
+  data = grub_ext2_mount (disk);
+  if (data)
+    {
+      *uuid = grub_malloc (40 + sizeof ('\0'));
+      grub_sprintf (*uuid, "%02x%02x-%02x-%02x-%02x-%02x%02x%02x",
+		    grub_be_to_cpu16 (data->sblock.uuid[0]), grub_be_to_cpu16 (data->sblock.uuid[1]),
+		    grub_be_to_cpu16 (data->sblock.uuid[2]), grub_be_to_cpu16 (data->sblock.uuid[3]),
+		    grub_be_to_cpu16 (data->sblock.uuid[4]), grub_be_to_cpu16 (data->sblock.uuid[5]),
+		    grub_be_to_cpu16 (data->sblock.uuid[6]), grub_be_to_cpu16 (data->sblock.uuid[7]));
+    }
+  else
+    *uuid = NULL;
 
 #ifndef GRUB_UTIL
   grub_dl_unref (my_mod);
@@ -881,6 +913,7 @@ static struct grub_fs grub_ext2_fs =
     .read = grub_ext2_read,
     .close = grub_ext2_close,
     .label = grub_ext2_label,
+    .uuid = grub_ext2_uuid,
     .next = 0
   };
 
include/grub/fs.h renamed from grub2/include/grub/fs.h +5 -0
diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/include/grub/fs.h ./include/grub/fs.h
@@ -51,6 +51,11 @@ struct grub_fs
      caller.  */
   grub_err_t (*label) (grub_device_t device, char **label);
 
+  /* Return the uuid of the device DEVICE in UUID.  The uuid is
+     returned in a grub_malloc'ed buffer and should be freed by the
+     caller.  */
+  grub_err_t (*uuid) (grub_device_t device, char **uuid);
+
   /* The next filesystem.  */
   struct grub_fs *next;
 };
util/grub.d/00_header.in renamed from grub2/util/grub.d/00_header.in +6 -2
diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/grub.d/00_header.in ./util/grub.d/00_header.in
@@ -38,8 +38,12 @@ set default=${GRUB_DEFAULT}
 set timeout=${GRUB_TIMEOUT}
 EOF
 
-if [ "x${GRUB_DRIVE}" = "x" ] ; then : ; else
-  echo "set root=${GRUB_DRIVE}"
+# If there's a filesystem UUID that GRUB is capable of identifiing, use it;
+# otherwise set root as per value in device.map.
+if [ "x${GRUB_DEVICE_BOOT_UUID}" = "x" ] ; then
+  echo "set root=`grub-probe --device ${GRUB_DEVICE_BOOT} --target=drive`"
+else
+  echo "search --fs_uuid ${GRUB_DEVICE_BOOT_UUID} --set"
 fi
 
 case ${platform}:${GRUB_TERMINAL} in
util/grub.d/10_hurd.in renamed from grub2/util/grub.d/10_hurd.in +2 -2
diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/grub.d/10_hurd.in ./util/grub.d/10_hurd.in
@@ -31,9 +31,9 @@ for i in /boot/gnumach.gz /boot/gnumach 
   if test -e $i ; then
     basename=`basename $i`
     dirname=`dirname $i`
-    grub_dirname=`echo ${dirname} | sed -e "s%^/boot%${GRUB_DRIVE_BOOT}%g"`
+    rel_dirname=`make_system_path_relative_to_its_root $dirname`
     echo "Found GNU Mach: $i" >&2
-    kernel=${grub_dirname}/${basename}
+    kernel=${rel_dirname}/${basename}
     at_least_one=true
   fi
 done
util/grub.d/10_linux.in renamed from grub2/util/grub.d/10_linux.in +5 -5
diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/grub.d/10_linux.in ./util/grub.d/10_linux.in
@@ -87,7 +87,7 @@ while [ "x$list" != "x" ] ; do
   echo "Found linux image: $linux" >&2
   basename=`basename $linux`
   dirname=`dirname $linux`
-  grub_dirname=`echo ${dirname} | sed -e "s%^/boot%${GRUB_DRIVE_BOOT}%g"`
+  rel_dirname=`make_system_path_relative_to_its_root $dirname`
   version=`echo $basename | sed -e "s,^[^0-9]*-,,g"`
   alt_version=`echo $version | sed -e "s,\.old$,,g"`
 
@@ -105,11 +105,11 @@ while [ "x$list" != "x" ] ; do
 
   cat << EOF
 menuentry "${OS}, linux ${version}" {
-	linux	${grub_dirname}/${basename} root=${GRUB_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}
+	linux	${rel_dirname}/${basename} root=${GRUB_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}
 EOF
   if test -n "${initrd}" ; then
     cat << EOF
-	initrd	${grub_dirname}/${initrd}
+	initrd	${rel_dirname}/${initrd}
 EOF
   fi
   cat << EOF
@@ -118,11 +118,11 @@ EOF
 
   cat << EOF
 menuentry "${OS}, linux ${version} (single-user mode)" {
-	linux	${grub_dirname}/${basename} root=${GRUB_DEVICE} ro single ${GRUB_CMDLINE_LINUX}
+	linux	${rel_dirname}/${basename} root=${GRUB_DEVICE} ro single ${GRUB_CMDLINE_LINUX}
 EOF
   if test -n "${initrd}" ; then
     cat << EOF
-	initrd	${grub_dirname}/${initrd}
+	initrd	${rel_dirname}/${initrd}
 EOF
   fi
   cat << EOF
util/grub-probe.c renamed from grub2/util/grub-probe.c +20 -11
diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/grub-probe.c ./util/grub-probe.c
@@ -44,6 +44,7 @@
 
 enum {
   PRINT_FS,
+  PRINT_FS_UUID,
   PRINT_DRIVE,
   PRINT_DEVICE,
   PRINT_PARTMAP,
@@ -110,6 +111,7 @@ probe (const char *path, char *device_na
   char *filebuf_via_grub = NULL, *filebuf_via_sys = NULL;
   int abstraction_type;
   grub_device_t dev = NULL;
+  grub_fs_t fs;
   
   if (path == NULL)
     {
@@ -185,10 +187,13 @@ probe (const char *path, char *device_na
       goto end;
     }
 
+  fs = grub_fs_probe (dev);
+  if (! fs)
+    grub_util_error ("%s", grub_errmsg);
+
   if (print == PRINT_FS)
     {
       struct stat st;
-      grub_fs_t fs;
 
       stat (path, &st);
 
@@ -210,19 +215,21 @@ probe (const char *path, char *device_na
 	  
 	  if (memcmp (filebuf_via_grub, filebuf_via_sys, file->size))
 	    grub_util_error ("files differ");
-
-	  fs = file->fs;
-	}
-      else
-	{
-	  fs = grub_fs_probe (dev);
-	  if (! fs)
-	    grub_util_error ("%s", grub_errmsg);
 	}
-
       printf ("%s\n", fs->name);
     }
 
+  if (print == PRINT_FS_UUID)
+    {
+      char *uuid;
+      if (! fs->uuid)
+	grub_util_error ("%s does not support UUIDs", fs->name);
+
+      fs->uuid (dev, &uuid);
+
+      printf ("%s\n", uuid);
+    }
+
  end:
   if (dev)
     grub_device_close (dev);
@@ -257,7 +264,7 @@ Probe device information for a given pat
 \n\
   -d, --device              given argument is a system device, not a path\n\
   -m, --device-map=FILE     use FILE as the device map [default=%s]\n\
-  -t, --target=(fs|drive|device|partmap|abstraction)\n\
+  -t, --target=(fs|fs_uuid|drive|device|partmap|abstraction)\n\
                             print filesystem module, GRUB drive, system device, partition map module or abstraction module [default=fs]\n\
   -h, --help                display this message and exit\n\
   -V, --version             print version information and exit\n\
@@ -302,6 +309,8 @@ main (int argc, char *argv[])
 	  case 't':
 	    if (!strcmp (optarg, "fs"))
 	      print = PRINT_FS;
+	    else if (!strcmp (optarg, "fs_uuid"))
+	      print = PRINT_FS_UUID;
 	    else if (!strcmp (optarg, "drive"))
 	      print = PRINT_DRIVE;
 	    else if (!strcmp (optarg, "device"))
util/update-grub.in renamed from grub2/util/update-grub.in +6 -10
diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/update-grub.in ./util/update-grub.in
@@ -102,20 +102,16 @@ fi
 
 # Device containing our userland.  Typically used for root= parameter.
 GRUB_DEVICE="`grub-probe --target=device /`"
+GRUB_DEVICE_UUID="`grub-probe --device ${GRUB_DEVICE} --target=fs_uuid 2> /dev/null`"
+
+# Device containing our /boot partition.  Usually the same as GRUB_DEVICE.
+GRUB_DEVICE_BOOT="`grub-probe --target=device /boot`"
+GRUB_DEVICE_BOOT_UUID="`grub-probe --device ${GRUB_DEVICE_BOOT} --target=fs_uuid 2> /dev/null`"
 
 # Filesystem for the device containing our userland.  Used for stuff like
 # choosing Hurd filesystem module.
 GRUB_FS="`grub-probe --target=fs / 2> /dev/null || echo unknown`"
 
-# GRUB path to /.  Only used for "set root=".  Not critical.
-GRUB_DRIVE="`grub-probe --target=drive /`" || true
-
-# GRUB path to /boot
-GRUB_DRIVE_BOOT="`convert_system_path_to_grub_path /boot`"
-
-# GRUB path to /boot/grub
-GRUB_DRIVE_BOOT_GRUB="`convert_system_path_to_grub_path /boot/grub`"
-
 if test -f ${sysconfdir}/default/grub ; then
   . ${sysconfdir}/default/grub
 fi
@@ -155,7 +151,7 @@ esac
 
 # These are defined in this script, export them here so that user can
 # override them.
-export GRUB_DEVICE GRUB_FS GRUB_DRIVE GRUB_DRIVE_BOOT GRUB_DRIVE_BOOT_GRUB GRUB_FONT_PATH GRUB_PRELOAD_MODULES
+export GRUB_DEVICE GRUB_DEVICE_UUID GRUB_DEVICE_BOOT GRUB_DEVICE_BOOT_UUID GRUB_FS GRUB_FONT_PATH GRUB_PRELOAD_MODULES
 
 # These are optional, user-defined variables.
 export GRUB_DEFAULT GRUB_TIMEOUT GRUB_DISTRIBUTOR GRUB_CMDLINE_LINUX GRUB_CMDLINE_LINUX_DEFAULT GRUB_TERMINAL GRUB_SERIAL_COMMAND
#1 lore raw
Quoted 10 lines · Robert Millan

This patch implements UUID support in ext2, then in grub-probe and search command, and finally in update-grub scripts.

The result is that when UUID can be used (i.e. our /boot is ext2), update-grub puts the number in grub.cfg as a parameter to search command, and no longer relies on hardcoded values for the `root' variable.

This is very helpful specially when device.map only maps to broken drive names (which happens always on OFW and sometimes on BIOS).

Nice, this is a much needed feature in grub2.

BTW, I think we can embed the UUID of the root device in core.img/grub.elf, so that when the traditional root device fails to locate normal.mod, it uses the UUID to scan for root device.

Signature · 1 line
Bean
#2 lore raw
Quoted 4 lines · Bean

BTW, I think we can embed the UUID of the root device in core.img/grub.elf, so that when the traditional root device fails to locate normal.mod, it uses the UUID to scan for root device.

Good idea. But I'd check for UUID first. When you find a UUID match, it's much more reliable than a filename one.

Signature · 5 lines
Robert Millan

<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)
#3 lore raw
Quoted 8 lines · Robert Millan
Quoted 4 lines · Bean

BTW, I think we can embed the UUID of the root device in core.img/grub.elf, so that when the traditional root device fails to locate normal.mod, it uses the UUID to scan for root device.

Good idea. But I'd check for UUID first. When you find a UUID match, it's much more reliable than a filename one.

Here are some thought about how to store it. We can reuse the space occupied by prefix. Instead of one variable, we use zero-end strings like:

path=/boot/grub \0 uuid=xxx-xxx-xxxx \0 \0

We can also add other variable, for example, debug and default. debug is useful as it can show message before grub.cfg is loaded, and default is used to control the default menu entry. Other variable should be set in grub.cfg, as space is quite limited here.

path=/boot/grub \0 uuid=xxx-xxx-xxxx \0 debug=all \0 default=1 \0 \0

The space need to expand slightly, I guess something about 100 byte is more than enough. The whole block is prefixed by a magic number and block length, so that it's easy to locate by utility.

This also solve the savedefault issue, we can add a command like this:

save_env /path_to_core.img variable_name ..

It finds the magic number, and store the value of selected variable in the embedded environment block.

Signature · 1 line
Bean
#4 lore raw
Quoted 27 lines · Bean
Quoted 8 lines · Robert Millan
Quoted 4 lines · Bean

BTW, I think we can embed the UUID of the root device in core.img/grub.elf, so that when the traditional root device fails to locate normal.mod, it uses the UUID to scan for root device.

Good idea. But I'd check for UUID first. When you find a UUID match, it's much more reliable than a filename one.

Here are some thought about how to store it. We can reuse the space occupied by prefix. Instead of one variable, we use zero-end strings like:

path=/boot/grub \0 uuid=xxx-xxx-xxxx \0 \0

We can also add other variable, for example, debug and default. debug is useful as it can show message before grub.cfg is loaded, and default is used to control the default menu entry. Other variable should be set in grub.cfg, as space is quite limited here.

path=/boot/grub \0 uuid=xxx-xxx-xxxx \0 debug=all \0 default=1 \0 \0

The space need to expand slightly, I guess something about 100 byte is more than enough. The whole block is prefixed by a magic number and block length, so that it's easy to locate by utility.

Quoted 6 lines

This also solve the savedefault issue, we can add a command like this:

save_env /path_to_core.img variable_name ..

It finds the magic number, and store the value of selected variable in the embedded environment block.

Then I suppose we could have a module that gets $uuid from environment and searches for it to set $prefix?

Maybe we could search for everything else in the same module too, this way we only have to iterate once. It could check for things like:

- filesystem uuid
- partmap uuid (when we support it)
- filename (based on user input via variable, rather than hardcoded normal.mod?)
- filesystem label

Signature · 5 lines
Robert Millan

<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)
#5 lore raw
Quoted 20 lines · Robert Millan
Quoted 20 lines

Here are some thought about how to store it. We can reuse the space occupied by prefix. Instead of one variable, we use zero-end strings like:

path=/boot/grub \0 uuid=xxx-xxx-xxxx \0 \0

We can also add other variable, for example, debug and default. debug is useful as it can show message before grub.cfg is loaded, and default is used to control the default menu entry. Other variable should be set in grub.cfg, as space is quite limited here.

path=/boot/grub \0 uuid=xxx-xxx-xxxx \0 debug=all \0 default=1 \0 \0

The space need to expand slightly, I guess something about 100 byte is more than enough. The whole block is prefixed by a magic number and block length, so that it's easy to locate by utility.

This also solve the savedefault issue, we can add a command like this:

Quoted 7 lines
Quoted 4 lines

save_env /path_to_core.img variable_name ..

It finds the magic number, and store the value of selected variable in the embedded environment block.

Then I suppose we could have a module that gets $uuid from environment and searches for it to set $prefix?

However, a problem with this is that it only works for i386-pc, untill we define a way to store this for each variant.

Perhaps we could instead use memdisk to store it? Rather than put a grub.cfg in memdisk, which would require builtin normal.mod, we could put a simpler file (let's call it 'env') with initial environment, in the binary format you describe. Does that work?

Then to support it in other platforms, we just need to implement memdisk in them.

Signature · 5 lines
Robert Millan

<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)
#6 lore raw
Quoted 32 lines · Robert Millan
Quoted 20 lines · Robert Millan
Quoted 20 lines

Here are some thought about how to store it. We can reuse the space occupied by prefix. Instead of one variable, we use zero-end strings like:

path=/boot/grub \0 uuid=xxx-xxx-xxxx \0 \0

We can also add other variable, for example, debug and default. debug is useful as it can show message before grub.cfg is loaded, and default is used to control the default menu entry. Other variable should be set in grub.cfg, as space is quite limited here.

path=/boot/grub \0 uuid=xxx-xxx-xxxx \0 debug=all \0 default=1 \0 \0

The space need to expand slightly, I guess something about 100 byte is more than enough. The whole block is prefixed by a magic number and block length, so that it's easy to locate by utility.

This also solve the savedefault issue, we can add a command like this:

Quoted 7 lines
Quoted 4 lines

save_env /path_to_core.img variable_name ..

It finds the magic number, and store the value of selected variable in the embedded environment block.

Then I suppose we could have a module that gets $uuid from environment and searches for it to set $prefix?

However, a problem with this is that it only works for i386-pc, untill we define a way to store this for each variant.

Quoted 7 lines

Perhaps we could instead use memdisk to store it? Rather than put a grub.cfg in memdisk, which would require builtin normal.mod, we could put a simpler file (let's call it 'env') with initial environment, in the binary format you describe. Does that work?

Then to support it in other platforms, we just need to implement memdisk in them.

We can use some function like grub_machine_get_env to return a pointer to the environment block, and parse it in main.c. The env block is string base, it can be used in any platform.

BTW, since you have changed the filesystem structure, you need to add the uuid member to other fs modules as well, otherwise it will cause compile error.

Signature · 1 line
Bean
#7 lore raw
Quoted 9 lines · Bean
Quoted 9 lines

However, a problem with this is that it only works for i386-pc, untill we define a way to store this for each variant.

Perhaps we could instead use memdisk to store it? Rather than put a grub.cfg in memdisk, which would require builtin normal.mod, we could put a simpler file (let's call it 'env') with initial environment, in the binary format you describe. Does that work?

Quoted 6 lines
Quoted 2 lines

Then to support it in other platforms, we just need to implement memdisk in them.

We can use some function like grub_machine_get_env to return a pointer to the environment block, and parse it in main.c. The env block is string base, it can be used in any platform.

Sounds fine.

Quoted 3 lines

BTW, since you have changed the filesystem structure, you need to add the uuid member to other fs modules as well, otherwise it will cause compile error.

Optional struct members are allowed (at least by gcc). I think we have many incomplete struct declarations throurough the code.

Signature · 5 lines
Robert Millan

<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)
#8 lore raw
In reply to Robert Millan

Committed.

Quoted 395 lines · Robert Millan

This patch implements UUID support in ext2, then in grub-probe and search command, and finally in update-grub scripts.

The result is that when UUID can be used (i.e. our /boot is ext2), update-grub puts the number in grub.cfg as a parameter to search command, and no longer relies on hardcoded values for the `root' variable.

This is very helpful specially when device.map only maps to broken drive names (which happens always on OFW and sometimes on BIOS).

Signature · 7 lines
Robert Millan

<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)

diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/commands/search.c ./commands/search.c
commands/search.c renamed from grub2/commands/search.c +56 -5
@@ -1,7 +1,7 @@
 /* search.c - search devices based on a file or a filesystem label */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2005,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2005,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -32,6 +32,7 @@ static const struct grub_arg_option opti
   {
     {"file", 'f', 0, "search devices by a file (default)", 0, 0},
     {"label", 'l', 0, "search devices by a filesystem label", 0, 0},
+    {"fs_uuid", 'u', 0, "search devices by a filesystem UUID", 0, 0},
     {"set", 's', GRUB_ARG_OPTION_OPTIONAL, "set a variable to the first device found", "VAR", ARG_TYPE_STRING},
     {0, 0, 0, 0, 0, 0}
   };
@@ -85,6 +86,54 @@ search_label (const char *key, const cha
 }
 
 static void
+search_fs_uuid (const char *key, const char *var)
+{
+  int count = 0;
+  auto int iterate_device (const char *name);
+
+  int iterate_device (const char *name)
+    {
+      grub_device_t dev;
+
+      dev = grub_device_open (name);
+      if (dev)
+	{
+	  grub_fs_t fs;
+	  
+	  fs = grub_fs_probe (dev);
+	  if (fs && fs->uuid)
+	    {
+	      char *uuid;
+	      
+	      (fs->uuid) (dev, &uuid);
+	      if (grub_errno == GRUB_ERR_NONE && uuid)
+		{
+		  if (grub_strcmp (uuid, key) == 0)
+		    {
+		      /* Found!  */
+		      grub_printf (" %s", name);
+		      if (count++ == 0 && var)
+			grub_env_set (var, name);
+		    }
+		  
+		  grub_free (uuid);
+		}
+	    }
+	  
+	  grub_device_close (dev);
+	}
+
+      grub_errno = GRUB_ERR_NONE;
+      return 0;
+    }
+  
+  grub_device_iterate (iterate_device);
+  
+  if (count == 0)
+    grub_error (GRUB_ERR_FILE_NOT_FOUND, "no such device: %s", key);
+}
+
+static void
 search_file (const char *key, const char *var)
 {
   int count = 0;
@@ -136,11 +185,13 @@ grub_cmd_search (struct grub_arg_list *s
   if (argc == 0)
     return grub_error (GRUB_ERR_INVALID_COMMAND, "no argument specified");
 
-  if (state[2].set)
-    var = state[2].arg ? : "root";
+  if (state[3].set)
+    var = state[3].arg ? state[3].arg : "root";
 
   if (state[1].set)
     search_label (args[0], var);
+  else if (state[2].set)
+    search_fs_uuid (args[0], var);
   else
     search_file (args[0], var);
 
@@ -151,8 +202,8 @@ GRUB_MOD_INIT(search)
 {
   (void) mod;			/* To stop warning. */
   grub_register_command ("search", grub_cmd_search, GRUB_COMMAND_FLAG_BOTH,
-			 "search [-f|-l|-s] NAME",
-			 "Search devices by a file or a filesystem label."
+			 "search [-f|-l|-u|-s] NAME",
+			 "Search devices by file, filesystem label or filesystem UUID."
 			 " If --set is specified, the first device found is"
 			 " set to a variable. If no variable name is"
 			 " specified, \"root\" is used.",

diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/fs/ext2.c ./fs/ext2.c

fs/ext2.c renamed from grub2/fs/ext2.c +35 -2
@@ -120,7 +120,7 @@ struct grub_ext2_sblock
   grub_uint32_t feature_compatibility;
   grub_uint32_t feature_incompat;
   grub_uint32_t feature_ro_compat;
-  grub_uint32_t unique_id[4];
+  grub_uint16_t uuid[8];
   char volume_name[16];
   char last_mounted_on[64];
   grub_uint32_t compression_info;
@@ -861,7 +861,39 @@ grub_ext2_label (grub_device_t device, c
   if (data)
     *label = grub_strndup (data->sblock.volume_name, 14);
   else
-    *label = 0;
+    *label = NULL;
+
+#ifndef GRUB_UTIL
+  grub_dl_unref (my_mod);
+#endif
+
+  grub_free (data);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_ext2_uuid (grub_device_t device, char **uuid)
+{
+  struct grub_ext2_data *data;
+  grub_disk_t disk = device->disk;
+
+#ifndef GRUB_UTIL
+  grub_dl_ref (my_mod);
+#endif
+
+  data = grub_ext2_mount (disk);
+  if (data)
+    {
+      *uuid = grub_malloc (40 + sizeof ('\0'));
+      grub_sprintf (*uuid, "%02x%02x-%02x-%02x-%02x-%02x%02x%02x",
+		    grub_be_to_cpu16 (data->sblock.uuid[0]), grub_be_to_cpu16 (data->sblock.uuid[1]),
+		    grub_be_to_cpu16 (data->sblock.uuid[2]), grub_be_to_cpu16 (data->sblock.uuid[3]),
+		    grub_be_to_cpu16 (data->sblock.uuid[4]), grub_be_to_cpu16 (data->sblock.uuid[5]),
+		    grub_be_to_cpu16 (data->sblock.uuid[6]), grub_be_to_cpu16 (data->sblock.uuid[7]));
+    }
+  else
+    *uuid = NULL;
 
 #ifndef GRUB_UTIL
   grub_dl_unref (my_mod);
@@ -881,6 +913,7 @@ static struct grub_fs grub_ext2_fs =
     .read = grub_ext2_read,
     .close = grub_ext2_close,
     .label = grub_ext2_label,
+    .uuid = grub_ext2_uuid,
     .next = 0
   };
 

diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/include/grub/fs.h ./include/grub/fs.h

include/grub/fs.h renamed from grub2/include/grub/fs.h +5 -0
@@ -51,6 +51,11 @@ struct grub_fs
      caller.  */
   grub_err_t (*label) (grub_device_t device, char **label);
 
+  /* Return the uuid of the device DEVICE in UUID.  The uuid is
+     returned in a grub_malloc'ed buffer and should be freed by the
+     caller.  */
+  grub_err_t (*uuid) (grub_device_t device, char **uuid);
+
   /* The next filesystem.  */
   struct grub_fs *next;
 };

diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/grub.d/00_header.in ./util/grub.d/00_header.in

util/grub.d/00_header.in renamed from grub2/util/grub.d/00_header.in +6 -2
@@ -38,8 +38,12 @@ set default=${GRUB_DEFAULT}
 set timeout=${GRUB_TIMEOUT}
 EOF
 
-if [ "x${GRUB_DRIVE}" = "x" ] ; then : ; else
-  echo "set root=${GRUB_DRIVE}"
+# If there's a filesystem UUID that GRUB is capable of identifiing, use it;
+# otherwise set root as per value in device.map.
+if [ "x${GRUB_DEVICE_BOOT_UUID}" = "x" ] ; then
+  echo "set root=`grub-probe --device ${GRUB_DEVICE_BOOT} --target=drive`"
+else
+  echo "search --fs_uuid ${GRUB_DEVICE_BOOT_UUID} --set"
 fi
 
 case ${platform}:${GRUB_TERMINAL} in

diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/grub.d/10_hurd.in ./util/grub.d/10_hurd.in

util/grub.d/10_hurd.in renamed from grub2/util/grub.d/10_hurd.in +2 -2
@@ -31,9 +31,9 @@ for i in /boot/gnumach.gz /boot/gnumach 
   if test -e $i ; then
     basename=`basename $i`
     dirname=`dirname $i`
-    grub_dirname=`echo ${dirname} | sed -e "s%^/boot%${GRUB_DRIVE_BOOT}%g"`
+    rel_dirname=`make_system_path_relative_to_its_root $dirname`
     echo "Found GNU Mach: $i" >&2
-    kernel=${grub_dirname}/${basename}
+    kernel=${rel_dirname}/${basename}
     at_least_one=true
   fi
 done

diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/grub.d/10_linux.in ./util/grub.d/10_linux.in

util/grub.d/10_linux.in renamed from grub2/util/grub.d/10_linux.in +5 -5
@@ -87,7 +87,7 @@ while [ "x$list" != "x" ] ; do
   echo "Found linux image: $linux" >&2
   basename=`basename $linux`
   dirname=`dirname $linux`
-  grub_dirname=`echo ${dirname} | sed -e "s%^/boot%${GRUB_DRIVE_BOOT}%g"`
+  rel_dirname=`make_system_path_relative_to_its_root $dirname`
   version=`echo $basename | sed -e "s,^[^0-9]*-,,g"`
   alt_version=`echo $version | sed -e "s,\.old$,,g"`
 
@@ -105,11 +105,11 @@ while [ "x$list" != "x" ] ; do
 
   cat << EOF
 menuentry "${OS}, linux ${version}" {
-	linux	${grub_dirname}/${basename} root=${GRUB_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}
+	linux	${rel_dirname}/${basename} root=${GRUB_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}
 EOF
   if test -n "${initrd}" ; then
     cat << EOF
-	initrd	${grub_dirname}/${initrd}
+	initrd	${rel_dirname}/${initrd}
 EOF
   fi
   cat << EOF
@@ -118,11 +118,11 @@ EOF
 
   cat << EOF
 menuentry "${OS}, linux ${version} (single-user mode)" {
-	linux	${grub_dirname}/${basename} root=${GRUB_DEVICE} ro single ${GRUB_CMDLINE_LINUX}
+	linux	${rel_dirname}/${basename} root=${GRUB_DEVICE} ro single ${GRUB_CMDLINE_LINUX}
 EOF
   if test -n "${initrd}" ; then
     cat << EOF
-	initrd	${grub_dirname}/${initrd}
+	initrd	${rel_dirname}/${initrd}
 EOF
   fi
   cat << EOF

diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/grub-probe.c ./util/grub-probe.c

util/grub-probe.c renamed from grub2/util/grub-probe.c +20 -11
@@ -44,6 +44,7 @@
 
 enum {
   PRINT_FS,
+  PRINT_FS_UUID,
   PRINT_DRIVE,
   PRINT_DEVICE,
   PRINT_PARTMAP,
@@ -110,6 +111,7 @@ probe (const char *path, char *device_na
   char *filebuf_via_grub = NULL, *filebuf_via_sys = NULL;
   int abstraction_type;
   grub_device_t dev = NULL;
+  grub_fs_t fs;
 
   if (path == NULL)
     {
@@ -185,10 +187,13 @@ probe (const char *path, char *device_na
       goto end;
     }
 
+  fs = grub_fs_probe (dev);
+  if (! fs)
+    grub_util_error ("%s", grub_errmsg);
+
   if (print == PRINT_FS)
     {
       struct stat st;
-      grub_fs_t fs;
 
       stat (path, &st);
 
@@ -210,19 +215,21 @@ probe (const char *path, char *device_na
 
 	  if (memcmp (filebuf_via_grub, filebuf_via_sys, file->size))
 	    grub_util_error ("files differ");
-
-	  fs = file->fs;
-	}
-      else
-	{
-	  fs = grub_fs_probe (dev);
-	  if (! fs)
-	    grub_util_error ("%s", grub_errmsg);
 	}
-
       printf ("%s\n", fs->name);
     }
 
+  if (print == PRINT_FS_UUID)
+    {
+      char *uuid;
+      if (! fs->uuid)
+	grub_util_error ("%s does not support UUIDs", fs->name);
+
+      fs->uuid (dev, &uuid);
+
+      printf ("%s\n", uuid);
+    }
+
  end:
   if (dev)
     grub_device_close (dev);
@@ -257,7 +264,7 @@ Probe device information for a given pat
 \n\
   -d, --device              given argument is a system device, not a path\n\
   -m, --device-map=FILE     use FILE as the device map [default=%s]\n\
-  -t, --target=(fs|drive|device|partmap|abstraction)\n\
+  -t, --target=(fs|fs_uuid|drive|device|partmap|abstraction)\n\
                             print filesystem module, GRUB drive, system device, partition map module or abstraction module [default=fs]\n\
   -h, --help                display this message and exit\n\
   -V, --version             print version information and exit\n\
@@ -302,6 +309,8 @@ main (int argc, char *argv[])
 	  case 't':
 	    if (!strcmp (optarg, "fs"))
 	      print = PRINT_FS;
+	    else if (!strcmp (optarg, "fs_uuid"))
+	      print = PRINT_FS_UUID;
 	    else if (!strcmp (optarg, "drive"))
 	      print = PRINT_DRIVE;
 	    else if (!strcmp (optarg, "device"))

diff -x configure -x config.h.in -x CVS -x '*~' -x '*.mk' -urp ../grub2/util/update-grub.in ./util/update-grub.in

util/update-grub.in renamed from grub2/util/update-grub.in +6 -10
@@ -102,20 +102,16 @@ fi
 
 # Device containing our userland.  Typically used for root= parameter.
 GRUB_DEVICE="`grub-probe --target=device /`"
+GRUB_DEVICE_UUID="`grub-probe --device ${GRUB_DEVICE} --target=fs_uuid 2> /dev/null`"
+
+# Device containing our /boot partition.  Usually the same as GRUB_DEVICE.
+GRUB_DEVICE_BOOT="`grub-probe --target=device /boot`"
+GRUB_DEVICE_BOOT_UUID="`grub-probe --device ${GRUB_DEVICE_BOOT} --target=fs_uuid 2> /dev/null`"
 
 # Filesystem for the device containing our userland.  Used for stuff like
 # choosing Hurd filesystem module.
 GRUB_FS="`grub-probe --target=fs / 2> /dev/null || echo unknown`"
 
-# GRUB path to /.  Only used for "set root=".  Not critical.
-GRUB_DRIVE="`grub-probe --target=drive /`" || true
-
-# GRUB path to /boot
-GRUB_DRIVE_BOOT="`convert_system_path_to_grub_path /boot`"
-
-# GRUB path to /boot/grub
-GRUB_DRIVE_BOOT_GRUB="`convert_system_path_to_grub_path /boot/grub`"
-
 if test -f ${sysconfdir}/default/grub ; then
   . ${sysconfdir}/default/grub
 fi
@@ -155,7 +151,7 @@ esac
 
 # These are defined in this script, export them here so that user can
 # override them.
-export GRUB_DEVICE GRUB_FS GRUB_DRIVE GRUB_DRIVE_BOOT GRUB_DRIVE_BOOT_GRUB GRUB_FONT_PATH GRUB_PRELOAD_MODULES
+export GRUB_DEVICE GRUB_DEVICE_UUID GRUB_DEVICE_BOOT GRUB_DEVICE_BOOT_UUID GRUB_FS GRUB_FONT_PATH GRUB_PRELOAD_MODULES
 
 # These are optional, user-defined variables.
 export GRUB_DEFAULT GRUB_TIMEOUT GRUB_DISTRIBUTOR GRUB_CMDLINE_LINUX GRUB_CMDLINE_LINUX_DEFAULT GRUB_TERMINAL GRUB_SERIAL_COMMAND

_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel

Signature · 5 lines
Robert Millan

<GPLv2> I know my rights; I want my phone call!
<DRM> What good is a phone call… if you are unable to speak?
(as seen on /.)