1105068Sphk/*-
2105068Sphk * Copyright (c) 2002 Poul-Henning Kamp
3105068Sphk * Copyright (c) 2002 Networks Associates Technology, Inc.
4105068Sphk * All rights reserved.
5105068Sphk *
6105068Sphk * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7105068Sphk * and NAI Labs, the Security Research Division of Network Associates, Inc.
8105068Sphk * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9105068Sphk * DARPA CHATS research program.
10105068Sphk *
11105068Sphk * Redistribution and use in source and binary forms, with or without
12105068Sphk * modification, are permitted provided that the following conditions
13105068Sphk * are met:
14105068Sphk * 1. Redistributions of source code must retain the above copyright
15105068Sphk *    notice, this list of conditions and the following disclaimer.
16105068Sphk * 2. Redistributions in binary form must reproduce the above copyright
17105068Sphk *    notice, this list of conditions and the following disclaimer in the
18105068Sphk *    documentation and/or other materials provided with the distribution.
19105068Sphk * 3. The names of the authors may not be used to endorse or promote
20105068Sphk *    products derived from this software without specific prior written
21105068Sphk *    permission.
22105068Sphk *
23105068Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24105068Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25105068Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26105068Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27105068Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28105068Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29105068Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30105068Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31105068Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32105068Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33105068Sphk * SUCH DAMAGE.
34105068Sphk */
35105068Sphk
36116196Sobrien#include <sys/cdefs.h>
37116196Sobrien__FBSDID("$FreeBSD$");
38116196Sobrien
39105068Sphk#include "opt_geom.h"
40105068Sphk
41105068Sphk#include <sys/param.h>
42105068Sphk#include <sys/systm.h>
43105068Sphk#include <sys/kernel.h>
44105068Sphk#include <sys/sysctl.h>
45105068Sphk#include <sys/bio.h>
46105068Sphk#include <sys/conf.h>
47105068Sphk#include <sys/disk.h>
48105068Sphk#include <sys/malloc.h>
49105068Sphk#include <sys/sysctl.h>
50113892Sphk#include <sys/sbuf.h>
51105068Sphk
52105068Sphk#include <sys/lock.h>
53105068Sphk#include <sys/mutex.h>
54112511Sphk
55112511Sphk#include <vm/vm.h>
56112511Sphk#include <vm/vm_extern.h>
57112511Sphk
58105068Sphk#include <geom/geom.h>
59105068Sphk#include <geom/geom_int.h>
60112709Sphk#define GCTL_TABLE 1
61112511Sphk#include <geom/geom_ctl.h>
62105068Sphk
63113892Sphk#include <machine/stdarg.h>
64113892Sphk
65105068Sphkstatic d_ioctl_t g_ctl_ioctl;
66105068Sphk
67112534Sphkstatic struct cdevsw g_ctl_cdevsw = {
68126080Sphk	.d_version =	D_VERSION,
69126080Sphk	.d_flags =	D_NEEDGIANT,
70112534Sphk	.d_ioctl =	g_ctl_ioctl,
71112534Sphk	.d_name =	"g_ctl",
72105068Sphk};
73105068Sphk
74112534Sphkvoid
75105068Sphkg_ctl_init(void)
76105068Sphk{
77105068Sphk
78216952Skib	make_dev_credf(MAKEDEV_ETERNAL, &g_ctl_cdevsw, 0, NULL,
79112534Sphk	    UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
80112709Sphk	KASSERT(GCTL_PARAM_RD == VM_PROT_READ,
81112709Sphk		("GCTL_PARAM_RD != VM_PROT_READ"));
82112709Sphk	KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE,
83112709Sphk		("GCTL_PARAM_WR != VM_PROT_WRITE"));
84105068Sphk}
85105068Sphk
86112511Sphk/*
87112511Sphk * Report an error back to the user in ascii format.  Return whatever copyout
88112511Sphk * returned, or EINVAL if it succeeded.
89112511Sphk */
90112709Sphkint
91113892Sphkgctl_error(struct gctl_req *req, const char *fmt, ...)
92112511Sphk{
93113892Sphk	va_list ap;
94112511Sphk
95115624Sphk	if (req == NULL)
96115624Sphk		return (EINVAL);
97115624Sphk
98115624Sphk	/* We only record the first error */
99144789Spjd	if (sbuf_done(req->serror)) {
100144789Spjd		if (!req->nerror)
101144789Spjd			req->nerror = EEXIST;
102144789Spjd	}
103115624Sphk	if (req->nerror)
104115624Sphk		return (req->nerror);
105115624Sphk
106115624Sphk	va_start(ap, fmt);
107115624Sphk	sbuf_vprintf(req->serror, fmt, ap);
108115949Sphk	va_end(ap);
109115624Sphk	sbuf_finish(req->serror);
110115624Sphk	if (g_debugflags & G_F_CTLDUMP)
111115624Sphk		printf("gctl %p error \"%s\"\n", req, sbuf_data(req->serror));
112144789Spjd	return (0);
113112511Sphk}
114112511Sphk
115112511Sphk/*
116112511Sphk * Allocate space and copyin() something.
117112511Sphk * XXX: this should really be a standard function in the kernel.
118112511Sphk */
119112511Sphkstatic void *
120115624Sphkgeom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len)
121112511Sphk{
122112511Sphk	void *ptr;
123112511Sphk
124112511Sphk	ptr = g_malloc(len, M_WAITOK);
125112511Sphk	if (ptr == NULL)
126115624Sphk		req->nerror = ENOMEM;
127112511Sphk	else
128115624Sphk		req->nerror = copyin(uaddr, ptr, len);
129115624Sphk	if (!req->nerror)
130112511Sphk		return (ptr);
131112511Sphk	if (ptr != NULL)
132112511Sphk		g_free(ptr);
133112511Sphk	return (NULL);
134112511Sphk}
135112511Sphk
136115624Sphkstatic void
137112709Sphkgctl_copyin(struct gctl_req *req)
138112511Sphk{
139115624Sphk	int error, i;
140112709Sphk	struct gctl_req_arg *ap;
141112511Sphk	char *p;
142112511Sphk
143115624Sphk	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap));
144112709Sphk	if (ap == NULL) {
145115624Sphk		req->nerror = ENOMEM;
146115624Sphk		req->arg = NULL;
147115624Sphk		return;
148112709Sphk	}
149112709Sphk
150115624Sphk	/* Nothing have been copyin()'ed yet */
151115624Sphk	for (i = 0; i < req->narg; i++) {
152115624Sphk		ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL);
153115624Sphk		ap[i].flag &= ~GCTL_PARAM_CHANGED;
154115624Sphk		ap[i].kvalue = NULL;
155115624Sphk	}
156115624Sphk
157115624Sphk	error = 0;
158115624Sphk	for (i = 0; i < req->narg; i++) {
159112709Sphk		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
160115624Sphk			error = gctl_error(req,
161115624Sphk			    "wrong param name length %d: %d", i, ap[i].nlen);
162112511Sphk			break;
163112709Sphk		}
164115624Sphk		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen);
165112709Sphk		if (p == NULL)
166112511Sphk			break;
167112709Sphk		if (p[ap[i].nlen - 1] != '\0') {
168112709Sphk			error = gctl_error(req, "unterminated param name");
169112511Sphk			g_free(p);
170112511Sphk			break;
171112511Sphk		}
172112709Sphk		ap[i].name = p;
173115624Sphk		ap[i].flag |= GCTL_PARAM_NAMEKERNEL;
174140367Sphk		if (ap[i].len <= 0) {
175115624Sphk			error = gctl_error(req, "negative param length");
176115624Sphk			break;
177115624Sphk		}
178115624Sphk		p = geom_alloc_copyin(req, ap[i].value, ap[i].len);
179115624Sphk		if (p == NULL)
180115624Sphk			break;
181115624Sphk		if ((ap[i].flag & GCTL_PARAM_ASCII) &&
182115624Sphk		    p[ap[i].len - 1] != '\0') {
183115624Sphk			error = gctl_error(req, "unterminated param value");
184115624Sphk			g_free(p);
185115624Sphk			break;
186115624Sphk		}
187115624Sphk		ap[i].kvalue = p;
188115624Sphk		ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
189112511Sphk	}
190115624Sphk	req->arg = ap;
191115624Sphk	return;
192115624Sphk}
193115624Sphk
194115624Sphkstatic void
195115624Sphkgctl_copyout(struct gctl_req *req)
196115624Sphk{
197115624Sphk	int error, i;
198115624Sphk	struct gctl_req_arg *ap;
199115624Sphk
200115624Sphk	if (req->nerror)
201115624Sphk		return;
202115624Sphk	error = 0;
203115624Sphk	ap = req->arg;
204115624Sphk	for (i = 0; i < req->narg; i++, ap++) {
205115624Sphk		if (!(ap->flag & GCTL_PARAM_CHANGED))
206115624Sphk			continue;
207115624Sphk		error = copyout(ap->kvalue, ap->value, ap->len);
208115624Sphk		if (!error)
209115624Sphk			continue;
210115624Sphk		req->nerror = error;
211115624Sphk		return;
212112511Sphk	}
213115624Sphk	return;
214112511Sphk}
215112511Sphk
216112511Sphkstatic void
217114531Sphkgctl_free(struct gctl_req *req)
218114531Sphk{
219114531Sphk	int i;
220114531Sphk
221115624Sphk	if (req->arg == NULL)
222115624Sphk		return;
223114531Sphk	for (i = 0; i < req->narg; i++) {
224115624Sphk		if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL)
225114531Sphk			g_free(req->arg[i].name);
226115624Sphk		if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) &&
227115624Sphk		    req->arg[i].len > 0)
228115624Sphk			g_free(req->arg[i].kvalue);
229114531Sphk	}
230114531Sphk	g_free(req->arg);
231115624Sphk	sbuf_delete(req->serror);
232114531Sphk}
233114531Sphk
234114531Sphkstatic void
235112709Sphkgctl_dump(struct gctl_req *req)
236112511Sphk{
237112511Sphk	u_int i;
238115624Sphk	int j;
239112709Sphk	struct gctl_req_arg *ap;
240112511Sphk
241115624Sphk	printf("Dump of gctl request at %p:\n", req);
242115624Sphk	if (req->nerror > 0) {
243115624Sphk		printf("  nerror:\t%d\n", req->nerror);
244115624Sphk		if (sbuf_len(req->serror) > 0)
245115624Sphk			printf("  error:\t\"%s\"\n", sbuf_data(req->serror));
246112511Sphk	}
247112511Sphk	for (i = 0; i < req->narg; i++) {
248112511Sphk		ap = &req->arg[i];
249115624Sphk		if (!(ap->flag & GCTL_PARAM_NAMEKERNEL))
250115624Sphk			printf("  param:\t%d@%p", ap->nlen, ap->name);
251115624Sphk		else
252115624Sphk			printf("  param:\t\"%s\"", ap->name);
253112709Sphk		printf(" [%s%s%d] = ",
254112709Sphk		    ap->flag & GCTL_PARAM_RD ? "R" : "",
255112709Sphk		    ap->flag & GCTL_PARAM_WR ? "W" : "",
256112709Sphk		    ap->len);
257115624Sphk		if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) {
258115624Sphk			printf(" =@ %p", ap->value);
259115624Sphk		} else if (ap->flag & GCTL_PARAM_ASCII) {
260115624Sphk			printf("\"%s\"", (char *)ap->kvalue);
261112511Sphk		} else if (ap->len > 0) {
262117150Sphk			for (j = 0; j < ap->len && j < 512; j++)
263115624Sphk				printf(" %02x", ((u_char *)ap->kvalue)[j]);
264112511Sphk		} else {
265115624Sphk			printf(" = %p", ap->kvalue);
266112511Sphk		}
267112511Sphk		printf("\n");
268112511Sphk	}
269112511Sphk}
270112511Sphk
271157581Smarcelint
272157581Smarcelgctl_set_param(struct gctl_req *req, const char *param, void const *ptr,
273157581Smarcel    int len)
274114670Sphk{
275115624Sphk	int i;
276114670Sphk	struct gctl_req_arg *ap;
277114670Sphk
278114670Sphk	for (i = 0; i < req->narg; i++) {
279114670Sphk		ap = &req->arg[i];
280114670Sphk		if (strcmp(param, ap->name))
281114670Sphk			continue;
282157581Smarcel		if (!(ap->flag & GCTL_PARAM_WR))
283157581Smarcel			return (EPERM);
284157581Smarcel		ap->flag |= GCTL_PARAM_CHANGED;
285115624Sphk		if (ap->len < len) {
286157581Smarcel			bcopy(ptr, ap->kvalue, ap->len);
287157581Smarcel			return (ENOSPC);
288114670Sphk		}
289115624Sphk		bcopy(ptr, ap->kvalue, len);
290157581Smarcel		return (0);
291114670Sphk	}
292157581Smarcel	return (EINVAL);
293114670Sphk}
294114670Sphk
295157581Smarcelvoid
296157581Smarcelgctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr,
297157581Smarcel    int len)
298157581Smarcel{
299157581Smarcel
300157581Smarcel	switch (gctl_set_param(req, param, ptr, len)) {
301157581Smarcel	case EPERM:
302157581Smarcel		gctl_error(req, "No write access %s argument", param);
303157581Smarcel		break;
304157581Smarcel	case ENOSPC:
305157581Smarcel		gctl_error(req, "Wrong length %s argument", param);
306157581Smarcel		break;
307157581Smarcel	case EINVAL:
308157581Smarcel		gctl_error(req, "Missing %s argument", param);
309157581Smarcel		break;
310157581Smarcel	}
311157581Smarcel}
312157581Smarcel
313112709Sphkvoid *
314112709Sphkgctl_get_param(struct gctl_req *req, const char *param, int *len)
315112709Sphk{
316115624Sphk	int i;
317112709Sphk	void *p;
318112709Sphk	struct gctl_req_arg *ap;
319112709Sphk
320112709Sphk	for (i = 0; i < req->narg; i++) {
321112709Sphk		ap = &req->arg[i];
322112709Sphk		if (strcmp(param, ap->name))
323112709Sphk			continue;
324112709Sphk		if (!(ap->flag & GCTL_PARAM_RD))
325112709Sphk			continue;
326115624Sphk		p = ap->kvalue;
327112709Sphk		if (len != NULL)
328115624Sphk			*len = ap->len;
329112709Sphk		return (p);
330112709Sphk	}
331112709Sphk	return (NULL);
332112709Sphk}
333112709Sphk
334115624Sphkchar const *
335115624Sphkgctl_get_asciiparam(struct gctl_req *req, const char *param)
336115624Sphk{
337115624Sphk	int i;
338115624Sphk	char const *p;
339115624Sphk	struct gctl_req_arg *ap;
340115624Sphk
341115624Sphk	for (i = 0; i < req->narg; i++) {
342115624Sphk		ap = &req->arg[i];
343115624Sphk		if (strcmp(param, ap->name))
344115624Sphk			continue;
345115624Sphk		if (!(ap->flag & GCTL_PARAM_RD))
346115624Sphk			continue;
347115624Sphk		p = ap->kvalue;
348115624Sphk		if (ap->len < 1) {
349115624Sphk			gctl_error(req, "No length argument (%s)", param);
350115624Sphk			return (NULL);
351115624Sphk		}
352115624Sphk		if (p[ap->len - 1] != '\0') {
353115624Sphk			gctl_error(req, "Unterminated argument (%s)", param);
354115624Sphk			return (NULL);
355115624Sphk		}
356115624Sphk		return (p);
357115624Sphk	}
358115624Sphk	return (NULL);
359115624Sphk}
360115624Sphk
361113893Sphkvoid *
362113893Sphkgctl_get_paraml(struct gctl_req *req, const char *param, int len)
363113893Sphk{
364113893Sphk	int i;
365113893Sphk	void *p;
366113893Sphk
367113893Sphk	p = gctl_get_param(req, param, &i);
368113893Sphk	if (p == NULL)
369113893Sphk		gctl_error(req, "Missing %s argument", param);
370113893Sphk	else if (i != len) {
371113893Sphk		p = NULL;
372113893Sphk		gctl_error(req, "Wrong length %s argument", param);
373113893Sphk	}
374113893Sphk	return (p);
375113893Sphk}
376113893Sphk
377115624Sphkstruct g_class *
378115624Sphkgctl_get_class(struct gctl_req *req, char const *arg)
379112709Sphk{
380115624Sphk	char const *p;
381112709Sphk	struct g_class *cp;
382112709Sphk
383115624Sphk	p = gctl_get_asciiparam(req, arg);
384112709Sphk	if (p == NULL)
385112709Sphk		return (NULL);
386112709Sphk	LIST_FOREACH(cp, &g_classes, class) {
387115624Sphk		if (!strcmp(p, cp->name))
388112709Sphk			return (cp);
389112709Sphk	}
390112709Sphk	return (NULL);
391112709Sphk}
392112709Sphk
393115624Sphkstruct g_geom *
394115624Sphkgctl_get_geom(struct gctl_req *req, struct g_class *mpr, char const *arg)
395112709Sphk{
396115624Sphk	char const *p;
397112709Sphk	struct g_class *mp;
398112709Sphk	struct g_geom *gp;
399112709Sphk
400115624Sphk	p = gctl_get_asciiparam(req, arg);
401168052Sdelphij	if (p == NULL)
402168052Sdelphij		return (NULL);
403168052Sdelphij	LIST_FOREACH(mp, &g_classes, class) {
404168052Sdelphij		if (mpr != NULL && mpr != mp)
405168052Sdelphij			continue;
406168052Sdelphij		LIST_FOREACH(gp, &mp->geom, geom) {
407168052Sdelphij			if (!strcmp(p, gp->name))
408168052Sdelphij				return (gp);
409112709Sphk		}
410112709Sphk	}
411168052Sdelphij	gctl_error(req, "Geom not found: \"%s\"", p);
412112709Sphk	return (NULL);
413112709Sphk}
414112709Sphk
415115624Sphkstruct g_provider *
416115624Sphkgctl_get_provider(struct gctl_req *req, char const *arg)
417112709Sphk{
418115624Sphk	char const *p;
419112709Sphk	struct g_provider *pp;
420112709Sphk
421115624Sphk	p = gctl_get_asciiparam(req, arg);
422112709Sphk	if (p == NULL)
423112709Sphk		return (NULL);
424115850Sphk	pp = g_provider_by_name(p);
425115850Sphk	if (pp != NULL)
426115850Sphk		return (pp);
427168052Sdelphij	gctl_error(req, "Provider not found: \"%s\"", p);
428112709Sphk	return (NULL);
429112709Sphk}
430112709Sphk
431115624Sphkstatic void
432115624Sphkg_ctl_req(void *arg, int flag __unused)
433112709Sphk{
434112709Sphk	struct g_class *mp;
435115624Sphk	struct gctl_req *req;
436115624Sphk	char const *verb;
437112709Sphk
438112709Sphk	g_topology_assert();
439115624Sphk	req = arg;
440115624Sphk	mp = gctl_get_class(req, "class");
441115726Sphk	if (mp == NULL) {
442115726Sphk		gctl_error(req, "Class not found");
443115624Sphk		return;
444115726Sphk	}
445150304Smarcel	if (mp->ctlreq == NULL) {
446150304Smarcel		gctl_error(req, "Class takes no requests");
447150304Smarcel		return;
448150304Smarcel	}
449115624Sphk	verb = gctl_get_param(req, "verb", NULL);
450150304Smarcel	if (verb == NULL) {
451150304Smarcel		gctl_error(req, "Verb missing");
452150304Smarcel		return;
453150304Smarcel	}
454150304Smarcel	mp->ctlreq(req, mp, verb);
455112709Sphk	g_topology_assert();
456112709Sphk}
457112709Sphk
458112709Sphk
459113876Sphkstatic int
460130585Sphkg_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
461112511Sphk{
462112709Sphk	struct gctl_req *req;
463136839Sphk	int nerror;
464112511Sphk
465112511Sphk	req = (void *)data;
466115624Sphk	req->nerror = 0;
467181463Sdes	req->serror = sbuf_new_auto();
468112709Sphk	/* It is an error if we cannot return an error text */
469115624Sphk	if (req->lerror < 2)
470112511Sphk		return (EINVAL);
471112709Sphk	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
472112709Sphk		return (EINVAL);
473112709Sphk
474112709Sphk	/* Check the version */
475112709Sphk	if (req->version != GCTL_VERSION)
476112709Sphk		return (gctl_error(req,
477112709Sphk		    "kernel and libgeom version mismatch."));
478112709Sphk
479112709Sphk	/* Get things on board */
480115624Sphk	gctl_copyin(req);
481112709Sphk
482112876Sphk	if (g_debugflags & G_F_CTLDUMP)
483112876Sphk		gctl_dump(req);
484115624Sphk
485115624Sphk	if (!req->nerror) {
486115624Sphk		g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL);
487115624Sphk		gctl_copyout(req);
488112709Sphk	}
489144789Spjd	if (sbuf_done(req->serror)) {
490144789Spjd		req->nerror = copyout(sbuf_data(req->serror), req->error,
491144789Spjd		    imin(req->lerror, sbuf_len(req->serror) + 1));
492144789Spjd	}
493115624Sphk
494136839Sphk	nerror = req->nerror;
495114531Sphk	gctl_free(req);
496136839Sphk	return (nerror);
497112511Sphk}
498112511Sphk
499112511Sphkstatic int
500130585Sphkg_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
501105068Sphk{
502105068Sphk	int error;
503105068Sphk
504105068Sphk	switch(cmd) {
505112511Sphk	case GEOM_CTL:
506112511Sphk		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
507112511Sphk		break;
508105068Sphk	default:
509115624Sphk		error = ENOIOCTL;
510105068Sphk		break;
511105068Sphk	}
512105068Sphk	return (error);
513105068Sphk
514105068Sphk}
515