xref: /inferno-os/appl/acme/fsys.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Fsys;
2
3include "common.m";
4
5sys : Sys;
6styx : Styx;
7styxaux : Styxaux;
8acme : Acme;
9dat : Dat;
10utils : Utils;
11look : Look;
12windowm : Windowm;
13xfidm : Xfidm;
14
15QTDIR, QTFILE, QTAPPEND : import Sys;
16DMDIR, DMAPPEND, Qid, ORCLOSE, OTRUNC, OREAD, OWRITE, ORDWR, Dir : import Sys;
17sprint : import sys;
18MAXWELEM, Rerror : import Styx;
19Qdir,Qacme,Qcons,Qconsctl,Qdraw,Qeditout,Qindex,Qlabel,Qnew,QWaddr,QWbody,QWconsctl,QWctl,QWdata,QWeditout,QWevent,QWrdsel,QWwrsel,QWtag,QMAX : import Dat;
20TRUE, FALSE : import Dat;
21cxfidalloc, cerr : import dat;
22Mntdir, Fid, Dirtab, Lock, Ref, Smsg0 : import dat;
23Tmsg, Rmsg : import styx;
24msize, version, fid, uname, aname, newfid, name, mode, offset, count, setmode : import styxaux;
25Xfid : import xfidm;
26row : import dat;
27Column : import Columnm;
28Window : import windowm;
29lookid : import look;
30warning, error : import utils;
31
32init(mods : ref Dat->Mods)
33{
34	messagesize = Styx->MAXRPC;
35
36	sys = mods.sys;
37	styx = mods.styx;
38	styxaux = mods.styxaux;
39	acme = mods.acme;
40	dat = mods.dat;
41	utils = mods.utils;
42	look = mods.look;
43	windowm = mods.windowm;
44	xfidm = mods.xfidm;
45}
46
47sfd, cfd : ref Sys->FD;
48
49Nhash : con 16;
50DEBUG : con 0;
51
52fids := array[Nhash] of ref Fid;
53
54Eperm := "permission denied";
55Eexist := "file does not exist";
56Enotdir := "not a directory";
57
58dirtab := array[10] of {
59	Dirtab ( ".",		QTDIR,		Qdir,			8r500|DMDIR ),
60	Dirtab ( "acme",	QTDIR,		Qacme,		8r500|DMDIR ),
61	Dirtab ( "cons",		QTFILE,		Qcons,		8r600 ),
62	Dirtab ( "consctl",	QTFILE,		Qconsctl,		8r000 ),
63	Dirtab ( "draw",		QTDIR,		Qdraw,		8r000|DMDIR ),
64	Dirtab ( "editout",	QTFILE,		Qeditout,		8r200 ),
65	Dirtab ( "index",	QTFILE,		Qindex,		8r400 ),
66	Dirtab ( "label",		QTFILE,		Qlabel,		8r600 ),
67	Dirtab ( "new",		QTDIR,		Qnew,		8r500|DMDIR ),
68	Dirtab ( nil,		0,			0,			0 ),
69};
70
71dirtabw := array[12] of {
72	Dirtab ( ".",		QTDIR,		Qdir,			8r500|DMDIR ),
73	Dirtab ( "addr",		QTFILE,		QWaddr,		8r600 ),
74	Dirtab ( "body",		QTAPPEND,	QWbody,		8r600|DMAPPEND ),
75	Dirtab ( "ctl",		QTFILE,		QWctl,		8r600 ),
76	Dirtab ( "consctl",	QTFILE,		QWconsctl,	8r200 ),
77	Dirtab ( "data",		QTFILE,		QWdata,		8r600 ),
78	Dirtab ( "editout",	QTFILE,		QWeditout,	8r200 ),
79	Dirtab ( "event",	QTFILE,		QWevent,		8r600 ),
80	Dirtab ( "rdsel",		QTFILE,		QWrdsel,		8r400 ),
81	Dirtab ( "wrsel",	QTFILE,		QWwrsel,		8r200 ),
82	Dirtab ( "tag",		QTAPPEND,	QWtag,		8r600|DMAPPEND ),
83	Dirtab ( nil, 		0,			0,			0 ),
84};
85
86Mnt : adt {
87	qlock : ref Lock;
88	id : int;
89	md : ref Mntdir;
90};
91
92mnt : Mnt;
93user : string;
94clockfd : ref Sys->FD;
95closing := 0;
96
97fsysinit()
98{
99	p :  array of ref Sys->FD;
100
101	p = array[2] of ref Sys->FD;
102	if(sys->pipe(p) < 0)
103		error("can't create pipe");
104	cfd = p[0];
105	sfd = p[1];
106	clockfd = sys->open("/dev/time", Sys->OREAD);
107	user = utils->getuser();
108	if (user == nil)
109		user = "Wile. E. Coyote";
110	mnt.qlock = Lock.init();
111	mnt.id = 0;
112	spawn fsysproc();
113}
114
115fsyscfd() : int
116{
117	return cfd.fd;
118}
119
120QID(w, q : int) : int
121{
122	return (w<<8)|q;
123}
124
125FILE(q : Qid) : int
126{
127	return int q.path & 16rFF;
128}
129
130WIN(q : Qid) : int
131{
132	return (int q.path>>8) & 16rFFFFFF;
133}
134
135# nullsmsg : Smsg;
136nullsmsg0 : Smsg0;
137
138fsysproc()
139{
140	n, ok : int;
141	x : ref Xfid;
142	f : ref Fid;
143	t : Smsg0;
144
145	acme->fsyspid = sys->pctl(0, nil);
146	x = nil;
147	for(;;){
148		if(x == nil){
149			cxfidalloc <-= nil;
150			x = <-cxfidalloc;
151		}
152		n = sys->read(sfd, x.buf, messagesize);
153		if(n <= 0) {
154			if (closing)
155				break;
156			error("i/o error on server channel");
157		}
158		(ok, x.fcall) = Tmsg.unpack(x.buf[0:n]);
159		if(ok < 0)
160			error("convert error in convM2S");
161		if(DEBUG)
162			utils->debug(sprint("%d:%s\n", x.tid, x.fcall.text()));
163		pick fc := x.fcall {
164			Version =>
165				f = nil;
166			Auth =>
167				f = nil;
168			* =>
169				f = allocfid(fid(x.fcall));
170		}
171		x.f = f;
172		pick fc := x.fcall {
173			Readerror =>	x = fsyserror();
174			Flush =>		x = fsysflush(x);
175			Version =>	x = fsysversion(x);
176			Auth =>		x = fsysauth(x);
177			Attach =>		x = fsysattach(x, f);
178			Walk =>		x = fsyswalk(x, f);
179			Open =>		x = fsysopen(x, f);
180			Create =>		x = fsyscreate(x);
181			Read =>		x = fsysread(x, f);
182			Write =>		x = fsyswrite(x);
183			Clunk =>		x = fsysclunk(x, f);
184			Remove =>	x = fsysremove(x);
185			Stat =>		x = fsysstat(x, f);
186			Wstat =>		x = fsyswstat(x);
187			# Clone =>	x = fsysclone(x, f);
188			* =>
189				x = respond(x, t, "bad fcall type");
190		}
191	}
192}
193
194fsysaddid(dir : string, ndir : int, incl : array of string, nincl : int) : ref Mntdir
195{
196	m : ref Mntdir;
197	id : int;
198
199	mnt.qlock.lock();
200	id = ++mnt.id;
201	m = ref Mntdir;
202	m.id = id;
203	m.dir =  dir;
204	m.refs = 1;	# one for Command, one will be incremented in attach
205	m.ndir = ndir;
206	m.next = mnt.md;
207	m.incl = incl;
208	m.nincl = nincl;
209	mnt.md = m;
210	mnt.qlock.unlock();
211	return m;
212}
213
214fsysdelid(idm : ref Mntdir)
215{
216	m, prev : ref Mntdir;
217	i : int;
218
219	if(idm == nil)
220		return;
221	mnt.qlock.lock();
222	if(--idm.refs > 0){
223		mnt.qlock.unlock();
224		return;
225	}
226	prev = nil;
227	for(m=mnt.md; m != nil; m=m.next){
228		if(m == idm){
229			if(prev != nil)
230				prev.next = m.next;
231			else
232				mnt.md = m.next;
233			for(i=0; i<m.nincl; i++)
234				m.incl[i] = nil;
235			m.incl = nil;
236			m.dir = nil;
237			m = nil;
238			mnt.qlock.unlock();
239			return;
240		}
241		prev = m;
242	}
243	mnt.qlock.unlock();
244	buf := sys->sprint("fsysdelid: can't find id %d\n", idm.id);
245	cerr <-= buf;
246}
247
248#
249# Called only in exec.l:run(), from a different FD group
250#
251fsysmount(dir : string, ndir : int, incl : array of string, nincl : int) : ref Mntdir
252{
253	m : ref Mntdir;
254
255	# close server side so don't hang if acme is half-exited
256	# sfd = nil;
257	m = fsysaddid(dir, ndir, incl, nincl);
258	buf := sys->sprint("%d", m.id);
259	if(sys->mount(cfd, nil, "/mnt/acme", Sys->MREPL, buf) < 0){
260		fsysdelid(m);
261		return nil;
262	}
263	# cfd = nil;
264	sys->bind("/mnt/acme", "/chan", Sys->MBEFORE);	# was MREPL
265	if(sys->bind("/mnt/acme", "/dev", Sys->MBEFORE) < 0){
266		fsysdelid(m);
267		return nil;
268	}
269	return m;
270}
271
272fsysclose()
273{
274	closing = 1;
275	# sfd = cfd = nil;
276}
277
278respond(x : ref Xfid, t0 : Smsg0, err : string) : ref Xfid
279{
280	t : ref Rmsg;
281
282	# t = nullsmsg;
283	tag := x.fcall.tag;
284	# fid := fid(x.fcall);
285	qid := t0.qid;
286	if(err != nil)
287		t = ref Rmsg.Error(tag, err);
288	else
289	pick fc := x.fcall {
290		Readerror =>	t = ref Rmsg.Error(tag, err);
291		Flush =>		t = ref Rmsg.Flush(tag);
292		Version =>	t = ref Rmsg.Version(tag, t0.msize, t0.version);
293		Auth =>		t = ref Rmsg.Auth(tag, qid);
294		# Clone =>	t = ref Rmsg.Clone(tag, fid);
295		Attach =>		t = ref Rmsg.Attach(tag, qid);
296		Walk =>		t = ref Rmsg.Walk(tag, t0.qids);
297		Open =>		t = ref Rmsg.Open(tag, qid, t0.iounit);
298		Create =>		t = ref Rmsg.Create(tag, qid, 0);
299		Read =>		if(t0.count == len t0.data)
300						t = ref Rmsg.Read(tag, t0.data);
301					else
302						t = ref Rmsg.Read(tag, t0.data[0: t0.count]);
303		Write =>		t = ref Rmsg.Write(tag, t0.count);
304		Clunk =>		t = ref Rmsg.Clunk(tag);
305		Remove =>	t = ref Rmsg.Remove(tag);
306		Stat =>		t = ref Rmsg.Stat(tag, t0.stat);
307		Wstat =>		t = ref Rmsg.Wstat(tag);
308
309	}
310	# t.qid = t0.qid;
311	# t.count = t0.count;
312	# t.data = t0.data;
313	# t.stat = t0.stat;
314	# t.fid = x.fcall.fid;
315	# t.tag = x.fcall.tag;
316	buf := t.pack();
317	if(buf == nil)
318		error("convert error in convS2M");
319	if(sys->write(sfd, buf, len buf) != len buf)
320		error("write error in respond");
321	buf = nil;
322	if(DEBUG)
323		utils->debug(sprint("%d:r: %s\n", x.tid, t.text()));
324	return x;
325}
326
327# fsysnop(x : ref Xfid) : ref Xfid
328# {
329# 	t : Smsg0;
330#
331# 	return respond(x, t, nil);
332# }
333
334fsyserror() : ref Xfid
335{
336	error("sys error : Terror");
337	return nil;
338}
339
340fsyssession(x : ref Xfid) : ref Xfid
341{
342	t : Smsg0;
343
344	# BUG: should shut everybody down ??
345	t = nullsmsg0;
346	return respond(x, t, nil);
347}
348
349fsysversion(x : ref Xfid) : ref Xfid
350{
351	t : Smsg0;
352
353	pick m := x.fcall {
354		Version =>
355			(t.msize, t.version) = styx->compatible(m, messagesize, nil);
356			messagesize = t.msize;
357			return respond(x, t, nil);
358	}
359	return respond(x, t, "acme: bad version");
360
361	# ms := msize(x.fcall);
362	# if(ms < 256)
363	# 	return respond(x, t, "version: message size too small");
364	# t.msize = messagesize = ms;
365	# v := version(x.fcall);
366	# if(len v < 6 || v[0: 6] != "9P2000")
367	# 	return respond(x, t, "unrecognized 9P version");
368	# t.version = "9P2000";
369	# return respond(x, t, nil);
370}
371
372fsysauth(x : ref Xfid) : ref Xfid
373{
374	t : Smsg0;
375
376	return respond(x, t, "acme: authentication not required");
377}
378
379fsysflush(x : ref Xfid) : ref Xfid
380{
381	x.c <-= Xfidm->Xflush;
382	return nil;
383}
384
385fsysattach(x : ref Xfid, f : ref Fid) : ref Xfid
386{
387	t : Smsg0;
388	id : int;
389	m : ref Mntdir;
390
391	if (uname(x.fcall) != user)
392		return respond(x, t, Eperm);
393	f.busy = TRUE;
394	f.open = FALSE;
395	f.qid = (Qid)(big Qdir, 0, QTDIR);
396	f.dir = dirtab;
397	f.nrpart = 0;
398	f.w = nil;
399	t.qid = f.qid;
400	f.mntdir = nil;
401	id = int aname(x.fcall);
402	mnt.qlock.lock();
403	for(m=mnt.md; m != nil; m=m.next)
404		if(m.id == id){
405			f.mntdir = m;
406			m.refs++;
407			break;
408		}
409	if(m == nil)
410		cerr <-= "unknown id in attach";
411	mnt.qlock.unlock();
412	return respond(x, t, nil);
413}
414
415fsyswalk(x : ref Xfid, f : ref Fid) : ref Xfid
416{
417	t : Smsg0;
418	c, i, j, id : int;
419	path, qtype : int;
420	d, dir : array of Dirtab;
421	w : ref Window;
422	nf : ref Fid;
423
424	if(f.open)
425		return respond(x, t, "walk of open file");
426	if(fid(x.fcall) != newfid(x.fcall)){
427		nf = allocfid(newfid(x.fcall));
428		if(nf.busy)
429			return respond(x, t, "newfid already in use");
430		nf.busy = TRUE;
431		nf.open = FALSE;
432		nf.mntdir = f.mntdir;
433		if(f.mntdir != nil)
434			f.mntdir.refs++;
435		nf.dir = f.dir;
436		nf.qid = f.qid;
437		nf.w = f.w;
438		nf.nrpart = 0;	# not open, so must be zero
439		if(nf.w != nil)
440			nf.w.refx.inc();
441		f = nf;	# walk f
442	}
443
444	qtype = QTFILE;
445	wqids: list of Qid;
446	err := string nil;
447	id = WIN(f.qid);
448	q := f.qid;
449	names := styxaux->names(x.fcall);
450	nwname := len names;
451
452	if(nwname > 0){
453		for(i = 0; i < nwname; i++){
454			if((q.qtype & QTDIR) == 0){
455				err = Enotdir;
456				break;
457			}
458
459			name := names[i];
460			if(name == ".."){
461				path = Qdir;
462				qtype = QTDIR;
463				id = 0;
464				if(w != nil){
465					w.close();
466					w = nil;
467				}
468				if(i == MAXWELEM){
469					err = "name too long";
470					break;
471				}
472				q.qtype = qtype;
473				q.vers = 0;
474				q.path = big QID(id, path);
475				wqids = q :: wqids;
476				continue;
477			}
478
479			# is it a numeric name?
480			regular := 0;
481			for(j=0; j < len name; j++) {
482				c = name[j];
483				if(c<'0' || '9'<c) {
484					regular = 1;
485					break;
486				}
487			}
488
489			if (!regular) {
490				# yes: it's a directory
491				if(w != nil)	# name has form 27/23; get out before losing w
492					break;
493				id = int name;
494				row.qlock.lock();
495				w = lookid(id, FALSE);
496				if(w == nil){
497					row.qlock.unlock();
498					break;
499				}
500				w.refx.inc();
501				path = Qdir;
502				qtype = QTDIR;
503				row.qlock.unlock();
504				dir = dirtabw;
505				if(i == MAXWELEM){
506					err = "name too long";
507					break;
508				}
509				q.qtype = qtype;
510				q.vers = 0;
511				q.path = big QID(id, path);
512				wqids = q :: wqids;
513				continue;
514			}
515			else {
516				# if(FILE(f.qid) == Qacme) 	# empty directory
517				#	break;
518				if(name == "new"){
519					if(w != nil)
520						error("w set in walk to new");
521					cw := chan of ref Window;
522					spawn x.walk(cw);
523					w = <- cw;
524					w.refx.inc();
525					path = QID(w.id, Qdir);
526					qtype = QTDIR;
527					id = w.id;
528					dir = dirtabw;
529					# x.c <-= Xfidm->Xwalk;
530					if(i == MAXWELEM){
531						err = "name too long";
532						break;
533					}
534					q.qtype = qtype;
535					q.vers = 0;
536					q.path = big QID(id, path);
537					wqids = q :: wqids;
538					continue;
539				}
540
541				if(id == 0)
542					d = dirtab;
543				else
544					d = dirtabw;
545				k := 1;	# skip '.'
546				found := 0;
547				for( ; d[k].name != nil; k++){
548					if(name == d[k].name){
549						path = d[k].qid;
550						qtype = d[k].qtype;
551						dir = d[k:];
552						if(i == MAXWELEM){
553							err = "name too long";
554							break;
555						}
556						q.qtype = qtype;
557						q.vers = 0;
558						q.path = big QID(id, path);
559						wqids = q :: wqids;
560						found = 1;
561						break;
562					}
563				}
564				if(found)
565					continue;
566				break;	# file not found
567			}
568		}
569
570		if(i == 0 && err == nil)
571			err = Eexist;
572	}
573
574	nwqid := len wqids;
575	if(nwqid > 0){
576		t.qids = array[nwqid] of Qid;
577		for(i = nwqid-1; i >= 0; i--){
578			t.qids[i] = hd wqids;
579			wqids = tl wqids;
580		}
581	}
582	if(err != nil || nwqid < nwname){
583		if(nf != nil){
584			nf.busy = FALSE;
585			fsysdelid(nf.mntdir);
586		}
587	}
588	else if(nwqid == nwname){
589		if(w != nil){
590			f.w = w;
591			w = nil;
592		}
593		if(dir != nil)
594			f.dir = dir;
595		f.qid = q;
596	}
597
598	if(w != nil)
599		w.close();
600
601	return respond(x, t, err);
602}
603
604fsysopen(x : ref Xfid, f : ref Fid) : ref Xfid
605{
606	t : Smsg0;
607	m : int;
608
609	# can't truncate anything, so just disregard
610	setmode(x.fcall, mode(x.fcall)&~OTRUNC);
611	# can't execute or remove anything
612	if(mode(x.fcall)&ORCLOSE)
613		return respond(x, t, Eperm);
614	case(mode(x.fcall)){
615	OREAD =>
616		m = 8r400;
617	OWRITE =>
618		m = 8r200;
619	ORDWR =>
620		m = 8r600;
621	* =>
622		return respond(x, t, Eperm);
623	}
624	if(((f.dir[0].perm&~(DMDIR|DMAPPEND))&m) != m)
625		return respond(x, t, Eperm);
626	x.c <-= Xfidm->Xopen;
627	return nil;
628}
629
630fsyscreate(x : ref Xfid) : ref Xfid
631{
632	t : Smsg0;
633
634	return respond(x, t, Eperm);
635}
636
637idcmp(a, b : int) : int
638{
639	return a-b;
640}
641
642qsort(a : array of int, n : int)
643{
644	i, j : int;
645	t : int;
646
647	while(n > 1) {
648		i = n>>1;
649		t = a[0]; a[0] = a[i]; a[i] = t;
650		i = 0;
651		j = n;
652		for(;;) {
653			do
654				i++;
655			while(i < n && idcmp(a[i], a[0]) < 0);
656			do
657				j--;
658			while(j > 0 && idcmp(a[j], a[0]) > 0);
659			if(j < i)
660				break;
661			t = a[i]; a[i] = a[j]; a[j] = t;
662		}
663		t = a[0]; a[0] = a[j]; a[j] = t;
664		n = n-j-1;
665		if(j >= n) {
666			qsort(a, j);
667			a = a[j+1:];
668		} else {
669			qsort(a[j+1:], n);
670			n = j;
671		}
672	}
673}
674
675fsysread(x : ref Xfid, f : ref Fid) : ref Xfid
676{
677	t : Smsg0;
678	b : array of byte;
679	i, id, n, o, e, j, k, nids : int;
680	ids : array of int;
681	d : array of Dirtab;
682	dt : Dirtab;
683	c : ref Column;
684	clock : int;
685
686	b = nil;
687	if(f.qid.qtype & QTDIR){
688		# if(int offset(x.fcall) % DIRLEN)
689		#	return respond(x, t, "illegal offset in directory");
690		if(FILE(f.qid) == Qacme){	# empty dir
691			t.data = nil;
692			t.count = 0;
693			respond(x, t, nil);
694			return x;
695		}
696		o = int offset(x.fcall);
697		e = int offset(x.fcall)+count(x.fcall);
698		clock = getclock();
699		b = array[messagesize] of byte;
700		id = WIN(f.qid);
701		n = 0;
702		if(id > 0)
703			d = dirtabw;
704		else
705			d = dirtab;
706		k = 1;	# first entry is '.'
707		leng := 0;
708		for(i=0; d[k].name!=nil && i<e; i+=leng){
709			bb := styx->packdir(dostat(WIN(x.f.qid), d[k], clock));
710			leng = len bb;
711			for (kk := 0; kk < leng; kk++)
712				b[kk+n] = bb[kk];
713			bb = nil;
714			if(leng <= Styx->BIT16SZ)
715				break;
716			if(i >= o)
717				n += leng;
718			k++;
719		}
720		if(id == 0){
721			row.qlock.lock();
722			nids = 0;
723			ids = nil;
724			for(j=0; j<row.ncol; j++){
725				c = row.col[j];
726				for(k=0; k<c.nw; k++){
727					oids := ids;
728					ids = array[nids+1] of int;
729					ids[0:] = oids[0:nids];
730					oids = nil;
731					ids[nids++] = c.w[k].id;
732				}
733			}
734			row.qlock.unlock();
735			qsort(ids, nids);
736			j = 0;
737			for(; j<nids && i<e; i+=leng){
738				k = ids[j];
739				dt.name = sys->sprint("%d", k);
740				dt.qid = QID(k, 0);
741				dt.qtype = QTDIR;
742				dt.perm = DMDIR|8r700;
743				bb := styx->packdir(dostat(k, dt, clock));
744				leng = len bb;
745				for (kk := 0; kk < leng; kk++)
746					b[kk+n] = bb[kk];
747				bb = nil;
748				if(leng == 0)
749					break;
750				if(i >= o)
751					n += leng;
752				j++;
753			}
754			ids = nil;
755		}
756		t.data = b;
757		t.count = n;
758		respond(x, t, nil);
759		b = nil;
760		return x;
761	}
762	x.c <-= Xfidm->Xread;
763	return nil;
764}
765
766fsyswrite(x : ref Xfid) : ref Xfid
767{
768	x.c <-= Xfidm->Xwrite;
769	return nil;
770}
771
772fsysclunk(x : ref Xfid, f : ref Fid) : ref Xfid
773{
774	t : Smsg0;
775
776	fsysdelid(f.mntdir);
777	if(f.open){
778		f.busy = FALSE;
779		f.open = FALSE;
780		x.c <-= Xfidm->Xclose;
781		return nil;
782	}
783	if(f.w != nil)
784		f.w.close();
785	f.busy = FALSE;
786	f.open = FALSE;
787	return respond(x, t, nil);
788}
789
790fsysremove(x : ref Xfid) : ref Xfid
791{
792	t : Smsg0;
793
794	return respond(x, t, Eperm);
795}
796
797fsysstat(x : ref Xfid, f : ref Fid) : ref Xfid
798{
799	t : Smsg0;
800
801	t.stat = dostat(WIN(x.f.qid), f.dir[0], getclock());
802	return respond(x, t, nil);
803}
804
805fsyswstat(x : ref Xfid) : ref Xfid
806{
807	t : Smsg0;
808
809	return respond(x, t, Eperm);
810}
811
812allocfid(fid : int) : ref Fid
813{
814	f, ff : ref Fid;
815	fh : int;
816
817	ff = nil;
818	fh = fid&(Nhash-1);
819	for(f=fids[fh]; f != nil; f=f.next)
820		if(f.fid == fid)
821			return f;
822		else if(ff==nil && f.busy==FALSE)
823			ff = f;
824	if(ff != nil){
825		ff.fid = fid;
826		return ff;
827	}
828	f = ref Fid;
829	f.busy = FALSE;
830	f.rpart = array[Sys->UTFmax] of byte;
831	f.nrpart = 0;
832	f.fid = fid;
833	f.next = fids[fh];
834	fids[fh] = f;
835	return f;
836}
837
838cbuf := array[32] of byte;
839
840getclock() : int
841{
842	sys->seek(clockfd, big 0, 0);
843	n := sys->read(clockfd, cbuf, len cbuf);
844	return int string cbuf[0:n];
845}
846
847dostat(id : int, dir : Dirtab, clock : int) : Sys->Dir
848{
849	d : Dir;
850
851	d.qid.path = big QID(id, dir.qid);
852	d.qid.vers = 0;
853	d.qid.qtype = dir.qtype;
854	d.mode = dir.perm;
855	d.length = big 0;	# would be nice to do better
856	d.name = dir.name;
857	d.uid = user;
858	d.gid = user;
859	d.atime = clock;
860	d.mtime = clock;
861	d.dtype = d.dev = 0;
862	return d;
863	# buf := styx->convD2M(d);
864	# d = nil;
865	# return buf;
866}
867