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$ 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 65 static d_ioctl_t g_ctl_ioctl; 66 67 static 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 74 void 75 g_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 */ 92 int 93 gctl_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 */ 122 static void * 123 geom_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 150 static int 151 gctl_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 198 static void 199 gctl_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 210 static void 211 gctl_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 254 void * 255 gctl_get_param(struct gctl_req *req, const char *param, int *len) 256 { 257 int i, error, j; 258 void *p; 259 struct gctl_req_arg *ap; 260 261 for (i = 0; i < req->narg; i++) { 262 ap = &req->arg[i]; 263 if (strcmp(param, ap->name)) 264 continue; 265 if (!(ap->flag & GCTL_PARAM_RD)) 266 continue; 267 if (ap->len > 0) 268 j = ap->len; 269 else 270 j = 0; 271 if (j != 0) 272 p = geom_alloc_copyin(req, ap->value, j, &error); 273 /* XXX: should not fail, tested prviously */ 274 else 275 p = ap->value; 276 if (len != NULL) 277 *len = j; 278 return (p); 279 } 280 return (NULL); 281 } 282 283 void * 284 gctl_get_paraml(struct gctl_req *req, const char *param, int len) 285 { 286 int i; 287 void *p; 288 289 p = gctl_get_param(req, param, &i); 290 if (p == NULL) 291 gctl_error(req, "Missing %s argument", param); 292 else if (i != len) { 293 g_free(p); 294 p = NULL; 295 gctl_error(req, "Wrong length %s argument", param); 296 } 297 return (p); 298 } 299 300 static struct g_class* 301 gctl_get_class(struct gctl_req *req) 302 { 303 char *p; 304 int len; 305 struct g_class *cp; 306 307 p = gctl_get_param(req, "class", &len); 308 if (p == NULL) 309 return (NULL); 310 if (p[len - 1] != '\0') { 311 gctl_error(req, "Unterminated class name"); 312 g_free(p); 313 return (NULL); 314 } 315 LIST_FOREACH(cp, &g_classes, class) { 316 if (!strcmp(p, cp->name)) { 317 g_free(p); 318 return (cp); 319 } 320 } 321 g_free(p); 322 gctl_error(req, "Class not found"); 323 return (NULL); 324 } 325 326 static struct g_geom* 327 gctl_get_geom(struct gctl_req *req, struct g_class *mpr) 328 { 329 char *p; 330 int len; 331 struct g_class *mp; 332 struct g_geom *gp; 333 334 p = gctl_get_param(req, "geom", &len); 335 if (p == NULL) 336 return (NULL); 337 if (p[len - 1] != '\0') { 338 gctl_error(req, "Unterminated provider name"); 339 g_free(p); 340 return (NULL); 341 } 342 LIST_FOREACH(mp, &g_classes, class) { 343 if (mpr != NULL && mpr != mp) 344 continue; 345 LIST_FOREACH(gp, &mp->geom, geom) { 346 if (!strcmp(p, gp->name)) { 347 g_free(p); 348 return (gp); 349 } 350 } 351 } 352 gctl_error(req, "Geom not found"); 353 g_free(p); 354 return (NULL); 355 } 356 357 static struct g_provider* 358 gctl_get_provider(struct gctl_req *req) 359 { 360 char *p; 361 int len; 362 struct g_class *cp; 363 struct g_geom *gp; 364 struct g_provider *pp; 365 366 p = gctl_get_param(req, "provider", &len); 367 if (p == NULL) 368 return (NULL); 369 if (p[len - 1] != '\0') { 370 gctl_error(req, "Unterminated provider name"); 371 g_free(p); 372 return (NULL); 373 } 374 LIST_FOREACH(cp, &g_classes, class) { 375 LIST_FOREACH(gp, &cp->geom, geom) { 376 LIST_FOREACH(pp, &gp->provider, provider) { 377 if (!strcmp(p, pp->name)) { 378 g_free(p); 379 return (pp); 380 } 381 } 382 } 383 } 384 gctl_error(req, "Provider not found"); 385 g_free(p); 386 return (NULL); 387 } 388 389 static int 390 gctl_create_geom(struct gctl_req *req) 391 { 392 struct g_class *mp; 393 struct g_provider *pp; 394 int error; 395 396 g_topology_assert(); 397 mp = gctl_get_class(req); 398 if (mp == NULL) 399 return (gctl_error(req, "Class not found")); 400 if (mp->create_geom == NULL) 401 return (gctl_error(req, "Class has no create_geom method")); 402 pp = gctl_get_provider(req); 403 error = mp->create_geom(req, mp, pp); 404 g_topology_assert(); 405 return (error); 406 } 407 408 static int 409 gctl_destroy_geom(struct gctl_req *req) 410 { 411 struct g_class *mp; 412 struct g_geom *gp; 413 int error; 414 415 g_topology_assert(); 416 mp = gctl_get_class(req); 417 if (mp == NULL) 418 return (gctl_error(req, "Class not found")); 419 if (mp->destroy_geom == NULL) 420 return (gctl_error(req, "Class has no destroy_geom method")); 421 gp = gctl_get_geom(req, mp); 422 if (gp == NULL) 423 return (gctl_error(req, "Geom not specified")); 424 if (gp->class != mp) 425 return (gctl_error(req, "Geom not of specificed class")); 426 error = mp->destroy_geom(req, mp, gp); 427 g_topology_assert(); 428 return (error); 429 } 430 431 static int 432 gctl_config_geom(struct gctl_req *req) 433 { 434 struct g_class *mp; 435 struct g_geom *gp; 436 char *verb; 437 int error, vlen; 438 439 g_topology_assert(); 440 mp = gctl_get_class(req); 441 if (mp == NULL) 442 return (gctl_error(req, "Class not found")); 443 if (mp->config_geom == NULL) 444 return (gctl_error(req, "Class has no config_geom method")); 445 gp = gctl_get_geom(req, mp); 446 if (gp == NULL) 447 return (gctl_error(req, "Geom not specified")); 448 if (gp->class != mp) 449 return (gctl_error(req, "Geom not of specificed class")); 450 verb = gctl_get_param(req, "verb", &vlen); 451 if (verb == NULL) 452 return (gctl_error(req, "Missing verb parameter")); 453 if (vlen < 2) { 454 g_free(verb); 455 return (gctl_error(req, "Too short verb parameter")); 456 } 457 if (verb[vlen - 1] != '\0') { 458 g_free(verb); 459 return (gctl_error(req, "Unterminated verb parameter")); 460 } 461 error = mp->config_geom(req, gp, verb); 462 g_free(verb); 463 g_topology_assert(); 464 return (error); 465 } 466 467 /* 468 * Handle ioctl from libgeom::geom_ctl.c 469 */ 470 static int 471 g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 472 { 473 int error; 474 int i; 475 struct gctl_req *req; 476 477 req = (void *)data; 478 /* It is an error if we cannot return an error text */ 479 if (req->lerror < 1) 480 return (EINVAL); 481 if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 482 return (EINVAL); 483 484 /* Check the version */ 485 if (req->version != GCTL_VERSION) 486 return (gctl_error(req, 487 "kernel and libgeom version mismatch.")); 488 489 /* Check the request type */ 490 for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++) 491 if (gcrt[i].request == req->request) 492 break; 493 if (gcrt[i].request == GCTL_INVALID_REQUEST) 494 return (gctl_error(req, "invalid request")); 495 req->reqt = &gcrt[i]; 496 497 /* Get things on board */ 498 error = gctl_copyin(req); 499 if (error) 500 return (error); 501 502 if (g_debugflags & G_F_CTLDUMP) 503 gctl_dump(req); 504 g_topology_lock(); 505 switch (req->request) { 506 case GCTL_CREATE_GEOM: 507 error = gctl_create_geom(req); 508 break; 509 case GCTL_DESTROY_GEOM: 510 error = gctl_destroy_geom(req); 511 break; 512 case GCTL_CONFIG_GEOM: 513 error = gctl_config_geom(req); 514 break; 515 default: 516 error = gctl_error(req, "XXX: TBD"); 517 break; 518 } 519 gctl_free(req); 520 g_topology_unlock(); 521 return (error); 522 } 523 524 static int 525 g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 526 { 527 int error; 528 529 switch(cmd) { 530 case GEOM_CTL: 531 DROP_GIANT(); 532 error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 533 PICKUP_GIANT(); 534 break; 535 default: 536 error = ENOTTY; 537 break; 538 } 539 return (error); 540 541 } 542