xref: /netbsd-src/sbin/gpt/migrate.c (revision f23b40fcf3dd6d9a261df7faa9fe691143fa80a3)
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.35 2019/03/03 02:28:14 jnemeth Exp $");
37 #endif
38 
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #define FSTYPENAMES
42 #define MBRPTYPENAMES
43 #ifdef HAVE_NBTOOL_CONFIG_H
44 #include <nbinclude/sys/bootblock.h>
45 #include <nbinclude/sys/disklabel.h>
46 #else
47 #include <sys/bootblock.h>
48 #include <sys/disklabel.h>
49 #endif
50 
51 #include <err.h>
52 #include <stddef.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 
58 #include "map.h"
59 #include "gpt.h"
60 #include "gpt_private.h"
61 
62 /*
63  * Allow compilation on platforms that do not have a BSD label.
64  * The values are valid for amd64, i386 and ia64 disklabels.
65  * XXX: use disklabel_params from disklabel.c
66  */
67 #ifndef LABELOFFSET
68 #define	LABELOFFSET	0
69 #endif
70 #ifndef LABELSECTOR
71 #define	LABELSECTOR	1
72 #endif
73 #ifndef RAW_PART
74 #define	RAW_PART	3
75 #endif
76 
77 /* FreeBSD filesystem types that don't match corresponding NetBSD types */
78 #define	FREEBSD_FS_VINUM	14
79 #define	FREEBSD_FS_ZFS		27
80 
81 static int cmd_migrate(gpt_t, int, char *[]);
82 
83 static const char *migratehelp[] = {
84 	"[-Afs] [-p partitions]",
85 };
86 
87 struct gpt_cmd c_migrate = {
88 	"migrate",
89 	cmd_migrate,
90 	migratehelp, __arraycount(migratehelp),
91 	GPT_SYNC,
92 };
93 
94 #define usage() gpt_usage(NULL, &c_migrate)
95 
96 static const char *
fstypename(u_int t)97 fstypename(u_int t)
98 {
99 	static char buf[64];
100 	if (t >= __arraycount(fstypenames)) {
101 		snprintf(buf, sizeof(buf), "*%u*", t);
102 		return buf;
103 	}
104 	return fstypenames[t];
105 }
106 
107 static const char *
mbrptypename(u_int t)108 mbrptypename(u_int t)
109 {
110 	static char buf[64];
111 	size_t i;
112 
113 	for (i = 0; i < __arraycount(mbr_ptypes); i++)
114 		if ((u_int)mbr_ptypes[i].id == t)
115 			return mbr_ptypes[i].name;
116 
117 	snprintf(buf, sizeof(buf), "*%u*", t);
118 	return buf;
119 }
120 
121 static gpt_type_t
freebsd_fstype_to_gpt_type(gpt_t gpt,u_int i,u_int fstype)122 freebsd_fstype_to_gpt_type(gpt_t gpt, u_int i, u_int fstype)
123 {
124 	switch (fstype) {
125 	case FS_UNUSED:
126 		return GPT_TYPE_INVALID;
127 	case FS_SWAP:
128 		return GPT_TYPE_FREEBSD_SWAP;
129 	case FS_BSDFFS:
130 		return GPT_TYPE_FREEBSD_UFS;
131 	case FREEBSD_FS_VINUM:
132 		return GPT_TYPE_FREEBSD_VINUM;
133 	case FREEBSD_FS_ZFS:
134 		return GPT_TYPE_FREEBSD_ZFS;
135 	default:
136 		gpt_warnx(gpt, "Unknown FreeBSD partition (%d)", fstype);
137 		return GPT_TYPE_INVALID;
138 	}
139 }
140 
141 static gpt_type_t
netbsd_fstype_to_gpt_type(gpt_t gpt,u_int i,u_int fstype)142 netbsd_fstype_to_gpt_type(gpt_t gpt, u_int i, u_int fstype)
143 {
144 	switch (fstype) {
145 	case FS_UNUSED:
146 		return GPT_TYPE_INVALID;
147 	case FS_HFS:
148 		return GPT_TYPE_APPLE_HFS;
149 	case FS_EX2FS:
150 		return GPT_TYPE_LINUX_DATA;
151 	case FS_SWAP:
152 		return GPT_TYPE_NETBSD_SWAP;
153 	case FS_BSDFFS:
154 		return GPT_TYPE_NETBSD_FFS;
155 	case FS_BSDLFS:
156 		return GPT_TYPE_NETBSD_LFS;
157 	case FS_RAID:
158 		return GPT_TYPE_NETBSD_RAIDFRAME;
159 	case FS_CCD:
160 		return GPT_TYPE_NETBSD_CCD;
161 	case FS_CGD:
162 		return GPT_TYPE_NETBSD_CGD;
163 	default:
164 		gpt_warnx(gpt, "Partition %u unknown type %s, "
165 		    "using \"Microsoft Basic Data\"", i, fstypename(fstype));
166 		return GPT_TYPE_MS_BASIC_DATA;
167 	}
168 }
169 
170 static struct gpt_ent *
migrate_disklabel(gpt_t gpt,off_t start,struct gpt_ent * ent,gpt_type_t (* convert)(gpt_t,u_int,u_int))171 migrate_disklabel(gpt_t gpt, off_t start, struct gpt_ent *ent,
172     gpt_type_t (*convert)(gpt_t, u_int, u_int))
173 {
174 	char *buf;
175 	struct disklabel *dl;
176 	off_t ofs, rawofs;
177 	unsigned int i;
178 	gpt_type_t type;
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, "MBR partition 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 
205 	if (gpt->verbose > 1)
206 		gpt_msg(gpt, "rawofs=%ju", (uintmax_t)rawofs);
207 	rawofs /= gpt->secsz;
208 
209 	for (i = 0; i < le16toh(dl->d_npartitions); i++) {
210 		if (gpt->verbose > 1)
211 			gpt_msg(gpt, "Disklabel partition %u type %s", i,
212 			    fstypename(dl->d_partitions[i].p_fstype));
213 
214 		type = (*convert)(gpt, i, dl->d_partitions[i].p_fstype);
215 		if (type == GPT_TYPE_INVALID)
216 			continue;
217 
218 		gpt_uuid_create(type, ent->ent_type,
219 		    ent->ent_name, sizeof(ent->ent_name));
220 
221 		ofs = (le32toh(dl->d_partitions[i].p_offset) *
222 		    le32toh(dl->d_secsize)) / gpt->secsz;
223 		ofs = (ofs > 0) ? ofs - rawofs : 0;
224 		ent->ent_lba_start = htole64((uint64_t)ofs);
225 		ent->ent_lba_end = htole64((uint64_t)(ofs +
226 		    (off_t)le32toh((uint64_t)dl->d_partitions[i].p_size)
227 		    - 1LL));
228 		ent++;
229 	}
230 
231 	free(buf);
232 	return ent;
233 }
234 
235 static int
migrate(gpt_t gpt,u_int parts,int force,int slice,int active)236 migrate(gpt_t gpt, u_int parts, int force, int slice, int active)
237 {
238 	off_t last = gpt_last(gpt);
239 	map_t map;
240 	struct gpt_ent *ent;
241 	struct mbr *mbr;
242 	uint32_t start, size;
243 	unsigned int i;
244 	gpt_type_t type = GPT_TYPE_INVALID;
245 
246 	map = map_find(gpt, MAP_TYPE_MBR);
247 	if (map == NULL || map->map_start != 0) {
248 		gpt_warnx(gpt, "No MBR in disk to convert");
249 		return -1;
250 	}
251 
252 	mbr = map->map_data;
253 
254 	if (gpt_create(gpt, last, parts, 0) == -1)
255 		return -1;
256 
257 	ent = gpt->tbl->map_data;
258 
259 	/* Mirror partitions. */
260 	for (i = 0; i < 4; i++) {
261 		start = le16toh(mbr->mbr_part[i].part_start_hi);
262 		start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
263 		size = le16toh(mbr->mbr_part[i].part_size_hi);
264 		size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
265 
266 		if (gpt->verbose > 1)
267 			gpt_msg(gpt, "MBR partition %u type %s", i,
268 			    mbrptypename(mbr->mbr_part[i].part_typ));
269 		switch (mbr->mbr_part[i].part_typ) {
270 		case MBR_PTYPE_UNUSED:
271 			continue;
272 
273 		case MBR_PTYPE_386BSD: /* FreeBSD */
274 			if (slice) {
275 				type = GPT_TYPE_FREEBSD;
276 				break;
277 			} else {
278 				ent = migrate_disklabel(gpt, start, ent,
279 				    freebsd_fstype_to_gpt_type);
280 				continue;
281 			}
282 
283 		case MBR_PTYPE_NETBSD:	/* NetBSD */
284 			ent = migrate_disklabel(gpt, start, ent,
285 			    netbsd_fstype_to_gpt_type);
286 			continue;
287 
288 		case MBR_PTYPE_EFI:
289 			type = GPT_TYPE_EFI;
290 			break;
291 
292 		case MBR_PTYPE_FAT12:
293 		case MBR_PTYPE_FAT16S:
294 		case MBR_PTYPE_FAT16B:
295 		case MBR_PTYPE_NTFS:
296 		case MBR_PTYPE_FAT32:
297 		case MBR_PTYPE_FAT32L:
298 		case MBR_PTYPE_FAT16L:
299 		case MBR_PTYPE_OS2_DOS12:
300 		case MBR_PTYPE_OS2_DOS16S:
301 		case MBR_PTYPE_OS2_DOS16B:
302 		case MBR_PTYPE_OS2_IFS:
303 		case MBR_PTYPE_HID_FAT32:
304 		case MBR_PTYPE_HID_FAT32_LBA:
305 		case MBR_PTYPE_HID_FAT16_LBA:
306 			type = GPT_TYPE_MS_BASIC_DATA;
307 			break;
308 
309 		default:
310 			if (!force) {
311 				gpt_warnx(gpt, "unknown partition type (%d)",
312 				    mbr->mbr_part[i].part_typ);
313 				return -1;
314 			}
315 			continue;
316 		}
317 		gpt_uuid_create(type, ent->ent_type, ent->ent_name,
318 		    sizeof(ent->ent_name));
319 		ent->ent_lba_start = htole64((uint64_t)start);
320 		ent->ent_lba_end = htole64((uint64_t)(start + size - 1LL));
321 		ent++;
322 	}
323 
324 	if (gpt_write_primary(gpt) == -1)
325 		return -1;
326 
327 	if (gpt_write_backup(gpt) == -1)
328 		return -1;
329 
330 	/*
331 	 * Turn the MBR into a Protective MBR.
332 	 */
333 	memset(mbr->mbr_part, 0, sizeof(mbr->mbr_part));
334 	gpt_create_pmbr_part(mbr->mbr_part, last, active);
335 	if (gpt_write(gpt, map) == -1) {
336 		gpt_warn(gpt, "Cant write PMBR");
337 		return -1;
338 	}
339 	return 0;
340 }
341 
342 static int
cmd_migrate(gpt_t gpt,int argc,char * argv[])343 cmd_migrate(gpt_t gpt, int argc, char *argv[])
344 {
345 	int ch;
346 	int force = 0;
347 	int slice = 0;
348 	int active = 0;
349 	u_int parts = 128;
350 
351 	/* Get the migrate options */
352 	while ((ch = getopt(argc, argv, "Afp:s")) != -1) {
353 		switch(ch) {
354 		case 'A':
355 			active = 1;
356 			break;
357 		case 'f':
358 			force = 1;
359 			break;
360 		case 'p':
361 			if (gpt_uint_get(gpt, &parts) == -1)
362 				return usage();
363 			break;
364 		case 's':
365 			slice = 1;
366 			break;
367 		default:
368 			return usage();
369 		}
370 	}
371 
372 	if (argc != optind)
373 		return usage();
374 
375 	return migrate(gpt, parts, force, slice, active);
376 }
377