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.21 2014/10/03 20:30:06 christos Exp $"); 37 #endif 38 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #ifdef HAVE_NBTOOL_CONFIG_H 42 #include <nbinclude/sys/bootblock.h> 43 #include <nbinclude/sys/disklabel.h> 44 #else 45 #include <sys/bootblock.h> 46 #include <sys/disklabel.h> 47 #endif 48 49 #include <err.h> 50 #include <stddef.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 #include "map.h" 57 #include "gpt.h" 58 59 /* 60 * Allow compilation on platforms that do not have a BSD label. 61 * The values are valid for amd64, i386 and ia64 disklabels. 62 * XXX: use disklabel_params from disklabel.c 63 */ 64 #ifndef LABELOFFSET 65 #define LABELOFFSET 0 66 #endif 67 #ifndef LABELSECTOR 68 #define LABELSECTOR 1 69 #endif 70 #ifndef RAW_PART 71 #define RAW_PART 3 72 #endif 73 74 /* FreeBSD filesystem types that don't match corresponding NetBSD types */ 75 #define FREEBSD_FS_VINUM 14 76 #define FREEBSD_FS_ZFS 27 77 78 static int force; 79 static int slice; 80 81 const char migratemsg[] = "migrate [-fs] device ..."; 82 83 __dead static void 84 usage_migrate(void) 85 { 86 87 fprintf(stderr, 88 "usage: %s %s\n", getprogname(), migratemsg); 89 exit(1); 90 } 91 92 static struct gpt_ent* 93 migrate_disklabel(int fd, off_t start, struct gpt_ent *ent) 94 { 95 char *buf; 96 struct disklabel *dl; 97 off_t ofs, rawofs; 98 int i; 99 100 buf = gpt_read(fd, start + LABELSECTOR, 1); 101 dl = (void*)(buf + LABELOFFSET); 102 103 if (le32toh(dl->d_magic) != DISKMAGIC || 104 le32toh(dl->d_magic2) != DISKMAGIC) { 105 warnx("%s: warning: FreeBSD slice without disklabel", 106 device_name); 107 free(buf); 108 return (ent); 109 } 110 111 rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) * 112 le32toh(dl->d_secsize); 113 for (i = 0; i < le16toh(dl->d_npartitions); i++) { 114 if (dl->d_partitions[i].p_fstype == FS_UNUSED) 115 continue; 116 ofs = le32toh(dl->d_partitions[i].p_offset) * 117 le32toh(dl->d_secsize); 118 if (ofs < rawofs) 119 rawofs = 0; 120 } 121 rawofs /= secsz; 122 123 for (i = 0; i < le16toh(dl->d_npartitions); i++) { 124 switch (dl->d_partitions[i].p_fstype) { 125 case FS_UNUSED: 126 continue; 127 case FS_SWAP: { 128 gpt_uuid_create(GPT_TYPE_FREEBSD_SWAP, ent->ent_type, 129 ent->ent_name, sizeof(ent->ent_name)); 130 break; 131 } 132 case FS_BSDFFS: { 133 gpt_uuid_create(GPT_TYPE_FREEBSD_UFS, ent->ent_type, 134 ent->ent_name, sizeof(ent->ent_name)); 135 break; 136 } 137 case FREEBSD_FS_VINUM: { 138 gpt_uuid_create(GPT_TYPE_FREEBSD_VINUM, ent->ent_type, 139 ent->ent_name, sizeof(ent->ent_name)); 140 break; 141 } 142 case FREEBSD_FS_ZFS: { 143 gpt_uuid_create(GPT_TYPE_FREEBSD_ZFS, ent->ent_type, 144 ent->ent_name, sizeof(ent->ent_name)); 145 break; 146 } 147 default: 148 warnx("%s: warning: unknown FreeBSD partition (%d)", 149 device_name, dl->d_partitions[i].p_fstype); 150 continue; 151 } 152 153 ofs = (le32toh(dl->d_partitions[i].p_offset) * 154 le32toh(dl->d_secsize)) / secsz; 155 ofs = (ofs > 0) ? ofs - rawofs : 0; 156 ent->ent_lba_start = htole64(start + ofs); 157 ent->ent_lba_end = htole64(start + ofs + 158 le32toh(dl->d_partitions[i].p_size) - 1LL); 159 ent++; 160 } 161 162 free(buf); 163 return (ent); 164 } 165 166 static struct gpt_ent* 167 migrate_netbsd_disklabel(int fd, off_t start, struct gpt_ent *ent) 168 { 169 char *buf; 170 struct disklabel *dl; 171 off_t ofs, rawofs; 172 int i; 173 174 buf = gpt_read(fd, start + LABELSECTOR, 1); 175 dl = (void*)(buf + LABELOFFSET); 176 177 if (le32toh(dl->d_magic) != DISKMAGIC || 178 le32toh(dl->d_magic2) != DISKMAGIC) { 179 warnx("%s: warning: NetBSD slice without disklabel", 180 device_name); 181 free(buf); 182 return (ent); 183 } 184 185 rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) * 186 le32toh(dl->d_secsize); 187 for (i = 0; i < le16toh(dl->d_npartitions); i++) { 188 if (dl->d_partitions[i].p_fstype == FS_UNUSED) 189 continue; 190 ofs = le32toh(dl->d_partitions[i].p_offset) * 191 le32toh(dl->d_secsize); 192 if (ofs < rawofs) 193 rawofs = 0; 194 } 195 rawofs /= secsz; 196 197 for (i = 0; i < le16toh(dl->d_npartitions); i++) { 198 switch (dl->d_partitions[i].p_fstype) { 199 case FS_UNUSED: 200 continue; 201 case FS_SWAP: { 202 gpt_uuid_create(GPT_TYPE_NETBSD_SWAP, ent->ent_type, 203 ent->ent_name, sizeof(ent->ent_name)); 204 break; 205 } 206 case FS_BSDFFS: { 207 gpt_uuid_create(GPT_TYPE_NETBSD_FFS, ent->ent_type, 208 ent->ent_name, sizeof(ent->ent_name)); 209 break; 210 } 211 case FS_BSDLFS: { 212 gpt_uuid_create(GPT_TYPE_NETBSD_LFS, ent->ent_type, 213 ent->ent_name, sizeof(ent->ent_name)); 214 break; 215 } 216 case FS_RAID: { 217 gpt_uuid_create(GPT_TYPE_NETBSD_RAIDFRAME, ent->ent_type, 218 ent->ent_name, sizeof(ent->ent_name)); 219 break; 220 } 221 case FS_CCD: { 222 gpt_uuid_create(GPT_TYPE_NETBSD_CCD, ent->ent_type, 223 ent->ent_name, sizeof(ent->ent_name)); 224 break; 225 } 226 case FS_CGD: { 227 gpt_uuid_create(GPT_TYPE_NETBSD_CGD, ent->ent_type, 228 ent->ent_name, sizeof(ent->ent_name)); 229 break; 230 } 231 default: 232 warnx("%s: warning: unknown NetBSD partition (%d)", 233 device_name, dl->d_partitions[i].p_fstype); 234 continue; 235 } 236 237 ofs = (le32toh(dl->d_partitions[i].p_offset) * 238 le32toh(dl->d_secsize)) / secsz; 239 ofs = (ofs > 0) ? ofs - rawofs : 0; 240 ent->ent_lba_start = htole64(ofs); 241 ent->ent_lba_end = htole64(ofs + 242 le32toh(dl->d_partitions[i].p_size) - 1LL); 243 ent++; 244 } 245 246 free(buf); 247 return (ent); 248 } 249 250 static void 251 migrate(int fd) 252 { 253 off_t blocks, last; 254 map_t *gpt, *tpg; 255 map_t *tbl, *lbt; 256 map_t *map; 257 struct gpt_hdr *hdr; 258 struct gpt_ent *ent; 259 struct mbr *mbr; 260 uint32_t start, size; 261 unsigned int i; 262 263 last = mediasz / secsz - 1LL; 264 265 map = map_find(MAP_TYPE_MBR); 266 if (map == NULL || map->map_start != 0) { 267 warnx("%s: error: no partitions to convert", device_name); 268 return; 269 } 270 271 mbr = map->map_data; 272 273 if (map_find(MAP_TYPE_PRI_GPT_HDR) != NULL || 274 map_find(MAP_TYPE_SEC_GPT_HDR) != NULL) { 275 warnx("%s: error: device already contains a GPT", device_name); 276 return; 277 } 278 279 /* Get the amount of free space after the MBR */ 280 blocks = map_free(1LL, 0LL); 281 if (blocks == 0LL) { 282 warnx("%s: error: no room for the GPT header", device_name); 283 return; 284 } 285 286 /* Don't create more than parts entries. */ 287 if ((uint64_t)(blocks - 1) * secsz > parts * sizeof(struct gpt_ent)) { 288 blocks = (parts * sizeof(struct gpt_ent)) / secsz; 289 if ((parts * sizeof(struct gpt_ent)) % secsz) 290 blocks++; 291 blocks++; /* Don't forget the header itself */ 292 } 293 294 /* Never cross the median of the device. */ 295 if ((blocks + 1LL) > ((last + 1LL) >> 1)) 296 blocks = ((last + 1LL) >> 1) - 1LL; 297 298 /* 299 * Get the amount of free space at the end of the device and 300 * calculate the size for the GPT structures. 301 */ 302 map = map_last(); 303 if (map->map_type != MAP_TYPE_UNUSED) { 304 warnx("%s: error: no room for the backup header", device_name); 305 return; 306 } 307 308 if (map->map_size < blocks) 309 blocks = map->map_size; 310 if (blocks == 1LL) { 311 warnx("%s: error: no room for the GPT table", device_name); 312 return; 313 } 314 315 blocks--; /* Number of blocks in the GPT table. */ 316 gpt = map_add(1LL, 1LL, MAP_TYPE_PRI_GPT_HDR, calloc(1, secsz)); 317 tbl = map_add(2LL, blocks, MAP_TYPE_PRI_GPT_TBL, 318 calloc(blocks, secsz)); 319 if (gpt == NULL || tbl == NULL) 320 return; 321 322 lbt = map_add(last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL, 323 tbl->map_data); 324 tpg = map_add(last, 1LL, MAP_TYPE_SEC_GPT_HDR, calloc(1, secsz)); 325 326 hdr = gpt->map_data; 327 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)); 328 hdr->hdr_revision = htole32(GPT_HDR_REVISION); 329 /* 330 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus 331 * contains padding we must not include in the size. 332 */ 333 hdr->hdr_size = htole32(GPT_HDR_SIZE); 334 hdr->hdr_lba_self = htole64(gpt->map_start); 335 hdr->hdr_lba_alt = htole64(tpg->map_start); 336 hdr->hdr_lba_start = htole64(tbl->map_start + blocks); 337 hdr->hdr_lba_end = htole64(lbt->map_start - 1LL); 338 gpt_uuid_generate(hdr->hdr_guid); 339 hdr->hdr_lba_table = htole64(tbl->map_start); 340 hdr->hdr_entries = htole32((blocks * secsz) / sizeof(struct gpt_ent)); 341 if (le32toh(hdr->hdr_entries) > parts) 342 hdr->hdr_entries = htole32(parts); 343 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent)); 344 345 ent = tbl->map_data; 346 for (i = 0; i < le32toh(hdr->hdr_entries); i++) { 347 gpt_uuid_generate(ent[i].ent_guid); 348 } 349 350 /* Mirror partitions. */ 351 for (i = 0; i < 4; i++) { 352 start = le16toh(mbr->mbr_part[i].part_start_hi); 353 start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo); 354 size = le16toh(mbr->mbr_part[i].part_size_hi); 355 size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo); 356 357 switch (mbr->mbr_part[i].part_typ) { 358 case MBR_PTYPE_UNUSED: 359 continue; 360 case MBR_PTYPE_386BSD: { /* FreeBSD */ 361 if (slice) { 362 gpt_uuid_create(GPT_TYPE_FREEBSD, 363 ent->ent_type, ent->ent_name, 364 sizeof(ent->ent_name)); 365 ent->ent_lba_start = htole64((uint64_t)start); 366 ent->ent_lba_end = htole64(start + size - 1LL); 367 ent++; 368 } else 369 ent = migrate_disklabel(fd, start, ent); 370 break; 371 } 372 case MBR_PTYPE_NETBSD: 373 ent = migrate_netbsd_disklabel(fd, start, ent); 374 break; 375 case MBR_PTYPE_EFI: { 376 gpt_uuid_create(GPT_TYPE_EFI, 377 ent->ent_type, ent->ent_name, 378 sizeof(ent->ent_name)); 379 ent->ent_lba_start = htole64((uint64_t)start); 380 ent->ent_lba_end = htole64(start + size - 1LL); 381 ent++; 382 break; 383 } 384 default: 385 if (!force) { 386 warnx("%s: error: unknown partition type (%d)", 387 device_name, mbr->mbr_part[i].part_typ); 388 return; 389 } 390 } 391 } 392 ent = tbl->map_data; 393 394 hdr->hdr_crc_table = htole32(crc32(ent, le32toh(hdr->hdr_entries) * 395 le32toh(hdr->hdr_entsz))); 396 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); 397 398 gpt_write(fd, gpt); 399 gpt_write(fd, tbl); 400 401 /* 402 * Create backup GPT. 403 */ 404 memcpy(tpg->map_data, gpt->map_data, secsz); 405 hdr = tpg->map_data; 406 hdr->hdr_lba_self = htole64(tpg->map_start); 407 hdr->hdr_lba_alt = htole64(gpt->map_start); 408 hdr->hdr_lba_table = htole64(lbt->map_start); 409 hdr->hdr_crc_self = 0; /* Don't ever forget this! */ 410 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); 411 412 gpt_write(fd, lbt); 413 gpt_write(fd, tpg); 414 415 map = map_find(MAP_TYPE_MBR); 416 mbr = map->map_data; 417 /* 418 * Turn the MBR into a Protective MBR. 419 */ 420 memset(mbr->mbr_part, 0, sizeof(mbr->mbr_part)); 421 mbr->mbr_part[0].part_shd = 0x00; 422 mbr->mbr_part[0].part_ssect = 0x02; 423 mbr->mbr_part[0].part_scyl = 0x00; 424 mbr->mbr_part[0].part_typ = MBR_PTYPE_PMBR; 425 mbr->mbr_part[0].part_ehd = 0xfe; 426 mbr->mbr_part[0].part_esect = 0xff; 427 mbr->mbr_part[0].part_ecyl = 0xff; 428 mbr->mbr_part[0].part_start_lo = htole16(1); 429 if (last > 0xffffffff) { 430 mbr->mbr_part[0].part_size_lo = htole16(0xffff); 431 mbr->mbr_part[0].part_size_hi = htole16(0xffff); 432 } else { 433 mbr->mbr_part[0].part_size_lo = htole16(last); 434 mbr->mbr_part[0].part_size_hi = htole16(last >> 16); 435 } 436 gpt_write(fd, map); 437 } 438 439 int 440 cmd_migrate(int argc, char *argv[]) 441 { 442 int ch, fd; 443 444 /* Get the migrate options */ 445 while ((ch = getopt(argc, argv, "fs")) != -1) { 446 switch(ch) { 447 case 'f': 448 force = 1; 449 break; 450 case 's': 451 slice = 1; 452 break; 453 default: 454 usage_migrate(); 455 } 456 } 457 458 if (argc == optind) 459 usage_migrate(); 460 461 while (optind < argc) { 462 fd = gpt_open(argv[optind++]); 463 if (fd == -1) { 464 warn("unable to open device '%s'", device_name); 465 continue; 466 } 467 468 migrate(fd); 469 470 gpt_close(fd); 471 } 472 473 return (0); 474 } 475