mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-10-30 23:34:47 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			267 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			267 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* gptsync.c - fill the mbr based on gpt entries  */
 | ||
| /* XXX: I don't know what to do if sector size isn't 512 bytes */
 | ||
| /*
 | ||
|  *  GRUB  --  GRand Unified Bootloader
 | ||
|  *  Copyright (C) 2009  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
 | ||
|  *  the Free Software Foundation, either version 3 of the License, or
 | ||
|  *  (at your option) any later version.
 | ||
|  *
 | ||
|  *  GRUB is distributed in the hope that it will be useful,
 | ||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||
|  *  GNU General Public License for more details.
 | ||
|  *
 | ||
|  *  You should have received a copy of the GNU General Public License
 | ||
|  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 | ||
|  */
 | ||
| 
 | ||
| #include <grub/command.h>
 | ||
| #include <grub/dl.h>
 | ||
| #include <grub/device.h>
 | ||
| #include <grub/disk.h>
 | ||
| #include <grub/msdos_partition.h>
 | ||
| #include <grub/partition.h>
 | ||
| #include <grub/misc.h>
 | ||
| #include <grub/mm.h>
 | ||
| #include <grub/fs.h>
 | ||
| #include <grub/i18n.h>
 | ||
| 
 | ||
| GRUB_MOD_LICENSE ("GPLv3+");
 | ||
| 
 | ||
| /* Convert a LBA address to a CHS address in the INT 13 format.  */
 | ||
| /* Taken from grub1. */
 | ||
| /* XXX: use hardcoded geometry of C = 1024, H = 255, S = 63.
 | ||
|    Is it a problem?
 | ||
| */
 | ||
| static void
 | ||
| lba_to_chs (grub_uint32_t lba, grub_uint8_t *cl, grub_uint8_t *ch,
 | ||
| 	    grub_uint8_t *dh)
 | ||
| {
 | ||
|   grub_uint32_t cylinder, head, sector;
 | ||
|   grub_uint32_t sectors = 63, heads = 255, cylinders = 1024;
 | ||
| 
 | ||
|   sector = lba % sectors + 1;
 | ||
|   head = (lba / sectors) % heads;
 | ||
|   cylinder = lba / (sectors * heads);
 | ||
| 
 | ||
|   if (cylinder >= cylinders)
 | ||
|     {
 | ||
|       *cl = *ch = *dh = 0xff;
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   *cl = sector | ((cylinder & 0x300) >> 2);
 | ||
|   *ch = cylinder & 0xFF;
 | ||
|   *dh = head;
 | ||
| }
 | ||
| 
 | ||
| static grub_err_t
 | ||
| grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)),
 | ||
| 		  int argc, char **args)
 | ||
| {
 | ||
|   grub_device_t dev;
 | ||
|   struct grub_msdos_partition_mbr mbr;
 | ||
|   struct grub_partition *partition;
 | ||
|   grub_disk_addr_t first_sector;
 | ||
|   int numactive = 0;
 | ||
|   int i;
 | ||
| 
 | ||
|   if (argc < 1)
 | ||
|     return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
 | ||
|   if (argc > 4)
 | ||
|     return grub_error (GRUB_ERR_BAD_ARGUMENT, "only 3 partitions can be "
 | ||
| 		       "in hybrid MBR");
 | ||
| 
 | ||
|   if (args[0][0] == '(' && args[0][grub_strlen (args[0]) - 1] == ')')
 | ||
|     {
 | ||
|       args[0][grub_strlen (args[0]) - 1] = 0;
 | ||
|       dev = grub_device_open (args[0] + 1);
 | ||
|       args[0][grub_strlen (args[0])] = ')';
 | ||
|     }
 | ||
|   else
 | ||
|     dev = grub_device_open (args[0]);
 | ||
| 
 | ||
|   if (! dev)
 | ||
|     return grub_errno;
 | ||
| 
 | ||
|   if (! dev->disk)
 | ||
|     {
 | ||
|       grub_device_close (dev);
 | ||
|       return grub_error (GRUB_ERR_BAD_ARGUMENT, "not a disk");
 | ||
|     }
 | ||
| 
 | ||
|   /* Read the protective MBR.  */
 | ||
|   if (grub_disk_read (dev->disk, 0, 0, sizeof (mbr), &mbr))
 | ||
|     {
 | ||
|       grub_device_close (dev);
 | ||
|       return grub_errno;
 | ||
|     }
 | ||
| 
 | ||
|   /* Check if it is valid.  */
 | ||
|   if (mbr.signature != grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE))
 | ||
