xref: /inferno-os/appl/cmd/install/archfs.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Archfs;
2
3include "sys.m";
4	sys : Sys;
5include "draw.m";
6include "bufio.m";
7	bufio : Bufio;
8include "arg.m";
9	arg : Arg;
10include "string.m";
11	str : String;
12include "daytime.m";
13	daytime : Daytime;
14include "styx.m";
15	styx: Styx;
16include "archfs.m";
17include "arch.m";
18	arch : Arch;
19
20# add write some day
21
22Iobuf : import bufio;
23Tmsg, Rmsg: import styx;
24
25Einuse		: con "fid already in use";
26Ebadfid		: con "bad fid";
27Eopen		: con "fid already opened";
28Enotfound	: con "file does not exist";
29Enotdir		: con "not a directory";
30Eperm		: con "permission denied";
31Ebadarg		: con "bad argument";
32Eexists		: con "file already exists";
33
34UID : con "inferno";
35GID : con "inferno";
36
37DEBUG: con 0;
38
39Dir : adt {
40	dir : Sys->Dir;
41	offset : int;
42	parent : cyclic ref Dir;
43	child : cyclic ref Dir;
44	sibling : cyclic ref Dir;
45};
46
47Fid : adt {
48	fid : int;
49	open: int;
50	dir : ref Dir;
51	next : cyclic ref Fid;
52};
53
54HTSZ : con 32;
55fidtab := array[HTSZ] of ref Fid;
56
57root : ref Dir;
58qid : int;
59mtpt := "/mnt";
60bio : ref Iobuf;
61buf : array of byte;
62skip := 0;
63
64# Archfs : module
65# {
66# 	init : fn(ctxt : ref Draw->Context, args : list of string);
67# };
68
69init(nil : ref Draw->Context, args : list of string)
70{
71	init0(nil, args, nil);
72}
73
74initc(args : list of string, c : chan of int)
75{
76	init0(nil, args, c);
77}
78
79chanint : chan of int;
80
81init0(nil : ref Draw->Context, args : list of string, chi : chan of int)
82{
83	chanint = chi;
84	sys = load Sys Sys->PATH;
85	bufio = load Bufio Bufio->PATH;
86	arg = load Arg Arg->PATH;
87	str = load String String->PATH;
88	daytime = load Daytime Daytime->PATH;
89	styx = load Styx Styx->PATH;
90	arch = load Arch Arch->PATH;
91	if (bufio == nil || arg == nil || styx == nil || arch == nil)
92		fatal("failed to load modules", 1);
93	styx->init();
94	arch->init(bufio);
95	arg->init(args);
96	while ((c := arg->opt()) != 0) {
97		case c {
98			'm' =>
99				mtpt = arg->arg();
100				if (mtpt == nil)
101					fatal("mount point missing", 1);
102			's' =>
103				skip = 1;
104		}
105	}
106	args = arg->argv();
107	if (args == nil)
108		fatal("missing archive file", 1);
109	buf = array[Sys->ATOMICIO] of byte;
110	# root = newdir("/", UID, GID, 8r755|Sys->DMDIR, daytime->now());
111	root = newdir(basename(mtpt), UID, GID, 8r755|Sys->DMDIR, daytime->now());
112	root.parent = root;
113	readarch(hd args, tl args);
114	p := array[2] of ref Sys->FD;
115	if(sys->pipe(p) < 0)
116		fatal("can't create pipe", 1);
117	ch := chan of ref Tmsg;
118	sync := chan of int;
119	spawn reader(p[1], ch, sync);
120	<- sync;
121	pidch := chan of int;
122	spawn serve(p[1], ch, pidch);
123	pid := <- pidch;
124	if(sys->mount(p[0], nil, mtpt, Sys->MREPL, nil) < 0)
125		fatal(sys->sprint("cannot mount archive on %s: %r", mtpt), 1);
126	p[0] = p[1] = nil;
127	if (chi != nil) {
128		chi <-= pid;
129		chanint = nil;
130	}
131}
132
133reply(fd: ref Sys->FD, m: ref Rmsg): int
134{
135	if(DEBUG)
136		sys->fprint(sys->fildes(2), "R: %s\n", m.text());
137	s := m.pack();
138	if(s == nil)
139		return -1;
140	return sys->write(fd, s, len s);
141}
142
143error(fd: ref Sys->FD, m: ref Tmsg, e : string)
144{
145	reply(fd, ref Rmsg.Error(m.tag, e));
146}
147
148reader(fd: ref Sys->FD, ch: chan of ref Tmsg, sync: chan of int)
149{
150	sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil);
151	sync <-= 1;
152	while((m := Tmsg.read(fd, Styx->MAXRPC)) != nil && tagof m != tagof Tmsg.Readerror)
153		ch <-= m;
154	ch <-= m;
155}
156
157serve(fd: ref Sys->FD, ch : chan of ref Tmsg, pidch : chan of int)
158{
159	e : string;
160	f : ref Fid;
161
162	pidch <-= sys->pctl(0, nil);
163	for (;;) {
164		m0 := <- ch;
165		if (m0 == nil)
166			return;
167		if(DEBUG)
168			sys->fprint(sys->fildes(2), "T: %s\n", m0.text());
169		pick m := m0 {
170			Readerror =>
171				fatal("read error on styx server", 1);
172			Version =>
173				(s, v) := styx->compatible(m, Styx->MAXRPC, Styx->VERSION);
174				reply(fd, ref Rmsg.Version(m.tag, s, v));
175			Auth =>
176				error(fd, m, "no authentication required");
177			Flush =>
178				reply(fd, ref Rmsg.Flush(m.tag));
179			Walk =>
180				(f, e) = mapfid(m.fid);
181				if (e != nil) {
182					error(fd, m, e);
183					continue;
184				}
185				if (f.open) {
186					error(fd, m, Eopen);
187					continue;
188				}
189				err := 0;
190				dir := f.dir;
191				nq := 0;
192				nn := len m.names;
193				qids := array[nn] of Sys->Qid;
194				if(nn > 0){
195					for(k := 0; k < nn; k++){
196						if ((dir.dir.mode & Sys->DMDIR) == 0) {
197							if(k == 0){
198								error(fd, m, Enotdir);
199								err = 1;
200							}
201							break;
202						}
203						dir  = lookup(dir, m.names[k]);
204						if (dir == nil) {
205							if(k == 0){
206								error(fd, m, Enotfound);
207								err = 1;
208							}
209							break;
210						}
211						qids[nq++] = dir.dir.qid;
212					}
213				}
214				if(err)
215					continue;
216				if(nq < nn)
217					qids = qids[0: nq];
218				if(nq == nn){
219					if(m.newfid != m.fid){
220						f = newfid(m.newfid);
221						if (f == nil) {
222							error(fd, m, Einuse);
223							continue;
224						}
225					}
226					f.dir = dir;
227				}
228				reply(fd, ref Rmsg.Walk(m.tag, qids));
229			Open =>
230				(f, e) = mapfid(m.fid);
231				if (e != nil) {
232					error(fd, m, e);
233					continue;
234				}
235				if (m.mode & (Sys->OWRITE|Sys->ORDWR|Sys->OTRUNC|Sys->ORCLOSE)) {
236					error(fd, m, Eperm);
237					continue;
238				}
239				f.open = 1;
240				reply(fd, ref Rmsg.Open(m.tag, f.dir.dir.qid, Styx->MAXFDATA));
241			Create =>
242				error(fd, m, Eperm);
243			Read =>
244				(f, e) = mapfid(m.fid);
245				if (e != nil) {
246					error(fd, m, e);
247					continue;
248				}
249				data := readdir(f.dir, int m.offset, m.count);
250				reply(fd, ref Rmsg.Read(m.tag, data));
251			Write =>
252				error(fd, m, Eperm);
253			Clunk =>
254				(f, e) = mapfid(m.fid);
255				if (e != nil) {
256					error(fd, m, e);
257					continue;
258				}
259				freefid(f);
260				reply(fd, ref Rmsg.Clunk(m.tag));
261			Stat =>
262				(f, e) = mapfid(m.fid);
263				if (e != nil) {
264					error(fd, m, e);
265					continue;
266				}
267				reply(fd, ref Rmsg.Stat(m.tag, f.dir.dir));
268			Remove =>
269				error(fd, m, Eperm);
270			Wstat =>
271				error(fd, m, Eperm);
272			Attach =>
273				f = newfid(m.fid);
274				if (f == nil) {
275					error(fd, m, Einuse);
276					continue;
277				}
278				f.dir = root;
279				reply(fd, ref Rmsg.Attach(m.tag, f.dir.dir.qid));
280			* =>
281				fatal("unknown styx message", 1);
282		}
283	}
284}
285
286newfid(fid : int) : ref Fid
287{
288	(f, nil) := mapfid(fid);
289	if(f != nil)
290		return nil;
291	f = ref Fid;
292	f.fid = fid;
293	f.open = 0;
294	hv := hashval(fid);
295	f.next = fidtab[hv];
296	fidtab[hv] = f;
297	return f;
298}
299
300freefid(f: ref Fid)
301{
302	hv := hashval(f.fid);
303	lf : ref Fid;
304	for(ff := fidtab[hv]; ff != nil; ff = ff.next){
305		if(f == ff){
306			if(lf == nil)
307				fidtab[hv] = ff.next;
308			else
309				lf.next = ff.next;
310			return;
311		}
312		lf = ff;
313	}
314	fatal("cannot find fid", 1);
315}
316
317mapfid(fid : int) : (ref Fid, string)
318{
319	hv := hashval(fid);
320	for (f := fidtab[hv]; f != nil; f = f.next)
321		if (int f.fid == fid)
322			break;
323	if (f == nil)
324		return (nil, Ebadfid);
325	if (f.dir == nil)
326		return (nil, Enotfound);
327	return (f, nil);
328}
329
330hashval(n : int) : int
331{
332	return (n & ~Sys->DMDIR)%HTSZ;
333}
334
335readarch(f : string, args : list of string)
336{
337	ar := arch->openarchfs(f);
338	if(ar == nil || ar.b == nil)
339		fatal(sys->sprint("cannot open %s(%r)\n", f), 1);
340	bio = ar.b;
341	while ((a := arch->gethdr(ar)) != nil) {
342		if (args != nil) {
343			if (!selected(a.name, args)) {
344				if (skip)
345					return;
346				arch->drain(ar, int a.d.length);
347				continue;
348			}
349			mkdirs("/", a.name);
350		}
351		d := mkdir(a.name, a.d.mode, a.d.mtime, a.d.uid, a.d.gid, 0);
352		if((a.d.mode & Sys->DMDIR) == 0) {
353			d.dir.length = a.d.length;
354			d.offset = int bio.offset();
355		}
356		arch->drain(ar, int a.d.length);
357	}
358	if (ar.err != nil)
359		fatal(ar.err, 0);
360}
361
362selected(s: string, args: list of string): int
363{
364	for(; args != nil; args = tl args)
365		if(fileprefix(hd args, s))
366			return 1;
367	return 0;
368}
369
370fileprefix(prefix, s: string): int
371{
372	n := len prefix;
373	m := len s;
374	if(n > m || !str->prefix(prefix, s))
375		return 0;
376	if(m > n && s[n] != '/')
377		return 0;
378	return 1;
379}
380
381basename(f : string) : string
382{
383	for (i := len f; i > 0; )
384		if (f[--i] == '/')
385			return f[i+1:];
386	return f;
387}
388
389split(p : string) : (string, string)
390{
391	if (p == nil)
392		fatal("nil string in split", 1);
393	if (p[0] != '/')
394		fatal("p0 not / in split", 1);
395	while (p[0] == '/')
396		p = p[1:];
397	i := 0;
398	while (i < len p && p[i] != '/')
399		i++;
400	if (i == len p)
401		return (p, nil);
402	else
403		return (p[0:i], p[i:]);
404}
405
406mkdirs(basedir, name: string)
407{
408	(nil, names) := sys->tokenize(name, "/");
409	while(names != nil) {
410		# sys->print("mkdir %s\n", basedir);
411		mkdir(basedir, 8r775|Sys->DMDIR, daytime->now(), UID, GID, 1);
412		if(tl names == nil)
413			break;
414		basedir = basedir + "/" + hd names;
415		names = tl names;
416	}
417}
418
419readdir(d : ref Dir, offset : int, n : int) : array of byte
420{
421	if (d.dir.mode & Sys->DMDIR)
422		return readd(d, offset, n);
423	else
424		return readf(d, offset, n);
425}
426
427readd(d : ref Dir, o : int, n : int) : array of byte
428{
429	k := 0;
430	m := 0;
431	b := array[n] of byte;
432	for (s := d.child; s != nil; s = s.sibling) {
433		l := styx->packdirsize(s.dir);
434		if(k < o){
435			k += l;
436			continue;
437		}
438		if(m+l > n)
439			break;
440		b[m: ] = styx->packdir(s.dir);
441		m += l;
442	}
443	return b[0: m];
444}
445
446readf(d : ref Dir, offset : int, n : int) : array of byte
447{
448	leng := int d.dir.length;
449	if (offset+n > leng)
450		n = leng-offset;
451	if (n <= 0 || offset < 0)
452		return nil;
453	bio.seek(big (d.offset+offset), Bufio->SEEKSTART);
454	a := array[n] of byte;
455	p := 0;
456	m := 0;
457	for ( ; n != 0; n -= m) {
458		l := len buf;
459		if (n < l)
460			l = n;
461		m = bio.read(buf, l);
462		if (m <= 0 || m != l)
463			fatal("premature eof", 1);
464		a[p:] = buf[0:m];
465		p += m;
466	}
467	return a;
468}
469
470mkdir(f : string, mode : int, mtime : int, uid : string, gid : string, existsok : int) : ref Dir
471{
472	if (f == "/")
473		return nil;
474	d := newdir(basename(f), uid, gid, mode, mtime);
475	addfile(d, f, existsok);
476	return d;
477}
478
479addfile(d : ref Dir, path : string, existsok : int)
480{
481	elem : string;
482
483	opath := path;
484	p := prev := root;
485	basedir := "";
486# sys->print("addfile %s : %s\n", d.dir.name, path);
487	while (path != nil) {
488		(elem, path) = split(path);
489		basedir += "/" + elem;
490		op := p;
491		p = lookup(p, elem);
492		if (path == nil) {
493			if (p != nil) {
494				if (!existsok && (p.dir.mode&Sys->DMDIR) == 0)
495					sys->fprint(sys->fildes(2), "addfile: %s already there", opath);
496					# fatal(sys->sprint("addfile: %s already there", opath), 1);
497				return;
498			}
499			if (prev.child == nil)
500				prev.child = d;
501			else {
502				for (s := prev.child; s.sibling != nil; s = s.sibling)
503					;
504				s.sibling = d;
505			}
506			d.parent = prev;
507		}
508		else {
509			if (p == nil) {
510				mkdir(basedir, 8r775|Sys->DMDIR, daytime->now(), UID, GID, 1);
511				p = lookup(op, elem);
512				if (p == nil)
513					fatal("bad file system", 1);
514			}
515		}
516		prev = p;
517	}
518}
519
520lookup(p : ref Dir, f : string) : ref Dir
521{
522	if ((p.dir.mode&Sys->DMDIR) == 0)
523		fatal("not a directory in lookup", 1);
524	if (f == ".")
525		return p;
526	if (f == "..")
527		return p.parent;
528	for (d := p.child; d != nil; d = d.sibling)
529		if (d.dir.name == f)
530			return d;
531	return nil;
532}
533
534newdir(name, uid, gid : string, mode, mtime : int) : ref Dir
535{
536	dir : Sys->Dir;
537
538	dir.name = name;
539	dir.uid = uid;
540	dir.gid = gid;
541	dir.qid.path = big (qid++);
542	if(mode&Sys->DMDIR)
543		dir.qid.qtype = Sys->QTDIR;
544	else
545		dir.qid.qtype = Sys->QTFILE;
546	dir.qid.vers = 0;
547	dir.mode = mode;
548	dir.atime = dir.mtime = mtime;
549	dir.length = big 0;
550	dir.dtype = 'X';
551	dir.dev = 0;
552
553	d := ref Dir;
554	d.dir = dir;
555	d.offset = 0;
556	return d;
557}
558
559# pr(d : ref Dir)
560# {
561#	dir := d.dir;
562#	sys->print("%s %s %s %x %x %x %d %d %d %d %d %d\n",
563#		dir.name, dir.uid, dir.gid, dir.qid.path, dir.qid.vers, dir.mode, dir.atime, dir.mtime, dir.length, dir.dtype, dir.dev, d.offset);
564# }
565
566fatal(e : string, pr: int)
567{
568	if(pr){
569		sys->fprint(sys->fildes(2), "fatal: %s\n", e);
570		if (chanint != nil)
571			chanint <-= -1;
572	}
573	else{
574		# probably not an archive file
575		if (chanint != nil)
576			chanint <-= -2;
577	}
578	exit;
579}
580