1/*	$NetBSD: disksubr.c,v 1.56 2007/10/17 19:55:14 garbled 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 * Copyright (C) 1993	Allen K. Briggs, Chris P. Caputo,
35 *			Michael L. Finch, Bradley A. Grantham, and
36 *			Lawrence A. Kesteloot
37 * All rights reserved.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 *    notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 *    notice, this list of conditions and the following disclaimer in the
46 *    documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 *    must display the following acknowledgement:
49 *	This product includes software developed by the Alice Group.
50 * 4. The names of the Alice Group or any of its members may not be used
51 *    to endorse or promote products derived from this software without
52 *    specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR
55 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
56 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
57 * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT,
58 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
59 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
60 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
61 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
62 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
63 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 *
65 */
66
67#include <sys/cdefs.h>
68__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.56 2007/10/17 19:55:14 garbled Exp $");
69
70#include <sys/param.h>
71#include <sys/systm.h>
72#include <sys/buf.h>
73#include <sys/disk.h>
74#include <sys/disklabel.h>
75#include <sys/bootblock.h>
76#include <sys/syslog.h>
77
78#include <sys/bswap.h>
79
80#define NUM_PARTS 32
81
82#define ROOT_PART 1
83#define UFS_PART 2
84#define SWAP_PART 3
85#define HFS_PART 4
86#define SCRATCH_PART 5
87
88int fat_types[] = {
89	MBR_PTYPE_FAT12, MBR_PTYPE_FAT16S,
90	MBR_PTYPE_FAT16B, MBR_PTYPE_FAT32,
91	MBR_PTYPE_FAT32L, MBR_PTYPE_FAT16L,
92	-1
93};
94
95static int getFreeLabelEntry(struct disklabel *);
96static int whichType(struct part_map_entry *);
97static void setpartition(struct part_map_entry *, struct partition *, int);
98static int getNamedType(struct part_map_entry *, int, struct disklabel *, int,
99	    int, int *);
100static char *read_mac_label(char *, struct disklabel *, int *);
101static char *read_mbr_label(char *, struct disklabel *, int *);
102static const char *read_bsd_label(char *, struct disklabel *, int *);
103
104
105/*
106 * Find an entry in the disk label that is unused and return it
107 * or -1 if no entry
108 */
109static int
110getFreeLabelEntry(struct disklabel *lp)
111{
112	int i;
113
114	for (i = 0; i < MAXPARTITIONS; i++) {
115		if ((i != RAW_PART)
116		    && (lp->d_partitions[i].p_fstype == FS_UNUSED))
117			return i;
118	}
119	return -1;
120}
121
122/*
123 * figure out what the type of the given part is and return it
124 */
125static int
126whichType(struct part_map_entry *part)
127{
128	struct blockzeroblock *bzb;
129	char typestr[32], *s;
130	int type;
131
132	if (part->pmSig != PART_ENTRY_MAGIC || part->pmPartType[0] == '\0')
133		return 0;
134
135	strncpy(typestr, (char *)part->pmPartType, sizeof(typestr));
136	typestr[sizeof(typestr) - 1] = '\0';
137	for (s = typestr; *s; s++)
138		if ((*s >= 'a') && (*s <= 'z'))
139			*s = (*s - 'a' + 'A');
140
141	if (strcmp(PART_TYPE_DRIVER, typestr) == 0 ||
142	    strcmp(PART_TYPE_DRIVER43, typestr) == 0 ||
143	    strcmp(PART_TYPE_DRIVERATA, typestr) == 0 ||
144	    strcmp(PART_TYPE_FWB_COMPONENT, typestr) == 0 ||
145	    strcmp(PART_TYPE_PARTMAP, typestr) == 0)
146		type = 0;
147	else if (strcmp(PART_TYPE_UNIX, typestr) == 0) {
148		/* unix part, swap, root, usr */
149		bzb = (struct blockzeroblock *)(&part->pmBootArgs);
150		if (bzb->bzbMagic != BZB_MAGIC)
151			type = 0;
152		else if (bzb->bzbFlags & BZB_ROOTFS)
153			type = ROOT_PART;
154		else if (bzb->bzbFlags & BZB_USRFS)
155			type = UFS_PART;
156		else if (bzb->bzbType == BZB_TYPESWAP)
157			type = SWAP_PART;
158		else
159			type = SCRATCH_PART;
160	} else if (strcmp(PART_TYPE_MAC, typestr) == 0)
161		type = HFS_PART;
162	else
163		type = SCRATCH_PART;	/* no known type */
164
165	return type;
166}
167
168static void
169setpartition(struct part_map_entry *part, struct partition *pp, int fstype)
170{
171	pp->p_size = part->pmPartBlkCnt;
172	pp->p_offset = part->pmPyPartStart;
173	pp->p_fstype = fstype;
174
175	part->pmPartType[0] = '\0';
176}
177
178static int
179getNamedType(struct part_map_entry *part, int num_parts, struct disklabel *lp,
180    int type, int alt, int *maxslot)
181{
182	struct blockzeroblock *bzb;
183	int i;
184
185	for (i = 0; i < num_parts; i++) {
186		if (whichType(part + i) != type)
187			continue;
188
189		if (type == ROOT_PART) {
190			bzb = (struct blockzeroblock *)
191			    (&(part + i)->pmBootArgs);
192			if (alt >= 0 && alt != bzb->bzbCluster)
193				continue;
194			setpartition(part + i, &lp->d_partitions[0], FS_BSDFFS);
195		} else if (type == UFS_PART) {
196			bzb = (struct blockzeroblock *)
197			    (&(part + i)->pmBootArgs);
198			if (alt >= 0 && alt != bzb->bzbCluster)
199				continue;
200			setpartition(part + i, &lp->d_partitions[6], FS_BSDFFS);
201			if (*maxslot < 6)
202				*maxslot = 6;
203		} else if (type == SWAP_PART) {
204			setpartition(part + i, &lp->d_partitions[1], FS_SWAP);
205			if (*maxslot < 1)
206				*maxslot = 1;
207		} else
208			printf("disksubr.c: can't do type %d\n", type);
209
210		return 0;
211	}
212
213	return -1;
214}
215
216/*
217 * MF --
218 * here's what i'm gonna do:
219 * read in the entire diskpartition table, it may be bigger or smaller
220 * than NUM_PARTS but read that many entries.  Each entry has a magic
221 * number so we'll know if an entry is crap.
222 * next fill in the disklabel with info like this
223 * next fill in the root, usr, and swap parts.
224 * then look for anything else and fit it in.
225 *	A: root
226 *	B: Swap
227 *	C: Whole disk
228 *	G: Usr
229 *
230 *
231 * I'm not entirely sure what netbsd386 wants in c & d
232 * 386bsd wants other stuff, so i'll leave them alone
233 *
234 * AKB -- I added to Mike's original algorithm by searching for a bzbCluster
235 *	of zero for root, first.  This allows A/UX to live on cluster 1 and
236 *	NetBSD to live on cluster 0--regardless of the actual order on the
237 *	disk.  This whole algorithm should probably be changed in the future.
238 */
239
240/*
241 * This uses sector zero.  If this contains what looks like a valid
242 * Macintosh boot sector, we attempt to fill in the disklabel structure
243 * with the partition data from block #1 on.
244 */
245static char *
246read_mac_label(char *dlbuf, struct disklabel *lp, int *match)
247{
248	u_int16_t *sbSigp;
249	struct part_map_entry *part;
250	struct partition *pp;
251	char *msg;
252	int i, slot, maxslot;
253
254	maxslot = 0;
255	*match = 0;
256	msg = NULL;
257
258	sbSigp = (u_int16_t *)dlbuf;
259	if (*sbSigp != DRIVER_MAP_MAGIC)
260		return msg;
261
262	/* Found Macintosh partition magic number; set up disklabel */
263	*match = (-1);
264
265	/* the Macintosh partition table starts at sector #1 */
266	part = (struct part_map_entry *)(dlbuf + DEV_BSIZE);
267
268	/* Fill in standard partitions */
269	lp->d_npartitions = RAW_PART + 1;
270	if (getNamedType(part, NUM_PARTS, lp, ROOT_PART, 0, &maxslot))
271		getNamedType(part, NUM_PARTS, lp, ROOT_PART, -1, &maxslot);
272	if (getNamedType(part, NUM_PARTS, lp, UFS_PART, 0, &maxslot))
273		getNamedType(part, NUM_PARTS, lp, UFS_PART, -1, &maxslot);
274	getNamedType(part, NUM_PARTS, lp, SWAP_PART, -1, &maxslot);
275
276	/* Now get as many of the rest of the partitions as we can */
277	for (i = 0; i < NUM_PARTS; i++) {
278		slot = getFreeLabelEntry(lp);
279		if (slot < 0)
280			break;
281
282		pp = &lp->d_partitions[slot];
283
284		switch (whichType(part + i)) {
285		case ROOT_PART:
286		/*
287		 * another root part will turn into a plain old
288		 * UFS_PART partition, live with it.
289		 */
290		case UFS_PART:
291			setpartition(part + i, pp, FS_BSDFFS);
292			break;
293		case SWAP_PART:
294			setpartition(part + i, pp, FS_SWAP);
295			break;
296		case HFS_PART:
297			setpartition(part + i, pp, FS_HFS);
298			break;
299		case SCRATCH_PART:
300			setpartition(part + i, pp, FS_OTHER);
301			break;
302		default:
303			slot = 0;
304			break;
305		}
306		if (slot > maxslot)
307			maxslot = slot;
308	}
309	lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
310	return msg;
311}
312
313/*
314 * Scan the disk buffer for a DOS style master boot record.
315 * Return if no match; otherwise, set up an in-core disklabel .
316 *
317 * XXX stuff like this really should be MI
318 *
319 * Since FFS is endian sensitive, we pay no effort in attempting to
320 * dig up *BSD/i386 disk labels that may be present on the disk.
321 * Hence anything but DOS partitions is treated as unknown FS type, but
322 * this should suffice to mount_msdos Zip and other removable media.
323 */
324static char *
325read_mbr_label(char *dlbuf, struct disklabel *lp, int *match)
326{
327	struct mbr_partition *dp;
328	struct partition *pp;
329	char *msg;
330	size_t mbr_lbl_off;
331	int i, *ip, slot, maxslot;
332
333	maxslot = 0;
334	*match = 0;
335	msg = NULL;
336
337	if (MBR_MAGIC != bswap16(*(u_int16_t *)(dlbuf + MBR_MAGIC_OFFSET)))
338		return msg;
339
340	/* Found MBR magic number; set up disklabel */
341	*match = (-1);
342	mbr_lbl_off = MBR_BBSECTOR * lp->d_secsize + MBR_PART_OFFSET;
343
344	dp = (struct mbr_partition *)(dlbuf + mbr_lbl_off);
345	for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
346		if (dp->mbrp_type == 0)
347			continue;
348
349		slot = getFreeLabelEntry(lp);
350		maxslot = (slot > maxslot) ? maxslot : slot;
351
352		pp = &lp->d_partitions[slot];
353		pp->p_fstype = FS_OTHER;
354		pp->p_offset = bswap32(dp->mbrp_start);
355		pp->p_size = bswap32(dp->mbrp_size);
356
357		for (ip = fat_types; *ip != -1; ip++) {
358			if (dp->mbrp_type == *ip) {
359				pp->p_fstype = FS_MSDOS;
360				break;
361			}
362		}
363	}
364	lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
365	return msg;
366}
367
368/*
369 * Scan the disk buffer in four byte steps for a native BSD disklabel
370 * (different ports have variable-sized bootcode before the label)
371 */
372static const char *
373read_bsd_label(char *dlbuf, struct disklabel *lp, int *match)
374{
375	struct disklabel *dlp;
376	const char *msg;
377	struct disklabel *blk_start, *blk_end;
378
379	*match = 0;
380	msg = NULL;
381
382	blk_start = (struct disklabel *)dlbuf;
383	blk_end = (struct disklabel *)(dlbuf + (NUM_PARTS << DEV_BSHIFT) -
384	    sizeof(struct disklabel));
385
386	for (dlp = blk_start; dlp <= blk_end;
387	     dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
388		if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC) {
389			/* Sanity check */
390			if (dlp->d_npartitions <= MAXPARTITIONS &&
391			    dkcksum(dlp) == 0) {
392				*lp = *dlp;
393				*match = (-1);
394			} else
395				msg = "Disk label corrupted";
396			break;
397		}
398	}
399	return msg;
400}
401
402/*
403 * Attempt to read a disk label from a device using the indicated strategy
404 * routine.  The label must be partly set up before this: secpercyl and
405 * anything required in the strategy routine (e.g., sector size) must be
406 * filled in before calling us.  Returns null on success and an error
407 * string on failure.
408 */
409const char *
410readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
411    struct cpu_disklabel *osdep)
412{
413	struct buf *bp;
414	const char *msg;
415	int size;
416
417	if (lp->d_secperunit == 0)
418		lp->d_secperunit = 0x1fffffff;
419
420	if (lp->d_secpercyl == 0)
421		return msg = "Zero secpercyl";
422
423	msg = NULL;
424
425	/*
426	 * Read in the first #(NUM_PARTS + 1) blocks of the disk.
427	 * The native Macintosh partition table starts at
428	 * sector #1, but we want #0 too for the BSD label.
429	 */
430
431	size = roundup((NUM_PARTS + 1) << DEV_BSHIFT, lp->d_secsize);
432	bp = geteblk(size);
433
434	bp->b_dev = dev;
435	bp->b_blkno = 0;
436	bp->b_resid = 0;
437	bp->b_bcount = size;
438	bp->b_flags |= B_READ;
439	bp->b_cylinder = 1 / lp->d_secpercyl;
440	(*strat)(bp);
441
442	if (biowait(bp)) {
443		msg = "I/O error reading block zero";
444	} else {
445		int match;
446
447		/* Add any offsets in the table handlers */
448		msg = read_mac_label(bp->b_data, lp, &match);
449		if (!match && msg == NULL)
450			msg = read_mbr_label(bp->b_data, lp, &match);
451		if (!match && msg == NULL)
452			msg = read_bsd_label(bp->b_data, lp, &match);
453		if (!match && msg == NULL)
454			msg = "no disk label";
455	}
456
457	brelse(bp, 0);
458	return (msg);
459}
460
461/*
462 * Check new disk label for sensibility before setting it.
463 */
464int
465setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask,
466    struct cpu_disklabel *osdep)
467{
468#if 0
469	int i;
470	struct partition *opp, *npp;
471
472	/* sanity clause */
473	if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 ||
474	    (nlp->d_secsize % DEV_BSIZE) != 0)
475		return(EINVAL);
476
477	/* special case to allow disklabel to be invalidated */
478	if (nlp->d_magic == 0xffffffff) {
479		*olp = *nlp;
480		return (0);
481	}
482
483	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
484	    dkcksum(nlp) != 0)
485		return (EINVAL);
486
487	/*
488	 * XXX We are missing any sort of check if other partition types,
489	 * e.g. Macintosh or (PC) BIOS, will be overwritten.
490	 */
491
492	while ((i = ffs(openmask)) != 0) {
493		i--;
494		openmask &= ~(1 << i);
495		if (nlp->d_npartitions <= i)
496			return (EBUSY);
497		opp = &olp->d_partitions[i];
498		npp = &nlp->d_partitions[i];
499		if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
500			return (EBUSY);
501		/*
502		 * Copy internally-set partition information
503		 * if new label doesn't include it.		XXX
504		 */
505		if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
506			npp->p_fstype = opp->p_fstype;
507			npp->p_fsize = opp->p_fsize;
508			npp->p_frag = opp->p_frag;
509			npp->p_cpg = opp->p_cpg;
510		}
511	}
512	nlp->d_checksum = 0;
513	nlp->d_checksum = dkcksum(nlp);
514	*olp = *nlp;
515#endif
516	return (0);
517}
518
519/*
520 * Write disk label back to device after modification.
521 *
522 *  MF - 8-14-93 This function is never called.  It is here just in case
523 *  we want to write dos disklabels some day. Really!
524 */
525int
526writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
527    struct cpu_disklabel *osdep)
528{
529#if 0
530	struct buf *bp;
531	struct disklabel *dlp;
532	int labelpart;
533	int error = 0;
534
535	labelpart = DISKPART(dev);
536	if (lp->d_partitions[labelpart].p_offset != 0) {
537		if (lp->d_partitions[0].p_offset != 0)
538			return (EXDEV);	/* not quite right */
539		labelpart = 0;
540	}
541	bp = geteblk((int)lp->d_secsize);
542	bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), labelpart);
543	bp->b_blkno = LABELSECTOR;
544	bp->b_bcount = lp->d_secsize;
545	bp->b_flags |= B_READ;
546	(*strat)(bp);
547	if (error = biowait(bp))
548		goto done;
549	for (dlp = (struct disklabel *)bp->b_data;
550	    dlp <= (struct disklabel *)
551	    ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp));
552	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
553		if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
554		    dkcksum(dlp) == 0) {
555			*dlp = *lp;
556			bp->b_oflags &= ~(BO_DONE);
557			bp->b_flags &= ~(B_READ);
558			bp->b_flags |= B_WRITE;
559			(*strat)(bp);
560			error = biowait(bp);
561			goto done;
562		}
563	}
564	error = ESRCH;
565done:
566	brelse(bp, 0);
567	return (error);
568#else
569	int i;
570
571	/*
572	 * Clear and re-analyze the ondisk Apple Disk Partition Map,
573	 * then recompute the faked incore disk label. This is necessary
574	 * for sysinst, which may have modified the disk layout. We don't
575	 * (yet?) support writing real BSD disk labels, so this hack
576	 * instead causes the DIOCWDINFO ioctl invoked by sysinst to
577	 * update the in-core disk label when it is "written" to disk.
578	 * This code was originally developed by Bob Nestor on 9/13/99.
579	 */
580	lp->d_npartitions = 0;
581	for (i = 0; i < MAXPARTITIONS; i++) {
582		lp->d_partitions[i].p_fstype = FS_UNUSED;
583		lp->d_partitions[i].p_offset = 0;
584		if (i != RAW_PART)
585			lp->d_partitions[i].p_size = 0;
586	}
587	return (readdisklabel(dev, strat, lp, osdep) ? EINVAL : 0);
588#endif
589}
590