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 35 #include <err.h> 36 #include <stddef.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <stdarg.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include "map.h" 44 #include "gpt.h" 45 46 static int force; 47 static int primary_only; 48 49 static void 50 usage_create(void) 51 { 52 fprintf(stderr, "usage: %s [-fp] device ...\n", getprogname()); 53 exit(1); 54 } 55 56 static void 57 usage_init(void) 58 { 59 fprintf(stderr, "usage: %s -f [-B] device ...\n", getprogname()); 60 fprintf(stderr, "\tnote: -f is mandatory for this command\n"); 61 exit(1); 62 } 63 64 static void 65 create(int fd) 66 { 67 uuid_t uuid; 68 off_t blocks, last; 69 map_t *gpt, *tpg; 70 map_t *tbl, *lbt; 71 map_t *map; 72 struct mbr *mbr; 73 struct gpt_hdr *hdr; 74 struct gpt_ent *ent; 75 unsigned int i; 76 77 last = mediasz / secsz - 1LL; 78 79 if (map_find(MAP_TYPE_PRI_GPT_HDR) != NULL || 80 map_find(MAP_TYPE_SEC_GPT_HDR) != NULL) { 81 warnx("%s: error: device already contains a GPT", device_name); 82 return; 83 } 84 map = map_find(MAP_TYPE_MBR); 85 if (map != NULL) { 86 if (!force) { 87 warnx("%s: error: device contains a MBR", device_name); 88 return; 89 } 90 91 /* Nuke the MBR in our internal map. */ 92 map->map_type = MAP_TYPE_UNUSED; 93 } 94 95 /* 96 * Create PMBR. 97 */ 98 if (map_find(MAP_TYPE_PMBR) == NULL) { 99 if (map_free(0LL, 1LL) == 0) { 100 warnx("%s: error: no room for the PMBR", device_name); 101 return; 102 } 103 mbr = gpt_read(fd, 0LL, 1); 104 bzero(mbr, sizeof(*mbr)); 105 mbr->mbr_sig = htole16(MBR_SIG); 106 mbr->mbr_part[0].part_shd = 0xff; 107 mbr->mbr_part[0].part_ssect = 0xff; 108 mbr->mbr_part[0].part_scyl = 0xff; 109 mbr->mbr_part[0].part_typ = 0xee; 110 mbr->mbr_part[0].part_ehd = 0xff; 111 mbr->mbr_part[0].part_esect = 0xff; 112 mbr->mbr_part[0].part_ecyl = 0xff; 113 mbr->mbr_part[0].part_start_lo = htole16(1); 114 if (last > 0xffffffff) { 115 mbr->mbr_part[0].part_size_lo = htole16(0xffff); 116 mbr->mbr_part[0].part_size_hi = htole16(0xffff); 117 } else { 118 mbr->mbr_part[0].part_size_lo = htole16(last); 119 mbr->mbr_part[0].part_size_hi = htole16(last >> 16); 120 } 121 map = map_add(0LL, 1LL, MAP_TYPE_PMBR, mbr); 122 gpt_write(fd, map); 123 } 124 125 /* Get the amount of free space after the MBR */ 126 blocks = map_free(1LL, 0LL); 127 if (blocks == 0LL) { 128 warnx("%s: error: no room for the GPT header", device_name); 129 return; 130 } 131 132 /* Don't create more than parts entries. */ 133 if ((uint64_t)(blocks - 1) * secsz > parts * sizeof(struct gpt_ent)) { 134 blocks = (parts * sizeof(struct gpt_ent)) / secsz; 135 if ((parts * sizeof(struct gpt_ent)) % secsz) 136 blocks++; 137 blocks++; /* Don't forget the header itself */ 138 } 139 140 /* Never cross the median of the device. */ 141 if ((blocks + 1LL) > ((last + 1LL) >> 1)) 142 blocks = ((last + 1LL) >> 1) - 1LL; 143 144 /* 145 * Get the amount of free space at the end of the device and 146 * calculate the size for the GPT structures. 147 */ 148 map = map_last(); 149 if (map->map_type != MAP_TYPE_UNUSED) { 150 warnx("%s: error: no room for the backup header", device_name); 151 return; 152 } 153 154 if (map->map_size < blocks) 155 blocks = map->map_size; 156 if (blocks == 1LL) { 157 warnx("%s: error: no room for the GPT table", device_name); 158 return; 159 } 160 161 blocks--; /* Number of blocks in the GPT table. */ 162 gpt = map_add(1LL, 1LL, MAP_TYPE_PRI_GPT_HDR, calloc(1, secsz)); 163 tbl = map_add(2LL, blocks, MAP_TYPE_PRI_GPT_TBL, 164 calloc(blocks, secsz)); 165 if (gpt == NULL || tbl == NULL) 166 return; 167 168 hdr = gpt->map_data; 169 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)); 170 hdr->hdr_revision = htole32(GPT_HDR_REVISION); 171 /* 172 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus 173 * contains padding we must not include in the size. 174 */ 175 hdr->hdr_size = htole32(offsetof(struct gpt_hdr, padding)); 176 hdr->hdr_lba_self = htole64(gpt->map_start); 177 hdr->hdr_lba_alt = htole64(last); 178 hdr->hdr_lba_start = htole64(tbl->map_start + blocks); 179 hdr->hdr_lba_end = htole64(last - blocks - 1LL); 180 uuid_create(&uuid, NULL); 181 le_uuid_enc(&hdr->hdr_uuid, &uuid); 182 hdr->hdr_lba_table = htole64(tbl->map_start); 183 hdr->hdr_entries = htole32((blocks * secsz) / sizeof(struct gpt_ent)); 184 if (le32toh(hdr->hdr_entries) > parts) 185 hdr->hdr_entries = htole32(parts); 186 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent)); 187 188 ent = tbl->map_data; 189 for (i = 0; i < le32toh(hdr->hdr_entries); i++) { 190 uuid_create(&uuid, NULL); 191 le_uuid_enc(&ent[i].ent_uuid, &uuid); 192 } 193 194 hdr->hdr_crc_table = htole32(crc32(ent, le32toh(hdr->hdr_entries) * 195 le32toh(hdr->hdr_entsz))); 196 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); 197 198 gpt_write(fd, gpt); 199 gpt_write(fd, tbl); 200 201 /* 202 * Create backup GPT if the user didn't suppress it. 203 */ 204 if (!primary_only) { 205 tpg = map_add(last, 1LL, MAP_TYPE_SEC_GPT_HDR, 206 calloc(1, secsz)); 207 lbt = map_add(last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL, 208 tbl->map_data); 209 memcpy(tpg->map_data, gpt->map_data, secsz); 210 hdr = tpg->map_data; 211 hdr->hdr_lba_self = htole64(tpg->map_start); 212 hdr->hdr_lba_alt = htole64(gpt->map_start); 213 hdr->hdr_lba_table = htole64(lbt->map_start); 214 hdr->hdr_crc_self = 0; /* Don't ever forget this! */ 215 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); 216 gpt_write(fd, lbt); 217 gpt_write(fd, tpg); 218 } 219 } 220 221 static 222 void 223 dosys(const char *ctl, ...) 224 { 225 va_list va; 226 char *scmd; 227 228 va_start(va, ctl); 229 vasprintf(&scmd, ctl, va); 230 va_end(va); 231 printf("%s\n", scmd); 232 if (system(scmd) != 0) { 233 fprintf(stderr, "Execution failed\n"); 234 free(scmd); 235 exit(1); 236 } 237 free(scmd); 238 } 239 240 int 241 cmd_create(int argc, char *argv[]) 242 { 243 int ch, fd; 244 245 while ((ch = getopt(argc, argv, "fp")) != -1) { 246 switch(ch) { 247 case 'f': 248 force = 1; 249 break; 250 case 'p': 251 primary_only = 1; 252 break; 253 default: 254 usage_create(); 255 } 256 } 257 258 if (argc == optind) 259 usage_create(); 260 261 while (optind < argc) { 262 fd = gpt_open(argv[optind++]); 263 if (fd == -1) { 264 warn("unable to open device '%s'", device_name); 265 continue; 266 } 267 268 create(fd); 269 270 gpt_close(fd); 271 } 272 273 return (0); 274 } 275 276 /* 277 * init 278 * 279 * Create a fresh gpt partition table and populate it with reasonable 280 * defaults. 281 */ 282 int 283 cmd_init(int argc, char *argv[]) 284 { 285 int ch, fd; 286 int with_boot = 0; 287 288 while ((ch = getopt(argc, argv, "fIB")) != -1) { 289 switch(ch) { 290 case 'f': 291 force = 1; 292 break; 293 case 'I': 294 fprintf(stderr, "Maybe you were trying to supply " 295 "fdisk options. This is the gpt " 296 "program\n"); 297 usage_init(); 298 /* NOT REACHED */ 299 break; 300 case 'B': 301 with_boot = 1; 302 break; 303 default: 304 usage_init(); 305 /* NOT REACHED */ 306 break; 307 } 308 } 309 310 if (argc == optind) { 311 usage_init(); 312 /* NOT REACHED */ 313 } 314 315 while (optind < argc) { 316 const char *path = argv[optind++]; 317 char *slice0; 318 char *slice1; 319 320 /* 321 * Destroy and [re]Create the gpt 322 */ 323 fd = gpt_open(path); 324 if (fd == -1) { 325 warn("unable to open device '%s'", device_name); 326 continue; 327 } 328 do_destroy(fd); 329 gpt_close(fd); 330 331 fd = gpt_open(path); 332 if (fd == -1) { 333 warn("unable to open device '%s'", device_name); 334 continue; 335 } 336 create(fd); 337 add_defaults(fd); 338 gpt_close(fd); 339 340 /* 341 * Setup slices 342 */ 343 if (strstr(device_name, "serno")) 344 asprintf(&slice0, "%s.s0", device_name); 345 else 346 asprintf(&slice0, "%ss0", device_name); 347 348 if (strstr(device_name, "serno")) 349 asprintf(&slice1, "%s.s1", device_name); 350 else 351 asprintf(&slice1, "%ss1", device_name); 352 353 /* 354 * Label slice1 355 */ 356 sleep(1); 357 dosys("disklabel -r -w %s %s auto", 358 (with_boot ? "-B" : ""), 359 slice1); 360 361 /* 362 * newfs_msdos slice0 363 */ 364 dosys("newfs_msdos %s", slice0); 365 366 /* 367 * If asked to setup an EFI boot, mount the dos partition 368 * and copy a file. 369 * 370 * We do not setup the dragonfly disklabel with -B 371 */ 372 if (with_boot) { 373 char *mtpt; 374 375 srandomdev(); 376 asprintf(&mtpt, "/tmp/gpt%08x%08x", 377 (int)random(), (int)random()); 378 if (mkdir(mtpt, 0700) < 0) { 379 fprintf(stderr, "cannot mkdir %s\n", mtpt); 380 exit(1); 381 } 382 383 dosys("mount_msdos %s %s", slice0, mtpt); 384 dosys("mkdir -p %s/efi/boot", mtpt); 385 dosys("cpdup /boot/boot1.efi %s/efi/boot/bootx64.efi", 386 mtpt); 387 dosys("umount %s", mtpt); 388 } 389 390 free(slice0); 391 free(slice1); 392 } 393 394 return (0); 395 } 396