1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2002 Poul-Henning Kamp 5 * Copyright (c) 2002 Networks Associates Technology, Inc. 6 * All rights reserved. 7 * Copyright (c) 2022 Alexander Motin <mav@FreeBSD.org> 8 * 9 * This software was developed for the FreeBSD Project by Poul-Henning Kamp 10 * and NAI Labs, the Security Research Division of Network Associates, Inc. 11 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 12 * DARPA CHATS research program. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. The names of the authors may not be used to endorse or promote 23 * products derived from this software without specific prior written 24 * permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/conf.h> 43 #include <sys/malloc.h> 44 #include <sys/sbuf.h> 45 46 #include <vm/vm.h> 47 #include <vm/vm_extern.h> 48 49 #include <geom/geom.h> 50 #include <geom/geom_int.h> 51 #define GCTL_TABLE 1 52 #include <geom/geom_ctl.h> 53 54 #include <machine/stdarg.h> 55 56 static d_ioctl_t g_ctl_ioctl; 57 58 static struct cdevsw g_ctl_cdevsw = { 59 .d_version = D_VERSION, 60 .d_flags = 0, 61 .d_ioctl = g_ctl_ioctl, 62 .d_name = "g_ctl", 63 }; 64 65 CTASSERT(GCTL_PARAM_RD == VM_PROT_READ); 66 CTASSERT(GCTL_PARAM_WR == VM_PROT_WRITE); 67 68 void 69 g_ctl_init(void) 70 { 71 72 make_dev_credf(MAKEDEV_ETERNAL, &g_ctl_cdevsw, 0, NULL, 73 UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL); 74 } 75 76 /* 77 * Report an error back to the user in ascii format. Return nerror 78 * or EINVAL if nerror isn't specified. 79 */ 80 int 81 gctl_error(struct gctl_req *req, const char *fmt, ...) 82 { 83 va_list ap; 84 85 if (req == NULL) 86 return (EINVAL); 87 88 /* We only record the first error */ 89 if (sbuf_done(req->serror)) { 90 if (!req->nerror) 91 req->nerror = EEXIST; 92 #ifdef DIAGNOSTIC 93 printf("gctl_error: buffer closed, message discarded.\n"); 94 #endif 95 return (req->nerror); 96 } 97 if (!req->nerror) 98 req->nerror = EINVAL; 99 100 /* If this is the last of several messages, indent it on a new line */ 101 if (sbuf_len(req->serror) > 0) 102 sbuf_cat(req->serror, "\n\t"); 103 va_start(ap, fmt); 104 sbuf_vprintf(req->serror, fmt, ap); 105 va_end(ap); 106 gctl_post_messages(req); 107 return (req->nerror); 108 } 109 110 /* 111 * The gctl_error() function will only report a single message. 112 * Commands that handle multiple devices may want to report a 113 * message for each of the devices. The gctl_msg() function 114 * can be called multiple times to post messages. When done 115 * the application must either call gctl_post_messages() or 116 * call gctl_error() to cause the messages to be reported to 117 * the calling process. 118 * 119 * The errno argument should be zero if it is an informational 120 * message or an errno value (EINVAL, EBUSY, etc) if it is an error. 121 * If any of the messages has a non-zero errno, the utility will 122 * EXIT_FAILURE. If only informational messages (with zero errno) 123 * are posted, the utility will EXIT_SUCCESS. 124 */ 125 void 126 gctl_msg(struct gctl_req *req, int errno, const char *fmt, ...) 127 { 128 va_list ap; 129 130 if (req == NULL) 131 return; 132 if (sbuf_done(req->serror)) { 133 #ifdef DIAGNOSTIC 134 printf("gctl_msg: buffer closed, message discarded.\n"); 135 #endif 136 return; 137 } 138 if (req->nerror == 0) 139 req->nerror = errno; 140 /* Put second and later messages indented on a new line */ 141 if (sbuf_len(req->serror) > 0) 142 sbuf_cat(req->serror, "\n\t"); 143 va_start(ap, fmt); 144 sbuf_vprintf(req->serror, fmt, ap); 145 va_end(ap); 146 } 147 148 /* 149 * Post the messages to the user. 150 */ 151 void 152 gctl_post_messages(struct gctl_req *req) 153 { 154 155 if (sbuf_done(req->serror)) { 156 #ifdef DIAGNOSTIC 157 printf("gctl_post_messages: message buffer already closed.\n"); 158 #endif 159 return; 160 } 161 sbuf_finish(req->serror); 162 if (g_debugflags & G_F_CTLDUMP) 163 printf("gctl %p message(s) \"%s\"\n", req, 164 sbuf_data(req->serror)); 165 } 166 167 /* 168 * Allocate space and copyin() something. 169 * XXX: this should really be a standard function in the kernel. 170 */ 171 static void * 172 geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len) 173 { 174 void *ptr; 175 176 ptr = g_malloc(len, M_WAITOK); 177 req->nerror = copyin(uaddr, ptr, len); 178 if (!req->nerror) 179 return (ptr); 180 g_free(ptr); 181 return (NULL); 182 } 183 184 static void 185 gctl_copyin(struct gctl_req *req) 186 { 187 struct gctl_req_arg *ap; 188 char *p; 189 u_int i; 190 191 if (req->narg > GEOM_CTL_ARG_MAX) { 192 gctl_error(req, "too many arguments"); 193 req->arg = NULL; 194 return; 195 } 196 197 ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap)); 198 if (ap == NULL) { 199 gctl_error(req, "bad control request"); 200 req->arg = NULL; 201 return; 202 } 203 204 /* Nothing have been copyin()'ed yet */ 205 for (i = 0; i < req->narg; i++) { 206 ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL); 207 ap[i].flag &= ~GCTL_PARAM_CHANGED; 208 ap[i].kvalue = NULL; 209 } 210 211 for (i = 0; i < req->narg; i++) { 212 if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) { 213 gctl_error(req, 214 "wrong param name length %d: %d", i, ap[i].nlen); 215 break; 216 } 217 p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen); 218 if (p == NULL) 219 break; 220 if (p[ap[i].nlen - 1] != '\0') { 221 gctl_error(req, "unterminated param name"); 222 g_free(p); 223 break; 224 } 225 ap[i].name = p; 226 ap[i].flag |= GCTL_PARAM_NAMEKERNEL; 227 if (ap[i].len <= 0) { 228 gctl_error(req, "negative param length"); 229 break; 230 } 231 if (ap[i].flag & GCTL_PARAM_RD) { 232 p = geom_alloc_copyin(req, ap[i].value, ap[i].len); 233 if (p == NULL) 234 break; 235 if ((ap[i].flag & GCTL_PARAM_ASCII) && 236 p[ap[i].len - 1] != '\0') { 237 gctl_error(req, "unterminated param value"); 238 g_free(p); 239 break; 240 } 241 } else { 242 p = g_malloc(ap[i].len, M_WAITOK | M_ZERO); 243 } 244 ap[i].kvalue = p; 245 ap[i].flag |= GCTL_PARAM_VALUEKERNEL; 246 } 247 req->arg = ap; 248 return; 249 } 250 251 static void 252 gctl_copyout(struct gctl_req *req) 253 { 254 int error, i; 255 struct gctl_req_arg *ap; 256 257 if (req->nerror) 258 return; 259 error = 0; 260 ap = req->arg; 261 for (i = 0; i < req->narg; i++, ap++) { 262 if (!(ap->flag & GCTL_PARAM_CHANGED)) 263 continue; 264 error = copyout(ap->kvalue, ap->value, ap->len); 265 if (!error) 266 continue; 267 req->nerror = error; 268 return; 269 } 270 return; 271 } 272 273 static void 274 gctl_free(struct gctl_req *req) 275 { 276 u_int i; 277 278 sbuf_delete(req->serror); 279 if (req->arg == NULL) 280 return; 281 for (i = 0; i < req->narg; i++) { 282 if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL) 283 g_free(req->arg[i].name); 284 if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) && 285 req->arg[i].len > 0) 286 g_free(req->arg[i].kvalue); 287 } 288 g_free(req->arg); 289 } 290 291 static void 292 gctl_dump(struct gctl_req *req, const char *what) 293 { 294 struct gctl_req_arg *ap; 295 u_int i; 296 int j; 297 298 printf("Dump of gctl %s at %p:\n", what, req); 299 if (req->nerror > 0) { 300 printf(" nerror:\t%d\n", req->nerror); 301 if (sbuf_len(req->serror) > 0) 302 printf(" error:\t\"%s\"\n", sbuf_data(req->serror)); 303 } 304 if (req->arg == NULL) 305 return; 306 for (i = 0; i < req->narg; i++) { 307 ap = &req->arg[i]; 308 if (!(ap->flag & GCTL_PARAM_NAMEKERNEL)) 309 printf(" param:\t%d@%p", ap->nlen, ap->name); 310 else 311 printf(" param:\t\"%s\"", ap->name); 312 printf(" [%s%s%d] = ", 313 ap->flag & GCTL_PARAM_RD ? "R" : "", 314 ap->flag & GCTL_PARAM_WR ? "W" : "", 315 ap->len); 316 if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) { 317 printf(" =@ %p", ap->value); 318 } else if (ap->flag & GCTL_PARAM_ASCII) { 319 printf("\"%s\"", (char *)ap->kvalue); 320 } else if (ap->len > 0) { 321 for (j = 0; j < ap->len && j < 512; j++) 322 printf(" %02x", ((u_char *)ap->kvalue)[j]); 323 } else { 324 printf(" = %p", ap->kvalue); 325 } 326 printf("\n"); 327 } 328 } 329 330 int 331 gctl_set_param(struct gctl_req *req, const char *param, void const *ptr, 332 int len) 333 { 334 u_int i; 335 struct gctl_req_arg *ap; 336 337 for (i = 0; i < req->narg; i++) { 338 ap = &req->arg[i]; 339 if (strcmp(param, ap->name)) 340 continue; 341 if (!(ap->flag & GCTL_PARAM_WR)) 342 return (EPERM); 343 ap->flag |= GCTL_PARAM_CHANGED; 344 if (ap->len < len) { 345 bcopy(ptr, ap->kvalue, ap->len); 346 return (ENOSPC); 347 } 348 bcopy(ptr, ap->kvalue, len); 349 return (0); 350 } 351 return (EINVAL); 352 } 353 354 void 355 gctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr, 356 int len) 357 { 358 359 switch (gctl_set_param(req, param, ptr, len)) { 360 case EPERM: 361 gctl_error(req, "No write access %s argument", param); 362 break; 363 case ENOSPC: 364 gctl_error(req, "Wrong length %s argument", param); 365 break; 366 case EINVAL: 367 gctl_error(req, "Missing %s argument", param); 368 break; 369 } 370 } 371 372 void * 373 gctl_get_param_flags(struct gctl_req *req, const char *param, int flags, int *len) 374 { 375 u_int i; 376 void *p; 377 struct gctl_req_arg *ap; 378 379 for (i = 0; i < req->narg; i++) { 380 ap = &req->arg[i]; 381 if (strcmp(param, ap->name)) 382 continue; 383 if ((ap->flag & flags) != flags) 384 continue; 385 p = ap->kvalue; 386 if (len != NULL) 387 *len = ap->len; 388 return (p); 389 } 390 return (NULL); 391 } 392 393 void * 394 gctl_get_param(struct gctl_req *req, const char *param, int *len) 395 { 396 397 return (gctl_get_param_flags(req, param, GCTL_PARAM_RD, len)); 398 } 399 400 char const * 401 gctl_get_asciiparam(struct gctl_req *req, const char *param) 402 { 403 char const *p; 404 int len; 405 406 p = gctl_get_param_flags(req, param, GCTL_PARAM_RD, &len); 407 if (p == NULL) 408 return (NULL); 409 if (len < 1) { 410 gctl_error(req, "Argument without length (%s)", param); 411 return (NULL); 412 } 413 if (p[len - 1] != '\0') { 414 gctl_error(req, "Unterminated argument (%s)", param); 415 return (NULL); 416 } 417 return (p); 418 } 419 420 void * 421 gctl_get_paraml_opt(struct gctl_req *req, const char *param, int len) 422 { 423 int i; 424 void *p; 425 426 p = gctl_get_param(req, param, &i); 427 if (i != len) { 428 p = NULL; 429 gctl_error(req, "Wrong length %s argument", param); 430 } 431 return (p); 432 } 433 434 void * 435 gctl_get_paraml(struct gctl_req *req, const char *param, int len) 436 { 437 void *p; 438 439 p = gctl_get_paraml_opt(req, param, len); 440 if (p == NULL) 441 gctl_error(req, "Missing %s argument", param); 442 return (p); 443 } 444 445 struct g_class * 446 gctl_get_class(struct gctl_req *req, char const *arg) 447 { 448 char const *p; 449 struct g_class *cp; 450 451 p = gctl_get_asciiparam(req, arg); 452 if (p == NULL) { 453 gctl_error(req, "Missing %s argument", arg); 454 return (NULL); 455 } 456 LIST_FOREACH(cp, &g_classes, class) { 457 if (!strcmp(p, cp->name)) 458 return (cp); 459 } 460 gctl_error(req, "Class not found: \"%s\"", p); 461 return (NULL); 462 } 463 464 struct g_geom * 465 gctl_get_geom(struct gctl_req *req, struct g_class *mp, char const *arg) 466 { 467 char const *p; 468 struct g_geom *gp; 469 470 MPASS(mp != NULL); 471 p = gctl_get_asciiparam(req, arg); 472 if (p == NULL) { 473 gctl_error(req, "Missing %s argument", arg); 474 return (NULL); 475 } 476 LIST_FOREACH(gp, &mp->geom, geom) 477 if (!strcmp(p, gp->name)) 478 return (gp); 479 gctl_error(req, "Geom not found: \"%s\"", p); 480 return (NULL); 481 } 482 483 struct g_provider * 484 gctl_get_provider(struct gctl_req *req, char const *arg) 485 { 486 char const *p; 487 struct g_provider *pp; 488 489 p = gctl_get_asciiparam(req, arg); 490 if (p == NULL) { 491 gctl_error(req, "Missing '%s' argument", arg); 492 return (NULL); 493 } 494 pp = g_provider_by_name(p); 495 if (pp != NULL) 496 return (pp); 497 gctl_error(req, "Provider not found: \"%s\"", p); 498 return (NULL); 499 } 500 501 static void 502 g_ctl_getxml(struct gctl_req *req, struct g_class *mp) 503 { 504 const char *name; 505 char *buf; 506 struct sbuf *sb; 507 int len, i = 0, n = 0, *parents; 508 struct g_geom *gp, **gps; 509 struct g_consumer *cp; 510 511 parents = gctl_get_paraml(req, "parents", sizeof(*parents)); 512 if (parents == NULL) 513 return; 514 name = gctl_get_asciiparam(req, "arg0"); 515 n = 0; 516 LIST_FOREACH(gp, &mp->geom, geom) { 517 if (name && strcmp(gp->name, name) != 0) 518 continue; 519 n++; 520 if (*parents) { 521 LIST_FOREACH(cp, &gp->consumer, consumer) 522 n++; 523 } 524 } 525 gps = g_malloc((n + 1) * sizeof(*gps), M_WAITOK); 526 i = 0; 527 LIST_FOREACH(gp, &mp->geom, geom) { 528 if (name && strcmp(gp->name, name) != 0) 529 continue; 530 gps[i++] = gp; 531 if (*parents) { 532 LIST_FOREACH(cp, &gp->consumer, consumer) { 533 if (cp->provider != NULL) 534 gps[i++] = cp->provider->geom; 535 } 536 } 537 } 538 KASSERT(i == n, ("different number of geoms found (%d != %d)", 539 i, n)); 540 gps[i] = 0; 541 542 buf = gctl_get_param_flags(req, "output", GCTL_PARAM_WR, &len); 543 if (buf == NULL) { 544 gctl_error(req, "output parameter missing"); 545 g_free(gps); 546 return; 547 } 548 sb = sbuf_new(NULL, buf, len, SBUF_FIXEDLEN | SBUF_INCLUDENUL); 549 g_conf_specific(sb, gps); 550 gctl_set_param(req, "output", buf, 0); 551 if (sbuf_error(sb)) 552 gctl_error(req, "output buffer overflow"); 553 sbuf_delete(sb); 554 g_free(gps); 555 } 556 557 static void 558 g_ctl_req(void *arg, int flag __unused) 559 { 560 struct g_class *mp; 561 struct gctl_req *req; 562 char const *verb; 563 564 g_topology_assert(); 565 req = arg; 566 mp = gctl_get_class(req, "class"); 567 if (mp == NULL) 568 return; 569 verb = gctl_get_param(req, "verb", NULL); 570 if (verb == NULL) { 571 gctl_error(req, "Verb missing"); 572 return; 573 } 574 if (strcmp(verb, "getxml") == 0) { 575 g_ctl_getxml(req, mp); 576 } else if (mp->ctlreq == NULL) { 577 gctl_error(req, "Class takes no requests"); 578 } else { 579 mp->ctlreq(req, mp, verb); 580 } 581 g_topology_assert(); 582 } 583 584 static int 585 g_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 586 { 587 struct gctl_req *req; 588 int nerror; 589 590 req = (void *)data; 591 req->nerror = 0; 592 /* It is an error if we cannot return an error text */ 593 if (req->lerror < 2) 594 return (EINVAL); 595 if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 596 return (EINVAL); 597 598 req->serror = sbuf_new_auto(); 599 /* Check the version */ 600 if (req->version != GCTL_VERSION) { 601 gctl_error(req, "kernel and libgeom version mismatch."); 602 req->arg = NULL; 603 } else { 604 /* Get things on board */ 605 gctl_copyin(req); 606 607 if (g_debugflags & G_F_CTLDUMP) 608 gctl_dump(req, "request"); 609 610 if (!req->nerror) { 611 g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL); 612 613 if (g_debugflags & G_F_CTLDUMP) 614 gctl_dump(req, "result"); 615 616 gctl_copyout(req); 617 } 618 } 619 if (sbuf_done(req->serror)) { 620 copyout(sbuf_data(req->serror), req->error, 621 imin(req->lerror, sbuf_len(req->serror) + 1)); 622 } 623 624 nerror = req->nerror; 625 gctl_free(req); 626 return (nerror); 627 } 628 629 static int 630 g_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 631 { 632 int error; 633 634 switch(cmd) { 635 case GEOM_CTL: 636 error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 637 break; 638 default: 639 error = ENOIOCTL; 640 break; 641 } 642 return (error); 643 644 } 645