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