1 /* $NetBSD: disksubr.c,v 1.18 2000/05/16 05:45:46 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1995 Leo Weppelman. 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Leo Weppelman. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #ifndef DISKLABEL_NBDA 34 #define DISKLABEL_NBDA /* required */ 35 #endif 36 37 #include "opt_compat_netbsd.h" 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/buf.h> 42 #include <ufs/ufs/dinode.h> 43 #include <ufs/ffs/fs.h> 44 #include <sys/disk.h> 45 #include <sys/disklabel.h> 46 #include <machine/ahdilabel.h> 47 48 /* 49 * BBSIZE in <ufs/ffs/fs.h> must be greater than 50 * or equal to BBMINSIZE in <machine/disklabel.h> 51 */ 52 #if BBSIZE < BBMINSIZE 53 #error BBSIZE smaller than BBMINSIZE 54 #endif 55 56 static void ck_label __P((struct disklabel *, struct cpu_disklabel *)); 57 static int bsd_label __P((dev_t, void (*)(struct buf *), 58 struct disklabel *, u_int, u_int *)); 59 static int ahdi_label __P((dev_t, void (*)(struct buf *), 60 struct disklabel *, struct cpu_disklabel *)); 61 static void ahdi_to_bsd __P((struct disklabel *, struct ahdi_ptbl *)); 62 static u_int ahdi_getparts __P((dev_t, void (*)(struct buf *), u_int, 63 u_int, u_int, struct ahdi_ptbl *)); 64 65 /* 66 * Determine the size of the transfer, and make sure it is 67 * within the boundaries of the partition. Adjust transfer 68 * if needed, and signal errors or early completion. 69 */ 70 int 71 bounds_check_with_label(bp, lp, wlabel) 72 struct buf *bp; 73 struct disklabel *lp; 74 int wlabel; 75 { 76 struct partition *pp; 77 u_int maxsz, sz; 78 79 pp = &lp->d_partitions[DISKPART(bp->b_dev)]; 80 if (bp->b_flags & B_RAW) { 81 if (bp->b_bcount & (lp->d_secsize - 1)) { 82 bp->b_error = EINVAL; 83 bp->b_flags |= B_ERROR; 84 return(-1); 85 } 86 if (lp->d_secsize < DEV_BSIZE) 87 maxsz = pp->p_size / (DEV_BSIZE / lp->d_secsize); 88 else maxsz = pp->p_size * (lp->d_secsize / DEV_BSIZE); 89 sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; 90 } else { 91 maxsz = pp->p_size; 92 sz = (bp->b_bcount + lp->d_secsize - 1) / lp->d_secsize; 93 } 94 95 if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { 96 if (bp->b_blkno == maxsz) { 97 /* 98 * trying to get one block beyond return EOF. 99 */ 100 bp->b_resid = bp->b_bcount; 101 return(0); 102 } 103 if (bp->b_blkno > maxsz || bp->b_blkno < 0) { 104 bp->b_error = EINVAL; 105 bp->b_flags |= B_ERROR; 106 return(-1); 107 } 108 sz = maxsz - bp->b_blkno; 109 110 /* 111 * adjust count down 112 */ 113 if (bp->b_flags & B_RAW) 114 bp->b_bcount = sz << DEV_BSHIFT; 115 else bp->b_bcount = sz * lp->d_secsize; 116 } 117 118 /* 119 * calc cylinder for disksort to order transfers with 120 */ 121 bp->b_cylinder = (bp->b_blkno + pp->p_offset) / lp->d_secpercyl; 122 return(1); 123 } 124 125 /* 126 * Attempt to read a disk label from a device using the 127 * indicated strategy routine. The label must be partly 128 * set up before this: 129 * secpercyl and anything required in the strategy routine 130 * (e.g. sector size) must be filled in before calling us. 131 * Returns NULL on success and an error string on failure. 132 */ 133 char * 134 readdisklabel(dev, strat, lp, clp) 135 dev_t dev; 136 void (*strat)(struct buf *); 137 struct disklabel *lp; 138 struct cpu_disklabel *clp; 139 { 140 int e; 141 142 bzero(clp, sizeof *clp); 143 144 /* 145 * Give some guaranteed validity to the disk label. 146 */ 147 if (lp->d_secsize == 0) 148 lp->d_secsize = DEV_BSIZE; 149 if (lp->d_secperunit == 0) 150 lp->d_secperunit = 0x1fffffff; 151 if (lp->d_secpercyl == 0) 152 return("Zero secpercyl"); 153 154 /* 155 * Some parts of the kernel (see scsipi/cd.c for an example) 156 * assume that stuff they already had setup in d_partitions 157 * is still there after reading the disklabel. Hence the 158 * 'if 0' 159 */ 160 #if 0 161 bzero(lp->d_partitions, sizeof lp->d_partitions); 162 #endif 163 164 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 165 lp->d_npartitions = RAW_PART + 1; 166 lp->d_bbsize = BBSIZE; 167 lp->d_sbsize = SBSIZE; 168 169 #ifdef DISKLABEL_NBDA 170 /* Try the native NetBSD/Atari format first. */ 171 e = bsd_label(dev, strat, lp, 0, &clp->cd_label); 172 #endif 173 #if 0 174 /* Other label formats go here. */ 175 if (e > 0) 176 e = foo_label(dev, strat, lp, ...); 177 #endif 178 #ifdef DISKLABEL_AHDI 179 /* The unprotected AHDI format comes last. */ 180 if (e > 0) 181 e = ahdi_label(dev, strat, lp, clp); 182 #endif 183 if (e < 0) 184 return("I/O error"); 185 186 /* Unknown format or unitialised volume? */ 187 if (e > 0) 188 uprintf("Warning: unknown disklabel format" 189 "- assuming empty disk\n"); 190 191 /* Calulate new checksum. */ 192 lp->d_magic = lp->d_magic2 = DISKMAGIC; 193 lp->d_checksum = 0; 194 lp->d_checksum = dkcksum(lp); 195 196 return(NULL); 197 } 198 199 /* 200 * Check new disk label for sensibility before setting it. 201 */ 202 int 203 setdisklabel(olp, nlp, openmask, clp) 204 struct disklabel *olp, *nlp; 205 u_long openmask; 206 struct cpu_disklabel *clp; 207 { 208 /* special case to allow disklabel to be invalidated */ 209 if (nlp->d_magic == 0xffffffff) { 210 *olp = *nlp; 211 return(0); 212 } 213 214 /* sanity clause */ 215 if (nlp->d_secpercyl == 0 || nlp->d_npartitions > MAXPARTITIONS 216 || nlp->d_secsize == 0 || (nlp->d_secsize % DEV_BSIZE) != 0 217 || nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC 218 || dkcksum(nlp) != 0) 219 return(EINVAL); 220 221 #ifdef DISKLABEL_AHDI 222 if (clp && clp->cd_bblock) 223 ck_label(nlp, clp); 224 #endif 225 while (openmask) { 226 struct partition *op, *np; 227 int i = ffs(openmask) - 1; 228 openmask &= ~(1 << i); 229 if (i >= nlp->d_npartitions) 230 return(EBUSY); 231 op = &olp->d_partitions[i]; 232 np = &nlp->d_partitions[i]; 233 if (np->p_offset != op->p_offset || np->p_size < op->p_size) 234 return(EBUSY); 235 /* 236 * Copy internally-set partition information 237 * if new label doesn't include it. XXX 238 */ 239 if (np->p_fstype == FS_UNUSED && op->p_fstype != FS_UNUSED) { 240 np->p_fstype = op->p_fstype; 241 np->p_fsize = op->p_fsize; 242 np->p_frag = op->p_frag; 243 np->p_cpg = op->p_cpg; 244 } 245 } 246 nlp->d_checksum = 0; 247 nlp->d_checksum = dkcksum(nlp); 248 *olp = *nlp; 249 return(0); 250 } 251 252 /* 253 * Write disk label back to device after modification. 254 */ 255 int 256 writedisklabel(dev, strat, lp, clp) 257 dev_t dev; 258 void (*strat)(struct buf *); 259 struct disklabel *lp; 260 struct cpu_disklabel *clp; 261 { 262 struct buf *bp; 263 u_int blk; 264 int rv; 265 266 blk = clp->cd_bblock; 267 if (blk == NO_BOOT_BLOCK) 268 return(ENXIO); 269 270 bp = geteblk(BBMINSIZE); 271 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART); 272 bp->b_flags = B_BUSY | B_READ; 273 bp->b_bcount = BBMINSIZE; 274 bp->b_blkno = blk; 275 bp->b_cylinder = blk / lp->d_secpercyl; 276 (*strat)(bp); 277 rv = biowait(bp); 278 if (!rv) { 279 struct bootblock *bb = (struct bootblock *)bp->b_data; 280 /* 281 * Allthough the disk pack label may appear anywhere 282 * in the boot block while reading, it is always 283 * written at a fixed location. 284 */ 285 if (clp->cd_label != LABELOFFSET) { 286 clp->cd_label = LABELOFFSET; 287 bzero(bb, sizeof(*bb)); 288 } 289 bb->bb_magic = (blk == 0) ? NBDAMAGIC : AHDIMAGIC; 290 BBSETLABEL(bb, lp); 291 292 bp->b_flags = B_BUSY | B_WRITE; 293 bp->b_bcount = BBMINSIZE; 294 bp->b_blkno = blk; 295 bp->b_cylinder = blk / lp->d_secpercyl; 296 (*strat)(bp); 297 rv = biowait(bp); 298 } 299 bp->b_flags |= B_INVAL | B_AGE; 300 brelse(bp); 301 return(rv); 302 } 303 304 /* 305 * Read bootblock at block `blkno' and check 306 * if it contains a valid NetBSD disk label. 307 * 308 * Returns: 0 if successfull, 309 * -1 if an I/O error occured, 310 * +1 if no valid label was found. 311 */ 312 static int 313 bsd_label(dev, strat, label, blkno, offset) 314 dev_t dev; 315 void (*strat)(struct buf *); 316 struct disklabel *label; 317 u_int blkno, 318 *offset; 319 { 320 struct buf *bp; 321 int rv; 322 323 bp = geteblk(BBMINSIZE); 324 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART); 325 bp->b_flags = B_BUSY | B_READ; 326 bp->b_bcount = BBMINSIZE; 327 bp->b_blkno = blkno; 328 bp->b_cylinder = blkno / label->d_secpercyl; 329 (*strat)(bp); 330 331 rv = -1; 332 if (!biowait(bp)) { 333 struct bootblock *bb; 334 u_int32_t *p, *end; 335 336 rv = 1; 337 bb = (struct bootblock *)bp->b_data; 338 end = (u_int32_t *)((char *)&bb[1] - sizeof(struct disklabel)); 339 for (p = (u_int32_t *)bb; p < end; ++p) { 340 struct disklabel *dl = (struct disklabel *)&p[1]; 341 /* 342 * Compatibility kludge: the boot block magic number is 343 * new in 1.1A, in previous versions the disklabel was 344 * stored at the end of the boot block (offset 7168). 345 */ 346 if ( ( (p[0] == NBDAMAGIC && blkno == 0) 347 || (p[0] == AHDIMAGIC && blkno != 0) 348 #ifdef COMPAT_11 349 || (char *)dl - (char *)bb == 7168 350 #endif 351 ) 352 && dl->d_npartitions <= MAXPARTITIONS 353 && dl->d_magic2 == DISKMAGIC 354 && dl->d_magic == DISKMAGIC 355 && dkcksum(dl) == 0 356 ) { 357 *offset = (char *)dl - (char *)bb; 358 *label = *dl; 359 rv = 0; 360 break; 361 } 362 } 363 } 364 365 bp->b_flags = B_INVAL | B_AGE | B_READ; 366 brelse(bp); 367 return(rv); 368 } 369 370 #ifdef DISKLABEL_AHDI 371 /* 372 * Check for consistency between the NetBSD partition table 373 * and the AHDI auxilary root sectors. There's no good reason 374 * to force such consistency, but issueing a warning may help 375 * an inexperienced sysadmin to prevent corruption of AHDI 376 * partitions. 377 */ 378 static void 379 ck_label(dl, cdl) 380 struct disklabel *dl; 381 struct cpu_disklabel *cdl; 382 { 383 u_int *rp, i; 384 385 for (i = 0; i < dl->d_npartitions; ++i) { 386 struct partition *p = &dl->d_partitions[i]; 387 if (i == RAW_PART || p->p_size == 0) 388 continue; 389 if ( (p->p_offset >= cdl->cd_bslst 390 && p->p_offset <= cdl->cd_bslend) 391 || (cdl->cd_bslst >= p->p_offset 392 && cdl->cd_bslst < p->p_offset + p->p_size)) { 393 uprintf("Warning: NetBSD partition %c includes" 394 " AHDI bad sector list\n", 'a'+i); 395 } 396 for (rp = &cdl->cd_roots[0]; *rp; ++rp) { 397 if (*rp >= p->p_offset 398 && *rp < p->p_offset + p->p_size) { 399 uprintf("Warning: NetBSD partition %c" 400 " includes AHDI auxilary root\n", 'a'+i); 401 } 402 } 403 } 404 } 405 406 /* 407 * Check volume for the existance of an AHDI label. Fetch 408 * NetBSD label from NBD or RAW partition, or otherwise 409 * create a fake NetBSD label based on the AHDI label. 410 * 411 * Returns: 0 if successful, 412 * -1 if an I/O error occured, 413 * +1 if no valid AHDI label was found. 414 */ 415 int 416 ahdi_label(dev, strat, dl, cdl) 417 dev_t dev; 418 void (*strat)(struct buf *); 419 struct disklabel *dl; 420 struct cpu_disklabel *cdl; 421 { 422 struct ahdi_ptbl apt; 423 u_int i; 424 int j; 425 426 /* 427 * The AHDI format requires a specific block size. 428 */ 429 if (dl->d_secsize != AHDI_BSIZE) 430 return(1); 431 432 /* 433 * Fetch the AHDI partition descriptors. 434 */ 435 apt.at_cdl = cdl; 436 apt.at_nroots = apt.at_nparts = 0; 437 i = ahdi_getparts(dev, strat, dl->d_secpercyl, 438 AHDI_BBLOCK, AHDI_BBLOCK, &apt); 439 if (i) { 440 if (i < dl->d_secperunit) 441 return(-1); /* disk read error */ 442 else return(1); /* reading past end of medium */ 443 } 444 445 /* 446 * Perform sanity checks. 447 */ 448 if (apt.at_bslst == 0 || apt.at_bslend == 0) { 449 /* 450 * Illegal according to Atari, however some hd-utils 451 * use it - notably ICD *sigh* 452 * Work around it..... 453 */ 454 apt.at_bslst = apt.at_bslend = 0; 455 uprintf("Warning: Illegal 'bad sector list' format" 456 "- assuming non exists\n"); 457 } 458 if (apt.at_hdsize == 0 || apt.at_nparts == 0) /* unlikely */ 459 return(1); 460 if (apt.at_nparts > AHDI_MAXPARTS) /* XXX kludge */ 461 return(-1); 462 for (i = 0; i < apt.at_nparts; ++i) { 463 struct ahdi_part *p1 = &apt.at_parts[i]; 464 465 for (j = 0; j < apt.at_nroots; ++j) { 466 u_int aux = apt.at_roots[j]; 467 if (aux >= p1->ap_st && aux <= p1->ap_end) 468 return(1); 469 } 470 for (j = i + 1; j < apt.at_nparts; ++j) { 471 struct ahdi_part *p2 = &apt.at_parts[j]; 472 if (p1->ap_st >= p2->ap_st && p1->ap_st <= p2->ap_end) 473 return(1); 474 if (p2->ap_st >= p1->ap_st && p2->ap_st <= p1->ap_end) 475 return(1); 476 } 477 if (p1->ap_st >= apt.at_bslst && p1->ap_st <= apt.at_bslend) 478 return(1); 479 if (apt.at_bslst >= p1->ap_st && apt.at_bslst <= p1->ap_end) 480 return(1); 481 } 482 483 /* 484 * Search for a NetBSD disk label 485 */ 486 apt.at_bblock = NO_BOOT_BLOCK; 487 for (i = 0; i < apt.at_nparts; ++i) { 488 struct ahdi_part *pd = &apt.at_parts[i]; 489 u_int id = *((u_int32_t *)&pd->ap_flg); 490 if (id == AHDI_PID_NBD || id == AHDI_PID_RAW) { 491 u_int blkno = pd->ap_st; 492 j = bsd_label(dev, strat, dl, blkno, &apt.at_label); 493 if (j < 0) { 494 return(j); /* I/O error */ 495 } 496 if (!j) { 497 apt.at_bblock = blkno; /* got it */ 498 ck_label(dl, cdl); 499 return(0); 500 } 501 /* 502 * Not yet, but if this is the first NBD partition 503 * on this volume, we'll mark it anyway as a possible 504 * destination for future writedisklabel() calls, just 505 * in case there is no valid disk label on any of the 506 * other AHDI partitions. 507 */ 508 if (id == AHDI_PID_NBD 509 && apt.at_bblock == NO_BOOT_BLOCK) 510 apt.at_bblock = blkno; 511 } 512 } 513 514 /* 515 * No NetBSD disk label on this volume, use the AHDI 516 * label to create a fake BSD label. If there is no 517 * NBD partition on this volume either, subsequent 518 * writedisklabel() calls will fail. 519 */ 520 ahdi_to_bsd(dl, &apt); 521 return(0); 522 } 523 524 /* 525 * Map the AHDI partition table to the NetBSD table. 526 * 527 * This means: 528 * Part 0 : Root 529 * Part 1 : Swap 530 * Part 2 : Whole disk 531 * Part 3.. : User partitions 532 * 533 * When more than one root partition is found, only the first one will 534 * be recognized as such. The others are mapped as user partitions. 535 */ 536 static void 537 ahdi_to_bsd(dl, apt) 538 struct disklabel *dl; 539 struct ahdi_ptbl *apt; 540 { 541 int i, have_root, user_part; 542 543 user_part = RAW_PART; 544 have_root = (apt->at_bblock != NO_BOOT_BLOCK); 545 546 for (i = 0; i < apt->at_nparts; ++i) { 547 struct ahdi_part *pd = &apt->at_parts[i]; 548 int fst, pno = -1; 549 550 switch (*((u_int32_t *)&pd->ap_flg)) { 551 case AHDI_PID_NBD: 552 /* 553 * If this partition has been marked as the 554 * first NBD partition, it will be the root 555 * partition. 556 */ 557 if (pd->ap_st == apt->at_bblock) 558 pno = 0; 559 /* FALL THROUGH */ 560 case AHDI_PID_NBR: 561 /* 562 * If there is no NBD partition and this is 563 * the first NBR partition, it will be the 564 * root partition. 565 */ 566 if (!have_root) { 567 have_root = 1; 568 pno = 0; 569 } 570 /* FALL THROUGH */ 571 case AHDI_PID_NBU: 572 fst = FS_BSDFFS; 573 break; 574 case AHDI_PID_NBS: 575 case AHDI_PID_SWP: 576 if (dl->d_partitions[1].p_size == 0) 577 pno = 1; 578 fst = FS_SWAP; 579 break; 580 case AHDI_PID_BGM: 581 case AHDI_PID_GEM: 582 fst = FS_MSDOS; 583 break; 584 default: 585 fst = FS_OTHER; 586 break; 587 } 588 if (pno < 0) { 589 if((pno = user_part + 1) >= MAXPARTITIONS) 590 continue; 591 user_part = pno; 592 } 593 dl->d_partitions[pno].p_size = pd->ap_end - pd->ap_st + 1; 594 dl->d_partitions[pno].p_offset = pd->ap_st; 595 dl->d_partitions[pno].p_fstype = fst; 596 } 597 dl->d_npartitions = user_part + 1; 598 } 599 600 /* 601 * Fetch the AHDI partitions and auxilary roots. 602 * 603 * Returns: 0 if successful, 604 * otherwise an I/O error occurred, and the 605 * number of the offending block is returned. 606 */ 607 static u_int 608 ahdi_getparts(dev, strat, secpercyl, rsec, esec, apt) 609 dev_t dev; 610 void (*strat)(struct buf *); 611 u_int secpercyl, 612 rsec, esec; 613 struct ahdi_ptbl *apt; 614 { 615 struct ahdi_part *part, *end; 616 struct ahdi_root *root; 617 struct buf *bp; 618 u_int rv; 619 620 bp = geteblk(AHDI_BSIZE); 621 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART); 622 bp->b_flags = B_BUSY | B_READ; 623 bp->b_bcount = AHDI_BSIZE; 624 bp->b_blkno = rsec; 625 bp->b_cylinder = rsec / secpercyl; 626 (*strat)(bp); 627 if (biowait(bp)) { 628 rv = rsec + (rsec == 0); 629 goto done; 630 } 631 root = (struct ahdi_root *)bp->b_data; 632 633 if (rsec == AHDI_BBLOCK) 634 end = &root->ar_parts[AHDI_MAXRPD]; 635 else end = &root->ar_parts[AHDI_MAXARPD]; 636 for (part = root->ar_parts; part < end; ++part) { 637 u_int id = *((u_int32_t *)&part->ap_flg); 638 if (!(id & 0x01000000)) 639 continue; 640 if ((id &= 0x00ffffff) == AHDI_PID_XGM) { 641 u_int offs = part->ap_st + esec; 642 if (apt->at_nroots < AHDI_MAXROOTS) 643 apt->at_roots[apt->at_nroots] = offs; 644 apt->at_nroots += 1; 645 rv = ahdi_getparts(dev, strat, secpercyl, offs, 646 (esec == AHDI_BBLOCK) ? offs : esec, apt); 647 if (rv) 648 goto done; 649 continue; 650 } 651 else if (apt->at_nparts < AHDI_MAXPARTS) { 652 struct ahdi_part *p = &apt->at_parts[apt->at_nparts]; 653 *((u_int32_t *)&p->ap_flg) = id; 654 p->ap_st = part->ap_st + rsec; 655 p->ap_end = p->ap_st + part->ap_size - 1; 656 } 657 apt->at_nparts += 1; 658 } 659 apt->at_hdsize = root->ar_hdsize; 660 apt->at_bslst = root->ar_bslst; 661 apt->at_bslend = root->ar_bslst + root->ar_bslsize - 1; 662 rv = 0; 663 done: 664 bp->b_flags = B_INVAL | B_AGE | B_READ; 665 brelse(bp); 666 return(rv); 667 } 668 #endif /* DISKLABEL_AHDI */ 669