diff options
author | phk <phk@FreeBSD.org> | 2003-03-23 10:16:14 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2003-03-23 10:16:14 +0000 |
commit | 35c6fd6e4d230bf1784a3b34fe9f8e016159d614 (patch) | |
tree | 44d6b0e7213b172d67bc6c90c9530944d49fe314 /sys/geom/geom_ctl.c | |
parent | daa779779c50fac88bb9b400c4841a2559f0ce3e (diff) | |
download | FreeBSD-src-35c6fd6e4d230bf1784a3b34fe9f8e016159d614.zip FreeBSD-src-35c6fd6e4d230bf1784a3b34fe9f8e016159d614.tar.gz |
Marshalling stuff for OAM API.
Diffstat (limited to 'sys/geom/geom_ctl.c')
-rw-r--r-- | sys/geom/geom_ctl.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/sys/geom/geom_ctl.c b/sys/geom/geom_ctl.c index 1cb502c..a206305 100644 --- a/sys/geom/geom_ctl.c +++ b/sys/geom/geom_ctl.c @@ -49,8 +49,16 @@ #include <sys/lock.h> #include <sys/mutex.h> + +#include <vm/vm.h> +#include <vm/vm_extern.h> + #include <geom/geom.h> #include <geom/geom_int.h> +#define GEOM_CTL_TABLE 1 +#include <geom/geom_ctl.h> +#include <geom/geom_ext.h> + static g_access_t g_ctl_access; static g_start_t g_ctl_start; @@ -193,6 +201,180 @@ g_ctl_ioctl_configgeom(dev_t dev, u_long cmd, caddr_t data, int fflag, struct th return(error); } +/* + * Report an error back to the user in ascii format. Return whatever copyout + * returned, or EINVAL if it succeeded. + * XXX: should not be static. + * XXX: should take printf like args. + */ +static int +g_ctl_seterror(struct geom_ctl_req *req, const char *errtxt) +{ + int error; + + error = copyout(errtxt, req->error, + imin(req->lerror, strlen(errtxt) + 1)); + if (!error) + error = EINVAL; + return (error); +} + +/* + * Allocate space and copyin() something. + * XXX: this should really be a standard function in the kernel. + */ +static void * +geom_alloc_copyin(void *uaddr, size_t len, int *errp) +{ + int error; + void *ptr; + + ptr = g_malloc(len, M_WAITOK); + if (ptr == NULL) + error = ENOMEM; + else + error = copyin(uaddr, ptr, len); + if (!error) + return (ptr); + *errp = error; + if (ptr != NULL) + g_free(ptr); + return (NULL); +} + + +/* + * XXX: This function is a nightmare. It walks through the request and + * XXX: makes sure that the various bits and pieces are there and copies + * XXX: some of them into kernel memory to make things easier. + * XXX: I really wish we had a standard marshalling layer somewhere. + */ + +static int +geom_ctl_copyin(struct geom_ctl_req *req) +{ + int error, i, j; + struct geom_ctl_req_arg *ap; + char *p; + + error = 0; + if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) + return (g_ctl_seterror(req, "No access to error field")); + ap = geom_alloc_copyin(req->arg, req->narg * sizeof(*ap), &error); + if (ap == NULL) + return (error); + for (i = 0; !error && i < req->narg; i++) { + if (ap[i].len < 0 && + !useracc(ap[i].value, 1 + -ap[i].len, VM_PROT_READ)) + error = g_ctl_seterror(req, "No access to param data"); + else if (ap[i].len > 0 && + !useracc(ap[i].value, ap[i].len, + VM_PROT_READ | VM_PROT_WRITE)) + error = g_ctl_seterror(req, "No access to param data"); + if (ap[i].name == NULL) + continue; + p = NULL; + if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) + error = EINVAL; + if (error) + break; + p = geom_alloc_copyin(ap[i].name, ap[i].nlen + 1, &error); + if (error) + break; + if (p[ap[i].nlen] != '\0') + error = EINVAL; + if (!error) { + ap[i].name = p; + ap[i].nlen = 0; + } else { + g_free(p); + break; + } + } + if (!error) { + req->arg = ap; + return (0); + } + for (j = 0; j < i; j++) + if (ap[j].nlen == 0 && ap[j].name != NULL) + g_free(ap[j].name); + g_free(ap); + return (error); +} + +static void +geom_ctl_dump(struct geom_ctl_req *req) +{ + u_int i; + int j, error; + struct geom_ctl_req_arg *ap; + void *p; + + + printf("Dump of geom_ctl %s request at %p:\n", req->reqt->name, req); + if (req->lerror > 0) { + p = geom_alloc_copyin(req->error, req->lerror, &error); + if (p != NULL) { + ((char *)p)[req->lerror - 1] = '\0'; + printf(" error:\t\"%s\"\n", (char *)p); + g_free(p); + } + } + for (i = 0; i < req->narg; i++) { + ap = &req->arg[i]; + if (ap->name != NULL) + printf(" param:\t\"%s\"", ap->name); + else + printf(" meta:\t@%jd", (intmax_t)ap->offset); + printf(" [%d] = ", ap->len); + if (ap->len < 0) { + p = geom_alloc_copyin(ap->value, 1 + -ap->len, &error); + ((char *)p)[-ap->len] = '\0'; + if (p != NULL) + printf("\"%s\"", (char *)p); + g_free(p); + } else if (ap->len > 0) { + p = geom_alloc_copyin(ap->value, ap->len, &error); + for (j = 0; j < ap->len; j++) + printf(" %02x", ((u_char *)p)[j]); + g_free(p); + } else { + printf(" = %p", ap->value); + } + printf("\n"); + } +} + +/* + * Handle ioctl from libgeom::geom_ctl.c + */ +static int +g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) +{ + int error; + int i; + struct geom_ctl_req *req; + + req = (void *)data; + if (req->lerror < 1) + return (EINVAL); + if (req->version != GEOM_CTL_VERSION) + return (g_ctl_seterror(req, + "Kernel and libgeom version skew.")); + for (i = 0; gcrt[i].request != GEOM_INVALID_REQUEST; i++) + if (gcrt[i].request == req->request) { + req->reqt = &gcrt[i]; + break; + } + if (gcrt[i].request == GEOM_INVALID_REQUEST) + return (g_ctl_seterror(req, "Invalid request")); + error = geom_ctl_copyin(req); + if (error) + return (error); + geom_ctl_dump(req); + return (0); +} + static int g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { @@ -204,6 +386,9 @@ g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) case GEOMCONFIGGEOM: error = g_ctl_ioctl_configgeom(dev, cmd, data, fflag, td); break; + case GEOM_CTL: + error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); + break; default: error = ENOTTY; break; |