xref: /openbsd/sbin/fdisk/mbr.c (revision 614d0f20)
1 /*	$OpenBSD: mbr.c,v 1.124 2023/05/17 12:59:37 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/param.h>	/* DEV_BSIZE */
20 #include <sys/ioctl.h>
21 #include <sys/disklabel.h>
22 #include <sys/dkio.h>
23 
24 #include <err.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "part.h"
30 #include "disk.h"
31 #include "misc.h"
32 #include "mbr.h"
33 #include "gpt.h"
34 
35 struct dos_mbr		default_dmbr;
36 
37 void		mbr_to_dos_mbr(const struct mbr *, struct dos_mbr *);
38 void		dos_mbr_to_mbr(const struct dos_mbr *, const uint64_t,
39     const uint64_t, struct mbr *);
40 
41 void
MBR_init(struct mbr * mbr)42 MBR_init(struct mbr *mbr)
43 {
44 	struct dos_partition	dp;
45 	struct prt		bootprt, obsdprt;
46 	daddr_t			daddr;
47 
48 	memset(&gmbr, 0, sizeof(gmbr));
49 	memset(&gh, 0, sizeof(gh));
50 	memset(&gp, 0, sizeof(gp));
51 
52 	if (mbr->mbr_lba_self != 0) {
53 		/* Extended MBR - save lba's, set sig, zap everything else. */
54 		memset(mbr->mbr_code, 0, sizeof(mbr->mbr_code));
55 		memset(mbr->mbr_prt, 0, sizeof(mbr->mbr_prt));
56 		mbr->mbr_signature = DOSMBR_SIGNATURE;
57 		return;
58 	}
59 
60 	memset(&obsdprt, 0, sizeof(obsdprt));
61 	memset(&bootprt, 0, sizeof(bootprt));
62 
63 	if (disk.dk_bootprt.prt_ns > 0) {
64 		bootprt = disk.dk_bootprt;
65 	} else {
66 		memcpy(&dp, &default_dmbr.dmbr_parts[0], sizeof(dp));
67 		PRT_dp_to_prt(&dp, 0, 0, &bootprt);
68 	}
69 
70 	if (bootprt.prt_ns > 0) {
71 		/* Start OpenBSD partition immediately after bootprt. */
72 		obsdprt.prt_bs = bootprt.prt_bs + bootprt.prt_ns;
73 	} else if (disk.dk_heads > 1 || disk.dk_cylinders > 1) {
74 		/*
75 		 * Start OpenBSD partition on power of 2 block number
76 		 * after the first track.
77 		 */
78 		daddr = 1;
79 		while (daddr < DL_SECTOBLK(&dl, disk.dk_sectors))
80 			daddr *= 2;
81 		obsdprt.prt_bs = DL_BLKTOSEC(&dl, daddr);
82 	} else {
83 		/* Start OpenBSD partition immediately after MBR. */
84 		obsdprt.prt_bs = 1;
85 	}
86 
87 	if (obsdprt.prt_bs >= disk.dk_size) {
88 		memset(&obsdprt, 0, sizeof(obsdprt));
89 	} else {
90 		obsdprt.prt_ns = disk.dk_size - obsdprt.prt_bs;
91 		obsdprt.prt_id = DOSPTYP_OPENBSD;
92 		if (bootprt.prt_flag != DOSACTIVE)
93 			obsdprt.prt_flag = DOSACTIVE;
94 	}
95 
96 	memset(mbr, 0, sizeof(*mbr));
97 	memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code));
98 	mbr->mbr_prt[0] = bootprt;
99 	mbr->mbr_prt[3] = obsdprt;
100 	mbr->mbr_signature = DOSMBR_SIGNATURE;
101 }
102 
103 void
dos_mbr_to_mbr(const struct dos_mbr * dmbr,const uint64_t lba_self,const uint64_t lba_firstembr,struct mbr * mbr)104 dos_mbr_to_mbr(const struct dos_mbr *dmbr, const uint64_t lba_self,
105     const uint64_t lba_firstembr, struct mbr *mbr)
106 {
107 	struct dos_partition	dos_parts[NDOSPART];
108 	uint8_t			*p;
109 	unsigned int		 i;
110 
111 	p = (uint8_t *)dmbr;
112 	mbr->mbr_dmbrzeros = 0;
113 	for (i = 0; i < sizeof(struct dos_mbr) && *p == 0; i++, p++)
114 		mbr->mbr_dmbrzeros++;
115 
116 	memcpy(mbr->mbr_code, dmbr->dmbr_boot, sizeof(mbr->mbr_code));
117 	mbr->mbr_lba_self = lba_self;
118 	mbr->mbr_lba_firstembr = lba_firstembr;
119 	mbr->mbr_signature = letoh16(dmbr->dmbr_sign);
120 
121 	memcpy(dos_parts, dmbr->dmbr_parts, sizeof(dos_parts));
122 
123 	for (i = 0; i < nitems(mbr->mbr_prt); i++) {
124 		memset(&mbr->mbr_prt[i], 0, sizeof(mbr->mbr_prt[i]));
125 		if (i < nitems(dmbr->dmbr_parts))
126 			PRT_dp_to_prt(&dos_parts[i], lba_self, lba_firstembr,
127 			    &mbr->mbr_prt[i]);
128 	}
129 }
130 
131 void
mbr_to_dos_mbr(const struct mbr * mbr,struct dos_mbr * dos_mbr)132 mbr_to_dos_mbr(const struct mbr *mbr, struct dos_mbr *dos_mbr)
133 {
134 	struct dos_partition	dos_partition;
135 	unsigned int		i;
136 
137 	memcpy(dos_mbr->dmbr_boot, mbr->mbr_code, sizeof(dos_mbr->dmbr_boot));
138 	dos_mbr->dmbr_sign = htole16(DOSMBR_SIGNATURE);
139 
140 	for (i = 0; i < nitems(dos_mbr->dmbr_parts); i++) {
141 		memset(&dos_partition, 0, sizeof(dos_partition));
142 		if (i < nitems(mbr->mbr_prt)) {
143 			PRT_prt_to_dp(&mbr->mbr_prt[i], mbr->mbr_lba_self,
144 			    mbr->mbr_lba_firstembr, &dos_partition);
145 		}
146 		memcpy(&dos_mbr->dmbr_parts[i], &dos_partition,
147 		    sizeof(dos_mbr->dmbr_parts[i]));
148 	}
149 }
150 
151 void
MBR_print(const struct mbr * mbr,const char * units)152 MBR_print(const struct mbr *mbr, const char *units)
153 {
154 	unsigned int		i;
155 
156 	DISK_printgeometry("s");
157 
158 	printf("Offset: %llu\t", mbr->mbr_lba_self);
159 	printf("Signature: 0x%X\n", (int)mbr->mbr_signature);
160 	PRT_print_parthdr();
161 
162 	for (i = 0; i < nitems(mbr->mbr_prt); i++)
163 		PRT_print_part(i, &mbr->mbr_prt[i], units);
164 }
165 
166 int
MBR_read(const uint64_t lba_self,const uint64_t lba_firstembr,struct mbr * mbr)167 MBR_read(const uint64_t lba_self, const uint64_t lba_firstembr, struct mbr *mbr)
168 {
169 	struct dos_mbr		 dos_mbr;
170 
171 	if (DISK_readbytes(&dos_mbr, lba_self, sizeof(dos_mbr)))
172 		return -1;
173 
174 	dos_mbr_to_mbr(&dos_mbr, lba_self, lba_firstembr, mbr);
175 
176 	return 0;
177 }
178 
179 int
MBR_write(const struct mbr * mbr)180 MBR_write(const struct mbr *mbr)
181 {
182 	struct dos_mbr		 dos_mbr;
183 
184 	mbr_to_dos_mbr(mbr, &dos_mbr);
185 
186 	if (DISK_writebytes(&dos_mbr, mbr->mbr_lba_self, sizeof(dos_mbr)))
187 		return -1;
188 
189 	/* Refresh in-kernel disklabel from the updated disk information. */
190 	if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
191 		warn("DIOCRLDINFO");
192 
193 	return 0;
194 }
195 
196 int
MBR_valid_prt(const struct mbr * mbr)197 MBR_valid_prt(const struct mbr *mbr)
198 {
199 	uint64_t		bs, ns;
200 	unsigned int		i, nprt;
201 	unsigned char		id;
202 
203 	if (mbr->mbr_dmbrzeros == sizeof(struct dos_mbr))
204 		return 1;	/* All zeros struct dos_mbr is editable. */
205 
206 	nprt = 0;
207 	for (i = 0; i < nitems(mbr->mbr_prt); i++) {
208 		bs = mbr->mbr_prt[i].prt_bs;
209 		ns = mbr->mbr_prt[i].prt_ns;
210 		id = mbr->mbr_prt[i].prt_id;
211 		if ((bs == 0 && ns == 0 && id == 0) ||
212 		    (bs < DL_GETDSIZE(&dl) && ns > 0 && ns <= DL_GETDSIZE(&dl)))
213 			nprt++;
214 	}
215 
216 	return nprt > 0 && mbr->mbr_signature == DOSMBR_SIGNATURE;
217 }
218