migrate.c revision 1.23 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.23 2015/12/01 09:05:33 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 const char migratemsg[] = "migrate [-fs] [-p <partitions>]";
84
85 static int
86 usage_migrate(void)
87 {
88
89 fprintf(stderr,
90 "usage: %s %s\n", getprogname(), migratemsg);
91 return -1;
92 }
93
94 static struct gpt_ent *
95 migrate_disklabel(gpt_t gpt, off_t start, struct gpt_ent *ent)
96 {
97 char *buf;
98 struct disklabel *dl;
99 off_t ofs, rawofs;
100 int i;
101
102 buf = gpt_read(gpt, start + LABELSECTOR, 1);
103 if (buf == NULL) {
104 gpt_warn(gpt, "Error reading label");
105 return NULL;
106 }
107 dl = (void*)(buf + LABELOFFSET);
108
109 if (le32toh(dl->d_magic) != DISKMAGIC ||
110 le32toh(dl->d_magic2) != DISKMAGIC) {
111 gpt_warnx(gpt, "FreeBSD slice without disklabel");
112 free(buf);
113 return (ent);
114 }
115
116 rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) *
117 le32toh(dl->d_secsize);
118 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
119 if (dl->d_partitions[i].p_fstype == FS_UNUSED)
120 continue;
121 ofs = le32toh(dl->d_partitions[i].p_offset) *
122 le32toh(dl->d_secsize);
123 if (ofs < rawofs)
124 rawofs = 0;
125 }
126 rawofs /= gpt->secsz;
127
128 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
129 switch (dl->d_partitions[i].p_fstype) {
130 case FS_UNUSED:
131 continue;
132 case FS_SWAP: {
133 gpt_uuid_create(GPT_TYPE_FREEBSD_SWAP, ent->ent_type,
134 ent->ent_name, sizeof(ent->ent_name));
135 break;
136 }
137 case FS_BSDFFS: {
138 gpt_uuid_create(GPT_TYPE_FREEBSD_UFS, ent->ent_type,
139 ent->ent_name, sizeof(ent->ent_name));
140 break;
141 }
142 case FREEBSD_FS_VINUM: {
143 gpt_uuid_create(GPT_TYPE_FREEBSD_VINUM, ent->ent_type,
144 ent->ent_name, sizeof(ent->ent_name));
145 break;
146 }
147 case FREEBSD_FS_ZFS: {
148 gpt_uuid_create(GPT_TYPE_FREEBSD_ZFS, ent->ent_type,
149 ent->ent_name, sizeof(ent->ent_name));
150 break;
151 }
152 default:
153 gpt_warnx(gpt, "Unknown FreeBSD partition (%d)",
154 dl->d_partitions[i].p_fstype);
155 continue;
156 }
157
158 ofs = (le32toh(dl->d_partitions[i].p_offset) *
159 le32toh(dl->d_secsize)) / gpt->secsz;
160 ofs = (ofs > 0) ? ofs - rawofs : 0;
161 ent->ent_lba_start = htole64(start + ofs);
162 ent->ent_lba_end = htole64(start + ofs +
163 le32toh(dl->d_partitions[i].p_size) - 1LL);
164 ent++;
165 }
166
167 free(buf);
168 return ent;
169 }
170
171 static struct gpt_ent*
172 migrate_netbsd_disklabel(gpt_t gpt, off_t start, struct gpt_ent *ent)
173 {
174 char *buf;
175 struct disklabel *dl;
176 off_t ofs, rawofs;
177 int i;
178
179 buf = gpt_read(gpt, start + LABELSECTOR, 1);
180 if (buf == NULL) {
181 gpt_warn(gpt, "Error reading label");
182 return NULL;
183 }
184 dl = (void*)(buf + LABELOFFSET);
185
186 if (le32toh(dl->d_magic) != DISKMAGIC ||
187 le32toh(dl->d_magic2) != DISKMAGIC) {
188 gpt_warnx(gpt, "NetBSD slice without disklabel");
189 free(buf);
190 return ent;
191 }
192
193 rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) *
194 le32toh(dl->d_secsize);
195 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
196 if (dl->d_partitions[i].p_fstype == FS_UNUSED)
197 continue;
198 ofs = le32toh(dl->d_partitions[i].p_offset) *
199 le32toh(dl->d_secsize);
200 if (ofs < rawofs)
201 rawofs = 0;
202 }
203 rawofs /= gpt->secsz;
204
205 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
206 switch (dl->d_partitions[i].p_fstype) {
207 case FS_UNUSED:
208 continue;
209 case FS_SWAP: {
210 gpt_uuid_create(GPT_TYPE_NETBSD_SWAP, ent->ent_type,
211 ent->ent_name, sizeof(ent->ent_name));
212 break;
213 }
214 case FS_BSDFFS: {
215 gpt_uuid_create(GPT_TYPE_NETBSD_FFS, ent->ent_type,
216 ent->ent_name, sizeof(ent->ent_name));
217 break;
218 }
219 case FS_BSDLFS: {
220 gpt_uuid_create(GPT_TYPE_NETBSD_LFS, ent->ent_type,
221 ent->ent_name, sizeof(ent->ent_name));
222 break;
223 }
224 case FS_RAID: {
225 gpt_uuid_create(GPT_TYPE_NETBSD_RAIDFRAME, ent->ent_type,
226 ent->ent_name, sizeof(ent->ent_name));
227 break;
228 }
229 case FS_CCD: {
230 gpt_uuid_create(GPT_TYPE_NETBSD_CCD, ent->ent_type,
231 ent->ent_name, sizeof(ent->ent_name));
232 break;
233 }
234 case FS_CGD: {
235 gpt_uuid_create(GPT_TYPE_NETBSD_CGD, ent->ent_type,
236 ent->ent_name, sizeof(ent->ent_name));
237 break;
238 }
239 default:
240 gpt_warnx(gpt, "Unknown NetBSD partition (%d)",
241 dl->d_partitions[i].p_fstype);
242 continue;
243 }
244
245 ofs = (le32toh(dl->d_partitions[i].p_offset) *
246 le32toh(dl->d_secsize)) / gpt->secsz;
247 ofs = (ofs > 0) ? ofs - rawofs : 0;
248 ent->ent_lba_start = htole64(ofs);
249 ent->ent_lba_end = htole64(ofs +
250 le32toh(dl->d_partitions[i].p_size) - 1LL);
251 ent++;
252 }
253
254 free(buf);
255 return ent;
256 }
257
258 static int
259 migrate(gpt_t gpt)
260 {
261 off_t blocks, last;
262 map_t map;
263 struct gpt_hdr *hdr;
264 struct gpt_ent *ent;
265 struct mbr *mbr;
266 uint32_t start, size;
267 unsigned int i;
268
269 last = gpt->mediasz / gpt->secsz - 1LL;
270
271 map = map_find(gpt, MAP_TYPE_MBR);
272 if (map == NULL || map->map_start != 0) {
273 gpt_warnx(gpt, "No partitions to convert");
274 return -1;
275 }
276
277 mbr = map->map_data;
278
279 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
280 map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
281 gpt_warnx(gpt, "Device already contains a GPT");
282 return -1;
283 }
284
285 /* Get the amount of free space after the MBR */
286 blocks = map_free(gpt, 1LL, 0LL);
287 if (blocks == 0LL) {
288 gpt_warnx(gpt, "No room for the GPT header");
289 return -1;
290 }
291
292 /* Don't create more than parts entries. */
293 if ((uint64_t)(blocks - 1) * gpt->secsz >
294 parts * sizeof(struct gpt_ent)) {
295 blocks = (parts * sizeof(struct gpt_ent)) / gpt->secsz;
296 if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
297 blocks++;
298 blocks++; /* Don't forget the header itself */
299 }
300
301 /* Never cross the median of the device. */
302 if ((blocks + 1LL) > ((last + 1LL) >> 1))
303 blocks = ((last + 1LL) >> 1) - 1LL;
304
305 /*
306 * Get the amount of free space at the end of the device and
307 * calculate the size for the GPT structures.
308 */
309 map = map_last(gpt);
310 if (map->map_type != MAP_TYPE_UNUSED) {
311 gpt_warnx(gpt, "No room for the backup header");
312 return -1;
313 }
314
315 if (map->map_size < blocks)
316 blocks = map->map_size;
317 if (blocks == 1LL) {
318 gpt_warnx(gpt, "No room for the GPT table");
319 return -1;
320 }
321
322 blocks--; /* Number of blocks in the GPT table. */
323 gpt->gpt = map_add(gpt, 1LL, 1LL, MAP_TYPE_PRI_GPT_HDR,
324 calloc(1, gpt->secsz));
325 gpt->tbl = map_add(gpt, 2LL, blocks, MAP_TYPE_PRI_GPT_TBL,
326 calloc(blocks, gpt->secsz));
327 if (gpt->gpt == NULL || gpt->tbl == NULL)
328 return -1;
329
330 gpt->lbt = map_add(gpt, last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL,
331 gpt->tbl->map_data);
332 gpt->tpg = map_add(gpt, last, 1LL, MAP_TYPE_SEC_GPT_HDR,
333 calloc(1, gpt->secsz));
334
335 hdr = gpt->gpt->map_data;
336 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
337 hdr->hdr_revision = htole32(GPT_HDR_REVISION);
338
339 /*
340 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
341 * contains padding we must not include in the size.
342 */
343 hdr->hdr_size = htole32(GPT_HDR_SIZE);
344 hdr->hdr_lba_self = htole64(gpt->gpt->map_start);
345 hdr->hdr_lba_alt = htole64(gpt->tpg->map_start);
346 hdr->hdr_lba_start = htole64(gpt->tbl->map_start + blocks);
347 hdr->hdr_lba_end = htole64(gpt->lbt->map_start - 1LL);
348 gpt_uuid_generate(hdr->hdr_guid);
349 hdr->hdr_lba_table = htole64(gpt->tbl->map_start);
350 hdr->hdr_entries = htole32((blocks * gpt->secsz) / sizeof(struct gpt_ent));
351 if (le32toh(hdr->hdr_entries) > parts)
352 hdr->hdr_entries = htole32(parts);
353 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
354
355 ent = gpt->tbl->map_data;
356 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
357 gpt_uuid_generate(ent[i].ent_guid);
358 }
359
360 /* Mirror partitions. */
361 for (i = 0; i < 4; i++) {
362 start = le16toh(mbr->mbr_part[i].part_start_hi);
363 start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
364 size = le16toh(mbr->mbr_part[i].part_size_hi);
365 size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
366
367 switch (mbr->mbr_part[i].part_typ) {
368 case MBR_PTYPE_UNUSED:
369 continue;
370 case MBR_PTYPE_386BSD: { /* FreeBSD */
371 if (slice) {
372 gpt_uuid_create(GPT_TYPE_FREEBSD,
373 ent->ent_type, ent->ent_name,
374 sizeof(ent->ent_name));
375 ent->ent_lba_start = htole64((uint64_t)start);
376 ent->ent_lba_end = htole64(start + size - 1LL);
377 ent++;
378 } else
379 ent = migrate_disklabel(gpt, start, ent);
380 break;
381 }
382 case MBR_PTYPE_NETBSD:
383 ent = migrate_netbsd_disklabel(gpt, start, ent);
384 break;
385 case MBR_PTYPE_EFI: {
386 gpt_uuid_create(GPT_TYPE_EFI,
387 ent->ent_type, ent->ent_name,
388 sizeof(ent->ent_name));
389 ent->ent_lba_start = htole64((uint64_t)start);
390 ent->ent_lba_end = htole64(start + size - 1LL);
391 ent++;
392 break;
393 }
394 default:
395 if (!force) {
396 gpt_warnx(gpt, "unknown partition type (%d)",
397 mbr->mbr_part[i].part_typ);
398 return -1;
399 }
400 break;
401 }
402 }
403
404 if (gpt_write_primary(gpt) == -1)
405 return -1;
406
407 /*
408 * Create backup GPT.
409 */
410 memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
411 hdr = gpt->tpg->map_data;
412 hdr->hdr_lba_self = htole64(gpt->tpg->map_start);
413 hdr->hdr_lba_alt = htole64(gpt->gpt->map_start);
414 hdr->hdr_lba_table = htole64(gpt->lbt->map_start);
415
416 if (gpt_write_backup(gpt) == -1)
417 return -1;
418
419 map = map_find(gpt, MAP_TYPE_MBR);
420 mbr = map->map_data;
421 /*
422 * Turn the MBR into a Protective MBR.
423 */
424 memset(mbr->mbr_part, 0, sizeof(mbr->mbr_part));
425 gpt_create_pmbr_part(mbr->mbr_part, last);
426 gpt_write(gpt, map);
427 return 0;
428 }
429
430 int
431 cmd_migrate(gpt_t gpt, int argc, char *argv[])
432 {
433 int ch;
434
435 parts = 128;
436
437 /* Get the migrate options */
438 while ((ch = getopt(argc, argv, "fp:s")) != -1) {
439 switch(ch) {
440 case 'f':
441 force = 1;
442 break;
443 case 'p':
444 parts = atoi(optarg);
445 break;
446 case 's':
447 slice = 1;
448 break;
449 default:
450 return usage_migrate();
451 }
452 }
453
454 if (argc != optind)
455 return usage_migrate();
456
457 return migrate(gpt);
458 }
459