/*- * Copyright (c) 2002 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #ifdef __FBSDID __FBSDID("$FreeBSD: src/sbin/gpt/migrate.c,v 1.16 2005/09/01 02:42:52 marcel Exp $"); #endif #ifdef __RCSID __RCSID("$NetBSD: migrate.c,v 1.32 2016/06/09 19:04:43 christos Exp $"); #endif #include #include #define FSTYPENAMES #define MBRPTYPENAMES #ifdef HAVE_NBTOOL_CONFIG_H #include #include #else #include #include #endif #include #include #include #include #include #include #include "map.h" #include "gpt.h" #include "gpt_private.h" /* * Allow compilation on platforms that do not have a BSD label. * The values are valid for amd64, i386 and ia64 disklabels. * XXX: use disklabel_params from disklabel.c */ #ifndef LABELOFFSET #define LABELOFFSET 0 #endif #ifndef LABELSECTOR #define LABELSECTOR 1 #endif #ifndef RAW_PART #define RAW_PART 3 #endif /* FreeBSD filesystem types that don't match corresponding NetBSD types */ #define FREEBSD_FS_VINUM 14 #define FREEBSD_FS_ZFS 27 static int cmd_migrate(gpt_t, int, char *[]); static const char *migratehelp[] = { "[-Afs] [-p partitions]", }; struct gpt_cmd c_migrate = { "migrate", cmd_migrate, migratehelp, __arraycount(migratehelp), 0, }; #define usage() gpt_usage(NULL, &c_migrate) static const char * fstypename(u_int t) { static char buf[64]; if (t >= __arraycount(fstypenames)) { snprintf(buf, sizeof(buf), "*%u*", t); return buf; } return fstypenames[t]; } static const char * mbrptypename(u_int t) { static char buf[64]; size_t i; for (i = 0; i < __arraycount(mbr_ptypes); i++) if ((u_int)mbr_ptypes[i].id == t) return mbr_ptypes[i].name; snprintf(buf, sizeof(buf), "*%u*", t); return buf; } static gpt_type_t freebsd_fstype_to_gpt_type(gpt_t gpt, u_int i, u_int fstype) { switch (fstype) { case FS_UNUSED: return GPT_TYPE_INVALID; case FS_SWAP: return GPT_TYPE_FREEBSD_SWAP; case FS_BSDFFS: return GPT_TYPE_FREEBSD_UFS; case FREEBSD_FS_VINUM: return GPT_TYPE_FREEBSD_VINUM; case FREEBSD_FS_ZFS: return GPT_TYPE_FREEBSD_ZFS; default: gpt_warnx(gpt, "Unknown FreeBSD partition (%d)", fstype); return GPT_TYPE_INVALID; } } static gpt_type_t netbsd_fstype_to_gpt_type(gpt_t gpt, u_int i, u_int fstype) { switch (fstype) { case FS_UNUSED: return GPT_TYPE_INVALID; case FS_HFS: return GPT_TYPE_APPLE_HFS; case FS_EX2FS: return GPT_TYPE_LINUX_DATA; case FS_SWAP: return GPT_TYPE_NETBSD_SWAP; case FS_BSDFFS: return GPT_TYPE_NETBSD_FFS; case FS_BSDLFS: return GPT_TYPE_NETBSD_LFS; case FS_RAID: return GPT_TYPE_NETBSD_RAIDFRAME; case FS_CCD: return GPT_TYPE_NETBSD_CCD; case FS_CGD: return GPT_TYPE_NETBSD_CGD; default: gpt_warnx(gpt, "Partition %u unknown type %s, " "using \"Microsoft Basic Data\"", i, fstypename(fstype)); return GPT_TYPE_MS_BASIC_DATA; } } 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)) { char *buf; struct disklabel *dl; off_t ofs, rawofs; unsigned int i; gpt_type_t type; buf = gpt_read(gpt, start + LABELSECTOR, 1); if (buf == NULL) { gpt_warn(gpt, "Error reading label"); return NULL; } dl = (void*)(buf + LABELOFFSET); if (le32toh(dl->d_magic) != DISKMAGIC || le32toh(dl->d_magic2) != DISKMAGIC) { gpt_warnx(gpt, "MBR partition without disklabel"); free(buf); return ent; } rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) * le32toh(dl->d_secsize); for (i = 0; i < le16toh(dl->d_npartitions); i++) { if (dl->d_partitions[i].p_fstype == FS_UNUSED) continue; ofs = le32toh(dl->d_partitions[i].p_offset) * le32toh(dl->d_secsize); if (ofs < rawofs) rawofs = 0; } if (gpt->verbose > 1) gpt_msg(gpt, "rawofs=%ju", (uintmax_t)rawofs); rawofs /= gpt->secsz; for (i = 0; i < le16toh(dl->d_npartitions); i++) { if (gpt->verbose > 1) gpt_msg(gpt, "Disklabel partition %u type %s", i, fstypename(dl->d_partitions[i].p_fstype)); type = (*convert)(gpt, i, dl->d_partitions[i].p_fstype); if (type == GPT_TYPE_INVALID) continue; gpt_uuid_create(type, ent->ent_type, ent->ent_name, sizeof(ent->ent_name)); ofs = (le32toh(dl->d_partitions[i].p_offset) * le32toh(dl->d_secsize)) / gpt->secsz; ofs = (ofs > 0) ? ofs - rawofs : 0; ent->ent_lba_start = htole64((uint64_t)ofs); ent->ent_lba_end = htole64((uint64_t)(ofs + (off_t)le32toh((uint64_t)dl->d_partitions[i].p_size) - 1LL)); ent++; } free(buf); return ent; } static int migrate(gpt_t gpt, u_int parts, int force, int slice, int active) { off_t last = gpt_last(gpt); map_t map; struct gpt_ent *ent; struct mbr *mbr; uint32_t start, size; unsigned int i; gpt_type_t type = GPT_TYPE_INVALID; map = map_find(gpt, MAP_TYPE_MBR); if (map == NULL || map->map_start != 0) { gpt_warnx(gpt, "No MBR in disk to convert"); return -1; } mbr = map->map_data; if (gpt_create(gpt, last, parts, 0) == -1) return -1; ent = gpt->tbl->map_data; /* Mirror partitions. */ for (i = 0; i < 4; i++) { start = le16toh(mbr->mbr_part[i].part_start_hi); start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo); size = le16toh(mbr->mbr_part[i].part_size_hi); size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo); if (gpt->verbose > 1) gpt_msg(gpt, "MBR partition %u type %s", i, mbrptypename(mbr->mbr_part[i].part_typ)); switch (mbr->mbr_part[i].part_typ) { case MBR_PTYPE_UNUSED: continue; case MBR_PTYPE_386BSD: /* FreeBSD */ if (slice) { type = GPT_TYPE_FREEBSD; break; } else { ent = migrate_disklabel(gpt, start, ent, freebsd_fstype_to_gpt_type); continue; } case MBR_PTYPE_NETBSD: /* NetBSD */ ent = migrate_disklabel(gpt, start, ent, netbsd_fstype_to_gpt_type); continue; case MBR_PTYPE_EFI: type = GPT_TYPE_EFI; break; default: if (!force) { gpt_warnx(gpt, "unknown partition type (%d)", mbr->mbr_part[i].part_typ); return -1; } continue; } gpt_uuid_create(type, ent->ent_type, ent->ent_name, sizeof(ent->ent_name)); ent->ent_lba_start = htole64((uint64_t)start); ent->ent_lba_end = htole64((uint64_t)(start + size - 1LL)); ent++; } if (gpt_write_primary(gpt) == -1) return -1; if (gpt_write_backup(gpt) == -1) return -1; /* * Turn the MBR into a Protective MBR. */ memset(mbr->mbr_part, 0, sizeof(mbr->mbr_part)); gpt_create_pmbr_part(mbr->mbr_part, last, active); if (gpt_write(gpt, map) == -1) { gpt_warn(gpt, "Cant write PMBR"); return -1; } return 0; } static int cmd_migrate(gpt_t gpt, int argc, char *argv[]) { int ch; int force = 0; int slice = 0; int active = 0; u_int parts = 128; /* Get the migrate options */ while ((ch = getopt(argc, argv, "Afp:s")) != -1) { switch(ch) { case 'A': active = 1; break; case 'f': force = 1; break; case 'p': if (gpt_uint_get(gpt, &parts) == -1) return usage(); break; case 's': slice = 1; break; default: return usage(); } } if (argc != optind) return usage(); return migrate(gpt, parts, force, slice, active); }