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 GEOM_CTL_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 } 78 79 static int 80 g_ctl_ioctl_configgeom(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 81 { 82 struct geomconfiggeom *gcp; 83 struct g_configargs ga; 84 int error; 85 86 error = 0; 87 bzero(&ga, sizeof ga); 88 gcp = (struct geomconfiggeom *)data; 89 ga.class = g_idclass(&gcp->class); 90 if (ga.class == NULL) 91 return (EINVAL); 92 if (ga.class->config == NULL) 93 return (EOPNOTSUPP); 94 ga.geom = g_idgeom(&gcp->geom); 95 ga.provider = g_idprovider(&gcp->provider); 96 ga.len = gcp->len; 97 if (gcp->len > 64 * 1024) 98 return (EINVAL); 99 else if (gcp->len == 0) { 100 ga.ptr = NULL; 101 } else { 102 ga.ptr = g_malloc(gcp->len, M_WAITOK); 103 error = copyin(gcp->ptr, ga.ptr, gcp->len); 104 if (error) { 105 g_free(ga.ptr); 106 return (error); 107 } 108 } 109 ga.flag = gcp->flag; 110 error = ga.class->config(&ga); 111 if (gcp->len != 0) 112 copyout(ga.ptr, gcp->ptr, gcp->len); /* Ignore error */ 113 gcp->class.u.id = (uintptr_t)ga.class; 114 gcp->class.len = 0; 115 gcp->geom.u.id = (uintptr_t)ga.geom; 116 gcp->geom.len = 0; 117 gcp->provider.u.id = (uintptr_t)ga.provider; 118 gcp->provider.len = 0; 119 return(error); 120 } 121 122 /* 123 * Report an error back to the user in ascii format. Return whatever copyout 124 * returned, or EINVAL if it succeeded. 125 * XXX: should not be static. 126 * XXX: should take printf like args. 127 */ 128 static int 129 g_ctl_seterror(struct geom_ctl_req *req, const char *errtxt) 130 { 131 int error; 132 133 error = copyout(errtxt, req->error, 134 imin(req->lerror, strlen(errtxt) + 1)); 135 if (!error) 136 error = EINVAL; 137 return (error); 138 } 139 140 /* 141 * Allocate space and copyin() something. 142 * XXX: this should really be a standard function in the kernel. 143 */ 144 static void * 145 geom_alloc_copyin(void *uaddr, size_t len, int *errp) 146 { 147 int error; 148 void *ptr; 149 150 ptr = g_malloc(len, M_WAITOK); 151 if (ptr == NULL) 152 error = ENOMEM; 153 else 154 error = copyin(uaddr, ptr, len); 155 if (!error) 156 return (ptr); 157 *errp = error; 158 if (ptr != NULL) 159 g_free(ptr); 160 return (NULL); 161 } 162 163 164 /* 165 * XXX: This function is a nightmare. It walks through the request and 166 * XXX: makes sure that the various bits and pieces are there and copies 167 * XXX: some of them into kernel memory to make things easier. 168 * XXX: I really wish we had a standard marshalling layer somewhere. 169 */ 170 171 static int 172 geom_ctl_copyin(struct geom_ctl_req *req) 173 { 174 int error, i, j; 175 struct geom_ctl_req_arg *ap; 176 char *p; 177 178 error = 0; 179 if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) 180 return (g_ctl_seterror(req, "No access to error field")); 181 ap = geom_alloc_copyin(req->arg, req->narg * sizeof(*ap), &error); 182 if (ap == NULL) 183 return (error); 184 for (i = 0; !error && i < req->narg; i++) { 185 if (ap[i].len < 0 && 186 !useracc(ap[i].value, 1 + -ap[i].len, VM_PROT_READ)) 187 error = g_ctl_seterror(req, "No access to param data"); 188 else if (ap[i].len > 0 && 189 !useracc(ap[i].value, ap[i].len, 190 VM_PROT_READ | VM_PROT_WRITE)) 191 error = g_ctl_seterror(req, "No access to param data"); 192 if (ap[i].name == NULL) 193 continue; 194 p = NULL; 195 if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) 196 error = EINVAL; 197 if (error) 198 break; 199 p = geom_alloc_copyin(ap[i].name, ap[i].nlen + 1, &error); 200 if (error) 201 break; 202 if (p[ap[i].nlen] != '\0') 203 error = EINVAL; 204 if (!error) { 205 ap[i].name = p; 206 ap[i].nlen = 0; 207 } else { 208 g_free(p); 209 break; 210 } 211 } 212 if (!error) { 213 req->arg = ap; 214 return (0); 215 } 216 for (j = 0; j < i; j++) 217 if (ap[j].nlen == 0 && ap[j].name != NULL) 218 g_free(ap[j].name); 219 g_free(ap); 220 return (error); 221 } 222 223 static void 224 geom_ctl_dump(struct geom_ctl_req *req) 225 { 226 u_int i; 227 int j, error; 228 struct geom_ctl_req_arg *ap; 229 void *p; 230 231 232 printf("Dump of geom_ctl %s request at %p:\n", req->reqt->name, req); 233 if (req->lerror > 0) { 234 p = geom_alloc_copyin(req->error, req->lerror, &error); 235 if (p != NULL) { 236 ((char *)p)[req->lerror - 1] = '\0'; 237 printf(" error:\t\"%s\"\n", (char *)p); 238 g_free(p); 239 } 240 } 241 for (i = 0; i < req->narg; i++) { 242 ap = &req->arg[i]; 243 if (ap->name != NULL) 244 printf(" param:\t\"%s\"", ap->name); 245 else 246 printf(" meta:\t@%jd", (intmax_t)ap->offset); 247 printf(" [%d] = ", ap->len); 248 if (ap->len < 0) { 249 p = geom_alloc_copyin(ap->value, 1 + -ap->len, &error); 250 ((char *)p)[-ap->len] = '\0'; 251 if (p != NULL) 252 printf("\"%s\"", (char *)p); 253 g_free(p); 254 } else if (ap->len > 0) { 255 p = geom_alloc_copyin(ap->value, ap->len, &error); 256 for (j = 0; j < ap->len; j++) 257 printf(" %02x", ((u_char *)p)[j]); 258 g_free(p); 259 } else { 260 printf(" = %p", ap->value); 261 } 262 printf("\n"); 263 } 264 } 265 266 /* 267 * Handle ioctl from libgeom::geom_ctl.c 268 */ 269 static int 270 g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 271 { 272 int error; 273 int i; 274 struct geom_ctl_req *req; 275 276 req = (void *)data; 277 if (req->lerror < 1) 278 return (EINVAL); 279 if (req->version != GEOM_CTL_VERSION) 280 return (g_ctl_seterror(req, 281 "Kernel and libgeom version skew.")); 282 for (i = 0; gcrt[i].request != GEOM_INVALID_REQUEST; i++) 283 if (gcrt[i].request == req->request) { 284 req->reqt = &gcrt[i]; 285 break; 286 } 287 if (gcrt[i].request == GEOM_INVALID_REQUEST) 288 return (g_ctl_seterror(req, "Invalid request")); 289 error = geom_ctl_copyin(req); 290 if (error) 291 return (error); 292 req->reqt = &gcrt[i]; 293 g_stall_events(); 294 geom_ctl_dump(req); 295 g_release_events(); 296 return (0); 297 } 298 299 static int 300 g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 301 { 302 int error; 303 304 switch(cmd) { 305 case GEOMCONFIGGEOM: 306 DROP_GIANT(); 307 g_topology_lock(); 308 error = g_ctl_ioctl_configgeom(dev, cmd, data, fflag, td); 309 g_topology_unlock(); 310 PICKUP_GIANT(); 311 break; 312 case GEOM_CTL: 313 error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); 314 break; 315 default: 316 error = ENOTTY; 317 break; 318 } 319 return (error); 320 321 } 322