migrate.c revision 1.24 1 /*-
2 * Copyright (c) 2002 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #if HAVE_NBTOOL_CONFIG_H
28 #include "nbtool_config.h"
29 #endif
30
31 #include <sys/cdefs.h>
32 #ifdef __FBSDID
33 __FBSDID("$FreeBSD: src/sbin/gpt/migrate.c,v 1.16 2005/09/01 02:42:52 marcel Exp $");
34 #endif
35 #ifdef __RCSID
36 __RCSID("$NetBSD: migrate.c,v 1.24 2015/12/01 16:32:19 christos Exp $");
37 #endif
38
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #ifdef HAVE_NBTOOL_CONFIG_H
42 #include <nbinclude/sys/bootblock.h>
43 #include <nbinclude/sys/disklabel.h>
44 #else
45 #include <sys/bootblock.h>
46 #include <sys/disklabel.h>
47 #endif
48
49 #include <err.h>
50 #include <stddef.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55
56 #include "map.h"
57 #include "gpt.h"
58 #include "gpt_private.h"
59
60 /*
61 * Allow compilation on platforms that do not have a BSD label.
62 * The values are valid for amd64, i386 and ia64 disklabels.
63 * XXX: use disklabel_params from disklabel.c
64 */
65 #ifndef LABELOFFSET
66 #define LABELOFFSET 0
67 #endif
68 #ifndef LABELSECTOR
69 #define LABELSECTOR 1
70 #endif
71 #ifndef RAW_PART
72 #define RAW_PART 3
73 #endif
74
75 /* FreeBSD filesystem types that don't match corresponding NetBSD types */
76 #define FREEBSD_FS_VINUM 14
77 #define FREEBSD_FS_ZFS 27
78
79 static int force;
80 static int slice;
81 static u_int parts;
82
83 static int cmd_migrate(gpt_t, int, char *[]);
84
85 static const char *migratehelp[] = {
86 "[-fs] [-p <partitions>]",
87 };
88
89 struct gpt_cmd c_migrate = {
90 "migrate",
91 cmd_migrate,
92 migratehelp, __arraycount(migratehelp),
93 0,
94 };
95
96 #define usage() gpt_usage(NULL, &c_migrate)
97
98 static struct gpt_ent *
99 migrate_disklabel(gpt_t gpt, off_t start, struct gpt_ent *ent)
100 {
101 char *buf;
102 struct disklabel *dl;
103 off_t ofs, rawofs;
104 int i;
105
106 buf = gpt_read(gpt, start + LABELSECTOR, 1);
107 if (buf == NULL) {
108 gpt_warn(gpt, "Error reading label");
109 return NULL;
110 }
111 dl = (void*)(buf + LABELOFFSET);
112
113 if (le32toh(dl->d_magic) != DISKMAGIC ||
114 le32toh(dl->d_magic2) != DISKMAGIC) {
115 gpt_warnx(gpt, "FreeBSD slice without disklabel");
116 free(buf);
117 return (ent);
118 }
119
120 rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) *
121 le32toh(dl->d_secsize);
122 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
123 if (dl->d_partitions[i].p_fstype == FS_UNUSED)
124 continue;
125 ofs = le32toh(dl->d_partitions[i].p_offset) *
126 le32toh(dl->d_secsize);
127 if (ofs < rawofs)
128 rawofs = 0;
129 }
130 rawofs /= gpt->secsz;
131
132 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
133 switch (dl->d_partitions[i].p_fstype) {
134 case FS_UNUSED:
135 continue;
136 case FS_SWAP: {
137 gpt_uuid_create(GPT_TYPE_FREEBSD_SWAP, ent->ent_type,
138 ent->ent_name, sizeof(ent->ent_name));
139 break;
140 }
141 case FS_BSDFFS: {
142 gpt_uuid_create(GPT_TYPE_FREEBSD_UFS, ent->ent_type,
143 ent->ent_name, sizeof(ent->ent_name));
144 break;
145 }
146 case FREEBSD_FS_VINUM: {
147 gpt_uuid_create(GPT_TYPE_FREEBSD_VINUM, ent->ent_type,
148 ent->ent_name, sizeof(ent->ent_name));
149 break;
150 }
151 case FREEBSD_FS_ZFS: {
152 gpt_uuid_create(GPT_TYPE_FREEBSD_ZFS, ent->ent_type,
153 ent->ent_name, sizeof(ent->ent_name));
154 break;
155 }
156 default:
157 gpt_warnx(gpt, "Unknown FreeBSD partition (%d)",
158 dl->d_partitions[i].p_fstype);
159 continue;
160 }
161
162 ofs = (le32toh(dl->d_partitions[i].p_offset) *
163 le32toh(dl->d_secsize)) / gpt->secsz;
164 ofs = (ofs > 0) ? ofs - rawofs : 0;
165 ent->ent_lba_start = htole64(start + ofs);
166 ent->ent_lba_end = htole64(start + ofs +
167 le32toh(dl->d_partitions[i].p_size) - 1LL);
168 ent++;
169 }
170
171 free(buf);
172 return ent;
173 }
174
175 static struct gpt_ent*
176 migrate_netbsd_disklabel(gpt_t gpt, off_t start, struct gpt_ent *ent)
177 {
178 char *buf;
179 struct disklabel *dl;
180 off_t ofs, rawofs;
181 int i;
182
183 buf = gpt_read(gpt, start + LABELSECTOR, 1);
184 if (buf == NULL) {
185 gpt_warn(gpt, "Error reading label");
186 return NULL;
187 }
188 dl = (void*)(buf + LABELOFFSET);
189
190 if (le32toh(dl->d_magic) != DISKMAGIC ||
191 le32toh(dl->d_magic2) != DISKMAGIC) {
192 gpt_warnx(gpt, "NetBSD slice without disklabel");
193 free(buf);
194 return ent;
195 }
196
197 rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) *
198 le32toh(dl->d_secsize);
199 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
200 if (dl->d_partitions[i].p_fstype == FS_UNUSED)
201 continue;
202 ofs = le32toh(dl->d_partitions[i].p_offset) *
203 le32toh(dl->d_secsize);
204 if (ofs < rawofs)
205 rawofs = 0;
206 }
207 rawofs /= gpt->secsz;
208
209 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
210 switch (dl->d_partitions[i].p_fstype) {
211 case FS_UNUSED:
212 continue;
213 case FS_SWAP: {
214 gpt_uuid_create(GPT_TYPE_NETBSD_SWAP, ent->ent_type,
215 ent->ent_name, sizeof(ent->ent_name));
216 break;
217 }
218 case FS_BSDFFS: {
219 gpt_uuid_create(GPT_TYPE_NETBSD_FFS, ent->ent_type,
220 ent->ent_name, sizeof(ent->ent_name));
221 break;
222 }
223 case FS_BSDLFS: {
224 gpt_uuid_create(GPT_TYPE_NETBSD_LFS, ent->ent_type,
225 ent->ent_name, sizeof(ent->ent_name));
226 break;
227 }
228 case FS_RAID: {
229 gpt_uuid_create(GPT_TYPE_NETBSD_RAIDFRAME, ent->ent_type,
230 ent->ent_name, sizeof(ent->ent_name));
231 break;
232 }
233 case FS_CCD: {
234 gpt_uuid_create(GPT_TYPE_NETBSD_CCD, ent->ent_type,
235 ent->ent_name, sizeof(ent->ent_name));
236 break;
237 }
238 case FS_CGD: {
239 gpt_uuid_create(GPT_TYPE_NETBSD_CGD, ent->ent_type,
240 ent->ent_name, sizeof(ent->ent_name));
241 break;
242 }
243 default:
244 gpt_warnx(gpt, "Unknown NetBSD partition (%d)",
245 dl->d_partitions[i].p_fstype);
246 continue;
247 }
248
249 ofs = (le32toh(dl->d_partitions[i].p_offset) *
250 le32toh(dl->d_secsize)) / gpt->secsz;
251 ofs = (ofs > 0) ? ofs - rawofs : 0;
252 ent->ent_lba_start = htole64(ofs);
253 ent->ent_lba_end = htole64(ofs +
254 le32toh(dl->d_partitions[i].p_size) - 1LL);
255 ent++;
256 }
257
258 free(buf);
259 return ent;
260 }
261
262 static int
263 migrate(gpt_t gpt)
264 {
265 off_t blocks, last;
266 map_t map;
267 struct gpt_hdr *hdr;
268 struct gpt_ent *ent;
269 struct mbr *mbr;
270 uint32_t start, size;
271 unsigned int i;
272
273 last = gpt->mediasz / gpt->secsz - 1LL;
274
275 map = map_find(gpt, MAP_TYPE_MBR);
276 if (map == NULL || map->map_start != 0) {
277 gpt_warnx(gpt, "No partitions to convert");
278 return -1;
279 }
280
281 mbr = map->map_data;
282
283 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
284 map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
285 gpt_warnx(gpt, "Device already contains a GPT");
286 return -1;
287 }
288
289 /* Get the amount of free space after the MBR */
290 blocks = map_free(gpt, 1LL, 0LL);
291 if (blocks == 0LL) {
292 gpt_warnx(gpt, "No room for the GPT header");
293 return -1;
294 }
295
296 /* Don't create more than parts entries. */
297 if ((uint64_t)(blocks - 1) * gpt->secsz >
298 parts * sizeof(struct gpt_ent)) {
299 blocks = (parts * sizeof(struct gpt_ent)) / gpt->secsz;
300 if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
301 blocks++;
302 blocks++; /* Don't forget the header itself */
303 }
304
305 /* Never cross the median of the device. */
306 if ((blocks + 1LL) > ((last + 1LL) >> 1))
307 blocks = ((last + 1LL) >> 1) - 1LL;
308
309 /*
310 * Get the amount of free space at the end of the device and
311 * calculate the size for the GPT structures.
312 */
313 map = map_last(gpt);
314 if (map->map_type != MAP_TYPE_UNUSED) {
315 gpt_warnx(gpt, "No room for the backup header");
316 return -1;
317 }
318
319 if (map->map_size < blocks)
320 blocks = map->map_size;
321 if (blocks == 1LL) {
322 gpt_warnx(gpt, "No room for the GPT table");
323 return -1;
324 }
325
326 blocks--; /* Number of blocks in the GPT table. */
327 gpt->gpt = map_add(gpt, 1LL, 1LL, MAP_TYPE_PRI_GPT_HDR,
328 calloc(1, gpt->secsz));
329 gpt->tbl = map_add(gpt, 2LL, blocks, MAP_TYPE_PRI_GPT_TBL,
330 calloc(blocks, gpt->secsz));
331 if (gpt->gpt == NULL || gpt->tbl == NULL)
332 return -1;
333
334 gpt->lbt = map_add(gpt, last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL,
335 gpt->tbl->map_data);
336 gpt->tpg = map_add(gpt, last, 1LL, MAP_TYPE_SEC_GPT_HDR,
337 calloc(1, gpt->secsz));
338
339 hdr = gpt->gpt->map_data;
340 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
341 hdr->hdr_revision = htole32(GPT_HDR_REVISION);
342
343 /*
344 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
345 * contains padding we must not include in the size.
346 */
347 hdr->hdr_size = htole32(GPT_HDR_SIZE);
348 hdr->hdr_lba_self = htole64(gpt->gpt->map_start);
349 hdr->hdr_lba_alt = htole64(gpt->tpg->map_start);
350 hdr->hdr_lba_start = htole64(gpt->tbl->map_start + blocks);
351 hdr->hdr_lba_end = htole64(gpt->lbt->map_start - 1LL);
352 gpt_uuid_generate(hdr->hdr_guid);
353 hdr->hdr_lba_table = htole64(gpt->tbl->map_start);
354 hdr->hdr_entries = htole32((blocks * gpt->secsz) / sizeof(struct gpt_ent));
355 if (le32toh(hdr->hdr_entries) > parts)
356 hdr->hdr_entries = htole32(parts);
357 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
358
359 ent = gpt->tbl->map_data;
360 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
361 gpt_uuid_generate(ent[i].ent_guid);
362 }
363
364 /* Mirror partitions. */
365 for (i = 0; i < 4; i++) {
366 start = le16toh(mbr->mbr_part[i].part_start_hi);
367 start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
368 size = le16toh(mbr->mbr_part[i].part_size_hi);
369 size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
370
371 switch (mbr->mbr_part[i].part_typ) {
372 case MBR_PTYPE_UNUSED:
373 continue;
374 case MBR_PTYPE_386BSD: { /* FreeBSD */
375 if (slice) {
376 gpt_uuid_create(GPT_TYPE_FREEBSD,
377 ent->ent_type, ent->ent_name,
378 sizeof(ent->ent_name));
379 ent->ent_lba_start = htole64((uint64_t)start);
380 ent->ent_lba_end = htole64(start + size - 1LL);
381 ent++;
382 } else
383 ent = migrate_disklabel(gpt, start, ent);
384 break;
385 }
386 case MBR_PTYPE_NETBSD:
387 ent = migrate_netbsd_disklabel(gpt, start, ent);
388 break;
389 case MBR_PTYPE_EFI: {
390 gpt_uuid_create(GPT_TYPE_EFI,
391 ent->ent_type, ent->ent_name,
392 sizeof(ent->ent_name));
393 ent->ent_lba_start = htole64((uint64_t)start);
394 ent->ent_lba_end = htole64(start + size - 1LL);
395 ent++;
396 break;
397 }
398 default:
399 if (!force) {
400 gpt_warnx(gpt, "unknown partition type (%d)",
401 mbr->mbr_part[i].part_typ);
402 return -1;
403 }
404 break;
405 }
406 }
407
408 if (gpt_write_primary(gpt) == -1)
409 return -1;
410
411 /*
412 * Create backup GPT.
413 */
414 memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
415 hdr = gpt->tpg->map_data;
416 hdr->hdr_lba_self = htole64(gpt->tpg->map_start);
417 hdr->hdr_lba_alt = htole64(gpt->gpt->map_start);
418 hdr->hdr_lba_table = htole64(gpt->lbt->map_start);
419
420 if (gpt_write_backup(gpt) == -1)
421 return -1;
422
423 map = map_find(gpt, MAP_TYPE_MBR);
424 mbr = map->map_data;
425 /*
426 * Turn the MBR into a Protective MBR.
427 */
428 memset(mbr->mbr_part, 0, sizeof(mbr->mbr_part));
429 gpt_create_pmbr_part(mbr->mbr_part, last);
430 gpt_write(gpt, map);
431 return 0;
432 }
433
434 static int
435 cmd_migrate(gpt_t gpt, int argc, char *argv[])
436 {
437 int ch;
438
439 parts = 128;
440
441 /* Get the migrate options */
442 while ((ch = getopt(argc, argv, "fp:s")) != -1) {
443 switch(ch) {
444 case 'f':
445 force = 1;
446 break;
447 case 'p':
448 parts = atoi(optarg);
449 break;
450 case 's':
451 slice = 1;
452 break;
453 default:
454 return usage();
455 }
456 }
457
458 if (argc != optind)
459 return usage();
460
461 return migrate(gpt);
462 }
463