1/*	$NetBSD: disksubr.c,v 1.23 2024/02/10 18:43:51 andvar Exp $	*/
2
3/*
4 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	@(#)ufs_disksubr.c	7.16 (Berkeley) 5/4/91
32 */
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.23 2024/02/10 18:43:51 andvar Exp $");
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/buf.h>
40#include <sys/disklabel.h>
41#include <sys/disk.h>
42#include <sys/syslog.h>
43
44#include "opt_mbr.h"
45
46int fat_types[] = { MBR_PTYPE_FAT12, MBR_PTYPE_FAT16S,
47		    MBR_PTYPE_FAT16B, MBR_PTYPE_FAT32,
48		    MBR_PTYPE_FAT32L, MBR_PTYPE_FAT16L,
49		    -1 };
50
51#define NO_MBR_SIGNATURE ((struct mbr_partition *) -1)
52
53static struct mbr_partition *
54mbr_findslice(struct mbr_partition* dp, struct buf *bp);
55
56/*
57 * Scan MBR for  NetBSD partition.  Return NO_MBR_SIGNATURE if no MBR found
58 * Otherwise, copy valid MBR partition-table into dp, and if a NetBSD
59 * partition is found, return a pointer to it; else return  NULL.
60 */
61static
62struct mbr_partition *
63mbr_findslice(struct mbr_partition *dp, struct buf *bp)
64{
65	struct mbr_partition *ourdp = NULL;
66	uint16_t *mbrmagicp;
67	int i;
68
69	/* Note: Magic number is little-endian. */
70	mbrmagicp = (uint16_t *)((char*)bp->b_data + MBR_MAGIC_OFFSET);
71	if (*mbrmagicp != MBR_MAGIC)
72		return (NO_MBR_SIGNATURE);
73
74	/* XXX how do we check veracity/bounds of this? */
75	memcpy(dp, (char*)bp->b_data + MBR_PART_OFFSET,
76		MBR_PART_COUNT * sizeof(*dp));
77
78	/* look for NetBSD partition */
79	for (i = 0; i < MBR_PART_COUNT; i++) {
80		if (dp[i].mbrp_type == MBR_PTYPE_NETBSD) {
81			ourdp = &dp[i];
82			break;
83		}
84	}
85
86#ifdef COMPAT_386BSD_MBRPART
87	/* didn't find it -- look for 386BSD partition */
88	if (!ourdp) {
89		for (i = 0; i < MBR_PART_COUNT; i++) {
90			if (dp[i].mbrp_type == MBR_PTYPE_386BSD) {
91				printf("WARNING: old BSD partition ID!\n");
92				ourdp = &dp[i];
93 				/*
94				 * If more than one matches, take last,
95				 * as NetBSD install tool does.
96				 */
97#if 0
98				break;
99#endif
100			}
101		}
102	}
103#endif	/* COMPAT_386BSD_MBRPART */
104
105		return (ourdp);
106}
107
108
109/*
110 * Attempt to read a disk label from a device
111 * using the indicated strategy routine.
112 * The label must be partly set up before this:
113 * secpercyl, secsize and anything required for a block i/o read
114 * operation in the driver's strategy/start routines
115 * must be filled in before calling us.
116 *
117 * If dos partition table requested, attempt to load it and
118 * find disklabel inside a DOS partition. Also, if bad block
119 * table needed, attempt to extract it as well. Return buffer
120 * for use in signalling errors if requested.
121 *
122 * Returns null on success and an error string on failure.
123 */
124const char *
125readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
126    struct cpu_disklabel *osdep)
127{
128	struct mbr_partition *dp;
129	struct partition *pp;
130	struct dkbad *bdp;
131	struct buf *bp;
132	struct disklabel *dlp;
133	const char *msg = NULL;
134	int dospartoff, cyl, i, *ip;
135
136	/* minimal requirements for archtypal disk label */
137	if (lp->d_secsize == 0)
138		lp->d_secsize = DEV_BSIZE;
139	if (lp->d_secperunit == 0)
140		lp->d_secperunit = 0x1fffffff;
141#if 0
142	if (lp->d_ncylinders == 16383) {
143		printf("disklabel: Disk > 8G ... readjusting chs %d/%d/%d to ",
144			lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors);
145		lp->d_ncylinders = lp->d_secperunit /  lp->d_ntracks / lp->d_nsectors;
146		printf("%d/%d/%d\n",
147			lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors);
148	}
149#endif
150	lp->d_npartitions = RAW_PART + 1;
151	for (i = 0; i < RAW_PART; i++) {
152		lp->d_partitions[i].p_size = 0;
153		lp->d_partitions[i].p_offset = 0;
154	}
155	if (lp->d_partitions[i].p_size == 0)
156		lp->d_partitions[i].p_size = 0x1fffffff;
157	lp->d_partitions[i].p_offset = 0;
158
159	/* get a buffer and initialize it */
160	bp = geteblk((int)lp->d_secsize);
161	bp->b_dev = dev;
162
163	/* do dos partitions in the process of getting disklabel? */
164	dospartoff = 0;
165	cyl = LABELSECTOR / lp->d_secpercyl;
166	if (!osdep)
167		goto nombrpart;
168	dp = osdep->mbrparts;
169
170	/* read master boot record */
171	bp->b_blkno = MBR_BBSECTOR;
172	bp->b_bcount = lp->d_secsize;
173	bp->b_flags |= B_READ;
174	bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
175	(*strat)(bp);
176
177	/* if successful, wander through dos partition table */
178	if (biowait(bp)) {
179		msg = "dos partition I/O error";
180		goto done;
181	} else {
182		struct mbr_partition *ourdp = NULL;
183
184		ourdp = mbr_findslice(dp, bp);
185		if (ourdp ==  NO_MBR_SIGNATURE)
186			goto nombrpart;
187
188		for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
189			/* Install in partition e, f, g, or h. */
190			pp = &lp->d_partitions[RAW_PART + 1 + i];
191			pp->p_offset = dp->mbrp_start;
192			pp->p_size = dp->mbrp_size;
193			for (ip = fat_types; *ip != -1; ip++) {
194				if (dp->mbrp_type == *ip)
195					pp->p_fstype = FS_MSDOS;
196			}
197			if (dp->mbrp_type == MBR_PTYPE_LNXEXT2)
198				pp->p_fstype = FS_EX2FS;
199
200			if (dp->mbrp_type == MBR_PTYPE_NTFS)
201				pp->p_fstype = FS_NTFS;
202
203			/* is this ours? */
204			if (dp == ourdp) {
205				/* need sector address for SCSI/IDE,
206				 cylinder for ESDI/ST506/RLL */
207				dospartoff = dp->mbrp_start;
208				cyl = MBR_PCYL(dp->mbrp_scyl, dp->mbrp_ssect);
209
210				/* update disklabel with details */
211				lp->d_partitions[2].p_size =
212				    dp->mbrp_size;
213				lp->d_partitions[2].p_offset =
214				    dp->mbrp_start;
215#if 0
216				if (lp->d_ntracks != dp->mbrp_ehd + 1 ||
217				    lp->d_nsectors != DPSECT(dp->mbrp_esect)) {
218					printf("disklabel: BIOS sees chs %d/%d/%d as ",
219						lp->d_ncylinders, lp->d_ntracks,
220						lp->d_nsectors);
221					lp->d_ntracks = dp->mbrp_ehd + 1;
222					lp->d_nsectors = DPSECT(dp->mbrp_esect);
223					lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
224					lp->d_ncylinders = lp->d_secperunit / lp->d_secpercyl;
225					if (! lp->d_ncylinders)
226						lp->d_ncylinders = 1;
227					printf("%d/%d/%d\n",
228						lp->d_ncylinders, lp->d_ntracks,
229						lp->d_nsectors);
230				    }
231#endif
232			}
233		}
234		lp->d_npartitions = RAW_PART + 1 + i;
235	}
236
237nombrpart:
238	/* next, dig out disk label */
239	bp->b_blkno = dospartoff + LABELSECTOR;
240	bp->b_cylinder = cyl;
241	bp->b_bcount = lp->d_secsize;
242	bp->b_oflags &= ~(BO_DONE);
243	bp->b_flags |= B_READ;
244	(*strat)(bp);
245
246	/* if successful, locate disk label within block and validate */
247	if (biowait(bp)) {
248		msg = "disk label I/O error";
249		goto done;
250	}
251	for (dlp = (struct disklabel *)bp->b_data;
252	    dlp <= (struct disklabel *)((char*)bp->b_data +
253		lp->d_secsize - sizeof(*dlp));
254	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
255		if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
256			if (msg == NULL)
257				msg = "no disk label";
258		} else if (dlp->d_npartitions > MAXPARTITIONS ||
259			   dkcksum(dlp) != 0)
260			msg = "disk label corrupted";
261		else {
262			*lp = *dlp;
263			msg = NULL;
264			break;
265		}
266	}
267
268	if (msg)
269		goto done;
270
271	/* obtain bad sector table if requested and present */
272	if (osdep && (lp->d_flags & D_BADSECT)) {
273		struct dkbad *db;
274
275		bdp = &osdep->bad;
276		i = 0;
277		do {
278			/* read a bad sector table */
279			bp->b_oflags &= ~(BO_DONE);
280			bp->b_flags |= B_READ;
281			bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i;
282			if (lp->d_secsize > DEV_BSIZE)
283				bp->b_blkno *= lp->d_secsize / DEV_BSIZE;
284			else
285				bp->b_blkno /= DEV_BSIZE / lp->d_secsize;
286			bp->b_bcount = lp->d_secsize;
287			bp->b_cylinder = lp->d_ncylinders - 1;
288			(*strat)(bp);
289
290			/* if successful, validate, otherwise try another */
291			if (biowait(bp)) {
292				msg = "bad sector table I/O error";
293			} else {
294				db = (struct dkbad *)(bp->b_data);
295#define DKBAD_MAGIC 0x4321
296				if (db->bt_mbz == 0
297					&& db->bt_flag == DKBAD_MAGIC) {
298					msg = NULL;
299					*bdp = *db;
300					break;
301				} else
302					msg = "bad sector table corrupted";
303			}
304		} while (bp->b_error != 0 && (i += 2) < 10 &&
305			i < lp->d_nsectors);
306	}
307
308done:
309	brelse(bp, 0);
310	return (msg);
311}
312
313/*
314 * Write disk label back to device after modification.
315 */
316int
317writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
318    struct cpu_disklabel *osdep)
319{
320	struct mbr_partition *dp;
321	struct buf *bp;
322	struct disklabel *dlp;
323	int error, dospartoff, cyl;
324
325	/* get a buffer and initialize it */
326	bp = geteblk((int)lp->d_secsize);
327	bp->b_dev = dev;
328
329	/* do dos partitions in the process of getting disklabel? */
330	dospartoff = 0;
331	cyl = LABELSECTOR / lp->d_secpercyl;
332	if (!osdep)
333		goto nombrpart;
334	dp = osdep->mbrparts;
335
336	/* read master boot record */
337	bp->b_blkno = MBR_BBSECTOR;
338	bp->b_bcount = lp->d_secsize;
339	bp->b_flags |= B_READ;
340	bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
341	(*strat)(bp);
342
343	if ((error = biowait(bp)) == 0) {
344		struct mbr_partition *ourdp = NULL;
345
346		ourdp = mbr_findslice(dp, bp);
347		if (ourdp ==  NO_MBR_SIGNATURE)
348			goto nombrpart;
349
350		if (ourdp) {
351			/* need sector address for SCSI/IDE,
352			 cylinder for ESDI/ST506/RLL */
353			dospartoff = ourdp->mbrp_start;
354			cyl = MBR_PCYL(ourdp->mbrp_scyl, ourdp->mbrp_ssect);
355		}
356	}
357
358nombrpart:
359#ifdef maybe
360	/* disklabel in appropriate location? */
361	if (lp->d_partitions[2].p_offset != 0
362		&& lp->d_partitions[2].p_offset != dospartoff) {
363		error = EXDEV;
364		goto done;
365	}
366#endif
367
368	/* next, dig out disk label */
369	bp->b_blkno = dospartoff + LABELSECTOR;
370	bp->b_cylinder = cyl;
371	bp->b_bcount = lp->d_secsize;
372	bp->b_oflags &= ~(BO_DONE);
373	bp->b_flags |= B_READ;
374	(*strat)(bp);
375
376	/* if successful, locate disk label within block and validate */
377	if ((error = biowait(bp)) != 0)
378		goto done;
379	for (dlp = (struct disklabel *)bp->b_data;
380	    dlp <= (struct disklabel *)((char *)bp->b_data +
381		lp->d_secsize - sizeof(*dlp));
382	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
383		if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
384		    dkcksum(dlp) == 0) {
385			*dlp = *lp;
386			bp->b_oflags &= ~(BO_DONE);
387			bp->b_flags &= ~(B_READ);
388			bp->b_flags |= B_WRITE;
389			(*strat)(bp);
390			error = biowait(bp);
391			goto done;
392		}
393	}
394	error = ESRCH;
395
396done:
397	brelse(bp, 0);
398	return (error);
399}
400