xref: /plan9-contrib/sys/src/cmd/srvold9p/srvold9p.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include "9p1.h"
6 
7 char	*user;
8 int	newfd;
9 int	roldfd;
10 int	woldfd;
11 int	debug;
12 int	dofcall;
13 QLock	servelock;
14 QLock	fidlock;
15 QLock	taglock;
16 int	mainpid;
17 int	ntag;
18 int	nfork;
19 char FLUSHED[] = "FLUSHED";
20 
21 enum{
22 	Maxfdata = 8192
23 };
24 
25 enum{
26 	Command,
27 	Network,
28 	File,
29 	Stdio,
30 };
31 
32 typedef struct Tag Tag;
33 struct Tag
34 {
35 	int	tag;
36 	int	flushed;
37 	int	received;
38 	int	ref;
39 	Tag	*next;
40 };
41 
42 typedef struct Message Message;
43 struct Message
44 {
45 	char	*data;
46 	int	n;
47 };
48 
49 typedef struct Fid Fid;
50 
51 struct Fid
52 {
53 	short	busy;
54 	short	allocated;
55 	int	fid;
56 	Qid	qid;
57 	ulong	newoffset;
58 	ulong	oldoffset;
59 	Fid	*next;
60 };
61 
62 Fid	*fids;
63 Tag	*tags;
64 
65 char	*rflush(Fcall*, Fcall*, char*),
66 	*rversion(Fcall*, Fcall*, char*),
67 	*rauth(Fcall*, Fcall*, char*),
68 	*rattach(Fcall*, Fcall*, char*),
69 	*rwalk(Fcall*, Fcall*, char*),
70 	*ropen(Fcall*, Fcall*, char*),
71 	*rcreate(Fcall*, Fcall*, char*),
72 	*rread(Fcall*, Fcall*, char*),
73 	*rwrite(Fcall*, Fcall*, char*),
74 	*rclunk(Fcall*, Fcall*, char*),
75 	*rremove(Fcall*, Fcall*, char*),
76 	*rstat(Fcall*, Fcall*, char*),
77 	*rwstat(Fcall*, Fcall*, char*);
78 
79 char 	*(*fcalls[])(Fcall*, Fcall*, char*) = {
80 	[Tversion]	rversion,
81 	[Tflush]	rflush,
82 	[Tauth]	rauth,
83 	[Tattach]	rattach,
84 	[Twalk]		rwalk,
85 	[Topen]		ropen,
86 	[Tcreate]	rcreate,
87 	[Tread]		rread,
88 	[Twrite]	rwrite,
89 	[Tclunk]	rclunk,
90 	[Tremove]	rremove,
91 	[Tstat]		rstat,
92 	[Twstat]	rwstat,
93 };
94 
95 char Etoolong[] = "name too long";
96 
97 void	connect(int, char*);
98 void	post(int, char*);
99 void	serve(void);
100 void	demux(void);
101 void*	emalloc(ulong);
102 char*	transact9p1(Fcall9p1*, Fcall9p1*, char*);
103 Fid*	newfid(int);
104 
105 struct
106 {
107 	char	chal[CHALLEN];		/* my challenge */
108 	char	rchal[CHALLEN];		/* his challenge */
109 	char	authid[NAMEREC];
110 	char	authdom[DOMLEN];
111 	int	id;
112 } ai;
113 
114 void
115 usage(void)
116 {
117 	fprint(2, "usage: srvold9p [-abcCd] [-u user] [-s | [-m mountpoint]] [-x 'command' | -n network-addr | -f file] [-F] [-p servicename]\n");
118 	exits("usage");
119 }
120 
121 void
122 main(int argc, char *argv[])
123 {
124 	int method;
125 	char *oldstring;
126 	char *mountpoint, *postname;
127 	int mountflag, mountfd;
128 	int p[2];
129 int i;
130 
131 	fmtinstall('F', fcallfmt);
132 	fmtinstall('G', fcallfmt9p1);
133 	fmtinstall('D', dirfmt);
134 
135 	user = getuser();
136 	mountpoint = nil;
137 	mountflag = 0;
138 	postname = nil;
139 	oldstring = nil;
140 	method = -1;
141 	mountfd = -1;
142 
143 	ARGBEGIN{
144 	case 'a':
145 		mountflag |= MAFTER;
146 		break;
147 	case 'b':
148 		mountflag |= MBEFORE;
149 		break;
150 	case 'c':
151 		mountflag |= MCREATE;
152 		break;
153 	case 'C':
154 		mountflag |= MCACHE;
155 		break;
156 	case 'd':
157 		debug++;
158 		break;
159 	case 'f':
160 		method = File;
161 		oldstring = ARGF();
162 		break;
163 	case 'F':
164 		dofcall++;
165 		break;
166 	case 'm':
167 		mountpoint = EARGF(usage());
168 		break;
169 	case 'n':
170 		method = Network;
171 		oldstring = ARGF();
172 		break;
173 	case 'p':
174 		postname = ARGF();
175 		if(postname == nil)
176 			usage();
177 		break;
178 	case 's':
179 		method = Stdio;
180 		break;
181 	case 'u':
182 		user = EARGF(usage());
183 		break;
184 	case 'x':
185 		method = Command;
186 		oldstring = ARGF();
187 		break;
188 	default:
189 		usage();
190 	}ARGEND;
191 
192 	if(method == Stdio){
193 		if(mountpoint!=nil || argc!=0)
194 			usage();
195 	}else{
196 		if(oldstring == nil || argc != 0 || (mountflag!=0 && mountpoint==nil))
197 			usage();
198 	}
199 
200 	rfork(RFNOTEG|RFREND);
201 
202 	connect(method, oldstring);
203 
204 	if(method == Stdio)
205 		newfd = 0;
206 	else{
207 		if(pipe(p) < 0)
208 			fatal("pipe: %r");
209 		if(postname != nil)
210 			post(p[0], postname);
211 		mountfd = p[0];
212 		newfd = p[1];
213 	}
214 	if(debug)
215 		fprint(2, "connected and posted\n");
216 
217 	switch(rfork(RFPROC|RFMEM|RFNAMEG|RFFDG)){
218 	case 0:
219 		mainpid = getpid();
220 		/* child does all the work */
221 		if(mountfd >= 0)
222 			close(mountfd);
223 		switch(rfork(RFPROC|RFMEM|RFFDG)){
224 		case 0:
225 			for(i = 0; i < 20; i++)
226 				if (i != roldfd) close(i);
227 			demux();
228 			return;
229 		case -1:
230 			fatal("fork error: %r");
231 			break;
232 		}
233 		for(i = 0; i < 20; i++)
234 			if (i != newfd && i != woldfd && (debug == 0 || i != 2)) close(i);
235 		serve();
236 		break;
237 	case -1:
238 		fatal("fork error: %r");
239 		break;
240 	default:
241 		/* parent mounts if required, then exits */
242 		if(mountpoint){
243 			if(mount(mountfd, -1, mountpoint, mountflag, "") < 0)
244 				fatal("can't mount: %r");
245 		}
246 		break;
247 	}
248 	exits(nil);
249 }
250 
251 void
252 connect(int method, char *oldstring)
253 {
254 	char *s;
255 	char dir[256];
256 
257 	switch(method){
258 	default:
259 		roldfd = -1;
260 		woldfd = -1;
261 		fatal("can't handle method type %d", method);
262 		break;
263 	case Network:
264 		s = netmkaddr(oldstring, 0, "9fs");
265 		roldfd = dial(s, 0, dir, 0);
266 		if(roldfd < 0)
267 			fatal("dial %s: %r", s);
268 		woldfd = roldfd;
269 		if(dofcall)
270 			roldfd = fcall(woldfd);
271 		break;
272 	case File:
273 		roldfd = open(oldstring, ORDWR);
274 		if(roldfd < 0)
275 			fatal("can't open %s: %r", oldstring);
276 		woldfd = roldfd;
277 		if(dofcall)
278 			roldfd = fcall(woldfd);
279 		break;
280 	case Stdio:
281 		roldfd = fcall(1);
282 		woldfd = 1;
283 		break;
284 	}
285 }
286 
287 void
288 post(int fd, char *srv)
289 {
290 	int f;
291 	char buf[128];
292 
293 	snprint(buf, sizeof buf, "/srv/%s", srv);
294 	f = create(buf, OWRITE, 0666);
295 	if(f < 0)
296 		fatal("can't create %s: %r", buf);
297 	sprint(buf, "%d", fd);
298 	if(write(f, buf, strlen(buf)) != strlen(buf))
299 		fatal("post write: %r");
300 	close(f);
301 }
302 
303 Fid *
304 newfid(int fid)
305 {
306 	Fid *f, *ff;
307 
308 	ff = 0;
309 	qlock(&fidlock);
310 	for(f = fids; f; f = f->next)
311 		if(f->fid == fid){
312 			f->allocated = 1;
313 			qunlock(&fidlock);
314 			return f;
315 		}
316 		else if(!ff && !f->allocated)
317 			ff = f;
318 	if(ff){
319 		ff->fid = fid;
320 		ff->allocated = 1;
321 		qunlock(&fidlock);
322 		return ff;
323 	}
324 	f = emalloc(sizeof *f);
325 	f->fid = fid;
326 	f->next = fids;
327 	f->allocated = 1;
328 	fids = f;
329 	qunlock(&fidlock);
330 	return f;
331 }
332 
333 /*
334  * Reads returning 9P1 messages and demultiplexes them.
335  * BUG: assumes one read per message.
336  */
337 void
338 demux(void)
339 {
340 	int m, n;
341 	char *data;
342 	Fcall9p1 r;
343 	Message *msg;
344 	Tag *t;
345 
346 	for(;;){
347 		data = malloc(IOHDRSZ+Maxfdata);	/* no need to clear memory */
348 		if(data == nil)
349 			fatal("demux malloc: %r");
350 		m = read(roldfd, data, IOHDRSZ+Maxfdata);
351 		if(m <= 0)
352 			fatal("read error talking to old system: %r");
353 		n = convM2S9p1(data, &r, m);
354 		if(n == 0)
355 			fatal("bad conversion receiving from old system");
356 		if(debug)
357 			fprint(2, "srvold9p:<=%G\n", &r);
358 		qlock(&taglock);
359 		for(t=tags; t!=nil; t=t->next)
360 			if(t->tag == r.tag){
361 				t->received = 1;
362 				break;
363 			}
364 		qunlock(&taglock);
365 		/*
366 		 * Fcall9p1 tag is used to rendezvous.
367 		 * Recipient converts message a second time, but that's OK.
368 		 */
369 		msg = emalloc(sizeof(Message));
370 		msg->data = data;
371 		msg->n = n;
372 		rendezvous(r.tag, (ulong)msg);
373 	}
374 }
375 
376 Tag*
377 newtag(int tag)
378 {
379 	Tag *t;
380 
381 	t = emalloc(sizeof(Tag));
382 	t->tag = tag;
383 	t->flushed = 0;
384 	t->received = 0;
385 	t->ref = 1;
386 	qlock(&taglock);
387 	t->next = tags;
388 	tags = t;
389 	qunlock(&taglock);
390 	return t;
391 }
392 
393 void
394 freetag(Tag *tag)	/* called with taglock set */
395 {
396 	Tag *t, *prev;
397 
398 	if(tag->ref-- == 1){
399 		prev = nil;
400 		for(t=tags; t!=nil; t=t->next){
401 			if(t == tag){
402 				if(prev == nil)
403 					tags = t->next;
404 				else
405 					prev->next = t->next;
406 				break;
407 			}
408 			prev = t;
409 		}
410 		if(t == nil)
411 			sysfatal("freetag");
412 		free(tag);
413 	}
414 }
415 
416 void
417 serve(void)
418 {
419 	char *err;
420 	int n;
421 	Fcall thdr;
422 	Fcall	rhdr;
423 	uchar mdata[IOHDRSZ+Maxfdata];
424 	char mdata9p1[IOHDRSZ+Maxfdata];
425 	Tag *tag;
426 
427 	for(;;){
428 		qlock(&servelock);
429 		for(;;){
430 			n = read9pmsg(newfd, mdata, sizeof mdata);
431 
432 			if(n == 0)
433 				continue;
434 			if(n < 0)
435 				break;
436 			if(n > 0 && convM2S(mdata, n, &thdr) > 0)
437 				break;
438 		}
439 		if(n>0 && servelock.head==nil)	/* no other processes waiting to read */
440 			switch(rfork(RFPROC|RFMEM)){
441 			case 0:
442 				/* child starts serving */
443 				continue;
444 				break;
445 			case -1:
446 				fatal("fork error: %r");
447 				break;
448 			default:
449 				break;
450 			}
451 		qunlock(&servelock);
452 
453 		if(n < 0)
454 			fatal(nil);	/* exit quietly; remote end has just hung up */
455 
456 		if(debug)
457 			fprint(2, "srvold9p:<-%F\n", &thdr);
458 
459 		tag = newtag(thdr.tag);
460 
461 		if(!fcalls[thdr.type])
462 			err = "bad fcall type";
463 		else
464 			err = (*fcalls[thdr.type])(&thdr, &rhdr, mdata9p1);
465 
466 		qlock(&taglock);
467 		if(tag->flushed){
468 			freetag(tag);
469 			qunlock(&taglock);
470 			continue;
471 		}
472 		qunlock(&taglock);
473 
474 		if(err){
475 			rhdr.type = Rerror;
476 			rhdr.ename = err;
477 		}else{
478 			rhdr.type = thdr.type + 1;
479 			rhdr.fid = thdr.fid;
480 		}
481 		rhdr.tag = thdr.tag;
482 		if(debug)
483 			fprint(2, "srvold9p:->%F\n", &rhdr);/**/
484 		n = convS2M(&rhdr, mdata, sizeof mdata);
485 		if(n == 0)
486 			fatal("convS2M error on write");
487 		if(write(newfd, mdata, n) != n)
488 			fatal("mount write");
489 
490 		qlock(&taglock);
491 		freetag(tag);
492 		qunlock(&taglock);
493 	}
494 }
495 
496 void
497 send9p1(Fcall9p1 *t, char *data)
498 {
499 	int m, n;
500 
501 	if(debug)
502 		fprint(2, "srvold9p:=>%G\n", t);
503 	n = convS2M9p1(t, data);
504 	if(n == 0)
505 		fatal("bad conversion sending to old system");
506 	m = write(woldfd, data, n);
507 	if(m != n)
508 		fatal("wrote %d to old system; should be %d", m, n);
509 }
510 
511 int
512 recv9p1(Fcall9p1 *r, int tag, char *data)
513 {
514 	int n;
515 	Message *msg;
516 
517 	msg = (Message*)rendezvous(tag, 0);
518 	if((ulong)msg == ~0UL)
519 		fatal("rendezvous: %r");
520 	if(msg == nil){
521 		if(debug)
522 			fprint(2, "recv flushed\n");
523 		return -1;
524 	}
525 	/* copy data to local buffer */
526 	memmove(data, msg->data, msg->n);
527 	n = convM2S9p1(data, r, msg->n);
528 	if(n == 0)
529 		fatal("bad conversion receiving from old system");
530 	free(msg->data);
531 	free(msg);
532 	return 1;
533 }
534 
535 char*
536 transact9p1(Fcall9p1 *t, Fcall9p1 *r, char *mdata9p1)
537 {
538 	send9p1(t, mdata9p1);
539 	if(recv9p1(r, t->tag, mdata9p1) < 0)
540 		return FLUSHED;
541 	if(r->type == Rerror9p1)
542 		return r->ename;
543 	if(r->type != t->type+1)
544 		fatal("bad message type; expected %d got %d", t->type+1, r->type);
545 	return nil;
546 }
547 
548 char*
549 rflush(Fcall *t, Fcall *, char *mdata9p1)
550 {
551 	Fcall9p1 t9, r9;
552 	Tag *oldt;
553 
554 	t9.type = Tflush9p1;
555 	t9.tag = t->tag;
556 	t9.oldtag = t->oldtag;
557 	qlock(&taglock);
558 	for(oldt=tags; oldt!=nil; oldt=oldt->next)
559 		if(oldt->tag == t->oldtag){
560 			oldt->flushed = 1;
561 			oldt->ref++;
562 			break;
563 		}
564 	qunlock(&taglock);
565 
566 	if(oldt == nil){	/* nothing to flush */
567 		if(debug)
568 			fprint(2, "no such tag to flush\n");
569 		return 0;
570 	}
571 
572 	transact9p1(&t9, &r9, mdata9p1);	/* can't error */
573 
574 	qlock(&taglock);
575 	if(oldt->received == 0){	/* wake up receiver */
576 		if(debug)
577 			fprint(2, "wake up receiver\n");
578 		oldt->received = 1;
579 		rendezvous(t->oldtag, 0);
580 	}
581 	freetag(oldt);
582 	qunlock(&taglock);
583 	return 0;
584 }
585 
586 char*
587 rversion(Fcall *t, Fcall *r, char*)
588 {
589 	Fid *f;
590 
591 	/* just ack; this one doesn't go to old service */
592 	if(t->msize > IOHDRSZ+Maxfdata)
593 		r->msize = IOHDRSZ+Maxfdata;
594 	else
595 		r->msize = t->msize;
596 	if(strncmp(t->version, "9P2000", 6) != 0)
597 		return "unknown 9P version";
598 	r->version = "9P2000";
599 
600 	qlock(&fidlock);
601 	for(f = fids; f; f = f->next){
602 		f->busy = 0;
603 		f->allocated = 0;
604 	}
605 	qunlock(&fidlock);
606 
607 	return 0;
608 }
609 
610 char*
611 rauth(Fcall *, Fcall *, char *)
612 {
613 	return "srvold9p: authentication not supported";
614 }
615 
616 #ifdef asdf
617 
618 void
619 memrandom(void *p, int n)
620 {
621 	ulong *lp;
622 	uchar *cp;
623 
624 	for(lp = p; n >= sizeof(ulong); n -= sizeof(ulong))
625 		*lp++ = fastrand();
626 	for(cp = (uchar*)lp; n > 0; n--)
627 		*cp++ = fastrand();
628 }
629 
630 char*
631 rsession(Fcall *t, Fcall *r, char *mdata9p1)
632 {
633 	char *err;
634 	Fcall9p1 t9, r9;
635 	Fid *f;
636 
637 	t9.type = Tsession9p1;
638 	t9.tag = t->tag;
639 	if(doauth)
640 		memrandom(t9.chal, sizeof t9.chal);
641 	else
642 		memset(t9.chal, 0, sizeof t9.chal);
643 	err = transact9p1(&t9, &r9, mdata9p1);
644 	if(err)
645 		return err;
646 
647 	qlock(&fidlock);
648 	for(f = fids; f; f = f->next){
649 		f->busy = 0;
650 		f->allocated = 0;
651 	}
652 	qunlock(&fidlock);
653 
654 	if(doauth){
655 		memmove(ai.authid, r9.authid, sizeof ai.authid);
656 		memmove(ai.authdom, r9.authdom, sizeof ai.authid);
657 		memmove(ai.rchal, r9.chal, sizeof ai.rchal);
658 		memmove(ai.chal, t9.chal, sizeof ai.chal);
659 		r->authid = ai.authid;
660 		r->authdom = ai.authdom;
661 		r->chal = (uchar*)ai.rchal;
662 		r->nchal = CHALLEN;
663 	} else {
664 		r->authid = "";
665 		r->authdom = "";
666 		r->nchal = 0;
667 		r->chal = nil;
668 	}
669 	return 0;
670 }
671 #endif
672 
673 char*
674 rattach(Fcall *t, Fcall *r, char *mdata9p1)
675 {
676 	char *err;
677 	Fcall9p1 t9, r9;
678 	Fid *f;
679 
680 	f = newfid(t->fid);
681 	if(f->busy)
682 		return "attach: fid in use";
683 	/* no authentication! */
684 	t9.type = Tattach9p1;
685 	t9.tag = t->tag;
686 	t9.fid = t->fid;
687 	strncpy(t9.uname, t->uname, NAMEREC);
688 	if(strcmp(user, "none") == 0)
689 		strncpy(t9.uname, user, NAMEREC);
690 	strncpy(t9.aname, t->aname, NAMEREC);
691 	memset(t9.ticket, 0, sizeof t9.ticket);
692 	memset(t9.auth, 0, sizeof t9.auth);
693 	err = transact9p1(&t9, &r9, mdata9p1);
694 	if(err)
695 		return err;
696 
697 	r->qid.path = r9.qid.path & ~0x80000000;
698 	r->qid.vers = r9.qid.version;
699 	r->qid.type = QTDIR;
700 	f->busy = 1;
701 	f->qid = r->qid;
702 	return 0;
703 }
704 
705 char*
706 rwalk(Fcall *t, Fcall *r, char *mdata9p1)
707 {
708 	char *err;
709 	Fcall9p1 t9, r9;
710 	int i, fid;
711 	Qid *q;
712 	Fid *f, *nf;
713 
714 	f = newfid(t->fid);
715 	if(!f->busy)
716 		return "walk: bad fid";
717 
718 	fid = t->fid;
719 	nf = nil;
720 	if(t->fid != t->newfid){
721 		nf = newfid(t->newfid);
722 		if(nf->busy)
723 			return "walk: newfid in use";
724 		t9.type = Tclone9p1;
725 		t9.tag = t->tag;
726 		t9.fid = t->fid;
727 		t9.newfid = t->newfid;
728 		err = transact9p1(&t9, &r9, mdata9p1);
729 		if(err){
730 			nf->busy = 0;
731 			nf->allocated = 0;
732 			return err;
733 		}
734 		fid = t->newfid;
735 		nf->busy = 1;
736 	}
737 
738 	err = nil;
739 	r->nwqid = 0;
740 	for(i=0; i<t->nwname && err==nil; i++){
741 		if(i > MAXWELEM)
742 			break;
743 		t9.type = Twalk9p1;
744 		t9.tag = t->tag;
745 		t9.fid = fid;
746 		strncpy(t9.name, t->wname[i], NAMEREC);
747 		err = transact9p1(&t9, &r9, mdata9p1);
748 		if(err == FLUSHED){
749 			i = -1;	/* guarantee cleanup */
750 			break;
751 		}
752 		if(err == nil){
753 			q = &r->wqid[r->nwqid++];
754 			q->type = QTFILE;
755 			if(r9.qid.path & 0x80000000)
756 				q->type = QTDIR;
757 			q->vers = r9.qid.version;
758 			q->path = r9.qid.path & ~0x80000000;
759 		}
760 	}
761 
762 	if(nf!=nil && (err!=nil || i<t->nwname)){
763 		/* clunk the new fid */
764 		t9.type = Tclunk9p1;
765 		t9.tag = t->tag;
766 		t9.fid = t->newfid;
767 		transact9p1(&t9, &r9, mdata9p1);
768 		/* ignore more errors */
769 		nf->busy = 0;
770 		nf->allocated = 0;
771 	}
772 
773 	if(i>0 && i==t->nwname && err==nil)
774 		f->qid = r->wqid[r->nwqid-1];
775 	if(i > 0)
776 		return 0;
777 	return err;
778 }
779 
780 char*
781 ropen(Fcall *t, Fcall *r, char *mdata9p1)
782 {
783 	char *err;
784 	Fcall9p1 t9, r9;
785 	Fid *f;
786 
787 	f = newfid(t->fid);
788 	if(!f->busy)
789 		return "open: bad fid";
790 
791 	t9.type = Topen9p1;
792 	t9.tag = t->tag;
793 	t9.fid = t->fid;
794 	t9.mode = t->mode;
795 	err = transact9p1(&t9, &r9, mdata9p1);
796 	if(err)
797 		return err;
798 
799 	r->qid.path = r9.qid.path & ~0x80000000;
800 	r->qid.vers = r9.qid.version;
801 	r->qid.type = QTFILE;
802 	if(r9.qid.path & 0x80000000)
803 		r->qid.type = QTDIR;
804 	f->qid = r->qid;
805 	f->newoffset = 0;
806 	f->oldoffset = 0;
807 	r->iounit = 0;
808 	return 0;
809 }
810 
811 char*
812 rcreate(Fcall *t, Fcall *r, char *mdata9p1)
813 {
814 	char *err;
815 	Fcall9p1 t9, r9;
816 	Fid *f;
817 
818 	f = newfid(t->fid);
819 	if(!f->busy)
820 		return "create: bad fid";
821 
822 	t9.type = Tcreate9p1;
823 	t9.tag = t->tag;
824 	t9.fid = t->fid;
825 	if(strlen(t->name)+1 >= NAMEREC)
826 		return "file name element too long";
827 	strncpy(t9.name, t->name, NAMEREC);
828 	t9.perm = t->perm;
829 	t9.mode = t->mode;
830 	err = transact9p1(&t9, &r9, mdata9p1);
831 	if(err)
832 		return err;
833 
834 	r->qid.path = r9.qid.path & ~0x80000000;
835 	r->qid.vers = r9.qid.version;
836 	r->qid.type = QTFILE;
837 	if(r9.qid.path & 0x80000000)
838 		r->qid.type = QTDIR;
839 	if(r9.qid.path & 0x40000000)
840 		r->qid.type |= QTAPPEND;
841 	if(r9.qid.path & 0x20000000)
842 		r->qid.type |= QTEXCL;
843 	f->qid = r->qid;
844 	r->iounit = 0;
845 	return 0;
846 }
847 
848 char*
849 dirrread(Fcall *t, Fcall *r, char *mdata9p1)
850 {
851 	char *err;
852 	Fcall9p1 t9, r9;
853 	Fid *f;
854 	int i, ndir, n, count;
855 	Dir d;
856 	uchar buf[Maxfdata];
857 	char *old;
858 
859 	f = newfid(t->fid);
860 	if(!f->busy)
861 		return "dirread: bad fid";
862 
863 	if(f->newoffset != t->offset)
864 		return "seek in directory disallowed";
865 
866 	t9.type = Tread9p1;
867 	t9.tag = t->tag;
868 	t9.fid = t->fid;
869 	t9.offset = f->oldoffset;
870 	t9.count = t->count;	/* new directories tend to be smaller, so this may overshoot */
871 	err = transact9p1(&t9, &r9, mdata9p1);
872 	if(err)
873 		return err;
874 
875 	ndir = r9.count/DIRREC;
876 	old = r9.data;
877 	count = 0;
878 	for(i=0; i<ndir; i++){
879 		if(convM2D9p1(old, &d) != DIRREC)
880 			return "bad dir conversion in read";
881 		n = convD2M(&d, buf+count, sizeof buf-count);
882 		if(n<=BIT16SZ || count+n>t->count)
883 			break;
884 		old += DIRREC;
885 		f->oldoffset += DIRREC;
886 		f->newoffset += n;
887 		count += n;
888 	}
889 	memmove(r9.data, buf, count);	/* put it back in stable storage */
890 	r->data = r9.data;
891 	r->count = count;
892 	return 0;
893 }
894 
895 char*
896 rread(Fcall *t, Fcall *r, char *mdata9p1)
897 {
898 	char *err;
899 	Fcall9p1 t9, r9;
900 	Fid *f;
901 
902 	f = newfid(t->fid);
903 	if(!f->busy)
904 		return "read: bad fid";
905 
906 	if(f->qid.type & QTDIR)
907 		return dirrread(t, r, mdata9p1);
908 
909 	t9.type = Tread9p1;
910 	t9.tag = t->tag;
911 	t9.fid = t->fid;
912 	t9.offset = t->offset;
913 	t9.count = t->count;
914 	err = transact9p1(&t9, &r9, mdata9p1);
915 	if(err)
916 		return err;
917 
918 	r->count = r9.count;
919 	r->data = r9.data;	/* points to stable storage */
920 	return 0;
921 }
922 
923 char*
924 rwrite(Fcall *t, Fcall *r, char *mdata9p1)
925 {
926 	char *err;
927 	Fcall9p1 t9, r9;
928 	Fid *f;
929 
930 	f = newfid(t->fid);
931 	if(!f->busy)
932 		return "write: bad fid";
933 
934 	t9.type = Twrite9p1;
935 	t9.tag = t->tag;
936 	t9.fid = t->fid;
937 	t9.offset = t->offset;
938 	t9.count = t->count;
939 	t9.data = t->data;
940 	err = transact9p1(&t9, &r9, mdata9p1);
941 	if(err)
942 		return err;
943 
944 	r->count = r9.count;
945 	return 0;
946 }
947 
948 char*
949 rclunk(Fcall *t, Fcall *, char *mdata9p1)
950 {
951 	Fcall9p1 t9, r9;
952 	Fid *f;
953 
954 	f = newfid(t->fid);
955 	if(!f->busy)
956 		return "clunk: bad fid";
957 	t9.type = Tclunk9p1;
958 	t9.tag = t->tag;
959 	t9.fid = t->fid;
960 	transact9p1(&t9, &r9, mdata9p1);
961 	f->busy = 0;
962 	f->allocated = 0;
963 	/* disregard error */
964 	return 0;
965 }
966 
967 char*
968 rremove(Fcall *t, Fcall*, char *mdata9p1)
969 {
970 	char *err;
971 	Fcall9p1 t9, r9;
972 	Fid *f;
973 
974 	f = newfid(t->fid);
975 	if(!f->busy)
976 		return "remove: bad fid";
977 	t9.type = Tremove9p1;
978 	t9.tag = t->tag;
979 	t9.fid = t->fid;
980 	err = transact9p1(&t9, &r9, mdata9p1);
981 	f->busy = 0;
982 	f->allocated = 0;
983 	return err;
984 }
985 
986 char*
987 rstat(Fcall *t, Fcall *r, char *mdata9p1)
988 {
989 	Fcall9p1 t9, r9;
990 	char *err;
991 	Fid *f;
992 	Dir d;
993 	uchar buf[256];	/* big enough; there's no long names */
994 
995 	f = newfid(t->fid);
996 	if(!f->busy)
997 		return "stat: bad fid";
998 
999 	t9.type = Tstat9p1;
1000 	t9.tag = t->tag;
1001 	t9.fid = t->fid;
1002 	err = transact9p1(&t9, &r9, mdata9p1);
1003 	if(err)
1004 		return err;
1005 
1006 	if(convM2D9p1(r9.stat, &d) != DIRREC)
1007 		return "bad conversion in stat";
1008 	r->stat = buf;
1009 	r->nstat = convD2M(&d, buf, sizeof buf);
1010 	return 0;
1011 }
1012 
1013 int
1014 anydefault(Dir *d)
1015 {
1016 	if(d->name[0] == '\0')
1017 		return 1;
1018 	if(d->uid[0] == '\0')
1019 		return 1;
1020 	if(d->gid[0] == '\0')
1021 		return 1;
1022 	if(d->mode == ~0)
1023 		return 1;
1024 	if(d->mtime == ~0)
1025 		return 1;
1026 	return 0;
1027 }
1028 
1029 char*
1030 rwstat(Fcall *t, Fcall *, char *mdata9p1)
1031 {
1032 	Fcall9p1 t9, r9;
1033 	char strs[DIRREC];
1034 	char *err;
1035 	Fid *f;
1036 	Dir d, cd;
1037 
1038 	f = newfid(t->fid);
1039 	if(!f->busy)
1040 		return "wstat: bad fid";
1041 
1042 	convM2D(t->stat, t->nstat, &d, strs);
1043 	cd = d;
1044 	if(anydefault(&d)){
1045 		/* must first stat file so we can copy current values */
1046 		t9.type = Tstat9p1;
1047 		t9.tag = t->tag;
1048 		t9.fid = t->fid;
1049 		err = transact9p1(&t9, &r9, mdata9p1);
1050 		if(err)
1051 			return err;
1052 		if(convM2D9p1(r9.stat, &cd) != DIRREC)
1053 			return "bad in conversion in wstat";
1054 
1055 		/* fill in default values */
1056 		if(d.name[0] != '\0'){
1057 			if(strlen(d.name) >= NAMEREC)
1058 				return Etoolong;
1059 			cd.name = d.name;
1060 		}
1061 		if(d.uid[0] != '\0'){
1062 			if(strlen(d.uid) >= NAMEREC)
1063 				return Etoolong;
1064 			cd.uid = d.uid;
1065 		}
1066 		if(d.gid[0] != '\0'){
1067 			if(strlen(d.gid) >= NAMEREC)
1068 				return Etoolong;
1069 			cd.gid = d.gid;
1070 		}
1071 		if(d.mode != ~0)
1072 			cd.mode = d.mode;
1073 		if(d.mtime != ~0)
1074 			cd.mtime = d.mtime;
1075 		if(d.length != ~0LL)
1076 			cd.length = d.length;
1077 	}
1078 
1079 	if(convD2M9p1(&cd, t9.stat) != DIRREC)
1080 		return "bad out conversion in wstat";
1081 
1082 	t9.type = Twstat9p1;
1083 	t9.tag = t->tag;
1084 	t9.fid = t->fid;
1085 	err = transact9p1(&t9, &r9, mdata9p1);
1086 	if(err)
1087 		return err;
1088 	return 0;
1089 }
1090 
1091 void *
1092 emalloc(ulong n)
1093 {
1094 	void *p;
1095 
1096 	p = malloc(n);
1097 	if(!p)
1098 		fatal("out of memory: %r");
1099 	memset(p, 0, n);
1100 	return p;
1101 }
1102 
1103 void
1104 fatal(char *fmt, ...)
1105 {
1106 	char buf[1024];
1107 	va_list arg;
1108 
1109 	if(fmt){
1110 		va_start(arg, fmt);
1111 		vseprint(buf, buf+sizeof(buf), fmt, arg);
1112 		va_end(arg);
1113 		fprint(2, "%s: (pid %d) %s\n", argv0, getpid(), buf);
1114 	}else
1115 		buf[0] = '\0';
1116 	if(mainpid){
1117 		/* two hits are sometimes needed */
1118 		postnote(PNGROUP, mainpid, "die1 - from srvold9p");
1119 		postnote(PNGROUP, mainpid, "die2 - from srvold9p");
1120 	}
1121 	exits(buf);
1122 }
1123