From: Andrei Borzenkov Subject: [PATCH] grub-install: allow none or multiple install devices on PC BIOS There are two main applications. 1. Omit install device to create generic image intended for chainloading from other master loader. Such image can be put on any device (or file system) and will still be able to find its $root. Currently even with --no-bootsector grub-install optimizes image by skipping UUID search if possible. 2. Redundant installation on multi-device filesystem, RAID or similar. This allows both optimizing image w.r.t. to using --prefix vs. load.cfg as well as creating image just once. Patch allows transparently use none or multiple installation devices, similar to grub_devices="/dev/sda /dev/sda1 /dev/sdb" grub-install $grub_devices where grub_devices can be empty and still do the right thing. --- grub-core/kern/disk.c | 9 ++- include/grub/disk.h | 2 + util/grub-install.c | 202 ++++++++++++++++++++++++++++---------------------- 3 files changed, 122 insertions(+), 91 deletions(-) diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index 789f8c0..29e39a5 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -168,8 +168,11 @@ grub_disk_dev_unregister (grub_disk_dev_t dev) /* Return the location of the first ',', if any, which is not escaped by a '\'. */ -static const char * -find_part_sep (const char *name) +#if !defined (GRUB_UTIL) +static +#endif +const char * +grub_find_part_sep (const char *name) { const char *p = name; char c; @@ -203,7 +206,7 @@ grub_disk_open (const char *name) disk->max_agglomerate = 1048576 >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS); - p = find_part_sep (name); + p = grub_find_part_sep (name); if (p) { grub_size_t len = p - name; diff --git a/include/grub/disk.h b/include/grub/disk.h index b385af8..254c920 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -256,6 +256,8 @@ void grub_ldm_fini (void); void grub_mdraid09_fini (void); void grub_mdraid1x_fini (void); void grub_diskfilter_fini (void); +const char *grub_find_part_sep (const char *); + #endif #endif /* ! GRUB_DISK_HEADER */ diff --git a/util/grub-install.c b/util/grub-install.c index 6c89c2b..9a0919c 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -57,7 +57,10 @@ static char *target; static int removable = 0; static int recheck = 0; static int update_nvram = 1; -static char *install_device = NULL; +static char **install_devices = NULL; +static char **install_drives = NULL; +static int n_install_devices; +static int n_allocated_devices; static char *debug_image = NULL; static char *rootdir = NULL; static char *bootdir = NULL; @@ -234,9 +237,12 @@ argp_parser (int key, char *arg, struct argp_state *state) return 0; case ARGP_KEY_ARG: - if (install_device) - grub_util_error ("%s", _("More than one install device?")); - install_device = xstrdup (arg); + if (n_install_devices >= n_allocated_devices) + { + n_allocated_devices += 16; + install_devices = xrealloc (install_devices, n_allocated_devices); + } + install_devices[n_install_devices++] = xstrdup (arg); return 0; default: @@ -534,25 +540,22 @@ probe_cryptodisk_uuid (grub_disk_t disk) } static int -is_same_disk (const char *a, const char *b) +is_same_disk (const char *root, char **install_devs) { - while (1) + char **d; + const char *root_part = grub_find_part_sep (root); + size_t root_len = root_part ? root_part - root : strlen (root); + + for (d = install_devs; *d; d++) { - if ((*a == ',' || *a == '\0') && (*b == ',' || *b == '\0')) - return 1; - if (*a != *b) + const char *p = grub_find_part_sep (*d); + size_t len = p ? p - *d : strlen (*d); + + if (len != root_len || (len && strncmp (root, *d, len))) return 0; - if (*a == '\\') - { - if (a[1] != b[1]) - return 0; - a += 2; - b += 2; - continue; - } - a++; - b++; } + + return 1; } static char * @@ -815,6 +818,21 @@ fill_core_services (const char *core_services) free (sysv_plist); } +static void +free_install_devices (void) +{ + size_t i; + + if (!install_devices) + return; + + for (i = 0; i < n_install_devices; i++) + free (install_devices[i]); + free (install_devices); + install_devices = NULL; + n_install_devices = n_allocated_devices = 0; +} + int main (int argc, char *argv[]) { @@ -835,6 +853,8 @@ main (int argc, char *argv[]) int efidir_is_mac = 0; int is_prep = 0; const char *pkgdatadir; + size_t i; + grub_util_host_init (&argc, &argv); product_version = xstrdup (PACKAGE_VERSION); @@ -926,14 +946,25 @@ main (int argc, char *argv[]) switch (platform) { case GRUB_INSTALL_PLATFORM_I386_PC: + break; + + default: + if (n_install_devices > 1) + grub_util_error ("%s", _("More than one install device?")); + break; + } + + switch (platform) + { case GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275: - if (!install_device) + if (!install_devices) grub_util_error ("%s", _("install device isn't specified")); break; case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275: - if (install_device) + if (install_devices) is_prep = 1; break; + case GRUB_INSTALL_PLATFORM_I386_PC: case GRUB_INSTALL_PLATFORM_MIPS_ARC: case GRUB_INSTALL_PLATFORM_MIPSEL_ARC: break; @@ -952,8 +983,7 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_MIPS_QEMU_MIPS: case GRUB_INSTALL_PLATFORM_I386_XEN: case GRUB_INSTALL_PLATFORM_X86_64_XEN: - free (install_device); - install_device = NULL; + free_install_devices (); break; /* pacify warning. */ @@ -1009,8 +1039,6 @@ main (int argc, char *argv[]) if (is_efi) { grub_fs_t fs; - free (install_device); - install_device = NULL; if (!efidir) { char *d = grub_util_path_concat (2, bootdir, "efi"); @@ -1045,7 +1073,8 @@ main (int argc, char *argv[]) if (!efidir_device_names || !efidir_device_names[0]) grub_util_error (_("cannot find a device for %s (is /dev mounted?)"), efidir); - install_device = efidir_device_names[0]; + /* We will use just the first device */ + install_devices = efidir_device_names; for (curdev = efidir_device_names; *curdev; curdev++) grub_util_pull_device (*curdev); @@ -1207,7 +1236,9 @@ main (int argc, char *argv[]) if (grub_strcmp (fs->name, "hfs") == 0 || grub_strcmp (fs->name, "hfsplus") == 0) { - install_device = macppcdir_device_names[0]; + /* We just use the first one */ + free_install_devices (); + install_devices = macppcdir_device_names; is_prep = 0; } } @@ -1319,35 +1350,38 @@ main (int argc, char *argv[]) debug_image); } char *prefix_drive = NULL; - char *install_drive = NULL; - if (install_device) + if (install_devices) { - if (install_device[0] == '(' - && install_device[grub_strlen (install_device) - 1] == ')') - { - size_t len = grub_strlen (install_device) - 2; - install_drive = xmalloc (len + 1); - memcpy (install_drive, install_device + 1, len); - install_drive[len] = '\0'; - } - else - { - grub_util_pull_device (install_device); - install_drive = grub_util_get_grub_dev (install_device); - if (!install_drive) - grub_util_error (_("cannot find a GRUB drive for %s. Check your device.map"), - install_device); - } + install_drives = xmalloc ((n_install_devices + 1) * sizeof (*install_drives)); + + for (i = 0; i < n_install_devices; i++) + if (install_devices[i][0] == '(' + && install_devices[i][grub_strlen (install_devices[i]) - 1] == ')') + { + size_t len = grub_strlen (install_devices[i]) - 2; + install_drives[i] = xmalloc (len + 1); + memcpy (install_drives[i], install_devices[i] + 1, len); + install_drives[i][len] = '\0'; + } + else + { + grub_util_pull_device (install_devices[i]); + install_drives[i] = grub_util_get_grub_dev (install_devices[i]); + if (!install_drives[i]) + grub_util_error (_("cannot find a GRUB drive for %s. Check your device.map"), + install_devices[i]); + } + install_drives[n_install_devices] = NULL; } if (!have_abstractions) { if ((disk_module && grub_strcmp (disk_module, "biosdisk") != 0) || grub_drives[1] - || (!install_drive + || (!install_drives && platform != GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275) - || (install_drive && !is_same_disk (grub_drives[0], install_drive)) + || (install_drives && !is_same_disk (grub_drives[0], install_drives)) || !have_bootdev (platform)) { char *uuid = NULL; @@ -1480,19 +1514,8 @@ main (int argc, char *argv[]) else { /* We need to hardcode the partition number in the core image's prefix. */ - char *p; - for (p = grub_drives[0]; *p; ) - { - if (*p == '\\' && p[1]) - { - p += 2; - continue; - } - if (*p == ',' || *p == '\0') - break; - p++; - } - prefix_drive = xasprintf ("(%s)", p); + const char *p = grub_find_part_sep (grub_drives[0]); + prefix_drive = xasprintf ("(%s)", p ?: ""); } } else @@ -1657,24 +1680,27 @@ main (int argc, char *argv[]) "boot.img"); grub_install_copy_file (boot_img_src, boot_img, 1); - grub_util_info ("%sgrub-bios-setup %s %s %s %s %s --directory='%s' --device-map='%s' '%s'", - /* TRANSLATORS: This is a prefix in the log to indicate that usually - a command would be executed but due to an option was skipped. */ - install_bootsector ? "" : _("NOT RUNNING: "), - allow_floppy ? "--allow-floppy " : "", - verbosity ? "--verbose " : "", - force ? "--force " : "", - !fs_probe ? "--skip-fs-probe" : "", - !add_rs_codes ? "--no-rs-codes" : "", - platdir, - device_map, - install_device); - - /* Now perform the installation. */ - if (install_bootsector) - grub_util_bios_setup (platdir, "boot.img", "core.img", - install_drive, force, - fs_probe, allow_floppy, add_rs_codes); + for (i = 0; i < n_install_devices; i++) + { + grub_util_info ("%sgrub-bios-setup %s %s %s %s %s --directory='%s' --device-map='%s' '%s'", + /* TRANSLATORS: This is a prefix in the log to indicate that usually + a command would be executed but due to an option was skipped. */ + install_bootsector ? "" : _("NOT RUNNING: "), + allow_floppy ? "--allow-floppy " : "", + verbosity ? "--verbose " : "", + force ? "--force " : "", + !fs_probe ? "--skip-fs-probe" : "", + !add_rs_codes ? "--no-rs-codes" : "", + platdir, + device_map, + install_devices[i]); + + /* Now perform the installation. */ + if (install_bootsector) + grub_util_bios_setup (platdir, "boot.img", "core.img", + install_drives[i], force, + fs_probe, allow_floppy, add_rs_codes); + } break; } case GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275: @@ -1694,12 +1720,12 @@ main (int argc, char *argv[]) !fs_probe ? "--skip-fs-probe" : "", platdir, device_map, - install_drive); + install_drives[0]); /* Now perform the installation. */ if (install_bootsector) grub_util_sparc_setup (platdir, "boot.img", "core.img", - install_drive, force, + install_drives[0], force, fs_probe, allow_floppy, 0 /* unused */ ); break; @@ -1735,7 +1761,7 @@ main (int argc, char *argv[]) fill_core_services (core_services); - ins_dev = grub_device_open (install_drive); + ins_dev = grub_device_open (install_drives[0]); bless (ins_dev, core_services, 0); @@ -1746,7 +1772,7 @@ main (int argc, char *argv[]) partno = ins_dev->disk->partition ? ins_dev->disk->partition->number + 1 : 0; - dev = grub_util_get_os_disk (install_device); + dev = grub_util_get_os_disk (install_devices[0]); grub_install_register_ieee1275 (0, dev, partno, "\\\\BootX"); } @@ -1758,10 +1784,10 @@ main (int argc, char *argv[]) break; } /* If a install device is defined, copy the core.elf to PReP partition. */ - if (is_prep && install_device && install_device[0]) + if (is_prep) { grub_device_t ins_dev; - ins_dev = grub_device_open (install_drive); + ins_dev = grub_device_open (install_drives[0]); if (!ins_dev || !is_prep_partition (ins_dev)) { grub_util_error ("%s", _("the chosen partition is not a PReP partition")); @@ -1773,13 +1799,13 @@ main (int argc, char *argv[]) } else { - char *s = xasprintf ("dd if=/dev/zero of=%s", install_device); + char *s = xasprintf ("dd if=/dev/zero of=%s", install_devices[0]); grub_util_error (_("the PReP partition is not empty. If you are sure you want to use it, run dd to clear it: `%s'"), s); } grub_device_close (ins_dev); if (update_nvram) - grub_install_register_ieee1275 (1, grub_util_get_os_disk (install_device), + grub_install_register_ieee1275 (1, grub_util_get_os_disk (install_devices[0]), 0, NULL); break; } @@ -1799,7 +1825,7 @@ main (int argc, char *argv[]) } break; case GRUB_INSTALL_PLATFORM_MIPS_ARC: - grub_install_sgi_setup (install_device, imgfile, "grub"); + grub_install_sgi_setup (install_devices[0], imgfile, "grub"); break; case GRUB_INSTALL_PLATFORM_I386_EFI: @@ -1835,7 +1861,7 @@ main (int argc, char *argv[]) fill_core_services(core_services); - ins_dev = grub_device_open (install_drive); + ins_dev = grub_device_open (install_drives[0]); bless (ins_dev, boot_efi, 1); if (!removable && update_nvram) -- tg: (080a208..) u/optional-install-device (depends on: master)