xref: /plan9/sys/src/9/port/devfs.c (revision b65f1be64966e4c3e4f2be91509008e1b9a3a980)
1ff56bef7SDavid du Colombier /*
2ff56bef7SDavid du Colombier  * File system devices.
3ff56bef7SDavid du Colombier  * Follows device config in Ken's file server.
4b99af9fbSDavid du Colombier  * Builds mirrors, concatenations, interleavings, and partitions
5b99af9fbSDavid du Colombier  * of devices out of other (inner) devices.
6cb9ca1c2SDavid du Colombier  * It is ok if inner devices are provided by this driver.
7cb9ca1c2SDavid du Colombier  *
8cb9ca1c2SDavid du Colombier  * Built files are grouped on different directories
9cb9ca1c2SDavid du Colombier  * (called trees, and used to represent disks).
10cb9ca1c2SDavid du Colombier  * The "#k/fs" tree is always available and never goes away.
11cb9ca1c2SDavid du Colombier  * Configuration changes happen only while no I/O is in progress.
12cb9ca1c2SDavid du Colombier  *
13cb9ca1c2SDavid du Colombier  * Default sector size is one byte unless changed by the "disk" ctl.
14ff56bef7SDavid du Colombier  */
15ff56bef7SDavid du Colombier 
16ff56bef7SDavid du Colombier #include "u.h"
17ff56bef7SDavid du Colombier #include "../port/lib.h"
18ff56bef7SDavid du Colombier #include "mem.h"
19ff56bef7SDavid du Colombier #include "dat.h"
20ff56bef7SDavid du Colombier #include "fns.h"
21ff56bef7SDavid du Colombier #include "io.h"
22ff56bef7SDavid du Colombier #include "ureg.h"
23ff56bef7SDavid du Colombier #include "../port/error.h"
24ff56bef7SDavid du Colombier 
25cb9ca1c2SDavid du Colombier enum
26cb9ca1c2SDavid du Colombier {
27cb9ca1c2SDavid du Colombier 	Fnone,
28d7459b31SDavid du Colombier 	Fmirror,		/* mirror of others */
29d7459b31SDavid du Colombier 	Fcat,			/* catenation of others */
30d7459b31SDavid du Colombier 	Finter,			/* interleaving of others */
31f9e1cf08SDavid du Colombier 	Fpart,			/* part of other */
32b99af9fbSDavid du Colombier 	Fclear,			/* start over */
33cb9ca1c2SDavid du Colombier 	Fdel,			/* delete a configure device */
34cb9ca1c2SDavid du Colombier 	Fdisk,			/* set default tree and sector sz*/
35ff56bef7SDavid du Colombier 
36cb9ca1c2SDavid du Colombier 	Sectorsz = 1,
37d7459b31SDavid du Colombier 	Blksize	= 8*1024,	/* for Finter only */
38ff56bef7SDavid du Colombier 
39cb9ca1c2SDavid du Colombier 	Incr = 5,		/* Increments for the dev array */
40cb9ca1c2SDavid du Colombier 
41cb9ca1c2SDavid du Colombier 	/*
42cb9ca1c2SDavid du Colombier 	 * All qids are decorated with the tree number.
43cb9ca1c2SDavid du Colombier 	 * #k/fs is tree number 0, is automatically added and
44cb9ca1c2SDavid du Colombier 	 * its first qid is for the ctl file. It never goes away.
45cb9ca1c2SDavid du Colombier 	 */
46cb9ca1c2SDavid du Colombier 	Qtop	= 0,		/* #k */
47cb9ca1c2SDavid du Colombier 	Qdir,			/* directory (#k/fs) */
48cb9ca1c2SDavid du Colombier 	Qctl,			/* ctl, only for #k/fs/ctl */
49cb9ca1c2SDavid du Colombier 	Qfirst,			/* first qid assigned for device */
50b99af9fbSDavid du Colombier 
51f366f900SDavid du Colombier 	Iswrite = 0,
52f366f900SDavid du Colombier 	Isread,
53f366f900SDavid du Colombier 
545864cca7SDavid du Colombier 	Optional = 0,
555864cca7SDavid du Colombier 	Mustexist,
565864cca7SDavid du Colombier 
57b99af9fbSDavid du Colombier 	/* tunable parameters */
58b99af9fbSDavid du Colombier 	Maxconf	= 4*1024,	/* max length for config */
59b99af9fbSDavid du Colombier 	Ndevs	= 32,		/* max. inner devs per command */
60cb9ca1c2SDavid du Colombier 	Ntrees	= 128,		/* max. number of trees */
61df5fa24dSDavid du Colombier 	Maxretries = 3,		/* max. retries of i/o errors */
62df5fa24dSDavid du Colombier 	Retrypause = 5000,	/* ms. to pause between retries */
63ff56bef7SDavid du Colombier };
64ff56bef7SDavid du Colombier 
65f366f900SDavid du Colombier typedef struct Inner Inner;
66cb9ca1c2SDavid du Colombier typedef struct Fsdev Fsdev;
67cb9ca1c2SDavid du Colombier typedef struct Tree Tree;
68cb9ca1c2SDavid du Colombier 
69f366f900SDavid du Colombier struct Inner
70f366f900SDavid du Colombier {
71f366f900SDavid du Colombier 	char	*iname;		/* inner device name */
72f366f900SDavid du Colombier 	vlong	isize;		/* size of inner device */
73f366f900SDavid du Colombier 	Chan	*idev;		/* inner device */
74f366f900SDavid du Colombier };
75f366f900SDavid du Colombier 
76ff56bef7SDavid du Colombier struct Fsdev
77ff56bef7SDavid du Colombier {
78cb9ca1c2SDavid du Colombier 	Ref;			/* one per Chan doing I/O */
79cb9ca1c2SDavid du Colombier 	int	gone;		/* true if removed */
80cb9ca1c2SDavid du Colombier 	int	vers;		/* qid version for this device */
81cb9ca1c2SDavid du Colombier 	int	type;		/* Fnone, Fmirror, ... */
82d7459b31SDavid du Colombier 	char	*name;		/* name for this fsdev */
83cb9ca1c2SDavid du Colombier 	Tree*	tree;		/* where the device is kept */
84f366f900SDavid du Colombier 	vlong	size;		/* min(inner[X].isize) */
85d7459b31SDavid du Colombier 	vlong	start;		/* start address (for Fpart) */
86cb9ca1c2SDavid du Colombier 	uint	ndevs;		/* number of inner devices */
87*b65f1be6SDavid du Colombier 	int	perm;		/* minimum of inner device perms */
88cb9ca1c2SDavid du Colombier 	Inner	*inner[Ndevs];	/* inner devices */
89ff56bef7SDavid du Colombier };
90ff56bef7SDavid du Colombier 
91cb9ca1c2SDavid du Colombier struct Tree
92cb9ca1c2SDavid du Colombier {
93cb9ca1c2SDavid du Colombier 	char	*name;		/* name for #k/<name> */
94cb9ca1c2SDavid du Colombier 	Fsdev	**devs;		/* devices in dir. */
95cb9ca1c2SDavid du Colombier 	uint	ndevs;		/* number of devices */
96cb9ca1c2SDavid du Colombier 	uint	nadevs;		/* number of allocated devices in devs */
97cb9ca1c2SDavid du Colombier };
98cb9ca1c2SDavid du Colombier 
99cb9ca1c2SDavid du Colombier #define dprint if(debug)print
100cb9ca1c2SDavid du Colombier 
101b99af9fbSDavid du Colombier extern Dev fsdevtab;		/* forward */
102b99af9fbSDavid du Colombier 
103cb9ca1c2SDavid du Colombier static RWlock lck;		/* r: use devices; w: change config  */
104cb9ca1c2SDavid du Colombier static Tree fstree;		/* The main "fs" tree. Never goes away */
105cb9ca1c2SDavid du Colombier static Tree *trees[Ntrees];	/* internal representation of config */
106cb9ca1c2SDavid du Colombier static int ntrees;		/* max number of trees */
107cb9ca1c2SDavid du Colombier static int qidvers;
108*b65f1be6SDavid du Colombier 
109cb9ca1c2SDavid du Colombier static char *disk;		/* default tree name used */
110cb9ca1c2SDavid du Colombier static char *source;		/* default inner device used */
111cb9ca1c2SDavid du Colombier static int sectorsz = Sectorsz;	/* default sector size */
112*b65f1be6SDavid du Colombier 
1135864cca7SDavid du Colombier static char confstr[Maxconf];	/* textual configuration */
114ff56bef7SDavid du Colombier 
115cb9ca1c2SDavid du Colombier static int debug;
116cb9ca1c2SDavid du Colombier 
117cb9ca1c2SDavid du Colombier static char cfgstr[] = "fsdev:\n";
118cb9ca1c2SDavid du Colombier 
119ff56bef7SDavid du Colombier static Qid tqid = {Qtop, 0, QTDIR};
120ff56bef7SDavid du Colombier static Qid cqid = {Qctl, 0, 0};
121ff56bef7SDavid du Colombier 
122cb9ca1c2SDavid du Colombier static char* tnames[] = {
123cb9ca1c2SDavid du Colombier 	[Fmirror]	"mirror",
124cb9ca1c2SDavid du Colombier 	[Fcat]		"cat",
125cb9ca1c2SDavid du Colombier 	[Finter]	"inter",
126cb9ca1c2SDavid du Colombier 	[Fpart]		"part",
127cb9ca1c2SDavid du Colombier };
128cb9ca1c2SDavid du Colombier 
129ff56bef7SDavid du Colombier static Cmdtab configs[] = {
130ff56bef7SDavid du Colombier 	Fmirror,"mirror",	0,
131ff56bef7SDavid du Colombier 	Fcat,	"cat",		0,
132ff56bef7SDavid du Colombier 	Finter,	"inter",	0,
133cb9ca1c2SDavid du Colombier 	Fpart,	"part",		0,
134b99af9fbSDavid du Colombier 	Fclear,	"clear",	1,
135cb9ca1c2SDavid du Colombier 	Fdel,	"del",		2,
136cb9ca1c2SDavid du Colombier 	Fdisk,	"disk",		0,
137ff56bef7SDavid du Colombier };
138ff56bef7SDavid du Colombier 
139cb9ca1c2SDavid du Colombier static char Egone[] = "device is gone";		/* file has been removed */
140ff56bef7SDavid du Colombier 
141cb9ca1c2SDavid du Colombier static char*
seprintdev(char * s,char * e,Fsdev * mp)142cb9ca1c2SDavid du Colombier seprintdev(char *s, char *e, Fsdev *mp)
143ff56bef7SDavid du Colombier {
144ff56bef7SDavid du Colombier 	int i;
145ff56bef7SDavid du Colombier 
146cb9ca1c2SDavid du Colombier 	if(mp == nil)
147cb9ca1c2SDavid du Colombier 		return seprint(s, e, "<null Fsdev>");
148cb9ca1c2SDavid du Colombier 	if(mp->type < 0 || mp->type >= nelem(tnames) || tnames[mp->type] == nil)
149cb9ca1c2SDavid du Colombier 		return seprint(s, e, "bad device type %d\n", mp->type);
150ff56bef7SDavid du Colombier 
151cb9ca1c2SDavid du Colombier 	s = strecpy(s, e, tnames[mp->type]);
152cb9ca1c2SDavid du Colombier 	if(mp->tree != &fstree)
153cb9ca1c2SDavid du Colombier 		s = seprint(s, e, " %s/%s", mp->tree->name, mp->name);
154cb9ca1c2SDavid du Colombier 	else
155cb9ca1c2SDavid du Colombier 		s = seprint(s, e, " %s", mp->name);
156cb9ca1c2SDavid du Colombier 	for(i = 0; i < mp->ndevs; i++)
157cb9ca1c2SDavid du Colombier 		s = seprint(s, e, " %s", mp->inner[i]->iname);
158cb9ca1c2SDavid du Colombier 	switch(mp->type){
159cb9ca1c2SDavid du Colombier 	case Fmirror:
160cb9ca1c2SDavid du Colombier 	case Fcat:
161cb9ca1c2SDavid du Colombier 	case Finter:
162cb9ca1c2SDavid du Colombier 		s = strecpy(s, e, "\n");
163cb9ca1c2SDavid du Colombier 		break;
164cb9ca1c2SDavid du Colombier 	case Fpart:
165cb9ca1c2SDavid du Colombier 		s = seprint(s, e, " %ulld %ulld\n", mp->start, mp->size);
166cb9ca1c2SDavid du Colombier 		break;
167cb9ca1c2SDavid du Colombier 	default:
168cb9ca1c2SDavid du Colombier 		panic("#k: seprintdev bug");
169cb9ca1c2SDavid du Colombier 	}
170cb9ca1c2SDavid du Colombier 	return s;
171cb9ca1c2SDavid du Colombier }
172cb9ca1c2SDavid du Colombier 
173cb9ca1c2SDavid du Colombier static vlong
mkpath(int tree,int devno)174cb9ca1c2SDavid du Colombier mkpath(int tree, int devno)
175cb9ca1c2SDavid du Colombier {
176cb9ca1c2SDavid du Colombier 	return (tree&0xFFFF)<<16 | devno&0xFFFF;
177cb9ca1c2SDavid du Colombier }
178cb9ca1c2SDavid du Colombier 
179cb9ca1c2SDavid du Colombier static int
path2treeno(int q)180cb9ca1c2SDavid du Colombier path2treeno(int q)
181cb9ca1c2SDavid du Colombier {
182cb9ca1c2SDavid du Colombier 	return q>>16 & 0xFFFF;
183cb9ca1c2SDavid du Colombier }
184cb9ca1c2SDavid du Colombier 
185cb9ca1c2SDavid du Colombier static int
path2devno(int q)186cb9ca1c2SDavid du Colombier path2devno(int q)
187cb9ca1c2SDavid du Colombier {
188cb9ca1c2SDavid du Colombier 	return q & 0xFFFF;
189cb9ca1c2SDavid du Colombier }
190cb9ca1c2SDavid du Colombier 
191cb9ca1c2SDavid du Colombier static Tree*
gettree(int i,int mustexist)192cb9ca1c2SDavid du Colombier gettree(int i, int mustexist)
193cb9ca1c2SDavid du Colombier {
194cb9ca1c2SDavid du Colombier 	dprint("gettree %d\n", i);
195cb9ca1c2SDavid du Colombier 	if(i < 0)
196cb9ca1c2SDavid du Colombier 		panic("#k: bug: bad tree index %d in gettree", i);
197cb9ca1c2SDavid du Colombier 	if(i >= ntrees || trees[i] == nil)
198cb9ca1c2SDavid du Colombier 		if(mustexist)
199cb9ca1c2SDavid du Colombier 			error(Enonexist);
200cb9ca1c2SDavid du Colombier 		else
201cb9ca1c2SDavid du Colombier 			return nil;
202cb9ca1c2SDavid du Colombier 	return trees[i];
203cb9ca1c2SDavid du Colombier }
204cb9ca1c2SDavid du Colombier 
205cb9ca1c2SDavid du Colombier static Fsdev*
getdev(Tree * t,int i,int mustexist)206cb9ca1c2SDavid du Colombier getdev(Tree *t, int i, int mustexist)
207cb9ca1c2SDavid du Colombier {
208cb9ca1c2SDavid du Colombier 	dprint("getdev %d\n", i);
209cb9ca1c2SDavid du Colombier 	if(i < 0)
210cb9ca1c2SDavid du Colombier 		panic("#k: bug: bad dev index %d in getdev", i);
211cb9ca1c2SDavid du Colombier 	if(i >= t->nadevs || t->devs[i] == nil)
212cb9ca1c2SDavid du Colombier 		if(mustexist)
213cb9ca1c2SDavid du Colombier 			error(Enonexist);
214cb9ca1c2SDavid du Colombier 		else
215cb9ca1c2SDavid du Colombier 			return nil;
216cb9ca1c2SDavid du Colombier 	return t->devs[i];
217cb9ca1c2SDavid du Colombier }
218cb9ca1c2SDavid du Colombier 
219cb9ca1c2SDavid du Colombier static Fsdev*
path2dev(int q)220cb9ca1c2SDavid du Colombier path2dev(int q)
221cb9ca1c2SDavid du Colombier {
222cb9ca1c2SDavid du Colombier 	Tree	*t;
223cb9ca1c2SDavid du Colombier 
224cb9ca1c2SDavid du Colombier 	dprint("path2dev %ux\n", q);
225cb9ca1c2SDavid du Colombier 	t = gettree(path2treeno(q), Mustexist);
226cb9ca1c2SDavid du Colombier 	return getdev(t, path2devno(q) - Qfirst, Mustexist);
227cb9ca1c2SDavid du Colombier }
228cb9ca1c2SDavid du Colombier 
229cb9ca1c2SDavid du Colombier static Tree*
treealloc(char * name)230cb9ca1c2SDavid du Colombier treealloc(char *name)
231cb9ca1c2SDavid du Colombier {
232cb9ca1c2SDavid du Colombier 	int	i;
233cb9ca1c2SDavid du Colombier 	Tree	*t;
234cb9ca1c2SDavid du Colombier 
235cb9ca1c2SDavid du Colombier 	dprint("treealloc %s\n", name);
236cb9ca1c2SDavid du Colombier 	for(i = 0; i < nelem(trees); i++)
237cb9ca1c2SDavid du Colombier 		if(trees[i] == nil)
238cb9ca1c2SDavid du Colombier 			break;
239cb9ca1c2SDavid du Colombier 	if(i == nelem(trees))
240cb9ca1c2SDavid du Colombier 		return nil;
241cb9ca1c2SDavid du Colombier 	t = trees[i] = mallocz(sizeof(Tree), 1);
242cb9ca1c2SDavid du Colombier 	if(t == nil)
243cb9ca1c2SDavid du Colombier 		return nil;
244cb9ca1c2SDavid du Colombier 	if(i == ntrees)
245cb9ca1c2SDavid du Colombier 		ntrees++;
246cb9ca1c2SDavid du Colombier 	kstrdup(&t->name, name);
247cb9ca1c2SDavid du Colombier 	return t;
248cb9ca1c2SDavid du Colombier }
249cb9ca1c2SDavid du Colombier 
250cb9ca1c2SDavid du Colombier static Tree*
lookuptree(char * name)251cb9ca1c2SDavid du Colombier lookuptree(char *name)
252cb9ca1c2SDavid du Colombier {
253cb9ca1c2SDavid du Colombier 	int i;
254cb9ca1c2SDavid du Colombier 
255cb9ca1c2SDavid du Colombier 	dprint("lookuptree %s\n", name);
256cb9ca1c2SDavid du Colombier 	for(i = 0; i < ntrees; i++)
257cb9ca1c2SDavid du Colombier 		if(trees[i] != nil && strcmp(trees[i]->name, name) == 0)
258cb9ca1c2SDavid du Colombier 			return trees[i];
259cb9ca1c2SDavid du Colombier 	return nil;
260cb9ca1c2SDavid du Colombier }
261cb9ca1c2SDavid du Colombier 
262cb9ca1c2SDavid du Colombier static Fsdev*
devalloc(Tree * t,char * name)263cb9ca1c2SDavid du Colombier devalloc(Tree *t, char *name)
264cb9ca1c2SDavid du Colombier {
265cb9ca1c2SDavid du Colombier 	int	i, ndevs;
266cb9ca1c2SDavid du Colombier 	Fsdev	*mp, **devs;
267cb9ca1c2SDavid du Colombier 
268cb9ca1c2SDavid du Colombier 	dprint("devalloc %s %s\n", t->name, name);
269cb9ca1c2SDavid du Colombier 	mp = mallocz(sizeof(Fsdev), 1);
270cb9ca1c2SDavid du Colombier 	if(mp == nil)
271cb9ca1c2SDavid du Colombier 		return nil;
272cb9ca1c2SDavid du Colombier 	for(i = 0; i < t->nadevs; i++)
273cb9ca1c2SDavid du Colombier 		if(t->devs[i] == nil)
274cb9ca1c2SDavid du Colombier 			break;
275cb9ca1c2SDavid du Colombier 	if(i >= t->nadevs){
276cb9ca1c2SDavid du Colombier 		if(t->nadevs % Incr == 0){
277cb9ca1c2SDavid du Colombier 			ndevs = t->nadevs + Incr;
278cb9ca1c2SDavid du Colombier 			devs = realloc(t->devs, ndevs * sizeof(Fsdev*));
279cb9ca1c2SDavid du Colombier 			if(devs == nil){
280cb9ca1c2SDavid du Colombier 				free(mp);
281cb9ca1c2SDavid du Colombier 				return nil;
282cb9ca1c2SDavid du Colombier 			}
283cb9ca1c2SDavid du Colombier 			t->devs = devs;
284cb9ca1c2SDavid du Colombier 		}
285cb9ca1c2SDavid du Colombier 		t->devs[t->nadevs] = nil;
286cb9ca1c2SDavid du Colombier 		t->nadevs++;
287cb9ca1c2SDavid du Colombier 	}
288cb9ca1c2SDavid du Colombier 	kstrdup(&mp->name, name);
289cb9ca1c2SDavid du Colombier 	mp->vers = ++qidvers;
290cb9ca1c2SDavid du Colombier 	mp->tree = t;
291cb9ca1c2SDavid du Colombier 	t->devs[i] = mp;
292cb9ca1c2SDavid du Colombier 	t->ndevs++;
293cb9ca1c2SDavid du Colombier 	return mp;
294ff56bef7SDavid du Colombier }
295ff56bef7SDavid du Colombier 
296ff56bef7SDavid du Colombier static void
deltree(Tree * t)297cb9ca1c2SDavid du Colombier deltree(Tree *t)
298ff56bef7SDavid du Colombier {
299ff56bef7SDavid du Colombier 	int i;
300cb9ca1c2SDavid du Colombier 
301cb9ca1c2SDavid du Colombier 	dprint("deltree %s\n", t->name);
302cb9ca1c2SDavid du Colombier 	for(i = 0; i < ntrees; i++)
303cb9ca1c2SDavid du Colombier 		if(trees[i] == t){
304cb9ca1c2SDavid du Colombier 			if(i > 0){		/* "fs" never goes away */
305cb9ca1c2SDavid du Colombier 				free(t->name);
306cb9ca1c2SDavid du Colombier 				free(t->devs);
307cb9ca1c2SDavid du Colombier 				free(t);
308cb9ca1c2SDavid du Colombier 				trees[i] = nil;
309cb9ca1c2SDavid du Colombier 			}
310cb9ca1c2SDavid du Colombier 			return;
311cb9ca1c2SDavid du Colombier 		}
312cb9ca1c2SDavid du Colombier 	panic("#k: deltree: bug: tree not found");
313cb9ca1c2SDavid du Colombier }
314cb9ca1c2SDavid du Colombier 
315cb9ca1c2SDavid du Colombier /*
316cb9ca1c2SDavid du Colombier  * A device is gone and we know that all its users are gone.
317cb9ca1c2SDavid du Colombier  * A tree is gone when all its devices are gone ("fs" is never gone).
318cb9ca1c2SDavid du Colombier  * Must close devices outside locks, so we could nest our own devices.
319cb9ca1c2SDavid du Colombier  */
320cb9ca1c2SDavid du Colombier static void
mdeldev(Fsdev * mp)321cb9ca1c2SDavid du Colombier mdeldev(Fsdev *mp)
322cb9ca1c2SDavid du Colombier {
323cb9ca1c2SDavid du Colombier 	int	i;
324cb9ca1c2SDavid du Colombier 	Inner	*in;
325cb9ca1c2SDavid du Colombier 	Tree	*t;
326cb9ca1c2SDavid du Colombier 
327cb9ca1c2SDavid du Colombier 	dprint("deldev %s gone %d ref %uld\n", mp->name, mp->gone, mp->ref);
328cb9ca1c2SDavid du Colombier 
329cb9ca1c2SDavid du Colombier 	mp->gone = 1;
330cb9ca1c2SDavid du Colombier 	mp->vers = ++qidvers;
331cb9ca1c2SDavid du Colombier 
332cb9ca1c2SDavid du Colombier 	wlock(&lck);
333cb9ca1c2SDavid du Colombier 	t = mp->tree;
334cb9ca1c2SDavid du Colombier 	for(i = 0; i < t->nadevs; i++)
335cb9ca1c2SDavid du Colombier 		if(t->devs[i] == mp){
336cb9ca1c2SDavid du Colombier 			t->devs[i] = nil;
337cb9ca1c2SDavid du Colombier 			t->ndevs--;
338cb9ca1c2SDavid du Colombier 			if(t->ndevs == 0)
339cb9ca1c2SDavid du Colombier 				deltree(t);
340cb9ca1c2SDavid du Colombier 			break;
341cb9ca1c2SDavid du Colombier 		}
342cb9ca1c2SDavid du Colombier 	wunlock(&lck);
343cb9ca1c2SDavid du Colombier 
344cb9ca1c2SDavid du Colombier 	free(mp->name);
345cb9ca1c2SDavid du Colombier 	for(i = 0; i < mp->ndevs; i++){
346cb9ca1c2SDavid du Colombier 		in = mp->inner[i];
347cb9ca1c2SDavid du Colombier 		if(in->idev != nil)
348cb9ca1c2SDavid du Colombier 			cclose(in->idev);
349cb9ca1c2SDavid du Colombier 		free(in->iname);
350cb9ca1c2SDavid du Colombier 		free(in);
351cb9ca1c2SDavid du Colombier 	}
352cb9ca1c2SDavid du Colombier 	if(debug)
353cb9ca1c2SDavid du Colombier 		memset(mp, 9, sizeof *mp);	/* poison */
354cb9ca1c2SDavid du Colombier 	free(mp);
355cb9ca1c2SDavid du Colombier }
356cb9ca1c2SDavid du Colombier 
357cb9ca1c2SDavid du Colombier /*
358cb9ca1c2SDavid du Colombier  * Delete one or all devices in one or all trees.
359cb9ca1c2SDavid du Colombier  */
360cb9ca1c2SDavid du Colombier static void
mdelctl(char * tname,char * dname)361cb9ca1c2SDavid du Colombier mdelctl(char *tname, char *dname)
362cb9ca1c2SDavid du Colombier {
363cb9ca1c2SDavid du Colombier 	int i, alldevs, alltrees, some;
364cb9ca1c2SDavid du Colombier 	Fsdev *mp;
365cb9ca1c2SDavid du Colombier 	Tree *t;
366cb9ca1c2SDavid du Colombier 
367cb9ca1c2SDavid du Colombier 	dprint("delctl %s\n", dname);
368cb9ca1c2SDavid du Colombier 	alldevs = strcmp(dname, "*") == 0;
369cb9ca1c2SDavid du Colombier 	alltrees = strcmp(tname, "*") == 0;
370cb9ca1c2SDavid du Colombier 	some = 0;
371cb9ca1c2SDavid du Colombier Again:
372cb9ca1c2SDavid du Colombier 	wlock(&lck);
373cb9ca1c2SDavid du Colombier 	for(i = 0; i < ntrees; i++){
374cb9ca1c2SDavid du Colombier 		t = trees[i];
375cb9ca1c2SDavid du Colombier 		if(t == nil)
376cb9ca1c2SDavid du Colombier 			continue;
377cb9ca1c2SDavid du Colombier 		if(alltrees == 0 && strcmp(t->name, tname) != 0)
378cb9ca1c2SDavid du Colombier 			continue;
379cb9ca1c2SDavid du Colombier 		for(i = 0; i < t->nadevs; i++){
380cb9ca1c2SDavid du Colombier 			mp = t->devs[i];
381cb9ca1c2SDavid du Colombier 			if(t->devs[i] == nil)
382cb9ca1c2SDavid du Colombier 				continue;
383cb9ca1c2SDavid du Colombier 			if(alldevs == 0 && strcmp(mp->name, dname) != 0)
384cb9ca1c2SDavid du Colombier 				continue;
385cb9ca1c2SDavid du Colombier 			/*
386cb9ca1c2SDavid du Colombier 			 * Careful: must close outside locks and that
387cb9ca1c2SDavid du Colombier 			 * may change the file tree we are looking at.
388cb9ca1c2SDavid du Colombier 			 */
389cb9ca1c2SDavid du Colombier 			some++;
390cb9ca1c2SDavid du Colombier 			mp->gone = 1;
391cb9ca1c2SDavid du Colombier 			if(mp->ref == 0){
392cb9ca1c2SDavid du Colombier 				incref(mp);	/* keep it there */
393cb9ca1c2SDavid du Colombier 				wunlock(&lck);
394cb9ca1c2SDavid du Colombier 				mdeldev(mp);
395cb9ca1c2SDavid du Colombier 				goto Again;	/* tree can change */
396cb9ca1c2SDavid du Colombier 			}
397cb9ca1c2SDavid du Colombier 		}
398cb9ca1c2SDavid du Colombier 	}
399cb9ca1c2SDavid du Colombier 	wunlock(&lck);
400cb9ca1c2SDavid du Colombier 	if(some == 0 && alltrees == 0)
401cb9ca1c2SDavid du Colombier 		error(Enonexist);
402cb9ca1c2SDavid du Colombier }
403cb9ca1c2SDavid du Colombier 
404cb9ca1c2SDavid du Colombier static void
setdsize(Fsdev * mp,vlong * ilen)405cb9ca1c2SDavid du Colombier setdsize(Fsdev* mp, vlong *ilen)
406cb9ca1c2SDavid du Colombier {
407cb9ca1c2SDavid du Colombier 	int	i;
408f9e1cf08SDavid du Colombier 	vlong	inlen;
409f366f900SDavid du Colombier 	Inner	*in;
410ff56bef7SDavid du Colombier 
411cb9ca1c2SDavid du Colombier 	dprint("setdsize %s\n", mp->name);
412ff56bef7SDavid du Colombier 	for (i = 0; i < mp->ndevs; i++){
413cb9ca1c2SDavid du Colombier 		in = mp->inner[i];
414cb9ca1c2SDavid du Colombier 		in->isize = ilen[i];
415cb9ca1c2SDavid du Colombier 		inlen = in->isize;
416ff56bef7SDavid du Colombier 		switch(mp->type){
417ff56bef7SDavid du Colombier 		case Finter:
418d7459b31SDavid du Colombier 			/* truncate to multiple of Blksize */
419f9e1cf08SDavid du Colombier 			inlen &= ~(Blksize-1);
420f9e1cf08SDavid du Colombier 			in->isize = inlen;
421f9e1cf08SDavid du Colombier 			/* fall through */
422f9e1cf08SDavid du Colombier 		case Fmirror:
423f9e1cf08SDavid du Colombier 			/* use size of smallest inner device */
424f9e1cf08SDavid du Colombier 			if (mp->size == 0 || mp->size > inlen)
425f9e1cf08SDavid du Colombier 				mp->size = inlen;
426f9e1cf08SDavid du Colombier 			break;
427f9e1cf08SDavid du Colombier 		case Fcat:
428f9e1cf08SDavid du Colombier 			mp->size += inlen;
429ff56bef7SDavid du Colombier 			break;
430ff56bef7SDavid du Colombier 		case Fpart:
431cb9ca1c2SDavid du Colombier 			if(mp->start > inlen)
432cb9ca1c2SDavid du Colombier 				error("partition starts after device end");
433f9e1cf08SDavid du Colombier 			if(inlen < mp->start + mp->size){
434cb9ca1c2SDavid du Colombier 				print("#k: %s: partition truncated from "
435f9e1cf08SDavid du Colombier 					"%lld to %lld bytes\n", mp->name,
436f9e1cf08SDavid du Colombier 					mp->size, inlen - mp->start);
437f9e1cf08SDavid du Colombier 				mp->size = inlen - mp->start;
438f9e1cf08SDavid du Colombier 			}
439ff56bef7SDavid du Colombier 			break;
440ff56bef7SDavid du Colombier 		}
441ff56bef7SDavid du Colombier 	}
442f9e1cf08SDavid du Colombier 	if(mp->type == Finter)
443f9e1cf08SDavid du Colombier 		mp->size *= mp->ndevs;
444ff56bef7SDavid du Colombier }
445ff56bef7SDavid du Colombier 
446ff56bef7SDavid du Colombier static void
validdevname(Tree * t,char * dname)447cb9ca1c2SDavid du Colombier validdevname(Tree *t, char *dname)
448ff56bef7SDavid du Colombier {
449ff56bef7SDavid du Colombier 	int i;
450ff56bef7SDavid du Colombier 
451cb9ca1c2SDavid du Colombier 	for(i = 0; i < t->nadevs; i++)
452cb9ca1c2SDavid du Colombier 		if(t->devs[i] != nil && strcmp(t->devs[i]->name, dname) == 0)
453cb9ca1c2SDavid du Colombier 			error(Eexist);
454ff56bef7SDavid du Colombier }
455ff56bef7SDavid du Colombier 
456cb9ca1c2SDavid du Colombier static void
parseconfig(char * a,long n,Cmdbuf ** cbp,Cmdtab ** ctp)457cb9ca1c2SDavid du Colombier parseconfig(char *a, long n, Cmdbuf **cbp, Cmdtab **ctp)
458cb9ca1c2SDavid du Colombier {
459cb9ca1c2SDavid du Colombier 	Cmdbuf	*cb;
460cb9ca1c2SDavid du Colombier 	Cmdtab	*ct;
461cb9ca1c2SDavid du Colombier 
462cb9ca1c2SDavid du Colombier 	*cbp = cb = parsecmd(a, n);
463cb9ca1c2SDavid du Colombier 	*ctp = ct = lookupcmd(cb, configs, nelem(configs));
464cb9ca1c2SDavid du Colombier 
465cb9ca1c2SDavid du Colombier 	cb->f++;			/* skip command */
466cb9ca1c2SDavid du Colombier 	cb->nf--;
467cb9ca1c2SDavid du Colombier 	switch(ct->index){
468cb9ca1c2SDavid du Colombier 	case Fmirror:
469cb9ca1c2SDavid du Colombier 	case Fcat:
470cb9ca1c2SDavid du Colombier 	case Finter:
471cb9ca1c2SDavid du Colombier 		if(cb->nf < 2)
472cb9ca1c2SDavid du Colombier 			error("too few arguments for ctl");
473cb9ca1c2SDavid du Colombier 		if(cb->nf - 1 > Ndevs)
474cb9ca1c2SDavid du Colombier 			error("too many devices in ctl");
475cb9ca1c2SDavid du Colombier 		break;
476cb9ca1c2SDavid du Colombier 	case Fdisk:
477cb9ca1c2SDavid du Colombier 		if(cb->nf < 1 || cb->nf > 3)
478cb9ca1c2SDavid du Colombier 			error("ctl usage: disk name [sz dev]");
479cb9ca1c2SDavid du Colombier 		break;
480cb9ca1c2SDavid du Colombier 	case Fpart:
481cb9ca1c2SDavid du Colombier 		if(cb->nf != 4 && (cb->nf != 3 || source == nil))
482cb9ca1c2SDavid du Colombier 			error("ctl usage: part new [file] off len");
483cb9ca1c2SDavid du Colombier 		break;
484cb9ca1c2SDavid du Colombier 	}
485cb9ca1c2SDavid du Colombier }
486cb9ca1c2SDavid du Colombier 
487cb9ca1c2SDavid du Colombier static void
parsename(char * name,char * disk,char ** tree,char ** dev)488cb9ca1c2SDavid du Colombier parsename(char *name, char *disk, char **tree, char **dev)
489cb9ca1c2SDavid du Colombier {
490cb9ca1c2SDavid du Colombier 	char *slash;
491cb9ca1c2SDavid du Colombier 
492cb9ca1c2SDavid du Colombier 	slash = strchr(name, '/');
493cb9ca1c2SDavid du Colombier 	if(slash == nil){
494cb9ca1c2SDavid du Colombier 		if(disk != nil)
495cb9ca1c2SDavid du Colombier 			*tree = disk;
496cb9ca1c2SDavid du Colombier 		else
497cb9ca1c2SDavid du Colombier 			*tree = "fs";
498cb9ca1c2SDavid du Colombier 		*dev = name;
499cb9ca1c2SDavid du Colombier 	}else{
500cb9ca1c2SDavid du Colombier 		*tree = name;
501cb9ca1c2SDavid du Colombier 		*slash++ = 0;
502cb9ca1c2SDavid du Colombier 		*dev = slash;
503cb9ca1c2SDavid du Colombier 	}
504cb9ca1c2SDavid du Colombier 	validname(*tree, 0);
505cb9ca1c2SDavid du Colombier 	validname(*dev, 0);
506cb9ca1c2SDavid du Colombier }
507cb9ca1c2SDavid du Colombier 
508*b65f1be6SDavid du Colombier static int
getattrs(Chan * c,vlong * lenp,int * permp)509*b65f1be6SDavid du Colombier getattrs(Chan *c, vlong *lenp, int *permp)
510cb9ca1c2SDavid du Colombier {
511cb9ca1c2SDavid du Colombier 	uchar	buf[128];	/* old DIRLEN plus a little should be plenty */
512cb9ca1c2SDavid du Colombier 	Dir	d;
513cb9ca1c2SDavid du Colombier 	long	l;
514cb9ca1c2SDavid du Colombier 
515*b65f1be6SDavid du Colombier 	*lenp = 0;
516*b65f1be6SDavid du Colombier 	*permp = 0;
517cb9ca1c2SDavid du Colombier 	l = devtab[c->type]->stat(c, buf, sizeof buf);
518*b65f1be6SDavid du Colombier 	if (l >= 0 && convM2D(buf, l, &d, nil) > 0) {
519*b65f1be6SDavid du Colombier 		*lenp = d.length;
520*b65f1be6SDavid du Colombier 		*permp = d.mode & 0777;
521*b65f1be6SDavid du Colombier 	}
522*b65f1be6SDavid du Colombier 	return l;
523cb9ca1c2SDavid du Colombier }
524ff56bef7SDavid du Colombier 
5255864cca7SDavid du Colombier /*
526cb9ca1c2SDavid du Colombier  * Process a single line of configuration,
527f9e1cf08SDavid du Colombier  * often of the form "cmd newname idev0 idev1".
528cb9ca1c2SDavid du Colombier  * locking is tricky, because we need a write lock to
529cb9ca1c2SDavid du Colombier  * add/remove devices yet adding/removing them may lead
530cb9ca1c2SDavid du Colombier  * to calls to this driver that require a read lock (when
531cb9ca1c2SDavid du Colombier  * inner devices are also provided by us).
5325864cca7SDavid du Colombier  */
533ff56bef7SDavid du Colombier static void
mconfig(char * a,long n)5345864cca7SDavid du Colombier mconfig(char* a, long n)
535ff56bef7SDavid du Colombier {
536b99af9fbSDavid du Colombier 	int	i;
537*b65f1be6SDavid du Colombier 	int	*iperm;
538b99af9fbSDavid du Colombier 	vlong	size, start;
539cb9ca1c2SDavid du Colombier 	vlong	*ilen;
540cb9ca1c2SDavid du Colombier 	char	*tname, *dname, *fakef[4];
541cb9ca1c2SDavid du Colombier 	Chan	**idev;
542ff56bef7SDavid du Colombier 	Cmdbuf	*cb;
543ff56bef7SDavid du Colombier 	Cmdtab	*ct;
544ff56bef7SDavid du Colombier 	Fsdev	*mp;
545f366f900SDavid du Colombier 	Inner	*inprv;
546cb9ca1c2SDavid du Colombier 	Tree	*t;
547ff56bef7SDavid du Colombier 
5485864cca7SDavid du Colombier 	/* ignore comments & empty lines */
5495864cca7SDavid du Colombier 	if (*a == '\0' || *a == '#' || *a == '\n')
5505864cca7SDavid du Colombier 		return;
5515864cca7SDavid du Colombier 
552cb9ca1c2SDavid du Colombier 	dprint("mconfig\n");
5535e96a66cSDavid du Colombier 	size = 0;
5545e96a66cSDavid du Colombier 	start = 0;
555ff56bef7SDavid du Colombier 	mp = nil;
556ff56bef7SDavid du Colombier 	cb = nil;
557cb9ca1c2SDavid du Colombier 	idev = nil;
558cb9ca1c2SDavid du Colombier 	ilen = nil;
559*b65f1be6SDavid du Colombier 	iperm = nil;
560b99af9fbSDavid du Colombier 
561ff56bef7SDavid du Colombier 	if(waserror()){
562ff56bef7SDavid du Colombier 		free(cb);
563ff56bef7SDavid du Colombier 		nexterror();
564ff56bef7SDavid du Colombier 	}
565b99af9fbSDavid du Colombier 
566cb9ca1c2SDavid du Colombier 	parseconfig(a, n, &cb, &ct);
567b99af9fbSDavid du Colombier 	switch (ct->index) {
568cb9ca1c2SDavid du Colombier 	case Fdisk:
569cb9ca1c2SDavid du Colombier 		kstrdup(&disk, cb->f[0]);
570cb9ca1c2SDavid du Colombier 		if(cb->nf >= 2)
571cb9ca1c2SDavid du Colombier 			sectorsz = strtoul(cb->f[1], 0, 0);
572cb9ca1c2SDavid du Colombier 		else
573cb9ca1c2SDavid du Colombier 			sectorsz = Sectorsz;
574cb9ca1c2SDavid du Colombier 		if(cb->nf == 3)
575cb9ca1c2SDavid du Colombier 			kstrdup(&source, cb->f[2]);
576cb9ca1c2SDavid du Colombier 		else{
577cb9ca1c2SDavid du Colombier 			free(source);
578cb9ca1c2SDavid du Colombier 			source = nil;
579cb9ca1c2SDavid du Colombier 		}
580cb9ca1c2SDavid du Colombier 		poperror();
581cb9ca1c2SDavid du Colombier 		free(cb);
582cb9ca1c2SDavid du Colombier 		return;
583cb9ca1c2SDavid du Colombier 	case Fclear:
584cb9ca1c2SDavid du Colombier 		poperror();
585cb9ca1c2SDavid du Colombier 		free(cb);
586cb9ca1c2SDavid du Colombier 		mdelctl("*", "*");		/* del everything */
587cb9ca1c2SDavid du Colombier 		return;
588b99af9fbSDavid du Colombier 	case Fpart:
589cb9ca1c2SDavid du Colombier 		if(cb->nf == 3){
590cb9ca1c2SDavid du Colombier 			/*
591cb9ca1c2SDavid du Colombier 			 * got a request in the format of sd(3),
592cb9ca1c2SDavid du Colombier 			 * pretend we got one in our format.
593cb9ca1c2SDavid du Colombier 			 * later we change end to be len.
594cb9ca1c2SDavid du Colombier 			 */
595cb9ca1c2SDavid du Colombier 			fakef[0] = cb->f[0];
596cb9ca1c2SDavid du Colombier 			fakef[1] = source;
597cb9ca1c2SDavid du Colombier 			fakef[2] = cb->f[1];
598cb9ca1c2SDavid du Colombier 			fakef[3] = cb->f[2];
599cb9ca1c2SDavid du Colombier 			cb->f = fakef;
600cb9ca1c2SDavid du Colombier 			cb->nf = 4;
601cb9ca1c2SDavid du Colombier 		}
602ff56bef7SDavid du Colombier 		start = strtoll(cb->f[2], nil, 10);
603b99af9fbSDavid du Colombier 		size =  strtoll(cb->f[3], nil, 10);
604cb9ca1c2SDavid du Colombier 		if(cb->f == fakef)
605cb9ca1c2SDavid du Colombier 			size -= start;		/* it was end */
606b99af9fbSDavid du Colombier 		cb->nf -= 2;
607b99af9fbSDavid du Colombier 		break;
608cb9ca1c2SDavid du Colombier 	}
609cb9ca1c2SDavid du Colombier 	parsename(cb->f[0], disk, &tname, &dname);
610cb9ca1c2SDavid du Colombier 	for(i = 1; i < cb->nf; i++)
611cb9ca1c2SDavid du Colombier 		validname(cb->f[i], 1);
612cb9ca1c2SDavid du Colombier 
613cb9ca1c2SDavid du Colombier 	if(ct->index == Fdel){
614cb9ca1c2SDavid du Colombier 		mdelctl(tname, dname);
615b99af9fbSDavid du Colombier 		poperror();
616b99af9fbSDavid du Colombier 		free(cb);
617b99af9fbSDavid du Colombier 		return;
618ff56bef7SDavid du Colombier 	}
619b99af9fbSDavid du Colombier 
620cb9ca1c2SDavid du Colombier 	/*
621cb9ca1c2SDavid du Colombier 	 * Open all inner devices while we have only a read lock.
622cb9ca1c2SDavid du Colombier 	 */
623cb9ca1c2SDavid du Colombier 	poperror();
624cb9ca1c2SDavid du Colombier 	rlock(&lck);
625f9e1cf08SDavid du Colombier 	if(waserror()){
626cb9ca1c2SDavid du Colombier 		runlock(&lck);
627cb9ca1c2SDavid du Colombier Fail:
628cb9ca1c2SDavid du Colombier 		for(i = 1; i < cb->nf; i++)
629cb9ca1c2SDavid du Colombier 			if(idev != nil && idev[i-1] != nil)
630cb9ca1c2SDavid du Colombier 				cclose(idev[i]);
631cb9ca1c2SDavid du Colombier 		if(mp != nil)
632cb9ca1c2SDavid du Colombier 			mdeldev(mp);
633cb9ca1c2SDavid du Colombier 		free(idev);
634cb9ca1c2SDavid du Colombier 		free(ilen);
635*b65f1be6SDavid du Colombier 		free(iperm);
636cb9ca1c2SDavid du Colombier 		free(cb);
637f9e1cf08SDavid du Colombier 		nexterror();
638f9e1cf08SDavid du Colombier 	}
639*b65f1be6SDavid du Colombier 	/* record names, lengths and perms of all named files */
640cb9ca1c2SDavid du Colombier 	idev = smalloc(sizeof(Chan*) * Ndevs);
641cb9ca1c2SDavid du Colombier 	ilen = smalloc(sizeof(vlong) * Ndevs);
642*b65f1be6SDavid du Colombier 	iperm = smalloc(sizeof(int) * Ndevs);
643ff56bef7SDavid du Colombier 	for(i = 1; i < cb->nf; i++){
644cb9ca1c2SDavid du Colombier 		idev[i-1] = namec(cb->f[i], Aopen, ORDWR, 0);
645*b65f1be6SDavid du Colombier 		getattrs(idev[i-1], &ilen[i-1], &iperm[i-1]);
646ff56bef7SDavid du Colombier 	}
647f9e1cf08SDavid du Colombier 	poperror();
648cb9ca1c2SDavid du Colombier 	runlock(&lck);
649cb9ca1c2SDavid du Colombier 
650cb9ca1c2SDavid du Colombier 	/*
651cb9ca1c2SDavid du Colombier 	 * Get a write lock and add the device if we can.
652cb9ca1c2SDavid du Colombier 	 */
653cb9ca1c2SDavid du Colombier 	wlock(&lck);
654cb9ca1c2SDavid du Colombier 	if(waserror()){
655cb9ca1c2SDavid du Colombier 		wunlock(&lck);
656cb9ca1c2SDavid du Colombier 		goto Fail;
657cb9ca1c2SDavid du Colombier 	}
658cb9ca1c2SDavid du Colombier 
659cb9ca1c2SDavid du Colombier 	t = lookuptree(tname);
660cb9ca1c2SDavid du Colombier 	if(t != nil)
661cb9ca1c2SDavid du Colombier 		validdevname(t, dname);
662cb9ca1c2SDavid du Colombier 	else
663cb9ca1c2SDavid du Colombier 		t = treealloc(tname);
664cb9ca1c2SDavid du Colombier 	if(t == nil)
665cb9ca1c2SDavid du Colombier 		error("no more trees");
666cb9ca1c2SDavid du Colombier 	mp = devalloc(t, dname);
667cb9ca1c2SDavid du Colombier 	if(mp == nil){
668cb9ca1c2SDavid du Colombier 		if(t->ndevs == 0)	/* it was created for us */
669cb9ca1c2SDavid du Colombier 			deltree(t);	/* but we will not mdeldev() */
670cb9ca1c2SDavid du Colombier 		error(Enomem);
671cb9ca1c2SDavid du Colombier 	}
672cb9ca1c2SDavid du Colombier 
673*b65f1be6SDavid du Colombier 	/* construct mp from iname, idev and iperm arrays */
674cb9ca1c2SDavid du Colombier 	mp->type = ct->index;
675cb9ca1c2SDavid du Colombier 	if(mp->type == Fpart){
676cb9ca1c2SDavid du Colombier 		mp->start = start * sectorsz;
677cb9ca1c2SDavid du Colombier 		mp->size = size * sectorsz;
678cb9ca1c2SDavid du Colombier 	}
679*b65f1be6SDavid du Colombier 	mp->perm = 0666;
680cb9ca1c2SDavid du Colombier 	for(i = 1; i < cb->nf; i++){
681cb9ca1c2SDavid du Colombier 		inprv = mp->inner[i-1] = mallocz(sizeof(Inner), 1);
682cb9ca1c2SDavid du Colombier 		if(inprv == nil)
683cb9ca1c2SDavid du Colombier 			error(Enomem);
684cb9ca1c2SDavid du Colombier 		mp->ndevs++;
685cb9ca1c2SDavid du Colombier 		kstrdup(&inprv->iname, cb->f[i]);
686cb9ca1c2SDavid du Colombier 		inprv->idev = idev[i-1];
687cb9ca1c2SDavid du Colombier 		idev[i-1] = nil;
688*b65f1be6SDavid du Colombier 		/* use the most restrictive of the inner permissions */
689*b65f1be6SDavid du Colombier 		mp->perm &= iperm[i-1];
690cb9ca1c2SDavid du Colombier 	}
691cb9ca1c2SDavid du Colombier 	setdsize(mp, ilen);
692b99af9fbSDavid du Colombier 
693b99af9fbSDavid du Colombier 	poperror();
694cb9ca1c2SDavid du Colombier 	wunlock(&lck);
695cb9ca1c2SDavid du Colombier 	free(idev);
696cb9ca1c2SDavid du Colombier 	free(ilen);
697*b65f1be6SDavid du Colombier 	free(iperm);
698ff56bef7SDavid du Colombier 	free(cb);
699ff56bef7SDavid du Colombier }
700ff56bef7SDavid du Colombier 
701ff56bef7SDavid du Colombier static void
rdconf(void)702ff56bef7SDavid du Colombier rdconf(void)
703ff56bef7SDavid du Colombier {
704ff56bef7SDavid du Colombier 	int mustrd;
705b99af9fbSDavid du Colombier 	char *c, *e, *p, *s;
706ff56bef7SDavid du Colombier 	Chan *cc;
7075864cca7SDavid du Colombier 	static int configed;
708ff56bef7SDavid du Colombier 
7095864cca7SDavid du Colombier 	/* only read config file once */
7105864cca7SDavid du Colombier 	if (configed)
7115864cca7SDavid du Colombier 		return;
7125864cca7SDavid du Colombier 	configed = 1;
7135864cca7SDavid du Colombier 
714cb9ca1c2SDavid du Colombier 	dprint("rdconf\n");
715cb9ca1c2SDavid du Colombier 	/* add the std "fs" tree */
716cb9ca1c2SDavid du Colombier 	trees[0] = &fstree;
717cb9ca1c2SDavid du Colombier 	ntrees++;
718cb9ca1c2SDavid du Colombier 	fstree.name = "fs";
719cb9ca1c2SDavid du Colombier 
7205864cca7SDavid du Colombier 	/* identify the config file */
721ff56bef7SDavid du Colombier 	s = getconf("fsconfig");
722ff56bef7SDavid du Colombier 	if (s == nil){
723ff56bef7SDavid du Colombier 		mustrd = 0;
724ff56bef7SDavid du Colombier 		s = "/dev/sdC0/fscfg";
725ff56bef7SDavid du Colombier 	} else
726ff56bef7SDavid du Colombier 		mustrd = 1;
7275864cca7SDavid du Colombier 
7285864cca7SDavid du Colombier 	/* read it */
7295864cca7SDavid du Colombier 	cc = nil;
730ff56bef7SDavid du Colombier 	c = nil;
731ff56bef7SDavid du Colombier 	if (waserror()){
7325864cca7SDavid du Colombier 		if (cc != nil)
7335864cca7SDavid du Colombier 			cclose(cc);
734ff56bef7SDavid du Colombier 		if (c)
735ff56bef7SDavid du Colombier 			free(c);
736ff56bef7SDavid du Colombier 		if (!mustrd)
737ff56bef7SDavid du Colombier 			return;
738ff56bef7SDavid du Colombier 		nexterror();
739ff56bef7SDavid du Colombier 	}
7405864cca7SDavid du Colombier 	cc = namec(s, Aopen, OREAD, 0);
7415864cca7SDavid du Colombier 	devtab[cc->type]->read(cc, confstr, sizeof confstr, 0);
7425864cca7SDavid du Colombier 	cclose(cc);
7435864cca7SDavid du Colombier 	cc = nil;
7445864cca7SDavid du Colombier 
7455864cca7SDavid du Colombier 	/* validate, copy and erase config; mconfig will repopulate confstr */
746cb9ca1c2SDavid du Colombier 	if (strncmp(confstr, cfgstr, sizeof cfgstr - 1) != 0)
747f366f900SDavid du Colombier 		error("bad #k config, first line must be: 'fsdev:\\n'");
748cb9ca1c2SDavid du Colombier 	kstrdup(&c, confstr + sizeof cfgstr - 1);
749b99af9fbSDavid du Colombier 	memset(confstr, 0, sizeof confstr);
7505864cca7SDavid du Colombier 
7515864cca7SDavid du Colombier 	/* process config copy one line at a time */
7525864cca7SDavid du Colombier 	for (p = c; p != nil && *p != '\0'; p = e){
753ff56bef7SDavid du Colombier 		e = strchr(p, '\n');
754ff56bef7SDavid du Colombier 		if (e == nil)
755ff56bef7SDavid du Colombier 			e = p + strlen(p);
7565864cca7SDavid du Colombier 		else
757e288d156SDavid du Colombier 			e++;
758ff56bef7SDavid du Colombier 		mconfig(p, e - p);
759ff56bef7SDavid du Colombier 	}
7605864cca7SDavid du Colombier 	USED(cc);		/* until now, can be used in waserror clause */
761ff56bef7SDavid du Colombier 	poperror();
762ff56bef7SDavid du Colombier }
763ff56bef7SDavid du Colombier 
764ff56bef7SDavid du Colombier static int
mgen(Chan * c,char *,Dirtab *,int,int i,Dir * dp)765ff56bef7SDavid du Colombier mgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
766ff56bef7SDavid du Colombier {
767cb9ca1c2SDavid du Colombier 	int	treeno;
768ff56bef7SDavid du Colombier 	Fsdev	*mp;
769cb9ca1c2SDavid du Colombier 	Qid	qid;
770cb9ca1c2SDavid du Colombier 	Tree	*t;
771ff56bef7SDavid du Colombier 
772cb9ca1c2SDavid du Colombier 	dprint("mgen %#ullx %d\n", c->qid.path, i);
773cb9ca1c2SDavid du Colombier 	qid.type = QTDIR;
774cb9ca1c2SDavid du Colombier 	qid.vers = 0;
775cb9ca1c2SDavid du Colombier 	if(c->qid.path == Qtop){
776cb9ca1c2SDavid du Colombier 		if(i == DEVDOTDOT){
777ff56bef7SDavid du Colombier 			devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
778ff56bef7SDavid du Colombier 			return 1;
779cb9ca1c2SDavid du Colombier 		}
780cb9ca1c2SDavid du Colombier 		t = gettree(i, Optional);
781cb9ca1c2SDavid du Colombier 		if(t == nil){
782cb9ca1c2SDavid du Colombier 			dprint("no\n");
783ff56bef7SDavid du Colombier 			return -1;
784ff56bef7SDavid du Colombier 		}
785cb9ca1c2SDavid du Colombier 		qid.path = mkpath(i, Qdir);
786cb9ca1c2SDavid du Colombier 		devdir(c, qid, t->name, 0, eve, DMDIR|0775, dp);
787ff56bef7SDavid du Colombier 		return 1;
788cb9ca1c2SDavid du Colombier 	}
789cb9ca1c2SDavid du Colombier 
790cb9ca1c2SDavid du Colombier 	treeno = path2treeno(c->qid.path);
791cb9ca1c2SDavid du Colombier 	t = gettree(treeno, Optional);
792cb9ca1c2SDavid du Colombier 	if(t == nil){
793cb9ca1c2SDavid du Colombier 		dprint("no\n");
794ff56bef7SDavid du Colombier 		return -1;
795ff56bef7SDavid du Colombier 	}
796cb9ca1c2SDavid du Colombier 	if((c->qid.type & QTDIR) != 0){
797cb9ca1c2SDavid du Colombier 		if(i == DEVDOTDOT){
798ff56bef7SDavid du Colombier 			devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
799ff56bef7SDavid du Colombier 			return 1;
800cb9ca1c2SDavid du Colombier 		}
801cb9ca1c2SDavid du Colombier 		if(treeno == 0){
802cb9ca1c2SDavid du Colombier 			/* take care of #k/fs/ctl */
803cb9ca1c2SDavid du Colombier 			if(i == 0){
804ff56bef7SDavid du Colombier 				devdir(c, cqid, "ctl", 0, eve, 0664, dp);
805ff56bef7SDavid du Colombier 				return 1;
806ff56bef7SDavid du Colombier 			}
807cb9ca1c2SDavid du Colombier 			i--;
808cb9ca1c2SDavid du Colombier 		}
809cb9ca1c2SDavid du Colombier 		mp = getdev(t, i, Optional);
810cb9ca1c2SDavid du Colombier 		if(mp == nil){
811cb9ca1c2SDavid du Colombier 			dprint("no\n");
812ff56bef7SDavid du Colombier 			return -1;
813cb9ca1c2SDavid du Colombier 		}
814cb9ca1c2SDavid du Colombier 		qid.type = QTFILE;
815cb9ca1c2SDavid du Colombier 		qid.vers = mp->vers;
816cb9ca1c2SDavid du Colombier 		qid.path = mkpath(treeno, Qfirst+i);
817*b65f1be6SDavid du Colombier 		devdir(c, qid, mp->name, mp->size, eve, mp->perm, dp);
818ff56bef7SDavid du Colombier 		return 1;
819ff56bef7SDavid du Colombier 	}
820ff56bef7SDavid du Colombier 
821cb9ca1c2SDavid du Colombier 	if(i == DEVDOTDOT){
822cb9ca1c2SDavid du Colombier 		qid.path = mkpath(treeno, Qdir);
823cb9ca1c2SDavid du Colombier 		devdir(c, qid, t->name, 0, eve, DMDIR|0775, dp);
824cb9ca1c2SDavid du Colombier 		return 1;
825cb9ca1c2SDavid du Colombier 	}
826cb9ca1c2SDavid du Colombier 	dprint("no\n");
827cb9ca1c2SDavid du Colombier 	return -1;
828cb9ca1c2SDavid du Colombier }
829cb9ca1c2SDavid du Colombier 
830ff56bef7SDavid du Colombier static Chan*
mattach(char * spec)831ff56bef7SDavid du Colombier mattach(char *spec)
832ff56bef7SDavid du Colombier {
833cb9ca1c2SDavid du Colombier 	dprint("mattach\n");
834b99af9fbSDavid du Colombier 	return devattach(fsdevtab.dc, spec);
835ff56bef7SDavid du Colombier }
836ff56bef7SDavid du Colombier 
837ff56bef7SDavid du Colombier static Walkqid*
mwalk(Chan * c,Chan * nc,char ** name,int nname)838ff56bef7SDavid du Colombier mwalk(Chan *c, Chan *nc, char **name, int nname)
839ff56bef7SDavid du Colombier {
840cb9ca1c2SDavid du Colombier 	Walkqid *wq;
841cb9ca1c2SDavid du Colombier 
842ff56bef7SDavid du Colombier 	rdconf();
843cb9ca1c2SDavid du Colombier 
844cb9ca1c2SDavid du Colombier 	dprint("mwalk %llux\n", c->qid.path);
845cb9ca1c2SDavid du Colombier 	rlock(&lck);
846cb9ca1c2SDavid du Colombier 	if(waserror()){
847cb9ca1c2SDavid du Colombier 		runlock(&lck);
848cb9ca1c2SDavid du Colombier 		nexterror();
849cb9ca1c2SDavid du Colombier 	}
850cb9ca1c2SDavid du Colombier 	wq = devwalk(c, nc, name, nname, 0, 0, mgen);
851cb9ca1c2SDavid du Colombier 	poperror();
852cb9ca1c2SDavid du Colombier 	runlock(&lck);
853cb9ca1c2SDavid du Colombier 	return wq;
854ff56bef7SDavid du Colombier }
855ff56bef7SDavid du Colombier 
856ff56bef7SDavid du Colombier static int
mstat(Chan * c,uchar * db,int n)857ff56bef7SDavid du Colombier mstat(Chan *c, uchar *db, int n)
858ff56bef7SDavid du Colombier {
859cb9ca1c2SDavid du Colombier 	int	p;
860ff56bef7SDavid du Colombier 	Dir	d;
861ff56bef7SDavid du Colombier 	Fsdev	*mp;
862cb9ca1c2SDavid du Colombier 	Qid	q;
863cb9ca1c2SDavid du Colombier 	Tree	*t;
864ff56bef7SDavid du Colombier 
865cb9ca1c2SDavid du Colombier 	dprint("mstat %llux\n", c->qid.path);
866cb9ca1c2SDavid du Colombier 	rlock(&lck);
867cb9ca1c2SDavid du Colombier 	if(waserror()){
868cb9ca1c2SDavid du Colombier 		runlock(&lck);
869cb9ca1c2SDavid du Colombier 		nexterror();
870cb9ca1c2SDavid du Colombier 	}
871ff56bef7SDavid du Colombier 	p = c->qid.path;
872b99af9fbSDavid du Colombier 	memset(&d, 0, sizeof d);
873ff56bef7SDavid du Colombier 	switch(p){
874ff56bef7SDavid du Colombier 	case Qtop:
875ff56bef7SDavid du Colombier 		devdir(c, tqid, "#k", 0, eve, DMDIR|0775, &d);
876ff56bef7SDavid du Colombier 		break;
877ff56bef7SDavid du Colombier 	case Qctl:
878ff56bef7SDavid du Colombier 		devdir(c, cqid, "ctl", 0, eve, 0664, &d);
879ff56bef7SDavid du Colombier 		break;
880ff56bef7SDavid du Colombier 	default:
881cb9ca1c2SDavid du Colombier 		t = gettree(path2treeno(p), Mustexist);
882cb9ca1c2SDavid du Colombier 		if(c->qid.type & QTDIR)
883cb9ca1c2SDavid du Colombier 			devdir(c, c->qid, t->name, 0, eve, DMDIR|0775, &d);
884cb9ca1c2SDavid du Colombier 		else{
885cb9ca1c2SDavid du Colombier 			mp = getdev(t, path2devno(p) - Qfirst, Mustexist);
886cb9ca1c2SDavid du Colombier 			q = c->qid;
887cb9ca1c2SDavid du Colombier 			q.vers = mp->vers;
888*b65f1be6SDavid du Colombier 			devdir(c, q, mp->name, mp->size, eve, mp->perm, &d);
889cb9ca1c2SDavid du Colombier 		}
890ff56bef7SDavid du Colombier 	}
891ff56bef7SDavid du Colombier 	n = convD2M(&d, db, n);
892ff56bef7SDavid du Colombier 	if (n == 0)
893ff56bef7SDavid du Colombier 		error(Ebadarg);
894cb9ca1c2SDavid du Colombier 	poperror();
895cb9ca1c2SDavid du Colombier 	runlock(&lck);
896ff56bef7SDavid du Colombier 	return n;
897ff56bef7SDavid du Colombier }
898ff56bef7SDavid du Colombier 
899ff56bef7SDavid du Colombier static Chan*
mopen(Chan * c,int omode)900ff56bef7SDavid du Colombier mopen(Chan *c, int omode)
901ff56bef7SDavid du Colombier {
902cb9ca1c2SDavid du Colombier 	int	q;
903cb9ca1c2SDavid du Colombier 	Fsdev	*mp;
904cb9ca1c2SDavid du Colombier 
905cb9ca1c2SDavid du Colombier 	dprint("mopen %llux\n", c->qid.path);
906ff56bef7SDavid du Colombier 	if((c->qid.type & QTDIR) && omode != OREAD)
907ff56bef7SDavid du Colombier 		error(Eperm);
908cb9ca1c2SDavid du Colombier 	if(c->qid.path != Qctl && (c->qid.type&QTDIR) == 0){
909cb9ca1c2SDavid du Colombier 		rlock(&lck);
910cb9ca1c2SDavid du Colombier 		if(waserror()){
911cb9ca1c2SDavid du Colombier 			runlock(&lck);
912cb9ca1c2SDavid du Colombier 			nexterror();
913cb9ca1c2SDavid du Colombier 		}
914cb9ca1c2SDavid du Colombier 		q = c->qid.path;
915cb9ca1c2SDavid du Colombier 		mp = path2dev(q);
916cb9ca1c2SDavid du Colombier 		if(mp->gone)
917cb9ca1c2SDavid du Colombier 			error(Egone);
918*b65f1be6SDavid du Colombier 		devpermcheck(eve, mp->perm, omode);
919cb9ca1c2SDavid du Colombier 		incref(mp);
920cb9ca1c2SDavid du Colombier 		poperror();
921cb9ca1c2SDavid du Colombier 		runlock(&lck);
922cb9ca1c2SDavid du Colombier 	}
923cb9ca1c2SDavid du Colombier 	/*
924cb9ca1c2SDavid du Colombier 	 * Our mgen does not return the info for the qid
925cb9ca1c2SDavid du Colombier 	 * but only for its children. Don't use devopen here.
926cb9ca1c2SDavid du Colombier 	 */
927cb9ca1c2SDavid du Colombier 	c->offset = 0;
928266d6380SDavid du Colombier 	c->mode = openmode(omode & ~OTRUNC);
929ff56bef7SDavid du Colombier 	c->flag |= COPEN;
930ff56bef7SDavid du Colombier 	return c;
931ff56bef7SDavid du Colombier }
932ff56bef7SDavid du Colombier 
933ff56bef7SDavid du Colombier static void
mclose(Chan * c)934cb9ca1c2SDavid du Colombier mclose(Chan *c)
935ff56bef7SDavid du Colombier {
936cb9ca1c2SDavid du Colombier 	int	mustdel, q;
937cb9ca1c2SDavid du Colombier 	Fsdev	*mp;
938ff56bef7SDavid du Colombier 
939cb9ca1c2SDavid du Colombier 	dprint("mclose %llux\n", c->qid.path);
940cb9ca1c2SDavid du Colombier 	if(c->qid.type & QTDIR || !(c->flag & COPEN))
941cb9ca1c2SDavid du Colombier 		return;
942cb9ca1c2SDavid du Colombier 	rlock(&lck);
943cb9ca1c2SDavid du Colombier 	if(waserror()){
944cb9ca1c2SDavid du Colombier 		runlock(&lck);
945cb9ca1c2SDavid du Colombier 		nexterror();
946cb9ca1c2SDavid du Colombier 	}
947cb9ca1c2SDavid du Colombier 	mustdel = 0;
948cb9ca1c2SDavid du Colombier 	mp = nil;
949cb9ca1c2SDavid du Colombier 	q = c->qid.path;
950cb9ca1c2SDavid du Colombier 	if(q == Qctl){
951cb9ca1c2SDavid du Colombier 		free(disk);
952cb9ca1c2SDavid du Colombier 		disk = nil;	/* restore defaults */
953cb9ca1c2SDavid du Colombier 		free(source);
954cb9ca1c2SDavid du Colombier 		source = nil;
955cb9ca1c2SDavid du Colombier 		sectorsz = Sectorsz;
956cb9ca1c2SDavid du Colombier 	}else{
957cb9ca1c2SDavid du Colombier 		mp = path2dev(q);
958cb9ca1c2SDavid du Colombier 		if(mp->gone != 0 && mp->ref == 1)
959cb9ca1c2SDavid du Colombier 			mustdel = 1;
960cb9ca1c2SDavid du Colombier 		else
961cb9ca1c2SDavid du Colombier 			decref(mp);
962cb9ca1c2SDavid du Colombier 	}
963cb9ca1c2SDavid du Colombier 	poperror();
964cb9ca1c2SDavid du Colombier 	runlock(&lck);
965cb9ca1c2SDavid du Colombier 	if(mustdel)
966cb9ca1c2SDavid du Colombier 		mdeldev(mp);
967cb9ca1c2SDavid du Colombier }
968f366f900SDavid du Colombier 
969f366f900SDavid du Colombier static long
io(Fsdev * mp,Inner * in,int isread,void * a,long l,vlong off)970f366f900SDavid du Colombier io(Fsdev *mp, Inner *in, int isread, void *a, long l, vlong off)
971f366f900SDavid du Colombier {
972f366f900SDavid du Colombier 	long wl;
973cb9ca1c2SDavid du Colombier 	Chan	*mc;
974f366f900SDavid du Colombier 
975cb9ca1c2SDavid du Colombier 	mc = in->idev;
976cb9ca1c2SDavid du Colombier 	if(mc == nil)
977cb9ca1c2SDavid du Colombier 		error(Egone);
978f366f900SDavid du Colombier 	if (waserror()) {
979266d6380SDavid du Colombier 		print("#k: %s: byte %,lld count %ld (of #k/%s): %s error: %s\n",
980707451d0SDavid du Colombier 			in->iname, off, l, mp->name, (isread? "read": "write"),
981f366f900SDavid du Colombier 			(up && up->errstr? up->errstr: ""));
982f366f900SDavid du Colombier 		nexterror();
983f366f900SDavid du Colombier 	}
984f9e1cf08SDavid du Colombier 	if (isread)
985f366f900SDavid du Colombier 		wl = devtab[mc->type]->read(mc, a, l, off);
986f9e1cf08SDavid du Colombier 	else
987f366f900SDavid du Colombier 		wl = devtab[mc->type]->write(mc, a, l, off);
988f366f900SDavid du Colombier 	poperror();
989f366f900SDavid du Colombier 	return wl;
990f366f900SDavid du Colombier }
991f366f900SDavid du Colombier 
992f9e1cf08SDavid du Colombier /* NB: a transfer could span multiple inner devices */
993ff56bef7SDavid du Colombier static long
catio(Fsdev * mp,int isread,void * a,long n,vlong off)994ff56bef7SDavid du Colombier catio(Fsdev *mp, int isread, void *a, long n, vlong off)
995ff56bef7SDavid du Colombier {
996ff56bef7SDavid du Colombier 	int	i;
997f9e1cf08SDavid du Colombier 	long	l, res;
998f366f900SDavid du Colombier 	Inner	*in;
999d7459b31SDavid du Colombier 
1000cb9ca1c2SDavid du Colombier 	if(debug)
1001cb9ca1c2SDavid du Colombier 		print("catio %d %p %ld %lld\n", isread, a, n, off);
1002ff56bef7SDavid du Colombier 	res = n;
1003f9e1cf08SDavid du Colombier 	for (i = 0; n > 0 && i < mp->ndevs; i++){
1004cb9ca1c2SDavid du Colombier 		in = mp->inner[i];
1005f9e1cf08SDavid du Colombier 		if (off >= in->isize){
1006f366f900SDavid du Colombier 			off -= in->isize;
1007f366f900SDavid du Colombier 			continue;		/* not there yet */
1008ff56bef7SDavid du Colombier 		}
1009f366f900SDavid du Colombier 		if (off + n > in->isize)
1010f366f900SDavid du Colombier 			l = in->isize - off;
1011ff56bef7SDavid du Colombier 		else
1012ff56bef7SDavid du Colombier 			l = n;
1013cb9ca1c2SDavid du Colombier 		if(debug)
1014cb9ca1c2SDavid du Colombier 			print("\tdev %d %p %ld %lld\n", i, a, l, off);
1015ff56bef7SDavid du Colombier 
1016f9e1cf08SDavid du Colombier 		if (io(mp, in, isread, a, l, off) != l)
1017f9e1cf08SDavid du Colombier 			error(Eio);
1018f366f900SDavid du Colombier 
1019ff56bef7SDavid du Colombier 		a = (char*)a + l;
1020ff56bef7SDavid du Colombier 		off = 0;
1021ff56bef7SDavid du Colombier 		n -= l;
1022ff56bef7SDavid du Colombier 	}
1023cb9ca1c2SDavid du Colombier 	if(debug)
1024cb9ca1c2SDavid du Colombier 		print("\tres %ld\n", res - n);
1025ff56bef7SDavid du Colombier 	return res - n;
1026ff56bef7SDavid du Colombier }
1027ff56bef7SDavid du Colombier 
1028ff56bef7SDavid du Colombier static long
interio(Fsdev * mp,int isread,void * a,long n,vlong off)1029ff56bef7SDavid du Colombier interio(Fsdev *mp, int isread, void *a, long n, vlong off)
1030ff56bef7SDavid du Colombier {
1031ff56bef7SDavid du Colombier 	int	i;
1032b99af9fbSDavid du Colombier 	long	boff, res, l, wl, wsz;
1033ff56bef7SDavid du Colombier 	vlong	woff, blk, mblk;
1034ff56bef7SDavid du Colombier 
1035ff56bef7SDavid du Colombier 	blk  = off / Blksize;
1036ff56bef7SDavid du Colombier 	boff = off % Blksize;
1037ff56bef7SDavid du Colombier 	wsz  = Blksize - boff;
1038ff56bef7SDavid du Colombier 	res = n;
1039ff56bef7SDavid du Colombier 	while(n > 0){
1040b99af9fbSDavid du Colombier 		mblk = blk / mp->ndevs;
1041ff56bef7SDavid du Colombier 		i    = blk % mp->ndevs;
1042ff56bef7SDavid du Colombier 		woff = mblk*Blksize + boff;
1043ff56bef7SDavid du Colombier 		if (n > wsz)
1044ff56bef7SDavid du Colombier 			l = wsz;
1045ff56bef7SDavid du Colombier 		else
1046ff56bef7SDavid du Colombier 			l = n;
1047f366f900SDavid du Colombier 
1048cb9ca1c2SDavid du Colombier 		wl = io(mp, mp->inner[i], isread, a, l, woff);
1049f9e1cf08SDavid du Colombier 		if (wl != l)
1050ff56bef7SDavid du Colombier 			error(Eio);
1051f366f900SDavid du Colombier 
1052ff56bef7SDavid du Colombier 		blk++;
1053ff56bef7SDavid du Colombier 		boff = 0;
1054ff56bef7SDavid du Colombier 		wsz = Blksize;
1055f9e1cf08SDavid du Colombier 		a = (char*)a + l;
1056f9e1cf08SDavid du Colombier 		n -= l;
1057ff56bef7SDavid du Colombier 	}
1058ff56bef7SDavid du Colombier 	return res;
1059ff56bef7SDavid du Colombier }
1060ff56bef7SDavid du Colombier 
1061cb9ca1c2SDavid du Colombier static char*
seprintconf(char * s,char * e)1062cb9ca1c2SDavid du Colombier seprintconf(char *s, char *e)
1063cb9ca1c2SDavid du Colombier {
1064cb9ca1c2SDavid du Colombier 	int	i, j;
1065cb9ca1c2SDavid du Colombier 	Tree	*t;
1066cb9ca1c2SDavid du Colombier 
1067cb9ca1c2SDavid du Colombier 	*s = 0;
1068cb9ca1c2SDavid du Colombier 	for(i = 0; i < ntrees; i++){
1069cb9ca1c2SDavid du Colombier 		t = trees[i];
1070cb9ca1c2SDavid du Colombier 		if(t != nil)
1071cb9ca1c2SDavid du Colombier 			for(j = 0; j < t->nadevs; j++)
1072cb9ca1c2SDavid du Colombier 				if(t->devs[j] != nil)
1073cb9ca1c2SDavid du Colombier 					s = seprintdev(s, e, t->devs[j]);
1074cb9ca1c2SDavid du Colombier 	}
1075cb9ca1c2SDavid du Colombier 	return s;
1076cb9ca1c2SDavid du Colombier }
1077cb9ca1c2SDavid du Colombier 
1078ff56bef7SDavid du Colombier static long
mread(Chan * c,void * a,long n,vlong off)1079ff56bef7SDavid du Colombier mread(Chan *c, void *a, long n, vlong off)
1080ff56bef7SDavid du Colombier {
1081266d6380SDavid du Colombier 	int	i, retry;
1082b99af9fbSDavid du Colombier 	long	l, res;
1083b99af9fbSDavid du Colombier 	Fsdev	*mp;
1084cb9ca1c2SDavid du Colombier 	Tree	*t;
1085ff56bef7SDavid du Colombier 
1086cb9ca1c2SDavid du Colombier 	dprint("mread %llux\n", c->qid.path);
1087cb9ca1c2SDavid du Colombier 	rlock(&lck);
1088cb9ca1c2SDavid du Colombier 	if(waserror()){
1089cb9ca1c2SDavid du Colombier 		runlock(&lck);
1090cb9ca1c2SDavid du Colombier 		nexterror();
10915864cca7SDavid du Colombier 	}
1092cb9ca1c2SDavid du Colombier 	res = -1;
1093cb9ca1c2SDavid du Colombier 	if(c->qid.type & QTDIR){
1094cb9ca1c2SDavid du Colombier 		res = devdirread(c, a, n, 0, 0, mgen);
1095cb9ca1c2SDavid du Colombier 		goto Done;
1096cb9ca1c2SDavid du Colombier 	}
1097cb9ca1c2SDavid du Colombier 	if(c->qid.path == Qctl){
1098cb9ca1c2SDavid du Colombier 		seprintconf(confstr, confstr + sizeof(confstr));
1099cb9ca1c2SDavid du Colombier 		res = readstr((long)off, a, n, confstr);
1100cb9ca1c2SDavid du Colombier 		goto Done;
1101cb9ca1c2SDavid du Colombier 	}
1102ff56bef7SDavid du Colombier 
1103cb9ca1c2SDavid du Colombier 	t = gettree(path2treeno(c->qid.path), Mustexist);
1104cb9ca1c2SDavid du Colombier 	mp = getdev(t, path2devno(c->qid.path) - Qfirst, Mustexist);
1105cb9ca1c2SDavid du Colombier 
1106cb9ca1c2SDavid du Colombier 	if(off >= mp->size){
1107cb9ca1c2SDavid du Colombier 		res = 0;
1108cb9ca1c2SDavid du Colombier 		goto Done;
1109cb9ca1c2SDavid du Colombier 	}
1110ff56bef7SDavid du Colombier 	if(off + n > mp->size)
1111ff56bef7SDavid du Colombier 		n = mp->size - off;
1112cb9ca1c2SDavid du Colombier 	if(n == 0){
1113cb9ca1c2SDavid du Colombier 		res = 0;
1114cb9ca1c2SDavid du Colombier 		goto Done;
1115cb9ca1c2SDavid du Colombier 	}
1116ff56bef7SDavid du Colombier 
1117ff56bef7SDavid du Colombier 	switch(mp->type){
1118f366f900SDavid du Colombier 	case Fcat:
1119f366f900SDavid du Colombier 		res = catio(mp, Isread, a, n, off);
1120f366f900SDavid du Colombier 		break;
1121f366f900SDavid du Colombier 	case Finter:
1122f366f900SDavid du Colombier 		res = interio(mp, Isread, a, n, off);
1123f366f900SDavid du Colombier 		break;
1124f366f900SDavid du Colombier 	case Fpart:
1125cb9ca1c2SDavid du Colombier 		res = io(mp, mp->inner[0], Isread, a, n, mp->start + off);
1126f366f900SDavid du Colombier 		break;
1127ff56bef7SDavid du Colombier 	case Fmirror:
1128266d6380SDavid du Colombier 		retry = 0;
1129266d6380SDavid du Colombier 		do {
1130266d6380SDavid du Colombier 			if (retry > 0) {
1131266d6380SDavid du Colombier 				print("#k/%s: retry %d read for byte %,lld "
1132266d6380SDavid du Colombier 					"count %ld: %s\n", mp->name, retry, off,
1133266d6380SDavid du Colombier 					n, (up && up->errstr? up->errstr: ""));
1134df5fa24dSDavid du Colombier 				/*
1135df5fa24dSDavid du Colombier 				 * pause before retrying in case it's due to
1136df5fa24dSDavid du Colombier 				 * a transient bus or controller problem.
1137df5fa24dSDavid du Colombier 				 */
1138df5fa24dSDavid du Colombier 				tsleep(&up->sleep, return0, 0, Retrypause);
1139266d6380SDavid du Colombier 			}
1140ff56bef7SDavid du Colombier 			for (i = 0; i < mp->ndevs; i++){
11416c54378cSDavid du Colombier 				if (waserror())
1142ff56bef7SDavid du Colombier 					continue;
1143cb9ca1c2SDavid du Colombier 				l = io(mp, mp->inner[i], Isread, a, n, off);
1144ff56bef7SDavid du Colombier 				poperror();
1145ff56bef7SDavid du Colombier 				if (l >= 0){
1146ff56bef7SDavid du Colombier 					res = l;
1147f366f900SDavid du Colombier 					break;		/* read a good copy */
1148ff56bef7SDavid du Colombier 				}
1149ff56bef7SDavid du Colombier 			}
1150df5fa24dSDavid du Colombier 		} while (i == mp->ndevs && ++retry <= Maxretries);
1151f9e1cf08SDavid du Colombier 		if (retry > Maxretries) {
1152266d6380SDavid du Colombier 			/* no mirror had a good copy of the block */
1153266d6380SDavid du Colombier 			print("#k/%s: byte %,lld count %ld: CAN'T READ "
1154266d6380SDavid du Colombier 				"from mirror: %s\n", mp->name, off, n,
1155266d6380SDavid du Colombier 				(up && up->errstr? up->errstr: ""));
1156266d6380SDavid du Colombier 			error(Eio);
1157266d6380SDavid du Colombier 		} else if (retry > 0)
1158266d6380SDavid du Colombier 			print("#k/%s: byte %,lld count %ld: retry read OK "
1159266d6380SDavid du Colombier 				"from mirror: %s\n", mp->name, off, n,
1160266d6380SDavid du Colombier 				(up && up->errstr? up->errstr: ""));
1161ff56bef7SDavid du Colombier 		break;
1162ff56bef7SDavid du Colombier 	}
1163cb9ca1c2SDavid du Colombier Done:
1164cb9ca1c2SDavid du Colombier 	poperror();
1165cb9ca1c2SDavid du Colombier 	runlock(&lck);
1166ff56bef7SDavid du Colombier 	return res;
1167ff56bef7SDavid du Colombier }
1168ff56bef7SDavid du Colombier 
1169ff56bef7SDavid du Colombier static long
mwrite(Chan * c,void * a,long n,vlong off)1170ff56bef7SDavid du Colombier mwrite(Chan *c, void *a, long n, vlong off)
1171ff56bef7SDavid du Colombier {
1172059f8267SDavid du Colombier 	int	i, allbad, anybad, retry;
1173b99af9fbSDavid du Colombier 	long	l, res;
1174b99af9fbSDavid du Colombier 	Fsdev	*mp;
1175cb9ca1c2SDavid du Colombier 	Tree	*t;
1176ff56bef7SDavid du Colombier 
1177cb9ca1c2SDavid du Colombier 	dprint("mwrite %llux\n", c->qid.path);
1178ff56bef7SDavid du Colombier 	if (c->qid.type & QTDIR)
1179cb9ca1c2SDavid du Colombier 		error(Eisdir);
1180ff56bef7SDavid du Colombier 	if (c->qid.path == Qctl){
1181ff56bef7SDavid du Colombier 		mconfig(a, n);
1182ff56bef7SDavid du Colombier 		return n;
1183ff56bef7SDavid du Colombier 	}
1184ff56bef7SDavid du Colombier 
1185cb9ca1c2SDavid du Colombier 	rlock(&lck);
1186cb9ca1c2SDavid du Colombier 	if(waserror()){
1187cb9ca1c2SDavid du Colombier 		runlock(&lck);
1188cb9ca1c2SDavid du Colombier 		nexterror();
1189cb9ca1c2SDavid du Colombier 	}
1190cb9ca1c2SDavid du Colombier 
1191cb9ca1c2SDavid du Colombier 	t = gettree(path2treeno(c->qid.path), Mustexist);
1192cb9ca1c2SDavid du Colombier 	mp = getdev(t, path2devno(c->qid.path) - Qfirst, Mustexist);
1193cb9ca1c2SDavid du Colombier 
1194cb9ca1c2SDavid du Colombier 	if(off >= mp->size){
1195cb9ca1c2SDavid du Colombier 		res = 0;
1196cb9ca1c2SDavid du Colombier 		goto Done;
1197cb9ca1c2SDavid du Colombier 	}
1198ff56bef7SDavid du Colombier 	if(off + n > mp->size)
1199ff56bef7SDavid du Colombier 		n = mp->size - off;
1200cb9ca1c2SDavid du Colombier 	if(n == 0){
1201cb9ca1c2SDavid du Colombier 		res = 0;
1202cb9ca1c2SDavid du Colombier 		goto Done;
1203cb9ca1c2SDavid du Colombier 	}
1204ff56bef7SDavid du Colombier 	res = n;
1205ff56bef7SDavid du Colombier 	switch(mp->type){
1206ff56bef7SDavid du Colombier 	case Fcat:
1207f366f900SDavid du Colombier 		res = catio(mp, Iswrite, a, n, off);
1208ff56bef7SDavid du Colombier 		break;
1209ff56bef7SDavid du Colombier 	case Finter:
1210f366f900SDavid du Colombier 		res = interio(mp, Iswrite, a, n, off);
1211ff56bef7SDavid du Colombier 		break;
1212ff56bef7SDavid du Colombier 	case Fpart:
1213cb9ca1c2SDavid du Colombier 		res = io(mp, mp->inner[0], Iswrite, a, n, mp->start + off);
1214f9e1cf08SDavid du Colombier 		if (res != n)
1215f9e1cf08SDavid du Colombier 			error(Eio);
1216f366f900SDavid du Colombier 		break;
1217f366f900SDavid du Colombier 	case Fmirror:
1218266d6380SDavid du Colombier 		retry = 0;
1219266d6380SDavid du Colombier 		do {
1220266d6380SDavid du Colombier 			if (retry > 0) {
1221266d6380SDavid du Colombier 				print("#k/%s: retry %d write for byte %,lld "
1222266d6380SDavid du Colombier 					"count %ld: %s\n", mp->name, retry, off,
1223266d6380SDavid du Colombier 					n, (up && up->errstr? up->errstr: ""));
1224df5fa24dSDavid du Colombier 				/*
1225df5fa24dSDavid du Colombier 				 * pause before retrying in case it's due to
1226df5fa24dSDavid du Colombier 				 * a transient bus or controller problem.
1227df5fa24dSDavid du Colombier 				 */
1228df5fa24dSDavid du Colombier 				tsleep(&up->sleep, return0, 0, Retrypause);
1229266d6380SDavid du Colombier 			}
1230f366f900SDavid du Colombier 			allbad = 1;
1231059f8267SDavid du Colombier 			anybad = 0;
1232f366f900SDavid du Colombier 			for (i = mp->ndevs - 1; i >= 0; i--){
1233059f8267SDavid du Colombier 				if (waserror()) {
1234059f8267SDavid du Colombier 					anybad = 1;
1235f366f900SDavid du Colombier 					continue;
1236059f8267SDavid du Colombier 				}
1237cb9ca1c2SDavid du Colombier 				l = io(mp, mp->inner[i], Iswrite, a, n, off);
1238f366f900SDavid du Colombier 				poperror();
1239059f8267SDavid du Colombier 				if (l == n)
1240f366f900SDavid du Colombier 					allbad = 0;	/* wrote a good copy */
1241059f8267SDavid du Colombier 				else
1242059f8267SDavid du Colombier 					anybad = 1;
1243f366f900SDavid du Colombier 			}
1244df5fa24dSDavid du Colombier 		} while (anybad && ++retry <= Maxretries);
1245266d6380SDavid du Colombier 		if (allbad) {
1246266d6380SDavid du Colombier 			/* no mirror took a good copy of the block */
1247266d6380SDavid du Colombier 			print("#k/%s: byte %,lld count %ld: CAN'T WRITE "
1248266d6380SDavid du Colombier 				"to mirror: %s\n", mp->name, off, n,
1249266d6380SDavid du Colombier 				(up && up->errstr? up->errstr: ""));
1250266d6380SDavid du Colombier 			error(Eio);
1251266d6380SDavid du Colombier 		} else if (retry > 0)
1252266d6380SDavid du Colombier 			print("#k/%s: byte %,lld count %ld: retry wrote OK "
1253266d6380SDavid du Colombier 				"to mirror: %s\n", mp->name, off, n,
1254266d6380SDavid du Colombier 				(up && up->errstr? up->errstr: ""));
1255266d6380SDavid du Colombier 
1256ff56bef7SDavid du Colombier 		break;
1257ff56bef7SDavid du Colombier 	}
1258cb9ca1c2SDavid du Colombier Done:
1259cb9ca1c2SDavid du Colombier 	poperror();
1260cb9ca1c2SDavid du Colombier 	runlock(&lck);
1261ff56bef7SDavid du Colombier 	return res;
1262ff56bef7SDavid du Colombier }
1263ff56bef7SDavid du Colombier 
1264413564c8SDavid du Colombier Dev fsdevtab = {
12656b6b9ac8SDavid du Colombier 	'k',
1266887f280bSDavid du Colombier 	"fs",
1267ff56bef7SDavid du Colombier 
1268ff56bef7SDavid du Colombier 	devreset,
1269ff56bef7SDavid du Colombier 	devinit,
1270ff56bef7SDavid du Colombier 	devshutdown,
1271ff56bef7SDavid du Colombier 	mattach,
1272ff56bef7SDavid du Colombier 	mwalk,
1273ff56bef7SDavid du Colombier 	mstat,
1274ff56bef7SDavid du Colombier 	mopen,
1275ff56bef7SDavid du Colombier 	devcreate,
1276ff56bef7SDavid du Colombier 	mclose,
1277ff56bef7SDavid du Colombier 	mread,
1278ff56bef7SDavid du Colombier 	devbread,
1279ff56bef7SDavid du Colombier 	mwrite,
1280ff56bef7SDavid du Colombier 	devbwrite,
1281ff56bef7SDavid du Colombier 	devremove,
1282ff56bef7SDavid du Colombier 	devwstat,
1283ff56bef7SDavid du Colombier 	devpower,
1284ff56bef7SDavid du Colombier 	devconfig,
1285ff56bef7SDavid du Colombier };
1286