migrate.c revision 1.31 1 1.1 christos /*-
2 1.1 christos * Copyright (c) 2002 Marcel Moolenaar
3 1.1 christos * All rights reserved.
4 1.1 christos *
5 1.1 christos * Redistribution and use in source and binary forms, with or without
6 1.1 christos * modification, are permitted provided that the following conditions
7 1.1 christos * are met:
8 1.1 christos *
9 1.1 christos * 1. Redistributions of source code must retain the above copyright
10 1.1 christos * notice, this list of conditions and the following disclaimer.
11 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright
12 1.1 christos * notice, this list of conditions and the following disclaimer in the
13 1.1 christos * documentation and/or other materials provided with the distribution.
14 1.1 christos *
15 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 1.1 christos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 1.1 christos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 1.1 christos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 1.1 christos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 1.1 christos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 1.1 christos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 1.1 christos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 1.1 christos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 1.1 christos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 1.1 christos */
26 1.1 christos
27 1.15 christos #if HAVE_NBTOOL_CONFIG_H
28 1.15 christos #include "nbtool_config.h"
29 1.15 christos #endif
30 1.15 christos
31 1.1 christos #include <sys/cdefs.h>
32 1.2 christos #ifdef __FBSDID
33 1.1 christos __FBSDID("$FreeBSD: src/sbin/gpt/migrate.c,v 1.16 2005/09/01 02:42:52 marcel Exp $");
34 1.2 christos #endif
35 1.2 christos #ifdef __RCSID
36 1.31 christos __RCSID("$NetBSD: migrate.c,v 1.31 2016/06/09 15:12:54 christos Exp $");
37 1.2 christos #endif
38 1.1 christos
39 1.1 christos #include <sys/types.h>
40 1.3 he #include <sys/param.h>
41 1.28 christos #define FSTYPENAMES
42 1.28 christos #define MBRPTYPENAMES
43 1.17 christos #ifdef HAVE_NBTOOL_CONFIG_H
44 1.17 christos #include <nbinclude/sys/bootblock.h>
45 1.17 christos #include <nbinclude/sys/disklabel.h>
46 1.17 christos #else
47 1.9 jnemeth #include <sys/bootblock.h>
48 1.1 christos #include <sys/disklabel.h>
49 1.17 christos #endif
50 1.1 christos
51 1.1 christos #include <err.h>
52 1.1 christos #include <stddef.h>
53 1.1 christos #include <stdio.h>
54 1.1 christos #include <stdlib.h>
55 1.1 christos #include <string.h>
56 1.1 christos #include <unistd.h>
57 1.1 christos
58 1.1 christos #include "map.h"
59 1.1 christos #include "gpt.h"
60 1.23 christos #include "gpt_private.h"
61 1.1 christos
62 1.1 christos /*
63 1.1 christos * Allow compilation on platforms that do not have a BSD label.
64 1.1 christos * The values are valid for amd64, i386 and ia64 disklabels.
65 1.15 christos * XXX: use disklabel_params from disklabel.c
66 1.1 christos */
67 1.1 christos #ifndef LABELOFFSET
68 1.1 christos #define LABELOFFSET 0
69 1.1 christos #endif
70 1.1 christos #ifndef LABELSECTOR
71 1.1 christos #define LABELSECTOR 1
72 1.1 christos #endif
73 1.15 christos #ifndef RAW_PART
74 1.15 christos #define RAW_PART 3
75 1.15 christos #endif
76 1.1 christos
77 1.10 jnemeth /* FreeBSD filesystem types that don't match corresponding NetBSD types */
78 1.10 jnemeth #define FREEBSD_FS_VINUM 14
79 1.10 jnemeth #define FREEBSD_FS_ZFS 27
80 1.10 jnemeth
81 1.24 christos static int cmd_migrate(gpt_t, int, char *[]);
82 1.4 riz
83 1.24 christos static const char *migratehelp[] = {
84 1.31 christos "[-afs] [-p partitions]",
85 1.24 christos };
86 1.24 christos
87 1.24 christos struct gpt_cmd c_migrate = {
88 1.24 christos "migrate",
89 1.24 christos cmd_migrate,
90 1.24 christos migratehelp, __arraycount(migratehelp),
91 1.24 christos 0,
92 1.24 christos };
93 1.1 christos
94 1.24 christos #define usage() gpt_usage(NULL, &c_migrate)
95 1.1 christos
96 1.28 christos static const char *
97 1.28 christos fstypename(u_int t)
98 1.28 christos {
99 1.28 christos static char buf[64];
100 1.28 christos if (t >= __arraycount(fstypenames)) {
101 1.28 christos snprintf(buf, sizeof(buf), "*%u*", t);
102 1.28 christos return buf;
103 1.28 christos }
104 1.28 christos return fstypenames[t];
105 1.28 christos }
106 1.28 christos
107 1.28 christos static const char *
108 1.28 christos mbrptypename(u_int t)
109 1.28 christos {
110 1.28 christos static char buf[64];
111 1.28 christos size_t i;
112 1.28 christos
113 1.28 christos for (i = 0; i < __arraycount(mbr_ptypes); i++)
114 1.28 christos if ((u_int)mbr_ptypes[i].id == t)
115 1.28 christos return mbr_ptypes[i].name;
116 1.28 christos
117 1.28 christos snprintf(buf, sizeof(buf), "*%u*", t);
118 1.28 christos return buf;
119 1.28 christos }
120 1.28 christos
121 1.29 christos static gpt_type_t
122 1.29 christos freebsd_fstype_to_gpt_type(gpt_t gpt, u_int i, u_int fstype)
123 1.1 christos {
124 1.29 christos switch (fstype) {
125 1.29 christos case FS_UNUSED:
126 1.29 christos return GPT_TYPE_INVALID;
127 1.29 christos case FS_SWAP:
128 1.29 christos return GPT_TYPE_FREEBSD_SWAP;
129 1.29 christos case FS_BSDFFS:
130 1.29 christos return GPT_TYPE_FREEBSD_UFS;
131 1.29 christos case FREEBSD_FS_VINUM:
132 1.29 christos return GPT_TYPE_FREEBSD_VINUM;
133 1.29 christos case FREEBSD_FS_ZFS:
134 1.29 christos return GPT_TYPE_FREEBSD_ZFS;
135 1.29 christos default:
136 1.29 christos gpt_warnx(gpt, "Unknown FreeBSD partition (%d)", fstype);
137 1.29 christos return GPT_TYPE_INVALID;
138 1.23 christos }
139 1.29 christos }
140 1.1 christos
141 1.29 christos static gpt_type_t
142 1.29 christos netbsd_fstype_to_gpt_type(gpt_t gpt, u_int i, u_int fstype)
143 1.29 christos {
144 1.29 christos switch (fstype) {
145 1.29 christos case FS_UNUSED:
146 1.29 christos return GPT_TYPE_INVALID;
147 1.29 christos case FS_HFS:
148 1.29 christos return GPT_TYPE_APPLE_HFS;
149 1.29 christos case FS_EX2FS:
150 1.29 christos return GPT_TYPE_LINUX_DATA;
151 1.29 christos case FS_SWAP:
152 1.29 christos return GPT_TYPE_NETBSD_SWAP;
153 1.29 christos case FS_BSDFFS:
154 1.29 christos return GPT_TYPE_NETBSD_FFS;
155 1.29 christos case FS_BSDLFS:
156 1.29 christos return GPT_TYPE_NETBSD_LFS;
157 1.29 christos case FS_RAID:
158 1.29 christos return GPT_TYPE_NETBSD_RAIDFRAME;
159 1.29 christos case FS_CCD:
160 1.29 christos return GPT_TYPE_NETBSD_CCD;
161 1.29 christos case FS_CGD:
162 1.29 christos return GPT_TYPE_NETBSD_CGD;
163 1.29 christos default:
164 1.29 christos gpt_warnx(gpt, "Partition %u unknown type %s, "
165 1.29 christos "using \"Microsoft Basic Data\"", i, fstypename(fstype));
166 1.29 christos return GPT_TYPE_MS_BASIC_DATA;
167 1.1 christos }
168 1.1 christos }
169 1.1 christos
170 1.29 christos static struct gpt_ent *
171 1.29 christos migrate_disklabel(gpt_t gpt, off_t start, struct gpt_ent *ent,
172 1.29 christos gpt_type_t (*convert)(gpt_t, u_int, u_int))
173 1.9 jnemeth {
174 1.9 jnemeth char *buf;
175 1.9 jnemeth struct disklabel *dl;
176 1.9 jnemeth off_t ofs, rawofs;
177 1.28 christos unsigned int i;
178 1.28 christos gpt_type_t type;
179 1.9 jnemeth
180 1.23 christos buf = gpt_read(gpt, start + LABELSECTOR, 1);
181 1.23 christos if (buf == NULL) {
182 1.23 christos gpt_warn(gpt, "Error reading label");
183 1.23 christos return NULL;
184 1.23 christos }
185 1.9 jnemeth dl = (void*)(buf + LABELOFFSET);
186 1.9 jnemeth
187 1.9 jnemeth if (le32toh(dl->d_magic) != DISKMAGIC ||
188 1.9 jnemeth le32toh(dl->d_magic2) != DISKMAGIC) {
189 1.29 christos gpt_warnx(gpt, "MBR partition without disklabel");
190 1.12 christos free(buf);
191 1.23 christos return ent;
192 1.9 jnemeth }
193 1.9 jnemeth
194 1.9 jnemeth rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) *
195 1.9 jnemeth le32toh(dl->d_secsize);
196 1.9 jnemeth for (i = 0; i < le16toh(dl->d_npartitions); i++) {
197 1.9 jnemeth if (dl->d_partitions[i].p_fstype == FS_UNUSED)
198 1.9 jnemeth continue;
199 1.9 jnemeth ofs = le32toh(dl->d_partitions[i].p_offset) *
200 1.9 jnemeth le32toh(dl->d_secsize);
201 1.9 jnemeth if (ofs < rawofs)
202 1.9 jnemeth rawofs = 0;
203 1.9 jnemeth }
204 1.29 christos
205 1.28 christos if (gpt->verbose > 1)
206 1.28 christos gpt_msg(gpt, "rawofs=%ju", (uintmax_t)rawofs);
207 1.23 christos rawofs /= gpt->secsz;
208 1.9 jnemeth
209 1.9 jnemeth for (i = 0; i < le16toh(dl->d_npartitions); i++) {
210 1.28 christos if (gpt->verbose > 1)
211 1.28 christos gpt_msg(gpt, "Disklabel partition %u type %s", i,
212 1.28 christos fstypename(dl->d_partitions[i].p_fstype));
213 1.28 christos
214 1.29 christos type = (*convert)(gpt, i, dl->d_partitions[i].p_fstype);
215 1.29 christos if (type == GPT_TYPE_INVALID)
216 1.9 jnemeth continue;
217 1.9 jnemeth
218 1.28 christos gpt_uuid_create(type, ent->ent_type,
219 1.28 christos ent->ent_name, sizeof(ent->ent_name));
220 1.28 christos
221 1.9 jnemeth ofs = (le32toh(dl->d_partitions[i].p_offset) *
222 1.23 christos le32toh(dl->d_secsize)) / gpt->secsz;
223 1.9 jnemeth ofs = (ofs > 0) ? ofs - rawofs : 0;
224 1.27 christos ent->ent_lba_start = htole64((uint64_t)ofs);
225 1.27 christos ent->ent_lba_end = htole64((uint64_t)(ofs +
226 1.27 christos (off_t)le32toh((uint64_t)dl->d_partitions[i].p_size)
227 1.27 christos - 1LL));
228 1.9 jnemeth ent++;
229 1.9 jnemeth }
230 1.9 jnemeth
231 1.12 christos free(buf);
232 1.23 christos return ent;
233 1.9 jnemeth }
234 1.9 jnemeth
235 1.23 christos static int
236 1.31 christos migrate(gpt_t gpt, u_int parts, int force, int slice, int active)
237 1.1 christos {
238 1.25 christos off_t last = gpt_last(gpt);
239 1.23 christos map_t map;
240 1.1 christos struct gpt_ent *ent;
241 1.1 christos struct mbr *mbr;
242 1.1 christos uint32_t start, size;
243 1.1 christos unsigned int i;
244 1.29 christos gpt_type_t type = GPT_TYPE_INVALID;
245 1.1 christos
246 1.23 christos map = map_find(gpt, MAP_TYPE_MBR);
247 1.1 christos if (map == NULL || map->map_start != 0) {
248 1.28 christos gpt_warnx(gpt, "No MBR in disk to convert");
249 1.23 christos return -1;
250 1.1 christos }
251 1.1 christos
252 1.1 christos mbr = map->map_data;
253 1.1 christos
254 1.25 christos if (gpt_create(gpt, last, parts, 0) == -1)
255 1.23 christos return -1;
256 1.1 christos
257 1.23 christos ent = gpt->tbl->map_data;
258 1.1 christos
259 1.1 christos /* Mirror partitions. */
260 1.1 christos for (i = 0; i < 4; i++) {
261 1.1 christos start = le16toh(mbr->mbr_part[i].part_start_hi);
262 1.1 christos start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
263 1.1 christos size = le16toh(mbr->mbr_part[i].part_size_hi);
264 1.1 christos size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
265 1.1 christos
266 1.28 christos if (gpt->verbose > 1)
267 1.28 christos gpt_msg(gpt, "MBR partition %u type %s", i,
268 1.28 christos mbrptypename(mbr->mbr_part[i].part_typ));
269 1.1 christos switch (mbr->mbr_part[i].part_typ) {
270 1.9 jnemeth case MBR_PTYPE_UNUSED:
271 1.1 christos continue;
272 1.29 christos
273 1.29 christos case MBR_PTYPE_386BSD: /* FreeBSD */
274 1.1 christos if (slice) {
275 1.29 christos type = GPT_TYPE_FREEBSD;
276 1.29 christos break;
277 1.29 christos } else {
278 1.29 christos ent = migrate_disklabel(gpt, start, ent,
279 1.29 christos freebsd_fstype_to_gpt_type);
280 1.29 christos continue;
281 1.29 christos }
282 1.29 christos
283 1.29 christos case MBR_PTYPE_NETBSD: /* NetBSD */
284 1.29 christos ent = migrate_disklabel(gpt, start, ent,
285 1.29 christos netbsd_fstype_to_gpt_type);
286 1.29 christos continue;
287 1.29 christos
288 1.29 christos case MBR_PTYPE_EFI:
289 1.29 christos type = GPT_TYPE_EFI;
290 1.1 christos break;
291 1.29 christos
292 1.1 christos default:
293 1.1 christos if (!force) {
294 1.23 christos gpt_warnx(gpt, "unknown partition type (%d)",
295 1.23 christos mbr->mbr_part[i].part_typ);
296 1.23 christos return -1;
297 1.1 christos }
298 1.29 christos continue;
299 1.1 christos }
300 1.29 christos gpt_uuid_create(type, ent->ent_type, ent->ent_name,
301 1.29 christos sizeof(ent->ent_name));
302 1.29 christos ent->ent_lba_start = htole64((uint64_t)start);
303 1.29 christos ent->ent_lba_end = htole64((uint64_t)(start + size - 1LL));
304 1.29 christos ent++;
305 1.1 christos }
306 1.1 christos
307 1.23 christos if (gpt_write_primary(gpt) == -1)
308 1.23 christos return -1;
309 1.1 christos
310 1.23 christos if (gpt_write_backup(gpt) == -1)
311 1.23 christos return -1;
312 1.1 christos
313 1.1 christos /*
314 1.1 christos * Turn the MBR into a Protective MBR.
315 1.1 christos */
316 1.16 christos memset(mbr->mbr_part, 0, sizeof(mbr->mbr_part));
317 1.31 christos gpt_create_pmbr_part(mbr->mbr_part, last, active);
318 1.25 christos if (gpt_write(gpt, map) == -1) {
319 1.25 christos gpt_warn(gpt, "Cant write PMBR");
320 1.25 christos return -1;
321 1.25 christos }
322 1.23 christos return 0;
323 1.1 christos }
324 1.1 christos
325 1.24 christos static int
326 1.23 christos cmd_migrate(gpt_t gpt, int argc, char *argv[])
327 1.1 christos {
328 1.23 christos int ch;
329 1.26 christos int force = 0;
330 1.26 christos int slice = 0;
331 1.31 christos int active = 0;
332 1.26 christos u_int parts = 128;
333 1.1 christos
334 1.1 christos /* Get the migrate options */
335 1.31 christos while ((ch = getopt(argc, argv, "afp:s")) != -1) {
336 1.1 christos switch(ch) {
337 1.31 christos case 'a':
338 1.31 christos active = 1;
339 1.31 christos break;
340 1.1 christos case 'f':
341 1.1 christos force = 1;
342 1.1 christos break;
343 1.23 christos case 'p':
344 1.30 christos if (gpt_uint_get(gpt, &parts) == -1)
345 1.27 christos return usage();
346 1.23 christos break;
347 1.1 christos case 's':
348 1.1 christos slice = 1;
349 1.1 christos break;
350 1.1 christos default:
351 1.24 christos return usage();
352 1.1 christos }
353 1.1 christos }
354 1.1 christos
355 1.23 christos if (argc != optind)
356 1.24 christos return usage();
357 1.1 christos
358 1.31 christos return migrate(gpt, parts, force, slice, active);
359 1.1 christos }
360