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