diff --git a/ChangeLog b/ChangeLog index 9113c0381..eda01e8ec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2012-02-27 Colin Watson + + * include/grub/partition.h (grub_partition_map): Change prototype of + embed to take a maximum value for nsectors. + * include/grub/emu/hostdisk.h (grub_util_ldm_embed): Likewise. + * include/grub/fs.h (grub_fs): Likewise. + * grub-core/partmap/msdos.c (embed_signatures): New array. + (pc_partition_map_embed): Check for and avoid sectors matching any + of the signatures in embed_signatures, up to max_nsectors. + * grub-core/partmap/gpt.c (gpt_partition_map_embed): Restrict + returned sector map to max_nsectors. + * grub-core/disk/ldm.c (grub_util_ldm_embed): Likewise. + * grub-core/fs/btrfs.c (grub_btrfs_embed): Likewise. + * grub-core/fs/zfs/zfs.c (grub_zfs_embed): Likewise. + * util/grub-setup.c (setup): Allow for the embedding area being + split into multiple blocklists. Tell dest_partmap->embed the + maximum number of sectors we care about. + +2012-02-27 Vladimir Serbinenko + + * include/grub/fs.h (grub_fs) [GRUB_UTIL]: Add blocklist_install field. + Specify blocklist_install and reserver_first_sector for all fs. + * util/grub-setup.c (setup): Use FIBMAP/FIEMAP on Linux. Check resulting + blocklists. + 2012-02-27 Vladimir Serbinenko * util/grub-install.in: Clarify strings. diff --git a/grub-core/disk/ldm.c b/grub-core/disk/ldm.c index 0fd853792..f6fc4638c 100644 --- a/grub-core/disk/ldm.c +++ b/grub-core/disk/ldm.c @@ -909,6 +909,7 @@ grub_util_is_ldm (grub_disk_t disk) grub_err_t grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors, + unsigned int max_nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { @@ -967,6 +968,8 @@ grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors, N_("your LDM embedding Partition is too small;" " embedding won't be possible")); *nsectors = lv->size; + if (*nsectors > max_nsectors) + *nsectors = max_nsectors; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index c7b09fa8b..a41ef30dc 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -1620,6 +1620,7 @@ grub_btrfs_label (grub_device_t device, char **label) static grub_err_t grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), unsigned int *nsectors, + unsigned int max_nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { @@ -1635,6 +1636,8 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), "It won't fit in the embedding area")); *nsectors = 64 * 2 - 1; + if (*nsectors > max_nsectors) + *nsectors = max_nsectors; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 232005043..d500e5c0d 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -3902,6 +3902,7 @@ grub_zfs_dir (grub_device_t device, const char *path, static grub_err_t grub_zfs_embed (grub_device_t device __attribute__ ((unused)), unsigned int *nsectors, + unsigned int max_nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { @@ -3917,6 +3918,8 @@ grub_zfs_embed (grub_device_t device __attribute__ ((unused)), "It won't fit in the embedding area")); *nsectors = (VDEV_BOOT_SIZE >> GRUB_DISK_SECTOR_BITS); + if (*nsectors > max_nsectors) + *nsectors = max_nsectors; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; diff --git a/grub-core/partmap/gpt.c b/grub-core/partmap/gpt.c index 8160b85d7..c51753d5f 100644 --- a/grub-core/partmap/gpt.c +++ b/grub-core/partmap/gpt.c @@ -128,6 +128,7 @@ grub_gpt_partition_map_iterate (grub_disk_t disk, #ifdef GRUB_UTIL static grub_err_t gpt_partition_map_embed (struct grub_disk *disk_, unsigned int *nsectors, + unsigned int max_nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { @@ -183,6 +184,8 @@ gpt_partition_map_embed (struct grub_disk *disk_, unsigned int *nsectors, " embedding won't be possible")); *nsectors = len; + if (*nsectors > max_nsectors) + *nsectors = max_nsectors; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; diff --git a/grub-core/partmap/msdos.c b/grub-core/partmap/msdos.c index 0049fbd46..2b3bf167c 100644 --- a/grub-core/partmap/msdos.c +++ b/grub-core/partmap/msdos.c @@ -30,6 +30,66 @@ GRUB_MOD_LICENSE ("GPLv3+"); static struct grub_partition_map grub_msdos_partition_map; +#ifdef GRUB_UTIL +#include + +struct embed_signature +{ + const char *name; + const char *signature; + int signature_len; + enum { TYPE_SOFTWARE, TYPE_RAID } type; +}; + +const char message_warn[][200] = { + [TYPE_RAID] = N_("Sector %llu is already in use by %s; avoiding it. " + "Please ask the manufacturer not to store data in MBR gap"), + [TYPE_SOFTWARE] = N_("Sector %llu is already in use by %s; avoiding it. " + "This software may cause boot or other problems in " + "future. Please ask its authors not to store data " + "in the boot track") +}; + + +/* Signatures of other software that may be using sectors in the embedding + area. */ +struct embed_signature embed_signatures[] = + { + { + .name = "ZISD", + .signature = "ZISD", + .signature_len = 4, + .type = TYPE_SOFTWARE + }, + { + .name = "FlexNet", + .signature = "\xd4\x41\xa0\xf5\x03\x00\x03\x00", + .signature_len = 8, + .type = TYPE_SOFTWARE + }, + { + .name = "FlexNet", + .signature = "\xd8\x41\xa0\xf5\x02\x00\x02\x00", + .signature_len = 8, + .type = TYPE_SOFTWARE + }, + { + /* from Ryan Perkins */ + .name = "HP Backup and Recovery Manager (?)", + .signature = "\x70\x8a\x5d\x46\x35\xc5\x1b\x93" + "\xae\x3d\x86\xfd\xb1\x55\x3e\xe0", + .signature_len = 16, + .type = TYPE_SOFTWARE + }, + { + .name = "HighPoint RAID controller", + .signature = "ycgl", + .signature_len = 4, + .type = TYPE_RAID + } + }; +#endif + grub_err_t grub_partition_msdos_iterate (grub_disk_t disk, int (*hook) (grub_disk_t disk, @@ -154,6 +214,7 @@ grub_partition_msdos_iterate (grub_disk_t disk, #ifdef GRUB_UTIL static grub_err_t pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, + unsigned int max_nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { @@ -247,13 +308,65 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, if (end >= *nsectors + 2) { - unsigned i; + unsigned i, j; + char *embed_signature_check; + unsigned int orig_nsectors, avail_nsectors; + + orig_nsectors = *nsectors; *nsectors = end - 2; + avail_nsectors = *nsectors; + if (*nsectors > max_nsectors) + *nsectors = max_nsectors; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; for (i = 0; i < *nsectors; i++) (*sectors)[i] = 1 + i; + + /* Check for software that is already using parts of the embedding + * area. + */ + embed_signature_check = grub_malloc (GRUB_DISK_SECTOR_SIZE); + for (i = 0; i < *nsectors; i++) + { + if (grub_disk_read (disk, (*sectors)[i], 0, GRUB_DISK_SECTOR_SIZE, + embed_signature_check)) + continue; + + for (j = 0; j < ARRAY_SIZE (embed_signatures); j++) + if (! grub_memcmp (embed_signatures[j].signature, + embed_signature_check, + embed_signatures[j].signature_len)) + break; + if (j == ARRAY_SIZE (embed_signatures)) + continue; + grub_util_warn (_(message_warn[embed_signatures[j].type]), + (*sectors)[i], embed_signatures[j].name); + avail_nsectors--; + if (avail_nsectors < *nsectors) + *nsectors = avail_nsectors; + + /* Avoid this sector. */ + for (j = i; j < *nsectors; j++) + (*sectors)[j]++; + + /* Have we run out of space? */ + if (avail_nsectors < orig_nsectors) + break; + + /* Make sure to check the next sector. */ + i--; + } + grub_free (embed_signature_check); + + if (*nsectors < orig_nsectors) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("other software is using the embedding area, and " + "there is not enough room for core.img. Such " + "software is often trying to store data in a way " + "that avoids detection. We recommend you " + "investigate")); + return GRUB_ERR_NONE; } diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index b3db85902..88c20f5c9 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -48,6 +48,7 @@ grub_util_is_ldm (grub_disk_t disk); #ifdef GRUB_UTIL grub_err_t grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors, + unsigned int max_nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors); #endif diff --git a/include/grub/fs.h b/include/grub/fs.h index 00b78591d..503d1a9cb 100644 --- a/include/grub/fs.h +++ b/include/grub/fs.h @@ -81,6 +81,7 @@ struct grub_fs #ifdef GRUB_UTIL /* Determine sectors available for embedding. */ grub_err_t (*embed) (grub_device_t device, unsigned int *nsectors, + unsigned int max_nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors); diff --git a/include/grub/partition.h b/include/grub/partition.h index 7c1e7f6b3..ec0a667d2 100644 --- a/include/grub/partition.h +++ b/include/grub/partition.h @@ -50,6 +50,7 @@ struct grub_partition_map #ifdef GRUB_UTIL /* Determine sectors available for embedding. */ grub_err_t (*embed) (struct grub_disk *disk, unsigned int *nsectors, + unsigned int max_nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors); #endif diff --git a/util/grub-setup.c b/util/grub-setup.c index 2b4dce4aa..300994cc9 100644 --- a/util/grub-setup.c +++ b/util/grub-setup.c @@ -337,7 +337,7 @@ setup (const char *dir, grub_disk_addr_t *sectors; int i; grub_fs_t fs; - unsigned int nsec; + unsigned int nsec, maxsec; /* Unlike root_dev, with dest_dev we're interested in the partition map even if dest_dev itself is a whole disk. */ @@ -457,14 +457,21 @@ setup (const char *dir, } nsec = core_sectors; + + maxsec = 2 * core_sectors; + if (maxsec > ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) + >> GRUB_DISK_SECTOR_BITS)) + maxsec = ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) + >> GRUB_DISK_SECTOR_BITS); + if (is_ldm) - err = grub_util_ldm_embed (dest_dev->disk, &nsec, + err = grub_util_ldm_embed (dest_dev->disk, &nsec, maxsec, GRUB_EMBED_PCBIOS, §ors); else if (dest_partmap) - err = dest_partmap->embed (dest_dev->disk, &nsec, + err = dest_partmap->embed (dest_dev->disk, &nsec, maxsec, GRUB_EMBED_PCBIOS, §ors); else - err = fs->embed (dest_dev, &nsec, + err = fs->embed (dest_dev, &nsec, maxsec, GRUB_EMBED_PCBIOS, §ors); if (!err && nsec < core_sectors) { @@ -472,6 +479,8 @@ setup (const char *dir, N_("Your embedding area is unusually small. " "core.img won't fit in it.")); } + err = dest_partmap->embed (dest_dev->disk, &nsec, maxsec, + GRUB_EMBED_PCBIOS, §ors); if (err) { @@ -480,12 +489,7 @@ setup (const char *dir, goto unable_to_embed; } - if (nsec > 2 * core_sectors) - nsec = 2 * core_sectors; - if (nsec > ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) - >> GRUB_DISK_SECTOR_BITS)) - nsec = ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) - >> GRUB_DISK_SECTOR_BITS); + assert (nsec <= maxsec); /* Clean out the blocklists. */ block = first_block; @@ -507,6 +511,13 @@ setup (const char *dir, save_blocklists (sectors[i] + grub_partition_get_start (container), 0, GRUB_DISK_SECTOR_SIZE); + /* Make sure that the last blocklist is a terminator. */ + if (block == first_block) + block--; + block->start = 0; + block->len = 0; + block->segment = 0; + write_rootdev (core_img, root_dev, boot_img, first_sector); core_img = realloc (core_img, nsec * GRUB_DISK_SECTOR_SIZE); @@ -535,12 +546,6 @@ setup (const char *dir, assert (grub_memcmp (tmp, core_img, core_size) == 0); free (tmp); - /* Make sure that the second blocklist is a terminator. */ - block = first_block - 1; - block->start = 0; - block->len = 0; - block->segment = 0; - /* Write the core image onto the disk. */ for (i = 0; i < nsec; i++) grub_disk_write (dest_dev->disk, sectors[i], 0,