xref: /openbsd/sbin/fdisk/mbr.c (revision 73471bf0)
1 /*	$OpenBSD: mbr.c,v 1.114 2021/12/11 20:09:28 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
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_parse(&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 		PRT_fix_CHS(&obsdprt);
95 	}
96 
97 	PRT_fix_CHS(&bootprt);
98 
99 	memset(mbr, 0, sizeof(*mbr));
100 	memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code));
101 	mbr->mbr_prt[0] = bootprt;
102 	mbr->mbr_prt[3] = obsdprt;
103 	mbr->mbr_signature = DOSMBR_SIGNATURE;
104 }
105 
106 void
107 dos_mbr_to_mbr(const struct dos_mbr *dmbr, const uint64_t lba_self,
108     const uint64_t lba_firstembr, struct mbr *mbr)
109 {
110 	struct dos_partition	dos_parts[NDOSPART];
111 	int			i;
112 
113 	memcpy(mbr->mbr_code, dmbr->dmbr_boot, sizeof(mbr->mbr_code));
114 	mbr->mbr_lba_self = lba_self;
115 	mbr->mbr_lba_firstembr = lba_firstembr;
116 	mbr->mbr_signature = letoh16(dmbr->dmbr_sign);
117 
118 	memcpy(dos_parts, dmbr->dmbr_parts, sizeof(dos_parts));
119 
120 	for (i = 0; i < NDOSPART; i++)
121 		PRT_parse(&dos_parts[i], lba_self, lba_firstembr,
122 		    &mbr->mbr_prt[i]);
123 }
124 
125 void
126 mbr_to_dos_mbr(const struct mbr *mbr, struct dos_mbr *dos_mbr)
127 {
128 	struct dos_partition	dos_partition;
129 	int			i;
130 
131 	memcpy(dos_mbr->dmbr_boot, mbr->mbr_code, sizeof(dos_mbr->dmbr_boot));
132 	dos_mbr->dmbr_sign = htole16(DOSMBR_SIGNATURE);
133 
134 	for (i = 0; i < NDOSPART; i++) {
135 		PRT_make(&mbr->mbr_prt[i], mbr->mbr_lba_self, mbr->mbr_lba_firstembr,
136 		    &dos_partition);
137 		memcpy(&dos_mbr->dmbr_parts[i], &dos_partition,
138 		    sizeof(dos_mbr->dmbr_parts[i]));
139 	}
140 }
141 
142 void
143 MBR_print(const struct mbr *mbr, const char *units)
144 {
145 	int			i;
146 
147 	DISK_printgeometry("s");
148 
149 	printf("Offset: %lld\t", (long long)mbr->mbr_lba_self);
150 	printf("Signature: 0x%X\n", (int)mbr->mbr_signature);
151 	PRT_print(0, NULL, units);
152 
153 	for (i = 0; i < NDOSPART; i++)
154 		PRT_print(i, &mbr->mbr_prt[i], units);
155 }
156 
157 int
158 MBR_read(const uint64_t lba_self, const uint64_t lba_firstembr, struct mbr *mbr)
159 {
160 	struct dos_mbr		 dos_mbr;
161 	char			*secbuf;
162 
163 	secbuf = DISK_readsectors(lba_self, 1);
164 	if (secbuf == NULL)
165 		return -1;
166 
167 	memcpy(&dos_mbr, secbuf, sizeof(dos_mbr));
168 	free(secbuf);
169 
170 	dos_mbr_to_mbr(&dos_mbr, lba_self, lba_firstembr, mbr);
171 
172 	return 0;
173 }
174 
175 int
176 MBR_write(const struct mbr *mbr)
177 {
178 	struct dos_mbr		 dos_mbr;
179 	char			*secbuf;
180 	int			 rslt;
181 
182 	secbuf = DISK_readsectors(mbr->mbr_lba_self, 1);
183 	if (secbuf == NULL)
184 		return -1;
185 
186 	mbr_to_dos_mbr(mbr, &dos_mbr);
187 	memcpy(secbuf, &dos_mbr, sizeof(dos_mbr));
188 
189 	rslt = DISK_writesectors(secbuf, mbr->mbr_lba_self, 1);
190 	free(secbuf);
191 	if (rslt)
192 		return -1;
193 
194 	/* Refresh in-kernel disklabel from the updated disk information. */
195 	if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
196 		warn("DIOCRLDINFO");
197 
198 	return 0;
199 }
200