geom_ctl.c revision 112709
1105068Sphk/*- 2105068Sphk * Copyright (c) 2002 Poul-Henning Kamp 3105068Sphk * Copyright (c) 2002 Networks Associates Technology, Inc. 4105068Sphk * All rights reserved. 5105068Sphk * 6105068Sphk * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7105068Sphk * and NAI Labs, the Security Research Division of Network Associates, Inc. 8105068Sphk * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9105068Sphk * DARPA CHATS research program. 10105068Sphk * 11105068Sphk * Redistribution and use in source and binary forms, with or without 12105068Sphk * modification, are permitted provided that the following conditions 13105068Sphk * are met: 14105068Sphk * 1. Redistributions of source code must retain the above copyright 15105068Sphk * notice, this list of conditions and the following disclaimer. 16105068Sphk * 2. Redistributions in binary form must reproduce the above copyright 17105068Sphk * notice, this list of conditions and the following disclaimer in the 18105068Sphk * documentation and/or other materials provided with the distribution. 19105068Sphk * 3. The names of the authors may not be used to endorse or promote 20105068Sphk * products derived from this software without specific prior written 21105068Sphk * permission. 22105068Sphk * 23105068Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24105068Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25105068Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26105068Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27105068Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28105068Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29105068Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30105068Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31105068Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32105068Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33105068Sphk * SUCH DAMAGE. 34105068Sphk * 35105068Sphk * $FreeBSD: head/sys/geom/geom_ctl.c 112709 2003-03-27 14:35:00Z phk $ 36105068Sphk */ 37105068Sphk 38105068Sphk#include "opt_geom.h" 39105068Sphk 40105068Sphk#include <sys/param.h> 41105068Sphk#include <sys/systm.h> 42105068Sphk#include <sys/kernel.h> 43105068Sphk#include <sys/sysctl.h> 44105068Sphk#include <sys/bio.h> 45105068Sphk#include <sys/conf.h> 46105068Sphk#include <sys/disk.h> 47105068Sphk#include <sys/malloc.h> 48105068Sphk#include <sys/sysctl.h> 49105068Sphk 50105068Sphk#include <sys/lock.h> 51105068Sphk#include <sys/mutex.h> 52112511Sphk 53112511Sphk#include <vm/vm.h> 54112511Sphk#include <vm/vm_extern.h> 55112511Sphk 56105068Sphk#include <geom/geom.h> 57105068Sphk#include <geom/geom_int.h> 58112709Sphk#define GCTL_TABLE 1 59112511Sphk#include <geom/geom_ctl.h> 60112511Sphk#include <geom/geom_ext.h> 61105068Sphk 62105068Sphkstatic d_ioctl_t g_ctl_ioctl; 63105068Sphk 64112534Sphkstatic struct cdevsw g_ctl_cdevsw = { 65112534Sphk .d_open = nullopen, 66112534Sphk .d_close = nullclose, 67112534Sphk .d_ioctl = g_ctl_ioctl, 68112534Sphk .d_name = "g_ctl", 69105068Sphk}; 70105068Sphk 71112534Sphkvoid 72105068Sphkg_ctl_init(void) 73105068Sphk{ 74105068Sphk 75112534Sphk make_dev(&g_ctl_cdevsw, 0, 76112534Sphk UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL); 77112709Sphk KASSERT(GCTL_PARAM_RD == VM_PROT_READ, 78112709Sphk ("GCTL_PARAM_RD != VM_PROT_READ")); 79112709Sphk KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE, 80112709Sphk ("GCTL_PARAM_WR != VM_PROT_WRITE")); 81105068Sphk} 82105068Sphk 83105068Sphkstatic int 84105092Sphkg_ctl_ioctl_configgeom(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 85105092Sphk{ 86105092Sphk struct geomconfiggeom *gcp; 87106518Sphk struct g_configargs ga; 88105092Sphk int error; 89105092Sphk 90105092Sphk error = 0; 91105504Sphk bzero(&ga, sizeof ga); 92105092Sphk gcp = (struct geomconfiggeom *)data; 93105092Sphk ga.class = g_idclass(&gcp->class); 94105092Sphk if (ga.class == NULL) 95105092Sphk return (EINVAL); 96106518Sphk if (ga.class->config == NULL) 97105092Sphk return (EOPNOTSUPP); 98105504Sphk ga.geom = g_idgeom(&gcp->geom); 99105092Sphk ga.provider = g_idprovider(&gcp->provider); 100105092Sphk ga.len = gcp->len; 101105092Sphk if (gcp->len > 64 * 1024) 102105092Sphk return (EINVAL); 103105092Sphk else if (gcp->len == 0) { 104105092Sphk ga.ptr = NULL; 105105092Sphk } else { 106111119Simp ga.ptr = g_malloc(gcp->len, M_WAITOK); 107106518Sphk error = copyin(gcp->ptr, ga.ptr, gcp->len); 108106518Sphk if (error) { 109106518Sphk g_free(ga.ptr); 110106518Sphk return (error); 111106518Sphk } 112105092Sphk } 113105504Sphk ga.flag = gcp->flag; 114106518Sphk error = ga.class->config(&ga); 115106518Sphk if (gcp->len != 0) 116106518Sphk copyout(ga.ptr, gcp->ptr, gcp->len); /* Ignore error */ 117105504Sphk gcp->class.u.id = (uintptr_t)ga.class; 118105504Sphk gcp->class.len = 0; 119105504Sphk gcp->geom.u.id = (uintptr_t)ga.geom; 120105504Sphk gcp->geom.len = 0; 121105504Sphk gcp->provider.u.id = (uintptr_t)ga.provider; 122105504Sphk gcp->provider.len = 0; 123105092Sphk return(error); 124105092Sphk} 125105092Sphk 126112511Sphk/* 127112511Sphk * Report an error back to the user in ascii format. Return whatever copyout 128112511Sphk * returned, or EINVAL if it succeeded. 129112511Sphk * XXX: should not be static. 130112511Sphk * XXX: should take printf like args. 131112511Sphk */ 132112709Sphkint 133112709Sphkgctl_error(struct gctl_req *req, const char *errtxt) 134112511Sphk{ 135112511Sphk int error; 136112511Sphk 137112511Sphk error = copyout(errtxt, req->error, 138112511Sphk imin(req->lerror, strlen(errtxt) + 1)); 139112511Sphk if (!error) 140112511Sphk error = EINVAL; 141112511Sphk return (error); 142112511Sphk} 143112511Sphk 144112511Sphk/* 145112511Sphk * Allocate space and copyin() something. 146112511Sphk * XXX: this should really be a standard function in the kernel. 147112511Sphk */ 148112511Sphkstatic void * 149112709Sphkgeom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len, int *errp) 150112511Sphk{ 151112511Sphk int error; 152112511Sphk void *ptr; 153112511Sphk 154112511Sphk ptr = g_malloc(len, M_WAITOK); 155112511Sphk if (ptr == NULL) 156112511Sphk error = ENOMEM; 157112511Sphk else 158112511Sphk error = copyin(uaddr, ptr, len); 159112511Sphk if (!error) 160112511Sphk return (ptr); 161112709Sphk gctl_error(req, "no access to argument"); 162112511Sphk *errp = error; 163112511Sphk if (ptr != NULL) 164112511Sphk g_free(ptr); 165112511Sphk return (NULL); 166112511Sphk} 167112511Sphk 168112511Sphk 169112511Sphk/* 170112511Sphk * XXX: This function is a nightmare. It walks through the request and 171112511Sphk * XXX: makes sure that the various bits and pieces are there and copies 172112511Sphk * XXX: some of them into kernel memory to make things easier. 173112511Sphk * XXX: I really wish we had a standard marshalling layer somewhere. 174112511Sphk */ 175112511Sphk 176112511Sphkstatic int 177112709Sphkgctl_copyin(struct gctl_req *req) 178112511Sphk{ 179112511Sphk int error, i, j; 180112709Sphk struct gctl_req_arg *ap; 181112511Sphk char *p; 182112511Sphk 183112511Sphk error = 0; 184112709Sphk ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap), &error); 185112709Sphk if (ap == NULL) { 186112709Sphk gctl_error(req, "copyin() of arguments failed"); 187112511Sphk return (error); 188112709Sphk } 189112709Sphk 190112511Sphk for (i = 0; !error && i < req->narg; i++) { 191112709Sphk if (ap[i].len > 0 && 192112709Sphk !useracc(ap[i].value, ap[i].len, 193112709Sphk ap[i].flag & GCTL_PARAM_RW)) 194112709Sphk error = gctl_error(req, "no access to param data"); 195112709Sphk if (ap[i].name == NULL) { 196112709Sphk if (req->reqt->meta) 197112709Sphk continue; 198112709Sphk error = gctl_error(req, 199112709Sphk "request does not take metadata arguments"); 200112709Sphk break; 201112709Sphk } 202112511Sphk p = NULL; 203112709Sphk if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) { 204112709Sphk error = gctl_error(req, "wrong param name length"); 205112511Sphk break; 206112709Sphk } 207112709Sphk p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen, &error); 208112709Sphk if (p == NULL) 209112511Sphk break; 210112709Sphk if (p[ap[i].nlen - 1] != '\0') { 211112709Sphk error = gctl_error(req, "unterminated param name"); 212112511Sphk g_free(p); 213112511Sphk break; 214112511Sphk } 215112709Sphk ap[i].name = p; 216112709Sphk ap[i].nlen = 0; 217112511Sphk } 218112511Sphk if (!error) { 219112511Sphk req->arg = ap; 220112511Sphk return (0); 221112511Sphk } 222112511Sphk for (j = 0; j < i; j++) 223112511Sphk if (ap[j].nlen == 0 && ap[j].name != NULL) 224112511Sphk g_free(ap[j].name); 225112511Sphk g_free(ap); 226112511Sphk return (error); 227112511Sphk} 228112511Sphk 229112511Sphkstatic void 230112709Sphkgctl_dump(struct gctl_req *req) 231112511Sphk{ 232112511Sphk u_int i; 233112511Sphk int j, error; 234112709Sphk struct gctl_req_arg *ap; 235112511Sphk void *p; 236112511Sphk 237112511Sphk 238112709Sphk printf("Dump of gctl %s request at %p:\n", req->reqt->name, req); 239112511Sphk if (req->lerror > 0) { 240112709Sphk p = geom_alloc_copyin(req, req->error, req->lerror, &error); 241112511Sphk if (p != NULL) { 242112511Sphk ((char *)p)[req->lerror - 1] = '\0'; 243112511Sphk printf(" error:\t\"%s\"\n", (char *)p); 244112511Sphk g_free(p); 245112511Sphk } 246112511Sphk } 247112511Sphk for (i = 0; i < req->narg; i++) { 248112511Sphk ap = &req->arg[i]; 249112511Sphk if (ap->name != NULL) 250112511Sphk printf(" param:\t\"%s\"", ap->name); 251112511Sphk else 252112511Sphk printf(" meta:\t@%jd", (intmax_t)ap->offset); 253112709Sphk printf(" [%s%s%d] = ", 254112709Sphk ap->flag & GCTL_PARAM_RD ? "R" : "", 255112709Sphk ap->flag & GCTL_PARAM_WR ? "W" : "", 256112709Sphk ap->len); 257112709Sphk if (ap->flag & GCTL_PARAM_ASCII) { 258112709Sphk p = geom_alloc_copyin(req, ap->value, ap->len, &error); 259112709Sphk if (p != NULL) { 260112709Sphk ((char *)p)[ap->len - 1] = '\0'; 261112511Sphk printf("\"%s\"", (char *)p); 262112709Sphk } 263112511Sphk g_free(p); 264112511Sphk } else if (ap->len > 0) { 265112709Sphk p = geom_alloc_copyin(req, ap->value, ap->len, &error); 266112511Sphk for (j = 0; j < ap->len; j++) 267112511Sphk printf(" %02x", ((u_char *)p)[j]); 268112511Sphk g_free(p); 269112511Sphk } else { 270112511Sphk printf(" = %p", ap->value); 271112511Sphk } 272112511Sphk printf("\n"); 273112511Sphk } 274112511Sphk} 275112511Sphk 276112709Sphkvoid * 277112709Sphkgctl_get_param(struct gctl_req *req, const char *param, int *len) 278112709Sphk{ 279112709Sphk int i, error, j; 280112709Sphk void *p; 281112709Sphk struct gctl_req_arg *ap; 282112709Sphk 283112709Sphk for (i = 0; i < req->narg; i++) { 284112709Sphk ap = &req->arg[i]; 285112709Sphk if (strcmp(param, ap->name)) 286112709Sphk continue; 287112709Sphk if (!(ap->flag & GCTL_PARAM_RD)) 288112709Sphk continue; 289112709Sphk if (ap->len > 0) 290112709Sphk j = ap->len; 291112709Sphk else 292112709Sphk j = 0; 293112709Sphk if (j != 0) 294112709Sphk p = geom_alloc_copyin(req, ap->value, j, &error); 295112709Sphk /* XXX: should not fail, tested prviously */ 296112709Sphk else 297112709Sphk p = ap->value; 298112709Sphk if (len != NULL) 299112709Sphk *len = j; 300112709Sphk return (p); 301112709Sphk } 302112709Sphk return (NULL); 303112709Sphk} 304112709Sphk 305112709Sphkstatic struct g_class* 306112709Sphkgctl_get_class(struct gctl_req *req) 307112709Sphk{ 308112709Sphk char *p; 309112709Sphk int len; 310112709Sphk struct g_class *cp; 311112709Sphk 312112709Sphk p = gctl_get_param(req, "class", &len); 313112709Sphk if (p == NULL) 314112709Sphk return (NULL); 315112709Sphk if (p[len - 1] != '\0') { 316112709Sphk gctl_error(req, "Unterminated class name"); 317112709Sphk g_free(p); 318112709Sphk return (NULL); 319112709Sphk } 320112709Sphk LIST_FOREACH(cp, &g_classes, class) { 321112709Sphk if (!strcmp(p, cp->name)) { 322112709Sphk g_free(p); 323112709Sphk return (cp); 324112709Sphk } 325112709Sphk } 326112709Sphk gctl_error(req, "Class not found"); 327112709Sphk return (NULL); 328112709Sphk} 329112709Sphk 330112709Sphkstatic struct g_geom* 331112709Sphkgctl_get_geom(struct gctl_req *req, struct g_class *mpr) 332112709Sphk{ 333112709Sphk char *p; 334112709Sphk int len; 335112709Sphk struct g_class *mp; 336112709Sphk struct g_geom *gp; 337112709Sphk 338112709Sphk p = gctl_get_param(req, "geom", &len); 339112709Sphk if (p == NULL) 340112709Sphk return (NULL); 341112709Sphk if (p[len - 1] != '\0') { 342112709Sphk gctl_error(req, "Unterminated provider name"); 343112709Sphk g_free(p); 344112709Sphk return (NULL); 345112709Sphk } 346112709Sphk LIST_FOREACH(mp, &g_classes, class) { 347112709Sphk if (mpr != NULL && mpr != mp) 348112709Sphk continue; 349112709Sphk LIST_FOREACH(gp, &mp->geom, geom) { 350112709Sphk if (!strcmp(p, gp->name)) { 351112709Sphk g_free(p); 352112709Sphk return (gp); 353112709Sphk } 354112709Sphk } 355112709Sphk } 356112709Sphk gctl_error(req, "Geom not found"); 357112709Sphk return (NULL); 358112709Sphk} 359112709Sphk 360112709Sphkstatic struct g_provider* 361112709Sphkgctl_get_provider(struct gctl_req *req) 362112709Sphk{ 363112709Sphk char *p; 364112709Sphk int len; 365112709Sphk struct g_class *cp; 366112709Sphk struct g_geom *gp; 367112709Sphk struct g_provider *pp; 368112709Sphk 369112709Sphk p = gctl_get_param(req, "provider", &len); 370112709Sphk if (p == NULL) 371112709Sphk return (NULL); 372112709Sphk if (p[len - 1] != '\0') { 373112709Sphk gctl_error(req, "Unterminated provider name"); 374112709Sphk g_free(p); 375112709Sphk return (NULL); 376112709Sphk } 377112709Sphk LIST_FOREACH(cp, &g_classes, class) { 378112709Sphk LIST_FOREACH(gp, &cp->geom, geom) { 379112709Sphk LIST_FOREACH(pp, &gp->provider, provider) { 380112709Sphk if (!strcmp(p, pp->name)) { 381112709Sphk g_free(p); 382112709Sphk return (pp); 383112709Sphk } 384112709Sphk } 385112709Sphk } 386112709Sphk } 387112709Sphk gctl_error(req, "Provider not found"); 388112709Sphk return (NULL); 389112709Sphk} 390112709Sphk 391112709Sphkstatic void 392112709Sphkgctl_create_geom(struct gctl_req *req) 393112709Sphk{ 394112709Sphk struct g_class *mp; 395112709Sphk struct g_provider *pp; 396112709Sphk 397112709Sphk g_topology_assert(); 398112709Sphk mp = gctl_get_class(req); 399112709Sphk if (mp == NULL) 400112709Sphk return; 401112709Sphk printf("Found class: %p\n", mp); 402112709Sphk if (mp->create_geom == NULL) { 403112709Sphk gctl_error(req, "Class has no create_geom method"); 404112709Sphk return; 405112709Sphk } 406112709Sphk pp = gctl_get_provider(req); 407112709Sphk printf("Found provider: %p\n", pp); 408112709Sphk mp->create_geom(req, mp, pp); 409112709Sphk g_topology_assert(); 410112709Sphk} 411112709Sphk 412112709Sphkstatic void 413112709Sphkgctl_destroy_geom(struct gctl_req *req) 414112709Sphk{ 415112709Sphk struct g_class *mp; 416112709Sphk struct g_geom *gp; 417112709Sphk 418112709Sphk g_topology_assert(); 419112709Sphk mp = gctl_get_class(req); 420112709Sphk if (mp == NULL) 421112709Sphk return; 422112709Sphk printf("Found class: %p\n", mp); 423112709Sphk if (mp->destroy_geom == NULL) { 424112709Sphk gctl_error(req, "Class has no destroy_geom method"); 425112709Sphk return; 426112709Sphk } 427112709Sphk gp = gctl_get_geom(req, mp); 428112709Sphk if (gp == NULL) { 429112709Sphk gctl_error(req, "Geom not specified"); 430112709Sphk return; 431112709Sphk } 432112709Sphk if (gp->class != mp) { 433112709Sphk gctl_error(req, "Geom not of specificed class"); 434112709Sphk return; 435112709Sphk } 436112709Sphk printf("Found geom: %p\n", gp); 437112709Sphk mp->destroy_geom(req, mp, gp); 438112709Sphk g_topology_assert(); 439112709Sphk} 440112709Sphk 441112511Sphk/* 442112511Sphk * Handle ioctl from libgeom::geom_ctl.c 443112511Sphk */ 444112511Sphkstatic int 445112511Sphkg_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 446112511Sphk{ 447112511Sphk int error; 448112511Sphk int i; 449112709Sphk struct gctl_req *req; 450112511Sphk 451112511Sphk req = (void *)data; 452112709Sphk /* It is an error if we cannot return an error text */ 453112511Sphk if (req->lerror < 1) 454112511Sphk return (EINVAL); 455112709Sphk if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 456112709Sphk return (EINVAL); 457112709Sphk 458112709Sphk /* Check the version */ 459112709Sphk if (req->version != GCTL_VERSION) 460112709Sphk return (gctl_error(req, 461112709Sphk "kernel and libgeom version mismatch.")); 462112709Sphk 463112709Sphk /* Check the request type */ 464112709Sphk for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++) 465112709Sphk if (gcrt[i].request == req->request) 466112511Sphk break; 467112709Sphk if (gcrt[i].request == GCTL_INVALID_REQUEST) 468112709Sphk return (gctl_error(req, "invalid request")); 469112709Sphk req->reqt = &gcrt[i]; 470112709Sphk 471112709Sphk /* Get things on board */ 472112709Sphk error = gctl_copyin(req); 473112511Sphk if (error) 474112511Sphk return (error); 475112709Sphk 476112709Sphk gctl_dump(req); 477112709Sphk#if 0 478112534Sphk g_stall_events(); 479112709Sphk#endif 480112709Sphk g_topology_lock(); 481112709Sphk switch (req->request) { 482112709Sphk case GCTL_CREATE_GEOM: 483112709Sphk gctl_create_geom(req); 484112709Sphk break; 485112709Sphk case GCTL_DESTROY_GEOM: 486112709Sphk gctl_destroy_geom(req); 487112709Sphk break; 488112709Sphk default: 489112709Sphk gctl_error(req, "XXX: TBD"); 490112709Sphk break; 491112709Sphk } 492112709Sphk g_topology_unlock(); 493112709Sphk#if 0 494112534Sphk g_release_events(); 495112709Sphk#endif 496112511Sphk return (0); 497112511Sphk} 498112511Sphk 499112511Sphkstatic int 500105068Sphkg_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 501105068Sphk{ 502105068Sphk int error; 503105068Sphk 504105068Sphk switch(cmd) { 505105092Sphk case GEOMCONFIGGEOM: 506112534Sphk DROP_GIANT(); 507112534Sphk g_topology_lock(); 508105092Sphk error = g_ctl_ioctl_configgeom(dev, cmd, data, fflag, td); 509112534Sphk g_topology_unlock(); 510112534Sphk PICKUP_GIANT(); 511105092Sphk break; 512112511Sphk case GEOM_CTL: 513112709Sphk DROP_GIANT(); 514112511Sphk error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 515112709Sphk PICKUP_GIANT(); 516112511Sphk break; 517105068Sphk default: 518105068Sphk error = ENOTTY; 519105068Sphk break; 520105068Sphk } 521105068Sphk return (error); 522105068Sphk 523105068Sphk} 524