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