xref: /plan9-contrib/sys/src/9/port/devfs.c (revision 3f9c83932f326ae8b6d81b36429957bc06a9813e)
1 /*
2  * File system devices.
3  * Follows device config in Ken's file server.
4  * Builds mirrors, concatenations, interleavings, and partitions
5  * of devices out of other (inner) devices.
6  */
7 
8 #include "u.h"
9 #include "../port/lib.h"
10 #include "mem.h"
11 #include "dat.h"
12 #include "fns.h"
13 #include "io.h"
14 #include "ureg.h"
15 #include "../port/error.h"
16 
17 enum {
18 	Fmirror,		/* mirror of others */
19 	Fcat,			/* catenation of others */
20 	Finter,			/* interleaving of others */
21 	Fpart,			/* part of others */
22 	Fclear,			/* start over */
23 
24 	Blksize	= 8*1024,	/* for Finter only */
25 
26 	Qtop	= 0,		/* top dir (contains "fs") */
27 	Qdir,			/* actual dir */
28 	Qctl,			/* ctl file */
29 	Qfirst,			/* first fs file */
30 
31 	/* tunable parameters */
32 	Maxconf	= 4*1024,	/* max length for config */
33 	Ndevs	= 32,		/* max. inner devs per command */
34 	Nfsdevs = 128,		/* max. created devs, total */
35 };
36 
37 #define	Cfgstr	"fsdev:\n"
38 
39 typedef struct Fsdev Fsdev;
40 struct Fsdev
41 {
42 	int	type;
43 	char	*name;		/* name for this fsdev */
44 	vlong	start;		/* start address (for Fpart) */
45 	vlong	size;		/* min(idev sizes) */
46 	int	ndevs;		/* number of inner devices */
47 	char	*iname[Ndevs];	/* inner device names */
48 	Chan	*idev[Ndevs];	/* inner devices */
49 	vlong	isize[Ndevs];	/* sizes for inner devices */
50 };
51 
52 extern Dev fsdevtab;		/* forward */
53 
54 /*
55  * Once configured, a fsdev is never removed.  The name of those
56  * configured is never nil.  We have no locks here.
57  */
58 static Fsdev	fsdev[Nfsdevs];
59 
60 static Qid	tqid = {Qtop, 0, QTDIR};
61 static Qid	dqid = {Qdir, 0, QTDIR};
62 static Qid	cqid = {Qctl, 0, 0};
63 
64 static Cmdtab configs[] = {
65 	Fmirror,"mirror",	0,
66 	Fcat,	"cat",		0,
67 	Finter,	"inter",	0,
68 	Fpart,	"part",		5,
69 	Fclear,	"clear",	1,
70 };
71 
72 static char	confstr[Maxconf];
73 static int	configed;
74 
75 
76 static Fsdev*
77 path2dev(int i, int mustexist)
78 {
79 	if (i < 0 || i >= nelem(fsdev))
80 		error("bug: bad index in devfsdev");
81 	if (mustexist && fsdev[i].name == nil)
82 		error(Enonexist);
83 
84 	if (fsdev[i].name == nil)
85 		return nil;
86 	else
87 		return &fsdev[i];
88 }
89 
90 static Fsdev*
91 devalloc(void)
92 {
93 	int	i;
94 
95 	for (i = 0; i < nelem(fsdev); i++)
96 		if (fsdev[i].name == nil)
97 			break;
98 	if (i == nelem(fsdev))
99 		error(Enodev);
100 
101 	return &fsdev[i];
102 }
103 
104 static void
105 setdsize(Fsdev* mp)
106 {
107 	int	i;
108 	long	l;
109 	uchar	buf[128];	/* old DIRLEN plus a little should be plenty */
110 	Chan	*mc;
111 	Dir	d;
112 
113 	if (mp->type != Fpart){
114 		mp->start= 0;
115 		mp->size = 0;
116 	}
117 	for (i = 0; i < mp->ndevs; i++){
118 		mc = mp->idev[i];
119 		l = devtab[mc->type]->stat(mc, buf, sizeof(buf));
120 		convM2D(buf, l, &d, nil);
121 		mp->isize[i] = d.length;
122 		switch(mp->type){
123 		case Fmirror:
124 			if (mp->size == 0 || mp->size > d.length)
125 				mp->size = d.length;
126 			break;
127 		case Fcat:
128 			mp->size += d.length;
129 			break;
130 		case Finter:
131 			/* truncate to multiple of Blksize */
132 			d.length &= ~(Blksize-1);
133 			mp->isize[i] = d.length;
134 			mp->size += d.length;
135 			break;
136 		case Fpart:
137 			/* should raise errors here? */
138 			if (mp->start > d.length)
139 				mp->start = d.length;
140 			if (d.length < mp->start + mp->size)
141 				mp->size = d.length - mp->start;
142 			break;
143 		}
144 	}
145 }
146 
147 static void
148 mpshut(Fsdev *mp)
149 {
150 	int	i;
151 	char	*nm;
152 
153 	nm = mp->name;
154 	mp->name = nil;		/* prevent others from using this. */
155 	if (nm)
156 		free(nm);
157 	for (i = 0; i < mp->ndevs; i++){
158 		if (mp->idev[i] != nil)
159 			cclose(mp->idev[i]);
160 		if (mp->iname[i])
161 			free(mp->iname[i]);
162 	}
163 	memset(mp, 0, sizeof(*mp));
164 }
165 
166 
167 static void
168 mconfig(char* a, long n)	/* "name idev0 idev1" */
169 {
170 	int	i;
171 	vlong	size, start;
172 	char	*c, *oldc;
173 	Cmdbuf	*cb;
174 	Cmdtab	*ct;
175 	Fsdev	*mp;
176 	static	QLock	lck;
177 
178 	size = 0;
179 	start = 0;
180 	if (confstr[0] == 0)
181 		seprint(confstr, confstr + sizeof confstr, Cfgstr);
182 	mp = nil;
183 	cb = nil;
184 	oldc = confstr + strlen(confstr);
185 	if (*a == '\0' || *a == '#' || *a == '\n')
186 		return;
187 
188 	qlock(&lck);
189 	if (waserror()){
190 		*oldc = 0;
191 		if (mp != nil)
192 			mpshut(mp);
193 		qunlock(&lck);
194 		if (cb)
195 			free(cb);
196 		nexterror();
197 	}
198 
199 	cb = parsecmd(a, n);
200 	c = oldc;
201 	for (i = 0; i < cb->nf; i++)
202 		c = seprint(c, confstr + sizeof confstr, "%s ", cb->f[i]);
203 	if (c > confstr)
204 		c[-1] = '\n';
205 	ct = lookupcmd(cb, configs, nelem(configs));
206 	cb->f++;			/* skip command */
207 	cb->nf--;
208 	if (cb->nf < 0)			/* nothing to see here, move along */
209 		ct->index = -1;
210 	switch (ct->index) {
211 	case Fpart:
212 		if (cb->nf < 4)
213 			error("too few fields in fs config");
214 		start = strtoll(cb->f[2], nil, 10);
215 		size =  strtoll(cb->f[3], nil, 10);
216 		cb->nf -= 2;
217 		break;
218 	case Fclear:
219 		for (mp = fsdev; mp < fsdev + nelem(fsdev); mp++)
220 			mpshut(mp);
221 		*confstr = '\0';
222 		/* FALL THROUGH */
223 	case -1:
224 		poperror();
225 		qunlock(&lck);
226 		free(cb);
227 		return;
228 	}
229 	if (cb->nf < 2)
230 		error("too few fields in fs config");
231 
232 	/* reject name if already in use */
233 	for (i = 0; i < nelem(fsdev); i++)
234 		if (fsdev[i].name != nil && strcmp(fsdev[i].name, cb->f[0])==0)
235 			error(Eexist);
236 
237 	if (cb->nf - 1 > Ndevs)
238 		error("too many devices; fix me, increase Ndevs");
239 	for (i = 0; i < cb->nf; i++)
240 		validname(cb->f[i], (i != 0));
241 
242 	mp = devalloc();
243 	mp->type = ct->index;
244 	if (mp->type == Fpart){
245 		mp->start = start;
246 		mp->size = size;
247 	}
248 	kstrdup(&mp->name, cb->f[0]);
249 	for (i = 1; i < cb->nf; i++){
250 		kstrdup(&mp->iname[i-1], cb->f[i]);
251 		mp->idev[i-1] = namec(mp->iname[i-1], Aopen, ORDWR, 0);
252 		if (mp->idev[i-1] == nil)
253 			error(Egreg);
254 		mp->ndevs++;
255 	}
256 	setdsize(mp);
257 	configed = 1;
258 
259 	poperror();
260 	qunlock(&lck);
261 	free(cb);
262 }
263 
264 static void
265 rdconf(void)
266 {
267 	int mustrd;
268 	char *c, *e, *p, *s;
269 	Chan *cc;
270 	Chan **ccp;
271 
272 	s = getconf("fsconfig");
273 	if (s == nil){
274 		mustrd = 0;
275 		s = "/dev/sdC0/fscfg";
276 	} else
277 		mustrd = 1;
278 	ccp = &cc;
279 	*ccp = nil;
280 	c = nil;
281 	if (waserror()){
282 		configed = 1;
283 		if (*ccp != nil)
284 			cclose(*ccp);
285 		if (c)
286 			free(c);
287 		if (!mustrd)
288 			return;
289 		nexterror();
290 	}
291 	*ccp = namec(s, Aopen, OREAD, 0);
292 	devtab[(*ccp)->type]->read(*ccp, confstr, sizeof confstr, 0);
293 	cclose(*ccp);
294 	*ccp = nil;
295 	if (strncmp(confstr, Cfgstr, strlen(Cfgstr)) != 0)
296 		error("Bad config, first line must be: 'fsdev:\\n'");
297 	kstrdup(&c, confstr + strlen(Cfgstr));
298 	memset(confstr, 0, sizeof confstr);
299 	for (p = c; p != nil && *p != 0; p = e){
300 		e = strchr(p, '\n');
301 		if (e == nil)
302 			e = p + strlen(p);
303 		if (e == p) {
304 			e++;
305 			continue;
306 		}
307 		mconfig(p, e - p);
308 	}
309 	poperror();
310 }
311 
312 
313 static int
314 mgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
315 {
316 	Qid	qid;
317 	Fsdev	*mp;
318 
319 	if (c->qid.path == Qtop)
320 		switch(i){
321 		case DEVDOTDOT:
322 			devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
323 			return 1;
324 		case 0:
325 			devdir(c, dqid, "fs", 0, eve, DMDIR|0775, dp);
326 			return 1;
327 		default:
328 			return -1;
329 		}
330 	if (c->qid.path != Qdir)
331 		switch(i){
332 		case DEVDOTDOT:
333 			devdir(c, dqid, "fs", 0, eve, DMDIR|0775, dp);
334 			return 1;
335 		default:
336 			return -1;
337 		}
338 	switch(i){
339 	case DEVDOTDOT:
340 		devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
341 		return 1;
342 	case 0:
343 		devdir(c, cqid, "ctl", 0, eve, 0664, dp);
344 		return 1;
345 	}
346 	i--;			/* for ctl */
347 	qid.path = Qfirst + i;
348 	qid.vers = 0;
349 	qid.type = 0;
350 	mp = path2dev(i, 0);
351 	if (mp == nil)
352 		return -1;
353 	kstrcpy(up->genbuf, mp->name, sizeof(up->genbuf));
354 	devdir(c, qid, up->genbuf, mp->size, eve, 0664, dp);
355 	return 1;
356 }
357 
358 static Chan*
359 mattach(char *spec)
360 {
361 	*confstr = 0;
362 	return devattach(fsdevtab.dc, spec);
363 }
364 
365 static Walkqid*
366 mwalk(Chan *c, Chan *nc, char **name, int nname)
367 {
368 	if (!configed)
369 		rdconf();
370 	return devwalk(c, nc, name, nname, 0, 0, mgen);
371 }
372 
373 static int
374 mstat(Chan *c, uchar *db, int n)
375 {
376 	Dir	d;
377 	Fsdev	*mp;
378 	int	p;
379 
380 	p = c->qid.path;
381 	memset(&d, 0, sizeof d);
382 	switch(p){
383 	case Qtop:
384 		devdir(c, tqid, "#k", 0, eve, DMDIR|0775, &d);
385 		break;
386 	case Qdir:
387 		devdir(c, dqid, "fs", 0, eve, DMDIR|0775, &d);
388 		break;
389 	case Qctl:
390 		devdir(c, cqid, "ctl", 0, eve, 0664, &d);
391 		break;
392 	default:
393 		mp = path2dev(p - Qfirst, 1);
394 		devdir(c, c->qid, mp->name, mp->size, eve, 0664, &d);
395 	}
396 	n = convD2M(&d, db, n);
397 	if (n == 0)
398 		error(Ebadarg);
399 	return n;
400 }
401 
402 static Chan*
403 mopen(Chan *c, int omode)
404 {
405 	if((c->qid.type & QTDIR) && omode != OREAD)
406 		error(Eperm);
407 	if (omode & OTRUNC)
408 		omode &= ~OTRUNC;
409 	c->mode = openmode(omode);
410 	c->flag |= COPEN;
411 	c->offset = 0;
412 	return c;
413 }
414 
415 static void
416 mclose(Chan*)
417 {
418 	/* that's easy */
419 }
420 
421 static long
422 catio(Fsdev *mp, int isread, void *a, long n, vlong off)
423 {
424 	int	i;
425 	Chan*	mc;
426 	long	l, wl, res;
427 
428 	// print("catio %d %p %ld %lld\n", isread, a, n, off);
429 	res = n;
430 	for (i = 0; n >= 0 && i < mp->ndevs ; i++){
431 		mc = mp->idev[i];
432 		if (off > mp->isize[i]){
433 			off -= mp->isize[i];
434 			continue;
435 		}
436 		if (off + n > mp->isize[i])
437 			l = mp->isize[i] - off;
438 		else
439 			l = n;
440 		// print("\tdev %d %p %ld %lld\n", i, a, l, off);
441 
442 		if (isread)
443 			wl = devtab[mc->type]->read(mc, a, l, off);
444 		else
445 			wl = devtab[mc->type]->write(mc, a, l, off);
446 		if (wl != l)
447 			error("#k: write failed");
448 		a = (char*)a + l;
449 		off = 0;
450 		n -= l;
451 	}
452 	// print("\tres %ld\n", res - n);
453 	return res - n;
454 }
455 
456 static long
457 interio(Fsdev *mp, int isread, void *a, long n, vlong off)
458 {
459 	int	i;
460 	long	boff, res, l, wl, wsz;
461 	vlong	woff, blk, mblk;
462 	Chan*	mc;
463 
464 	blk  = off / Blksize;
465 	boff = off % Blksize;
466 	wsz  = Blksize - boff;
467 	res = n;
468 	while(n > 0){
469 		mblk = blk / mp->ndevs;
470 		i    = blk % mp->ndevs;
471 		mc   = mp->idev[i];
472 		woff = mblk*Blksize + boff;
473 		if (n > wsz)
474 			l = wsz;
475 		else
476 			l = n;
477 		if (isread)
478 			wl = devtab[mc->type]->read(mc, a, l, woff);
479 		else
480 			wl = devtab[mc->type]->write(mc, a, l, woff);
481 		if (wl != l || l == 0)
482 			error(Eio);
483 		a = (char*)a + l;
484 		n -= l;
485 		blk++;
486 		boff = 0;
487 		wsz = Blksize;
488 	}
489 	return res;
490 }
491 
492 static long
493 mread(Chan *c, void *a, long n, vlong off)
494 {
495 	int	i;
496 	long	l, res;
497 	Chan	*mc;
498 	Fsdev	*mp;
499 
500 	if (c->qid.type & QTDIR)
501 		return devdirread(c, a, n, 0, 0, mgen);
502 	if (c->qid.path == Qctl)
503 		return readstr((long)off, a, n, confstr + strlen(Cfgstr));
504 	i = c->qid.path - Qfirst;
505 	mp = path2dev(i, 1);
506 
507 	if (off >= mp->size)
508 		return 0;
509 	if (off + n > mp->size)
510 		n = mp->size - off;
511 	if (n == 0)
512 		return 0;
513 
514 	res = -1;
515 	switch(mp->type){
516 	case Fmirror:
517 		for (i = 0; i < mp->ndevs; i++){
518 			mc = mp->idev[i];
519 			if (waserror()){
520 				/*
521 				 * if a read fails we let the user know and try
522 				 * another device.
523 				 */
524 				print("#k: mread: (%llx %d): %s\n",
525 					c->qid.path, i, up->errstr);
526 				continue;
527 			}
528 			l = devtab[mc->type]->read(mc, a, n, off);
529 			poperror();
530 			if (l >= 0){
531 				res = l;
532 				break;
533 			}
534 		}
535 		if (i == mp->ndevs)
536 			error(Eio);
537 		break;
538 	case Fcat:
539 		res = catio(mp, 1, a, n, off);
540 		break;
541 	case Finter:
542 		res = interio(mp, 1, a, n, off);
543 		break;
544 	case Fpart:
545 		off += mp->start;
546 		mc = mp->idev[0];
547 		res = devtab[mc->type]->read(mc, a, n, off);
548 		break;
549 	}
550 	return res;
551 }
552 
553 static long
554 mwrite(Chan *c, void *a, long n, vlong off)
555 {
556 	int	i;
557 	long	l, res;
558 	Chan	*mc;
559 	Fsdev	*mp;
560 
561 	if (c->qid.type & QTDIR)
562 		error(Eperm);
563 	if (c->qid.path == Qctl){
564 		mconfig(a, n);
565 		return n;
566 	}
567 	mp = path2dev(c->qid.path - Qfirst, 1);
568 
569 	if (off >= mp->size)
570 		return 0;
571 	if (off + n > mp->size)
572 		n = mp->size - off;
573 	if (n == 0)
574 		return 0;
575 	res = n;
576 	switch(mp->type){
577 	case Fmirror:
578 		for (i = mp->ndevs - 1; i >= 0; i--){
579 			mc = mp->idev[i];
580 			l = devtab[mc->type]->write(mc, a, n, off);
581 			if (l < res)
582 				res = l;
583 		}
584 		break;
585 	case Fcat:
586 		res = catio(mp, 0, a, n, off);
587 		break;
588 	case Finter:
589 		res = interio(mp, 0, a, n, off);
590 		break;
591 	case Fpart:
592 		mc = mp->idev[0];
593 		off += mp->start;
594 		l = devtab[mc->type]->write(mc, a, n, off);
595 		if (l < res)
596 			res = l;
597 		break;
598 	}
599 	return res;
600 }
601 
602 Dev fsdevtab = {
603 	'k',
604 	"devfs",
605 
606 	devreset,
607 	devinit,
608 	devshutdown,
609 	mattach,
610 	mwalk,
611 	mstat,
612 	mopen,
613 	devcreate,
614 	mclose,
615 	mread,
616 	devbread,
617 	mwrite,
618 	devbwrite,
619 	devremove,
620 	devwstat,
621 	devpower,
622 	devconfig,
623 };
624