g_part.c revision 188352
1/*- 2 * Copyright (c) 2002, 2005-2008 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/geom/part/g_part.c 188352 2009-02-08 23:39:30Z marcel $"); 29 30#include <sys/param.h> 31#include <sys/bio.h> 32#include <sys/diskmbr.h> 33#include <sys/endian.h> 34#include <sys/kernel.h> 35#include <sys/kobj.h> 36#include <sys/limits.h> 37#include <sys/lock.h> 38#include <sys/malloc.h> 39#include <sys/mutex.h> 40#include <sys/queue.h> 41#include <sys/sbuf.h> 42#include <sys/systm.h> 43#include <sys/uuid.h> 44#include <geom/geom.h> 45#include <geom/geom_ctl.h> 46#include <geom/geom_int.h> 47#include <geom/part/g_part.h> 48 49#include "g_part_if.h" 50 51static kobj_method_t g_part_null_methods[] = { 52 { 0, 0 } 53}; 54 55static struct g_part_scheme g_part_null_scheme = { 56 "(none)", 57 g_part_null_methods, 58 sizeof(struct g_part_table), 59}; 60 61TAILQ_HEAD(, g_part_scheme) g_part_schemes = 62 TAILQ_HEAD_INITIALIZER(g_part_schemes); 63 64struct g_part_alias_list { 65 const char *lexeme; 66 enum g_part_alias alias; 67} g_part_alias_list[G_PART_ALIAS_COUNT] = { 68 { "apple-hfs", G_PART_ALIAS_APPLE_HFS }, 69 { "efi", G_PART_ALIAS_EFI }, 70 { "freebsd", G_PART_ALIAS_FREEBSD }, 71 { "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT }, 72 { "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP }, 73 { "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS }, 74 { "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM }, 75 { "freebsd-zfs", G_PART_ALIAS_FREEBSD_ZFS }, 76 { "mbr", G_PART_ALIAS_MBR } 77}; 78 79/* 80 * The GEOM partitioning class. 81 */ 82static g_ctl_req_t g_part_ctlreq; 83static g_ctl_destroy_geom_t g_part_destroy_geom; 84static g_fini_t g_part_fini; 85static g_init_t g_part_init; 86static g_taste_t g_part_taste; 87 88static g_access_t g_part_access; 89static g_dumpconf_t g_part_dumpconf; 90static g_orphan_t g_part_orphan; 91static g_spoiled_t g_part_spoiled; 92static g_start_t g_part_start; 93 94static struct g_class g_part_class = { 95 .name = "PART", 96 .version = G_VERSION, 97 /* Class methods. */ 98 .ctlreq = g_part_ctlreq, 99 .destroy_geom = g_part_destroy_geom, 100 .fini = g_part_fini, 101 .init = g_part_init, 102 .taste = g_part_taste, 103 /* Geom methods. */ 104 .access = g_part_access, 105 .dumpconf = g_part_dumpconf, 106 .orphan = g_part_orphan, 107 .spoiled = g_part_spoiled, 108 .start = g_part_start, 109}; 110 111DECLARE_GEOM_CLASS(g_part_class, g_part); 112 113enum g_part_ctl { 114 G_PART_CTL_NONE, 115 G_PART_CTL_ADD, 116 G_PART_CTL_BOOTCODE, 117 G_PART_CTL_COMMIT, 118 G_PART_CTL_CREATE, 119 G_PART_CTL_DELETE, 120 G_PART_CTL_DESTROY, 121 G_PART_CTL_MODIFY, 122 G_PART_CTL_MOVE, 123 G_PART_CTL_RECOVER, 124 G_PART_CTL_RESIZE, 125 G_PART_CTL_SET, 126 G_PART_CTL_UNDO, 127 G_PART_CTL_UNSET 128}; 129 130/* 131 * Support functions. 132 */ 133 134static void g_part_wither(struct g_geom *, int); 135 136const char * 137g_part_alias_name(enum g_part_alias alias) 138{ 139 int i; 140 141 for (i = 0; i < G_PART_ALIAS_COUNT; i++) { 142 if (g_part_alias_list[i].alias != alias) 143 continue; 144 return (g_part_alias_list[i].lexeme); 145 } 146 147 return (NULL); 148} 149 150void 151g_part_geometry_heads(off_t blocks, u_int sectors, off_t *bestchs, 152 u_int *bestheads) 153{ 154 static u_int candidate_heads[] = { 1, 2, 16, 32, 64, 128, 255, 0 }; 155 off_t chs, cylinders; 156 u_int heads; 157 int idx; 158 159 *bestchs = 0; 160 *bestheads = 0; 161 for (idx = 0; candidate_heads[idx] != 0; idx++) { 162 heads = candidate_heads[idx]; 163 cylinders = blocks / heads / sectors; 164 if (cylinders < heads || cylinders < sectors) 165 break; 166 if (cylinders > 1023) 167 continue; 168 chs = cylinders * heads * sectors; 169 if (chs > *bestchs || (chs == *bestchs && *bestheads == 1)) { 170 *bestchs = chs; 171 *bestheads = heads; 172 } 173 } 174} 175 176static void 177g_part_geometry(struct g_part_table *table, struct g_consumer *cp, 178 off_t blocks) 179{ 180 static u_int candidate_sectors[] = { 1, 9, 17, 33, 63, 0 }; 181 off_t chs, bestchs; 182 u_int heads, sectors; 183 int idx; 184 185 if (g_getattr("GEOM::fwsectors", cp, §ors) != 0 || sectors == 0 || 186 g_getattr("GEOM::fwheads", cp, &heads) != 0 || heads == 0) { 187 table->gpt_fixgeom = 0; 188 table->gpt_heads = 0; 189 table->gpt_sectors = 0; 190 bestchs = 0; 191 for (idx = 0; candidate_sectors[idx] != 0; idx++) { 192 sectors = candidate_sectors[idx]; 193 g_part_geometry_heads(blocks, sectors, &chs, &heads); 194 if (chs == 0) 195 continue; 196 /* 197 * Prefer a geometry with sectors > 1, but only if 198 * it doesn't bump down the numbver of heads to 1. 199 */ 200 if (chs > bestchs || (chs == bestchs && heads > 1 && 201 table->gpt_sectors == 1)) { 202 bestchs = chs; 203 table->gpt_heads = heads; 204 table->gpt_sectors = sectors; 205 } 206 } 207 /* 208 * If we didn't find a geometry at all, then the disk is 209 * too big. This means we can use the maximum number of 210 * heads and sectors. 211 */ 212 if (bestchs == 0) { 213 table->gpt_heads = 255; 214 table->gpt_sectors = 63; 215 } 216 } else { 217 table->gpt_fixgeom = 1; 218 table->gpt_heads = heads; 219 table->gpt_sectors = sectors; 220 } 221} 222 223struct g_part_entry * 224g_part_new_entry(struct g_part_table *table, int index, quad_t start, 225 quad_t end) 226{ 227 struct g_part_entry *entry, *last; 228 229 last = NULL; 230 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 231 if (entry->gpe_index == index) 232 break; 233 if (entry->gpe_index > index) { 234 entry = NULL; 235 break; 236 } 237 last = entry; 238 } 239 if (entry == NULL) { 240 entry = g_malloc(table->gpt_scheme->gps_entrysz, 241 M_WAITOK | M_ZERO); 242 entry->gpe_index = index; 243 if (last == NULL) 244 LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry); 245 else 246 LIST_INSERT_AFTER(last, entry, gpe_entry); 247 } else 248 entry->gpe_offset = 0; 249 entry->gpe_start = start; 250 entry->gpe_end = end; 251 return (entry); 252} 253 254static void 255g_part_new_provider(struct g_geom *gp, struct g_part_table *table, 256 struct g_part_entry *entry) 257{ 258 char buf[32]; 259 struct g_consumer *cp; 260 struct g_provider *pp; 261 off_t offset; 262 263 cp = LIST_FIRST(&gp->consumer); 264 pp = cp->provider; 265 266 offset = entry->gpe_start * pp->sectorsize; 267 if (entry->gpe_offset < offset) 268 entry->gpe_offset = offset; 269 270 if (entry->gpe_pp == NULL) { 271 entry->gpe_pp = g_new_providerf(gp, "%s%s", gp->name, 272 G_PART_NAME(table, entry, buf, sizeof(buf))); 273 entry->gpe_pp->private = entry; /* Close the circle. */ 274 } 275 entry->gpe_pp->index = entry->gpe_index - 1; /* index is 1-based. */ 276 entry->gpe_pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) * 277 pp->sectorsize; 278 entry->gpe_pp->mediasize -= entry->gpe_offset - offset; 279 entry->gpe_pp->sectorsize = pp->sectorsize; 280 entry->gpe_pp->flags = pp->flags & G_PF_CANDELETE; 281 if (pp->stripesize > 0) { 282 entry->gpe_pp->stripesize = pp->stripesize; 283 entry->gpe_pp->stripeoffset = (pp->stripeoffset + 284 entry->gpe_offset) % pp->stripesize; 285 } 286 g_error_provider(entry->gpe_pp, 0); 287} 288 289static int 290g_part_parm_geom(const char *p, struct g_geom **v) 291{ 292 struct g_geom *gp; 293 294 LIST_FOREACH(gp, &g_part_class.geom, geom) { 295 if (!strcmp(p, gp->name)) 296 break; 297 } 298 if (gp == NULL) 299 return (EINVAL); 300 *v = gp; 301 return (0); 302} 303 304static int 305g_part_parm_provider(const char *p, struct g_provider **v) 306{ 307 struct g_provider *pp; 308 309 pp = g_provider_by_name(p); 310 if (pp == NULL) 311 return (EINVAL); 312 *v = pp; 313 return (0); 314} 315 316static int 317g_part_parm_quad(const char *p, quad_t *v) 318{ 319 char *x; 320 quad_t q; 321 322 q = strtoq(p, &x, 0); 323 if (*x != '\0' || q < 0) 324 return (EINVAL); 325 *v = q; 326 return (0); 327} 328 329static int 330g_part_parm_scheme(const char *p, struct g_part_scheme **v) 331{ 332 struct g_part_scheme *s; 333 334 TAILQ_FOREACH(s, &g_part_schemes, scheme_list) { 335 if (s == &g_part_null_scheme) 336 continue; 337 if (!strcasecmp(s->name, p)) 338 break; 339 } 340 if (s == NULL) 341 return (EINVAL); 342 *v = s; 343 return (0); 344} 345 346static int 347g_part_parm_str(const char *p, const char **v) 348{ 349 350 if (p[0] == '\0') 351 return (EINVAL); 352 *v = p; 353 return (0); 354} 355 356static int 357g_part_parm_uint(const char *p, u_int *v) 358{ 359 char *x; 360 long l; 361 362 l = strtol(p, &x, 0); 363 if (*x != '\0' || l < 0 || l > INT_MAX) 364 return (EINVAL); 365 *v = (unsigned int)l; 366 return (0); 367} 368 369static int 370g_part_probe(struct g_geom *gp, struct g_consumer *cp, int depth) 371{ 372 struct g_part_scheme *iter, *scheme; 373 struct g_part_table *table; 374 int pri, probe; 375 376 table = gp->softc; 377 scheme = (table != NULL) ? table->gpt_scheme : NULL; 378 pri = (scheme != NULL) ? G_PART_PROBE(table, cp) : INT_MIN; 379 if (pri == 0) 380 goto done; 381 if (pri > 0) { /* error */ 382 scheme = NULL; 383 pri = INT_MIN; 384 } 385 386 TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) { 387 if (iter == &g_part_null_scheme) 388 continue; 389 table = (void *)kobj_create((kobj_class_t)iter, M_GEOM, 390 M_WAITOK); 391 table->gpt_gp = gp; 392 table->gpt_scheme = iter; 393 table->gpt_depth = depth; 394 probe = G_PART_PROBE(table, cp); 395 if (probe <= 0 && probe > pri) { 396 pri = probe; 397 scheme = iter; 398 if (gp->softc != NULL) 399 kobj_delete((kobj_t)gp->softc, M_GEOM); 400 gp->softc = table; 401 if (pri == 0) 402 goto done; 403 } else 404 kobj_delete((kobj_t)table, M_GEOM); 405 } 406 407done: 408 return ((scheme == NULL) ? ENXIO : 0); 409} 410 411/* 412 * Control request functions. 413 */ 414 415static int 416g_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp) 417{ 418 char buf[32]; 419 struct g_geom *gp; 420 struct g_provider *pp; 421 struct g_part_entry *delent, *last, *entry; 422 struct g_part_table *table; 423 struct sbuf *sb; 424 quad_t end; 425 unsigned int index; 426 int error; 427 428 gp = gpp->gpp_geom; 429 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 430 g_topology_assert(); 431 432 pp = LIST_FIRST(&gp->consumer)->provider; 433 table = gp->softc; 434 end = gpp->gpp_start + gpp->gpp_size - 1; 435 436 if (gpp->gpp_start < table->gpt_first || 437 gpp->gpp_start > table->gpt_last) { 438 gctl_error(req, "%d start '%jd'", EINVAL, 439 (intmax_t)gpp->gpp_start); 440 return (EINVAL); 441 } 442 if (end < gpp->gpp_start || end > table->gpt_last) { 443 gctl_error(req, "%d size '%jd'", EINVAL, 444 (intmax_t)gpp->gpp_size); 445 return (EINVAL); 446 } 447 if (gpp->gpp_index > table->gpt_entries) { 448 gctl_error(req, "%d index '%d'", EINVAL, gpp->gpp_index); 449 return (EINVAL); 450 } 451 452 delent = last = NULL; 453 index = (gpp->gpp_index > 0) ? gpp->gpp_index : 1; 454 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 455 if (entry->gpe_deleted) { 456 if (entry->gpe_index == index) 457 delent = entry; 458 continue; 459 } 460 if (entry->gpe_index == index) 461 index = entry->gpe_index + 1; 462 if (entry->gpe_index < index) 463 last = entry; 464 if (entry->gpe_internal) 465 continue; 466 if (gpp->gpp_start >= entry->gpe_start && 467 gpp->gpp_start <= entry->gpe_end) { 468 gctl_error(req, "%d start '%jd'", ENOSPC, 469 (intmax_t)gpp->gpp_start); 470 return (ENOSPC); 471 } 472 if (end >= entry->gpe_start && end <= entry->gpe_end) { 473 gctl_error(req, "%d end '%jd'", ENOSPC, (intmax_t)end); 474 return (ENOSPC); 475 } 476 if (gpp->gpp_start < entry->gpe_start && end > entry->gpe_end) { 477 gctl_error(req, "%d size '%jd'", ENOSPC, 478 (intmax_t)gpp->gpp_size); 479 return (ENOSPC); 480 } 481 } 482 if (gpp->gpp_index > 0 && index != gpp->gpp_index) { 483 gctl_error(req, "%d index '%d'", EEXIST, gpp->gpp_index); 484 return (EEXIST); 485 } 486 487 entry = (delent == NULL) ? g_malloc(table->gpt_scheme->gps_entrysz, 488 M_WAITOK | M_ZERO) : delent; 489 entry->gpe_index = index; 490 entry->gpe_start = gpp->gpp_start; 491 entry->gpe_end = end; 492 error = G_PART_ADD(table, entry, gpp); 493 if (error) { 494 gctl_error(req, "%d", error); 495 if (delent == NULL) 496 g_free(entry); 497 return (error); 498 } 499 if (delent == NULL) { 500 if (last == NULL) 501 LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry); 502 else 503 LIST_INSERT_AFTER(last, entry, gpe_entry); 504 entry->gpe_created = 1; 505 } else { 506 entry->gpe_deleted = 0; 507 entry->gpe_modified = 1; 508 } 509 g_part_new_provider(gp, table, entry); 510 511 /* Provide feedback if so requested. */ 512 if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 513 sb = sbuf_new_auto(); 514 sbuf_printf(sb, "%s%s added\n", gp->name, 515 G_PART_NAME(table, entry, buf, sizeof(buf))); 516 sbuf_finish(sb); 517 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 518 sbuf_delete(sb); 519 } 520 return (0); 521} 522 523static int 524g_part_ctl_bootcode(struct gctl_req *req, struct g_part_parms *gpp) 525{ 526 struct g_geom *gp; 527 struct g_part_table *table; 528 struct sbuf *sb; 529 int error, sz; 530 531 gp = gpp->gpp_geom; 532 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 533 g_topology_assert(); 534 535 table = gp->softc; 536 sz = table->gpt_scheme->gps_bootcodesz; 537 if (sz == 0) { 538 error = ENODEV; 539 goto fail; 540 } 541 if (gpp->gpp_codesize > sz) { 542 error = EFBIG; 543 goto fail; 544 } 545 546 error = G_PART_BOOTCODE(table, gpp); 547 if (error) 548 goto fail; 549 550 /* Provide feedback if so requested. */ 551 if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 552 sb = sbuf_new_auto(); 553 sbuf_printf(sb, "%s has bootcode\n", gp->name); 554 sbuf_finish(sb); 555 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 556 sbuf_delete(sb); 557 } 558 return (0); 559 560 fail: 561 gctl_error(req, "%d", error); 562 return (error); 563} 564 565static int 566g_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp) 567{ 568 struct g_consumer *cp; 569 struct g_geom *gp; 570 struct g_provider *pp; 571 struct g_part_entry *entry, *tmp; 572 struct g_part_table *table; 573 char *buf; 574 int error, i; 575 576 gp = gpp->gpp_geom; 577 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 578 g_topology_assert(); 579 580 table = gp->softc; 581 if (!table->gpt_opened) { 582 gctl_error(req, "%d", EPERM); 583 return (EPERM); 584 } 585 586 cp = LIST_FIRST(&gp->consumer); 587 if ((table->gpt_smhead | table->gpt_smtail) != 0) { 588 pp = cp->provider; 589 buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 590 while (table->gpt_smhead != 0) { 591 i = ffs(table->gpt_smhead) - 1; 592 error = g_write_data(cp, i * pp->sectorsize, buf, 593 pp->sectorsize); 594 if (error) { 595 g_free(buf); 596 goto fail; 597 } 598 table->gpt_smhead &= ~(1 << i); 599 } 600 while (table->gpt_smtail != 0) { 601 i = ffs(table->gpt_smtail) - 1; 602 error = g_write_data(cp, pp->mediasize - (i + 1) * 603 pp->sectorsize, buf, pp->sectorsize); 604 if (error) { 605 g_free(buf); 606 goto fail; 607 } 608 table->gpt_smtail &= ~(1 << i); 609 } 610 g_free(buf); 611 } 612 613 if (table->gpt_scheme == &g_part_null_scheme) { 614 g_access(cp, -1, -1, -1); 615 g_part_wither(gp, ENXIO); 616 return (0); 617 } 618 619 error = G_PART_WRITE(table, cp); 620 if (error) 621 goto fail; 622 623 LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) { 624 if (!entry->gpe_deleted) { 625 entry->gpe_created = 0; 626 entry->gpe_modified = 0; 627 continue; 628 } 629 LIST_REMOVE(entry, gpe_entry); 630 g_free(entry); 631 } 632 table->gpt_created = 0; 633 table->gpt_opened = 0; 634 g_access(cp, -1, -1, -1); 635 return (0); 636 637fail: 638 gctl_error(req, "%d", error); 639 return (error); 640} 641 642static int 643g_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp) 644{ 645 struct g_consumer *cp; 646 struct g_geom *gp; 647 struct g_provider *pp; 648 struct g_part_scheme *scheme; 649 struct g_part_table *null, *table; 650 struct sbuf *sb; 651 int attr, error; 652 653 pp = gpp->gpp_provider; 654 scheme = gpp->gpp_scheme; 655 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name)); 656 g_topology_assert(); 657 658 /* Check that there isn't already a g_part geom on the provider. */ 659 error = g_part_parm_geom(pp->name, &gp); 660 if (!error) { 661 null = gp->softc; 662 if (null->gpt_scheme != &g_part_null_scheme) { 663 gctl_error(req, "%d geom '%s'", EEXIST, pp->name); 664 return (EEXIST); 665 } 666 } else 667 null = NULL; 668 669 if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) && 670 (gpp->gpp_entries < scheme->gps_minent || 671 gpp->gpp_entries > scheme->gps_maxent)) { 672 gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries); 673 return (EINVAL); 674 } 675 676 if (null == NULL) 677 gp = g_new_geomf(&g_part_class, "%s", pp->name); 678 gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM, 679 M_WAITOK); 680 table = gp->softc; 681 table->gpt_gp = gp; 682 table->gpt_scheme = gpp->gpp_scheme; 683 table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ? 684 gpp->gpp_entries : scheme->gps_minent; 685 LIST_INIT(&table->gpt_entry); 686 if (null == NULL) { 687 cp = g_new_consumer(gp); 688 error = g_attach(cp, pp); 689 if (error == 0) 690 error = g_access(cp, 1, 1, 1); 691 if (error != 0) { 692 g_part_wither(gp, error); 693 gctl_error(req, "%d geom '%s'", error, pp->name); 694 return (error); 695 } 696 table->gpt_opened = 1; 697 } else { 698 cp = LIST_FIRST(&gp->consumer); 699 table->gpt_opened = null->gpt_opened; 700 table->gpt_smhead = null->gpt_smhead; 701 table->gpt_smtail = null->gpt_smtail; 702 } 703 704 g_topology_unlock(); 705 706 /* Make sure the provider has media. */ 707 if (pp->mediasize == 0 || pp->sectorsize == 0) { 708 error = ENODEV; 709 goto fail; 710 } 711 712 /* Make sure we can nest and if so, determine our depth. */ 713 error = g_getattr("PART::isleaf", cp, &attr); 714 if (!error && attr) { 715 error = ENODEV; 716 goto fail; 717 } 718 error = g_getattr("PART::depth", cp, &attr); 719 table->gpt_depth = (!error) ? attr + 1 : 0; 720 721 /* If we're nested, get the absolute sector offset on disk. */ 722 if (table->gpt_depth) { 723 error = g_getattr("PART::offset", cp, &attr); 724 if (error) 725 goto fail; 726 table->gpt_offset = attr; 727 } 728 729 /* 730 * Synthesize a disk geometry. Some partitioning schemes 731 * depend on it and since some file systems need it even 732 * when the partitition scheme doesn't, we do it here in 733 * scheme-independent code. 734 */ 735 g_part_geometry(table, cp, pp->mediasize / pp->sectorsize); 736 737 error = G_PART_CREATE(table, gpp); 738 if (error) 739 goto fail; 740 741 g_topology_lock(); 742 743 table->gpt_created = 1; 744 if (null != NULL) 745 kobj_delete((kobj_t)null, M_GEOM); 746 747 /* 748 * Support automatic commit by filling in the gpp_geom 749 * parameter. 750 */ 751 gpp->gpp_parms |= G_PART_PARM_GEOM; 752 gpp->gpp_geom = gp; 753 754 /* Provide feedback if so requested. */ 755 if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 756 sb = sbuf_new_auto(); 757 sbuf_printf(sb, "%s created\n", gp->name); 758 sbuf_finish(sb); 759 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 760 sbuf_delete(sb); 761 } 762 return (0); 763 764fail: 765 g_topology_lock(); 766 if (null == NULL) { 767 g_access(cp, -1, -1, -1); 768 g_part_wither(gp, error); 769 } else { 770 kobj_delete((kobj_t)gp->softc, M_GEOM); 771 gp->softc = null; 772 } 773 gctl_error(req, "%d provider", error); 774 return (error); 775} 776 777static int 778g_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp) 779{ 780 char buf[32]; 781 struct g_geom *gp; 782 struct g_provider *pp; 783 struct g_part_entry *entry; 784 struct g_part_table *table; 785 struct sbuf *sb; 786 787 gp = gpp->gpp_geom; 788 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 789 g_topology_assert(); 790 791 table = gp->softc; 792 793 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 794 if (entry->gpe_deleted || entry->gpe_internal) 795 continue; 796 if (entry->gpe_index == gpp->gpp_index) 797 break; 798 } 799 if (entry == NULL) { 800 gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index); 801 return (ENOENT); 802 } 803 804 pp = entry->gpe_pp; 805 if (pp != NULL) { 806 if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) { 807 gctl_error(req, "%d", EBUSY); 808 return (EBUSY); 809 } 810 811 pp->private = NULL; 812 entry->gpe_pp = NULL; 813 } 814 815 if (entry->gpe_created) { 816 LIST_REMOVE(entry, gpe_entry); 817 g_free(entry); 818 } else { 819 entry->gpe_modified = 0; 820 entry->gpe_deleted = 1; 821 } 822 823 if (pp != NULL) 824 g_wither_provider(pp, ENXIO); 825 826 /* Provide feedback if so requested. */ 827 if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 828 sb = sbuf_new_auto(); 829 sbuf_printf(sb, "%s%s deleted\n", gp->name, 830 G_PART_NAME(table, entry, buf, sizeof(buf))); 831 sbuf_finish(sb); 832 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 833 sbuf_delete(sb); 834 } 835 return (0); 836} 837 838static int 839g_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp) 840{ 841 struct g_geom *gp; 842 struct g_part_entry *entry; 843 struct g_part_table *null, *table; 844 struct sbuf *sb; 845 int error; 846 847 gp = gpp->gpp_geom; 848 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 849 g_topology_assert(); 850 851 table = gp->softc; 852 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 853 if (entry->gpe_deleted || entry->gpe_internal) 854 continue; 855 gctl_error(req, "%d", EBUSY); 856 return (EBUSY); 857 } 858 859 error = G_PART_DESTROY(table, gpp); 860 if (error) { 861 gctl_error(req, "%d", error); 862 return (error); 863 } 864 865 gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM, 866 M_WAITOK); 867 null = gp->softc; 868 null->gpt_gp = gp; 869 null->gpt_scheme = &g_part_null_scheme; 870 LIST_INIT(&null->gpt_entry); 871 null->gpt_depth = table->gpt_depth; 872 null->gpt_opened = table->gpt_opened; 873 null->gpt_smhead = table->gpt_smhead; 874 null->gpt_smtail = table->gpt_smtail; 875 876 while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) { 877 LIST_REMOVE(entry, gpe_entry); 878 g_free(entry); 879 } 880 kobj_delete((kobj_t)table, M_GEOM); 881 882 /* Provide feedback if so requested. */ 883 if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 884 sb = sbuf_new_auto(); 885 sbuf_printf(sb, "%s destroyed\n", gp->name); 886 sbuf_finish(sb); 887 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 888 sbuf_delete(sb); 889 } 890 return (0); 891} 892 893static int 894g_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp) 895{ 896 char buf[32]; 897 struct g_geom *gp; 898 struct g_part_entry *entry; 899 struct g_part_table *table; 900 struct sbuf *sb; 901 int error; 902 903 gp = gpp->gpp_geom; 904 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 905 g_topology_assert(); 906 907 table = gp->softc; 908 909 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 910 if (entry->gpe_deleted || entry->gpe_internal) 911 continue; 912 if (entry->gpe_index == gpp->gpp_index) 913 break; 914 } 915 if (entry == NULL) { 916 gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index); 917 return (ENOENT); 918 } 919 920 error = G_PART_MODIFY(table, entry, gpp); 921 if (error) { 922 gctl_error(req, "%d", error); 923 return (error); 924 } 925 926 if (!entry->gpe_created) 927 entry->gpe_modified = 1; 928 929 /* Provide feedback if so requested. */ 930 if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 931 sb = sbuf_new_auto(); 932 sbuf_printf(sb, "%s%s modified\n", gp->name, 933 G_PART_NAME(table, entry, buf, sizeof(buf))); 934 sbuf_finish(sb); 935 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 936 sbuf_delete(sb); 937 } 938 return (0); 939} 940 941static int 942g_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp) 943{ 944 gctl_error(req, "%d verb 'move'", ENOSYS); 945 return (ENOSYS); 946} 947 948static int 949g_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp) 950{ 951 gctl_error(req, "%d verb 'recover'", ENOSYS); 952 return (ENOSYS); 953} 954 955static int 956g_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp) 957{ 958 gctl_error(req, "%d verb 'resize'", ENOSYS); 959 return (ENOSYS); 960} 961 962static int 963g_part_ctl_setunset(struct gctl_req *req, struct g_part_parms *gpp, 964 unsigned int set) 965{ 966 char buf[32]; 967 struct g_geom *gp; 968 struct g_part_entry *entry; 969 struct g_part_table *table; 970 struct sbuf *sb; 971 int error; 972 973 gp = gpp->gpp_geom; 974 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 975 g_topology_assert(); 976 977 table = gp->softc; 978 979 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 980 if (entry->gpe_deleted || entry->gpe_internal) 981 continue; 982 if (entry->gpe_index == gpp->gpp_index) 983 break; 984 } 985 if (entry == NULL) { 986 gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index); 987 return (ENOENT); 988 } 989 990 error = G_PART_SETUNSET(table, entry, gpp->gpp_attrib, set); 991 if (error) { 992 gctl_error(req, "%d attrib '%s'", error, gpp->gpp_attrib); 993 return (error); 994 } 995 996 /* Provide feedback if so requested. */ 997 if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 998 sb = sbuf_new_auto(); 999 sbuf_printf(sb, "%s%s has %s %sset\n", gp->name, 1000 G_PART_NAME(table, entry, buf, sizeof(buf)), 1001 gpp->gpp_attrib, (set) ? "" : "un"); 1002 sbuf_finish(sb); 1003 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 1004 sbuf_delete(sb); 1005 } 1006 return (0); 1007} 1008 1009static int 1010g_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp) 1011{ 1012 struct g_consumer *cp; 1013 struct g_provider *pp; 1014 struct g_geom *gp; 1015 struct g_part_entry *entry, *tmp; 1016 struct g_part_table *table; 1017 int error, reprobe; 1018 1019 gp = gpp->gpp_geom; 1020 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 1021 g_topology_assert(); 1022 1023 table = gp->softc; 1024 if (!table->gpt_opened) { 1025 gctl_error(req, "%d", EPERM); 1026 return (EPERM); 1027 } 1028 1029 cp = LIST_FIRST(&gp->consumer); 1030 LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) { 1031 entry->gpe_modified = 0; 1032 if (entry->gpe_created) { 1033 pp = entry->gpe_pp; 1034 if (pp != NULL) { 1035 pp->private = NULL; 1036 entry->gpe_pp = NULL; 1037 g_wither_provider(pp, ENXIO); 1038 } 1039 entry->gpe_deleted = 1; 1040 } 1041 if (entry->gpe_deleted) { 1042 LIST_REMOVE(entry, gpe_entry); 1043 g_free(entry); 1044 } 1045 } 1046 1047 g_topology_unlock(); 1048 1049 reprobe = (table->gpt_scheme == &g_part_null_scheme || 1050 table->gpt_created) ? 1 : 0; 1051 1052 if (reprobe) { 1053 if (!LIST_EMPTY(&table->gpt_entry)) { 1054 error = EBUSY; 1055 goto fail; 1056 } 1057 error = g_part_probe(gp, cp, table->gpt_depth); 1058 if (error) { 1059 g_topology_lock(); 1060 g_access(cp, -1, -1, -1); 1061 g_part_wither(gp, error); 1062 return (0); 1063 } 1064 table = gp->softc; 1065 } 1066 1067 error = G_PART_READ(table, cp); 1068 if (error) 1069 goto fail; 1070 1071 g_topology_lock(); 1072 1073 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 1074 if (!entry->gpe_internal) 1075 g_part_new_provider(gp, table, entry); 1076 } 1077 1078 table->gpt_opened = 0; 1079 g_access(cp, -1, -1, -1); 1080 return (0); 1081 1082fail: 1083 g_topology_lock(); 1084 gctl_error(req, "%d", error); 1085 return (error); 1086} 1087 1088static void 1089g_part_wither(struct g_geom *gp, int error) 1090{ 1091 struct g_part_entry *entry; 1092 struct g_part_table *table; 1093 1094 table = gp->softc; 1095 if (table != NULL) { 1096 while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) { 1097 LIST_REMOVE(entry, gpe_entry); 1098 g_free(entry); 1099 } 1100 if (gp->softc != NULL) { 1101 kobj_delete((kobj_t)gp->softc, M_GEOM); 1102 gp->softc = NULL; 1103 } 1104 } 1105 g_wither_geom(gp, error); 1106} 1107 1108/* 1109 * Class methods. 1110 */ 1111 1112static void 1113g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb) 1114{ 1115 struct g_part_parms gpp; 1116 struct g_part_table *table; 1117 struct gctl_req_arg *ap; 1118 const char *p; 1119 enum g_part_ctl ctlreq; 1120 unsigned int i, mparms, oparms, parm; 1121 int auto_commit, close_on_error; 1122 int error, len, modifies; 1123 1124 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb)); 1125 g_topology_assert(); 1126 1127 ctlreq = G_PART_CTL_NONE; 1128 modifies = 1; 1129 mparms = 0; 1130 oparms = G_PART_PARM_FLAGS | G_PART_PARM_OUTPUT | G_PART_PARM_VERSION; 1131 switch (*verb) { 1132 case 'a': 1133 if (!strcmp(verb, "add")) { 1134 ctlreq = G_PART_CTL_ADD; 1135 mparms |= G_PART_PARM_GEOM | G_PART_PARM_SIZE | 1136 G_PART_PARM_START | G_PART_PARM_TYPE; 1137 oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL; 1138 } 1139 break; 1140 case 'b': 1141 if (!strcmp(verb, "bootcode")) { 1142 ctlreq = G_PART_CTL_BOOTCODE; 1143 mparms |= G_PART_PARM_GEOM | G_PART_PARM_BOOTCODE; 1144 } 1145 break; 1146 case 'c': 1147 if (!strcmp(verb, "commit")) { 1148 ctlreq = G_PART_CTL_COMMIT; 1149 mparms |= G_PART_PARM_GEOM; 1150 modifies = 0; 1151 } else if (!strcmp(verb, "create")) { 1152 ctlreq = G_PART_CTL_CREATE; 1153 mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME; 1154 oparms |= G_PART_PARM_ENTRIES; 1155 } 1156 break; 1157 case 'd': 1158 if (!strcmp(verb, "delete")) { 1159 ctlreq = G_PART_CTL_DELETE; 1160 mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX; 1161 } else if (!strcmp(verb, "destroy")) { 1162 ctlreq = G_PART_CTL_DESTROY; 1163 mparms |= G_PART_PARM_GEOM; 1164 } 1165 break; 1166 case 'm': 1167 if (!strcmp(verb, "modify")) { 1168 ctlreq = G_PART_CTL_MODIFY; 1169 mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX; 1170 oparms |= G_PART_PARM_LABEL | G_PART_PARM_TYPE; 1171 } else if (!strcmp(verb, "move")) { 1172 ctlreq = G_PART_CTL_MOVE; 1173 mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX; 1174 } 1175 break; 1176 case 'r': 1177 if (!strcmp(verb, "recover")) { 1178 ctlreq = G_PART_CTL_RECOVER; 1179 mparms |= G_PART_PARM_GEOM; 1180 } else if (!strcmp(verb, "resize")) { 1181 ctlreq = G_PART_CTL_RESIZE; 1182 mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX; 1183 } 1184 break; 1185 case 's': 1186 if (!strcmp(verb, "set")) { 1187 ctlreq = G_PART_CTL_SET; 1188 mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM | 1189 G_PART_PARM_INDEX; 1190 } 1191 break; 1192 case 'u': 1193 if (!strcmp(verb, "undo")) { 1194 ctlreq = G_PART_CTL_UNDO; 1195 mparms |= G_PART_PARM_GEOM; 1196 modifies = 0; 1197 } else if (!strcmp(verb, "unset")) { 1198 ctlreq = G_PART_CTL_UNSET; 1199 mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM | 1200 G_PART_PARM_INDEX; 1201 } 1202 break; 1203 } 1204 if (ctlreq == G_PART_CTL_NONE) { 1205 gctl_error(req, "%d verb '%s'", EINVAL, verb); 1206 return; 1207 } 1208 1209 bzero(&gpp, sizeof(gpp)); 1210 for (i = 0; i < req->narg; i++) { 1211 ap = &req->arg[i]; 1212 parm = 0; 1213 switch (ap->name[0]) { 1214 case 'a': 1215 if (!strcmp(ap->name, "attrib")) 1216 parm = G_PART_PARM_ATTRIB; 1217 break; 1218 case 'b': 1219 if (!strcmp(ap->name, "bootcode")) 1220 parm = G_PART_PARM_BOOTCODE; 1221 break; 1222 case 'c': 1223 if (!strcmp(ap->name, "class")) 1224 continue; 1225 break; 1226 case 'e': 1227 if (!strcmp(ap->name, "entries")) 1228 parm = G_PART_PARM_ENTRIES; 1229 break; 1230 case 'f': 1231 if (!strcmp(ap->name, "flags")) 1232 parm = G_PART_PARM_FLAGS; 1233 break; 1234 case 'g': 1235 if (!strcmp(ap->name, "geom")) 1236 parm = G_PART_PARM_GEOM; 1237 break; 1238 case 'i': 1239 if (!strcmp(ap->name, "index")) 1240 parm = G_PART_PARM_INDEX; 1241 break; 1242 case 'l': 1243 if (!strcmp(ap->name, "label")) 1244 parm = G_PART_PARM_LABEL; 1245 break; 1246 case 'o': 1247 if (!strcmp(ap->name, "output")) 1248 parm = G_PART_PARM_OUTPUT; 1249 break; 1250 case 'p': 1251 if (!strcmp(ap->name, "provider")) 1252 parm = G_PART_PARM_PROVIDER; 1253 break; 1254 case 's': 1255 if (!strcmp(ap->name, "scheme")) 1256 parm = G_PART_PARM_SCHEME; 1257 else if (!strcmp(ap->name, "size")) 1258 parm = G_PART_PARM_SIZE; 1259 else if (!strcmp(ap->name, "start")) 1260 parm = G_PART_PARM_START; 1261 break; 1262 case 't': 1263 if (!strcmp(ap->name, "type")) 1264 parm = G_PART_PARM_TYPE; 1265 break; 1266 case 'v': 1267 if (!strcmp(ap->name, "verb")) 1268 continue; 1269 else if (!strcmp(ap->name, "version")) 1270 parm = G_PART_PARM_VERSION; 1271 break; 1272 } 1273 if ((parm & (mparms | oparms)) == 0) { 1274 gctl_error(req, "%d param '%s'", EINVAL, ap->name); 1275 return; 1276 } 1277 if (parm == G_PART_PARM_BOOTCODE) 1278 p = gctl_get_param(req, ap->name, &len); 1279 else 1280 p = gctl_get_asciiparam(req, ap->name); 1281 if (p == NULL) { 1282 gctl_error(req, "%d param '%s'", ENOATTR, ap->name); 1283 return; 1284 } 1285 switch (parm) { 1286 case G_PART_PARM_ATTRIB: 1287 error = g_part_parm_str(p, &gpp.gpp_attrib); 1288 break; 1289 case G_PART_PARM_BOOTCODE: 1290 gpp.gpp_codeptr = p; 1291 gpp.gpp_codesize = len; 1292 error = 0; 1293 break; 1294 case G_PART_PARM_ENTRIES: 1295 error = g_part_parm_uint(p, &gpp.gpp_entries); 1296 break; 1297 case G_PART_PARM_FLAGS: 1298 if (p[0] == '\0') 1299 continue; 1300 error = g_part_parm_str(p, &gpp.gpp_flags); 1301 break; 1302 case G_PART_PARM_GEOM: 1303 error = g_part_parm_geom(p, &gpp.gpp_geom); 1304 break; 1305 case G_PART_PARM_INDEX: 1306 error = g_part_parm_uint(p, &gpp.gpp_index); 1307 break; 1308 case G_PART_PARM_LABEL: 1309 /* An empty label is always valid. */ 1310 gpp.gpp_label = p; 1311 error = 0; 1312 break; 1313 case G_PART_PARM_OUTPUT: 1314 error = 0; /* Write-only parameter */ 1315 break; 1316 case G_PART_PARM_PROVIDER: 1317 error = g_part_parm_provider(p, &gpp.gpp_provider); 1318 break; 1319 case G_PART_PARM_SCHEME: 1320 error = g_part_parm_scheme(p, &gpp.gpp_scheme); 1321 break; 1322 case G_PART_PARM_SIZE: 1323 error = g_part_parm_quad(p, &gpp.gpp_size); 1324 break; 1325 case G_PART_PARM_START: 1326 error = g_part_parm_quad(p, &gpp.gpp_start); 1327 break; 1328 case G_PART_PARM_TYPE: 1329 error = g_part_parm_str(p, &gpp.gpp_type); 1330 break; 1331 case G_PART_PARM_VERSION: 1332 error = g_part_parm_uint(p, &gpp.gpp_version); 1333 break; 1334 default: 1335 error = EDOOFUS; 1336 break; 1337 } 1338 if (error) { 1339 gctl_error(req, "%d %s '%s'", error, ap->name, p); 1340 return; 1341 } 1342 gpp.gpp_parms |= parm; 1343 } 1344 if ((gpp.gpp_parms & mparms) != mparms) { 1345 parm = mparms - (gpp.gpp_parms & mparms); 1346 gctl_error(req, "%d param '%x'", ENOATTR, parm); 1347 return; 1348 } 1349 1350 /* Obtain permissions if possible/necessary. */ 1351 close_on_error = 0; 1352 table = NULL; /* Suppress uninit. warning. */ 1353 if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) { 1354 table = gpp.gpp_geom->softc; 1355 if (table != NULL && !table->gpt_opened) { 1356 error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer), 1357 1, 1, 1); 1358 if (error) { 1359 gctl_error(req, "%d geom '%s'", error, 1360 gpp.gpp_geom->name); 1361 return; 1362 } 1363 table->gpt_opened = 1; 1364 close_on_error = 1; 1365 } 1366 } 1367 1368 error = EDOOFUS; /* Prevent bogus uninit. warning. */ 1369 switch (ctlreq) { 1370 case G_PART_CTL_NONE: 1371 panic("%s", __func__); 1372 case G_PART_CTL_ADD: 1373 error = g_part_ctl_add(req, &gpp); 1374 break; 1375 case G_PART_CTL_BOOTCODE: 1376 error = g_part_ctl_bootcode(req, &gpp); 1377 break; 1378 case G_PART_CTL_COMMIT: 1379 error = g_part_ctl_commit(req, &gpp); 1380 break; 1381 case G_PART_CTL_CREATE: 1382 error = g_part_ctl_create(req, &gpp); 1383 break; 1384 case G_PART_CTL_DELETE: 1385 error = g_part_ctl_delete(req, &gpp); 1386 break; 1387 case G_PART_CTL_DESTROY: 1388 error = g_part_ctl_destroy(req, &gpp); 1389 break; 1390 case G_PART_CTL_MODIFY: 1391 error = g_part_ctl_modify(req, &gpp); 1392 break; 1393 case G_PART_CTL_MOVE: 1394 error = g_part_ctl_move(req, &gpp); 1395 break; 1396 case G_PART_CTL_RECOVER: 1397 error = g_part_ctl_recover(req, &gpp); 1398 break; 1399 case G_PART_CTL_RESIZE: 1400 error = g_part_ctl_resize(req, &gpp); 1401 break; 1402 case G_PART_CTL_SET: 1403 error = g_part_ctl_setunset(req, &gpp, 1); 1404 break; 1405 case G_PART_CTL_UNDO: 1406 error = g_part_ctl_undo(req, &gpp); 1407 break; 1408 case G_PART_CTL_UNSET: 1409 error = g_part_ctl_setunset(req, &gpp, 0); 1410 break; 1411 } 1412 1413 /* Implement automatic commit. */ 1414 if (!error) { 1415 auto_commit = (modifies && 1416 (gpp.gpp_parms & G_PART_PARM_FLAGS) && 1417 strchr(gpp.gpp_flags, 'C') != NULL) ? 1 : 0; 1418 if (auto_commit) { 1419 KASSERT(gpp.gpp_parms & G_PART_PARM_GEOM, (__func__)); 1420 error = g_part_ctl_commit(req, &gpp); 1421 } 1422 } 1423 1424 if (error && close_on_error) { 1425 g_access(LIST_FIRST(&gpp.gpp_geom->consumer), -1, -1, -1); 1426 table->gpt_opened = 0; 1427 } 1428} 1429 1430static int 1431g_part_destroy_geom(struct gctl_req *req, struct g_class *mp, 1432 struct g_geom *gp) 1433{ 1434 1435 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name)); 1436 g_topology_assert(); 1437 1438 g_part_wither(gp, EINVAL); 1439 return (0); 1440} 1441 1442static struct g_geom * 1443g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 1444{ 1445 struct g_consumer *cp; 1446 struct g_geom *gp; 1447 struct g_part_entry *entry; 1448 struct g_part_table *table; 1449 struct root_hold_token *rht; 1450 int attr, depth; 1451 int error; 1452 1453 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name)); 1454 g_topology_assert(); 1455 1456 /* 1457 * Create a GEOM with consumer and hook it up to the provider. 1458 * With that we become part of the topology. Optain read access 1459 * to the provider. 1460 */ 1461 gp = g_new_geomf(mp, "%s", pp->name); 1462 cp = g_new_consumer(gp); 1463 error = g_attach(cp, pp); 1464 if (error == 0) 1465 error = g_access(cp, 1, 0, 0); 1466 if (error != 0) { 1467 g_part_wither(gp, error); 1468 return (NULL); 1469 } 1470 1471 rht = root_mount_hold(mp->name); 1472 g_topology_unlock(); 1473 1474 /* 1475 * Short-circuit the whole probing galore when there's no 1476 * media present. 1477 */ 1478 if (pp->mediasize == 0 || pp->sectorsize == 0) { 1479 error = ENODEV; 1480 goto fail; 1481 } 1482 1483 /* Make sure we can nest and if so, determine our depth. */ 1484 error = g_getattr("PART::isleaf", cp, &attr); 1485 if (!error && attr) { 1486 error = ENODEV; 1487 goto fail; 1488 } 1489 error = g_getattr("PART::depth", cp, &attr); 1490 depth = (!error) ? attr + 1 : 0; 1491 1492 error = g_part_probe(gp, cp, depth); 1493 if (error) 1494 goto fail; 1495 1496 table = gp->softc; 1497 1498 /* If we're nested, get the absolute sector offset on disk. */ 1499 if (table->gpt_depth) { 1500 error = g_getattr("PART::offset", cp, &attr); 1501 if (error) 1502 goto fail; 1503 table->gpt_offset = attr; 1504 } 1505 1506 /* 1507 * Synthesize a disk geometry. Some partitioning schemes 1508 * depend on it and since some file systems need it even 1509 * when the partitition scheme doesn't, we do it here in 1510 * scheme-independent code. 1511 */ 1512 g_part_geometry(table, cp, pp->mediasize / pp->sectorsize); 1513 1514 error = G_PART_READ(table, cp); 1515 if (error) 1516 goto fail; 1517 1518 g_topology_lock(); 1519 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 1520 if (!entry->gpe_internal) 1521 g_part_new_provider(gp, table, entry); 1522 } 1523 1524 root_mount_rel(rht); 1525 g_access(cp, -1, 0, 0); 1526 return (gp); 1527 1528 fail: 1529 g_topology_lock(); 1530 root_mount_rel(rht); 1531 g_access(cp, -1, 0, 0); 1532 g_part_wither(gp, error); 1533 return (NULL); 1534} 1535 1536/* 1537 * Geom methods. 1538 */ 1539 1540static int 1541g_part_access(struct g_provider *pp, int dr, int dw, int de) 1542{ 1543 struct g_consumer *cp; 1544 1545 G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr, 1546 dw, de)); 1547 1548 cp = LIST_FIRST(&pp->geom->consumer); 1549 1550 /* We always gain write-exclusive access. */ 1551 return (g_access(cp, dr, dw, dw + de)); 1552} 1553 1554static void 1555g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 1556 struct g_consumer *cp, struct g_provider *pp) 1557{ 1558 char buf[64]; 1559 struct g_part_entry *entry; 1560 struct g_part_table *table; 1561 1562 KASSERT(sb != NULL && gp != NULL, (__func__)); 1563 table = gp->softc; 1564 1565 if (indent == NULL) { 1566 KASSERT(cp == NULL && pp != NULL, (__func__)); 1567 entry = pp->private; 1568 if (entry == NULL) 1569 return; 1570 sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index, 1571 (uintmax_t)entry->gpe_offset, 1572 G_PART_TYPE(table, entry, buf, sizeof(buf))); 1573 /* 1574 * libdisk compatibility quirk - the scheme dumps the 1575 * slicer name and partition type in a way that is 1576 * compatible with libdisk. When libdisk is not used 1577 * anymore, this should go away. 1578 */ 1579 G_PART_DUMPCONF(table, entry, sb, indent); 1580 } else if (cp != NULL) { /* Consumer configuration. */ 1581 KASSERT(pp == NULL, (__func__)); 1582 /* none */ 1583 } else if (pp != NULL) { /* Provider configuration. */ 1584 entry = pp->private; 1585 if (entry == NULL) 1586 return; 1587 sbuf_printf(sb, "%s<start>%ju</start>\n", indent, 1588 (uintmax_t)entry->gpe_start); 1589 sbuf_printf(sb, "%s<end>%ju</end>\n", indent, 1590 (uintmax_t)entry->gpe_end); 1591 sbuf_printf(sb, "%s<index>%u</index>\n", indent, 1592 entry->gpe_index); 1593 sbuf_printf(sb, "%s<type>%s</type>\n", indent, 1594 G_PART_TYPE(table, entry, buf, sizeof(buf))); 1595 sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent, 1596 (uintmax_t)entry->gpe_offset); 1597 sbuf_printf(sb, "%s<length>%ju</length>\n", indent, 1598 (uintmax_t)pp->mediasize); 1599 G_PART_DUMPCONF(table, entry, sb, indent); 1600 } else { /* Geom configuration. */ 1601 sbuf_printf(sb, "%s<scheme>%s</scheme>\n", indent, 1602 table->gpt_scheme->name); 1603 sbuf_printf(sb, "%s<entries>%u</entries>\n", indent, 1604 table->gpt_entries); 1605 sbuf_printf(sb, "%s<first>%ju</first>\n", indent, 1606 (uintmax_t)table->gpt_first); 1607 sbuf_printf(sb, "%s<last>%ju</last>\n", indent, 1608 (uintmax_t)table->gpt_last); 1609 sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent, 1610 table->gpt_sectors); 1611 sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent, 1612 table->gpt_heads); 1613 G_PART_DUMPCONF(table, NULL, sb, indent); 1614 } 1615} 1616 1617static void 1618g_part_orphan(struct g_consumer *cp) 1619{ 1620 struct g_provider *pp; 1621 1622 pp = cp->provider; 1623 KASSERT(pp != NULL, (__func__)); 1624 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name)); 1625 g_topology_assert(); 1626 1627 KASSERT(pp->error != 0, (__func__)); 1628 g_part_wither(cp->geom, pp->error); 1629} 1630 1631static void 1632g_part_spoiled(struct g_consumer *cp) 1633{ 1634 1635 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name)); 1636 g_topology_assert(); 1637 1638 g_part_wither(cp->geom, ENXIO); 1639} 1640 1641static void 1642g_part_start(struct bio *bp) 1643{ 1644 struct bio *bp2; 1645 struct g_consumer *cp; 1646 struct g_geom *gp; 1647 struct g_part_entry *entry; 1648 struct g_part_table *table; 1649 struct g_kerneldump *gkd; 1650 struct g_provider *pp; 1651 1652 pp = bp->bio_to; 1653 gp = pp->geom; 1654 table = gp->softc; 1655 cp = LIST_FIRST(&gp->consumer); 1656 1657 G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd, 1658 pp->name)); 1659 1660 entry = pp->private; 1661 if (entry == NULL) { 1662 g_io_deliver(bp, ENXIO); 1663 return; 1664 } 1665 1666 switch(bp->bio_cmd) { 1667 case BIO_DELETE: 1668 case BIO_READ: 1669 case BIO_WRITE: 1670 if (bp->bio_offset >= pp->mediasize) { 1671 g_io_deliver(bp, EIO); 1672 return; 1673 } 1674 bp2 = g_clone_bio(bp); 1675 if (bp2 == NULL) { 1676 g_io_deliver(bp, ENOMEM); 1677 return; 1678 } 1679 if (bp2->bio_offset + bp2->bio_length > pp->mediasize) 1680 bp2->bio_length = pp->mediasize - bp2->bio_offset; 1681 bp2->bio_done = g_std_done; 1682 bp2->bio_offset += entry->gpe_offset; 1683 g_io_request(bp2, cp); 1684 return; 1685 case BIO_FLUSH: 1686 break; 1687 case BIO_GETATTR: 1688 if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads)) 1689 return; 1690 if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors)) 1691 return; 1692 if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf)) 1693 return; 1694 if (g_handleattr_int(bp, "PART::depth", table->gpt_depth)) 1695 return; 1696 if (g_handleattr_int(bp, "PART::offset", 1697 table->gpt_offset + entry->gpe_start)) 1698 return; 1699 if (g_handleattr_str(bp, "PART::scheme", 1700 table->gpt_scheme->name)) 1701 return; 1702 if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) { 1703 /* 1704 * Check that the partition is suitable for kernel 1705 * dumps. Typically only swap partitions should be 1706 * used. 1707 */ 1708 if (!G_PART_DUMPTO(table, entry)) { 1709 g_io_deliver(bp, ENXIO); 1710 return; 1711 } 1712 gkd = (struct g_kerneldump *)bp->bio_data; 1713 if (gkd->offset >= pp->mediasize) { 1714 g_io_deliver(bp, EIO); 1715 return; 1716 } 1717 if (gkd->offset + gkd->length > pp->mediasize) 1718 gkd->length = pp->mediasize - gkd->offset; 1719 gkd->offset += entry->gpe_offset; 1720 } 1721 break; 1722 default: 1723 g_io_deliver(bp, EOPNOTSUPP); 1724 return; 1725 } 1726 1727 bp2 = g_clone_bio(bp); 1728 if (bp2 == NULL) { 1729 g_io_deliver(bp, ENOMEM); 1730 return; 1731 } 1732 bp2->bio_done = g_std_done; 1733 g_io_request(bp2, cp); 1734} 1735 1736static void 1737g_part_init(struct g_class *mp) 1738{ 1739 1740 TAILQ_INSERT_HEAD(&g_part_schemes, &g_part_null_scheme, scheme_list); 1741} 1742 1743static void 1744g_part_fini(struct g_class *mp) 1745{ 1746 1747 TAILQ_REMOVE(&g_part_schemes, &g_part_null_scheme, scheme_list); 1748} 1749 1750static void 1751g_part_unload_event(void *arg, int flag) 1752{ 1753 struct g_consumer *cp; 1754 struct g_geom *gp; 1755 struct g_provider *pp; 1756 struct g_part_scheme *scheme; 1757 struct g_part_table *table; 1758 uintptr_t *xchg; 1759 int acc, error; 1760 1761 if (flag == EV_CANCEL) 1762 return; 1763 1764 xchg = arg; 1765 error = 0; 1766 scheme = (void *)(*xchg); 1767 1768 g_topology_assert(); 1769 1770 LIST_FOREACH(gp, &g_part_class.geom, geom) { 1771 table = gp->softc; 1772 if (table->gpt_scheme != scheme) 1773 continue; 1774 1775 acc = 0; 1776 LIST_FOREACH(pp, &gp->provider, provider) 1777 acc += pp->acr + pp->acw + pp->ace; 1778 LIST_FOREACH(cp, &gp->consumer, consumer) 1779 acc += cp->acr + cp->acw + cp->ace; 1780 1781 if (!acc) 1782 g_part_wither(gp, ENOSYS); 1783 else 1784 error = EBUSY; 1785 } 1786 1787 if (!error) 1788 TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list); 1789 1790 *xchg = error; 1791} 1792 1793int 1794g_part_modevent(module_t mod, int type, struct g_part_scheme *scheme) 1795{ 1796 uintptr_t arg; 1797 int error; 1798 1799 switch (type) { 1800 case MOD_LOAD: 1801 TAILQ_INSERT_TAIL(&g_part_schemes, scheme, scheme_list); 1802 1803 error = g_retaste(&g_part_class); 1804 if (error) 1805 TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list); 1806 break; 1807 case MOD_UNLOAD: 1808 arg = (uintptr_t)scheme; 1809 error = g_waitfor_event(g_part_unload_event, &arg, M_WAITOK, 1810 NULL); 1811 if (!error) 1812 error = (arg == (uintptr_t)scheme) ? EDOOFUS : arg; 1813 break; 1814 default: 1815 error = EOPNOTSUPP; 1816 break; 1817 } 1818 1819 return (error); 1820} 1821