sys/kern/subr_disklabel64.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 | /* * Copyright (c) 2007 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Matthew Dillon <dillon@backplane.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of The DragonFly Project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific, prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/conf.h> #include <sys/disklabel.h> #include <sys/disklabel64.h> #include <sys/diskslice.h> #include <sys/disk.h> #include <sys/kern_syscall.h> #include <sys/buf2.h> /* * Alignment against physical start (verses slice start). We use a megabyte * here. Why do we use a megabyte? Because SSDs already use large 128K * blocks internally (for MLC) and who the hell knows in the future. * * This way if the sysop picks sane values for partition sizes everything * will be nicely aligned, particularly swap for e.g. swapcache, and * clustered operations against larger physical sector sizes for newer HDs, * and so forth. */ #define PALIGN_SIZE (1024 * 1024) #define PALIGN_MASK (PALIGN_SIZE - 1) /* * Retrieve the partition start and extent, in blocks. Return 0 on success, * EINVAL on error. */ static int l64_getpartbounds(struct diskslices *ssp, disklabel_t lp, u_int32_t part, u_int64_t *start, u_int64_t *blocks) { struct partition64 *pp; if (part >= lp.lab64->d_npartitions) return (EINVAL); pp = &lp.lab64->d_partitions[part]; if ((pp->p_boffset & (ssp->dss_secsize - 1)) || (pp->p_bsize & (ssp->dss_secsize - 1))) { return (EINVAL); } *start = pp->p_boffset / ssp->dss_secsize; *blocks = pp->p_bsize / ssp->dss_secsize; return(0); } /* * Get the filesystem type XXX - diskslices code needs to use uuids */ static void l64_loadpartinfo(disklabel_t lp, u_int32_t part, struct partinfo *dpart) { struct partition64 *pp; const size_t uuid_size = sizeof(struct uuid); if (part < lp.lab64->d_npartitions) { pp = &lp.lab64->d_partitions[part]; dpart->fstype_uuid = pp->p_type_uuid; dpart->storage_uuid = pp->p_stor_uuid; dpart->fstype = pp->p_fstype; } else { bzero(&dpart->fstype_uuid, uuid_size); bzero(&dpart->storage_uuid, uuid_size); dpart->fstype = 0; } } /* * Get the number of partitions */ static u_int32_t l64_getnumparts(disklabel_t lp) { return(lp.lab64->d_npartitions); } static int l64_getpackname(disklabel_t lp, char *buf, size_t bytes) { size_t slen; if (lp.lab64->d_packname[0] == 0) { buf[0] = 0; return -1; } slen = strnlen(lp.lab64->d_packname, sizeof(lp.lab64->d_packname)); if (slen >= bytes) slen = bytes - 1; bcopy(lp.lab64->d_packname, buf, slen); buf[slen] = 0; return 0; } static void l64_freedisklabel(disklabel_t *lpp) { kfree((*lpp).lab64, M_DEVBUF); (*lpp).lab64 = NULL; } /* * Attempt to read a disk label from a device. 64 bit disklabels are * sector-agnostic and begin at offset 0 on the device. * * Returns NULL on sucess, and an error string on failure. */ static const char * l64_readdisklabel(cdev_t dev, struct diskslice *sp, disklabel_t *lpp, struct disk_info *info) { struct buf *bp; struct disklabel64 *dlp; const char *msg; uint32_t savecrc; size_t dlpcrcsize; size_t bpsize; int secsize; /* * XXX I/O size is subject to device DMA limitations */ secsize = info->d_media_blksize; bpsize = roundup2(sizeof(*dlp), secsize); bp = getpbuf_mem(NULL); KKASSERT(bpsize <= bp->b_bufsize); bp->b_bio1.bio_offset = 0; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; bp->b_bcount = bpsize; bp->b_flags &= ~B_INVAL; bp->b_flags |= B_FAILONDIS; bp->b_cmd = BUF_CMD_READ; dev_dstrategy(dev, &bp->b_bio1); if (biowait(&bp->b_bio1, "labrd")) { msg = "I/O error"; } else { dlp = (struct disklabel64 *)bp->b_data; dlpcrcsize = offsetof(struct disklabel64, d_partitions[dlp->d_npartitions]) - offsetof(struct disklabel64, d_magic); savecrc = dlp->d_crc; dlp->d_crc = 0; if (dlp->d_magic != DISKMAGIC64) { msg = "no disk label"; } else if (dlp->d_npartitions > MAXPARTITIONS64) { msg = "disklabel64 corrupted, too many partitions"; } else if (savecrc != crc32(&dlp->d_magic, dlpcrcsize)) { msg = "disklabel64 corrupted, bad CRC"; } else { dlp->d_crc = savecrc; (*lpp).lab64 = kmalloc(sizeof(*dlp), M_DEVBUF, M_WAITOK|M_ZERO); *(*lpp).lab64 = *dlp; msg = NULL; } } bp->b_flags |= B_INVAL | B_AGE; relpbuf(bp, NULL); return (msg); } /* * If everything is good, copy olpx to nlpx. Check to see if any * open partitions would change. */ static int l64_setdisklabel(disklabel_t olpx, disklabel_t nlpx, struct diskslices *ssp, struct diskslice *sp, u_int32_t *openmask) { struct disklabel64 *olp, *nlp; struct partition64 *opp, *npp; uint32_t savecrc; uint64_t slicebsize; size_t nlpcrcsize; int i; olp = olpx.lab64; nlp = nlpx.lab64; slicebsize = (uint64_t)sp->ds_size * ssp->dss_secsize; if (nlp->d_magic != DISKMAGIC64) return (EINVAL); if (nlp->d_npartitions > MAXPARTITIONS64) return (EINVAL); savecrc = nlp->d_crc; nlp->d_crc = 0; nlpcrcsize = offsetof(struct disklabel64, d_partitions[nlp->d_npartitions]) - offsetof(struct disklabel64, d_magic); if (crc32(&nlp->d_magic, nlpcrcsize) != savecrc) { nlp->d_crc = savecrc; return (EINVAL); } nlp->d_crc = savecrc; /* * Check if open partitions have changed */ i = 0; while (i < MAXPARTITIONS64) { if (openmask[i >> 5] == 0) { i += 32; continue; } if ((openmask[i >> 5] & (1 << (i & 31))) == 0) { ++i; continue; } if (nlp->d_npartitions <= i) return (EBUSY); opp = &olp->d_partitions[i]; npp = &nlp->d_partitions[i]; if (npp->p_boffset != opp->p_boffset || npp->p_bsize < opp->p_bsize) { return (EBUSY); } /* * Do not allow p_type_uuid or p_stor_uuid to change if * the partition is currently open. */ if (bcmp(&npp->p_type_uuid, &opp->p_type_uuid, sizeof(npp->p_type_uuid)) != 0) { return (EBUSY); } if (bcmp(&npp->p_stor_uuid, &opp->p_stor_uuid, sizeof(npp->p_stor_uuid)) != 0) { return (EBUSY); } ++i; } /* * Make sure the label and partition offsets and sizes are sane. */ if (nlp->d_total_size > slicebsize) return (ENOSPC); if (nlp->d_total_size & (ssp->dss_secsize - 1)) return (EINVAL); if (nlp->d_bbase & (ssp->dss_secsize - 1)) return (EINVAL); if (nlp->d_pbase & (ssp->dss_secsize - 1)) return (EINVAL); if (nlp->d_pstop & (ssp->dss_secsize - 1)) return (EINVAL); if (nlp->d_abase & (ssp->dss_secsize - 1)) return (EINVAL); for (i = 0; i < nlp->d_npartitions; ++i) { npp = &nlp->d_partitions[i]; if (npp->p_bsize == 0) { if (npp->p_boffset != 0) return (EINVAL); continue; } if (npp->p_boffset & (ssp->dss_secsize - 1)) return (EINVAL); if (npp->p_bsize & (ssp->dss_secsize - 1)) return (EINVAL); if (npp->p_boffset < nlp->d_pbase) return (ENOSPC); if (npp->p_boffset + npp->p_bsize > nlp->d_total_size) return (ENOSPC); } /* * Structurally we may add code to make modifications above in the * future, so regenerate the crc anyway. */ nlp->d_crc = 0; nlp->d_crc = crc32(&nlp->d_magic, nlpcrcsize); *olp = *nlp; return (0); } /* * Write disk label back to device after modification. */ static int l64_writedisklabel(cdev_t dev, struct diskslices *ssp, struct diskslice *sp, disklabel_t lpx) { struct disklabel64 *lp; struct disklabel64 *dlp; struct buf *bp; int error = 0; size_t bpsize; int secsize; lp = lpx.lab64; /* * XXX I/O size is subject to device DMA limitations */ secsize = ssp->dss_secsize; bpsize = roundup2(sizeof(*lp), secsize); bp = getpbuf_mem(NULL); KKASSERT(bpsize <= bp->b_bufsize); bp->b_bio1.bio_offset = 0; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; bp->b_bcount = bpsize; bp->b_flags |= B_FAILONDIS; /* * Because our I/O is larger then the label, and because we do not * write the d_reserved0[] area, do a read-modify-write. */ bp->b_flags &= ~B_INVAL; bp->b_cmd = BUF_CMD_READ; KKASSERT(dkpart(dev) == WHOLE_SLICE_PART); dev_dstrategy(dev, &bp->b_bio1); error = biowait(&bp->b_bio1, "labrd"); if (error) goto done; dlp = (void *)bp->b_data; bcopy(&lp->d_magic, &dlp->d_magic, sizeof(*lp) - offsetof(struct disklabel64, d_magic)); bp->b_cmd = BUF_CMD_WRITE; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; KKASSERT(dkpart(dev) == WHOLE_SLICE_PART); dev_dstrategy(dev, &bp->b_bio1); error = biowait(&bp->b_bio1, "labwr"); done: bp->b_flags |= B_INVAL | B_AGE; relpbuf(bp, NULL); return (error); } /* * Create a disklabel based on a disk_info structure for the purposes of * DSO_COMPATLABEL - cases where no real label exists on the storage medium. * * If a diskslice is passed, the label is truncated to the slice. * * NOTE! This is not a legal label because d_bbase and d_pbase are both * set to 0. */ static disklabel_t l64_clone_label(struct disk_info *info, struct diskslice *sp) { struct disklabel64 *lp; disklabel_t res; uint32_t blksize = info->d_media_blksize; size_t lpcrcsize; lp = kmalloc(sizeof *lp, M_DEVBUF, M_WAITOK | M_ZERO); if (sp) lp->d_total_size = (uint64_t)sp->ds_size * blksize; else lp->d_total_size = info->d_media_blocks * blksize; lp->d_magic = DISKMAGIC64; lp->d_align = blksize; lp->d_npartitions = MAXPARTITIONS64; lp->d_pstop = lp->d_total_size; /* * Create a dummy 'c' part and a dummy 'a' part (if requested). * Note that the 'c' part is really a hack. 64 bit disklabels * do not use 'c' to mean the raw partition. */ lp->d_partitions[2].p_boffset = 0; lp->d_partitions[2].p_bsize = lp->d_total_size; /* XXX SET FS TYPE */ if (info->d_dsflags & DSO_COMPATPARTA) { lp->d_partitions[0].p_boffset = 0; lp->d_partitions[0].p_bsize = lp->d_total_size; /* XXX SET FS TYPE */ } lpcrcsize = offsetof(struct disklabel64, d_partitions[lp->d_npartitions]) - offsetof(struct disklabel64, d_magic); lp->d_crc = crc32(&lp->d_magic, lpcrcsize); res.lab64 = lp; return (res); } /* * Create a virgin disklabel64 suitable for writing to the media. * * disklabel64 always reserves 32KB for a boot area and leaves room * for up to RESPARTITIONS64 partitions. */ static void l64_makevirginlabel(disklabel_t lpx, struct diskslices *ssp, struct diskslice *sp, struct disk_info *info) { struct disklabel64 *lp = lpx.lab64; struct partition64 *pp; uint32_t blksize; uint32_t ressize; uint64_t blkmask; /* 64 bits so we can ~ */ uint64_t doffset; size_t lpcrcsize; doffset = sp->ds_offset * info->d_media_blksize; /* * Setup the initial label. Use of a block size of at least 4KB * for calculating the initial reserved areas to allow some degree * of portability between media with different sector sizes. * * Note that the modified blksize is stored in d_align as a hint * to the disklabeling program. */ bzero(lp, sizeof(*lp)); if ((blksize = info->d_media_blksize) < 4096) blksize = 4096; blkmask = blksize - 1; if (sp) lp->d_total_size = (uint64_t)sp->ds_size * ssp->dss_secsize; else lp->d_total_size = info->d_media_blocks * info->d_media_blksize; lp->d_magic = DISKMAGIC64; lp->d_align = blksize; lp->d_npartitions = MAXPARTITIONS64; kern_uuidgen(&lp->d_stor_uuid, 1); ressize = offsetof(struct disklabel64, d_partitions[RESPARTITIONS64]); ressize = (ressize + (uint32_t)blkmask) & ~blkmask; /* Reserve space for the stage2 boot code */ lp->d_bbase = ressize; lp->d_pbase = lp->d_bbase + ((BOOT2SIZE64 + blkmask) & ~blkmask); /* Reserve space for the backup label at the slice end */ lp->d_abase = lp->d_total_size - ressize; /* * NOTE: The pbase and pstop are calculated to align to PALIGN_SIZE * and adjusted with the slice offset, so the partitions are * aligned relative to the start of the physical disk. */ lp->d_pbase = ((doffset + lp->d_pbase + PALIGN_MASK) & ~(uint64_t)PALIGN_MASK) - doffset; lp->d_pstop = ((lp->d_abase - lp->d_pbase) & ~(uint64_t)PALIGN_MASK) + lp->d_pbase; /* * All partitions are left empty unless DSO_COMPATPARTA is set */ if (info->d_dsflags & DSO_COMPATPARTA) { pp = &lp->d_partitions[0]; pp->p_boffset = lp->d_pbase; pp->p_bsize = lp->d_pstop - lp->d_pbase; /* XXX SET FS TYPE */ } lpcrcsize = offsetof(struct disklabel64, d_partitions[lp->d_npartitions]) - offsetof(struct disklabel64, d_magic); lp->d_crc = crc32(&lp->d_magic, lpcrcsize); } /* * Set the number of blocks at the beginning of the slice which have * been reserved for label operations. This area will be write-protected * when accessed via the slice. * * For now just protect the label area proper. Do not protect the * boot area. Note partitions in 64 bit disklabels do not overlap * the disklabel or boot area. */ static void l64_adjust_label_reserved(struct diskslices *ssp, int slice, struct diskslice *sp) { struct disklabel64 *lp = sp->ds_label.lab64; sp->ds_reserved = lp->d_bbase / ssp->dss_secsize; } struct disklabel_ops disklabel64_ops = { .labelsize = sizeof(struct disklabel64), .op_readdisklabel = l64_readdisklabel, .op_setdisklabel = l64_setdisklabel, .op_writedisklabel = l64_writedisklabel, .op_clone_label = l64_clone_label, .op_adjust_label_reserved = l64_adjust_label_reserved, .op_getpartbounds = l64_getpartbounds, .op_loadpartinfo = l64_loadpartinfo, .op_getnumparts = l64_getnumparts, .op_getpackname = l64_getpackname, .op_makevirginlabel = l64_makevirginlabel, .op_freedisklabel = l64_freedisklabel }; |