geom_ctl.c revision 112511
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 112511 2003-03-23 10:16:14Z 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> 58112511Sphk#define GEOM_CTL_TABLE 1 59112511Sphk#include <geom/geom_ctl.h> 60112511Sphk#include <geom/geom_ext.h> 61105068Sphk 62112511Sphk 63105068Sphkstatic g_access_t g_ctl_access; 64105068Sphkstatic g_start_t g_ctl_start; 65105068Sphkstatic void g_ctl_init(void); 66105068Sphkstatic d_ioctl_t g_ctl_ioctl; 67105068Sphk 68105068Sphkstruct g_class g_ctl_class = { 69105068Sphk "GEOMCTL", 70105068Sphk NULL, 71105068Sphk NULL, 72105068Sphk G_CLASS_INITIALIZER 73105068Sphk}; 74105068Sphk 75105068SphkDECLARE_GEOM_CLASS_INIT(g_ctl_class, g_ctl, g_ctl_init); 76105068Sphk 77105068Sphk/* 78105068Sphk * We cannot do create our geom.ctl geom/provider in g_ctl_init() because 79105068Sphk * the event thread has to finish adding our class first and that doesn't 80105068Sphk * happen until later. We know however, that the events are processed in 81105068Sphk * FIFO order, so scheduling g_ctl_init2() with g_call_me() is safe. 82105068Sphk */ 83105068Sphk 84105068Sphkstatic void 85105068Sphkg_ctl_init2(void *p __unused) 86105068Sphk{ 87105068Sphk struct g_geom *gp; 88105068Sphk struct g_provider *pp; 89105068Sphk 90105068Sphk g_topology_assert(); 91105068Sphk gp = g_new_geomf(&g_ctl_class, "geom.ctl"); 92105068Sphk gp->start = g_ctl_start; 93105068Sphk gp->access = g_ctl_access; 94105068Sphk pp = g_new_providerf(gp, "%s", gp->name); 95105551Sphk pp->sectorsize = 512; 96105068Sphk g_error_provider(pp, 0); 97105068Sphk} 98105068Sphk 99105068Sphkstatic void 100105068Sphkg_ctl_init(void) 101105068Sphk{ 102105068Sphk mtx_unlock(&Giant); 103105068Sphk g_add_class(&g_ctl_class); 104105068Sphk g_call_me(g_ctl_init2, NULL); 105105068Sphk mtx_lock(&Giant); 106105068Sphk} 107105068Sphk 108105068Sphk/* 109105068Sphk * We allow any kind of access. Access control is handled at the devfs 110105068Sphk * level. 111105068Sphk */ 112105068Sphk 113105068Sphkstatic int 114105068Sphkg_ctl_access(struct g_provider *pp, int r, int w, int e) 115105068Sphk{ 116105068Sphk int error; 117105068Sphk 118105068Sphk g_trace(G_T_ACCESS, "g_ctl_access(%s, %d, %d, %d)", 119105068Sphk pp->name, r, w, e); 120105068Sphk 121105068Sphk g_topology_assert(); 122105068Sphk error = 0; 123105068Sphk return (error); 124105068Sphk} 125105068Sphk 126105068Sphkstatic void 127105068Sphkg_ctl_start(struct bio *bp) 128105068Sphk{ 129105068Sphk struct g_ioctl *gio; 130105068Sphk int error; 131105068Sphk 132105068Sphk switch(bp->bio_cmd) { 133105068Sphk case BIO_DELETE: 134105068Sphk case BIO_READ: 135105068Sphk case BIO_WRITE: 136105068Sphk error = EOPNOTSUPP; 137105068Sphk break; 138105068Sphk case BIO_GETATTR: 139105068Sphk case BIO_SETATTR: 140105068Sphk if (strcmp(bp->bio_attribute, "GEOM::ioctl") || 141105068Sphk bp->bio_length != sizeof *gio) { 142105068Sphk error = EOPNOTSUPP; 143105068Sphk break; 144105068Sphk } 145105068Sphk gio = (struct g_ioctl *)bp->bio_data; 146105068Sphk gio->func = g_ctl_ioctl; 147105068Sphk error = EDIRIOCTL; 148105068Sphk break; 149105068Sphk default: 150105068Sphk error = EOPNOTSUPP; 151105068Sphk break; 152105068Sphk } 153105068Sphk g_io_deliver(bp, error); 154105068Sphk return; 155105068Sphk} 156105068Sphk 157105068Sphk/* 158105092Sphk * All the stuff above is really just needed to get to the stuff below 159105068Sphk */ 160105068Sphk 161105068Sphkstatic int 162105092Sphkg_ctl_ioctl_configgeom(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 163105092Sphk{ 164105092Sphk struct geomconfiggeom *gcp; 165106518Sphk struct g_configargs ga; 166105092Sphk int error; 167105092Sphk 168105092Sphk error = 0; 169105504Sphk bzero(&ga, sizeof ga); 170105092Sphk gcp = (struct geomconfiggeom *)data; 171105092Sphk ga.class = g_idclass(&gcp->class); 172105092Sphk if (ga.class == NULL) 173105092Sphk return (EINVAL); 174106518Sphk if (ga.class->config == NULL) 175105092Sphk return (EOPNOTSUPP); 176105504Sphk ga.geom = g_idgeom(&gcp->geom); 177105092Sphk ga.provider = g_idprovider(&gcp->provider); 178105092Sphk ga.len = gcp->len; 179105092Sphk if (gcp->len > 64 * 1024) 180105092Sphk return (EINVAL); 181105092Sphk else if (gcp->len == 0) { 182105092Sphk ga.ptr = NULL; 183105092Sphk } else { 184111119Simp ga.ptr = g_malloc(gcp->len, M_WAITOK); 185106518Sphk error = copyin(gcp->ptr, ga.ptr, gcp->len); 186106518Sphk if (error) { 187106518Sphk g_free(ga.ptr); 188106518Sphk return (error); 189106518Sphk } 190105092Sphk } 191105504Sphk ga.flag = gcp->flag; 192106518Sphk error = ga.class->config(&ga); 193106518Sphk if (gcp->len != 0) 194106518Sphk copyout(ga.ptr, gcp->ptr, gcp->len); /* Ignore error */ 195105504Sphk gcp->class.u.id = (uintptr_t)ga.class; 196105504Sphk gcp->class.len = 0; 197105504Sphk gcp->geom.u.id = (uintptr_t)ga.geom; 198105504Sphk gcp->geom.len = 0; 199105504Sphk gcp->provider.u.id = (uintptr_t)ga.provider; 200105504Sphk gcp->provider.len = 0; 201105092Sphk return(error); 202105092Sphk} 203105092Sphk 204112511Sphk/* 205112511Sphk * Report an error back to the user in ascii format. Return whatever copyout 206112511Sphk * returned, or EINVAL if it succeeded. 207112511Sphk * XXX: should not be static. 208112511Sphk * XXX: should take printf like args. 209112511Sphk */ 210105092Sphkstatic int 211112511Sphkg_ctl_seterror(struct geom_ctl_req *req, const char *errtxt) 212112511Sphk{ 213112511Sphk int error; 214112511Sphk 215112511Sphk error = copyout(errtxt, req->error, 216112511Sphk imin(req->lerror, strlen(errtxt) + 1)); 217112511Sphk if (!error) 218112511Sphk error = EINVAL; 219112511Sphk return (error); 220112511Sphk} 221112511Sphk 222112511Sphk/* 223112511Sphk * Allocate space and copyin() something. 224112511Sphk * XXX: this should really be a standard function in the kernel. 225112511Sphk */ 226112511Sphkstatic void * 227112511Sphkgeom_alloc_copyin(void *uaddr, size_t len, int *errp) 228112511Sphk{ 229112511Sphk int error; 230112511Sphk void *ptr; 231112511Sphk 232112511Sphk ptr = g_malloc(len, M_WAITOK); 233112511Sphk if (ptr == NULL) 234112511Sphk error = ENOMEM; 235112511Sphk else 236112511Sphk error = copyin(uaddr, ptr, len); 237112511Sphk if (!error) 238112511Sphk return (ptr); 239112511Sphk *errp = error; 240112511Sphk if (ptr != NULL) 241112511Sphk g_free(ptr); 242112511Sphk return (NULL); 243112511Sphk} 244112511Sphk 245112511Sphk 246112511Sphk/* 247112511Sphk * XXX: This function is a nightmare. It walks through the request and 248112511Sphk * XXX: makes sure that the various bits and pieces are there and copies 249112511Sphk * XXX: some of them into kernel memory to make things easier. 250112511Sphk * XXX: I really wish we had a standard marshalling layer somewhere. 251112511Sphk */ 252112511Sphk 253112511Sphkstatic int 254112511Sphkgeom_ctl_copyin(struct geom_ctl_req *req) 255112511Sphk{ 256112511Sphk int error, i, j; 257112511Sphk struct geom_ctl_req_arg *ap; 258112511Sphk char *p; 259112511Sphk 260112511Sphk error = 0; 261112511Sphk if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 262112511Sphk return (g_ctl_seterror(req, "No access to error field")); 263112511Sphk ap = geom_alloc_copyin(req->arg, req->narg * sizeof(*ap), &error); 264112511Sphk if (ap == NULL) 265112511Sphk return (error); 266112511Sphk for (i = 0; !error && i < req->narg; i++) { 267112511Sphk if (ap[i].len < 0 && 268112511Sphk !useracc(ap[i].value, 1 + -ap[i].len, VM_PROT_READ)) 269112511Sphk error = g_ctl_seterror(req, "No access to param data"); 270112511Sphk else if (ap[i].len > 0 && 271112511Sphk !useracc(ap[i].value, ap[i].len, 272112511Sphk VM_PROT_READ | VM_PROT_WRITE)) 273112511Sphk error = g_ctl_seterror(req, "No access to param data"); 274112511Sphk if (ap[i].name == NULL) 275112511Sphk continue; 276112511Sphk p = NULL; 277112511Sphk if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) 278112511Sphk error = EINVAL; 279112511Sphk if (error) 280112511Sphk break; 281112511Sphk p = geom_alloc_copyin(ap[i].name, ap[i].nlen + 1, &error); 282112511Sphk if (error) 283112511Sphk break; 284112511Sphk if (p[ap[i].nlen] != '\0') 285112511Sphk error = EINVAL; 286112511Sphk if (!error) { 287112511Sphk ap[i].name = p; 288112511Sphk ap[i].nlen = 0; 289112511Sphk } else { 290112511Sphk g_free(p); 291112511Sphk break; 292112511Sphk } 293112511Sphk } 294112511Sphk if (!error) { 295112511Sphk req->arg = ap; 296112511Sphk return (0); 297112511Sphk } 298112511Sphk for (j = 0; j < i; j++) 299112511Sphk if (ap[j].nlen == 0 && ap[j].name != NULL) 300112511Sphk g_free(ap[j].name); 301112511Sphk g_free(ap); 302112511Sphk return (error); 303112511Sphk} 304112511Sphk 305112511Sphkstatic void 306112511Sphkgeom_ctl_dump(struct geom_ctl_req *req) 307112511Sphk{ 308112511Sphk u_int i; 309112511Sphk int j, error; 310112511Sphk struct geom_ctl_req_arg *ap; 311112511Sphk void *p; 312112511Sphk 313112511Sphk 314112511Sphk printf("Dump of geom_ctl %s request at %p:\n", req->reqt->name, req); 315112511Sphk if (req->lerror > 0) { 316112511Sphk p = geom_alloc_copyin(req->error, req->lerror, &error); 317112511Sphk if (p != NULL) { 318112511Sphk ((char *)p)[req->lerror - 1] = '\0'; 319112511Sphk printf(" error:\t\"%s\"\n", (char *)p); 320112511Sphk g_free(p); 321112511Sphk } 322112511Sphk } 323112511Sphk for (i = 0; i < req->narg; i++) { 324112511Sphk ap = &req->arg[i]; 325112511Sphk if (ap->name != NULL) 326112511Sphk printf(" param:\t\"%s\"", ap->name); 327112511Sphk else 328112511Sphk printf(" meta:\t@%jd", (intmax_t)ap->offset); 329112511Sphk printf(" [%d] = ", ap->len); 330112511Sphk if (ap->len < 0) { 331112511Sphk p = geom_alloc_copyin(ap->value, 1 + -ap->len, &error); 332112511Sphk ((char *)p)[-ap->len] = '\0'; 333112511Sphk if (p != NULL) 334112511Sphk printf("\"%s\"", (char *)p); 335112511Sphk g_free(p); 336112511Sphk } else if (ap->len > 0) { 337112511Sphk p = geom_alloc_copyin(ap->value, ap->len, &error); 338112511Sphk for (j = 0; j < ap->len; j++) 339112511Sphk printf(" %02x", ((u_char *)p)[j]); 340112511Sphk g_free(p); 341112511Sphk } else { 342112511Sphk printf(" = %p", ap->value); 343112511Sphk } 344112511Sphk printf("\n"); 345112511Sphk } 346112511Sphk} 347112511Sphk 348112511Sphk/* 349112511Sphk * Handle ioctl from libgeom::geom_ctl.c 350112511Sphk */ 351112511Sphkstatic int 352112511Sphkg_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 353112511Sphk{ 354112511Sphk int error; 355112511Sphk int i; 356112511Sphk struct geom_ctl_req *req; 357112511Sphk 358112511Sphk req = (void *)data; 359112511Sphk if (req->lerror < 1) 360112511Sphk return (EINVAL); 361112511Sphk if (req->version != GEOM_CTL_VERSION) 362112511Sphk return (g_ctl_seterror(req, 363112511Sphk "Kernel and libgeom version skew.")); 364112511Sphk for (i = 0; gcrt[i].request != GEOM_INVALID_REQUEST; i++) 365112511Sphk if (gcrt[i].request == req->request) { 366112511Sphk req->reqt = &gcrt[i]; 367112511Sphk break; 368112511Sphk } 369112511Sphk if (gcrt[i].request == GEOM_INVALID_REQUEST) 370112511Sphk return (g_ctl_seterror(req, "Invalid request")); 371112511Sphk error = geom_ctl_copyin(req); 372112511Sphk if (error) 373112511Sphk return (error); 374112511Sphk geom_ctl_dump(req); 375112511Sphk return (0); 376112511Sphk} 377112511Sphk 378112511Sphkstatic int 379105068Sphkg_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 380105068Sphk{ 381105068Sphk int error; 382105068Sphk 383105068Sphk DROP_GIANT(); 384105068Sphk g_topology_lock(); 385105068Sphk switch(cmd) { 386105092Sphk case GEOMCONFIGGEOM: 387105092Sphk error = g_ctl_ioctl_configgeom(dev, cmd, data, fflag, td); 388105092Sphk break; 389112511Sphk case GEOM_CTL: 390112511Sphk error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 391112511Sphk break; 392105068Sphk default: 393105068Sphk error = ENOTTY; 394105068Sphk break; 395105068Sphk } 396105068Sphk g_topology_unlock(); 397105068Sphk PICKUP_GIANT(); 398105068Sphk return (error); 399105068Sphk 400105068Sphk} 401