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