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