geom_ctl.c revision 114670
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 * $FreeBSD: head/sys/geom/geom_ctl.c 114670 2003-05-04 19:24:34Z phk $ 36 */ 37 38#include "opt_geom.h" 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/kernel.h> 43#include <sys/sysctl.h> 44#include <sys/bio.h> 45#include <sys/conf.h> 46#include <sys/disk.h> 47#include <sys/malloc.h> 48#include <sys/sysctl.h> 49#include <sys/sbuf.h> 50 51#include <sys/lock.h> 52#include <sys/mutex.h> 53 54#include <vm/vm.h> 55#include <vm/vm_extern.h> 56 57#include <geom/geom.h> 58#include <geom/geom_int.h> 59#define GCTL_TABLE 1 60#include <geom/geom_ctl.h> 61#include <geom/geom_ext.h> 62 63#include <machine/stdarg.h> 64 65static d_ioctl_t g_ctl_ioctl; 66 67static struct cdevsw g_ctl_cdevsw = { 68 .d_open = nullopen, 69 .d_close = nullclose, 70 .d_ioctl = g_ctl_ioctl, 71 .d_name = "g_ctl", 72}; 73 74void 75g_ctl_init(void) 76{ 77 78 make_dev(&g_ctl_cdevsw, 0, 79 UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL); 80 KASSERT(GCTL_PARAM_RD == VM_PROT_READ, 81 ("GCTL_PARAM_RD != VM_PROT_READ")); 82 KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE, 83 ("GCTL_PARAM_WR != VM_PROT_WRITE")); 84} 85 86/* 87 * Report an error back to the user in ascii format. Return whatever copyout 88 * returned, or EINVAL if it succeeded. 89 * XXX: should not be static. 90 * XXX: should take printf like args. 91 */ 92int 93gctl_error(struct gctl_req *req, const char *fmt, ...) 94{ 95 int error; 96 va_list ap; 97 struct sbuf *sb; 98 99 sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 100 if (sb == NULL) { 101 error = copyout(fmt, req->error, 102 imin(req->lerror, strlen(fmt) + 1)); 103 } else { 104 va_start(ap, fmt); 105 sbuf_vprintf(sb, fmt, ap); 106 sbuf_finish(sb); 107 if (g_debugflags & G_F_CTLDUMP) 108 printf("gctl %p error \"%s\"\n", req, sbuf_data(sb)); 109 error = copyout(sbuf_data(sb), req->error, 110 imin(req->lerror, sbuf_len(sb) + 1)); 111 } 112 if (!error) 113 error = EINVAL; 114 sbuf_delete(sb); 115 return (error); 116} 117 118/* 119 * Allocate space and copyin() something. 120 * XXX: this should really be a standard function in the kernel. 121 */ 122static void * 123geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len, int *errp) 124{ 125 int error; 126 void *ptr; 127 128 ptr = g_malloc(len, M_WAITOK); 129 if (ptr == NULL) 130 error = ENOMEM; 131 else 132 error = copyin(uaddr, ptr, len); 133 if (!error) 134 return (ptr); 135 gctl_error(req, "no access to argument"); 136 *errp = error; 137 if (ptr != NULL) 138 g_free(ptr); 139 return (NULL); 140} 141 142 143/* 144 * XXX: This function is a nightmare. It walks through the request and 145 * XXX: makes sure that the various bits and pieces are there and copies 146 * XXX: some of them into kernel memory to make things easier. 147 * XXX: I really wish we had a standard marshalling layer somewhere. 148 */ 149 150static int 151gctl_copyin(struct gctl_req *req) 152{ 153 int error, i, j; 154 struct gctl_req_arg *ap; 155 char *p; 156 157 error = 0; 158 ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap), &error); 159 if (ap == NULL) { 160 gctl_error(req, "copyin() of arguments failed"); 161 return (error); 162 } 163 164 for (i = 0; !error && i < req->narg; i++) { 165 if (ap[i].len > 0 && 166 !useracc(ap[i].value, ap[i].len, 167 ap[i].flag & GCTL_PARAM_RW)) 168 error = gctl_error(req, "no access to param data"); 169 if (error) 170 break; 171 p = NULL; 172 if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) { 173 error = gctl_error(req, "wrong param name length"); 174 break; 175 } 176 p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen, &error); 177 if (p == NULL) 178 break; 179 if (p[ap[i].nlen - 1] != '\0') { 180 error = gctl_error(req, "unterminated param name"); 181 g_free(p); 182 break; 183 } 184 ap[i].name = p; 185 ap[i].nlen = 0; 186 } 187 if (!error) { 188 req->arg = ap; 189 return (0); 190 } 191 for (j = 0; j < i; j++) 192 if (ap[j].nlen == 0 && ap[j].name != NULL) 193 g_free(ap[j].name); 194 g_free(ap); 195 return (error); 196} 197 198static void 199gctl_free(struct gctl_req *req) 200{ 201 int i; 202 203 for (i = 0; i < req->narg; i++) { 204 if (req->arg[i].nlen == 0 && req->arg[i].name != NULL) 205 g_free(req->arg[i].name); 206 } 207 g_free(req->arg); 208} 209 210static void 211gctl_dump(struct gctl_req *req) 212{ 213 u_int i; 214 int j, error; 215 struct gctl_req_arg *ap; 216 void *p; 217 218 219 printf("Dump of gctl %s request at %p:\n", req->reqt->name, req); 220 if (req->lerror > 0) { 221 p = geom_alloc_copyin(req, req->error, req->lerror, &error); 222 if (p != NULL) { 223 ((char *)p)[req->lerror - 1] = '\0'; 224 printf(" error:\t\"%s\"\n", (char *)p); 225 g_free(p); 226 } 227 } 228 for (i = 0; i < req->narg; i++) { 229 ap = &req->arg[i]; 230 printf(" param:\t\"%s\"", ap->name); 231 printf(" [%s%s%d] = ", 232 ap->flag & GCTL_PARAM_RD ? "R" : "", 233 ap->flag & GCTL_PARAM_WR ? "W" : "", 234 ap->len); 235 if (ap->flag & GCTL_PARAM_ASCII) { 236 p = geom_alloc_copyin(req, ap->value, ap->len, &error); 237 if (p != NULL) { 238 ((char *)p)[ap->len - 1] = '\0'; 239 printf("\"%s\"", (char *)p); 240 } 241 g_free(p); 242 } else if (ap->len > 0) { 243 p = geom_alloc_copyin(req, ap->value, ap->len, &error); 244 for (j = 0; j < ap->len; j++) 245 printf(" %02x", ((u_char *)p)[j]); 246 g_free(p); 247 } else { 248 printf(" = %p", ap->value); 249 } 250 printf("\n"); 251 } 252} 253 254int 255gctl_set_param(struct gctl_req *req, const char *param, void *ptr, int len) 256{ 257 int i, error; 258 struct gctl_req_arg *ap; 259 260 for (i = 0; i < req->narg; i++) { 261 ap = &req->arg[i]; 262 if (strcmp(param, ap->name)) 263 continue; 264 if (!(ap->flag & GCTL_PARAM_WR)) { 265 gctl_error(req, "No write access %s argument", param); 266 return (EINVAL); 267 } 268 if (ap->len != len) { 269 gctl_error(req, "Wrong length %s argument", param); 270 return (EINVAL); 271 } 272 error = copyout(ptr, ap->value, len); 273 return (error); 274 } 275 gctl_error(req, "Missing %s argument", param); 276 return (EINVAL); 277} 278 279void * 280gctl_get_param(struct gctl_req *req, const char *param, int *len) 281{ 282 int i, error, j; 283 void *p; 284 struct gctl_req_arg *ap; 285 286 for (i = 0; i < req->narg; i++) { 287 ap = &req->arg[i]; 288 if (strcmp(param, ap->name)) 289 continue; 290 if (!(ap->flag & GCTL_PARAM_RD)) 291 continue; 292 if (ap->len > 0) 293 j = ap->len; 294 else 295 j = 0; 296 if (j != 0) 297 p = geom_alloc_copyin(req, ap->value, j, &error); 298 /* XXX: should not fail, tested prviously */ 299 else 300 p = ap->value; 301 if (len != NULL) 302 *len = j; 303 return (p); 304 } 305 return (NULL); 306} 307 308void * 309gctl_get_paraml(struct gctl_req *req, const char *param, int len) 310{ 311 int i; 312 void *p; 313 314 p = gctl_get_param(req, param, &i); 315 if (p == NULL) 316 gctl_error(req, "Missing %s argument", param); 317 else if (i != len) { 318 g_free(p); 319 p = NULL; 320 gctl_error(req, "Wrong length %s argument", param); 321 } 322 return (p); 323} 324 325static struct g_class* 326gctl_get_class(struct gctl_req *req) 327{ 328 char *p; 329 int len; 330 struct g_class *cp; 331 332 p = gctl_get_param(req, "class", &len); 333 if (p == NULL) 334 return (NULL); 335 if (p[len - 1] != '\0') { 336 gctl_error(req, "Unterminated class name"); 337 g_free(p); 338 return (NULL); 339 } 340 LIST_FOREACH(cp, &g_classes, class) { 341 if (!strcmp(p, cp->name)) { 342 g_free(p); 343 return (cp); 344 } 345 } 346 g_free(p); 347 gctl_error(req, "Class not found"); 348 return (NULL); 349} 350 351static struct g_geom* 352gctl_get_geom(struct gctl_req *req, struct g_class *mpr) 353{ 354 char *p; 355 int len; 356 struct g_class *mp; 357 struct g_geom *gp; 358 359 p = gctl_get_param(req, "geom", &len); 360 if (p == NULL) 361 return (NULL); 362 if (p[len - 1] != '\0') { 363 gctl_error(req, "Unterminated provider name"); 364 g_free(p); 365 return (NULL); 366 } 367 LIST_FOREACH(mp, &g_classes, class) { 368 if (mpr != NULL && mpr != mp) 369 continue; 370 LIST_FOREACH(gp, &mp->geom, geom) { 371 if (!strcmp(p, gp->name)) { 372 g_free(p); 373 return (gp); 374 } 375 } 376 } 377 gctl_error(req, "Geom not found"); 378 g_free(p); 379 return (NULL); 380} 381 382static struct g_provider* 383gctl_get_provider(struct gctl_req *req) 384{ 385 char *p; 386 int len; 387 struct g_class *cp; 388 struct g_geom *gp; 389 struct g_provider *pp; 390 391 p = gctl_get_param(req, "provider", &len); 392 if (p == NULL) 393 return (NULL); 394 if (p[len - 1] != '\0') { 395 gctl_error(req, "Unterminated provider name"); 396 g_free(p); 397 return (NULL); 398 } 399 LIST_FOREACH(cp, &g_classes, class) { 400 LIST_FOREACH(gp, &cp->geom, geom) { 401 LIST_FOREACH(pp, &gp->provider, provider) { 402 if (!strcmp(p, pp->name)) { 403 g_free(p); 404 return (pp); 405 } 406 } 407 } 408 } 409 gctl_error(req, "Provider not found"); 410 g_free(p); 411 return (NULL); 412} 413 414static int 415gctl_create_geom(struct gctl_req *req) 416{ 417 struct g_class *mp; 418 struct g_provider *pp; 419 int error; 420 421 g_topology_assert(); 422 mp = gctl_get_class(req); 423 if (mp == NULL) 424 return (gctl_error(req, "Class not found")); 425 if (mp->create_geom == NULL) 426 return (gctl_error(req, "Class has no create_geom method")); 427 pp = gctl_get_provider(req); 428 error = mp->create_geom(req, mp, pp); 429 g_topology_assert(); 430 return (error); 431} 432 433static int 434gctl_destroy_geom(struct gctl_req *req) 435{ 436 struct g_class *mp; 437 struct g_geom *gp; 438 int error; 439 440 g_topology_assert(); 441 mp = gctl_get_class(req); 442 if (mp == NULL) 443 return (gctl_error(req, "Class not found")); 444 if (mp->destroy_geom == NULL) 445 return (gctl_error(req, "Class has no destroy_geom method")); 446 gp = gctl_get_geom(req, mp); 447 if (gp == NULL) 448 return (gctl_error(req, "Geom not specified")); 449 if (gp->class != mp) 450 return (gctl_error(req, "Geom not of specificed class")); 451 error = mp->destroy_geom(req, mp, gp); 452 g_topology_assert(); 453 return (error); 454} 455 456static int 457gctl_config_geom(struct gctl_req *req) 458{ 459 struct g_class *mp; 460 struct g_geom *gp; 461 char *verb; 462 int error, vlen; 463 464 g_topology_assert(); 465 mp = gctl_get_class(req); 466 if (mp == NULL) 467 return (gctl_error(req, "Class not found")); 468 if (mp->config_geom == NULL) 469 return (gctl_error(req, "Class has no config_geom method")); 470 gp = gctl_get_geom(req, mp); 471 if (gp == NULL) 472 return (gctl_error(req, "Geom not specified")); 473 if (gp->class != mp) 474 return (gctl_error(req, "Geom not of specificed class")); 475 verb = gctl_get_param(req, "verb", &vlen); 476 if (verb == NULL) 477 return (gctl_error(req, "Missing verb parameter")); 478 if (vlen < 2) { 479 g_free(verb); 480 return (gctl_error(req, "Too short verb parameter")); 481 } 482 if (verb[vlen - 1] != '\0') { 483 g_free(verb); 484 return (gctl_error(req, "Unterminated verb parameter")); 485 } 486 error = mp->config_geom(req, gp, verb); 487 g_free(verb); 488 g_topology_assert(); 489 return (error); 490} 491 492/* 493 * Handle ioctl from libgeom::geom_ctl.c 494 */ 495static int 496g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 497{ 498 int error; 499 int i; 500 struct gctl_req *req; 501 502 req = (void *)data; 503 /* It is an error if we cannot return an error text */ 504 if (req->lerror < 1) 505 return (EINVAL); 506 if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 507 return (EINVAL); 508 509 /* Check the version */ 510 if (req->version != GCTL_VERSION) 511 return (gctl_error(req, 512 "kernel and libgeom version mismatch.")); 513 514 /* Check the request type */ 515 for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++) 516 if (gcrt[i].request == req->request) 517 break; 518 if (gcrt[i].request == GCTL_INVALID_REQUEST) 519 return (gctl_error(req, "invalid request")); 520 req->reqt = &gcrt[i]; 521 522 /* Get things on board */ 523 error = gctl_copyin(req); 524 if (error) 525 return (error); 526 527 if (g_debugflags & G_F_CTLDUMP) 528 gctl_dump(req); 529 g_topology_lock(); 530 switch (req->request) { 531 case GCTL_CREATE_GEOM: 532 error = gctl_create_geom(req); 533 break; 534 case GCTL_DESTROY_GEOM: 535 error = gctl_destroy_geom(req); 536 break; 537 case GCTL_CONFIG_GEOM: 538 error = gctl_config_geom(req); 539 break; 540 default: 541 error = gctl_error(req, "XXX: TBD"); 542 break; 543 } 544 gctl_free(req); 545 g_topology_unlock(); 546 return (error); 547} 548 549static int 550g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 551{ 552 int error; 553 554 switch(cmd) { 555 case GEOM_CTL: 556 DROP_GIANT(); 557 error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 558 PICKUP_GIANT(); 559 break; 560 default: 561 error = ENOTTY; 562 break; 563 } 564 return (error); 565 566} 567