geom_ctl.c revision 112876
1156952Sume/*- 2156952Sume * Copyright (c) 2002 Poul-Henning Kamp 3156952Sume * Copyright (c) 2002 Networks Associates Technology, Inc. 4156952Sume * All rights reserved. 5156952Sume * 6156952Sume * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7156952Sume * and NAI Labs, the Security Research Division of Network Associates, Inc. 8156952Sume * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9156952Sume * DARPA CHATS research program. 10156952Sume * 11156952Sume * Redistribution and use in source and binary forms, with or without 12156952Sume * modification, are permitted provided that the following conditions 13156952Sume * are met: 14156952Sume * 1. Redistributions of source code must retain the above copyright 15156952Sume * notice, this list of conditions and the following disclaimer. 16156952Sume * 2. Redistributions in binary form must reproduce the above copyright 17156952Sume * notice, this list of conditions and the following disclaimer in the 18156952Sume * documentation and/or other materials provided with the distribution. 19170242Sume * 3. The names of the authors may not be used to endorse or promote 20156952Sume * products derived from this software without specific prior written 21156952Sume * permission. 22156952Sume * 23156952Sume * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24156952Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25156952Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26156952Sume * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27156952Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28156952Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29156952Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30156952Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31156952Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32156952Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33156952Sume * SUCH DAMAGE. 34156952Sume * 35156952Sume * $FreeBSD: head/sys/geom/geom_ctl.c 112876 2003-03-31 18:35:37Z phk $ 36156952Sume */ 37156952Sume 38156952Sume#include "opt_geom.h" 39156952Sume 40156952Sume#include <sys/param.h> 41156952Sume#include <sys/systm.h> 42156952Sume#include <sys/kernel.h> 43156952Sume#include <sys/sysctl.h> 44170242Sume#include <sys/bio.h> 45156952Sume#include <sys/conf.h> 46156952Sume#include <sys/disk.h> 47156952Sume#include <sys/malloc.h> 48156952Sume#include <sys/sysctl.h> 49156952Sume 50156952Sume#include <sys/lock.h> 51156952Sume#include <sys/mutex.h> 52156952Sume 53156952Sume#include <vm/vm.h> 54156952Sume#include <vm/vm_extern.h> 55156952Sume 56156952Sume#include <geom/geom.h> 57156952Sume#include <geom/geom_int.h> 58156952Sume#define GCTL_TABLE 1 59156952Sume#include <geom/geom_ctl.h> 60156952Sume#include <geom/geom_ext.h> 61156952Sume 62156952Sumestatic d_ioctl_t g_ctl_ioctl; 63156952Sume 64156952Sumestatic struct cdevsw g_ctl_cdevsw = { 65156952Sume .d_open = nullopen, 66156952Sume .d_close = nullclose, 67156952Sume .d_ioctl = g_ctl_ioctl, 68156952Sume .d_name = "g_ctl", 69156952Sume}; 70156952Sume 71156952Sumevoid 72156952Sumeg_ctl_init(void) 73156952Sume{ 74156952Sume 75156952Sume make_dev(&g_ctl_cdevsw, 0, 76156952Sume UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL); 77156952Sume KASSERT(GCTL_PARAM_RD == VM_PROT_READ, 78156952Sume ("GCTL_PARAM_RD != VM_PROT_READ")); 79156952Sume KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE, 80156952Sume ("GCTL_PARAM_WR != VM_PROT_WRITE")); 81156952Sume} 82156952Sume 83156952Sumestatic int 84156952Sumeg_ctl_ioctl_configgeom(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 85156952Sume{ 86170242Sume struct geomconfiggeom *gcp; 87156952Sume struct g_configargs ga; 88170242Sume int error; 89156952Sume 90170242Sume error = 0; 91170242Sume bzero(&ga, sizeof ga); 92156952Sume gcp = (struct geomconfiggeom *)data; 93170242Sume ga.class = g_idclass(&gcp->class); 94170242Sume if (ga.class == NULL) 95156952Sume return (EINVAL); 96156952Sume if (ga.class->config == NULL) 97156952Sume return (EOPNOTSUPP); 98156952Sume ga.geom = g_idgeom(&gcp->geom); 99156952Sume ga.provider = g_idprovider(&gcp->provider); 100156952Sume ga.len = gcp->len; 101156952Sume if (gcp->len > 64 * 1024) 102156952Sume return (EINVAL); 103156952Sume else if (gcp->len == 0) { 104156952Sume ga.ptr = NULL; 105156952Sume } else { 106156952Sume ga.ptr = g_malloc(gcp->len, M_WAITOK); 107156952Sume error = copyin(gcp->ptr, ga.ptr, gcp->len); 108156952Sume if (error) { 109156952Sume g_free(ga.ptr); 110156952Sume return (error); 111156952Sume } 112156952Sume } 113156952Sume ga.flag = gcp->flag; 114156952Sume error = ga.class->config(&ga); 115156952Sume if (gcp->len != 0) 116156952Sume copyout(ga.ptr, gcp->ptr, gcp->len); /* Ignore error */ 117156952Sume gcp->class.u.id = (uintptr_t)ga.class; 118156952Sume gcp->class.len = 0; 119156952Sume gcp->geom.u.id = (uintptr_t)ga.geom; 120156952Sume gcp->geom.len = 0; 121156952Sume gcp->provider.u.id = (uintptr_t)ga.provider; 122156952Sume gcp->provider.len = 0; 123170242Sume return(error); 124156952Sume} 125156952Sume 126156952Sume/* 127156952Sume * Report an error back to the user in ascii format. Return whatever copyout 128156952Sume * returned, or EINVAL if it succeeded. 129156952Sume * XXX: should not be static. 130156952Sume * XXX: should take printf like args. 131156952Sume */ 132156952Sumeint 133156952Sumegctl_error(struct gctl_req *req, const char *errtxt) 134156952Sume{ 135156952Sume int error; 136156952Sume 137156952Sume error = copyout(errtxt, req->error, 138156952Sume imin(req->lerror, strlen(errtxt) + 1)); 139156952Sume if (!error) 140156952Sume error = EINVAL; 141156952Sume return (error); 142156952Sume} 143156952Sume 144156952Sume/* 145156952Sume * Allocate space and copyin() something. 146156952Sume * XXX: this should really be a standard function in the kernel. 147156952Sume */ 148156952Sumestatic void * 149156952Sumegeom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len, int *errp) 150156952Sume{ 151156952Sume int error; 152156952Sume void *ptr; 153156952Sume 154156952Sume ptr = g_malloc(len, M_WAITOK); 155156952Sume if (ptr == NULL) 156156952Sume error = ENOMEM; 157156952Sume else 158156952Sume error = copyin(uaddr, ptr, len); 159156952Sume if (!error) 160156952Sume return (ptr); 161156952Sume gctl_error(req, "no access to argument"); 162156952Sume *errp = error; 163156952Sume if (ptr != NULL) 164156952Sume g_free(ptr); 165156952Sume return (NULL); 166156952Sume} 167156952Sume 168156952Sume 169156952Sume/* 170156952Sume * XXX: This function is a nightmare. It walks through the request and 171156952Sume * XXX: makes sure that the various bits and pieces are there and copies 172156952Sume * XXX: some of them into kernel memory to make things easier. 173156952Sume * XXX: I really wish we had a standard marshalling layer somewhere. 174156952Sume */ 175156952Sume 176156952Sumestatic int 177156952Sumegctl_copyin(struct gctl_req *req) 178156952Sume{ 179156952Sume int error, i, j; 180156952Sume struct gctl_req_arg *ap; 181156952Sume char *p; 182156952Sume 183156952Sume error = 0; 184156952Sume ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap), &error); 185156952Sume if (ap == NULL) { 186156952Sume gctl_error(req, "copyin() of arguments failed"); 187156952Sume return (error); 188170242Sume } 189156952Sume 190170242Sume for (i = 0; !error && i < req->narg; i++) { 191156952Sume if (ap[i].len > 0 && 192170242Sume !useracc(ap[i].value, ap[i].len, 193170242Sume ap[i].flag & GCTL_PARAM_RW)) 194170242Sume error = gctl_error(req, "no access to param data"); 195170242Sume if (ap[i].name == NULL) { 196170242Sume if (req->reqt->meta) 197156952Sume continue; 198170242Sume error = gctl_error(req, 199156952Sume "request does not take metadata arguments"); 200156952Sume break; 201156952Sume } 202156952Sume p = NULL; 203156952Sume if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) { 204156952Sume error = gctl_error(req, "wrong param name length"); 205156952Sume break; 206156952Sume } 207156952Sume p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen, &error); 208156952Sume if (p == NULL) 209156952Sume break; 210156952Sume if (p[ap[i].nlen - 1] != '\0') { 211156952Sume error = gctl_error(req, "unterminated param name"); 212156952Sume g_free(p); 213156952Sume break; 214156952Sume } 215170242Sume ap[i].name = p; 216156952Sume ap[i].nlen = 0; 217170242Sume } 218156952Sume if (!error) { 219156952Sume req->arg = ap; 220156952Sume return (0); 221156952Sume } 222156952Sume for (j = 0; j < i; j++) 223156952Sume if (ap[j].nlen == 0 && ap[j].name != NULL) 224156952Sume g_free(ap[j].name); 225156952Sume g_free(ap); 226156952Sume return (error); 227156952Sume} 228156952Sume 229156952Sumestatic void 230156952Sumegctl_dump(struct gctl_req *req) 231156952Sume{ 232156952Sume u_int i; 233156952Sume int j, error; 234156952Sume struct gctl_req_arg *ap; 235156952Sume void *p; 236156952Sume 237156952Sume 238156952Sume printf("Dump of gctl %s request at %p:\n", req->reqt->name, req); 239156952Sume if (req->lerror > 0) { 240156952Sume p = geom_alloc_copyin(req, req->error, req->lerror, &error); 241156952Sume if (p != NULL) { 242156952Sume ((char *)p)[req->lerror - 1] = '\0'; 243156952Sume printf(" error:\t\"%s\"\n", (char *)p); 244156952Sume g_free(p); 245156952Sume } 246156952Sume } 247156952Sume for (i = 0; i < req->narg; i++) { 248156952Sume ap = &req->arg[i]; 249156952Sume if (ap->name != NULL) 250156952Sume printf(" param:\t\"%s\"", ap->name); 251156952Sume else 252156952Sume printf(" meta:\t@%jd", (intmax_t)ap->offset); 253156952Sume printf(" [%s%s%d] = ", 254156952Sume ap->flag & GCTL_PARAM_RD ? "R" : "", 255156952Sume ap->flag & GCTL_PARAM_WR ? "W" : "", 256156952Sume ap->len); 257156952Sume if (ap->flag & GCTL_PARAM_ASCII) { 258156952Sume p = geom_alloc_copyin(req, ap->value, ap->len, &error); 259156952Sume if (p != NULL) { 260156952Sume ((char *)p)[ap->len - 1] = '\0'; 261156952Sume printf("\"%s\"", (char *)p); 262170242Sume } 263156952Sume g_free(p); 264156952Sume } else if (ap->len > 0) { 265156952Sume p = geom_alloc_copyin(req, ap->value, ap->len, &error); 266156952Sume for (j = 0; j < ap->len; j++) 267156952Sume printf(" %02x", ((u_char *)p)[j]); 268156952Sume g_free(p); 269156952Sume } else { 270156952Sume printf(" = %p", ap->value); 271156952Sume } 272156952Sume printf("\n"); 273156952Sume } 274156952Sume} 275156952Sume 276156952Sumevoid * 277156952Sumegctl_get_param(struct gctl_req *req, const char *param, int *len) 278156952Sume{ 279156952Sume int i, error, j; 280156952Sume void *p; 281156952Sume struct gctl_req_arg *ap; 282156952Sume 283156952Sume for (i = 0; i < req->narg; i++) { 284156952Sume ap = &req->arg[i]; 285156952Sume if (strcmp(param, ap->name)) 286156952Sume continue; 287156952Sume if (!(ap->flag & GCTL_PARAM_RD)) 288156952Sume continue; 289156952Sume if (ap->len > 0) 290156952Sume j = ap->len; 291156952Sume else 292156952Sume j = 0; 293156952Sume if (j != 0) 294156952Sume p = geom_alloc_copyin(req, ap->value, j, &error); 295156952Sume /* XXX: should not fail, tested prviously */ 296156952Sume else 297156952Sume p = ap->value; 298156952Sume if (len != NULL) 299156952Sume *len = j; 300170242Sume return (p); 301156952Sume } 302156952Sume return (NULL); 303156952Sume} 304156952Sume 305156952Sumestatic struct g_class* 306156952Sumegctl_get_class(struct gctl_req *req) 307156952Sume{ 308156952Sume char *p; 309156952Sume int len; 310156952Sume struct g_class *cp; 311156952Sume 312156952Sume p = gctl_get_param(req, "class", &len); 313156952Sume if (p == NULL) 314156952Sume return (NULL); 315156952Sume if (p[len - 1] != '\0') { 316156952Sume gctl_error(req, "Unterminated class name"); 317170242Sume g_free(p); 318156952Sume return (NULL); 319156952Sume } 320156952Sume LIST_FOREACH(cp, &g_classes, class) { 321156952Sume if (!strcmp(p, cp->name)) { 322156952Sume g_free(p); 323156952Sume return (cp); 324170242Sume } 325156952Sume } 326170242Sume gctl_error(req, "Class not found"); 327156952Sume return (NULL); 328170242Sume} 329170242Sume 330156952Sumestatic struct g_geom* 331170242Sumegctl_get_geom(struct gctl_req *req, struct g_class *mpr) 332156952Sume{ 333156952Sume char *p; 334156952Sume int len; 335156952Sume struct g_class *mp; 336156952Sume struct g_geom *gp; 337156952Sume 338156952Sume p = gctl_get_param(req, "geom", &len); 339156952Sume if (p == NULL) 340156952Sume return (NULL); 341156952Sume if (p[len - 1] != '\0') { 342156952Sume gctl_error(req, "Unterminated provider name"); 343156952Sume g_free(p); 344156952Sume return (NULL); 345156952Sume } 346156952Sume LIST_FOREACH(mp, &g_classes, class) { 347156952Sume if (mpr != NULL && mpr != mp) 348156952Sume continue; 349156952Sume LIST_FOREACH(gp, &mp->geom, geom) { 350156952Sume if (!strcmp(p, gp->name)) { 351156952Sume g_free(p); 352156952Sume return (gp); 353156952Sume } 354156952Sume } 355156952Sume } 356156952Sume gctl_error(req, "Geom not found"); 357156952Sume return (NULL); 358156952Sume} 359156952Sume 360156952Sumestatic struct g_provider* 361156952Sumegctl_get_provider(struct gctl_req *req) 362156952Sume{ 363156952Sume char *p; 364156952Sume int len; 365156952Sume struct g_class *cp; 366156952Sume struct g_geom *gp; 367156952Sume struct g_provider *pp; 368156952Sume 369156952Sume p = gctl_get_param(req, "provider", &len); 370156952Sume if (p == NULL) 371156952Sume return (NULL); 372156952Sume if (p[len - 1] != '\0') { 373156952Sume gctl_error(req, "Unterminated provider name"); 374156952Sume g_free(p); 375156952Sume return (NULL); 376156952Sume } 377156952Sume LIST_FOREACH(cp, &g_classes, class) { 378170242Sume LIST_FOREACH(gp, &cp->geom, geom) { 379156952Sume LIST_FOREACH(pp, &gp->provider, provider) { 380170242Sume if (!strcmp(p, pp->name)) { 381156952Sume g_free(p); 382170242Sume return (pp); 383156952Sume } 384156952Sume } 385156952Sume } 386156952Sume } 387156952Sume gctl_error(req, "Provider not found"); 388156952Sume return (NULL); 389156952Sume} 390156952Sume 391156952Sumestatic void 392156952Sumegctl_create_geom(struct gctl_req *req) 393156952Sume{ 394156952Sume struct g_class *mp; 395156952Sume struct g_provider *pp; 396156952Sume 397156952Sume g_topology_assert(); 398156952Sume mp = gctl_get_class(req); 399156952Sume if (mp == NULL) 400156952Sume return; 401156952Sume if (mp->create_geom == NULL) { 402156952Sume gctl_error(req, "Class has no create_geom method"); 403156952Sume return; 404156952Sume } 405156952Sume pp = gctl_get_provider(req); 406156952Sume mp->create_geom(req, mp, pp); 407156952Sume g_topology_assert(); 408156952Sume} 409156952Sume 410156952Sumestatic void 411156952Sumegctl_destroy_geom(struct gctl_req *req) 412156952Sume{ 413156952Sume struct g_class *mp; 414156952Sume struct g_geom *gp; 415156952Sume 416156952Sume g_topology_assert(); 417156952Sume mp = gctl_get_class(req); 418156952Sume if (mp == NULL) 419156952Sume return; 420156952Sume if (mp->destroy_geom == NULL) { 421156952Sume gctl_error(req, "Class has no destroy_geom method"); 422156952Sume return; 423156952Sume } 424156952Sume gp = gctl_get_geom(req, mp); 425156952Sume if (gp == NULL) { 426156952Sume gctl_error(req, "Geom not specified"); 427156952Sume return; 428156952Sume } 429156952Sume if (gp->class != mp) { 430156952Sume gctl_error(req, "Geom not of specificed class"); 431170242Sume return; 432156952Sume } 433156952Sume mp->destroy_geom(req, mp, gp); 434156952Sume g_topology_assert(); 435156952Sume} 436156952Sume 437156952Sume/* 438156952Sume * Handle ioctl from libgeom::geom_ctl.c 439156952Sume */ 440156952Sumestatic int 441156952Sumeg_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 442156952Sume{ 443156952Sume int error; 444156952Sume int i; 445156952Sume struct gctl_req *req; 446156952Sume 447156952Sume req = (void *)data; 448156952Sume /* It is an error if we cannot return an error text */ 449170242Sume if (req->lerror < 1) 450156952Sume return (EINVAL); 451156952Sume if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 452156952Sume return (EINVAL); 453156952Sume 454156952Sume /* Check the version */ 455156952Sume if (req->version != GCTL_VERSION) 456156952Sume return (gctl_error(req, 457156952Sume "kernel and libgeom version mismatch.")); 458170242Sume 459156952Sume /* Check the request type */ 460170242Sume for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++) 461156952Sume if (gcrt[i].request == req->request) 462170242Sume break; 463170242Sume if (gcrt[i].request == GCTL_INVALID_REQUEST) 464156952Sume return (gctl_error(req, "invalid request")); 465170242Sume req->reqt = &gcrt[i]; 466170242Sume 467156952Sume /* Get things on board */ 468170242Sume error = gctl_copyin(req); 469156952Sume if (error) 470170242Sume return (error); 471156952Sume 472170242Sume if (g_debugflags & G_F_CTLDUMP) 473156952Sume gctl_dump(req); 474156952Sume#if 0 475156952Sume g_stall_events(); 476156952Sume#endif 477156952Sume g_topology_lock(); 478156952Sume switch (req->request) { 479156952Sume case GCTL_CREATE_GEOM: 480156952Sume gctl_create_geom(req); 481156952Sume break; 482156952Sume case GCTL_DESTROY_GEOM: 483156952Sume gctl_destroy_geom(req); 484156952Sume break; 485156952Sume default: 486156952Sume gctl_error(req, "XXX: TBD"); 487156952Sume break; 488156952Sume } 489156952Sume g_topology_unlock(); 490156952Sume#if 0 491156952Sume g_release_events(); 492156952Sume#endif 493156952Sume return (0); 494170242Sume} 495156952Sume 496156952Sumestatic int 497156952Sumeg_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 498156952Sume{ 499156952Sume int error; 500156952Sume 501156952Sume switch(cmd) { 502156952Sume case GEOMCONFIGGEOM: 503156952Sume DROP_GIANT(); 504156952Sume g_topology_lock(); 505156952Sume error = g_ctl_ioctl_configgeom(dev, cmd, data, fflag, td); 506156952Sume g_topology_unlock(); 507156952Sume PICKUP_GIANT(); 508156952Sume break; 509156952Sume case GEOM_CTL: 510156952Sume DROP_GIANT(); 511156952Sume error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 512156952Sume PICKUP_GIANT(); 513156952Sume break; 514156952Sume default: 515156952Sume error = ENOTTY; 516156952Sume break; 517156952Sume } 518156952Sume return (error); 519156952Sume 520156952Sume} 521156952Sume