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
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: stable/11/sys/geom/geom_ctl.c 344584 2019-02-26 15:06:44Z markj $");
38
39#include "opt_geom.h"
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/sysctl.h>
45#include <sys/bio.h>
46#include <sys/conf.h>
47#include <sys/disk.h>
48#include <sys/malloc.h>
49#include <sys/sysctl.h>
50#include <sys/sbuf.h>
51
52#include <sys/lock.h>
53#include <sys/mutex.h>
54
55#include <vm/vm.h>
56#include <vm/vm_extern.h>
57
58#include <geom/geom.h>
59#include <geom/geom_int.h>
60#define GCTL_TABLE 1
61#include <geom/geom_ctl.h>
62
63#include <machine/stdarg.h>
64
65static d_ioctl_t g_ctl_ioctl;
66
67static struct cdevsw g_ctl_cdevsw = {
68	.d_version =	D_VERSION,
69	.d_flags =	D_NEEDGIANT,
70	.d_ioctl =	g_ctl_ioctl,
71	.d_name =	"g_ctl",
72};
73
74void
75g_ctl_init(void)
76{
77
78	make_dev_credf(MAKEDEV_ETERNAL, &g_ctl_cdevsw, 0, NULL,
79	    UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
80	KASSERT(GCTL_PARAM_RD == VM_PROT_READ,
81		("GCTL_PARAM_RD != VM_PROT_READ"));
82	KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE,
83		("GCTL_PARAM_WR != VM_PROT_WRITE"));
84}
85
86/*
87 * Report an error back to the user in ascii format.  Return nerror
88 * or EINVAL if nerror isn't specified.
89 */
90int
91gctl_error(struct gctl_req *req, const char *fmt, ...)
92{
93	va_list ap;
94
95	if (req == NULL)
96		return (EINVAL);
97
98	/* We only record the first error */
99	if (sbuf_done(req->serror)) {
100		if (!req->nerror)
101			req->nerror = EEXIST;
102		return (req->nerror);
103	}
104	if (!req->nerror)
105		req->nerror = EINVAL;
106
107	va_start(ap, fmt);
108	sbuf_vprintf(req->serror, fmt, ap);
109	va_end(ap);
110	sbuf_finish(req->serror);
111	if (g_debugflags & G_F_CTLDUMP)
112		printf("gctl %p error \"%s\"\n", req, sbuf_data(req->serror));
113	return (req->nerror);
114}
115
116/*
117 * Allocate space and copyin() something.
118 * XXX: this should really be a standard function in the kernel.
119 */
120static void *
121geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len)
122{
123	void *ptr;
124
125	ptr = g_malloc(len, M_WAITOK);
126	req->nerror = copyin(uaddr, ptr, len);
127	if (!req->nerror)
128		return (ptr);
129	g_free(ptr);
130	return (NULL);
131}
132
133static void
134gctl_copyin(struct gctl_req *req)
135{
136	struct gctl_req_arg *ap;
137	char *p;
138	u_int i;
139
140	if (req->narg > GEOM_CTL_ARG_MAX) {
141		gctl_error(req, "too many arguments");
142		req->arg = NULL;
143		return;
144	}
145
146	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap));
147	if (ap == NULL) {
148		gctl_error(req, "bad control request");
149		req->arg = NULL;
150		return;
151	}
152
153	/* Nothing have been copyin()'ed yet */
154	for (i = 0; i < req->narg; i++) {
155		ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL);
156		ap[i].flag &= ~GCTL_PARAM_CHANGED;
157		ap[i].kvalue = NULL;
158	}
159
160	for (i = 0; i < req->narg; i++) {
161		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
162			gctl_error(req,
163			    "wrong param name length %d: %d", i, ap[i].nlen);
164			break;
165		}
166		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen);
167		if (p == NULL)
168			break;
169		if (p[ap[i].nlen - 1] != '\0') {
170			gctl_error(req, "unterminated param name");
171			g_free(p);
172			break;
173		}
174		ap[i].name = p;
175		ap[i].flag |= GCTL_PARAM_NAMEKERNEL;
176		if (ap[i].len <= 0) {
177			gctl_error(req, "negative param length");
178			break;
179		}
180		p = geom_alloc_copyin(req, ap[i].value, ap[i].len);
181		if (p == NULL)
182			break;
183		if ((ap[i].flag & GCTL_PARAM_ASCII) &&
184		    p[ap[i].len - 1] != '\0') {
185			gctl_error(req, "unterminated param value");
186			g_free(p);
187			break;
188		}
189		ap[i].kvalue = p;
190		ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
191	}
192	req->arg = ap;
193	return;
194}
195
196static void
197gctl_copyout(struct gctl_req *req)
198{
199	int error, i;
200	struct gctl_req_arg *ap;
201
202	if (req->nerror)
203		return;
204	error = 0;
205	ap = req->arg;
206	for (i = 0; i < req->narg; i++, ap++) {
207		if (!(ap->flag & GCTL_PARAM_CHANGED))
208			continue;
209		error = copyout(ap->kvalue, ap->value, ap->len);
210		if (!error)
211			continue;
212		req->nerror = error;
213		return;
214	}
215	return;
216}
217
218static void
219gctl_free(struct gctl_req *req)
220{
221	u_int i;
222
223	sbuf_delete(req->serror);
224	if (req->arg == NULL)
225		return;
226	for (i = 0; i < req->narg; i++) {
227		if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL)
228			g_free(req->arg[i].name);
229		if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) &&
230		    req->arg[i].len > 0)
231			g_free(req->arg[i].kvalue);
232	}
233	g_free(req->arg);
234}
235
236static void
237gctl_dump(struct gctl_req *req)
238{
239	struct gctl_req_arg *ap;
240	u_int i;
241	int j;
242
243	printf("Dump of gctl request at %p:\n", req);
244	if (req->nerror > 0) {
245		printf("  nerror:\t%d\n", req->nerror);
246		if (sbuf_len(req->serror) > 0)
247			printf("  error:\t\"%s\"\n", sbuf_data(req->serror));
248	}
249	if (req->arg == NULL)
250		return;
251	for (i = 0; i < req->narg; i++) {
252		ap = &req->arg[i];
253		if (!(ap->flag & GCTL_PARAM_NAMEKERNEL))
254			printf("  param:\t%d@%p", ap->nlen, ap->name);
255		else
256			printf("  param:\t\"%s\"", ap->name);
257		printf(" [%s%s%d] = ",
258		    ap->flag & GCTL_PARAM_RD ? "R" : "",
259		    ap->flag & GCTL_PARAM_WR ? "W" : "",
260		    ap->len);
261		if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) {
262			printf(" =@ %p", ap->value);
263		} else if (ap->flag & GCTL_PARAM_ASCII) {
264			printf("\"%s\"", (char *)ap->kvalue);
265		} else if (ap->len > 0) {
266			for (j = 0; j < ap->len && j < 512; j++)
267				printf(" %02x", ((u_char *)ap->kvalue)[j]);
268		} else {
269			printf(" = %p", ap->kvalue);
270		}
271		printf("\n");
272	}
273}
274
275int
276gctl_set_param(struct gctl_req *req, const char *param, void const *ptr,
277    int len)
278{
279	u_int i;
280	struct gctl_req_arg *ap;
281
282	for (i = 0; i < req->narg; i++) {
283		ap = &req->arg[i];
284		if (strcmp(param, ap->name))
285			continue;
286		if (!(ap->flag & GCTL_PARAM_WR))
287			return (EPERM);
288		ap->flag |= GCTL_PARAM_CHANGED;
289		if (ap->len < len) {
290			bcopy(ptr, ap->kvalue, ap->len);
291			return (ENOSPC);
292		}
293		bcopy(ptr, ap->kvalue, len);
294		return (0);
295	}
296	return (EINVAL);
297}
298
299void
300gctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr,
301    int len)
302{
303
304	switch (gctl_set_param(req, param, ptr, len)) {
305	case EPERM:
306		gctl_error(req, "No write access %s argument", param);
307		break;
308	case ENOSPC:
309		gctl_error(req, "Wrong length %s argument", param);
310		break;
311	case EINVAL:
312		gctl_error(req, "Missing %s argument", param);
313		break;
314	}
315}
316
317void *
318gctl_get_param(struct gctl_req *req, const char *param, int *len)
319{
320	u_int i;
321	void *p;
322	struct gctl_req_arg *ap;
323
324	for (i = 0; i < req->narg; i++) {
325		ap = &req->arg[i];
326		if (strcmp(param, ap->name))
327			continue;
328		if (!(ap->flag & GCTL_PARAM_RD))
329			continue;
330		p = ap->kvalue;
331		if (len != NULL)
332			*len = ap->len;
333		return (p);
334	}
335	return (NULL);
336}
337
338char const *
339gctl_get_asciiparam(struct gctl_req *req, const char *param)
340{
341	u_int i;
342	char const *p;
343	struct gctl_req_arg *ap;
344
345	for (i = 0; i < req->narg; i++) {
346		ap = &req->arg[i];
347		if (strcmp(param, ap->name))
348			continue;
349		if (!(ap->flag & GCTL_PARAM_RD))
350			continue;
351		p = ap->kvalue;
352		if (ap->len < 1) {
353			gctl_error(req, "No length argument (%s)", param);
354			return (NULL);
355		}
356		if (p[ap->len - 1] != '\0') {
357			gctl_error(req, "Unterminated argument (%s)", param);
358			return (NULL);
359		}
360		return (p);
361	}
362	return (NULL);
363}
364
365void *
366gctl_get_paraml(struct gctl_req *req, const char *param, int len)
367{
368	int i;
369	void *p;
370
371	p = gctl_get_param(req, param, &i);
372	if (p == NULL)
373		gctl_error(req, "Missing %s argument", param);
374	else if (i != len) {
375		p = NULL;
376		gctl_error(req, "Wrong length %s argument", param);
377	}
378	return (p);
379}
380
381struct g_class *
382gctl_get_class(struct gctl_req *req, char const *arg)
383{
384	char const *p;
385	struct g_class *cp;
386
387	p = gctl_get_asciiparam(req, arg);
388	if (p == NULL)
389		return (NULL);
390	LIST_FOREACH(cp, &g_classes, class) {
391		if (!strcmp(p, cp->name))
392			return (cp);
393	}
394	return (NULL);
395}
396
397struct g_geom *
398gctl_get_geom(struct gctl_req *req, struct g_class *mpr, char const *arg)
399{
400	char const *p;
401	struct g_class *mp;
402	struct g_geom *gp;
403
404	p = gctl_get_asciiparam(req, arg);
405	if (p == NULL)
406		return (NULL);
407	LIST_FOREACH(mp, &g_classes, class) {
408		if (mpr != NULL && mpr != mp)
409			continue;
410		LIST_FOREACH(gp, &mp->geom, geom) {
411			if (!strcmp(p, gp->name))
412				return (gp);
413		}
414	}
415	gctl_error(req, "Geom not found: \"%s\"", p);
416	return (NULL);
417}
418
419struct g_provider *
420gctl_get_provider(struct gctl_req *req, char const *arg)
421{
422	char const *p;
423	struct g_provider *pp;
424
425	p = gctl_get_asciiparam(req, arg);
426	if (p == NULL)
427		return (NULL);
428	pp = g_provider_by_name(p);
429	if (pp != NULL)
430		return (pp);
431	gctl_error(req, "Provider not found: \"%s\"", p);
432	return (NULL);
433}
434
435static void
436g_ctl_req(void *arg, int flag __unused)
437{
438	struct g_class *mp;
439	struct gctl_req *req;
440	char const *verb;
441
442	g_topology_assert();
443	req = arg;
444	mp = gctl_get_class(req, "class");
445	if (mp == NULL) {
446		gctl_error(req, "Class not found");
447		return;
448	}
449	if (mp->ctlreq == NULL) {
450		gctl_error(req, "Class takes no requests");
451		return;
452	}
453	verb = gctl_get_param(req, "verb", NULL);
454	if (verb == NULL) {
455		gctl_error(req, "Verb missing");
456		return;
457	}
458	mp->ctlreq(req, mp, verb);
459	g_topology_assert();
460}
461
462
463static int
464g_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
465{
466	struct gctl_req *req;
467	int nerror;
468
469	req = (void *)data;
470	req->nerror = 0;
471	/* It is an error if we cannot return an error text */
472	if (req->lerror < 2)
473		return (EINVAL);
474	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
475		return (EINVAL);
476
477	req->serror = sbuf_new_auto();
478	/* Check the version */
479	if (req->version != GCTL_VERSION) {
480		gctl_error(req, "kernel and libgeom version mismatch.");
481		req->arg = NULL;
482	} else {
483		/* Get things on board */
484		gctl_copyin(req);
485
486		if (g_debugflags & G_F_CTLDUMP)
487			gctl_dump(req);
488
489		if (!req->nerror) {
490			g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL);
491			gctl_copyout(req);
492		}
493	}
494	if (sbuf_done(req->serror)) {
495		copyout(sbuf_data(req->serror), req->error,
496		    imin(req->lerror, sbuf_len(req->serror) + 1));
497	}
498
499	nerror = req->nerror;
500	gctl_free(req);
501	return (nerror);
502}
503
504static int
505g_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
506{
507	int error;
508
509	switch(cmd) {
510	case GEOM_CTL:
511		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
512		break;
513	default:
514		error = ENOIOCTL;
515		break;
516	}
517	return (error);
518
519}
520