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