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