1/* $NetBSD: disksubr.c,v 1.16 2007/10/17 19:54:15 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 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.16 2007/10/17 19:54:15 garbled Exp $"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/buf.h> 38#include <sys/disklabel.h> 39#include <sys/disk.h> 40 41/* 42 * Attempt to read a disk label from a device 43 * using the indicated strategy routine. 44 * The label must be partly set up before this: 45 * secpercyl and anything required in the strategy routine 46 * (e.g., sector size) must be filled in before calling us. 47 * Returns null on success and an error string on failure. 48 */ 49const char * 50readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 51 struct cpu_disklabel *clp) 52{ 53 struct buf *bp; 54 struct disklabel *dlp; 55 struct dkbad *bdp; 56 const char *msg = NULL; 57 uint32_t secperunit; 58 int i; 59 60 /* minimal requirements for archtypal disk label */ 61 if (lp->d_secsize == 0) 62 lp->d_secsize = DEV_BSIZE; 63 if (lp->d_secperunit == 0) 64 lp->d_secperunit = 0x1fffffff; 65 lp->d_npartitions = RAW_PART + 1; 66 if (lp->d_partitions[RAW_PART].p_size == 0) 67 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 68 lp->d_partitions[RAW_PART].p_offset = 0; 69 70 secperunit = lp->d_secperunit; 71 72 /* obtain buffer to probe drive with */ 73 bp = geteblk((int)lp->d_secsize); 74 bp->b_dev = dev; 75 76 /* Next, dig out disk label. If successful, locate disk 77 * label within block and validate. 78 */ 79 if (disk_read_sectors(strat, lp, bp, LABELSECTOR, 1) != 0) { 80 msg = "disk label read error"; 81 goto done; 82 } 83 84 for (dlp = (struct disklabel *)bp->b_data; 85 dlp <= (struct disklabel *)((char *)bp->b_data + lp->d_secsize - 86 sizeof(*dlp)); 87 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 88 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { 89 if (msg == NULL) 90 msg = "no disk label"; 91 } else if (dlp->d_npartitions > MAXPARTITIONS || 92 dkcksum(dlp) != 0) 93 msg = "disk label corrupted"; 94 else { 95 *lp = *dlp; 96 msg = NULL; 97 break; 98 } 99 } 100 101 if (msg) 102 goto done; 103 104 if ((msg = convertdisklabel(lp, strat, bp, secperunit)) != NULL) 105 goto done; 106 107 /* obtain bad sector table if requested and present */ 108 if (clp && (bdp = &clp->bad) != NULL && (lp->d_flags & D_BADSECT)) { 109 struct dkbad *db; 110 111 i = 0; 112 do { 113 /* read a bad sector table */ 114 bp->b_oflags &= ~(BO_DONE); 115 bp->b_flags |= B_READ; 116 bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i; 117 if (lp->d_secsize > DEV_BSIZE) 118 bp->b_blkno *= lp->d_secsize / DEV_BSIZE; 119 else 120 bp->b_blkno /= DEV_BSIZE / lp->d_secsize; 121 bp->b_bcount = lp->d_secsize; 122 bp->b_cylinder = lp->d_ncylinders - 1; 123 (*strat)(bp); 124 125 /* if successful, validate, otherwise try another */ 126 if (biowait(bp)) { 127 msg = "bad sector table I/O error"; 128 } else { 129 db = (struct dkbad *)(bp->b_data); 130#define DKBAD_MAGIC 0x4321 131 if (db->bt_mbz == 0 132 && db->bt_flag == DKBAD_MAGIC) { 133 msg = NULL; 134 *bdp = *db; 135 break; 136 } else 137 msg = "bad sector table corrupted"; 138 } 139 } while (bp->b_error != 0 && (i += 2) < 10 && 140 i < lp->d_nsectors); 141 } 142 143done: 144 brelse(bp, 0); 145 return (msg); 146} 147 148/* 149 * Check new disk label for sensibility before setting it. 150 */ 151int 152setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, 153 struct cpu_disklabel *clp) 154{ 155 int i; 156 struct partition *opp, *npp; 157 158 /* sanity clause */ 159 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 || 160 (nlp->d_secsize % DEV_BSIZE) != 0) 161 return(EINVAL); 162 163 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 164 dkcksum(nlp) != 0) 165 return (EINVAL); 166 167 while ((i = ffs(openmask)) != 0) { 168 i--; 169 openmask &= ~(1 << i); 170 if (nlp->d_npartitions <= i) 171 return (EBUSY); 172 opp = &olp->d_partitions[i]; 173 npp = &nlp->d_partitions[i]; 174 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 175 return (EBUSY); 176 /* 177 * Copy internally-set partition information 178 * if new label doesn't include it. XXX 179 */ 180 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 181 npp->p_fstype = opp->p_fstype; 182 npp->p_fsize = opp->p_fsize; 183 npp->p_frag = opp->p_frag; 184 npp->p_cpg = opp->p_cpg; 185 } 186 } 187 nlp->d_checksum = 0; 188 nlp->d_checksum = dkcksum(nlp); 189 *olp = *nlp; 190 return (0); 191} 192 193/* 194 * Write disk label back to device after modification. 195 * this means write out the Rigid disk blocks to represent the 196 * label. Hope the user was carefull. 197 */ 198int 199writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 200 struct cpu_disklabel *clp) 201{ 202 struct buf *bp; 203 struct disklabel *dlp; 204 int error = 0; 205 206 bp = geteblk((int)lp->d_secsize); 207 bp->b_dev = dev; 208 bp->b_blkno = LABELSECTOR; 209 bp->b_cylinder = 0; 210 bp->b_bcount = lp->d_secsize; 211 bp->b_flags |= B_READ; /* get current label */ 212 (*strat)(bp); 213 if ((error = biowait(bp)) != 0) 214 goto done; 215 216 dlp = (struct disklabel *)((char *)bp->b_data + LABELOFFSET); 217 *dlp = *lp; /* struct assignment */ 218 219 bp->b_oflags &= ~(BO_DONE); 220 bp->b_flags &= ~(B_READ); 221 bp->b_flags |= B_WRITE; 222 (*strat)(bp); 223 error = biowait(bp); 224 225done: 226 brelse(bp, 0); 227 return (error); 228} 229