1 /*- 2 * Copyright (c) 2002 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * 'init' directive added by Matthew Dillon. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $FreeBSD: src/sbin/gpt/create.c,v 1.11 2005/08/31 01:47:19 marcel Exp $ 29 * $DragonFly: src/sbin/gpt/create.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ 30 */ 31 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/errno.h> 35 #include <sys/param.h> 36 #include <bus/cam/scsi/scsi_daio.h> 37 38 #include <err.h> 39 #include <stddef.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <stdarg.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include "map.h" 47 #include "gpt.h" 48 49 static int force; 50 static int primary_only; 51 52 static void do_erase(int fd); 53 54 static void 55 usage_create(void) 56 { 57 fprintf(stderr, "usage: %s [-fp] device ...\n", getprogname()); 58 exit(1); 59 } 60 61 static void 62 usage_init(void) 63 { 64 fprintf(stderr, "usage: %s -f [-B] [-E] device ...\n", getprogname()); 65 fprintf(stderr, "\tnote: -f is mandatory for this command\n"); 66 exit(1); 67 } 68 69 static void 70 create(int fd) 71 { 72 uuid_t uuid; 73 off_t blocks, last; 74 map_t *gpt, *tpg; 75 map_t *tbl, *lbt; 76 map_t *map; 77 struct mbr *mbr; 78 struct gpt_hdr *hdr; 79 struct gpt_ent *ent; 80 unsigned int i; 81 82 last = mediasz / secsz - 1LL; 83 84 if (map_find(MAP_TYPE_PRI_GPT_HDR) != NULL || 85 map_find(MAP_TYPE_SEC_GPT_HDR) != NULL) { 86 warnx("%s: error: device already contains a GPT", device_name); 87 return; 88 } 89 map = map_find(MAP_TYPE_MBR); 90 if (map != NULL) { 91 if (!force) { 92 warnx("%s: error: device contains a MBR", device_name); 93 return; 94 } 95 96 /* Nuke the MBR in our internal map. */ 97 map->map_type = MAP_TYPE_UNUSED; 98 } 99 100 /* 101 * Create PMBR. 102 */ 103 if (map_find(MAP_TYPE_PMBR) == NULL) { 104 if (map_free(0LL, 1LL) == 0) { 105 warnx("%s: error: no room for the PMBR", device_name); 106 return; 107 } 108 mbr = gpt_read(fd, 0LL, 1); 109 bzero(mbr, sizeof(*mbr)); 110 mbr->mbr_sig = htole16(MBR_SIG); 111 mbr->mbr_part[0].part_shd = 0xff; 112 mbr->mbr_part[0].part_ssect = 0xff; 113 mbr->mbr_part[0].part_scyl = 0xff; 114 mbr->mbr_part[0].part_typ = 0xee; 115 mbr->mbr_part[0].part_ehd = 0xff; 116 mbr->mbr_part[0].part_esect = 0xff; 117 mbr->mbr_part[0].part_ecyl = 0xff; 118 mbr->mbr_part[0].part_start_lo = htole16(1); 119 if (last > 0xffffffff) { 120 mbr->mbr_part[0].part_size_lo = htole16(0xffff); 121 mbr->mbr_part[0].part_size_hi = htole16(0xffff); 122 } else { 123 mbr->mbr_part[0].part_size_lo = htole16(last); 124 mbr->mbr_part[0].part_size_hi = htole16(last >> 16); 125 } 126 map = map_add(0LL, 1LL, MAP_TYPE_PMBR, mbr); 127 gpt_write(fd, map); 128 } 129 130 /* Get the amount of free space after the MBR */ 131 blocks = map_free(1LL, 0LL); 132 if (blocks == 0LL) { 133 warnx("%s: error: no room for the GPT header", device_name); 134 return; 135 } 136 137 /* Don't create more than parts entries. */ 138 if ((uint64_t)(blocks - 1) * secsz > parts * sizeof(struct gpt_ent)) { 139 blocks = (parts * sizeof(struct gpt_ent)) / secsz; 140 if ((parts * sizeof(struct gpt_ent)) % secsz) 141 blocks++; 142 blocks++; /* Don't forget the header itself */ 143 } 144 145 /* Never cross the median of the device. */ 146 if ((blocks + 1LL) > ((last + 1LL) >> 1)) 147 blocks = ((last + 1LL) >> 1) - 1LL; 148 149 /* 150 * Get the amount of free space at the end of the device and 151 * calculate the size for the GPT structures. 152 */ 153 map = map_last(); 154 if (map->map_type != MAP_TYPE_UNUSED) { 155 warnx("%s: error: no room for the backup header", device_name); 156 return; 157 } 158 159 if (map->map_size < blocks) 160 blocks = map->map_size; 161 if (blocks == 1LL) { 162 warnx("%s: error: no room for the GPT table", device_name); 163 return; 164 } 165 166 blocks--; /* Number of blocks in the GPT table. */ 167 gpt = map_add(1LL, 1LL, MAP_TYPE_PRI_GPT_HDR, calloc(1, secsz)); 168 tbl = map_add(2LL, blocks, MAP_TYPE_PRI_GPT_TBL, 169 calloc(blocks, secsz)); 170 if (gpt == NULL || tbl == NULL) 171 return; 172 173 hdr = gpt->map_data; 174 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)); 175 hdr->hdr_revision = htole32(GPT_HDR_REVISION); 176 /* 177 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus 178 * contains padding we must not include in the size. 179 */ 180 hdr->hdr_size = htole32(offsetof(struct gpt_hdr, padding)); 181 hdr->hdr_lba_self = htole64(gpt->map_start); 182 hdr->hdr_lba_alt = htole64(last); 183 hdr->hdr_lba_start = htole64(tbl->map_start + blocks); 184 hdr->hdr_lba_end = htole64(last - blocks - 1LL); 185 uuid_create(&uuid, NULL); 186 le_uuid_enc(&hdr->hdr_uuid, &uuid); 187 hdr->hdr_lba_table = htole64(tbl->map_start); 188 hdr->hdr_entries = htole32((blocks * secsz) / sizeof(struct gpt_ent)); 189 if (le32toh(hdr->hdr_entries) > parts) 190 hdr->hdr_entries = htole32(parts); 191 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent)); 192 193 ent = tbl->map_data; 194 for (i = 0; i < le32toh(hdr->hdr_entries); i++) { 195 uuid_create(&uuid, NULL); 196 le_uuid_enc(&ent[i].ent_uuid, &uuid); 197 } 198 199 hdr->hdr_crc_table = htole32(crc32(ent, le32toh(hdr->hdr_entries) * 200 le32toh(hdr->hdr_entsz))); 201 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); 202 203 gpt_write(fd, gpt); 204 gpt_write(fd, tbl); 205 206 /* 207 * Create backup GPT if the user didn't suppress it. 208 */ 209 if (!primary_only) { 210 tpg = map_add(last, 1LL, MAP_TYPE_SEC_GPT_HDR, 211 calloc(1, secsz)); 212 lbt = map_add(last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL, 213 tbl->map_data); 214 memcpy(tpg->map_data, gpt->map_data, secsz); 215 hdr = tpg->map_data; 216 hdr->hdr_lba_self = htole64(tpg->map_start); 217 hdr->hdr_lba_alt = htole64(gpt->map_start); 218 hdr->hdr_lba_table = htole64(lbt->map_start); 219 hdr->hdr_crc_self = 0; /* Don't ever forget this! */ 220 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); 221 gpt_write(fd, lbt); 222 gpt_write(fd, tpg); 223 } 224 } 225 226 static 227 void 228 dosys(const char *ctl, ...) 229 { 230 va_list va; 231 char *scmd; 232 233 va_start(va, ctl); 234 vasprintf(&scmd, ctl, va); 235 va_end(va); 236 printf("%s\n", scmd); 237 if (system(scmd) != 0) { 238 fprintf(stderr, "Execution failed\n"); 239 free(scmd); 240 exit(1); 241 } 242 free(scmd); 243 } 244 245 int 246 cmd_create(int argc, char *argv[]) 247 { 248 int ch, fd; 249 250 while ((ch = getopt(argc, argv, "fp")) != -1) { 251 switch(ch) { 252 case 'f': 253 force = 1; 254 break; 255 case 'p': 256 primary_only = 1; 257 break; 258 default: 259 usage_create(); 260 } 261 } 262 263 if (argc == optind) 264 usage_create(); 265 266 while (optind < argc) { 267 fd = gpt_open(argv[optind++]); 268 if (fd == -1) { 269 warn("unable to open device '%s'", device_name); 270 continue; 271 } 272 273 create(fd); 274 275 gpt_close(fd); 276 } 277 278 return (0); 279 } 280 281 /* 282 * init 283 * 284 * Create a fresh gpt partition table and populate it with reasonable 285 * defaults. 286 */ 287 int 288 cmd_init(int argc, char *argv[]) 289 { 290 int ch, fd; 291 int with_boot = 0; 292 int with_trim = 0; 293 294 while ((ch = getopt(argc, argv, "fBEI")) != -1) { 295 switch(ch) { 296 case 'f': 297 force = 1; 298 break; 299 case 'I': 300 fprintf(stderr, "Maybe you were trying to supply " 301 "fdisk options. This is the gpt " 302 "program\n"); 303 usage_init(); 304 /* NOT REACHED */ 305 break; 306 case 'E': 307 with_trim = 1; 308 break; 309 case 'B': 310 with_boot = 1; 311 break; 312 default: 313 usage_init(); 314 /* NOT REACHED */ 315 break; 316 } 317 } 318 319 if (argc == optind) { 320 usage_init(); 321 /* NOT REACHED */ 322 } 323 324 while (optind < argc) { 325 const char *path = argv[optind++]; 326 char *slice0; 327 char *slice1; 328 329 /* 330 * Destroy and [re]Create the gpt 331 */ 332 fd = gpt_open(path); 333 if (fd == -1) { 334 warn("unable to open device '%s'", path); 335 continue; 336 } 337 if (with_trim) { 338 do_erase(fd); 339 gpt_close(fd); 340 sleep(1); 341 fd = gpt_open(path); 342 if (fd == -1) { 343 warn("unable to reopen device '%s'", path); 344 continue; 345 } 346 } 347 do_destroy(fd); 348 gpt_close(fd); 349 sleep(1); 350 351 fd = gpt_open(path); 352 if (fd == -1) { 353 warn("unable to open device '%s'", device_name); 354 continue; 355 } 356 create(fd); 357 add_defaults(fd); 358 gpt_close(fd); 359 sleep(1); 360 361 /* 362 * Setup slices 363 */ 364 if (strstr(device_name, "serno")) 365 asprintf(&slice0, "%s.s0", device_name); 366 else 367 asprintf(&slice0, "%ss0", device_name); 368 369 if (strstr(device_name, "serno")) 370 asprintf(&slice1, "%s.s1", device_name); 371 else 372 asprintf(&slice1, "%ss1", device_name); 373 374 /* 375 * Label slice1 376 */ 377 dosys("disklabel -r -w %s %s auto", 378 (with_boot ? "-B" : ""), 379 slice1); 380 sleep(1); 381 382 /* 383 * newfs_msdos slice0 384 */ 385 dosys("newfs_msdos %s", slice0); 386 387 /* 388 * If asked to setup an EFI boot, mount the dos partition 389 * and copy a file. 390 * 391 * We do not setup the dragonfly disklabel with -B 392 */ 393 if (with_boot) { 394 char *mtpt; 395 396 srandomdev(); 397 asprintf(&mtpt, "/tmp/gpt%08x%08x", 398 (int)random(), (int)random()); 399 if (mkdir(mtpt, 0700) < 0) { 400 fprintf(stderr, "cannot mkdir %s\n", mtpt); 401 exit(1); 402 } 403 404 dosys("mount_msdos %s %s", slice0, mtpt); 405 dosys("mkdir -p %s/efi/boot", mtpt); 406 dosys("cpdup /boot/boot1.efi %s/efi/boot/bootx64.efi", 407 mtpt); 408 dosys("umount %s", mtpt); 409 } 410 411 free(slice0); 412 free(slice1); 413 } 414 415 return (0); 416 } 417 418 static void 419 do_erase(int fd) 420 { 421 off_t ioarg[2]; 422 423 ioarg[0] = 0; 424 ioarg[1] = mediasz; 425 426 if (ioctl(fd, DAIOCTRIM, ioarg) < 0) { 427 printf("Trim error %s\n", strerror(errno)); 428 printf("Continuing\n"); 429 } else { 430 printf("Trim completed ok\n"); 431 } 432 } 433