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