|     {
 | ||
|       grub_device_close (dev);
 | ||
|       return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");
 | ||
|     }
 | ||
| 
 | ||
|   /* Make sure the MBR is a protective MBR and not a normal MBR.  */
 | ||
|   for (i = 0; i < 4; i++)
 | ||
|     if (mbr.entries[i].type == GRUB_PC_PARTITION_TYPE_GPT_DISK)
 | ||
|       break;
 | ||
|   if (i == 4)
 | ||
|     {
 | ||
|       grub_device_close (dev);
 | ||
|       return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found");
 | ||
|     }
 | ||
| 
 | ||
|   first_sector = dev->disk->total_sectors;
 | ||
|   for (i = 1; i < argc; i++)
 | ||
|     {
 | ||
|       char *separator, csep = 0;
 | ||
|       grub_uint8_t type;
 | ||
|       separator = grub_strchr (args[i], '+');
 | ||
|       if (! separator)
 | ||
| 	separator = grub_strchr (args[i], '-');
 | ||
|       if (separator)
 | ||
| 	{
 | ||
| 	  csep = *separator;
 | ||
| 	  *separator = 0;
 | ||
| 	}
 | ||
|       partition = grub_partition_probe (dev->disk, args[i]);
 | ||
|       if (separator)
 | ||
| 	*separator = csep;
 | ||
|       if (! partition)
 | ||
| 	{
 | ||
| 	  grub_device_close (dev);
 | ||
| 	  return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
 | ||
| 			     N_("no such partition"));
 | ||
| 	}
 | ||
| 
 | ||
|       if (partition->start + partition->len > 0xffffffff)
 | ||
| 	{
 | ||
| 	  grub_device_close (dev);
 | ||
| 	  return grub_error (GRUB_ERR_OUT_OF_RANGE,
 | ||
| 			     "only partitions residing in the first 2TB "
 | ||
| 			     "can be present in hybrid MBR");
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
|       if (first_sector > partition->start)
 | ||
| 	first_sector = partition->start;
 | ||
| 
 | ||
|       if (separator && *(separator + 1))
 | ||
| 	type = grub_strtoul (separator + 1, 0, 0);
 | ||
|       else
 | ||
| 	{
 | ||
| 	  grub_fs_t fs = 0;
 | ||
| 	  dev->disk->partition = partition;
 | ||
| 	  fs = grub_fs_probe (dev);
 | ||
| 
 | ||
| 	  /* Unknown filesystem isn't fatal. */
 | ||
| 	  if (grub_errno == GRUB_ERR_UNKNOWN_FS)
 | ||
| 	    {
 | ||
| 	      fs = 0;
 | ||
| 	      grub_errno = GRUB_ERR_NONE;
 | ||
| 	    }
 | ||
| 
 | ||
| 	  if (fs && grub_strcmp (fs->name, "ntfs") == 0)
 | ||
| 	    type = GRUB_PC_PARTITION_TYPE_NTFS;
 | ||
| 	  else if (fs && grub_strcmp (fs->name, "fat") == 0)
 | ||
| 	    /* FIXME: detect FAT16. */
 | ||
| 	    type = GRUB_PC_PARTITION_TYPE_FAT32_LBA;
 | ||
| 	  else if (fs && (grub_strcmp (fs->name, "hfsplus") == 0
 | ||
| 			  || grub_strcmp (fs->name, "hfs") == 0))
 | ||
| 	    type = GRUB_PC_PARTITION_TYPE_HFS;
 | ||
| 	  else
 | ||
| 	    /* FIXME: detect more types. */
 | ||
| 	    type = GRUB_PC_PARTITION_TYPE_EXT2FS;
 | ||
| 
 | ||
| 	  dev->disk->partition = 0;
 | ||
| 	}
 | ||
| 
 | ||
|       mbr.entries[i].flag = (csep == '+') ? 0x80 : 0;
 | ||
|       if (csep == '+')
 | ||
| 	{
 | ||
| 	  numactive++;
 | ||
| 	  if (numactive == 2)
 | ||
| 	    {
 | ||
| 	      grub_device_close (dev);
 | ||
| 	      return grub_error (GRUB_ERR_BAD_ARGUMENT,
 | ||
| 				 "only one partition can be active");
 | ||
| 	    }
 | ||
| 	}
 | ||
|       mbr.entries[i].type = type;
 | ||
|       mbr.entries[i].start = grub_cpu_to_le32 (partition->start);
 | ||
|       lba_to_chs (partition->start,
 | ||
| 		  &(mbr.entries[i].start_sector),
 | ||
| 		  &(mbr.entries[i].start_cylinder),
 | ||
| 		  &(mbr.entries[i].start_head));
 | ||
|       lba_to_chs (partition->start + partition->len - 1,
 | ||
| 		  &(mbr.entries[i].end_sector),
 | ||
| 		  &(mbr.entries[i].end_cylinder),
 | ||
| 		  &(mbr.entries[i].end_head));
 | ||
|       mbr.entries[i].length = grub_cpu_to_le32 (partition->len);
 | ||
|       grub_free (partition);
 | ||
|     }
 | ||
