xref: /inferno-os/appl/lib/styxlib.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Styxlib;
2
3#
4# Copyright © 1999 Vita Nuova Limited.  All rights reserved.
5# Revisions copyright © 2002 Vita Nuova Holdings Limited.  All rights reserved.
6#
7
8include "sys.m";
9	sys: Sys;
10include "styx.m";
11	styx: Styx;
12	Tmsg, Rmsg: import styx;
13
14include "styxlib.m";
15
16CHANHASHSIZE: con 32;
17starttime: int;
18timefd: ref Sys->FD;
19
20DEBUG: con 0;
21
22init(s: Styx): string
23{
24	sys = load Sys Sys->PATH;
25	styx = s;	# our caller inits
26	return nil;
27}
28
29Styxserver.new(fd: ref Sys->FD): (chan of ref Tmsg, ref Styxserver)
30{
31	starttime = now();
32	srv := ref Styxserver(fd, array[CHANHASHSIZE] of list of ref Chan, getuname(), 0);
33	if(fd == nil)
34		return (nil, srv);
35	tchan := chan of ref Tmsg;
36	sync := chan of int;
37	spawn tmsgreader(fd, srv, tchan, sync);
38	<-sync;
39	return (tchan, srv);
40}
41
42now(): int
43{
44	if(timefd == nil){
45		timefd = sys->open("/dev/time", sys->OREAD);
46		if(timefd == nil)
47			return 0;
48	}
49	buf := array[64] of byte;
50	sys->seek(timefd, big 0, 0);
51	n := sys->read(timefd, buf, len buf);
52	if(n < 0)
53		return 0;
54
55	t := (big string buf[0:n]) / big 1000000;
56	return int t;
57}
58
59
60getuname(): string
61{
62	if ((fd := sys->open("/dev/user", Sys->OREAD)) == nil)
63		return "unknown";
64	buf := array[Sys->NAMEMAX] of byte;
65	n := sys->read(fd, buf, len buf);
66	if (n <= 0)
67		return "unknown";
68	return string buf[0:n];
69}
70
71tmsgreader(fd: ref Sys->FD, srv: ref Styxserver, tchan: chan of ref Tmsg, sync: chan of int)
72{
73	sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil);
74	sync <-= 1;
75	fd = sys->fildes(fd.fd);
76	while((m := Tmsg.read(fd, srv.msize)) != nil && tagof m != tagof Tmsg.Readerror){
77		tchan <-= m;
78		m = nil;
79	}
80	tchan <-= m;
81}
82
83Styxserver.reply(srv: self ref Styxserver, m: ref Rmsg): int
84{
85	if (DEBUG)
86		sys->fprint(sys->fildes(2), "%s\n", m.text());
87	a := m.pack();
88	if(a == nil)
89		return -1;
90	return sys->write(srv.fd, a, len a);
91}
92
93Styxserver.devversion(srv: self ref Styxserver, m: ref Tmsg.Version): int
94{
95	if(srv.msize <= 0)
96		srv.msize = Styx->MAXRPC;
97	(msize, version) := styx->compatible(m, srv.msize, Styx->VERSION);
98	if(msize < 128){
99		srv.reply(ref Rmsg.Error(m.tag, "unusable message size"));
100		return -1;
101	}
102	srv.msize = msize;
103	srv.reply(ref Rmsg.Version(m.tag, msize, version));
104	return 0;
105}
106
107Styxserver.devauth(srv: self ref Styxserver, m: ref Tmsg.Auth)
108{
109	srv.reply(ref Rmsg.Error(m.tag, "authentication not required"));
110}
111
112Styxserver.devattach(srv: self ref Styxserver, m: ref Tmsg.Attach): ref Chan
113{
114	c := srv.newchan(m.fid);
115	if (c == nil) {
116		srv.reply(ref Rmsg.Error(m.tag, Einuse));
117		return nil;
118	}
119	c.uname = m.uname;
120	c.qid.qtype = Sys->QTDIR;
121	c.qid.path = big 0;
122	c.path = "dev";
123	srv.reply(ref Rmsg.Attach(m.tag, c.qid));
124	return c;
125}
126
127Styxserver.clone(srv: self ref Styxserver, oc: ref Chan, newfid: int): ref Chan
128{
129	c := srv.newchan(newfid);
130	if (c == nil)
131		return nil;
132	c.qid = oc.qid;
133	c.uname  = oc.uname;
134	c.open = oc.open;
135	c.mode = oc.mode;
136	c.path = oc.path;
137	c.data = oc.data;
138	return c;
139}
140
141Styxserver.devflush(srv: self ref Styxserver, m: ref Tmsg.Flush)
142{
143	srv.reply(ref Rmsg.Flush(m.tag));
144}
145
146Styxserver.devwalk(srv: self ref Styxserver, m: ref Tmsg.Walk,
147							gen: Dirgenmod, tab: array of Dirtab): ref Chan
148{
149	c := srv.fidtochan(m.fid);
150	if (c == nil) {
151		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
152		return nil;
153	}
154	if (c.open) {
155		srv.reply(ref Rmsg.Error(m.tag, Eopen));
156		return nil;
157	}
158	if (!c.isdir()) {
159		srv.reply(ref Rmsg.Error(m.tag, Enotdir));
160		return nil;
161	}
162	# should check permissions here?
163	qids: array of Sys->Qid;
164	cc := ref *c;	# walk a temporary copy
165	if(len m.names > 0){
166		qids = array[len m.names] of Sys->Qid;
167		for(i := 0; i < len m.names; i++){
168			for(k := 0;; k++){
169				(ok, d) := gen->dirgen(srv, cc, tab, k);
170				if(ok < 0){
171					if(i == 0)
172						srv.reply(ref Rmsg.Error(m.tag, Enotfound));
173					else
174						srv.reply(ref Rmsg.Walk(m.tag, qids[0:i]));
175					return nil;
176				}
177				if (d.name == m.names[i]) {
178					cc.qid = d.qid;
179					cc.path = d.name;
180					qids[i] = cc.qid;
181					break;
182				}
183			}
184		}
185	}
186	# successful walk
187	if(m.newfid != m.fid){
188		# clone/walk
189		nc := srv.clone(cc, m.newfid);
190		if(nc == nil){
191			srv.reply(ref Rmsg.Error(m.tag, Einuse));
192			return nil;
193		}
194		c = nc;
195	}else{
196		# walk c itself
197		c.qid = cc.qid;
198		c.path = cc.path;
199	}
200	srv.reply(ref Rmsg.Walk(m.tag, qids));
201	return c;
202}
203
204Styxserver.devclunk(srv: self ref Styxserver, m: ref Tmsg.Clunk): ref Chan
205{
206	c := srv.fidtochan(m.fid);
207	if (c == nil) {
208		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
209		return nil;
210	}
211	srv.chanfree(c);
212	srv.reply(ref Rmsg.Clunk(m.tag));
213	return c;
214}
215
216Styxserver.devstat(srv: self ref Styxserver, m: ref Tmsg.Stat,
217							gen: Dirgenmod, tab: array of Dirtab)
218{
219	c := srv.fidtochan(m.fid);
220	if (c == nil) {
221		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
222		return;
223	}
224	i := 0;
225	(ok, d) := gen->dirgen(srv, c, tab, i++);
226	while (ok >= 0) {
227		if (ok > 0 && c.qid.path == d.qid.path) {
228			srv.reply(ref Rmsg.Stat(m.tag, d));
229			return;
230		}
231		(ok, d) = gen->dirgen(srv, c, tab, i++);
232	}
233	# auto-generate entry for directory if not found.
234	# XXX this is asking for trouble, as the permissions given
235	# on stat() of a directory can be different from those given
236	# when reading the directory's entry in its parent dir.
237	if (c.qid.qtype & Sys->QTDIR)
238		srv.reply(ref Rmsg.Stat(m.tag, devdir(c, c.qid, c.path, big 0, srv.uname, Sys->DMDIR|8r555)));
239	else
240		srv.reply(ref Rmsg.Error(m.tag, Enotfound));
241}
242
243Styxserver.devdirread(srv: self ref Styxserver, m: ref Tmsg.Read,
244							gen: Dirgenmod, tab: array of Dirtab)
245{
246	c := srv.fidtochan(m.fid);
247	if (c == nil) {
248		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
249		return;
250	}
251	offset := int m.offset;
252	data := array[m.count] of byte;
253	start := 0;
254	n := 0;
255	for (k := 0;; k++) {
256		(ok, d) := gen->dirgen(srv, c, tab, k);
257		if(ok < 0){
258			srv.reply(ref Rmsg.Read(m.tag, data[0:n]));
259			return;
260		}
261		size := styx->packdirsize(d);
262		if(start < offset){
263			start += size;
264			continue;
265		}
266		if(n+size > m.count)
267			break;
268		data[n:] = styx->packdir(d);
269		n += size;
270	}
271	srv.reply(ref Rmsg.Read(m.tag, data[0:n]));
272}
273
274Styxserver.devopen(srv: self ref Styxserver, m: ref Tmsg.Open,
275							gen: Dirgenmod, tab: array of Dirtab): ref Chan
276{
277	c := srv.fidtochan(m.fid);
278	if (c == nil) {
279		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
280		return nil;
281	}
282	omode := m.mode;
283	i := 0;
284	(ok, d) := gen->dirgen(srv, c, tab, i++);
285	while (ok >= 0) {
286		# XXX dev.c checks vers as well... is that desirable?
287		if (ok > 0 && c.qid.path == d.qid.path) {
288			if (openok(omode, d.mode, c.uname, d.uid, d.gid)) {
289				c.qid.vers = d.qid.vers;
290				break;
291			}
292			srv.reply(ref Rmsg.Error(m.tag, Eperm));
293			return nil;
294		}
295		(ok, d) = gen->dirgen(srv, c, tab, i++);
296	}
297	if ((c.qid.qtype & Sys->QTDIR) && omode != Sys->OREAD) {
298		srv.reply(ref Rmsg.Error(m.tag, Eperm));
299		return nil;
300	}
301	if ((c.mode = openmode(omode)) == -1) {
302		srv.reply(ref Rmsg.Error(m.tag, Ebadarg));
303		return nil;
304	}
305	c.open = 1;
306	c.mode = omode;
307	srv.reply(ref Rmsg.Open(m.tag, c.qid, Styx->MAXFDATA));
308	return c;
309}
310
311Styxserver.devremove(srv: self ref Styxserver, m: ref Tmsg.Remove): ref Chan
312{
313	c := srv.fidtochan(m.fid);
314	if (c == nil) {
315		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
316		return nil;
317	}
318	srv.chanfree(c);
319	srv.reply(ref Rmsg.Error(m.tag, Eperm));
320	return c;
321}
322
323Styxserver.fidtochan(srv: self ref Styxserver, fid: int): ref Chan
324{
325	for (l := srv.chans[fid & (CHANHASHSIZE-1)]; l != nil; l = tl l)
326		if ((hd l).fid == fid)
327			return hd l;
328	return nil;
329}
330
331Styxserver.chanfree(srv: self ref Styxserver, c: ref Chan)
332{
333	slot := c.fid & (CHANHASHSIZE-1);
334	nl: list of ref Chan;
335	for (l := srv.chans[slot]; l != nil; l = tl l)
336		if ((hd l).fid != c.fid)
337			nl = (hd l) :: nl;
338	srv.chans[slot] = nl;
339}
340
341Styxserver.chanlist(srv: self ref Styxserver): list of ref Chan
342{
343	cl: list of ref Chan;
344	for (i := 0; i < len srv.chans; i++)
345		for (l := srv.chans[i]; l != nil; l = tl l)
346			cl = hd l :: cl;
347	return cl;
348}
349
350Styxserver.newchan(srv: self ref Styxserver, fid: int): ref Chan
351{
352	# fid already in use
353	if ((c := srv.fidtochan(fid)) != nil)
354		return nil;
355	c = ref Chan;
356	c.qid = Sys->Qid(big 0, 0, Sys->QTFILE);
357	c.open = 0;
358	c.mode = 0;
359	c.fid = fid;
360	slot := fid & (CHANHASHSIZE-1);
361	srv.chans[slot] = c :: srv.chans[slot];
362	return c;
363}
364
365devdir(nil: ref Chan, qid: Sys->Qid, name: string, length: big,
366				user: string, perm: int): Sys->Dir
367{
368	d: Sys->Dir;
369	d.name = name;
370	d.qid = qid;
371	d.dtype = 'X';
372	d.dev = 0;		# XXX what should this be?
373	d.mode = perm;
374	if (qid.qtype & Sys->QTDIR)
375		d.mode |= Sys->DMDIR;
376	d.atime = starttime;	# XXX should be better than this.
377	d.mtime = starttime;
378	d.length = length;
379	d.uid = user;
380	d.gid = user;
381	return d;
382}
383
384readbytes(m: ref Tmsg.Read, d: array of byte): ref Rmsg.Read
385{
386	r := ref Rmsg.Read(m.tag, nil);
387	offset := int m.offset;
388	if (offset >= len d)
389		return r;
390	e := offset + m.count;
391	if (e > len d)
392		e = len d;
393	r.data = d[offset:e];
394	return r;
395}
396
397readnum(m: ref Tmsg.Read, val, size: int): ref Rmsg.Read
398{
399	return readbytes(m, sys->aprint("%-*d", size, val));
400}
401
402readstr(m: ref Tmsg.Read, d: string): ref Rmsg.Read
403{
404	return readbytes(m, array of byte d);
405}
406
407dirgenmodule(): Dirgenmod
408{
409	return load Dirgenmod "$self";
410}
411
412dirgen(srv: ref Styxserver, c: ref Styxlib->Chan,
413				tab: array of Dirtab, i: int): (int, Sys->Dir)
414{
415	d: Sys->Dir;
416	if (tab == nil || i >= len tab)
417		return (-1, d);
418	return (1, devdir(c, tab[i].qid, tab[i].name, tab[i].length, srv.uname, tab[i].perm));
419}
420
421openmode(o: int): int
422{
423	OTRUNC, ORCLOSE, OREAD, ORDWR: import Sys;
424	if(o >= (OTRUNC|ORCLOSE|ORDWR))
425		return -1;
426	o &= ~(OTRUNC|ORCLOSE);
427	if(o > ORDWR)
428		return -1;
429	return o;
430}
431
432access := array[] of {8r400, 8r200, 8r600, 8r100};
433openok(omode, perm: int, uname, funame, nil: string): int
434{
435	# XXX what should we do about groups?
436	# this is inadequate anyway:
437	# OTRUNC
438	# user should be allowed to open it if permission
439	# is allowed to others.
440	mode: int;
441	if (uname == funame)
442		mode = perm;
443	else
444		mode = perm << 6;
445
446	t := access[omode & 3];
447	return ((t & mode) == t);
448}
449
450Chan.isdir(c: self ref Chan): int
451{
452	return (c.qid.qtype & Sys->QTDIR) != 0;
453}
454