1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2002 Poul-Henning Kamp 5 * Copyright (c) 2002 Networks Associates Technology, Inc. 6 * All rights reserved. 7 * 8 * This software was developed for the FreeBSD Project by Poul-Henning Kamp 9 * and NAI Labs, the Security Research Division of Network Associates, Inc. 10 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 11 * DARPA CHATS research program. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. The names of the authors may not be used to endorse or promote 22 * products derived from this software without specific prior written 23 * permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #include "opt_geom.h" 42 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/kernel.h> 46 #include <sys/sysctl.h> 47 #include <sys/bio.h> 48 #include <sys/conf.h> 49 #include <sys/disk.h> 50 #include <sys/malloc.h> 51 #include <sys/sysctl.h> 52 #include <sys/sbuf.h> 53 54 #include <sys/lock.h> 55 #include <sys/mutex.h> 56 57 #include <vm/vm.h> 58 #include <vm/vm_extern.h> 59 60 #include <geom/geom.h> 61 #include <geom/geom_int.h> 62 #define GCTL_TABLE 1 63 #include <geom/geom_ctl.h> 64 65 #include <machine/stdarg.h> 66 67 static d_ioctl_t g_ctl_ioctl; 68 69 static struct cdevsw g_ctl_cdevsw = { 70 .d_version = D_VERSION, 71 .d_flags = 0, 72 .d_ioctl = g_ctl_ioctl, 73 .d_name = "g_ctl", 74 }; 75 76 void 77 g_ctl_init(void) 78 { 79 80 make_dev_credf(MAKEDEV_ETERNAL, &g_ctl_cdevsw, 0, NULL, 81 UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL); 82 KASSERT(GCTL_PARAM_RD == VM_PROT_READ, 83 ("GCTL_PARAM_RD != VM_PROT_READ")); 84 KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE, 85 ("GCTL_PARAM_WR != VM_PROT_WRITE")); 86 } 87 88 /* 89 * Report an error back to the user in ascii format. Return nerror 90 * or EINVAL if nerror isn't specified. 91 */ 92 int 93 gctl_error(struct gctl_req *req, const char *fmt, ...) 94 { 95 va_list ap; 96 97 if (req == NULL) 98 return (EINVAL); 99 100 /* We only record the first error */ 101 if (sbuf_done(req->serror)) { 102 if (!req->nerror) 103 req->nerror = EEXIST; 104 return (req->nerror); 105 } 106 if (!req->nerror) 107 req->nerror = EINVAL; 108 109 va_start(ap, fmt); 110 sbuf_vprintf(req->serror, fmt, ap); 111 va_end(ap); 112 sbuf_finish(req->serror); 113 if (g_debugflags & G_F_CTLDUMP) 114 printf("gctl %p error \"%s\"\n", req, sbuf_data(req->serror)); 115 return (req->nerror); 116 } 117 118 /* 119 * Allocate space and copyin() something. 120 * XXX: this should really be a standard function in the kernel. 121 */ 122 static void * 123 geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len) 124 { 125 void *ptr; 126 127 ptr = g_malloc(len, M_WAITOK); 128 req->nerror = copyin(uaddr, ptr, len); 129 if (!req->nerror) 130 return (ptr); 131 g_free(ptr); 132 return (NULL); 133 } 134 135 static void 136 gctl_copyin(struct gctl_req *req) 137 { 138 struct gctl_req_arg *ap; 139 char *p; 140 u_int i; 141 142 if (req->narg > GEOM_CTL_ARG_MAX) { 143 gctl_error(req, "too many arguments"); 144 req->arg = NULL; 145 return; 146 } 147 148 ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap)); 149 if (ap == NULL) { 150 gctl_error(req, "bad control request"); 151 req->arg = NULL; 152 return; 153 } 154 155 /* Nothing have been copyin()'ed yet */ 156 for (i = 0; i < req->narg; i++) { 157 ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL); 158 ap[i].flag &= ~GCTL_PARAM_CHANGED; 159 ap[i].kvalue = NULL; 160 } 161 162 for (i = 0; i < req->narg; i++) { 163 if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) { 164 gctl_error(req, 165 "wrong param name length %d: %d", i, ap[i].nlen); 166 break; 167 } 168 p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen); 169 if (p == NULL) 170 break; 171 if (p[ap[i].nlen - 1] != '\0') { 172 gctl_error(req, "unterminated param name"); 173 g_free(p); 174 break; 175 } 176 ap[i].name = p; 177 ap[i].flag |= GCTL_PARAM_NAMEKERNEL; 178 if (ap[i].len <= 0) { 179 gctl_error(req, "negative param length"); 180 break; 181 } 182 p = geom_alloc_copyin(req, ap[i].value, ap[i].len); 183 if (p == NULL) 184 break; 185 if ((ap[i].flag & GCTL_PARAM_ASCII) && 186 p[ap[i].len - 1] != '\0') { 187 gctl_error(req, "unterminated param value"); 188 g_free(p); 189 break; 190 } 191 ap[i].kvalue = p; 192 ap[i].flag |= GCTL_PARAM_VALUEKERNEL; 193 } 194 req->arg = ap; 195 return; 196 } 197 198 static void 199 gctl_copyout(struct gctl_req *req) 200 { 201 int error, i; 202 struct gctl_req_arg *ap; 203 204 if (req->nerror) 205 return; 206 error = 0; 207 ap = req->arg; 208 for (i = 0; i < req->narg; i++, ap++) { 209 if (!(ap->flag & GCTL_PARAM_CHANGED)) 210 continue; 211 error = copyout(ap->kvalue, ap->value, ap->len); 212 if (!error) 213 continue; 214 req->nerror = error; 215 return; 216 } 217 return; 218 } 219 220 static void 221 gctl_free(struct gctl_req *req) 222 { 223 u_int i; 224 225 sbuf_delete(req->serror); 226 if (req->arg == NULL) 227 return; 228 for (i = 0; i < req->narg; i++) { 229 if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL) 230 g_free(req->arg[i].name); 231 if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) && 232 req->arg[i].len > 0) 233 g_free(req->arg[i].kvalue); 234 } 235 g_free(req->arg); 236 } 237 238 static void 239 gctl_dump(struct gctl_req *req) 240 { 241 struct gctl_req_arg *ap; 242 u_int i; 243 int j; 244 245 printf("Dump of gctl request at %p:\n", req); 246 if (req->nerror > 0) { 247 printf(" nerror:\t%d\n", req->nerror); 248 if (sbuf_len(req->serror) > 0) 249 printf(" error:\t\"%s\"\n", sbuf_data(req->serror)); 250 } 251 if (req->arg == NULL) 252 return; 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 < 512; j++) 269 printf(" %02x", ((u_char *)ap->kvalue)[j]); 270 } else { 271 printf(" = %p", ap->kvalue); 272 } 273 printf("\n"); 274 } 275 } 276 277 int 278 gctl_set_param(struct gctl_req *req, const char *param, void const *ptr, 279 int len) 280 { 281 u_int i; 282 struct gctl_req_arg *ap; 283 284 for (i = 0; i < req->narg; i++) { 285 ap = &req->arg[i]; 286 if (strcmp(param, ap->name)) 287 continue; 288 if (!(ap->flag & GCTL_PARAM_WR)) 289 return (EPERM); 290 ap->flag |= GCTL_PARAM_CHANGED; 291 if (ap->len < len) { 292 bcopy(ptr, ap->kvalue, ap->len); 293 return (ENOSPC); 294 } 295 bcopy(ptr, ap->kvalue, len); 296 return (0); 297 } 298 return (EINVAL); 299 } 300 301 void 302 gctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr, 303 int len) 304 { 305 306 switch (gctl_set_param(req, param, ptr, len)) { 307 case EPERM: 308 gctl_error(req, "No write access %s argument", param); 309 break; 310 case ENOSPC: 311 gctl_error(req, "Wrong length %s argument", param); 312 break; 313 case EINVAL: 314 gctl_error(req, "Missing %s argument", param); 315 break; 316 } 317 } 318 319 void * 320 gctl_get_param(struct gctl_req *req, const char *param, int *len) 321 { 322 u_int i; 323 void *p; 324 struct gctl_req_arg *ap; 325 326 for (i = 0; i < req->narg; i++) { 327 ap = &req->arg[i]; 328 if (strcmp(param, ap->name)) 329 continue; 330 if (!(ap->flag & GCTL_PARAM_RD)) 331 continue; 332 p = ap->kvalue; 333 if (len != NULL) 334 *len = ap->len; 335 return (p); 336 } 337 return (NULL); 338 } 339 340 char const * 341 gctl_get_asciiparam(struct gctl_req *req, const char *param) 342 { 343 u_int i; 344 char const *p; 345 struct gctl_req_arg *ap; 346 347 for (i = 0; i < req->narg; i++) { 348 ap = &req->arg[i]; 349 if (strcmp(param, ap->name)) 350 continue; 351 if (!(ap->flag & GCTL_PARAM_RD)) 352 continue; 353 p = ap->kvalue; 354 if (ap->len < 1) { 355 gctl_error(req, "No length argument (%s)", param); 356 return (NULL); 357 } 358 if (p[ap->len - 1] != '\0') { 359 gctl_error(req, "Unterminated argument (%s)", param); 360 return (NULL); 361 } 362 return (p); 363 } 364 return (NULL); 365 } 366 367 void * 368 gctl_get_paraml_opt(struct gctl_req *req, const char *param, int len) 369 { 370 int i; 371 void *p; 372 373 p = gctl_get_param(req, param, &i); 374 if (i != len) { 375 p = NULL; 376 gctl_error(req, "Wrong length %s argument", param); 377 } 378 return (p); 379 } 380 381 void * 382 gctl_get_paraml(struct gctl_req *req, const char *param, int len) 383 { 384 void *p; 385 386 p = gctl_get_paraml_opt(req, param, len); 387 if (p == NULL) 388 gctl_error(req, "Missing %s argument", param); 389 return (p); 390 } 391 392 struct g_class * 393 gctl_get_class(struct gctl_req *req, char const *arg) 394 { 395 char const *p; 396 struct g_class *cp; 397 398 p = gctl_get_asciiparam(req, arg); 399 if (p == NULL) 400 return (NULL); 401 LIST_FOREACH(cp, &g_classes, class) { 402 if (!strcmp(p, cp->name)) 403 return (cp); 404 } 405 return (NULL); 406 } 407 408 struct g_geom * 409 gctl_get_geom(struct gctl_req *req, struct g_class *mpr, char const *arg) 410 { 411 char const *p; 412 struct g_class *mp; 413 struct g_geom *gp; 414 415 p = gctl_get_asciiparam(req, arg); 416 if (p == NULL) 417 return (NULL); 418 LIST_FOREACH(mp, &g_classes, class) { 419 if (mpr != NULL && mpr != mp) 420 continue; 421 LIST_FOREACH(gp, &mp->geom, geom) { 422 if (!strcmp(p, gp->name)) 423 return (gp); 424 } 425 } 426 gctl_error(req, "Geom not found: \"%s\"", p); 427 return (NULL); 428 } 429 430 struct g_provider * 431 gctl_get_provider(struct gctl_req *req, char const *arg) 432 { 433 char const *p; 434 struct g_provider *pp; 435 436 p = gctl_get_asciiparam(req, arg); 437 if (p == NULL) 438 return (NULL); 439 pp = g_provider_by_name(p); 440 if (pp != NULL) 441 return (pp); 442 gctl_error(req, "Provider not found: \"%s\"", p); 443 return (NULL); 444 } 445 446 static void 447 g_ctl_req(void *arg, int flag __unused) 448 { 449 struct g_class *mp; 450 struct gctl_req *req; 451 char const *verb; 452 453 g_topology_assert(); 454 req = arg; 455 mp = gctl_get_class(req, "class"); 456 if (mp == NULL) { 457 gctl_error(req, "Class not found"); 458 return; 459 } 460 if (mp->ctlreq == NULL) { 461 gctl_error(req, "Class takes no requests"); 462 return; 463 } 464 verb = gctl_get_param(req, "verb", NULL); 465 if (verb == NULL) { 466 gctl_error(req, "Verb missing"); 467 return; 468 } 469 mp->ctlreq(req, mp, verb); 470 g_topology_assert(); 471 } 472 473 474 static int 475 g_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 476 { 477 struct gctl_req *req; 478 int nerror; 479 480 req = (void *)data; 481 req->nerror = 0; 482 /* It is an error if we cannot return an error text */ 483 if (req->lerror < 2) 484 return (EINVAL); 485 if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 486 return (EINVAL); 487 488 req->serror = sbuf_new_auto(); 489 /* Check the version */ 490 if (req->version != GCTL_VERSION) { 491 gctl_error(req, "kernel and libgeom version mismatch."); 492 req->arg = NULL; 493 } else { 494 /* Get things on board */ 495 gctl_copyin(req); 496 497 if (g_debugflags & G_F_CTLDUMP) 498 gctl_dump(req); 499 500 if (!req->nerror) { 501 g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL); 502 gctl_copyout(req); 503 } 504 } 505 if (sbuf_done(req->serror)) { 506 copyout(sbuf_data(req->serror), req->error, 507 imin(req->lerror, sbuf_len(req->serror) + 1)); 508 } 509 510 nerror = req->nerror; 511 gctl_free(req); 512 return (nerror); 513 } 514 515 static int 516 g_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 517 { 518 int error; 519 520 switch(cmd) { 521 case GEOM_CTL: 522 error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 523 break; 524 default: 525 error = ENOIOCTL; 526 break; 527 } 528 return (error); 529 530 } 531