xref: /openbsd/sbin/fdisk/fdisk.c (revision 7f367d37)
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 
47 int			y_flag;
48 
49 void			parse_bootprt(const char *);
50 void			get_default_dmbr(const char *);
51 
52 static void
usage(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 file] "
60 	    "disk\n", __progname);
61 	exit(1);
62 }
63 
64 int
main(int argc,char * argv[])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 			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 
206 done:
207 	close(disk.dk_fd);
208 
209 	return 0;
210 }
211 
212 void
parse_bootprt(const char * arg)213 parse_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 
260 void
get_default_dmbr(const char * mbrfile)261 get_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