xref: /netbsd/sys/kern/subr_disk_mbr.c (revision 9703a26e)
1*9703a26eSgutteridge /*	$NetBSD: subr_disk_mbr.c,v 1.58 2023/04/03 18:03:24 gutteridge Exp $	*/
2d38f6f31Sdsl 
3d38f6f31Sdsl /*
4d38f6f31Sdsl  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
5d38f6f31Sdsl  * All rights reserved.
6d38f6f31Sdsl  *
7d38f6f31Sdsl  * Redistribution and use in source and binary forms, with or without
8d38f6f31Sdsl  * modification, are permitted provided that the following conditions
9d38f6f31Sdsl  * are met:
10d38f6f31Sdsl  * 1. Redistributions of source code must retain the above copyright
11d38f6f31Sdsl  *    notice, this list of conditions and the following disclaimer.
12d38f6f31Sdsl  * 2. Redistributions in binary form must reproduce the above copyright
13d38f6f31Sdsl  *    notice, this list of conditions and the following disclaimer in the
14d38f6f31Sdsl  *    documentation and/or other materials provided with the distribution.
15aad01611Sagc  * 3. Neither the name of the University nor the names of its contributors
16d38f6f31Sdsl  *    may be used to endorse or promote products derived from this software
17d38f6f31Sdsl  *    without specific prior written permission.
18d38f6f31Sdsl  *
19d38f6f31Sdsl  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20d38f6f31Sdsl  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21d38f6f31Sdsl  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22d38f6f31Sdsl  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23d38f6f31Sdsl  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24d38f6f31Sdsl  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25d38f6f31Sdsl  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26d38f6f31Sdsl  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27d38f6f31Sdsl  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28d38f6f31Sdsl  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29d38f6f31Sdsl  * SUCH DAMAGE.
30d38f6f31Sdsl  *
31d38f6f31Sdsl  *	@(#)ufs_disksubr.c	7.16 (Berkeley) 5/4/91
32d38f6f31Sdsl  */
33d38f6f31Sdsl 
34d38f6f31Sdsl /*
35d38f6f31Sdsl  * Code to find a NetBSD label on a disk that contains an i386 style MBR.
36d38f6f31Sdsl  * The first NetBSD label found in the 2nd sector of a NetBSD partition
3740bac108Sdsl  * is used.
3840bac108Sdsl  * If we don't find a label searching the MBR, we look at the start of the
3940bac108Sdsl  * disk, if that fails then a label is faked up from the MBR.
40d38f6f31Sdsl  *
41339f5c57Sreinoud  * If there isn't a disklabel or anything in the MBR then the disc is searched
42339f5c57Sreinoud  * for ecma-167/iso9660/udf style partition indicators.
43339f5c57Sreinoud  * Useful for media or files that contain single filesystems (etc).
44d38f6f31Sdsl  *
45d38f6f31Sdsl  * This code will read host endian netbsd labels from little endian MBR.
46d38f6f31Sdsl  *
47d38f6f31Sdsl  * Based on the i386 disksubr.c
48d38f6f31Sdsl  *
4940bac108Sdsl  * Since the mbr only has 32bit fields for sector addresses, we do the same.
5040bac108Sdsl  *
51d38f6f31Sdsl  * XXX There are potential problems writing labels to disks where there
52d38f6f31Sdsl  * is only space for 8 netbsd partitions but this code has been compiled
53d38f6f31Sdsl  * with MAXPARTITIONS=16.
54d38f6f31Sdsl  */
55d38f6f31Sdsl 
56d38f6f31Sdsl #include <sys/cdefs.h>
57*9703a26eSgutteridge __KERNEL_RCSID(0, "$NetBSD: subr_disk_mbr.c,v 1.58 2023/04/03 18:03:24 gutteridge Exp $");
584a1c505cSmrg 
594a1c505cSmrg #ifdef _KERNEL_OPT
604a1c505cSmrg #include "opt_mbr.h"
614a1c505cSmrg #include "opt_disklabel.h"
624a1c505cSmrg #endif /* _KERNEL_OPT */
63d38f6f31Sdsl 
64d38f6f31Sdsl #include <sys/param.h>
65d38f6f31Sdsl #include <sys/systm.h>
66d38f6f31Sdsl #include <sys/buf.h>
67db12d3f8Sdyoung #include <sys/bootblock.h>
68d38f6f31Sdsl #include <sys/disklabel.h>
69d38f6f31Sdsl #include <sys/disk.h>
70d38f6f31Sdsl #include <sys/syslog.h>
71339f5c57Sreinoud #include <sys/vnode.h>
72339f5c57Sreinoud #include <sys/fcntl.h>
73339f5c57Sreinoud #include <sys/conf.h>
74339f5c57Sreinoud #include <sys/cdio.h>
7509dbb89bSpooka #include <sys/dkbad.h>
76339f5c57Sreinoud #include <fs/udf/ecma167-udf.h>
77339f5c57Sreinoud 
78339f5c57Sreinoud #include <sys/kauth.h>
79d38f6f31Sdsl 
80a89c49eeSdsl typedef struct mbr_partition mbr_partition_t;
81a89c49eeSdsl 
82db0d6983Sdsl /*
838257134aSpooka  * We allocate a buffer 3 sectors large, and look in all....
84db0d6983Sdsl  * That means we find labels written by other ports with different offsets.
85db0d6983Sdsl  * LABELSECTOR and LABELOFFSET are only used if the disk doesn't have a label.
86db0d6983Sdsl  */
878257134aSpooka #define SCANBLOCKS 3
888257134aSpooka #define DISKLABEL_SIZE 404
898257134aSpooka #if LABELSECTOR*DEV_BSIZE + LABELOFFSET > SCANBLOCKS*DEV_BSIZE - DISKLABEL_SIZE
90b2cb120dSpooka #if _MACHINE != ews4800mips /* XXX: fail silently, ews4800mips LABELSECTOR */
91db0d6983Sdsl #error Invalid LABELSECTOR or LABELOFFSET
92db0d6983Sdsl #endif
9325362a67Stsutsui #endif
94db0d6983Sdsl 
95d38f6f31Sdsl #define MBR_LABELSECTOR	1
96d38f6f31Sdsl 
97d38f6f31Sdsl #define SCAN_CONTINUE	0
98d38f6f31Sdsl #define SCAN_FOUND	1
99d38f6f31Sdsl #define SCAN_ERROR	2
100d38f6f31Sdsl 
101d38f6f31Sdsl typedef struct mbr_args {
102d38f6f31Sdsl 	struct disklabel *lp;
103d38f6f31Sdsl 	void		(*strat)(struct buf *);
104d38f6f31Sdsl 	struct buf	*bp;
105d38f6f31Sdsl 	const char	*msg;
106d38f6f31Sdsl 	int		error;
10740bac108Sdsl 	int		written;	/* number of times we wrote label */
108db0d6983Sdsl 	int		found_mbr;	/* set if disk has a valid mbr */
10940bac108Sdsl 	uint		label_sector;	/* where we found the label */
110db0d6983Sdsl 	int		action;
1111ee8f4a4Sdyoung 	uint32_t	secperunit;
112d38f6f31Sdsl #define READ_LABEL	1
11340bac108Sdsl #define UPDATE_LABEL	2
11440bac108Sdsl #define WRITE_LABEL	3
115db0d6983Sdsl } mbr_args_t;
116db0d6983Sdsl 
117db0d6983Sdsl static int validate_label(mbr_args_t *, uint);
118a89c49eeSdsl static int look_netbsd_part(mbr_args_t *, mbr_partition_t *, int, uint);
119a89c49eeSdsl static int write_netbsd_label(mbr_args_t *, mbr_partition_t *, int, uint);
120d38f6f31Sdsl 
121d38f6f31Sdsl static int
read_sector(mbr_args_t * a,uint sector,int count)122db0d6983Sdsl read_sector(mbr_args_t *a, uint sector, int count)
123d38f6f31Sdsl {
124d38f6f31Sdsl 	int error;
125d38f6f31Sdsl 
1261ee8f4a4Sdyoung 	error = disk_read_sectors(a->strat, a->lp, a->bp, sector, count);
127d38f6f31Sdsl 	if (error != 0)
128d38f6f31Sdsl 		a->error = error;
129d38f6f31Sdsl 	return error;
130d38f6f31Sdsl }
131d38f6f31Sdsl 
132d38f6f31Sdsl /*
133d38f6f31Sdsl  * Scan MBR for partitions, call 'action' routine for each.
134d38f6f31Sdsl  */
135d38f6f31Sdsl 
136d38f6f31Sdsl static int
scan_mbr(mbr_args_t * a,int (* actn)(mbr_args_t *,mbr_partition_t *,int,uint))137a89c49eeSdsl scan_mbr(mbr_args_t *a, int (*actn)(mbr_args_t *, mbr_partition_t *, int, uint))
138d38f6f31Sdsl {
139a89c49eeSdsl 	mbr_partition_t ptns[MBR_PART_COUNT];
140a89c49eeSdsl 	mbr_partition_t *dp;
1411c33b4e6Slukem 	struct mbr_sector *mbr;
142d38f6f31Sdsl 	uint ext_base, this_ext, next_ext;
143d38f6f31Sdsl 	int rval;
144d38f6f31Sdsl 	int i;
1451214d130Schristos 	int j;
146d38f6f31Sdsl #ifdef COMPAT_386BSD_MBRPART
147d38f6f31Sdsl 	int dp_386bsd = -1;
1481214d130Schristos 	int ap_386bsd = -1;
149d38f6f31Sdsl #endif
150d38f6f31Sdsl 
151d38f6f31Sdsl 	ext_base = 0;
152d38f6f31Sdsl 	this_ext = 0;
153d38f6f31Sdsl 	for (;;) {
154db0d6983Sdsl 		if (read_sector(a, this_ext, 1)) {
155d38f6f31Sdsl 			a->msg = "dos partition I/O error";
156d38f6f31Sdsl 			return SCAN_ERROR;
157d38f6f31Sdsl 		}
158d38f6f31Sdsl 
159d38f6f31Sdsl 		/* Note: Magic number is little-endian. */
160d38f6f31Sdsl 		mbr = (void *)a->bp->b_data;
1611c33b4e6Slukem 		if (mbr->mbr_magic != htole16(MBR_MAGIC))
16240bac108Sdsl 			return SCAN_CONTINUE;
163d38f6f31Sdsl 
1649708c4d5Smatt 		/*
1659708c4d5Smatt 		 * If this is a protective MBR, bail now.
1669708c4d5Smatt 		 */
1679708c4d5Smatt 		if (mbr->mbr_parts[0].mbrp_type == MBR_PTYPE_PMBR
1689708c4d5Smatt 		    && mbr->mbr_parts[1].mbrp_type == MBR_PTYPE_UNUSED
1699708c4d5Smatt 		    && mbr->mbr_parts[2].mbrp_type == MBR_PTYPE_UNUSED
1709708c4d5Smatt 		    && mbr->mbr_parts[3].mbrp_type == MBR_PTYPE_UNUSED)
1719708c4d5Smatt 			return SCAN_CONTINUE;
1729708c4d5Smatt 
173d38f6f31Sdsl 		/* Copy data out of buffer so action can use bp */
174d38f6f31Sdsl 		memcpy(ptns, &mbr->mbr_parts, sizeof ptns);
175d38f6f31Sdsl 
1764ead7c35Schristos 		/* Look for drivers and skip them */
177db0d6983Sdsl 		if (ext_base == 0 && ptns[0].mbrp_type == MBR_PTYPE_DM6_DDO) {
178d9585f8dSjmmv 			/* We've found a DM6 DDO partition type (used by
179d9585f8dSjmmv 			 * the Ontrack Disk Manager drivers).
180d9585f8dSjmmv 			 *
181d9585f8dSjmmv 			 * Ensure that there are no other partitions in the
182d9585f8dSjmmv 			 * MBR and jump to the real partition table (stored
183d9585f8dSjmmv 			 * in the first sector of the second track). */
1844f3d5a9cSthorpej 			bool ok = true;
1854ead7c35Schristos 
1864ead7c35Schristos 			for (i = 1; i < MBR_PART_COUNT; i++)
1874ead7c35Schristos 				if (ptns[i].mbrp_type != MBR_PTYPE_UNUSED)
1884f3d5a9cSthorpej 					ok = false;
1894ead7c35Schristos 
1904ead7c35Schristos 			if (ok) {
191ddeaed40Schristos 				this_ext = le32toh(a->lp->d_secpercyl /
192ddeaed40Schristos 				    a->lp->d_ntracks);
1934ead7c35Schristos 				continue;
1944ead7c35Schristos 			}
1954ead7c35Schristos 		}
1964ead7c35Schristos 
197d38f6f31Sdsl 		/* look for NetBSD partition */
198d38f6f31Sdsl 		next_ext = 0;
199d38f6f31Sdsl 		dp = ptns;
2001214d130Schristos 		j = 0;
2011c33b4e6Slukem 		for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
2021214d130Schristos 			if (dp->mbrp_type == MBR_PTYPE_UNUSED)
203d38f6f31Sdsl 				continue;
204db0d6983Sdsl 			/* Check end of partition is inside disk limits */
205db0d6983Sdsl 			if ((uint64_t)ext_base + le32toh(dp->mbrp_start) +
206db0d6983Sdsl 			    le32toh(dp->mbrp_size) > a->lp->d_secperunit) {
207db0d6983Sdsl 				/* This mbr doesn't look good.... */
208db0d6983Sdsl 				a->msg = "mbr partition exceeds disk size";
209db0d6983Sdsl 				/* ...but don't report this as an error (yet) */
210db0d6983Sdsl 				return SCAN_CONTINUE;
211db0d6983Sdsl 			}
212db0d6983Sdsl 			a->found_mbr = 1;
2131c33b4e6Slukem 			if (MBR_IS_EXTENDED(dp->mbrp_type)) {
214d38f6f31Sdsl 				next_ext = le32toh(dp->mbrp_start);
215d38f6f31Sdsl 				continue;
216d38f6f31Sdsl 			}
217d38f6f31Sdsl #ifdef COMPAT_386BSD_MBRPART
2181c33b4e6Slukem 			if (dp->mbrp_type == MBR_PTYPE_386BSD) {
219d38f6f31Sdsl 				/*
220d38f6f31Sdsl 				 * If more than one matches, take last,
221d38f6f31Sdsl 				 * as NetBSD install tool does.
222d38f6f31Sdsl 				 */
2231214d130Schristos 				if (this_ext == 0) {
224d38f6f31Sdsl 					dp_386bsd = i;
2251214d130Schristos 					ap_386bsd = j;
2261214d130Schristos 				}
227d38f6f31Sdsl 				continue;
228d38f6f31Sdsl 			}
229d38f6f31Sdsl #endif
2301214d130Schristos 			rval = (*actn)(a, dp, j, this_ext);
231d38f6f31Sdsl 			if (rval != SCAN_CONTINUE)
232d38f6f31Sdsl 				return rval;
2331214d130Schristos 			j++;
234d38f6f31Sdsl 		}
235d38f6f31Sdsl 		if (next_ext == 0)
236d38f6f31Sdsl 			break;
237d38f6f31Sdsl 		if (ext_base == 0) {
238d38f6f31Sdsl 			ext_base = next_ext;
239d38f6f31Sdsl 			next_ext = 0;
240d38f6f31Sdsl 		}
241d38f6f31Sdsl 		next_ext += ext_base;
242d38f6f31Sdsl 		if (next_ext <= this_ext)
243d38f6f31Sdsl 			break;
244d38f6f31Sdsl 		this_ext = next_ext;
245d38f6f31Sdsl 	}
246d38f6f31Sdsl #ifdef COMPAT_386BSD_MBRPART
247d38f6f31Sdsl 	if (this_ext == 0 && dp_386bsd != -1)
2481214d130Schristos 		return (*actn)(a, &ptns[dp_386bsd], ap_386bsd, 0);
249d38f6f31Sdsl #endif
250d38f6f31Sdsl 	return SCAN_CONTINUE;
251d38f6f31Sdsl }
252d38f6f31Sdsl 
253339f5c57Sreinoud 
254339f5c57Sreinoud static void
scan_iso_vrs_session(mbr_args_t * a,uint32_t first_sector,int * is_iso9660,int * is_udf)255339f5c57Sreinoud scan_iso_vrs_session(mbr_args_t *a, uint32_t first_sector,
256339f5c57Sreinoud 	int *is_iso9660, int *is_udf)
257339f5c57Sreinoud {
258339f5c57Sreinoud 	struct vrs_desc *vrsd;
259339f5c57Sreinoud 	uint64_t vrs;
260339f5c57Sreinoud 	int sector_size;
2610da18cc3Sreinoud 	int blks, inc;
262339f5c57Sreinoud 
263339f5c57Sreinoud 	sector_size = a->lp->d_secsize;
264339f5c57Sreinoud 	blks = sector_size / DEV_BSIZE;
2650da18cc3Sreinoud 	inc  = MAX(1, 2048 / sector_size);
266339f5c57Sreinoud 
267339f5c57Sreinoud 	/* by definition */
268339f5c57Sreinoud 	vrs = ((32*1024 + sector_size - 1) / sector_size)
269339f5c57Sreinoud 	        + first_sector;
270339f5c57Sreinoud 
271339f5c57Sreinoud 	/* read first vrs sector */
272a0d3b4c9Sreinoud 	if (read_sector(a, vrs * blks, 1))
273339f5c57Sreinoud 		return;
274339f5c57Sreinoud 
275339f5c57Sreinoud 	/* skip all CD001 records */
276339f5c57Sreinoud 	vrsd = a->bp->b_data;
2770da18cc3Sreinoud 	/* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */
278339f5c57Sreinoud 	while (memcmp(vrsd->identifier, "CD001", 5) == 0) {
279339f5c57Sreinoud 		/* for sure */
280339f5c57Sreinoud 		*is_iso9660 = first_sector;
281339f5c57Sreinoud 
2820da18cc3Sreinoud 		vrs += inc;
283a0d3b4c9Sreinoud 		if (read_sector(a, vrs * blks, 1))
284339f5c57Sreinoud 			return;
285339f5c57Sreinoud 	}
286339f5c57Sreinoud 
287339f5c57Sreinoud 	/* search for BEA01 */
288339f5c57Sreinoud 	vrsd = a->bp->b_data;
289339f5c57Sreinoud 	/* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */
290339f5c57Sreinoud 	if (memcmp(vrsd->identifier, "BEA01", 5))
291339f5c57Sreinoud 		return;
292339f5c57Sreinoud 
293339f5c57Sreinoud 	/* read successor */
2940da18cc3Sreinoud 	vrs += inc;
295a0d3b4c9Sreinoud 	if (read_sector(a, vrs * blks, 1))
296339f5c57Sreinoud 		return;
297339f5c57Sreinoud 
298339f5c57Sreinoud 	/* check for NSR[23] */
299339f5c57Sreinoud 	vrsd = a->bp->b_data;
3000da18cc3Sreinoud 	/* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */
301339f5c57Sreinoud 	if (memcmp(vrsd->identifier, "NSR0", 4))
302339f5c57Sreinoud 		return;
303339f5c57Sreinoud 
304339f5c57Sreinoud 	*is_udf = first_sector;
305339f5c57Sreinoud }
306339f5c57Sreinoud 
307339f5c57Sreinoud 
308339f5c57Sreinoud /*
309339f5c57Sreinoud  * Scan for ISO Volume Recognition Sequences
310339f5c57Sreinoud  */
311339f5c57Sreinoud 
312339f5c57Sreinoud static int
scan_iso_vrs(mbr_args_t * a)313339f5c57Sreinoud scan_iso_vrs(mbr_args_t *a)
314339f5c57Sreinoud {
315339f5c57Sreinoud 	struct mmc_discinfo  di;
316339f5c57Sreinoud 	struct mmc_trackinfo ti;
317339f5c57Sreinoud 	dev_t dev;
318339f5c57Sreinoud 	uint64_t sector;
319339f5c57Sreinoud 	int is_iso9660, is_udf;
320339f5c57Sreinoud 	int tracknr, sessionnr;
321339f5c57Sreinoud 	int new_session, error;
322339f5c57Sreinoud 
323339f5c57Sreinoud 	is_iso9660 = is_udf = -1;
324339f5c57Sreinoud 
325339f5c57Sreinoud 	/* parse all sessions of disc if we're on a SCSI MMC device */
326339f5c57Sreinoud 	if (a->lp->d_flags & D_SCSI_MMC) {
327339f5c57Sreinoud 		/* get disc info */
328339f5c57Sreinoud 		dev = a->bp->b_dev;
329339f5c57Sreinoud 		error = bdev_ioctl(dev, MMCGETDISCINFO, &di, FKIOCTL, curlwp);
330339f5c57Sreinoud 		if (error)
331339f5c57Sreinoud 			return SCAN_CONTINUE;
332339f5c57Sreinoud 
333*9703a26eSgutteridge 		/* go through all (data) tracks */
334339f5c57Sreinoud 		sessionnr = -1;
335339f5c57Sreinoud 		for (tracknr = di.first_track;
336339f5c57Sreinoud 		    tracknr <= di.first_track_last_session; tracknr++)
337339f5c57Sreinoud 		{
338339f5c57Sreinoud 			ti.tracknr = tracknr;
339339f5c57Sreinoud 			error = bdev_ioctl(dev, MMCGETTRACKINFO, &ti,
340339f5c57Sreinoud 					FKIOCTL, curlwp);
341339f5c57Sreinoud 			if (error)
342339f5c57Sreinoud 				return SCAN_CONTINUE;
343339f5c57Sreinoud 			new_session = (ti.sessionnr != sessionnr);
344339f5c57Sreinoud 			sessionnr = ti.sessionnr;
345339f5c57Sreinoud 			if (new_session) {
346339f5c57Sreinoud 				if (ti.flags & MMC_TRACKINFO_BLANK)
347339f5c57Sreinoud 					continue;
348339f5c57Sreinoud 				if (!(ti.flags & MMC_TRACKINFO_DATA))
349339f5c57Sreinoud 					continue;
350339f5c57Sreinoud 				sector = ti.track_start;
351339f5c57Sreinoud 				scan_iso_vrs_session(a, sector,
352339f5c57Sreinoud 					&is_iso9660, &is_udf);
353339f5c57Sreinoud 			}
354339f5c57Sreinoud 		}
355339f5c57Sreinoud 	} else {
356339f5c57Sreinoud 		/* try start of disc */
357339f5c57Sreinoud 		sector = 0;
358339f5c57Sreinoud 		scan_iso_vrs_session(a, sector, &is_iso9660, &is_udf);
359339f5c57Sreinoud 	}
360339f5c57Sreinoud 
361339f5c57Sreinoud 	if ((is_iso9660 < 0) && (is_udf < 0))
362339f5c57Sreinoud 		return SCAN_CONTINUE;
363339f5c57Sreinoud 
36469f4b23bSreinoud 	strncpy(a->lp->d_typename, "iso partition", 16);
36569f4b23bSreinoud 
366106bf8d8Smlelstv 	/* adjust session information for iso9660 partition */
367339f5c57Sreinoud 	if (is_iso9660 >= 0) {
368339f5c57Sreinoud 		/* set 'a' partition to iso9660 */
369339f5c57Sreinoud 		a->lp->d_partitions[0].p_offset = 0;
370339f5c57Sreinoud 		a->lp->d_partitions[0].p_size   = a->lp->d_secperunit;
371339f5c57Sreinoud 		a->lp->d_partitions[0].p_cdsession = is_iso9660;
372339f5c57Sreinoud 		a->lp->d_partitions[0].p_fstype = FS_ISO9660;
373339f5c57Sreinoud 	}
374339f5c57Sreinoud 
375339f5c57Sreinoud 	/* UDF doesn't care about the cd session specified here */
376339f5c57Sreinoud 
377339f5c57Sreinoud 	return SCAN_FOUND;
378339f5c57Sreinoud }
379339f5c57Sreinoud 
380339f5c57Sreinoud 
381d38f6f31Sdsl /*
382d38f6f31Sdsl  * Attempt to read a disk label from a device
383d38f6f31Sdsl  * using the indicated strategy routine.
384d38f6f31Sdsl  * The label must be partly set up before this:
385d38f6f31Sdsl  * secpercyl, secsize and anything required for a block i/o read
386d38f6f31Sdsl  * operation in the driver's strategy/start routines
387d38f6f31Sdsl  * must be filled in before calling us.
388d38f6f31Sdsl  *
389d38f6f31Sdsl  * If dos partition table requested, attempt to load it and
390d38f6f31Sdsl  * find disklabel inside a DOS partition. Also, if bad block
391d38f6f31Sdsl  * table needed, attempt to extract it as well. Return buffer
392d38f6f31Sdsl  * for use in signalling errors if requested.
393d38f6f31Sdsl  *
394d38f6f31Sdsl  * Returns null on success and an error string on failure.
395d38f6f31Sdsl  */
396d38f6f31Sdsl const char *
readdisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * osdep)397a89c49eeSdsl readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
398a89c49eeSdsl     struct cpu_disklabel *osdep)
399d38f6f31Sdsl {
400d38f6f31Sdsl 	int rval;
401d38f6f31Sdsl 	int i;
402d38f6f31Sdsl 	mbr_args_t a;
403d38f6f31Sdsl 
40440bac108Sdsl 	memset(&a, 0, sizeof a);
405d38f6f31Sdsl 	a.lp = lp;
406d38f6f31Sdsl 	a.strat = strat;
407db0d6983Sdsl 	a.action = READ_LABEL;
408d38f6f31Sdsl 
409d38f6f31Sdsl 	/* minimal requirements for architypal disk label */
410d38f6f31Sdsl 	if (lp->d_secsize == 0)
411d38f6f31Sdsl 		lp->d_secsize = DEV_BSIZE;
412d38f6f31Sdsl 	if (lp->d_secperunit == 0)
413d38f6f31Sdsl 		lp->d_secperunit = 0x1fffffff;
4141ee8f4a4Sdyoung 	a.secperunit = lp->d_secperunit;
415d38f6f31Sdsl 	lp->d_npartitions = RAW_PART + 1;
416d38f6f31Sdsl 	for (i = 0; i < RAW_PART; i++) {
417d38f6f31Sdsl 		lp->d_partitions[i].p_size = 0;
418d38f6f31Sdsl 		lp->d_partitions[i].p_offset = 0;
419d38f6f31Sdsl 	}
420d38f6f31Sdsl 	if (lp->d_partitions[RAW_PART].p_size == 0)
421db0d6983Sdsl 		lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
422d38f6f31Sdsl 	lp->d_partitions[RAW_PART].p_offset = 0;
423d38f6f31Sdsl 
424d38f6f31Sdsl 	/*
425d38f6f31Sdsl 	 * Set partition 'a' to be the whole disk.
426d38f6f31Sdsl 	 * Cleared if we find an mbr or a netbsd label.
427d38f6f31Sdsl 	 */
428d38f6f31Sdsl 	lp->d_partitions[0].p_size = lp->d_partitions[RAW_PART].p_size;
429d38f6f31Sdsl 	lp->d_partitions[0].p_fstype = FS_BSDFFS;
430d38f6f31Sdsl 
431339f5c57Sreinoud 	/*
432a0d3b4c9Sreinoud 	 * Get a buffer big enough to read a disklabel in and initialize it
4338257134aSpooka 	 * make it three sectors long for the validate_label(); see comment at
434a0d3b4c9Sreinoud 	 * start of file.
435339f5c57Sreinoud 	 */
4368257134aSpooka 	a.bp = geteblk(SCANBLOCKS * (int)lp->d_secsize);
437d38f6f31Sdsl 	a.bp->b_dev = dev;
438d38f6f31Sdsl 
439d38f6f31Sdsl 	if (osdep)
440d38f6f31Sdsl 		/*
441d38f6f31Sdsl 		 * Scan mbr searching for netbsd partition and saving
442d38f6f31Sdsl 		 * bios partition information to use if the netbsd one
443d38f6f31Sdsl 		 * is absent.
444d38f6f31Sdsl 		 */
445d38f6f31Sdsl 		rval = scan_mbr(&a, look_netbsd_part);
446d38f6f31Sdsl 	else
447d38f6f31Sdsl 		rval = SCAN_CONTINUE;
448d38f6f31Sdsl 
44940bac108Sdsl 	if (rval == SCAN_CONTINUE) {
450d38f6f31Sdsl 		/* Look at start of disk */
451db0d6983Sdsl 		rval = validate_label(&a, 0);
452d38f6f31Sdsl 	}
453d38f6f31Sdsl 
454339f5c57Sreinoud 	if (rval == SCAN_CONTINUE) {
455339f5c57Sreinoud 		rval = scan_iso_vrs(&a);
456339f5c57Sreinoud 	}
45740bac108Sdsl #if 0
45840bac108Sdsl 	/*
45940bac108Sdsl 	 * Save sector where we found the label for the 'don't overwrite
46040bac108Sdsl 	 * the label' check in bounds_check_with_label.
46140bac108Sdsl 	 */
46240bac108Sdsl 	if (rval == SCAN_FOUND)
46340bac108Sdsl 		xxx->label_sector = a.label_sector;
46440bac108Sdsl #endif
46540bac108Sdsl 
466d38f6f31Sdsl 	/* Obtain bad sector table if requested and present */
46709dbb89bSpooka #ifdef __HAVE_DISKLABEL_DKBAD
468d38f6f31Sdsl 	if (rval == SCAN_FOUND && osdep && (lp->d_flags & D_BADSECT)) {
46909dbb89bSpooka 		struct dkbad *bdp, *db;
470d38f6f31Sdsl 		int blkno;
471d38f6f31Sdsl 
472d38f6f31Sdsl 		bdp = &osdep->bad;
473d38f6f31Sdsl 		i = 0;
474d38f6f31Sdsl 		rval = SCAN_ERROR;
475d38f6f31Sdsl 		do {
476d38f6f31Sdsl 			/* read a bad sector table */
477d38f6f31Sdsl 			blkno = lp->d_secperunit - lp->d_nsectors + i;
478d38f6f31Sdsl 			if (lp->d_secsize > DEV_BSIZE)
479d38f6f31Sdsl 				blkno *= lp->d_secsize / DEV_BSIZE;
480d38f6f31Sdsl 			else
481d38f6f31Sdsl 				blkno /= DEV_BSIZE / lp->d_secsize;
482d38f6f31Sdsl 			/* if successful, validate, otherwise try another */
483db0d6983Sdsl 			if (read_sector(&a, blkno, 1)) {
484d38f6f31Sdsl 				a.msg = "bad sector table I/O error";
485d38f6f31Sdsl 				continue;
486d38f6f31Sdsl 			}
487d38f6f31Sdsl 			db = (struct dkbad *)(a.bp->b_data);
488d38f6f31Sdsl #define DKBAD_MAGIC 0x4321
489d38f6f31Sdsl 			if (db->bt_mbz != 0 || db->bt_flag != DKBAD_MAGIC) {
490d38f6f31Sdsl 				a.msg = "bad sector table corrupted";
491d38f6f31Sdsl 				continue;
492d38f6f31Sdsl 			}
493d38f6f31Sdsl 			rval = SCAN_FOUND;
494d38f6f31Sdsl 			*bdp = *db;
495d38f6f31Sdsl 			break;
49666fefd11Sad 		} while (a.bp->b_error && (i += 2) < 10 &&
497d38f6f31Sdsl 			i < lp->d_nsectors);
498d38f6f31Sdsl 	}
49909dbb89bSpooka #endif /* __HAVE_DISKLABEL_DKBAD */
500d38f6f31Sdsl 
5019f56dfa5Sad 	brelse(a.bp, 0);
502db0d6983Sdsl 	if (rval == SCAN_ERROR || rval == SCAN_CONTINUE)
503cb6200f8Scube 		return a.msg;
504d38f6f31Sdsl 	return NULL;
505d38f6f31Sdsl }
506d38f6f31Sdsl 
507d38f6f31Sdsl static int
look_netbsd_part(mbr_args_t * a,mbr_partition_t * dp,int slot,uint ext_base)508a89c49eeSdsl look_netbsd_part(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base)
509d38f6f31Sdsl {
510d38f6f31Sdsl 	struct partition *pp;
511d38f6f31Sdsl 	int ptn_base = ext_base + le32toh(dp->mbrp_start);
512d38f6f31Sdsl 	int rval;
513d38f6f31Sdsl 
514d38f6f31Sdsl 	if (
515d38f6f31Sdsl #ifdef COMPAT_386BSD_MBRPART
5161c33b4e6Slukem 	    dp->mbrp_type == MBR_PTYPE_386BSD ||
517d38f6f31Sdsl #endif
5181c33b4e6Slukem 	    dp->mbrp_type == MBR_PTYPE_NETBSD) {
519db0d6983Sdsl 		rval = validate_label(a, ptn_base);
520d38f6f31Sdsl 
5215f7cb96aSdsl #if RAW_PART == 3
522d38f6f31Sdsl 		/* Put actual location where we found the label into ptn 2 */
5235f7cb96aSdsl 		if (rval == SCAN_FOUND || a->lp->d_partitions[2].p_size == 0) {
524d38f6f31Sdsl 			a->lp->d_partitions[2].p_size = le32toh(dp->mbrp_size);
525d38f6f31Sdsl 			a->lp->d_partitions[2].p_offset = ptn_base;
526d38f6f31Sdsl 		}
5275f7cb96aSdsl #endif
528d38f6f31Sdsl 
529d38f6f31Sdsl 		/* If we got a netbsd label look no further */
530d38f6f31Sdsl 		if (rval == SCAN_FOUND)
531d38f6f31Sdsl 			return rval;
532d38f6f31Sdsl 	}
533d38f6f31Sdsl 
534d38f6f31Sdsl 	/* Install main partitions into e..h and extended into i+ */
535d38f6f31Sdsl 	if (ext_base == 0)
536d38f6f31Sdsl 		slot += 4;
537d38f6f31Sdsl 	else {
5381c33b4e6Slukem 		slot = 4 + MBR_PART_COUNT;
539d38f6f31Sdsl 		pp = &a->lp->d_partitions[slot];
540d38f6f31Sdsl 		for (; slot < MAXPARTITIONS; pp++, slot++) {
541d38f6f31Sdsl 			/* This gets called twice - avoid duplicates */
542d38f6f31Sdsl 			if (pp->p_offset == ptn_base &&
543d38f6f31Sdsl 			    pp->p_size == le32toh(dp->mbrp_size))
544d38f6f31Sdsl 				break;
545d38f6f31Sdsl 			if (pp->p_size == 0)
546d38f6f31Sdsl 				break;
547d38f6f31Sdsl 		}
548d38f6f31Sdsl 	}
549d38f6f31Sdsl 
550d38f6f31Sdsl 	if (slot < MAXPARTITIONS) {
551d38f6f31Sdsl 		/* Stop 'a' being the entire disk */
552d38f6f31Sdsl 		a->lp->d_partitions[0].p_size = 0;
553d38f6f31Sdsl 		a->lp->d_partitions[0].p_fstype = 0;
554d38f6f31Sdsl 
555d38f6f31Sdsl 		/* save partition info */
556d38f6f31Sdsl 		pp = &a->lp->d_partitions[slot];
557d38f6f31Sdsl 		pp->p_offset = ptn_base;
558d38f6f31Sdsl 		pp->p_size = le32toh(dp->mbrp_size);
5591c33b4e6Slukem 		pp->p_fstype = xlat_mbr_fstype(dp->mbrp_type);
560d38f6f31Sdsl 
561d38f6f31Sdsl 		if (slot >= a->lp->d_npartitions)
562d38f6f31Sdsl 			a->lp->d_npartitions = slot + 1;
563d38f6f31Sdsl 	}
564d38f6f31Sdsl 
565d38f6f31Sdsl 	return SCAN_CONTINUE;
566d38f6f31Sdsl }
567d38f6f31Sdsl 
56827e7f6e2Skamil __noubsan
56978497bf9Schristos static bool
check_label_magic(const struct disklabel * dlp,uint32_t diskmagic)57078497bf9Schristos check_label_magic(const struct disklabel *dlp, uint32_t diskmagic)
57178497bf9Schristos {
5722783cd3dSkamil 	return memcmp(&dlp->d_magic, &diskmagic, sizeof(diskmagic)) == 0 &&
5732783cd3dSkamil 	    memcmp(&dlp->d_magic2, &diskmagic, sizeof(diskmagic)) == 0;
57478497bf9Schristos }
575d38f6f31Sdsl 
576a0bccedeSrin #ifdef DISKLABEL_EI
577a0bccedeSrin /*
578a0bccedeSrin  * - For read, convert a label to the native byte order.
579a0bccedeSrin  * - For update or write, if a label already exists, keep its byte order.
580a0bccedeSrin  *   Otherwise, write a new label in the native byte order.
581a0bccedeSrin  */
582a0bccedeSrin #endif
583d38f6f31Sdsl static int
validate_label(mbr_args_t * a,uint label_sector)584db0d6983Sdsl validate_label(mbr_args_t *a, uint label_sector)
585d38f6f31Sdsl {
58638522d36Skamil 	struct disklabel *dlp;
587db0d6983Sdsl 	char *dlp_lim, *dlp_byte;
588d38f6f31Sdsl 	int error;
589a0bccedeSrin #ifdef DISKLABEL_EI
590a0bccedeSrin 	int swapped = 0;
591a0bccedeSrin 	uint16_t npartitions;
592a0bccedeSrin #endif
593d38f6f31Sdsl 
594d38f6f31Sdsl 	/* Next, dig out disk label */
5958257134aSpooka 	if (read_sector(a, label_sector, SCANBLOCKS)) {
596d38f6f31Sdsl 		a->msg = "disk label read failed";
597d38f6f31Sdsl 		return SCAN_ERROR;
598d38f6f31Sdsl 	}
599d38f6f31Sdsl 
600d38f6f31Sdsl 	/* Locate disk label within block and validate */
601d38f6f31Sdsl 	/*
602d38f6f31Sdsl 	 * XXX (dsl) This search may be a waste of time, a lot of other i386
603d38f6f31Sdsl 	 * code assumes the label is at offset LABELOFFSET (=0) in the sector.
604d38f6f31Sdsl 	 *
60540bac108Sdsl 	 * If we want to support disks from other netbsd ports, then the
606d38f6f31Sdsl 	 * code should also allow for a shorter label nearer the end of
607d38f6f31Sdsl 	 * the disk sector, and (IIRC) labels within 8k of the disk start.
608d38f6f31Sdsl 	 */
60940bac108Sdsl 	dlp = (void *)a->bp->b_data;
61053524e44Schristos 	dlp_lim = (char *)a->bp->b_data + a->bp->b_bcount - sizeof *dlp;
611789917a9Schristos 	for (;; dlp = (void *)((char *)dlp + sizeof(uint32_t))) {
612db0d6983Sdsl 		if ((char *)dlp > dlp_lim) {
613db0d6983Sdsl 			if (a->action != WRITE_LABEL)
61440bac108Sdsl 				return SCAN_CONTINUE;
615fcddf71fSwiz 			/* Write at arch. dependent default location */
61653524e44Schristos 			dlp_byte = (char *)a->bp->b_data + LABELOFFSET;
617db0d6983Sdsl 			if (label_sector)
618db0d6983Sdsl 				dlp_byte += MBR_LABELSECTOR * a->lp->d_secsize;
619db0d6983Sdsl 			else
620db0d6983Sdsl 				dlp_byte += LABELSECTOR * a->lp->d_secsize;
621db0d6983Sdsl 			dlp = (void *)dlp_byte;
622db0d6983Sdsl 			break;
623db0d6983Sdsl 		}
62438522d36Skamil 		if (!check_label_magic(dlp, DISKMAGIC))
625a0bccedeSrin #ifdef DISKLABEL_EI
626a0bccedeSrin 		{
62738522d36Skamil 			if (!check_label_magic(dlp, bswap32(DISKMAGIC)))
628d38f6f31Sdsl 				continue;
629a0bccedeSrin 
630a0bccedeSrin 			/*
631a0bccedeSrin 			 * The label is in the other byte order. We need to
632a0bccedeSrin 			 * checksum before swapping the byte order.
633a0bccedeSrin 			 */
63438522d36Skamil 			npartitions = bswap16(dlp->d_npartitions);
635a0bccedeSrin 			if (npartitions > MAXPARTITIONS ||
63638522d36Skamil 			    dkcksum_sized(dlp, npartitions) != 0)
637a0bccedeSrin 				goto corrupted;
638a0bccedeSrin 
639a0bccedeSrin 			swapped = 1;
640a0bccedeSrin 		}
641a0bccedeSrin #else
642a0bccedeSrin 			continue;
643a0bccedeSrin #endif
64438522d36Skamil 		else if (dlp->d_npartitions > MAXPARTITIONS ||
64538522d36Skamil 			 dkcksum(dlp) != 0) {
646a0bccedeSrin #ifdef DISKLABEL_EI
647a0bccedeSrin corrupted:
648a0bccedeSrin #endif
649d38f6f31Sdsl 			a->msg = "disk label corrupted";
650d38f6f31Sdsl 			continue;
651d38f6f31Sdsl 		}
65240bac108Sdsl 		break;
65340bac108Sdsl 	}
65440bac108Sdsl 
655db0d6983Sdsl 	switch (a->action) {
656d38f6f31Sdsl 	case READ_LABEL:
657a0bccedeSrin #ifdef DISKLABEL_EI
658a0bccedeSrin 		if (swapped)
6594a1c505cSmrg 			disklabel_swap(a->lp, dlp);
660a0bccedeSrin 		else
66138522d36Skamil 			*a->lp = *dlp;
662a0bccedeSrin #else
66338522d36Skamil 		*a->lp = *dlp;
664a0bccedeSrin #endif
6651ee8f4a4Sdyoung 		if ((a->msg = convertdisklabel(a->lp, a->strat, a->bp,
6661ee8f4a4Sdyoung 		                              a->secperunit)) != NULL)
6671ee8f4a4Sdyoung 			return SCAN_ERROR;
66840bac108Sdsl 		a->label_sector = label_sector;
66940bac108Sdsl 		return SCAN_FOUND;
67040bac108Sdsl 	case UPDATE_LABEL:
671d38f6f31Sdsl 	case WRITE_LABEL:
672a0bccedeSrin #ifdef DISKLABEL_EI
673a0bccedeSrin 		/* DO NOT swap a->lp itself for later references. */
674a0bccedeSrin 		if (swapped)
6754a1c505cSmrg 			disklabel_swap(dlp, a->lp);
676a0bccedeSrin 		else
67738522d36Skamil 			*dlp = *a->lp;
678a0bccedeSrin #else
67938522d36Skamil 		*dlp = *a->lp;
680a0bccedeSrin #endif
6814a780c9aSad 		a->bp->b_oflags &= ~BO_DONE;
6824a780c9aSad 		a->bp->b_flags &= ~B_READ;
683d38f6f31Sdsl 		a->bp->b_flags |= B_WRITE;
684d38f6f31Sdsl 		(*a->strat)(a->bp);
685d38f6f31Sdsl 		error = biowait(a->bp);
686d38f6f31Sdsl 		if (error != 0) {
687d38f6f31Sdsl 			a->error = error;
688d38f6f31Sdsl 			a->msg = "disk label write failed";
689d38f6f31Sdsl 			return SCAN_ERROR;
690d38f6f31Sdsl 		}
69140bac108Sdsl 		a->written++;
69240bac108Sdsl 		/* Write label to all mbr partitions */
693d38f6f31Sdsl 		return SCAN_CONTINUE;
69440bac108Sdsl 	default:
69540bac108Sdsl 		return SCAN_ERROR;
69640bac108Sdsl 	}
697d38f6f31Sdsl }
698d38f6f31Sdsl 
699d38f6f31Sdsl /*
700d38f6f31Sdsl  * Write disk label back to device after modification.
701d38f6f31Sdsl  */
702d38f6f31Sdsl int
writedisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * osdep)703a89c49eeSdsl writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
704a89c49eeSdsl     struct cpu_disklabel *osdep)
705d38f6f31Sdsl {
706d38f6f31Sdsl 	mbr_args_t a;
707d38f6f31Sdsl 
70840bac108Sdsl 	memset(&a, 0, sizeof a);
709d38f6f31Sdsl 	a.lp = lp;
710d38f6f31Sdsl 	a.strat = strat;
711d38f6f31Sdsl 
712d38f6f31Sdsl 	/* get a buffer and initialize it */
7138257134aSpooka 	a.bp = geteblk(SCANBLOCKS * (int)lp->d_secsize);
714d38f6f31Sdsl 	a.bp->b_dev = dev;
715d38f6f31Sdsl 
716db0d6983Sdsl 	/* osdep => we expect an mbr with label in netbsd ptn */
717db0d6983Sdsl 	a.action = osdep != NULL ? WRITE_LABEL : UPDATE_LABEL;
718db0d6983Sdsl 
719db0d6983Sdsl 	/* Write/update the label to every netbsd mbr partition */
72040bac108Sdsl 	scan_mbr(&a, write_netbsd_label);
721d38f6f31Sdsl 
722db0d6983Sdsl 	/* Old write the label at the start of the volume on disks that
723db0d6983Sdsl 	 * don't have a valid mbr (always update an existing one) */
724db0d6983Sdsl 	a.action = a.found_mbr ? UPDATE_LABEL : WRITE_LABEL;
725db0d6983Sdsl 	validate_label(&a, 0);
72640bac108Sdsl 
72740bac108Sdsl 	if (a.written == 0 && a.error == 0)
728d38f6f31Sdsl 		a.error = ESRCH;
729d38f6f31Sdsl 
7309f56dfa5Sad 	brelse(a.bp, 0);
731d38f6f31Sdsl 	return a.error;
732d38f6f31Sdsl }
733d38f6f31Sdsl 
734d38f6f31Sdsl static int
write_netbsd_label(mbr_args_t * a,mbr_partition_t * dp,int slot,uint ext_base)7351a7bc55dSyamt write_netbsd_label(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base)
736d38f6f31Sdsl {
737d38f6f31Sdsl 	int ptn_base = ext_base + le32toh(dp->mbrp_start);
738d38f6f31Sdsl 
7391c33b4e6Slukem 	if (dp->mbrp_type != MBR_PTYPE_NETBSD)
740d38f6f31Sdsl 		return SCAN_CONTINUE;
741d38f6f31Sdsl 
742db0d6983Sdsl 	return validate_label(a, ptn_base);
743d38f6f31Sdsl }
744