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