1/*	$OpenBSD: fdisk.c,v 1.146 2022/07/17 12:53:19 krw Exp $	*/
2
3/*
4 * Copyright (c) 1997 Tobias Weingartner
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/disklabel.h>
21
22#include <ctype.h>
23#include <err.h>
24#include <fcntl.h>
25#include <paths.h>
26#include <stdint.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#include "part.h"
33#include "disk.h"
34#include "mbr.h"
35#include "cmd.h"
36#include "misc.h"
37#include "user.h"
38#include "gpt.h"
39
40#define	INIT_GPT		1
41#define	INIT_GPTPARTITIONS	2
42#define	INIT_MBR		3
43#define	INIT_MBRBOOTCODE	4
44
45#define	_PATH_MBR		_PATH_BOOTDIR "mbr"
46
47int			y_flag;
48
49void			parse_bootprt(const char *);
50void			get_default_dmbr(const char *);
51
52static void
53usage(void)
54{
55	extern char		* __progname;
56
57	fprintf(stderr, "usage: %s "
58	    "[-evy] [-A | -g | -i | -u] [-b blocks[@offset[:type]]]\n"
59	    "\t[-l blocks | -c cylinders -h heads -s sectors] [-f file] "
60	    "disk\n", __progname);
61	exit(1);
62}
63
64int
65main(int argc, char *argv[])
66{
67	struct mbr		 mbr;
68	const char		*mbrfile = NULL;
69	const char		*errstr;
70	int			 ch;
71	int			 e_flag = 0, init = 0;
72	int			 verbosity = TERSE;
73	int			 oflags = O_RDONLY;
74
75	while ((ch = getopt(argc, argv, "Ab:c:ef:gh:il:s:uvy")) != -1) {
76		switch(ch) {
77		case 'A':
78			init = INIT_GPTPARTITIONS;
79			break;
80		case 'b':
81			parse_bootprt(optarg);
82			if (disk.dk_bootprt.prt_id != DOSPTYP_EFISYS)
83				disk.dk_bootprt.prt_flag = DOSACTIVE;
84			break;
85		case 'c':
86			disk.dk_cylinders = strtonum(optarg, 1, 262144, &errstr);
87			if (errstr)
88				errx(1, "Cylinder argument %s [1..262144].",
89				    errstr);
90			disk.dk_size = 0;
91			break;
92		case 'e':
93			e_flag = 1;
94			break;
95		case 'f':
96			mbrfile = optarg;
97			break;
98		case 'g':
99			init = INIT_GPT;
100			break;
101		case 'h':
102			disk.dk_heads = strtonum(optarg, 1, 256, &errstr);
103			if (errstr)
104				errx(1, "Head argument %s [1..256].", errstr);
105			disk.dk_size = 0;
106			break;
107		case 'i':
108			init = INIT_MBR;
109			break;
110		case 'l':
111			disk.dk_size = strtonum(optarg, BLOCKALIGNMENT,
112			    UINT32_MAX, &errstr);
113			if (errstr)
114				errx(1, "Block argument %s [%u..%u].", errstr,
115				    BLOCKALIGNMENT, UINT32_MAX);
116			disk.dk_cylinders = disk.dk_heads = disk.dk_sectors = 0;
117			break;
118		case 's':
119			disk.dk_sectors = strtonum(optarg, 1, 63, &errstr);
120			if (errstr)
121				errx(1, "Sector argument %s [1..63].", errstr);
122			disk.dk_size = 0;
123			break;
124		case 'u':
125			init = INIT_MBRBOOTCODE;
126			break;
127		case 'v':
128			verbosity = VERBOSE;
129			break;
130		case 'y':
131			y_flag = 1;
132			break;
133		default:
134			usage();
135		}
136	}
137	argc -= optind;
138	argv += optind;
139
140	if (argc != 1)
141		usage();
142
143	if ((disk.dk_cylinders || disk.dk_heads || disk.dk_sectors) &&
144	    (disk.dk_cylinders * disk.dk_heads * disk.dk_sectors == 0))
145		usage();
146
147	if (init || e_flag)
148		oflags = O_RDWR;
149
150	get_default_dmbr(mbrfile);
151
152	DISK_open(argv[0], oflags);
153	if (oflags == O_RDONLY) {
154		if (pledge("stdio", NULL) == -1)
155			err(1, "pledge");
156		USER_print_disk(verbosity);
157		goto done;
158	}
159
160	/*
161	 * "stdio" to talk to the outside world.
162	 * "proc exec" for man page display.
163	 * "disklabel" for DIOCRLDINFO.
164	 */
165	if (pledge("stdio disklabel proc exec", NULL) == -1)
166		err(1, "pledge");
167
168	switch (init) {
169	case INIT_GPT:
170		if (GPT_init(GHANDGP))
171			errx(1, "-g could not create valid GPT");
172		if (ask_yn("Do you wish to write new GPT?"))
173			Xwrite(NULL, &gmbr);
174		break;
175	case INIT_GPTPARTITIONS:
176		if (GPT_read(ANYGPT))
177			errx(1, "-A requires a valid GPT");
178		if (GPT_init(GPONLY))
179			errx(1, "-A could not create valid GPT");
180		if (ask_yn("Do you wish to write new GPT?"))
181			Xwrite(NULL, &gmbr);
182		break;
183	case INIT_MBR:
184		mbr.mbr_lba_self = mbr.mbr_lba_firstembr = 0;
185		MBR_init(&mbr);
186		if (ask_yn("Do you wish to write new MBR?"))
187			Xwrite(NULL, &mbr);
188		break;
189	case INIT_MBRBOOTCODE:
190		if (GPT_read(ANYGPT) == 0)
191			errx(1, "-u not available for GPT");
192		if (MBR_read(0, 0, &mbr))
193			errx(1, "Can't read MBR!");
194		memcpy(mbr.mbr_code, default_dmbr.dmbr_boot,
195		    sizeof(mbr.mbr_code));
196		if (ask_yn("Do you wish to write new MBR?"))
197			Xwrite(NULL, &mbr);
198		break;
199	default:
200		break;
201	}
202
203	if (e_flag)
204		USER_edit(0, 0);
205
206done:
207	close(disk.dk_fd);
208
209	return 0;
210}
211
212void
213parse_bootprt(const char *arg)
214{
215	const char		*errstr;
216	char			*poffset, *ptype;
217	int			 partitiontype;
218	uint32_t		 blockcount, blockoffset;
219
220	blockoffset = BLOCKALIGNMENT;
221	partitiontype = DOSPTYP_EFISYS;
222	ptype = NULL;
223
224	/* First number: # of 512-byte blocks in boot partition. */
225	poffset = strchr(arg, '@');
226	if (poffset != NULL)
227		*poffset++ = '\0';
228	if (poffset != NULL) {
229		ptype = strchr(poffset, ':');
230		if (ptype != NULL)
231			*ptype++ = '\0';
232	}
233
234	blockcount = strtonum(arg, 1, UINT32_MAX, &errstr);
235	if (errstr)
236		errx(1, "Block argument %s [%u..%u].", errstr, 1, UINT32_MAX);
237
238	if (poffset == NULL)
239		goto done;
240
241	/* Second number: # of 512-byte blocks to offset partition start. */
242	blockoffset = strtonum(poffset, 1, UINT32_MAX, &errstr);
243	if (errstr)
244		errx(1, "Block offset argument %s [%u..%u].", errstr, 1,
245		    UINT32_MAX);
246
247	if (ptype == NULL)
248		goto done;
249
250	partitiontype = hex_octet(ptype);
251	if (partitiontype == -1)
252		errx(1, "Block type is not a 1-2 digit hex value");
253
254 done:
255	disk.dk_bootprt.prt_ns = blockcount;
256	disk.dk_bootprt.prt_bs = blockoffset;
257	disk.dk_bootprt.prt_id = partitiontype;
258}
259
260void
261get_default_dmbr(const char *mbrfile)
262{
263	struct dos_mbr		*dmbr = &default_dmbr;
264	ssize_t			 len, sz;
265	int			 fd;
266
267	if (mbrfile == NULL)
268#ifdef HAS_MBR
269		mbrfile = _PATH_MBR;
270#else
271		return;
272#endif
273
274	fd = open(mbrfile, O_RDONLY);
275	if (fd == -1)
276		err(1, "%s", mbrfile);
277
278	sz = sizeof(*dmbr);
279	len = read(fd, dmbr, sz);
280	if (len == -1)
281		err(1, "read('%s')", mbrfile);
282	else if (len != sz)
283		errx(1, "read('%s'): read %zd bytes of %zd", mbrfile, len, sz);
284
285	close(fd);
286}
287