xref: /openbsd-src/sbin/fdisk/fdisk.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*	$OpenBSD: fdisk.c,v 1.140 2021/10/19 19:38:10 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 
47 int			y_flag;
48 
49 void			parse_bootprt(const char *);
50 void			get_default_dmbr(const char *);
51 
52 static void
53 usage(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 mbrfile] disk\n",
60 	    __progname);
61 	exit(1);
62 }
63 
64 int
65 main(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 			break;
83 		case 'c':
84 			disk.dk_cylinders = strtonum(optarg, 1, 262144, &errstr);
85 			if (errstr)
86 				errx(1, "Cylinder argument %s [1..262144].",
87 				    errstr);
88 			disk.dk_size = 0;
89 			break;
90 		case 'e':
91 			e_flag = 1;
92 			break;
93 		case 'f':
94 			mbrfile = optarg;
95 			break;
96 		case 'g':
97 			init = INIT_GPT;
98 			break;
99 		case 'h':
100 			disk.dk_heads = strtonum(optarg, 1, 256, &errstr);
101 			if (errstr)
102 				errx(1, "Head argument %s [1..256].", errstr);
103 			disk.dk_size = 0;
104 			break;
105 		case 'i':
106 			init = INIT_MBR;
107 			break;
108 		case 'l':
109 			disk.dk_size = strtonum(optarg, BLOCKALIGNMENT, UINT32_MAX, &errstr);
110 			if (errstr)
111 				errx(1, "Block argument %s [%u..%u].", errstr,
112 				    BLOCKALIGNMENT, UINT32_MAX);
113 			disk.dk_cylinders = disk.dk_heads = disk.dk_sectors = 0;
114 			break;
115 		case 's':
116 			disk.dk_sectors = strtonum(optarg, 1, 63, &errstr);
117 			if (errstr)
118 				errx(1, "Sector argument %s [1..63].", errstr);
119 			disk.dk_size = 0;
120 			break;
121 		case 'u':
122 			init = INIT_MBRBOOTCODE;
123 			break;
124 		case 'v':
125 			verbosity = VERBOSE;
126 			break;
127 		case 'y':
128 			y_flag = 1;
129 			break;
130 		default:
131 			usage();
132 		}
133 	}
134 	argc -= optind;
135 	argv += optind;
136 
137 	if (argc != 1)
138 		usage();
139 
140 	if ((disk.dk_cylinders || disk.dk_heads || disk.dk_sectors) &&
141 	    (disk.dk_cylinders * disk.dk_heads * disk.dk_sectors == 0))
142 		usage();
143 
144 	if (init || e_flag)
145 		oflags = O_RDWR;
146 
147 	get_default_dmbr(mbrfile);
148 
149 	DISK_open(argv[0], oflags);
150 	if (oflags == O_RDONLY) {
151 		if (pledge("stdio", NULL) == -1)
152 			err(1, "pledge");
153 		USER_print_disk(verbosity);
154 		goto done;
155 	}
156 
157 	/*
158 	 * "stdio" to talk to the outside world.
159 	 * "proc exec" for man page display.
160 	 * "disklabel" for DIOCRLDINFO.
161 	 */
162 	if (pledge("stdio disklabel proc exec", NULL) == -1)
163 		err(1, "pledge");
164 
165 	switch (init) {
166 	case INIT_GPT:
167 		GPT_init(GHANDGP);
168 		if (ask_yn("Do you wish to write new GPT?"))
169 			Xwrite(NULL, &gmbr);
170 		break;
171 	case INIT_GPTPARTITIONS:
172 		if (GPT_read(ANYGPT))
173 			errx(1, "-A requires a valid GPT");
174 		GPT_init(GPONLY);
175 		if (ask_yn("Do you wish to write new GPT?"))
176 			Xwrite(NULL, &gmbr);
177 		break;
178 	case INIT_MBR:
179 		mbr.mbr_lba_self = mbr.mbr_lba_firstembr = 0;
180 		MBR_init(&mbr);
181 		if (ask_yn("Do you wish to write new MBR?"))
182 			Xwrite(NULL, &mbr);
183 		break;
184 	case INIT_MBRBOOTCODE:
185 		if (MBR_read(0, 0, &mbr))
186 			errx(1, "Can't read MBR!");
187 		memcpy(mbr.mbr_code, default_dmbr.dmbr_boot,
188 		    sizeof(mbr.mbr_code));
189 		if (ask_yn("Do you wish to write new MBR?"))
190 			Xwrite(NULL, &mbr);
191 		break;
192 	default:
193 		break;
194 	}
195 
196 	if (e_flag)
197 		USER_edit(0, 0);
198 
199 done:
200 	close(disk.dk_fd);
201 
202 	return 0;
203 }
204 
205 void
206 parse_bootprt(const char *arg)
207 {
208 	const char		*errstr;
209 	char			*poffset, *ptype;
210 	int			 partitiontype;
211 	uint32_t		 blockcount, blockoffset;
212 
213 	blockoffset = BLOCKALIGNMENT;
214 	partitiontype = DOSPTYP_EFISYS;
215 	ptype = NULL;
216 
217 	/* First number: # of 512-byte blocks in boot partition. */
218 	poffset = strchr(arg, '@');
219 	if (poffset != NULL)
220 		*poffset++ = '\0';
221 	if (poffset != NULL) {
222 		ptype = strchr(poffset, ':');
223 		if (ptype != NULL)
224 			*ptype++ = '\0';
225 	}
226 
227 	blockcount = strtonum(arg, 1, UINT32_MAX, &errstr);
228 	if (errstr)
229 		errx(1, "Block argument %s [%u..%u].", errstr, 1, UINT32_MAX);
230 
231 	if (poffset == NULL)
232 		goto done;
233 
234 	/* Second number: # of 512-byte blocks to offset partition start. */
235 	blockoffset = strtonum(poffset, 1, UINT32_MAX, &errstr);
236 	if (errstr)
237 		errx(1, "Block offset argument %s [%u..%u].", errstr, 1,
238 		    UINT32_MAX);
239 
240 	if (ptype == NULL)
241 		goto done;
242 
243 	partitiontype = hex_octet(ptype);
244 	if (partitiontype == -1)
245 		errx(1, "Block type is not a 1-2 digit hex value");
246 
247  done:
248 	disk.dk_bootprt.prt_ns = blockcount;
249 	disk.dk_bootprt.prt_bs = blockoffset;
250 	disk.dk_bootprt.prt_id = partitiontype;
251 }
252 
253 void
254 get_default_dmbr(const char *mbrfile)
255 {
256 	struct dos_mbr		*dmbr = &default_dmbr;
257 	ssize_t			 len, sz;
258 	int			 fd;
259 
260 	if (mbrfile == NULL)
261 #ifdef HAS_MBR
262 		mbrfile = _PATH_MBR;
263 #else
264 		return;
265 #endif
266 
267 	fd = open(mbrfile, O_RDONLY);
268 	if (fd == -1)
269 		err(1, "%s", mbrfile);
270 
271 	sz = sizeof(*dmbr);
272 	len = read(fd, dmbr, sz);
273 	close(fd);
274 
275 	if (len == -1)
276 		err(1, "read('%s')", mbrfile);
277 	else if (len != sz)
278 		errx(1, "read('%s'): read %zd bytes of %zd", mbrfile, len, sz);
279 }
280