geom_bsd.c revision 185518
1/*- 2 * Copyright (c) 2002 Poul-Henning Kamp 3 * Copyright (c) 2002 Networks Associates Technology, Inc. 4 * All rights reserved. 5 * 6 * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7 * and NAI Labs, the Security Research Division of Network Associates, Inc. 8 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9 * DARPA CHATS research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. The names of the authors may not be used to endorse or promote 20 * products derived from this software without specific prior written 21 * permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36/* 37 * This is the method for dealing with BSD disklabels. It has been 38 * extensively (by my standards at least) commented, in the vain hope that 39 * it will serve as the source in future copy&paste operations. 40 */ 41 42#include <sys/cdefs.h> 43__FBSDID("$FreeBSD: head/sys/geom/geom_bsd.c 185518 2008-12-01 15:02:00Z ivoras $"); 44 45#include <sys/param.h> 46#include <sys/endian.h> 47#include <sys/systm.h> 48#include <sys/kernel.h> 49#include <sys/fcntl.h> 50#include <sys/conf.h> 51#include <sys/bio.h> 52#include <sys/malloc.h> 53#include <sys/lock.h> 54#include <sys/mutex.h> 55#include <sys/md5.h> 56#include <sys/errno.h> 57#include <sys/disklabel.h> 58#include <sys/gpt.h> 59#include <sys/uuid.h> 60#include <geom/geom.h> 61#include <geom/geom_slice.h> 62 63#define BSD_CLASS_NAME "BSD" 64 65#define ALPHA_LABEL_OFFSET 64 66#define HISTORIC_LABEL_OFFSET 512 67 68#define LABELSIZE (148 + 16 * MAXPARTITIONS) 69 70static void g_bsd_hotwrite(void *arg, int flag); 71/* 72 * Our private data about one instance. All the rest is handled by the 73 * slice code and stored in its softc, so this is just the stuff 74 * specific to BSD disklabels. 75 */ 76struct g_bsd_softc { 77 off_t labeloffset; 78 off_t mbroffset; 79 off_t rawoffset; 80 struct disklabel ondisk; 81 u_char label[LABELSIZE]; 82 u_char labelsum[16]; 83}; 84 85/* 86 * Modify our slicer to match proposed disklabel, if possible. 87 * This is where we make sure we don't do something stupid. 88 */ 89static int 90g_bsd_modify(struct g_geom *gp, u_char *label) 91{ 92 int i, error; 93 struct partition *ppp; 94 struct g_slicer *gsp; 95 struct g_consumer *cp; 96 struct g_bsd_softc *ms; 97 u_int secsize, u; 98 off_t rawoffset, o; 99 struct disklabel dl; 100 MD5_CTX md5sum; 101 102 g_topology_assert(); 103 gsp = gp->softc; 104 ms = gsp->softc; 105 106 error = bsd_disklabel_le_dec(label, &dl, MAXPARTITIONS); 107 if (error) { 108 return (error); 109 } 110 111 /* Get dimensions of our device. */ 112 cp = LIST_FIRST(&gp->consumer); 113 secsize = cp->provider->sectorsize; 114 115 /* ... or a smaller sector size. */ 116 if (dl.d_secsize < secsize) { 117 return (EINVAL); 118 } 119 120 /* ... or a non-multiple sector size. */ 121 if (dl.d_secsize % secsize != 0) { 122 return (EINVAL); 123 } 124 125 /* Historical braindamage... */ 126 rawoffset = (off_t)dl.d_partitions[RAW_PART].p_offset * dl.d_secsize; 127 128 for (i = 0; i < dl.d_npartitions; i++) { 129 ppp = &dl.d_partitions[i]; 130 if (ppp->p_size == 0) 131 continue; 132 o = (off_t)ppp->p_offset * dl.d_secsize; 133 134 if (o < rawoffset) 135 rawoffset = 0; 136 } 137 138 if (rawoffset != 0 && (off_t)rawoffset != ms->mbroffset) 139 printf("WARNING: %s expected rawoffset %jd, found %jd\n", 140 gp->name, 141 (intmax_t)ms->mbroffset/dl.d_secsize, 142 (intmax_t)rawoffset/dl.d_secsize); 143 144 /* Don't munge open partitions. */ 145 for (i = 0; i < dl.d_npartitions; i++) { 146 ppp = &dl.d_partitions[i]; 147 148 o = (off_t)ppp->p_offset * dl.d_secsize; 149 if (o == 0) 150 o = rawoffset; 151 error = g_slice_config(gp, i, G_SLICE_CONFIG_CHECK, 152 o - rawoffset, 153 (off_t)ppp->p_size * dl.d_secsize, 154 dl.d_secsize, 155 "%s%c", gp->name, 'a' + i); 156 if (error) 157 return (error); 158 } 159 160 /* Look good, go for it... */ 161 for (u = 0; u < gsp->nslice; u++) { 162 ppp = &dl.d_partitions[u]; 163 o = (off_t)ppp->p_offset * dl.d_secsize; 164 if (o == 0) 165 o = rawoffset; 166 g_slice_config(gp, u, G_SLICE_CONFIG_SET, 167 o - rawoffset, 168 (off_t)ppp->p_size * dl.d_secsize, 169 dl.d_secsize, 170 "%s%c", gp->name, 'a' + u); 171 } 172 173 /* Update our softc */ 174 ms->ondisk = dl; 175 if (label != ms->label) 176 bcopy(label, ms->label, LABELSIZE); 177 ms->rawoffset = rawoffset; 178 179 /* 180 * In order to avoid recursively attaching to the same 181 * on-disk label (it's usually visible through the 'c' 182 * partition) we calculate an MD5 and ask if other BSD's 183 * below us love that label. If they do, we don't. 184 */ 185 MD5Init(&md5sum); 186 MD5Update(&md5sum, ms->label, sizeof(ms->label)); 187 MD5Final(ms->labelsum, &md5sum); 188 189 return (0); 190} 191 192/* 193 * This is an internal helper function, called multiple times from the taste 194 * function to try to locate a disklabel on the disk. More civilized formats 195 * will not need this, as there is only one possible place on disk to look 196 * for the magic spot. 197 */ 198 199static int 200g_bsd_try(struct g_geom *gp, struct g_slicer *gsp, struct g_consumer *cp, int secsize, struct g_bsd_softc *ms, off_t offset) 201{ 202 int error; 203 u_char *buf; 204 struct disklabel *dl; 205 off_t secoff; 206 207 /* 208 * We need to read entire aligned sectors, and we assume that the 209 * disklabel does not span sectors, so one sector is enough. 210 */ 211 secoff = offset % secsize; 212 buf = g_read_data(cp, offset - secoff, secsize, NULL); 213 if (buf == NULL) 214 return (ENOENT); 215 216 /* Decode into our native format. */ 217 dl = &ms->ondisk; 218 error = bsd_disklabel_le_dec(buf + secoff, dl, MAXPARTITIONS); 219 if (!error) 220 bcopy(buf + secoff, ms->label, LABELSIZE); 221 222 /* Remember to free the buffer g_read_data() gave us. */ 223 g_free(buf); 224 225 ms->labeloffset = offset; 226 return (error); 227} 228 229/* 230 * This function writes the current label to disk, possibly updating 231 * the alpha SRM checksum. 232 */ 233 234static int 235g_bsd_writelabel(struct g_geom *gp, u_char *bootcode) 236{ 237 off_t secoff; 238 u_int secsize; 239 struct g_consumer *cp; 240 struct g_slicer *gsp; 241 struct g_bsd_softc *ms; 242 u_char *buf; 243 uint64_t sum; 244 int error, i; 245 246 gsp = gp->softc; 247 ms = gsp->softc; 248 cp = LIST_FIRST(&gp->consumer); 249 /* Get sector size, we need it to read data. */ 250 secsize = cp->provider->sectorsize; 251 secoff = ms->labeloffset % secsize; 252 if (bootcode == NULL) { 253 buf = g_read_data(cp, ms->labeloffset - secoff, secsize, &error); 254 if (buf == NULL) 255 return (error); 256 bcopy(ms->label, buf + secoff, sizeof(ms->label)); 257 } else { 258 buf = bootcode; 259 bcopy(ms->label, buf + ms->labeloffset, sizeof(ms->label)); 260 } 261 if (ms->labeloffset == ALPHA_LABEL_OFFSET) { 262 sum = 0; 263 for (i = 0; i < 63; i++) 264 sum += le64dec(buf + i * 8); 265 le64enc(buf + 504, sum); 266 } 267 if (bootcode == NULL) { 268 error = g_write_data(cp, ms->labeloffset - secoff, buf, secsize); 269 g_free(buf); 270 } else { 271 error = g_write_data(cp, 0, bootcode, BBSIZE); 272 } 273 return(error); 274} 275 276/* 277 * If the user tries to overwrite our disklabel through an open partition 278 * or via a magicwrite config call, we end up here and try to prevent 279 * footshooting as best we can. 280 */ 281static void 282g_bsd_hotwrite(void *arg, int flag) 283{ 284 struct bio *bp; 285 struct g_geom *gp; 286 struct g_slicer *gsp; 287 struct g_slice *gsl; 288 struct g_bsd_softc *ms; 289 u_char *p; 290 int error; 291 292 g_topology_assert(); 293 /* 294 * We should never get canceled, because that would amount to a removal 295 * of the geom while there was outstanding I/O requests. 296 */ 297 KASSERT(flag != EV_CANCEL, ("g_bsd_hotwrite cancelled")); 298 bp = arg; 299 gp = bp->bio_to->geom; 300 gsp = gp->softc; 301 ms = gsp->softc; 302 gsl = &gsp->slices[bp->bio_to->index]; 303 p = (u_char*)bp->bio_data + ms->labeloffset 304 - (bp->bio_offset + gsl->offset); 305 error = g_bsd_modify(gp, p); 306 if (error) { 307 g_io_deliver(bp, EPERM); 308 return; 309 } 310 g_slice_finish_hot(bp); 311} 312 313/*- 314 * This start routine is only called for non-trivial requests, all the 315 * trivial ones are handled autonomously by the slice code. 316 * For requests we handle here, we must call the g_io_deliver() on the 317 * bio, and return non-zero to indicate to the slice code that we did so. 318 * This code executes in the "DOWN" I/O path, this means: 319 * * No sleeping. 320 * * Don't grab the topology lock. 321 * * Don't call biowait, g_getattr(), g_setattr() or g_read_data() 322 */ 323static int 324g_bsd_ioctl(struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td) 325{ 326 struct g_geom *gp; 327 struct g_bsd_softc *ms; 328 struct g_slicer *gsp; 329 u_char *label; 330 int error; 331 332 gp = pp->geom; 333 gsp = gp->softc; 334 ms = gsp->softc; 335 336 switch(cmd) { 337 case DIOCGDINFO: 338 /* Return a copy of the disklabel to userland. */ 339 bsd_disklabel_le_dec(ms->label, data, MAXPARTITIONS); 340 return(0); 341 case DIOCBSDBB: { 342 struct g_consumer *cp; 343 u_char *buf; 344 void *p; 345 int error, i; 346 uint64_t sum; 347 348 if (!(fflag & FWRITE)) 349 return (EPERM); 350 /* The disklabel to set is the ioctl argument. */ 351 buf = g_malloc(BBSIZE, M_WAITOK); 352 p = *(void **)data; 353 error = copyin(p, buf, BBSIZE); 354 if (!error) { 355 /* XXX: Rude, but supposedly safe */ 356 DROP_GIANT(); 357 g_topology_lock(); 358 /* Validate and modify our slice instance to match. */ 359 error = g_bsd_modify(gp, buf + ms->labeloffset); 360 if (!error) { 361 cp = LIST_FIRST(&gp->consumer); 362 if (ms->labeloffset == ALPHA_LABEL_OFFSET) { 363 sum = 0; 364 for (i = 0; i < 63; i++) 365 sum += le64dec(buf + i * 8); 366 le64enc(buf + 504, sum); 367 } 368 error = g_write_data(cp, 0, buf, BBSIZE); 369 } 370 g_topology_unlock(); 371 PICKUP_GIANT(); 372 } 373 g_free(buf); 374 return (error); 375 } 376 case DIOCSDINFO: 377 case DIOCWDINFO: { 378 if (!(fflag & FWRITE)) 379 return (EPERM); 380 label = g_malloc(LABELSIZE, M_WAITOK); 381 /* The disklabel to set is the ioctl argument. */ 382 bsd_disklabel_le_enc(label, data); 383 384 DROP_GIANT(); 385 g_topology_lock(); 386 /* Validate and modify our slice instance to match. */ 387 error = g_bsd_modify(gp, label); 388 if (error == 0 && cmd == DIOCWDINFO) 389 error = g_bsd_writelabel(gp, NULL); 390 g_topology_unlock(); 391 PICKUP_GIANT(); 392 g_free(label); 393 return(error); 394 } 395 default: 396 return (ENOIOCTL); 397 } 398} 399 400static int 401g_bsd_start(struct bio *bp) 402{ 403 struct g_geom *gp; 404 struct g_bsd_softc *ms; 405 struct g_slicer *gsp; 406 407 gp = bp->bio_to->geom; 408 gsp = gp->softc; 409 ms = gsp->softc; 410 if (bp->bio_cmd == BIO_GETATTR) { 411 if (g_handleattr(bp, "BSD::labelsum", ms->labelsum, 412 sizeof(ms->labelsum))) 413 return (1); 414 } 415 return (0); 416} 417 418/* 419 * Dump configuration information in XML format. 420 * Notice that the function is called once for the geom and once for each 421 * consumer and provider. We let g_slice_dumpconf() do most of the work. 422 */ 423static void 424g_bsd_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp) 425{ 426 struct g_bsd_softc *ms; 427 struct g_slicer *gsp; 428 429 gsp = gp->softc; 430 ms = gsp->softc; 431 g_slice_dumpconf(sb, indent, gp, cp, pp); 432 if (indent != NULL && pp == NULL && cp == NULL) { 433 sbuf_printf(sb, "%s<labeloffset>%jd</labeloffset>\n", 434 indent, (intmax_t)ms->labeloffset); 435 sbuf_printf(sb, "%s<rawoffset>%jd</rawoffset>\n", 436 indent, (intmax_t)ms->rawoffset); 437 sbuf_printf(sb, "%s<mbroffset>%jd</mbroffset>\n", 438 indent, (intmax_t)ms->mbroffset); 439 } else if (pp != NULL) { 440 if (indent == NULL) 441 sbuf_printf(sb, " ty %d", 442 ms->ondisk.d_partitions[pp->index].p_fstype); 443 else 444 sbuf_printf(sb, "%s<type>%d</type>\n", indent, 445 ms->ondisk.d_partitions[pp->index].p_fstype); 446 } 447} 448 449/* 450 * The taste function is called from the event-handler, with the topology 451 * lock already held and a provider to examine. The flags are unused. 452 * 453 * If flags == G_TF_NORMAL, the idea is to take a bite of the provider and 454 * if we find valid, consistent magic on it, build a geom on it. 455 * 456 * There may be cases where the operator would like to put a BSD-geom on 457 * providers which do not meet all of the requirements. This can be done 458 * by instead passing the G_TF_INSIST flag, which will override these 459 * checks. 460 * 461 * The final flags value is G_TF_TRANSPARENT, which instructs the method 462 * to put a geom on top of the provider and configure it to be as transparent 463 * as possible. This is not really relevant to the BSD method and therefore 464 * not implemented here. 465 */ 466 467static struct uuid freebsd_slice = GPT_ENT_TYPE_FREEBSD; 468 469static struct g_geom * 470g_bsd_taste(struct g_class *mp, struct g_provider *pp, int flags) 471{ 472 struct g_geom *gp; 473 struct g_consumer *cp; 474 int error, i; 475 struct g_bsd_softc *ms; 476 u_int secsize; 477 struct g_slicer *gsp; 478 u_char hash[16]; 479 MD5_CTX md5sum; 480 struct uuid uuid; 481 482 g_trace(G_T_TOPOLOGY, "bsd_taste(%s,%s)", mp->name, pp->name); 483 g_topology_assert(); 484 485 /* We don't implement transparent inserts. */ 486 if (flags == G_TF_TRANSPARENT) 487 return (NULL); 488 489 /* 490 * BSD labels are a subclass of the general "slicing" topology so 491 * a lot of the work can be done by the common "slice" code. 492 * Create a geom with space for MAXPARTITIONS providers, one consumer 493 * and a softc structure for us. Specify the provider to attach 494 * the consumer to and our "start" routine for special requests. 495 * The provider is opened with mode (1,0,0) so we can do reads 496 * from it. 497 */ 498 gp = g_slice_new(mp, MAXPARTITIONS, pp, &cp, &ms, 499 sizeof(*ms), g_bsd_start); 500 if (gp == NULL) 501 return (NULL); 502 503 /* Get the geom_slicer softc from the geom. */ 504 gsp = gp->softc; 505 506 /* 507 * The do...while loop here allows us to have multiple escapes 508 * using a simple "break". This improves code clarity without 509 * ending up in deep nesting and without using goto or come from. 510 */ 511 do { 512 /* 513 * If the provider is an MBR we will only auto attach 514 * to type 165 slices in the G_TF_NORMAL case. We will 515 * attach to any other type. 516 */ 517 error = g_getattr("MBR::type", cp, &i); 518 if (!error) { 519 if (i != 165 && flags == G_TF_NORMAL) 520 break; 521 error = g_getattr("MBR::offset", cp, &ms->mbroffset); 522 if (error) 523 break; 524 } 525 526 /* Same thing if we are inside a PC98 */ 527 error = g_getattr("PC98::type", cp, &i); 528 if (!error) { 529 if (i != 0xc494 && flags == G_TF_NORMAL) 530 break; 531 error = g_getattr("PC98::offset", cp, &ms->mbroffset); 532 if (error) 533 break; 534 } 535 536 /* Same thing if we are inside a GPT */ 537 error = g_getattr("GPT::type", cp, &uuid); 538 if (!error) { 539 if (memcmp(&uuid, &freebsd_slice, sizeof(uuid)) != 0 && 540 flags == G_TF_NORMAL) 541 break; 542 } 543 544 /* Get sector size, we need it to read data. */ 545 secsize = cp->provider->sectorsize; 546 if (secsize < 512) 547 break; 548 549 /* First look for a label at the start of the second sector. */ 550 error = g_bsd_try(gp, gsp, cp, secsize, ms, secsize); 551 552 /* 553 * If sector size is not 512 the label still can be at 554 * offset 512, not at the start of the second sector. At least 555 * it's true for labels created by the FreeBSD's bsdlabel(8). 556 */ 557 if (error && secsize != HISTORIC_LABEL_OFFSET) 558 error = g_bsd_try(gp, gsp, cp, secsize, ms, 559 HISTORIC_LABEL_OFFSET); 560 561 /* Next, look for alpha labels */ 562 if (error) 563 error = g_bsd_try(gp, gsp, cp, secsize, ms, 564 ALPHA_LABEL_OFFSET); 565 566 /* If we didn't find a label, punt. */ 567 if (error) 568 break; 569 570 /* 571 * In order to avoid recursively attaching to the same 572 * on-disk label (it's usually visible through the 'c' 573 * partition) we calculate an MD5 and ask if other BSD's 574 * below us love that label. If they do, we don't. 575 */ 576 MD5Init(&md5sum); 577 MD5Update(&md5sum, ms->label, sizeof(ms->label)); 578 MD5Final(ms->labelsum, &md5sum); 579 580 error = g_getattr("BSD::labelsum", cp, &hash); 581 if (!error && !bcmp(ms->labelsum, hash, sizeof(hash))) 582 break; 583 584 /* 585 * Process the found disklabel, and modify our "slice" 586 * instance to match it, if possible. 587 */ 588 error = g_bsd_modify(gp, ms->label); 589 } while (0); 590 591 /* Success or failure, we can close our provider now. */ 592 g_access(cp, -1, 0, 0); 593 594 /* If we have configured any providers, return the new geom. */ 595 if (gsp->nprovider > 0) { 596 g_slice_conf_hot(gp, 0, ms->labeloffset, LABELSIZE, 597 G_SLICE_HOT_ALLOW, G_SLICE_HOT_DENY, G_SLICE_HOT_CALL); 598 gsp->hot = g_bsd_hotwrite; 599 return (gp); 600 } 601 /* 602 * ...else push the "self-destruct" button, by spoiling our own 603 * consumer. This triggers a call to g_slice_spoiled which will 604 * dismantle what was setup. 605 */ 606 g_slice_spoiled(cp); 607 return (NULL); 608} 609 610struct h0h0 { 611 struct g_geom *gp; 612 struct g_bsd_softc *ms; 613 u_char *label; 614 int error; 615}; 616 617static void 618g_bsd_callconfig(void *arg, int flag) 619{ 620 struct h0h0 *hp; 621 622 hp = arg; 623 hp->error = g_bsd_modify(hp->gp, hp->label); 624 if (!hp->error) 625 hp->error = g_bsd_writelabel(hp->gp, NULL); 626} 627 628/* 629 * NB! curthread is user process which GCTL'ed. 630 */ 631static void 632g_bsd_config(struct gctl_req *req, struct g_class *mp, char const *verb) 633{ 634 u_char *label; 635 int error; 636 struct h0h0 h0h0; 637 struct g_geom *gp; 638 struct g_slicer *gsp; 639 struct g_consumer *cp; 640 struct g_bsd_softc *ms; 641 642 g_topology_assert(); 643 gp = gctl_get_geom(req, mp, "geom"); 644 if (gp == NULL) 645 return; 646 cp = LIST_FIRST(&gp->consumer); 647 gsp = gp->softc; 648 ms = gsp->softc; 649 if (!strcmp(verb, "read mbroffset")) { 650 gctl_set_param_err(req, "mbroffset", &ms->mbroffset, 651 sizeof(ms->mbroffset)); 652 return; 653 } else if (!strcmp(verb, "write label")) { 654 label = gctl_get_paraml(req, "label", LABELSIZE); 655 if (label == NULL) 656 return; 657 h0h0.gp = gp; 658 h0h0.ms = gsp->softc; 659 h0h0.label = label; 660 h0h0.error = -1; 661 /* XXX: Does this reference register with our selfdestruct code ? */ 662 error = g_access(cp, 1, 1, 1); 663 if (error) { 664 gctl_error(req, "could not access consumer"); 665 return; 666 } 667 g_bsd_callconfig(&h0h0, 0); 668 error = h0h0.error; 669 g_access(cp, -1, -1, -1); 670 } else if (!strcmp(verb, "write bootcode")) { 671 label = gctl_get_paraml(req, "bootcode", BBSIZE); 672 if (label == NULL) 673 return; 674 /* XXX: Does this reference register with our selfdestruct code ? */ 675 error = g_access(cp, 1, 1, 1); 676 if (error) { 677 gctl_error(req, "could not access consumer"); 678 return; 679 } 680 error = g_bsd_writelabel(gp, label); 681 g_access(cp, -1, -1, -1); 682 } else { 683 gctl_error(req, "Unknown verb parameter"); 684 } 685 686 return; 687} 688 689/* Finally, register with GEOM infrastructure. */ 690static struct g_class g_bsd_class = { 691 .name = BSD_CLASS_NAME, 692 .version = G_VERSION, 693 .taste = g_bsd_taste, 694 .ctlreq = g_bsd_config, 695 .dumpconf = g_bsd_dumpconf, 696 .ioctl = g_bsd_ioctl, 697}; 698 699DECLARE_GEOM_CLASS(g_bsd_class, g_bsd); 700