geom_ctl.c revision 113893
1156230Smux/*- 2156230Smux * Copyright (c) 2002 Poul-Henning Kamp 3156230Smux * Copyright (c) 2002 Networks Associates Technology, Inc. 4156230Smux * All rights reserved. 5156230Smux * 6156230Smux * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7156230Smux * and NAI Labs, the Security Research Division of Network Associates, Inc. 8156230Smux * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9156230Smux * DARPA CHATS research program. 10156230Smux * 11156230Smux * Redistribution and use in source and binary forms, with or without 12156230Smux * modification, are permitted provided that the following conditions 13156230Smux * are met: 14156230Smux * 1. Redistributions of source code must retain the above copyright 15156230Smux * notice, this list of conditions and the following disclaimer. 16156230Smux * 2. Redistributions in binary form must reproduce the above copyright 17156230Smux * notice, this list of conditions and the following disclaimer in the 18156230Smux * documentation and/or other materials provided with the distribution. 19156230Smux * 3. The names of the authors may not be used to endorse or promote 20156230Smux * products derived from this software without specific prior written 21156230Smux * permission. 22156230Smux * 23156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26156230Smux * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33156230Smux * SUCH DAMAGE. 34156230Smux * 35156230Smux * $FreeBSD: head/sys/geom/geom_ctl.c 113893 2003-04-23 08:03:47Z phk $ 36156230Smux */ 37156230Smux 38156230Smux#include "opt_geom.h" 39156230Smux 40156230Smux#include <sys/param.h> 41156230Smux#include <sys/systm.h> 42156230Smux#include <sys/kernel.h> 43156230Smux#include <sys/sysctl.h> 44156230Smux#include <sys/bio.h> 45156230Smux#include <sys/conf.h> 46156230Smux#include <sys/disk.h> 47156230Smux#include <sys/malloc.h> 48156230Smux#include <sys/sysctl.h> 49156230Smux#include <sys/sbuf.h> 50156230Smux 51156230Smux#include <sys/lock.h> 52156230Smux#include <sys/mutex.h> 53156230Smux 54156230Smux#include <vm/vm.h> 55156230Smux#include <vm/vm_extern.h> 56156230Smux 57156230Smux#include <geom/geom.h> 58156230Smux#include <geom/geom_int.h> 59156230Smux#define GCTL_TABLE 1 60156230Smux#include <geom/geom_ctl.h> 61156230Smux#include <geom/geom_ext.h> 62156230Smux 63156230Smux#include <machine/stdarg.h> 64156230Smux 65156230Smuxstatic d_ioctl_t g_ctl_ioctl; 66156230Smux 67156230Smuxstatic struct cdevsw g_ctl_cdevsw = { 68156230Smux .d_open = nullopen, 69156230Smux .d_close = nullclose, 70156230Smux .d_ioctl = g_ctl_ioctl, 71156230Smux .d_name = "g_ctl", 72156230Smux}; 73156230Smux 74156230Smuxvoid 75156230Smuxg_ctl_init(void) 76156230Smux{ 77156230Smux 78156230Smux make_dev(&g_ctl_cdevsw, 0, 79156230Smux UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL); 80156230Smux KASSERT(GCTL_PARAM_RD == VM_PROT_READ, 81156230Smux ("GCTL_PARAM_RD != VM_PROT_READ")); 82156230Smux KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE, 83156230Smux ("GCTL_PARAM_WR != VM_PROT_WRITE")); 84156230Smux} 85156230Smux 86156230Smux/* 87156230Smux * Report an error back to the user in ascii format. Return whatever copyout 88156230Smux * returned, or EINVAL if it succeeded. 89156230Smux * XXX: should not be static. 90156230Smux * XXX: should take printf like args. 91156230Smux */ 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 271void * 272gctl_get_paraml(struct gctl_req *req, const char *param, int len) 273{ 274 int i; 275 void *p; 276 277 p = gctl_get_param(req, param, &i); 278 if (p == NULL) 279 gctl_error(req, "Missing %s argument", param); 280 else if (i != len) { 281 g_free(p); 282 p = NULL; 283 gctl_error(req, "Wrong length %s argument", param); 284 } 285 return (p); 286} 287 288static struct g_class* 289gctl_get_class(struct gctl_req *req) 290{ 291 char *p; 292 int len; 293 struct g_class *cp; 294 295 p = gctl_get_param(req, "class", &len); 296 if (p == NULL) 297 return (NULL); 298 if (p[len - 1] != '\0') { 299 gctl_error(req, "Unterminated class name"); 300 g_free(p); 301 return (NULL); 302 } 303 LIST_FOREACH(cp, &g_classes, class) { 304 if (!strcmp(p, cp->name)) { 305 g_free(p); 306 return (cp); 307 } 308 } 309 gctl_error(req, "Class not found"); 310 return (NULL); 311} 312 313static struct g_geom* 314gctl_get_geom(struct gctl_req *req, struct g_class *mpr) 315{ 316 char *p; 317 int len; 318 struct g_class *mp; 319 struct g_geom *gp; 320 321 p = gctl_get_param(req, "geom", &len); 322 if (p == NULL) 323 return (NULL); 324 if (p[len - 1] != '\0') { 325 gctl_error(req, "Unterminated provider name"); 326 g_free(p); 327 return (NULL); 328 } 329 LIST_FOREACH(mp, &g_classes, class) { 330 if (mpr != NULL && mpr != mp) 331 continue; 332 LIST_FOREACH(gp, &mp->geom, geom) { 333 if (!strcmp(p, gp->name)) { 334 g_free(p); 335 return (gp); 336 } 337 } 338 } 339 gctl_error(req, "Geom not found"); 340 return (NULL); 341} 342 343static struct g_provider* 344gctl_get_provider(struct gctl_req *req) 345{ 346 char *p; 347 int len; 348 struct g_class *cp; 349 struct g_geom *gp; 350 struct g_provider *pp; 351 352 p = gctl_get_param(req, "provider", &len); 353 if (p == NULL) 354 return (NULL); 355 if (p[len - 1] != '\0') { 356 gctl_error(req, "Unterminated provider name"); 357 g_free(p); 358 return (NULL); 359 } 360 LIST_FOREACH(cp, &g_classes, class) { 361 LIST_FOREACH(gp, &cp->geom, geom) { 362 LIST_FOREACH(pp, &gp->provider, provider) { 363 if (!strcmp(p, pp->name)) { 364 g_free(p); 365 return (pp); 366 } 367 } 368 } 369 } 370 gctl_error(req, "Provider not found"); 371 return (NULL); 372} 373 374static int 375gctl_create_geom(struct gctl_req *req) 376{ 377 struct g_class *mp; 378 struct g_provider *pp; 379 int error; 380 381 g_topology_assert(); 382 mp = gctl_get_class(req); 383 if (mp == NULL) 384 return (gctl_error(req, "Class not found")); 385 if (mp->create_geom == NULL) 386 return (gctl_error(req, "Class has no create_geom method")); 387 pp = gctl_get_provider(req); 388 error = mp->create_geom(req, mp, pp); 389 g_topology_assert(); 390 return (error); 391} 392 393static int 394gctl_destroy_geom(struct gctl_req *req) 395{ 396 struct g_class *mp; 397 struct g_geom *gp; 398 int error; 399 400 g_topology_assert(); 401 mp = gctl_get_class(req); 402 if (mp == NULL) 403 return (gctl_error(req, "Class not found")); 404 if (mp->destroy_geom == NULL) 405 return (gctl_error(req, "Class has no destroy_geom method")); 406 gp = gctl_get_geom(req, mp); 407 if (gp == NULL) 408 return (gctl_error(req, "Geom not specified")); 409 if (gp->class != mp) 410 return (gctl_error(req, "Geom not of specificed class")); 411 error = mp->destroy_geom(req, mp, gp); 412 g_topology_assert(); 413 return (error); 414} 415 416static int 417gctl_config_geom(struct gctl_req *req) 418{ 419 struct g_class *mp; 420 struct g_geom *gp; 421 char *verb; 422 int error, vlen; 423 424 g_topology_assert(); 425 mp = gctl_get_class(req); 426 if (mp == NULL) 427 return (gctl_error(req, "Class not found")); 428 if (mp->config_geom == NULL) 429 return (gctl_error(req, "Class has no config_geom method")); 430 gp = gctl_get_geom(req, mp); 431 if (gp == NULL) 432 return (gctl_error(req, "Geom not specified")); 433 if (gp->class != mp) 434 return (gctl_error(req, "Geom not of specificed class")); 435 verb = gctl_get_param(req, "verb", &vlen); 436 if (verb == NULL) 437 return (gctl_error(req, "Missing verb parameter")); 438 if (vlen < 2) { 439 g_free(verb); 440 return (gctl_error(req, "Too short verb parameter")); 441 } 442 if (verb[vlen - 1] != '\0') { 443 g_free(verb); 444 return (gctl_error(req, "Unterminated verb parameter")); 445 } 446 error = mp->config_geom(req, gp, verb); 447 g_free(verb); 448 g_topology_assert(); 449 return (error); 450} 451 452/* 453 * Handle ioctl from libgeom::geom_ctl.c 454 */ 455static int 456g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 457{ 458 int error; 459 int i; 460 struct gctl_req *req; 461 462 req = (void *)data; 463 /* It is an error if we cannot return an error text */ 464 if (req->lerror < 1) 465 return (EINVAL); 466 if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 467 return (EINVAL); 468 469 /* Check the version */ 470 if (req->version != GCTL_VERSION) 471 return (gctl_error(req, 472 "kernel and libgeom version mismatch.")); 473 474 /* Check the request type */ 475 for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++) 476 if (gcrt[i].request == req->request) 477 break; 478 if (gcrt[i].request == GCTL_INVALID_REQUEST) 479 return (gctl_error(req, "invalid request")); 480 req->reqt = &gcrt[i]; 481 482 /* Get things on board */ 483 error = gctl_copyin(req); 484 if (error) 485 return (error); 486 487 if (g_debugflags & G_F_CTLDUMP) 488 gctl_dump(req); 489 g_topology_lock(); 490 switch (req->request) { 491 case GCTL_CREATE_GEOM: 492 error = gctl_create_geom(req); 493 break; 494 case GCTL_DESTROY_GEOM: 495 error = gctl_destroy_geom(req); 496 break; 497 case GCTL_CONFIG_GEOM: 498 error = gctl_config_geom(req); 499 break; 500 default: 501 error = gctl_error(req, "XXX: TBD"); 502 break; 503 } 504 g_topology_unlock(); 505 return (error); 506} 507 508static int 509g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 510{ 511 int error; 512 513 switch(cmd) { 514 case GEOM_CTL: 515 DROP_GIANT(); 516 error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 517 PICKUP_GIANT(); 518 break; 519 default: 520 error = ENOTTY; 521 break; 522 } 523 return (error); 524 525} 526