migrate.c revision 1.25 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.25 2015/12/01 19:25:24 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 last = gpt_last(gpt);
266 map_t map;
267 struct gpt_ent *ent;
268 struct mbr *mbr;
269 uint32_t start, size;
270 unsigned int i;
271
272 map = map_find(gpt, MAP_TYPE_MBR);
273 if (map == NULL || map->map_start != 0) {
274 gpt_warnx(gpt, "No partitions to convert");
275 return -1;
276 }
277
278 mbr = map->map_data;
279
280 if (gpt_create(gpt, last, parts, 0) == -1)
281 return -1;
282
283 ent = gpt->tbl->map_data;
284
285 /* Mirror partitions. */
286 for (i = 0; i < 4; i++) {
287 start = le16toh(mbr->mbr_part[i].part_start_hi);
288 start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
289 size = le16toh(mbr->mbr_part[i].part_size_hi);
290 size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
291
292 switch (mbr->mbr_part[i].part_typ) {
293 case MBR_PTYPE_UNUSED:
294 continue;
295 case MBR_PTYPE_386BSD: { /* FreeBSD */
296 if (slice) {
297 gpt_uuid_create(GPT_TYPE_FREEBSD,
298 ent->ent_type, ent->ent_name,
299 sizeof(ent->ent_name));
300 ent->ent_lba_start = htole64((uint64_t)start);
301 ent->ent_lba_end = htole64(start + size - 1LL);
302 ent++;
303 } else
304 ent = migrate_disklabel(gpt, start, ent);
305 break;
306 }
307 case MBR_PTYPE_NETBSD:
308 ent = migrate_netbsd_disklabel(gpt, start, ent);
309 break;
310 case MBR_PTYPE_EFI: {
311 gpt_uuid_create(GPT_TYPE_EFI,
312 ent->ent_type, ent->ent_name,
313 sizeof(ent->ent_name));
314 ent->ent_lba_start = htole64((uint64_t)start);
315 ent->ent_lba_end = htole64(start + size - 1LL);
316 ent++;
317 break;
318 }
319 default:
320 if (!force) {
321 gpt_warnx(gpt, "unknown partition type (%d)",
322 mbr->mbr_part[i].part_typ);
323 return -1;
324 }
325 break;
326 }
327 }
328
329 if (gpt_write_primary(gpt) == -1)
330 return -1;
331
332 if (gpt_write_backup(gpt) == -1)
333 return -1;
334
335 /*
336 * Turn the MBR into a Protective MBR.
337 */
338 memset(mbr->mbr_part, 0, sizeof(mbr->mbr_part));
339 gpt_create_pmbr_part(mbr->mbr_part, last);
340 if (gpt_write(gpt, map) == -1) {
341 gpt_warn(gpt, "Cant write PMBR");
342 return -1;
343 }
344 return 0;
345 }
346
347 static int
348 cmd_migrate(gpt_t gpt, int argc, char *argv[])
349 {
350 int ch;
351
352 parts = 128;
353
354 /* Get the migrate options */
355 while ((ch = getopt(argc, argv, "fp:s")) != -1) {
356 switch(ch) {
357 case 'f':
358 force = 1;
359 break;
360 case 'p':
361 parts = atoi(optarg);
362 break;
363 case 's':
364 slice = 1;
365 break;
366 default:
367 return usage();
368 }
369 }
370
371 if (argc != optind)
372 return usage();
373
374 return migrate(gpt);
375 }
376