migrate.c revision 1.27 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.27 2015/12/03 02:02:43 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 cmd_migrate(gpt_t, int, char *[]);
80
81 static const char *migratehelp[] = {
82 "[-fs] [-p partitions]",
83 };
84
85 struct gpt_cmd c_migrate = {
86 "migrate",
87 cmd_migrate,
88 migratehelp, __arraycount(migratehelp),
89 0,
90 };
91
92 #define usage() gpt_usage(NULL, &c_migrate)
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((uint64_t)(start + ofs));
162 ent->ent_lba_end = htole64((uint64_t)(start + ofs +
163 (off_t)le32toh((uint64_t)dl->d_partitions[i].p_size)
164 - 1LL));
165 ent++;
166 }
167
168 free(buf);
169 return ent;
170 }
171
172 static struct gpt_ent*
173 migrate_netbsd_disklabel(gpt_t gpt, off_t start, struct gpt_ent *ent)
174 {
175 char *buf;
176 struct disklabel *dl;
177 off_t ofs, rawofs;
178 int i;
179
180 buf = gpt_read(gpt, start + LABELSECTOR, 1);
181 if (buf == NULL) {
182 gpt_warn(gpt, "Error reading label");
183 return NULL;
184 }
185 dl = (void*)(buf + LABELOFFSET);
186
187 if (le32toh(dl->d_magic) != DISKMAGIC ||
188 le32toh(dl->d_magic2) != DISKMAGIC) {
189 gpt_warnx(gpt, "NetBSD slice without disklabel");
190 free(buf);
191 return ent;
192 }
193
194 rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) *
195 le32toh(dl->d_secsize);
196 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
197 if (dl->d_partitions[i].p_fstype == FS_UNUSED)
198 continue;
199 ofs = le32toh(dl->d_partitions[i].p_offset) *
200 le32toh(dl->d_secsize);
201 if (ofs < rawofs)
202 rawofs = 0;
203 }
204 rawofs /= gpt->secsz;
205
206 for (i = 0; i < le16toh(dl->d_npartitions); i++) {
207 switch (dl->d_partitions[i].p_fstype) {
208 case FS_UNUSED:
209 continue;
210 case FS_SWAP: {
211 gpt_uuid_create(GPT_TYPE_NETBSD_SWAP, ent->ent_type,
212 ent->ent_name, sizeof(ent->ent_name));
213 break;
214 }
215 case FS_BSDFFS: {
216 gpt_uuid_create(GPT_TYPE_NETBSD_FFS, ent->ent_type,
217 ent->ent_name, sizeof(ent->ent_name));
218 break;
219 }
220 case FS_BSDLFS: {
221 gpt_uuid_create(GPT_TYPE_NETBSD_LFS, ent->ent_type,
222 ent->ent_name, sizeof(ent->ent_name));
223 break;
224 }
225 case FS_RAID: {
226 gpt_uuid_create(GPT_TYPE_NETBSD_RAIDFRAME, ent->ent_type,
227 ent->ent_name, sizeof(ent->ent_name));
228 break;
229 }
230 case FS_CCD: {
231 gpt_uuid_create(GPT_TYPE_NETBSD_CCD, ent->ent_type,
232 ent->ent_name, sizeof(ent->ent_name));
233 break;
234 }
235 case FS_CGD: {
236 gpt_uuid_create(GPT_TYPE_NETBSD_CGD, ent->ent_type,
237 ent->ent_name, sizeof(ent->ent_name));
238 break;
239 }
240 default:
241 gpt_warnx(gpt, "Unknown NetBSD partition (%d)",
242 dl->d_partitions[i].p_fstype);
243 continue;
244 }
245
246 ofs = (le32toh(dl->d_partitions[i].p_offset) *
247 le32toh(dl->d_secsize)) / gpt->secsz;
248 ofs = (ofs > 0) ? ofs - rawofs : 0;
249 ent->ent_lba_start = htole64((uint64_t)ofs);
250 ent->ent_lba_end = htole64((uint64_t)(ofs +
251 (off_t)le32toh((uint64_t)dl->d_partitions[i].p_size)
252 - 1LL));
253 ent++;
254 }
255
256 free(buf);
257 return ent;
258 }
259
260 static int
261 migrate(gpt_t gpt, u_int parts, int force, int slice)
262 {
263 off_t last = gpt_last(gpt);
264 map_t map;
265 struct gpt_ent *ent;
266 struct mbr *mbr;
267 uint32_t start, size;
268 unsigned int i;
269
270 map = map_find(gpt, MAP_TYPE_MBR);
271 if (map == NULL || map->map_start != 0) {
272 gpt_warnx(gpt, "No partitions to convert");
273 return -1;
274 }
275
276 mbr = map->map_data;
277
278 if (gpt_create(gpt, last, parts, 0) == -1)
279 return -1;
280
281 ent = gpt->tbl->map_data;
282
283 /* Mirror partitions. */
284 for (i = 0; i < 4; i++) {
285 start = le16toh(mbr->mbr_part[i].part_start_hi);
286 start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
287 size = le16toh(mbr->mbr_part[i].part_size_hi);
288 size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
289
290 switch (mbr->mbr_part[i].part_typ) {
291 case MBR_PTYPE_UNUSED:
292 continue;
293 case MBR_PTYPE_386BSD: { /* FreeBSD */
294 if (slice) {
295 gpt_uuid_create(GPT_TYPE_FREEBSD,
296 ent->ent_type, ent->ent_name,
297 sizeof(ent->ent_name));
298 ent->ent_lba_start = htole64((uint64_t)start);
299 ent->ent_lba_end = htole64(
300 (uint64_t)(start + size - 1LL));
301 ent++;
302 } else
303 ent = migrate_disklabel(gpt, start, ent);
304 break;
305 }
306 case MBR_PTYPE_NETBSD:
307 ent = migrate_netbsd_disklabel(gpt, start, ent);
308 break;
309 case MBR_PTYPE_EFI: {
310 gpt_uuid_create(GPT_TYPE_EFI,
311 ent->ent_type, ent->ent_name,
312 sizeof(ent->ent_name));
313 ent->ent_lba_start = htole64((uint64_t)start);
314 ent->ent_lba_end = htole64(
315 (uint64_t)(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 int force = 0;
352 int slice = 0;
353 u_int parts = 128;
354
355 /* Get the migrate options */
356 while ((ch = getopt(argc, argv, "fp:s")) != -1) {
357 switch(ch) {
358 case 'f':
359 force = 1;
360 break;
361 case 'p':
362 if (gpt_uint_get(&parts) == -1)
363 return usage();
364 break;
365 case 's':
366 slice = 1;
367 break;
368 default:
369 return usage();
370 }
371 }
372
373 if (argc != optind)
374 return usage();
375
376 return migrate(gpt, parts, force, slice);
377 }
378