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