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