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