]> git.proxmox.com Git - grub2.git/blob - commands/gptsync.c
merge mainline up to r2075 (videomask merge)
[grub2.git] / commands / gptsync.c
1 /* gptsync.c - fill the mbr based on gpt entries */
2 /* XXX: I don't know what to do if sector size isn't 512 bytes */
3 /*
4 * GRUB -- GRand Unified Bootloader
5 * Copyright (C) 2009 Free Software Foundation, Inc.
6 *
7 * GRUB is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * GRUB is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <grub/command.h>
22 #include <grub/dl.h>
23 #include <grub/device.h>
24 #include <grub/disk.h>
25 #include <grub/msdos_partition.h>
26 #include <grub/partition.h>
27 #include <grub/misc.h>
28 #include <grub/mm.h>
29 #include <grub/fs.h>
30 #include <grub/i18n.h>
31
32 /* Convert a LBA address to a CHS address in the INT 13 format. */
33 /* Taken from grub1. */
34 /* XXX: use hardcoded geometry of C = 1024, H = 255, S = 63.
35 Is it a problem?
36 */
37 static void
38 lba_to_chs (int lba, grub_uint8_t *cl, grub_uint8_t *ch,
39 grub_uint8_t *dh)
40 {
41 int cylinder, head, sector;
42 int sectors = 63, heads = 255, cylinders = 1024;
43
44 sector = lba % sectors + 1;
45 head = (lba / sectors) % heads;
46 cylinder = lba / (sectors * heads);
47
48 if (cylinder >= cylinders)
49 {
50 *cl = *ch = *dh = 0xff;
51 return;
52 }
53
54 *cl = sector | ((cylinder & 0x300) >> 2);
55 *ch = cylinder & 0xFF;
56 *dh = head;
57 }
58
59 static grub_err_t
60 grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)),
61 int argc, char **args)
62 {
63 grub_device_t dev;
64 struct grub_msdos_partition_mbr mbr;
65 struct grub_partition *partition;
66 grub_disk_addr_t first_sector;
67 int numactive = 0;
68
69 if (argc < 1)
70 return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
71 if (argc > 4)
72 return grub_error (GRUB_ERR_BAD_ARGUMENT, "only 3 partitions can be "
73 "in hybrid MBR");
74
75 if (args[0][0] == '(' && args[0][grub_strlen (args[0]) - 1] == ')')
76 {
77 args[0][grub_strlen (args[0]) - 1] = 0;
78 dev = grub_device_open (args[0] + 1);
79 args[0][grub_strlen (args[0])] = ')';
80 }
81 else
82 dev = grub_device_open (args[0]);
83
84 if (! dev)
85 return grub_errno;
86
87 if (! dev->disk)
88 {
89 grub_device_close (dev);
90 return grub_error (GRUB_ERR_BAD_ARGUMENT, "not a disk");
91 }
92
93 /* Read the protective MBR. */
94 if (grub_disk_read (dev->disk, 0, 0, sizeof (mbr), &mbr))
95 {
96 grub_device_close (dev);
97 return grub_errno;
98 }
99
100 /* Check if it is valid. */
101 if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE))
102 {
103 grub_device_close (dev);
104 return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");
105 }
106
107 /* Make sure the MBR is a protective MBR and not a normal MBR. */
108 if (mbr.entries[0].type != GRUB_PC_PARTITION_TYPE_GPT_DISK)
109 {
110 grub_device_close (dev);
111 return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found");
112 }
113
114 int i;
115 first_sector = dev->disk->total_sectors;
116 for (i = 1; i < argc; i++)
117 {
118 char *separator, csep = 0;
119 grub_uint8_t type;
120 separator = grub_strchr (args[i], '+');
121 if (! separator)
122 separator = grub_strchr (args[i], '-');
123 if (separator)
124 {
125 csep = *separator;
126 *separator = 0;
127 }
128 partition = grub_partition_probe (dev->disk, args[i]);
129 if (separator)
130 *separator = csep;
131 if (! partition)
132 {
133 grub_device_close (dev);
134 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such partition");
135 }
136
137 if (partition->start + partition->len > 0xffffffff)
138 {
139 grub_device_close (dev);
140 return grub_error (GRUB_ERR_OUT_OF_RANGE,
141 "only partitions resding in the first 2TB "
142 "can be presen in hybrid MBR");
143 }
144
145
146 if (first_sector > partition->start)
147 first_sector = partition->start;
148
149 if (separator && *(separator + 1))
150 type = grub_strtoul (separator + 1, 0, 0);
151 else
152 {
153 grub_fs_t fs = 0;
154 dev->disk->partition = partition;
155 fs = grub_fs_probe (dev);
156
157 /* Unknown filesystem isn't fatal. */
158 if (grub_errno == GRUB_ERR_UNKNOWN_FS)
159 {
160 fs = 0;
161 grub_errno = GRUB_ERR_NONE;
162 }
163
164 if (fs && grub_strcmp (fs->name, "ntfs") == 0)
165 type = GRUB_PC_PARTITION_TYPE_NTFS;
166 else if (fs && grub_strcmp (fs->name, "fat") == 0)
167 /* FIXME: detect FAT16. */
168 type = GRUB_PC_PARTITION_TYPE_FAT32_LBA;
169 else if (fs && (grub_strcmp (fs->name, "hfsplus") == 0
170 || grub_strcmp (fs->name, "hfs") == 0))
171 type = GRUB_PC_PARTITION_TYPE_HFS;
172 else
173 /* FIXME: detect more types. */
174 type = GRUB_PC_PARTITION_TYPE_EXT2FS;
175
176 dev->disk->partition = 0;
177 }
178
179 mbr.entries[i].flag = (csep == '+') ? 0x80 : 0;
180 if (csep == '+')
181 {
182 numactive++;
183 if (numactive == 2)
184 {
185 grub_device_close (dev);
186 return grub_error (GRUB_ERR_BAD_ARGUMENT,
187 "only one partition can be active");
188 }
189 }
190 mbr.entries[i].type = type;
191 mbr.entries[i].start = grub_cpu_to_le32 (partition->start);
192 lba_to_chs (partition->start,
193 &(mbr.entries[i].start_sector),
194 &(mbr.entries[i].start_cylinder),
195 &(mbr.entries[i].start_head));
196 lba_to_chs (partition->start + partition->len - 1,
197 &(mbr.entries[i].end_sector),
198 &(mbr.entries[i].end_cylinder),
199 &(mbr.entries[i].end_head));
200 mbr.entries[i].length = grub_cpu_to_le32 (partition->len);
201 grub_free (partition);
202 }
203 for (; i < 4; i++)
204 grub_memset (&(mbr.entries[i]), 0, sizeof (mbr.entries[i]));
205
206 /* The protective partition. */
207 if (first_sector > 0xffffffff)
208 first_sector = 0xffffffff;
209 else
210 first_sector--;
211 mbr.entries[0].flag = 0;
212 mbr.entries[0].type = GRUB_PC_PARTITION_TYPE_GPT_DISK;
213 mbr.entries[0].start = grub_cpu_to_le32 (1);
214 lba_to_chs (1,
215 &(mbr.entries[0].start_sector),
216 &(mbr.entries[0].start_cylinder),
217 &(mbr.entries[0].start_head));
218 lba_to_chs (first_sector,
219 &(mbr.entries[0].end_sector),
220 &(mbr.entries[0].end_cylinder),
221 &(mbr.entries[0].end_head));
222 mbr.entries[0].length = grub_cpu_to_le32 (first_sector);
223
224 mbr.signature = grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE);
225
226 if (grub_disk_write (dev->disk, 0, 0, sizeof (mbr), &mbr))
227 {
228 grub_device_close (dev);
229 return grub_errno;
230 }
231
232 grub_printf ("New MBR is written to '%s'\n", args[0]);
233
234 return GRUB_ERR_NONE;
235 }
236
237
238 static grub_command_t cmd;
239 \f
240 GRUB_MOD_INIT(gptsync)
241 {
242 (void) mod; /* To stop warning. */
243 cmd = grub_register_command ("gptsync", grub_cmd_gptsync,
244 N_("DEVICE [PARTITION[+/-[TYPE]]] ..."),
245 N_("Fill hybrid MBR of GPT drive DEVICE. "
246 "specified partitions will be a part "
247 "of hybrid mbr. Up to 3 partitions are "
248 "allowed. TYPE is an MBR type. "
249 "+ means that partition is active. "
250 "Only one partition can be active."));
251 }
252
253 GRUB_MOD_FINI(gptsync)
254 {
255 grub_unregister_command (cmd);
256 }