xref: /inferno-os/appl/lib/ninep.b (revision e45fa0eb0763b57d6fb0649c064bc3b95ccdea6c)
1implement Ninep;
2
3include "sys.m";
4	sys: Sys;
5
6include "9p.m";
7
8STR: con BIT16SZ;	# string length
9TAG: con BIT16SZ;
10FID: con BIT32SZ;
11QID: con BIT8SZ+BIT32SZ+BIT64SZ;
12LEN: con BIT16SZ;	# stat and qid array lengths
13COUNT: con BIT32SZ;
14OFFSET: con BIT64SZ;
15
16H: con BIT32SZ+BIT8SZ+BIT16SZ;	# minimum header length: size[4] type tag[2]
17
18#
19# the following array could be shorter if it were indexed by (type-Tversion)
20#
21hdrlen := array[Tmax] of
22{
23Tversion =>	H+COUNT+STR,	# size[4] Tversion tag[2] msize[4] version[s]
24Rversion =>	H+COUNT+STR,	# size[4] Rversion tag[2] msize[4] version[s]
25
26Tauth =>	H+FID+STR+STR,		# size[4] Tauth tag[2] afid[4] uname[s] aname[s]
27Rauth =>	H+QID,			# size[4] Rauth tag[2] aqid[13]
28
29Rerror =>	H+STR,		# size[4] Rerror tag[2] ename[s]
30
31Tflush =>	H+TAG,		# size[4] Tflush tag[2] oldtag[2]
32Rflush =>	H,			# size[4] Rflush tag[2]
33
34Tattach =>	H+FID+FID+STR+STR,	# size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
35Rattach =>	H+QID,		# size[4] Rattach tag[2] qid[13]
36
37Twalk =>	H+FID+FID+LEN,	# size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
38Rwalk =>	H+LEN,		# size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
39
40Topen =>	H+FID+BIT8SZ,		# size[4] Topen tag[2] fid[4] mode[1]
41Ropen =>	H+QID+COUNT,	# size[4] Ropen tag[2] qid[13] iounit[4]
42
43Tcreate =>	H+FID+STR+BIT32SZ+BIT8SZ,	# size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1]
44Rcreate =>	H+QID+COUNT,	# size[4] Rcreate tag[2] qid[13] iounit[4]
45
46Tread =>	H+FID+OFFSET+COUNT,	# size[4] Tread tag[2] fid[4] offset[8] count[4]
47Rread =>	H+COUNT,		# size[4] Rread tag[2] count[4] data[count]
48
49Twrite =>	H+FID+OFFSET+COUNT,	# size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
50Rwrite =>	H+COUNT,	# size[4] Rwrite tag[2] count[4]
51
52Tclunk =>	H+FID,	# size[4] Tclunk tag[2] fid[4]
53Rclunk =>	H,		# size[4] Rclunk tag[2]
54
55Tremove =>	H+FID,	# size[4] Tremove tag[2] fid[4]
56Rremove =>	H,	# size[4] Rremove tag[2]
57
58Tstat =>	H+FID,	# size[4] Tstat tag[2] fid[4]
59Rstat =>	H+LEN,	# size[4] Rstat tag[2] stat[n]
60
61Twstat =>	H+FID+LEN,	# size[4] Twstat tag[2] fid[4] stat[n]
62Rwstat =>	H,	# size[4] Rwstat tag[2]
63};
64
65init()
66{
67	sys = load Sys Sys->PATH;
68}
69
70utflen(s: string): int
71{
72	# the domain is 16-bit unicode only, which is all that Inferno now implements
73	n := l := len s;
74	for(i:=0; i<l; i++)
75		if((c := s[i]) > 16r7F){
76			n++;
77			if(c > 16r7FF)
78				n++;
79		}
80	return n;
81}
82
83packdirsize(d: Sys->Dir): int
84{
85	return STATFIXLEN+utflen(d.name)+utflen(d.uid)+utflen(d.gid)+utflen(d.muid);
86}
87
88packdir(f: Sys->Dir): array of byte
89{
90	ds := packdirsize(f);
91	a := array[ds] of byte;
92	# size[2]
93	a[0] = byte (ds-LEN);
94	a[1] = byte ((ds-LEN)>>8);
95	# type[2]
96	a[2] = byte f.dtype;
97	a[3] = byte (f.dtype>>8);
98	# dev[4]
99	a[4] = byte f.dev;
100	a[5] = byte (f.dev>>8);
101	a[6] = byte (f.dev>>16);
102	a[7] = byte (f.dev>>24);
103	# qid.type[1]
104	# qid.vers[4]
105	# qid.path[8]
106	pqid(a, 8, f.qid);
107	# mode[4]
108	a[21] = byte f.mode;
109	a[22] = byte (f.mode>>8);
110	a[23] = byte (f.mode>>16);
111	a[24] = byte (f.mode>>24);
112	# atime[4]
113	a[25] = byte f.atime;
114	a[26] = byte (f.atime>>8);
115	a[27] = byte (f.atime>>16);
116	a[28] = byte (f.atime>>24);
117	# mtime[4]
118	a[29] = byte f.mtime;
119	a[30] = byte (f.mtime>>8);
120	a[31] = byte (f.mtime>>16);
121	a[32] = byte (f.mtime>>24);
122	# length[8]
123	p64(a, 33, big f.length);
124	# name[s]
125	i := pstring(a, 33+BIT64SZ, f.name);
126	i = pstring(a, i, f.uid);
127	i = pstring(a, i, f.gid);
128	i = pstring(a, i, f.muid);
129	if(i != len a)
130		raise "assertion: Ninep->packdir: bad count";	# can't happen unless packedsize is wrong
131	return a;
132}
133
134pqid(a: array of byte, o: int, q: Sys->Qid): int
135{
136	a[o] = byte q.qtype;
137	v := q.vers;
138	a[o+1] = byte v;
139	a[o+2] = byte (v>>8);
140	a[o+3] = byte (v>>16);
141	a[o+4] = byte (v>>24);
142	v = int q.path;
143	a[o+5] = byte v;
144	a[o+6] = byte (v>>8);
145	a[o+7] = byte (v>>16);
146	a[o+8] = byte (v>>24);
147	v = int (q.path >> 32);
148	a[o+9] = byte v;
149	a[o+10] = byte (v>>8);
150	a[o+11] = byte (v>>16);
151	a[o+12] = byte (v>>24);
152	return o+QID;
153}
154
155pstring(a: array of byte, o: int, s: string): int
156{
157	sa := array of byte s;	# could do conversion ourselves
158	n := len sa;
159	a[o] = byte n;
160	a[o+1] = byte (n>>8);
161	a[o+2:] = sa;
162	return o+LEN+n;
163}
164
165p32(a: array of byte, o: int, v: int): int
166{
167	a[o] = byte v;
168	a[o+1] = byte (v>>8);
169	a[o+2] = byte (v>>16);
170	a[o+3] = byte (v>>24);
171	return o+BIT32SZ;
172}
173
174p64(a: array of byte, o: int, b: big): int
175{
176	i := int b;
177	a[o] = byte i;
178	a[o+1] = byte (i>>8);
179	a[o+2] = byte (i>>16);
180	a[o+3] = byte (i>>24);
181	i = int (b>>32);
182	a[o+4] = byte i;
183	a[o+5] = byte (i>>8);
184	a[o+6] = byte (i>>16);
185	a[o+7] = byte (i>>24);
186	return o+BIT64SZ;
187}
188
189unpackdir(a: array of byte): (int, Sys->Dir)
190{
191	dir: Sys->Dir;
192
193	if(len a < STATFIXLEN)
194		return (0, dir);
195	# size[2]
196	sz := ((int a[1] << 8) | int a[0])+LEN;	# bytes this packed dir should occupy
197	if(len a < sz)
198		return (0, dir);
199	# type[2]
200	dir.dtype = (int a[3]<<8) | int a[2];
201	# dev[4]
202	dir.dev = (((((int a[7] << 8) | int a[6]) << 8) | int a[5]) << 8) | int a[4];
203	# qid.type[1]
204	# qid.vers[4]
205	# qid.path[8]
206	dir.qid = gqid(a, 8);
207	# mode[4]
208	dir.mode = (((((int a[24] << 8) | int a[23]) << 8) | int a[22]) << 8) | int a[21];
209	# atime[4]
210	dir.atime = (((((int a[28] << 8) | int a[27]) << 8) | int a[26]) << 8) | int a[25];
211	# mtime[4]
212	dir.mtime = (((((int a[32] << 8) | int a[31]) << 8) | int a[30]) << 8) | int a[29];
213	# length[8]
214	v0 := (((((int a[36] << 8) | int a[35]) << 8) | int a[34]) << 8) | int a[33];
215	v1 := (((((int a[40] << 8) | int a[39]) << 8) | int a[38]) << 8) | int a[37];
216	dir.length = (big v1 << 32) | (big v0 & 16rFFFFFFFF);
217	# name[s], uid[s], gid[s], muid[s]
218	i: int;
219	(dir.name, i) = gstring(a, 41);
220	(dir.uid, i) = gstring(a, i);
221	(dir.gid, i) = gstring(a, i);
222	(dir.muid, i) = gstring(a, i);
223	if(i != sz)
224		return (0, dir);
225	return (i, dir);
226}
227
228gqid(f: array of byte, i: int): Sys->Qid
229{
230	qtype := int f[i];
231	vers := (((((int f[i+4] << 8) | int f[i+3]) << 8) | int f[i+2]) << 8) | int f[i+1];
232	i += BIT8SZ+BIT32SZ;
233	path0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
234	i += BIT32SZ;
235	path1 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
236	path := (big path1 << 32) | (big path0 & 16rFFFFFFFF);
237	return (path, vers, qtype);
238}
239
240g32(f: array of byte, i: int): int
241{
242	return (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
243}
244
245g64(f: array of byte, i: int): big
246{
247	b0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
248	b1 := (((((int f[i+7] << 8) | int f[i+6]) << 8) | int f[i+5]) << 8) | int f[i+4];
249	return (big b1 << 32) | (big b0 & 16rFFFFFFFF);
250}
251
252gstring(a: array of byte, o: int): (string, int)
253{
254	if(o < 0 || o+STR > len a)
255		return (nil, -1);
256	l := (int a[o+1] << 8) | int a[o];
257	o += STR;
258	e := o+l;
259	if(e > len a)
260		return (nil, -1);
261	return (string a[o:e], e);
262}
263
264ttag2type := array[] of {
265tagof Tmsg.Readerror => 0,
266tagof Tmsg.Version => Tversion,
267tagof Tmsg.Auth => Tauth,
268tagof Tmsg.Attach => Tattach,
269tagof Tmsg.Flush => Tflush,
270tagof Tmsg.Walk => Twalk,
271tagof Tmsg.Open => Topen,
272tagof Tmsg.Create => Tcreate,
273tagof Tmsg.Read => Tread,
274tagof Tmsg.Write => Twrite,
275tagof Tmsg.Clunk => Tclunk,
276tagof Tmsg.Stat => Tstat,
277tagof Tmsg.Remove => Tremove,
278tagof Tmsg.Wstat => Twstat,
279};
280
281Tmsg.mtype(t: self ref Tmsg): int
282{
283	return ttag2type[tagof t];
284}
285
286Tmsg.packedsize(t: self ref Tmsg): int
287{
288	mtype := ttag2type[tagof t];
289	if(mtype <= 0)
290		return 0;
291	ml := hdrlen[mtype];
292	pick m := t {
293	Version =>
294		ml += utflen(m.version);
295	Auth =>
296		ml += utflen(m.uname)+utflen(m.aname);
297	Attach =>
298		ml += utflen(m.uname)+utflen(m.aname);
299	Walk =>
300		for(i:=0; i<len m.names; i++)
301			ml += STR+utflen(m.names[i]);
302	Create =>
303		ml += utflen(m.name);
304	Write =>
305		ml += len m.data;
306	Wstat =>
307		ml += packdirsize(m.stat);
308	}
309	return ml;
310}
311
312Tmsg.pack(t: self ref Tmsg): array of byte
313{
314	if(t == nil)
315		return nil;
316	ds := t.packedsize();
317	if(ds <= 0)
318		return nil;
319	d := array[ds] of byte;
320	d[0] = byte ds;
321	d[1] = byte (ds>>8);
322	d[2] = byte (ds>>16);
323	d[3] = byte (ds>>24);
324	d[4] = byte ttag2type[tagof t];
325	d[5] = byte t.tag;
326	d[6] = byte (t.tag >> 8);
327	pick m := t {
328	Version =>
329		p32(d, H, m.msize);
330		pstring(d, H+COUNT, m.version);
331	Auth =>
332		p32(d, H, m.afid);
333		o := pstring(d, H+FID, m.uname);
334		pstring(d, o, m.aname);
335	Flush =>
336		v := m.oldtag;
337		d[H] = byte v;
338		d[H+1] = byte (v>>8);
339	Attach =>
340		p32(d, H, m.fid);
341		p32(d, H+FID, m.afid);
342		o := pstring(d, H+2*FID, m.uname);
343		pstring(d, o, m.aname);
344	Walk =>
345		d[H] = byte m.fid;
346		d[H+1] = byte (m.fid>>8);
347		d[H+2] = byte (m.fid>>16);
348		d[H+3] = byte (m.fid>>24);
349		d[H+FID] = byte m.newfid;
350		d[H+FID+1] = byte (m.newfid>>8);
351		d[H+FID+2] = byte (m.newfid>>16);
352		d[H+FID+3] = byte (m.newfid>>24);
353		n := len m.names;
354		d[H+2*FID] = byte n;
355		d[H+2*FID+1] = byte (n>>8);
356		o := H+2*FID+LEN;
357		for(i := 0; i < n; i++)
358			o = pstring(d, o, m.names[i]);
359	Open =>
360		p32(d, H, m.fid);
361		d[H+FID] = byte m.mode;
362	Create =>
363		p32(d, H, m.fid);
364		o := pstring(d, H+FID, m.name);
365		p32(d, o, m.perm);
366		d[o+BIT32SZ] = byte m.mode;
367	Read =>
368		p32(d, H, m.fid);
369		p64(d, H+FID, m.offset);
370		p32(d, H+FID+OFFSET, m.count);
371	Write =>
372		p32(d, H, m.fid);
373		p64(d, H+FID, m.offset);
374		n := len m.data;
375		p32(d, H+FID+OFFSET, n);
376		d[H+FID+OFFSET+COUNT:] = m.data;
377	Clunk or Remove or Stat =>
378		p32(d, H, m.fid);
379	Wstat =>
380		p32(d, H, m.fid);
381		stat := packdir(m.stat);
382		n := len stat;
383		d[H+FID] = byte n;
384		d[H+FID+1] = byte (n>>8);
385		d[H+FID+LEN:] = stat;
386	* =>
387		raise sys->sprint("assertion: Ninep->Tmsg.pack: bad tag: %d", tagof t);
388	}
389	return d;
390}
391
392Tmsg.unpack(f: array of byte): (int, ref Tmsg)
393{
394	if(len f < H)
395		return (0, nil);
396	size := (int f[1] << 8) | int f[0];
397	size |= ((int f[3] << 8) | int f[2]) << 16;
398	if(len f != size){
399		if(len f < size)
400			return (0, nil);	# need more data
401		f = f[0:size];	# trim to exact length
402	}
403	mtype := int f[4];
404	if(mtype >= len hdrlen || (mtype&1) != 0 || size < hdrlen[mtype])
405		return (-1, nil);
406
407	tag := (int f[6] << 8) | int f[5];
408	fid := 0;
409	if(hdrlen[mtype] >= H+FID)
410		fid = g32(f, H);	# fid is always in same place: extract it once for all if there
411
412	# return out of each case body for a legal message;
413	# break out of the case for an illegal one
414
415Decode:
416	case mtype {
417	* =>
418		sys->print("styx: Tmsg.unpack: bad type %d\n", mtype);
419	Tversion =>
420		msize := fid;
421		(version, o) := gstring(f, H+COUNT);
422		if(o <= 0)
423			break;
424		return (o, ref Tmsg.Version(tag, msize, version));
425	Tauth =>
426		(uname, o1) := gstring(f, H+FID);
427		(aname, o2) := gstring(f, o1);
428		if(o2 <= 0)
429			break;
430		return (o2, ref Tmsg.Auth(tag, fid, uname, aname));
431	Tflush =>
432		oldtag := (int f[H+1] << 8) | int f[H];
433		return (H+TAG, ref Tmsg.Flush(tag, oldtag));
434	Tattach =>
435		afid := g32(f, H+FID);
436		(uname, o1) := gstring(f, H+2*FID);
437		(aname, o2) := gstring(f, o1);
438		if(o2 <= 0)
439			break;
440		return (o2, ref Tmsg.Attach(tag, fid, afid, uname, aname));
441	Twalk =>
442		newfid := g32(f, H+FID);
443		n := (int f[H+2*FID+1] << 8) | int f[H+2*FID];
444		if(n > MAXWELEM)
445			break;
446		o := H+2*FID+LEN;
447		names: array of string = nil;
448		if(n > 0){
449			names = array[n] of string;
450			for(i:=0; i<n; i++){
451				(names[i], o) = gstring(f, o);
452				if(o <= 0)
453					break Decode;
454			}
455		}
456		return (o, ref Tmsg.Walk(tag, fid, newfid, names));
457	Topen =>
458		return (H+FID+BIT8SZ, ref Tmsg.Open(tag, fid, int f[H+FID]));
459	Tcreate =>
460		(name, o) := gstring(f, H+FID);
461		if(o <= 0 || o+BIT32SZ+BIT8SZ > len f)
462			break;
463		perm := g32(f, o);
464		o += BIT32SZ;
465		mode := int f[o++];
466		return (o, ref Tmsg.Create(tag, fid, name, perm, mode));
467	Tread =>
468		offset := g64(f, H+FID);
469		count := g32(f, H+FID+OFFSET);
470		return (H+FID+OFFSET+COUNT, ref Tmsg.Read(tag, fid, offset, count));
471	Twrite =>
472		offset := g64(f, H+FID);
473		count := g32(f, H+FID+OFFSET);
474		O: con H+FID+OFFSET+COUNT;
475		if(count > len f-O)
476			break;
477		data := f[O:O+count];
478		return (O+count, ref Tmsg.Write(tag, fid, offset, data));
479	Tclunk =>
480		return (H+FID, ref Tmsg.Clunk(tag, fid));
481	Tremove =>
482		return (H+FID, ref Tmsg.Remove(tag, fid));
483	Tstat =>
484		return (H+FID, ref Tmsg.Stat(tag, fid));
485	Twstat =>
486		n := int (f[H+FID+1]<<8) | int f[H+FID];
487		if(len f < H+FID+LEN+n)
488			break;
489		(ds, stat) := unpackdir(f[H+FID+LEN:]);
490		if(ds != n){
491			sys->print("Ninep->Tmsg.unpack: wstat count: %d/%d\n", ds, n);	# temporary
492			break;
493		}
494		return (H+FID+LEN+n, ref Tmsg.Wstat(tag, fid, stat));
495	}
496	return (-1, nil);		# illegal
497}
498
499tmsgname := array[] of {
500tagof Tmsg.Readerror => "Readerror",
501tagof Tmsg.Version => "Version",
502tagof Tmsg.Auth => "Auth",
503tagof Tmsg.Attach => "Attach",
504tagof Tmsg.Flush => "Flush",
505tagof Tmsg.Walk => "Walk",
506tagof Tmsg.Open => "Open",
507tagof Tmsg.Create => "Create",
508tagof Tmsg.Read => "Read",
509tagof Tmsg.Write => "Write",
510tagof Tmsg.Clunk => "Clunk",
511tagof Tmsg.Stat => "Stat",
512tagof Tmsg.Remove => "Remove",
513tagof Tmsg.Wstat => "Wstat",
514};
515
516Tmsg.text(t: self ref Tmsg): string
517{
518	if(t == nil)
519		return "nil";
520	s := sys->sprint("Tmsg.%s(%ud", tmsgname[tagof t], t.tag);
521	pick m:= t {
522	* =>
523		return s + ",ILLEGAL)";
524	Readerror =>
525		return s + sys->sprint(",\"%s\")", m.error);
526	Version =>
527		return s + sys->sprint(",%d,\"%s\")", m.msize, m.version);
528	Auth =>
529		return s + sys->sprint(",%ud,\"%s\",\"%s\")", m.afid, m.uname, m.aname);
530	Flush =>
531		return s + sys->sprint(",%ud)", m.oldtag);
532	Attach =>
533		return s + sys->sprint(",%ud,%ud,\"%s\",\"%s\")", m.fid, m.afid, m.uname, m.aname);
534	Walk =>
535		s += sys->sprint(",%ud,%ud", m.fid, m.newfid);
536		if(len m.names != 0){
537			s += ",array[] of {";
538			for(i := 0; i < len m.names; i++){
539				c := ",";
540				if(i == 0)
541					c = "";
542				s += sys->sprint("%s\"%s\"", c, m.names[i]);
543			}
544			s += "}";
545		}else
546			s += ",nil";
547		return s + ")";
548	Open =>
549		return s + sys->sprint(",%ud,%d)", m.fid, m.mode);
550	Create =>
551		return s + sys->sprint(",%ud,\"%s\",8r%uo,%d)", m.fid, m.name, m.perm, m.mode);
552	Read =>
553		return s + sys->sprint(",%ud,%bd,%ud)", m.fid, m.offset, m.count);
554	Write =>
555		return s + sys->sprint(",%ud,%bd,array[%d] of byte)", m.fid, m.offset, len m.data);
556	Clunk or
557	Remove or
558	Stat =>
559		return s + sys->sprint(",%ud)", m.fid);
560	Wstat =>
561		return s + sys->sprint(",%ud,%s)", m.fid, dir2text(m.stat));
562	}
563}
564
565Tmsg.read(fd: ref Sys->FD, msglim: int): ref Tmsg
566{
567	(msg, err) := readmsg(fd, msglim);
568	if(err != nil)
569		return ref Tmsg.Readerror(0, err);
570	if(msg == nil)
571		return nil;
572	(nil, m) := Tmsg.unpack(msg);
573	if(m == nil)
574		return ref Tmsg.Readerror(0, "bad 9P T-message format");
575	return m;
576}
577
578rtag2type := array[] of {
579tagof Rmsg.Version	=> Rversion,
580tagof Rmsg.Auth	=> Rauth,
581tagof Rmsg.Error	=> Rerror,
582tagof Rmsg.Flush	=> Rflush,
583tagof Rmsg.Attach	=> Rattach,
584tagof Rmsg.Walk	=> Rwalk,
585tagof Rmsg.Open	=> Ropen,
586tagof Rmsg.Create	=> Rcreate,
587tagof Rmsg.Read	=> Rread,
588tagof Rmsg.Write	=> Rwrite,
589tagof Rmsg.Clunk	=> Rclunk,
590tagof Rmsg.Remove	=> Rremove,
591tagof Rmsg.Stat	=> Rstat,
592tagof Rmsg.Wstat	=> Rwstat,
593};
594
595Rmsg.mtype(r: self ref Rmsg): int
596{
597	return rtag2type[tagof r];
598}
599
600Rmsg.packedsize(r: self ref Rmsg): int
601{
602	mtype := rtag2type[tagof r];
603	if(mtype <= 0)
604		return 0;
605	ml := hdrlen[mtype];
606	pick m := r {
607	Version =>
608		ml += utflen(m.version);
609	Error =>
610		ml += utflen(m.ename);
611	Walk =>
612		ml += QID*len m.qids;
613	Read =>
614		ml += len m.data;
615	Stat =>
616		ml += packdirsize(m.stat);
617	}
618	return ml;
619}
620
621Rmsg.pack(r: self ref Rmsg): array of byte
622{
623	if(r == nil)
624		return nil;
625	ps := r.packedsize();
626	if(ps <= 0)
627		return nil;
628	d := array[ps] of byte;
629	d[0] = byte ps;
630	d[1] = byte (ps>>8);
631	d[2] = byte (ps>>16);
632	d[3] = byte (ps>>24);
633	d[4] = byte rtag2type[tagof r];
634	d[5] = byte r.tag;
635	d[6] = byte (r.tag >> 8);
636	pick m := r {
637	Version =>
638		p32(d, H, m.msize);
639		pstring(d, H+BIT32SZ, m.version);
640	Auth =>
641		pqid(d, H, m.aqid);
642	Flush or
643	Clunk or
644	Remove or
645	Wstat =>
646		;	# nothing more required
647	Error	=>
648		pstring(d, H, m.ename);
649	Attach =>
650		pqid(d, H, m.qid);
651	Walk =>
652		n := len m.qids;
653		d[H] = byte n;
654		d[H+1] = byte (n>>8);
655		o := H+LEN;
656		for(i:=0; i<n; i++){
657			pqid(d, o, m.qids[i]);
658			o += QID;
659		}
660	Create or
661	Open =>
662		pqid(d, H, m.qid);
663		p32(d, H+QID, m.iounit);
664	Read =>
665		v := len m.data;
666		d[H] = byte v;
667		d[H+1] = byte (v>>8);
668		d[H+2] = byte (v>>16);
669		d[H+3] = byte (v>>24);
670		d[H+4:] = m.data;
671	Write =>
672		v := m.count;
673		d[H] = byte v;
674		d[H+1] = byte (v>>8);
675		d[H+2] = byte (v>>16);
676		d[H+3] = byte (v>>24);
677	Stat =>
678		stat := packdir(m.stat);
679		v := len stat;
680		d[H] = byte v;
681		d[H+1] = byte (v>>8);
682		d[H+2:] = stat;		# should avoid copy?
683	* =>
684		raise sys->sprint("assertion: Ninep->Rmsg.pack: missed case: tag %d", tagof r);
685	}
686	return d;
687}
688
689Rmsg.unpack(f: array of byte): (int, ref Rmsg)
690{
691	if(len f < H)
692		return (0, nil);
693	size := (int f[1] << 8) | int f[0];
694	size |= ((int f[3] << 8) | int f[2]) << 16;	# size includes itself
695	if(len f != size){
696		if(len f < size)
697			return (0, nil);	# need more data
698		f = f[0:size];	# trim to exact length
699	}
700	mtype := int f[4];
701	if(mtype >= len hdrlen || (mtype&1) == 0 || size < hdrlen[mtype])
702		return (-1, nil);
703
704	tag := (int f[6] << 8) | int f[5];
705
706	# return out of each case body for a legal message;
707	# break out of the case for an illegal one
708
709	case mtype {
710	* =>
711		sys->print("Ninep->Rmsg.unpack: bad type %d\n", mtype);	# temporary
712	Rversion =>
713		msize := g32(f, H);
714		(version, o) := gstring(f, H+BIT32SZ);
715		if(o <= 0)
716			break;
717		return (o, ref Rmsg.Version(tag, msize, version));
718	Rauth =>
719		return (H+QID, ref Rmsg.Auth(tag, gqid(f, H)));
720	Rflush =>
721		return (H, ref Rmsg.Flush(tag));
722	Rerror =>
723		(ename, o) := gstring(f, H);
724		if(o <= 0)
725			break;
726		return (o, ref Rmsg.Error(tag, ename));
727	Rclunk =>
728		return (H, ref Rmsg.Clunk(tag));
729	Rremove =>
730		return (H, ref Rmsg.Remove(tag));
731	Rwstat=>
732		return (H, ref Rmsg.Wstat(tag));
733	Rattach =>
734		return (H+QID, ref Rmsg.Attach(tag, gqid(f, H)));
735	Rwalk =>
736		nqid := (int f[H+1] << 8) | int f[H];
737		if(len f < H+LEN+nqid*QID)
738			break;
739		o := H+LEN;
740		qids := array[nqid] of Sys->Qid;
741		for(i:=0; i<nqid; i++){
742			qids[i] = gqid(f, o);
743			o += QID;
744		}
745		return (o, ref Rmsg.Walk(tag, qids));
746	Ropen =>
747		return (H+QID+COUNT, ref Rmsg.Open(tag, gqid(f, H), g32(f, H+QID)));
748	Rcreate=>
749		return (H+QID+COUNT, ref Rmsg.Create(tag, gqid(f, H), g32(f, H+QID)));
750	Rread =>
751		count := g32(f, H);
752		if(len f < H+COUNT+count)
753			break;
754		data := f[H+COUNT:H+COUNT+count];
755		return (H+COUNT+count, ref Rmsg.Read(tag, data));
756	Rwrite =>
757		return (H+COUNT, ref Rmsg.Write(tag, g32(f, H)));
758	Rstat =>
759		n := (int f[H+1] << 8) | int f[H];
760		if(len f < H+LEN+n)
761			break;
762		(ds, d) := unpackdir(f[H+LEN:]);
763		if(ds <= 0)
764			break;
765		if(ds != n){
766			sys->print("Ninep->Rmsg.unpack: stat count: %d/%d\n", ds, n);		# temporary
767			break;
768		}
769		return (H+LEN+n, ref Rmsg.Stat(tag, d));
770	}
771	return (-1, nil);		# illegal
772}
773
774rmsgname := array[] of {
775tagof Rmsg.Version => "Version",
776tagof Rmsg.Auth => "Auth",
777tagof Rmsg.Attach => "Attach",
778tagof Rmsg.Error => "Error",
779tagof Rmsg.Flush => "Flush",
780tagof Rmsg.Walk => "Walk",
781tagof Rmsg.Create => "Create",
782tagof Rmsg.Open => "Open",
783tagof Rmsg.Read => "Read",
784tagof Rmsg.Write => "Write",
785tagof Rmsg.Clunk => "Clunk",
786tagof Rmsg.Remove => "Remove",
787tagof Rmsg.Stat => "Stat",
788tagof Rmsg.Wstat => "Wstat",
789};
790
791Rmsg.text(r: self ref Rmsg): string
792{
793	if(sys == nil)
794		sys = load Sys Sys->PATH;
795	if(r == nil)
796		return "nil";
797	s := sys->sprint("Rmsg.%s(%ud", rmsgname[tagof r], r.tag);
798	pick m := r {
799	* =>
800		return s + "ERROR)";
801	Readerror =>
802		return s + sys->sprint(",\"%s\")", m.error);
803	Version =>
804		return s + sys->sprint(",%d,\"%s\")", m.msize, m.version);
805	Auth =>
806		return s+sys->sprint(",%s)", qid2text(m.aqid));
807	Error =>
808		return s+sys->sprint(",\"%s\")", m.ename);
809	Flush or
810	Clunk or
811	Remove or
812	Wstat =>
813		return s+")";
814	Attach =>
815		return s+sys->sprint(",%s)", qid2text(m.qid));
816	Walk	 =>
817		s += ",array[] of {";
818		for(i := 0; i < len m.qids; i++){
819			c := "";
820			if(i != 0)
821				c = ",";
822			s += sys->sprint("%s%s", c, qid2text(m.qids[i]));
823		}
824		return s+"})";
825	Create or
826	Open =>
827		return s+sys->sprint(",%s,%d)", qid2text(m.qid), m.iounit);
828	Read =>
829		return s+sys->sprint(",array[%d] of byte)", len m.data);
830	Write =>
831		return s+sys->sprint(",%d)", m.count);
832	Stat =>
833		return s+sys->sprint(",%s)", dir2text(m.stat));
834	}
835}
836
837Rmsg.read(fd: ref Sys->FD, msglim: int): ref Rmsg
838{
839	(msg, err) := readmsg(fd, msglim);
840	if(err != nil)
841		return ref Rmsg.Readerror(0, err);
842	if(msg == nil)
843		return nil;
844	(nil, m) := Rmsg.unpack(msg);
845	if(m == nil)
846		return ref Rmsg.Readerror(0, "bad 9P R-message format");
847	return m;
848}
849
850Rmsg.write(m: self ref Rmsg, fd: ref Sys->FD, msize: int): int
851{
852	if(msize == 0)
853		m = ref Rmsg.Error(m.tag, "Tversion not seen");
854	d := m.pack();
855	if(msize != 0 && len d > msize){
856		m = ref Rmsg.Error(m.tag, "9P reply didn't fit");
857		d = m.pack();
858	}
859	n := len d;
860	if(sys->write(fd, d, n) != n)
861		return -1;
862	return 0;
863}
864
865dir2text(d: Sys->Dir): string
866{
867	return sys->sprint("Dir(\"%s\",\"%s\",\"%s\",%s,8r%uo,%d,%d,%bd,16r%ux,%d)",
868		d.name, d.uid, d.gid, qid2text(d.qid), d.mode, d.atime, d.mtime, d.length, d.dtype, d.dev);
869}
870
871qid2text(q: Sys->Qid): string
872{
873	return sys->sprint("Qid(16r%ubx,%d,16r%.2ux)", q.path, q.vers, q.qtype);
874}
875
876readmsg(fd: ref Sys->FD, msglim: int): (array of byte, string)
877{
878	if(msglim <= 0)
879		msglim = DEFMSIZE;
880	sbuf := array[BIT32SZ] of byte;
881	if((n := sys->readn(fd, sbuf, BIT32SZ)) != BIT32SZ){
882		if(n == 0)
883			return (nil, nil);
884		return (nil, sys->sprint("%r"));
885	}
886	ml := (int sbuf[1] << 8) | int sbuf[0];
887	ml |= ((int sbuf[3] << 8) | int sbuf[2]) << 16;
888	if(ml <= BIT32SZ)
889		return (nil, "invalid 9P message size");
890	if(ml > msglim)
891		return (nil, "9P message longer than agreed");
892	buf := array[ml] of byte;
893	buf[0:] = sbuf;
894	if((n = sys->readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){
895		if(n == 0)
896			return (nil, "9P message truncated");
897		return (nil, sys->sprint("%r"));
898	}
899	return (buf, nil);
900}
901
902istmsg(f: array of byte): int
903{
904	if(len f < H)
905		return -1;
906	return (int f[BIT32SZ] & 1) == 0;
907}
908
909compatible(t: ref Tmsg.Version, msize: int, version: string): (int, string)
910{
911	if(version == nil)
912		version = VERSION;
913	if(t.msize < msize)
914		msize = t.msize;
915	v := t.version;
916	if(len v < 2 || v[0:2] != "9P")
917		return (msize, "unknown");
918	for(i:=2; i<len v; i++)
919		if((c := v[i]) == '.'){
920			v = v[0:i];
921			break;
922		}else if(!(c >= '0' && c <= '9'))
923			return (msize, "unknown");	# fussier than Plan 9
924	if(v < VERSION)
925		return (msize, "unknown");
926	if(v < version)
927		version = v;
928	return (msize, version);
929}
930