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