Home | History | Annotate | Line # | Download | only in kern
subr_disk_4bsd.c revision 1.3
      1 /*	$NetBSD: subr_disk_4bsd.c,v 1.3 2026/08/07 13:49:24 thorpej Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1982, 1986, 1988, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  * (c) UNIX System Laboratories, Inc.
      7  * All or some portions of this file are derived from material licensed
      8  * to the University of California by American Telephone and Telegraph
      9  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
     10  * the permission of UNIX System Laboratories, Inc.
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted provided that the following conditions
     14  * are met:
     15  * 1. Redistributions of source code must retain the above copyright
     16  *    notice, this list of conditions and the following disclaimer.
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  * 3. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  *
     36  *	@(#)ufs_disksubr.c	8.5 (Berkeley) 1/21/94
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 __KERNEL_RCSID(0, "$NetBSD: subr_disk_4bsd.c,v 1.3 2026/08/07 13:49:24 thorpej Exp $");
     41 
     42 #include <sys/param.h>
     43 #include <sys/systm.h>
     44 #include <sys/buf.h>
     45 #include <sys/disk.h>
     46 #include <sys/disklabel.h>
     47 
     48 /*
     49  * Implementation of readdisklabel() and writedisklabel() for the simple
     50  * "basic scheme from 4BSD".
     51  */
     52 
     53 /*
     54  * We prefer the platform's "native" label sector / offset.  Look for
     55  * it there first.  Otherwise, scan the first 3 disk sectors for the
     56  * label.  When updating a label, we will write it to where we found
     57  * it.  If writing a new label from scratch, we will write it to the
     58  * "native" location.
     59  */
     60 static int label_sectors[] = { LABELSECTOR,  0,  1,  2 };
     61 static int label_offsets[] = { LABELOFFSET, -1, -1, -1 };
     62 
     63 #define	DISKLABEL_SIZE(x)						\
     64 	(offsetof(struct disklabel, d_partitions) +			\
     65 	 (sizeof(struct partition) * (x)))
     66 
     67 /* Smallest 4.4BSD disklabel that was ever in use. */
     68 #define	DISKLABEL_MINSIZE	DISKLABEL_SIZE(8)
     69 
     70 static bool
     71 taste_label(const struct disklabel *lp, void *buf, size_t secsize)
     72 {
     73 	uint16_t npartitions;
     74 
     75 	if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC) {
     76 		return false;
     77 	}
     78 
     79 	npartitions = lp->d_npartitions;
     80 
     81 	if ((uintptr_t)lp + DISKLABEL_SIZE(npartitions) >
     82 	    (uintptr_t)buf + secsize) {
     83 		/* doesn't fit in the sector. */
     84 		return false;
     85 	}
     86 
     87 	if (npartitions > MAXPARTITIONS || dkcksum(lp)) {
     88 		/* disklabel is corrupted. */
     89 		return false;
     90 	}
     91 
     92 	return true;
     93 }
     94 
     95 static struct disklabel *
     96 find_label_in_sector(void *buf, size_t secsize, int offs)
     97 {
     98 	struct disklabel *lp = buf;
     99 	uintptr_t lp_lim = ((uintptr_t)buf + secsize - DISKLABEL_MINSIZE);
    100 
    101 	if (offs != -1) {
    102 		lp = (void *)((uintptr_t)lp + offs);
    103 		return taste_label(lp, buf, secsize) ? lp : NULL;
    104 	}
    105 
    106 	for (;; lp = (void *)((uintptr_t)lp + sizeof(uint32_t))) {
    107 		if ((uintptr_t)lp > lp_lim) {
    108 			/* Not found in this sector. */
    109 			return NULL;
    110 		}
    111 		if (taste_label(lp, buf, secsize)) {
    112 			return lp;
    113 		}
    114 	}
    115 }
    116 
    117 /*
    118  * Attempt to read a disk label from a device using the indicated strategy
    119  * routine.  The label must be partly set up before this: secpercyl and
    120  * anything required in the strategy routine (e.g., sector size) must be
    121  * filled in before calling us.  Returns null on success and an error
    122  * string on failure.
    123  */
    124 const char *
    125 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
    126     struct cpu_disklabel *osdep)
    127 {
    128 	struct buf *bp;
    129 	struct disklabel *dlp;
    130 	const char *msg = NULL;
    131 	int i;
    132 
    133 	if (lp->d_secsize == 0)
    134 		lp->d_secsize = DEV_BSIZE;
    135 	if (lp->d_secperunit == 0)
    136 		lp->d_secperunit = 0x1fffffff;
    137 	if (lp->d_npartitions < RAW_PART + 1)
    138 		lp->d_npartitions = RAW_PART + 1;
    139 	for (i = 0; i < RAW_PART; i++) {
    140 		lp->d_partitions[i].p_size = 0;
    141 		lp->d_partitions[i].p_offset = 0;
    142 	}
    143 	if (lp->d_partitions[RAW_PART].p_size == 0)
    144 		lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
    145 	lp->d_partitions[RAW_PART].p_offset = 0;
    146 
    147 	lp->d_partitions[0].p_size = lp->d_partitions[RAW_PART].p_size;
    148 	lp->d_partitions[0].p_fstype = FS_BSDFFS;
    149 
    150 	bp = geteblk((int)lp->d_secsize);
    151 	bp->b_dev = dev;
    152 
    153 	for (i = 0; i < __arraycount(label_sectors); i++) {
    154 		if (disk_read_sectors(strat, lp, bp, label_sectors[i], 1)) {
    155 			msg = "I/O error";
    156 			goto done;
    157 		}
    158 
    159 		dlp = find_label_in_sector(bp->b_data, DEV_BSIZE,
    160 					   label_offsets[i]);
    161 		if (dlp != NULL) {
    162 			*lp = *dlp;
    163 			msg = NULL;
    164 			break;
    165 		}
    166 	}
    167  done:
    168 	brelse(bp, 0);
    169 	return msg;
    170 }
    171 
    172 /*
    173  * Write disk label back to device after modification.
    174  */
    175 int
    176 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
    177     struct cpu_disklabel *osdep)
    178 {
    179 	struct buf *bp;
    180 	struct disklabel *dlp;
    181 	int labelpart;
    182 	int i, error = 0;
    183 
    184 	labelpart = DISKPART(dev);
    185 	if (lp->d_partitions[labelpart].p_offset != 0) {
    186 		if (lp->d_partitions[0].p_offset != 0)
    187 			return EXDEV;			/* not quite right */
    188 		labelpart = 0;
    189 	}
    190 	bp = geteblk((int)lp->d_secsize);
    191 	bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), labelpart);
    192 
    193 	for (i = 0; i < __arraycount(label_sectors); i++) {
    194 		if ((error = disk_read_sectors(strat, lp, bp,
    195 					       label_sectors[i], 1)) != 0) {
    196 			goto done;
    197 		}
    198 
    199 		dlp = find_label_in_sector(bp->b_data, DEV_BSIZE,
    200 					   label_offsets[i]);
    201 		if (dlp != NULL) {
    202 			*dlp = *lp;
    203 			bp->b_oflags &= ~(BO_DONE);
    204 			bp->b_flags &= ~(B_READ);
    205 			bp->b_flags |= B_WRITE;
    206 			(*strat)(bp);
    207 			error = biowait(bp);
    208 			goto done;
    209 		}
    210 	}
    211 	error = ESRCH;
    212  done:
    213 	brelse(bp, 0);
    214 	return error;
    215 }
    216