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