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
81static int cmd_migrate(gpt_t, int, char *[]);
82
83static const char *migratehelp[] = {
84	"[-Afs] [-p partitions]",
85};
86
87struct 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
96static const char *
97fstypename(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
107static const char *
108mbrptypename(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
121static gpt_type_t
122freebsd_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
141static gpt_type_t
142netbsd_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
170static struct gpt_ent *
171migrate_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
235static int
236migrate(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
342static int
343cmd_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