|   for (; i < 4; i++)
 | ||
|     grub_memset (&(mbr.entries[i]), 0, sizeof (mbr.entries[i]));
 | ||
| 
 | ||
|   /* The protective partition. */
 | ||
|   if (first_sector > 0xffffffff)
 | ||
|     first_sector = 0xffffffff;
 | ||
|   else
 | ||
|     first_sector--;
 | ||
|   mbr.entries[0].flag = 0;
 | ||
|   mbr.entries[0].type = GRUB_PC_PARTITION_TYPE_GPT_DISK;
 | ||
|   mbr.entries[0].start = grub_cpu_to_le32_compile_time (1);
 | ||
|   lba_to_chs (1,
 | ||
| 	      &(mbr.entries[0].start_sector),
 | ||
| 	      &(mbr.entries[0].start_cylinder),
 | ||
| 	      &(mbr.entries[0].start_head));
 | ||
|   lba_to_chs (first_sector,
 | ||
| 	      &(mbr.entries[0].end_sector),
 | ||
| 	      &(mbr.entries[0].end_cylinder),
 | ||
| 	      &(mbr.entries[0].end_head));
 | ||
|   mbr.entries[0].length = grub_cpu_to_le32 (first_sector);
 | ||
| 
 | ||
|   mbr.signature = grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE);
 | ||
| 
 | ||
|   if (grub_disk_write (dev->disk, 0, 0, sizeof (mbr), &mbr))
 | ||
|     {
 | ||
|       grub_device_close (dev);
 | ||
|       return grub_errno;
 | ||
|     }
 | ||
| 
 | ||
|   grub_device_close (dev);
 | ||
| 
 | ||
|   grub_printf_ (N_("New MBR is written to `%s'\n"), args[0]);
 | ||
| 
 | ||
|   return GRUB_ERR_NONE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static grub_command_t cmd;
 | ||
| 
 | ||
| GRUB_MOD_INIT(gptsync)
 | ||
| {
 | ||
|   (void) mod;			/* To stop warning. */
 | ||
|   cmd = grub_register_command ("gptsync", grub_cmd_gptsync,
 | ||
| 			       N_("DEVICE [PARTITION[+/-[TYPE]]] ..."),
 | ||
| 			       /* TRANSLATORS: MBR type is one-byte partition
 | ||
| 				  type id.  */
 | ||
| 			       N_("Fill hybrid MBR of GPT drive DEVICE. "
 | ||
| 			       "Specified partitions will be a part "
 | ||
| 			       "of hybrid MBR. Up to 3 partitions are "
 | ||
| 			       "allowed. TYPE is an MBR type. "
 | ||
| 			       "+ means that partition is active. "
 | ||
| 			       "Only one partition can be active."));
 | ||
| }
 | ||
| 
 | ||
| GRUB_MOD_FINI(gptsync)
 | ||
| {
 | ||
|   grub_unregister_command (cmd);
 | ||
| }
 | 
