geom_ctl.c revision 113892
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 113892 2003-04-23 07:50:01Z 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_dump(struct gctl_req *req) 200{ 201 u_int i; 202 int j, error; 203 struct gctl_req_arg *ap; 204 void *p; 205 206 207 printf("Dump of gctl %s request at %p:\n", req->reqt->name, req); 208 if (req->lerror > 0) { 209 p = geom_alloc_copyin(req, req->error, req->lerror, &error); 210 if (p != NULL) { 211 ((char *)p)[req->lerror - 1] = '\0'; 212 printf(" error:\t\"%s\"\n", (char *)p); 213 g_free(p); 214 } 215 } 216 for (i = 0; i < req->narg; i++) { 217 ap = &req->arg[i]; 218 printf(" param:\t\"%s\"", ap->name); 219 printf(" [%s%s%d] = ", 220 ap->flag & GCTL_PARAM_RD ? "R" : "", 221 ap->flag & GCTL_PARAM_WR ? "W" : "", 222 ap->len); 223 if (ap->flag & GCTL_PARAM_ASCII) { 224 p = geom_alloc_copyin(req, ap->value, ap->len, &error); 225 if (p != NULL) { 226 ((char *)p)[ap->len - 1] = '\0'; 227 printf("\"%s\"", (char *)p); 228 } 229 g_free(p); 230 } else if (ap->len > 0) { 231 p = geom_alloc_copyin(req, ap->value, ap->len, &error); 232 for (j = 0; j < ap->len; j++) 233 printf(" %02x", ((u_char *)p)[j]); 234 g_free(p); 235 } else { 236 printf(" = %p", ap->value); 237 } 238 printf("\n"); 239 } 240} 241 242void * 243gctl_get_param(struct gctl_req *req, const char *param, int *len) 244{ 245 int i, error, j; 246 void *p; 247 struct gctl_req_arg *ap; 248 249 for (i = 0; i < req->narg; i++) { 250 ap = &req->arg[i]; 251 if (strcmp(param, ap->name)) 252 continue; 253 if (!(ap->flag & GCTL_PARAM_RD)) 254 continue; 255 if (ap->len > 0) 256 j = ap->len; 257 else 258 j = 0; 259 if (j != 0) 260 p = geom_alloc_copyin(req, ap->value, j, &error); 261 /* XXX: should not fail, tested prviously */ 262 else 263 p = ap->value; 264 if (len != NULL) 265 *len = j; 266 return (p); 267 } 268 return (NULL); 269} 270 271static struct g_class* 272gctl_get_class(struct gctl_req *req) 273{ 274 char *p; 275 int len; 276 struct g_class *cp; 277 278 p = gctl_get_param(req, "class", &len); 279 if (p == NULL) 280 return (NULL); 281 if (p[len - 1] != '\0') { 282 gctl_error(req, "Unterminated class name"); 283 g_free(p); 284 return (NULL); 285 } 286 LIST_FOREACH(cp, &g_classes, class) { 287 if (!strcmp(p, cp->name)) { 288 g_free(p); 289 return (cp); 290 } 291 } 292 gctl_error(req, "Class not found"); 293 return (NULL); 294} 295 296static struct g_geom* 297gctl_get_geom(struct gctl_req *req, struct g_class *mpr) 298{ 299 char *p; 300 int len; 301 struct g_class *mp; 302 struct g_geom *gp; 303 304 p = gctl_get_param(req, "geom", &len); 305 if (p == NULL) 306 return (NULL); 307 if (p[len - 1] != '\0') { 308 gctl_error(req, "Unterminated provider name"); 309 g_free(p); 310 return (NULL); 311 } 312 LIST_FOREACH(mp, &g_classes, class) { 313 if (mpr != NULL && mpr != mp) 314 continue; 315 LIST_FOREACH(gp, &mp->geom, geom) { 316 if (!strcmp(p, gp->name)) { 317 g_free(p); 318 return (gp); 319 } 320 } 321 } 322 gctl_error(req, "Geom not found"); 323 return (NULL); 324} 325 326static struct g_provider* 327gctl_get_provider(struct gctl_req *req) 328{ 329 char *p; 330 int len; 331 struct g_class *cp; 332 struct g_geom *gp; 333 struct g_provider *pp; 334 335 p = gctl_get_param(req, "provider", &len); 336 if (p == NULL) 337 return (NULL); 338 if (p[len - 1] != '\0') { 339 gctl_error(req, "Unterminated provider name"); 340 g_free(p); 341 return (NULL); 342 } 343 LIST_FOREACH(cp, &g_classes, class) { 344 LIST_FOREACH(gp, &cp->geom, geom) { 345 LIST_FOREACH(pp, &gp->provider, provider) { 346 if (!strcmp(p, pp->name)) { 347 g_free(p); 348 return (pp); 349 } 350 } 351 } 352 } 353 gctl_error(req, "Provider not found"); 354 return (NULL); 355} 356 357static int 358gctl_create_geom(struct gctl_req *req) 359{ 360 struct g_class *mp; 361 struct g_provider *pp; 362 int error; 363 364 g_topology_assert(); 365 mp = gctl_get_class(req); 366 if (mp == NULL) 367 return (gctl_error(req, "Class not found")); 368 if (mp->create_geom == NULL) 369 return (gctl_error(req, "Class has no create_geom method")); 370 pp = gctl_get_provider(req); 371 error = mp->create_geom(req, mp, pp); 372 g_topology_assert(); 373 return (error); 374} 375 376static int 377gctl_destroy_geom(struct gctl_req *req) 378{ 379 struct g_class *mp; 380 struct g_geom *gp; 381 int error; 382 383 g_topology_assert(); 384 mp = gctl_get_class(req); 385 if (mp == NULL) 386 return (gctl_error(req, "Class not found")); 387 if (mp->destroy_geom == NULL) 388 return (gctl_error(req, "Class has no destroy_geom method")); 389 gp = gctl_get_geom(req, mp); 390 if (gp == NULL) 391 return (gctl_error(req, "Geom not specified")); 392 if (gp->class != mp) 393 return (gctl_error(req, "Geom not of specificed class")); 394 error = mp->destroy_geom(req, mp, gp); 395 g_topology_assert(); 396 return (error); 397} 398 399static int 400gctl_config_geom(struct gctl_req *req) 401{ 402 struct g_class *mp; 403 struct g_geom *gp; 404 char *verb; 405 int error, vlen; 406 407 g_topology_assert(); 408 mp = gctl_get_class(req); 409 if (mp == NULL) 410 return (gctl_error(req, "Class not found")); 411 if (mp->config_geom == NULL) 412 return (gctl_error(req, "Class has no config_geom method")); 413 gp = gctl_get_geom(req, mp); 414 if (gp == NULL) 415 return (gctl_error(req, "Geom not specified")); 416 if (gp->class != mp) 417 return (gctl_error(req, "Geom not of specificed class")); 418 verb = gctl_get_param(req, "verb", &vlen); 419 if (verb == NULL) 420 return (gctl_error(req, "Missing verb parameter")); 421 if (vlen < 2) { 422 g_free(verb); 423 return (gctl_error(req, "Too short verb parameter")); 424 } 425 if (verb[vlen - 1] != '\0') { 426 g_free(verb); 427 return (gctl_error(req, "Unterminated verb parameter")); 428 } 429 error = mp->config_geom(req, gp, verb); 430 g_free(verb); 431 g_topology_assert(); 432 return (error); 433} 434 435/* 436 * Handle ioctl from libgeom::geom_ctl.c 437 */ 438static int 439g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 440{ 441 int error; 442 int i; 443 struct gctl_req *req; 444 445 req = (void *)data; 446 /* It is an error if we cannot return an error text */ 447 if (req->lerror < 1) 448 return (EINVAL); 449 if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 450 return (EINVAL); 451 452 /* Check the version */ 453 if (req->version != GCTL_VERSION) 454 return (gctl_error(req, 455 "kernel and libgeom version mismatch.")); 456 457 /* Check the request type */ 458 for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++) 459 if (gcrt[i].request == req->request) 460 break; 461 if (gcrt[i].request == GCTL_INVALID_REQUEST) 462 return (gctl_error(req, "invalid request")); 463 req->reqt = &gcrt[i]; 464 465 /* Get things on board */ 466 error = gctl_copyin(req); 467 if (error) 468 return (error); 469 470 if (g_debugflags & G_F_CTLDUMP) 471 gctl_dump(req); 472 g_topology_lock(); 473 switch (req->request) { 474 case GCTL_CREATE_GEOM: 475 error = gctl_create_geom(req); 476 break; 477 case GCTL_DESTROY_GEOM: 478 error = gctl_destroy_geom(req); 479 break; 480 case GCTL_CONFIG_GEOM: 481 error = gctl_config_geom(req); 482 break; 483 default: 484 error = gctl_error(req, "XXX: TBD"); 485 break; 486 } 487 g_topology_unlock(); 488 return (error); 489} 490 491static int 492g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 493{ 494 int error; 495 496 switch(cmd) { 497 case GEOM_CTL: 498 DROP_GIANT(); 499 error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 500 PICKUP_GIANT(); 501 break; 502 default: 503 error = ENOTTY; 504 break; 505 } 506 return (error); 507 508} 509