Home | History | Annotate | Line # | Download | only in gpt
      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.36  christos __RCSID("$NetBSD: migrate.c,v 1.36 2025/02/23 20:47:19 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.32  christos 	"[-Afs] [-p partitions]",
     85  1.24  christos };
     86  1.24  christos 
     87  1.36  christos const 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.33   jnemeth 	GPT_SYNC,
     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.36  christos freebsd_fstype_to_gpt_type(gpt_t gpt, u_int i __unused, 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.34    martin 		case MBR_PTYPE_FAT12:
    293  1.34    martin 		case MBR_PTYPE_FAT16S:
    294  1.34    martin 		case MBR_PTYPE_FAT16B:
    295  1.35   jnemeth 		case MBR_PTYPE_NTFS:
    296  1.34    martin 		case MBR_PTYPE_FAT32:
    297  1.34    martin 		case MBR_PTYPE_FAT32L:
    298  1.34    martin 		case MBR_PTYPE_FAT16L:
    299  1.35   jnemeth 		case MBR_PTYPE_OS2_DOS12:
    300  1.35   jnemeth 		case MBR_PTYPE_OS2_DOS16S:
    301  1.35   jnemeth 		case MBR_PTYPE_OS2_DOS16B:
    302  1.35   jnemeth 		case MBR_PTYPE_OS2_IFS:
    303  1.35   jnemeth 		case MBR_PTYPE_HID_FAT32:
    304  1.35   jnemeth 		case MBR_PTYPE_HID_FAT32_LBA:
    305  1.35   jnemeth 		case MBR_PTYPE_HID_FAT16_LBA:
    306  1.34    martin 			type = GPT_TYPE_MS_BASIC_DATA;
    307  1.34    martin 			break;
    308  1.34    martin 
    309   1.1  christos 		default:
    310   1.1  christos 			if (!force) {
    311  1.23  christos 				gpt_warnx(gpt, "unknown partition type (%d)",
    312  1.23  christos 				    mbr->mbr_part[i].part_typ);
    313  1.23  christos 				return -1;
    314   1.1  christos 			}
    315  1.29  christos 			continue;
    316   1.1  christos 		}
    317  1.29  christos 		gpt_uuid_create(type, ent->ent_type, ent->ent_name,
    318  1.29  christos 		    sizeof(ent->ent_name));
    319  1.29  christos 		ent->ent_lba_start = htole64((uint64_t)start);
    320  1.29  christos 		ent->ent_lba_end = htole64((uint64_t)(start + size - 1LL));
    321  1.29  christos 		ent++;
    322   1.1  christos 	}
    323   1.1  christos 
    324  1.23  christos 	if (gpt_write_primary(gpt) == -1)
    325  1.23  christos 		return -1;
    326   1.1  christos 
    327  1.23  christos 	if (gpt_write_backup(gpt) == -1)
    328  1.23  christos 		return -1;
    329   1.1  christos 
    330   1.1  christos 	/*
    331   1.1  christos 	 * Turn the MBR into a Protective MBR.
    332   1.1  christos 	 */
    333  1.16  christos 	memset(mbr->mbr_part, 0, sizeof(mbr->mbr_part));
    334  1.31  christos 	gpt_create_pmbr_part(mbr->mbr_part, last, active);
    335  1.25  christos 	if (gpt_write(gpt, map) == -1) {
    336  1.25  christos 		gpt_warn(gpt, "Cant write PMBR");
    337  1.25  christos 		return -1;
    338  1.25  christos 	}
    339  1.23  christos 	return 0;
    340   1.1  christos }
    341   1.1  christos 
    342  1.24  christos static int
    343  1.23  christos cmd_migrate(gpt_t gpt, int argc, char *argv[])
    344   1.1  christos {
    345  1.23  christos 	int ch;
    346  1.26  christos 	int force = 0;
    347  1.26  christos 	int slice = 0;
    348  1.31  christos 	int active = 0;
    349  1.26  christos 	u_int parts = 128;
    350   1.1  christos 
    351   1.1  christos 	/* Get the migrate options */
    352  1.32  christos 	while ((ch = getopt(argc, argv, "Afp:s")) != -1) {
    353   1.1  christos 		switch(ch) {
    354  1.32  christos 		case 'A':
    355  1.31  christos 			active = 1;
    356  1.31  christos 			break;
    357   1.1  christos 		case 'f':
    358   1.1  christos 			force = 1;
    359   1.1  christos 			break;
    360  1.23  christos 		case 'p':
    361  1.30  christos 			if (gpt_uint_get(gpt, &parts) == -1)
    362  1.27  christos 				return usage();
    363  1.23  christos 			break;
    364   1.1  christos 		case 's':
    365   1.1  christos 			slice = 1;
    366   1.1  christos 			break;
    367   1.1  christos 		default:
    368  1.24  christos 			return usage();
    369   1.1  christos 		}
    370   1.1  christos 	}
    371   1.1  christos 
    372  1.23  christos 	if (argc != optind)
    373  1.24  christos 		return usage();
    374   1.1  christos 
    375  1.31  christos 	return migrate(gpt, parts, force, slice, active);
    376   1.1  christos }
    377