geom_ctl.c revision 112927
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 112927 2003-04-01 07:33:56Z 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 50#include <sys/lock.h> 51#include <sys/mutex.h> 52 53#include <vm/vm.h> 54#include <vm/vm_extern.h> 55 56#include <geom/geom.h> 57#include <geom/geom_int.h> 58#define GCTL_TABLE 1 59#include <geom/geom_ctl.h> 60#include <geom/geom_ext.h> 61 62static d_ioctl_t g_ctl_ioctl; 63 64static struct cdevsw g_ctl_cdevsw = { 65 .d_open = nullopen, 66 .d_close = nullclose, 67 .d_ioctl = g_ctl_ioctl, 68 .d_name = "g_ctl", 69}; 70 71void 72g_ctl_init(void) 73{ 74 75 make_dev(&g_ctl_cdevsw, 0, 76 UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL); 77 KASSERT(GCTL_PARAM_RD == VM_PROT_READ, 78 ("GCTL_PARAM_RD != VM_PROT_READ")); 79 KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE, 80 ("GCTL_PARAM_WR != VM_PROT_WRITE")); 81} 82 83/* 84 * Report an error back to the user in ascii format. Return whatever copyout 85 * returned, or EINVAL if it succeeded. 86 * XXX: should not be static. 87 * XXX: should take printf like args. 88 */ 89int 90gctl_error(struct gctl_req *req, const char *errtxt) 91{ 92 int error; 93 94 error = copyout(errtxt, req->error, 95 imin(req->lerror, strlen(errtxt) + 1)); 96 if (!error) 97 error = EINVAL; 98 return (error); 99} 100 101/* 102 * Allocate space and copyin() something. 103 * XXX: this should really be a standard function in the kernel. 104 */ 105static void * 106geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len, int *errp) 107{ 108 int error; 109 void *ptr; 110 111 ptr = g_malloc(len, M_WAITOK); 112 if (ptr == NULL) 113 error = ENOMEM; 114 else 115 error = copyin(uaddr, ptr, len); 116 if (!error) 117 return (ptr); 118 gctl_error(req, "no access to argument"); 119 *errp = error; 120 if (ptr != NULL) 121 g_free(ptr); 122 return (NULL); 123} 124 125 126/* 127 * XXX: This function is a nightmare. It walks through the request and 128 * XXX: makes sure that the various bits and pieces are there and copies 129 * XXX: some of them into kernel memory to make things easier. 130 * XXX: I really wish we had a standard marshalling layer somewhere. 131 */ 132 133static int 134gctl_copyin(struct gctl_req *req) 135{ 136 int error, i, j; 137 struct gctl_req_arg *ap; 138 char *p; 139 140 error = 0; 141 ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap), &error); 142 if (ap == NULL) { 143 gctl_error(req, "copyin() of arguments failed"); 144 return (error); 145 } 146 147 for (i = 0; !error && i < req->narg; i++) { 148 if (ap[i].len > 0 && 149 !useracc(ap[i].value, ap[i].len, 150 ap[i].flag & GCTL_PARAM_RW)) 151 error = gctl_error(req, "no access to param data"); 152 if (ap[i].name == NULL) { 153 if (req->reqt->meta) 154 continue; 155 error = gctl_error(req, 156 "request does not take metadata arguments"); 157 break; 158 } 159 p = NULL; 160 if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) { 161 error = gctl_error(req, "wrong param name length"); 162 break; 163 } 164 p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen, &error); 165 if (p == NULL) 166 break; 167 if (p[ap[i].nlen - 1] != '\0') { 168 error = gctl_error(req, "unterminated param name"); 169 g_free(p); 170 break; 171 } 172 ap[i].name = p; 173 ap[i].nlen = 0; 174 } 175 if (!error) { 176 req->arg = ap; 177 return (0); 178 } 179 for (j = 0; j < i; j++) 180 if (ap[j].nlen == 0 && ap[j].name != NULL) 181 g_free(ap[j].name); 182 g_free(ap); 183 return (error); 184} 185 186static void 187gctl_dump(struct gctl_req *req) 188{ 189 u_int i; 190 int j, error; 191 struct gctl_req_arg *ap; 192 void *p; 193 194 195 printf("Dump of gctl %s request at %p:\n", req->reqt->name, req); 196 if (req->lerror > 0) { 197 p = geom_alloc_copyin(req, req->error, req->lerror, &error); 198 if (p != NULL) { 199 ((char *)p)[req->lerror - 1] = '\0'; 200 printf(" error:\t\"%s\"\n", (char *)p); 201 g_free(p); 202 } 203 } 204 for (i = 0; i < req->narg; i++) { 205 ap = &req->arg[i]; 206 if (ap->name != NULL) 207 printf(" param:\t\"%s\"", ap->name); 208 else 209 printf(" meta:\t@%jd", (intmax_t)ap->offset); 210 printf(" [%s%s%d] = ", 211 ap->flag & GCTL_PARAM_RD ? "R" : "", 212 ap->flag & GCTL_PARAM_WR ? "W" : "", 213 ap->len); 214 if (ap->flag & GCTL_PARAM_ASCII) { 215 p = geom_alloc_copyin(req, ap->value, ap->len, &error); 216 if (p != NULL) { 217 ((char *)p)[ap->len - 1] = '\0'; 218 printf("\"%s\"", (char *)p); 219 } 220 g_free(p); 221 } else if (ap->len > 0) { 222 p = geom_alloc_copyin(req, ap->value, ap->len, &error); 223 for (j = 0; j < ap->len; j++) 224 printf(" %02x", ((u_char *)p)[j]); 225 g_free(p); 226 } else { 227 printf(" = %p", ap->value); 228 } 229 printf("\n"); 230 } 231} 232 233void * 234gctl_get_param(struct gctl_req *req, const char *param, int *len) 235{ 236 int i, error, j; 237 void *p; 238 struct gctl_req_arg *ap; 239 240 for (i = 0; i < req->narg; i++) { 241 ap = &req->arg[i]; 242 if (strcmp(param, ap->name)) 243 continue; 244 if (!(ap->flag & GCTL_PARAM_RD)) 245 continue; 246 if (ap->len > 0) 247 j = ap->len; 248 else 249 j = 0; 250 if (j != 0) 251 p = geom_alloc_copyin(req, ap->value, j, &error); 252 /* XXX: should not fail, tested prviously */ 253 else 254 p = ap->value; 255 if (len != NULL) 256 *len = j; 257 return (p); 258 } 259 return (NULL); 260} 261 262static struct g_class* 263gctl_get_class(struct gctl_req *req) 264{ 265 char *p; 266 int len; 267 struct g_class *cp; 268 269 p = gctl_get_param(req, "class", &len); 270 if (p == NULL) 271 return (NULL); 272 if (p[len - 1] != '\0') { 273 gctl_error(req, "Unterminated class name"); 274 g_free(p); 275 return (NULL); 276 } 277 LIST_FOREACH(cp, &g_classes, class) { 278 if (!strcmp(p, cp->name)) { 279 g_free(p); 280 return (cp); 281 } 282 } 283 gctl_error(req, "Class not found"); 284 return (NULL); 285} 286 287static struct g_geom* 288gctl_get_geom(struct gctl_req *req, struct g_class *mpr) 289{ 290 char *p; 291 int len; 292 struct g_class *mp; 293 struct g_geom *gp; 294 295 p = gctl_get_param(req, "geom", &len); 296 if (p == NULL) 297 return (NULL); 298 if (p[len - 1] != '\0') { 299 gctl_error(req, "Unterminated provider name"); 300 g_free(p); 301 return (NULL); 302 } 303 LIST_FOREACH(mp, &g_classes, class) { 304 if (mpr != NULL && mpr != mp) 305 continue; 306 LIST_FOREACH(gp, &mp->geom, geom) { 307 if (!strcmp(p, gp->name)) { 308 g_free(p); 309 return (gp); 310 } 311 } 312 } 313 gctl_error(req, "Geom not found"); 314 return (NULL); 315} 316 317static struct g_provider* 318gctl_get_provider(struct gctl_req *req) 319{ 320 char *p; 321 int len; 322 struct g_class *cp; 323 struct g_geom *gp; 324 struct g_provider *pp; 325 326 p = gctl_get_param(req, "provider", &len); 327 if (p == NULL) 328 return (NULL); 329 if (p[len - 1] != '\0') { 330 gctl_error(req, "Unterminated provider name"); 331 g_free(p); 332 return (NULL); 333 } 334 LIST_FOREACH(cp, &g_classes, class) { 335 LIST_FOREACH(gp, &cp->geom, geom) { 336 LIST_FOREACH(pp, &gp->provider, provider) { 337 if (!strcmp(p, pp->name)) { 338 g_free(p); 339 return (pp); 340 } 341 } 342 } 343 } 344 gctl_error(req, "Provider not found"); 345 return (NULL); 346} 347 348static void 349gctl_create_geom(struct gctl_req *req) 350{ 351 struct g_class *mp; 352 struct g_provider *pp; 353 354 g_topology_assert(); 355 mp = gctl_get_class(req); 356 if (mp == NULL) 357 return; 358 if (mp->create_geom == NULL) { 359 gctl_error(req, "Class has no create_geom method"); 360 return; 361 } 362 pp = gctl_get_provider(req); 363 mp->create_geom(req, mp, pp); 364 g_topology_assert(); 365} 366 367static void 368gctl_destroy_geom(struct gctl_req *req) 369{ 370 struct g_class *mp; 371 struct g_geom *gp; 372 373 g_topology_assert(); 374 mp = gctl_get_class(req); 375 if (mp == NULL) 376 return; 377 if (mp->destroy_geom == NULL) { 378 gctl_error(req, "Class has no destroy_geom method"); 379 return; 380 } 381 gp = gctl_get_geom(req, mp); 382 if (gp == NULL) { 383 gctl_error(req, "Geom not specified"); 384 return; 385 } 386 if (gp->class != mp) { 387 gctl_error(req, "Geom not of specificed class"); 388 return; 389 } 390 mp->destroy_geom(req, mp, gp); 391 g_topology_assert(); 392} 393 394/* 395 * Handle ioctl from libgeom::geom_ctl.c 396 */ 397static int 398g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 399{ 400 int error; 401 int i; 402 struct gctl_req *req; 403 404 req = (void *)data; 405 /* It is an error if we cannot return an error text */ 406 if (req->lerror < 1) 407 return (EINVAL); 408 if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 409 return (EINVAL); 410 411 /* Check the version */ 412 if (req->version != GCTL_VERSION) 413 return (gctl_error(req, 414 "kernel and libgeom version mismatch.")); 415 416 /* Check the request type */ 417 for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++) 418 if (gcrt[i].request == req->request) 419 break; 420 if (gcrt[i].request == GCTL_INVALID_REQUEST) 421 return (gctl_error(req, "invalid request")); 422 req->reqt = &gcrt[i]; 423 424 /* Get things on board */ 425 error = gctl_copyin(req); 426 if (error) 427 return (error); 428 429 if (g_debugflags & G_F_CTLDUMP) 430 gctl_dump(req); 431#if 0 432 g_stall_events(); 433#endif 434 g_topology_lock(); 435 switch (req->request) { 436 case GCTL_CREATE_GEOM: 437 gctl_create_geom(req); 438 break; 439 case GCTL_DESTROY_GEOM: 440 gctl_destroy_geom(req); 441 break; 442 default: 443 gctl_error(req, "XXX: TBD"); 444 break; 445 } 446 g_topology_unlock(); 447#if 0 448 g_release_events(); 449#endif 450 return (0); 451} 452 453static int 454g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 455{ 456 int error; 457 458 switch(cmd) { 459 case GEOM_CTL: 460 DROP_GIANT(); 461 error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 462 PICKUP_GIANT(); 463 break; 464 default: 465 error = ENOTTY; 466 break; 467 } 468 return (error); 469 470} 471