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