xref: /dragonfly/sbin/gpt/create.c (revision de78d61c)
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