Add a feature to backup the old boot sectors before overwritting them with grub2 boot and core images. Users can later recover the previous boot sectors with grub-install --recover option.
P.S. I found this might be a useful feature after I overwrote my second PGP encrypted hard disk (Windows XP installed) boot sectors by grub2 by mistake. Signed-off-by: Zhu Yi <yi....@intel.com> --- diff --git a/util/i386/pc/grub-install.in b/util/i386/pc/grub-install.in index 8a06213..c33bd87 100644 --- a/util/i386/pc/grub-install.in +++ b/util/i386/pc/grub-install.in @@ -51,6 +51,7 @@ no_floppy= force_lba= recheck=no debug=no +recover= if [ "${target_cpu}-${platform}" = "i386-pc" ] ; then disk_module=biosdisk @@ -77,6 +78,7 @@ Install GRUB on your drive. --no-floppy do not probe any floppy drive --recheck probe a device map even if it already exists --force install even if problems are detected + --recover restore the previous boot sectors EOF if [ "${target_cpu}-${platform}" = "i386-pc" ] ; then cat <<EOF @@ -129,6 +131,10 @@ for option in "$@"; do debug=yes ;; -f | --force) setup_force="--force" ;; + --recover) + recover="$grub_prefix/bootsectors.bak" ;; + --recover=*) + recover=`echo "$option" | sed 's/--recover=//'` ;; -*) echo "Unrecognized option \`$option'" 1>&2 usage @@ -203,6 +209,37 @@ else exit 1 fi +if test -f "$recover"; then + if test `stat -c%s $recover` -eq 512; then + echo "The backup file $recover contains MBR only. Did you install" + echo "grub2 using UNRELIABLE blocklist with --force? If so, try to" + echo "recover with `dd if=$recover of=$1 bs=512 count=1`. But you" + echo "take your own risk!" + exit 1 + fi + start=`od -j512 -N8 -An -td8 $recover` + start2=`od -j92 -N8 -An -td8 $grubdir/boot.img` + + # Synaty check for the recovery file + if test $start -ne $start2; then + echo "Error: start position of $recover doesn't match with boot.img" + exit 1 + fi + if test $((`stat -c%s $recover` - 520)) -ne \ + `stat -c%s $grubdir/core.img`; then + echo "Error: $recover doesn't match current core.img" + exit 1 + fi + + # Recovery + dd if=$recover of=$install_device bs=512 count=1 > /dev/null 2>&1 + dd if=$recover of=$install_device skip=520 seek=`expr $start \* 512` \ + bs=1 > /dev/null 2>&1 + rm -f $recover + echo "Recover boot sectors from $recover successfully" + exit 0 +fi + # Create the GRUB directory if it is not present. test -d "$bootdir" || mkdir "$bootdir" || exit 1 test -d "$grubdir" || mkdir "$grubdir" || exit 1 diff --git a/util/i386/pc/grub-setup.c b/util/i386/pc/grub-setup.c index c536be0..e862e9b 100644 --- a/util/i386/pc/grub-setup.c +++ b/util/i386/pc/grub-setup.c @@ -45,7 +45,9 @@ static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_P #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> +#include <fcntl.h> #include <dirent.h> +#include <errno.h> #include <grub/util/getroot.h> #define _GNU_SOURCE 1 @@ -53,6 +55,7 @@ static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_P #define DEFAULT_BOOT_FILE "boot.img" #define DEFAULT_CORE_FILE "core.img" +#define DEFAULT_BACKUP_FILE "bootsectors.bak" /* This is the blocklist used in the diskboot image. */ struct boot_blocklist @@ -85,7 +88,7 @@ grub_refresh (void) static void setup (const char *dir, - const char *boot_file, const char *core_file, + const char *boot_file, const char *core_file, const char *backup_file, const char *root, const char *dest, int must_embed, int force, int fs_probe) { char *boot_path, *core_path, *core_path_dev; @@ -396,6 +399,63 @@ setup (const char *dir, block->len = 0; block->segment = 0; + int grub_disk_backup(grub_disk_t disk, grub_disk_addr_t sector, + grub_off_t offset, grub_size_t size, const char *path) + { + char *tmp_buf; + char mbr[GRUB_DISK_SECTOR_SIZE]; + int fd; + + grub_util_info ("opening the backup file `%s'", path); + fd = open (path, O_CREAT|O_EXCL|O_WRONLY); + if (fd < 0) { + if (errno == EEXIST) + return 0; + else { + fprintf (stderr, "couldn't open backup file `%s'", path); + return -1; + } + } + + fp = fdopen (fd, "wb"); + if (! fp) { + close (fd); + return -1; + } + + /* Backup MBR */ + if (grub_disk_read (disk, 0, 0, GRUB_DISK_SECTOR_SIZE, mbr) != + GRUB_ERR_NONE) { + fclose (fp); + return -1; + } + + grub_util_write_image (mbr, GRUB_DISK_SECTOR_SIZE, fp); + + /* Record the start position of core image */ + if (fwrite(§or, sizeof(sector), 1, fp) != 1) { + fclose (fp); + return -1; + } + + /* Backup the sectors will be overwritten by core image */ + tmp_buf = xmalloc (size); + if (grub_disk_read (disk, sector, offset, size, tmp_buf) != GRUB_ERR_NONE) { + fclose (fp); + return -1; + } + + grub_util_write_image (tmp_buf, size, fp); + + fclose (fp); + return 0; + } + + /* Backup MBR and the sectors will be overwritten by core image */ + if (grub_disk_backup (dest_dev->disk, embed_region.start, 0, core_size, + grub_util_get_path (dir, backup_file))) + grub_util_error ("failed to backup previous boot sectors"); + /* Write the core image onto the disk. */ if (grub_disk_write (dest_dev->disk, embed_region.start, 0, core_size, core_img)) grub_util_error ("%s", grub_errmsg); @@ -548,6 +608,11 @@ unable_to_embed: grub_util_write_image (core_img, GRUB_DISK_SECTOR_SIZE * 2, fp); fclose (fp); + /* Backup MBR */ + if (grub_disk_backup (dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, + grub_util_get_path (dir, backup_file))) + grub_util_error ("failed to backup previous boot sectors"); + /* Write the boot image onto the disk. */ if (grub_disk_write (dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, boot_img)) grub_util_error ("%s", grub_errmsg); @@ -596,6 +661,7 @@ DEVICE must be a GRUB device (e.g. ``(hd0,1)'').\n\ -d, --directory=DIR use GRUB files in the directory DIR [default=%s]\n\ -m, --device-map=FILE use FILE as the device map [default=%s]\n\ -r, --root-device=DEV use DEV as the root device [default=guessed]\n\ + -k, --backup-file=FILE use FILE as the backup file [default=%s]\n\ -f, --force install even if problems are detected\n\ -s, --skip-fs-probe do not probe for filesystems in DEVICE\n\ -h, --help display this message and exit\n\ @@ -605,7 +671,7 @@ DEVICE must be a GRUB device (e.g. ``(hd0,1)'').\n\ Report bugs to <%s>.\n\ ", DEFAULT_BOOT_FILE, DEFAULT_CORE_FILE, DEFAULT_DIRECTORY, - DEFAULT_DEVICE_MAP, PACKAGE_BUGREPORT); + DEFAULT_DEVICE_MAP, DEFAULT_BACKUP_FILE, PACKAGE_BUGREPORT); exit (status); } @@ -627,6 +693,7 @@ main (int argc, char *argv[]) { char *boot_file = 0; char *core_file = 0; + char *backup_file = 0; char *dir = 0; char *dev_map = 0; char *root_dev = 0; @@ -638,7 +705,7 @@ main (int argc, char *argv[]) /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "b:c:d:m:r:hVvf", options, 0); + int c = getopt_long (argc, argv, "b:c:d:m:r:k:hVvf", options, 0); if (c == -1) break; @@ -680,6 +747,13 @@ main (int argc, char *argv[]) root_dev = xstrdup (optarg); break; + case 'k': + if (backup_file) + free (backup_file); + + backup_file = xstrdup (optarg); + break; + case 'f': force = 1; break; @@ -789,6 +863,7 @@ main (int argc, char *argv[]) setup (dir ? : DEFAULT_DIRECTORY, boot_file ? : DEFAULT_BOOT_FILE, core_file ? : DEFAULT_CORE_FILE, + backup_file ? : DEFAULT_BACKUP_FILE, root_dev, grub_util_get_grub_dev (devicelist[i]), 1, force, fs_probe); } } @@ -798,6 +873,7 @@ main (int argc, char *argv[]) setup (dir ? : DEFAULT_DIRECTORY, boot_file ? : DEFAULT_BOOT_FILE, core_file ? : DEFAULT_CORE_FILE, + backup_file ? : DEFAULT_BACKUP_FILE, root_dev, dest_dev, must_embed, force, fs_probe); /* Free resources. */ @@ -806,6 +882,7 @@ main (int argc, char *argv[]) free (boot_file); free (core_file); + free (backup_file); free (dir); free (dev_map); free (root_dev); _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel