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