xref: /inferno-os/tools/libstyx/styxserver.c (revision a60fa48ce2f27a689f276bea9538b5db2b74ff86)
1 #include <lib9.h>
2 #include <styx.h>
3 #include "styxserver.h"
4 #include "styxaux.h"
5 
6 #define MAXSTAT	512
7 #define EMSGLEN			256		/* %r */
8 
9 #define TABSZ	32	/* power of 2 */
10 
11 static	unsigned long		boottime;
12 static	char*	eve = "inferno";
13 static	int		Debug = 0;
14 
15 char Enomem[] =		"out of memory";
16 char Eperm[] =			"permission denied";
17 char Enodev[] =		"no free devices";
18 char Ehungup[] =		"write to hungup channel";
19 char	Eexist[] =			"file exists";
20 char Enonexist[] =		"file does not exist";
21 char Ebadcmd[] =		"bad command";
22 char Ebadarg[] =			"bad arg in system call";
23 char Enofid[] =			"no such fid";
24 char Enotdir[] =		"not a directory";
25 char	Eopen[] =			"already open";
26 char	Ebadfid[] =		"bad fid";
27 
28 /* client state */
29 enum{
30 	CDISC = 01,
31 	CNREAD = 02,
32 	CRECV = 04,
33 };
34 
35 typedef struct Walkqid Walkqid;
36 
37 struct Fid
38 {
39 	Client 	*client;
40 	Fid *next;
41 	short	fid;
42 	ushort	open;
43 	ushort	mode;	/* read/write */
44 	ulong	offset;	/* in file */
45 	int		dri;		/* dirread index */
46 	Qid		qid;
47 };
48 
49 struct Walkqid
50 {
51 	Fid	*clone;
52 	int	nqid;
53 	Qid	qid[1];
54 };
55 
56 #define ASSERT(A,B) styxassert((int)A,B)
57 
58 static int hash(Path);
59 static void deletefids(Client *);
60 
61 static void
62 styxfatal(char *fmt, ...)
63 {
64 	char buf[1024], *out;
65 	va_list arg;
66 	out = seprint(buf, buf+sizeof(buf), "Fatal error: ");
67 	va_start(arg, fmt);
68 	out = vseprint(out, buf+sizeof(buf), fmt, arg);
69 	va_end(arg);
70 	write(2, buf, out-buf);
71 	styxexit(1);
72 }
73 
74 static void
75 styxassert(int true, char *reason)
76 {
77 	if(!true)
78 		styxfatal("assertion failed: %s\n", reason);
79 }
80 
81 void *
82 styxmalloc(int bytes)
83 {
84 	char *m = malloc(bytes);
85 	if(m == nil)
86 		styxfatal(Enomem);
87 	memset(m, 0, bytes);
88 	return m;
89 }
90 
91 void
92 styxfree(void *p)
93 {
94 	free(p);
95 }
96 
97 void
98 styxdebug()
99 {
100 	Debug = 1;
101 }
102 
103 static Client *
104 newclient(Styxserver *server, int fd)
105 {
106 	Client *c = (Client *)styxmalloc(sizeof(Client));
107 
108 	if(Debug)
109 		fprint(2, "New client at %lux\n", (ulong)c);
110 	c->server = server;
111 	c->fd = fd;
112 	c->nread = 0;
113 	c->nc = 0;
114 	c->state = 0;
115 	c->fids = nil;
116 	c->uname = strdup(eve);
117 	c->aname = strdup("");
118 	c->next = server->clients;
119 	server->clients = c;
120 	if(server->ops->newclient)
121 		server->ops->newclient(c);
122 	return c;
123 }
124 
125 static void
126 freeclient(Client *c)
127 {
128 	Client **p;
129 	Styxserver *server;
130 
131 	if(Debug)
132 		fprint(2, "Freeing client at %lux\n", (ulong)c);
133 	server = c->server;
134 	if(server->ops->freeclient)
135 		server->ops->freeclient(c);
136 	for(p = &server->clients; *p; p = &(*p)->next)
137 		if(*p == c){
138 			styxclosesocket(c->fd);
139 			*p = c->next;
140 			deletefids(c);
141 			free(c->uname);
142 			free(c->aname);
143 			styxfree(c);
144 			return;
145 		}
146 }
147 
148 static int
149 nbread(Client *c, int nr)
150 {
151 	int nb;
152 
153 	if(c->state&CDISC)
154 		return -1;
155 	nb = styxrecv(c->server, c->fd, c->msg + c->nread, nr, 0);
156 	if(nb <= 0){
157 		c->nread = 0;
158 		c->state |= CDISC;
159 		return -1;
160 	}
161 	c->nread += nb;
162 	return 0;
163 }
164 
165 static int
166 rd(Client *c, Fcall *r)
167 {
168 	if(c->nc > 0){	/* last convM2S consumed nc bytes */
169 		c->nread -= c->nc;
170 		if((int)c->nread < 0){
171 			r->ename = "negative size in rd";
172 			return -1;
173 		}
174 		memmove(c->msg, c->msg+c->nc, c->nread);
175 		c->nc = 0;
176 	}
177 	if(c->state&CRECV){
178 		if(nbread(c, MSGMAX - c->nread) != 0){
179 			r->ename = "unexpected EOF";
180 			return -1;
181 		}
182 		c->state &= ~CRECV;
183 	}
184 	c->nc = convM2S((uchar*)(c->msg), c->nread, r);
185 	if(c->nc < 0){
186 		r->ename = "bad message format";
187 		return -1;
188 	}
189 	if(c->nc == 0 && c->nread > 0){
190 		c->nread = 0;
191 		c->state &= ~CNREAD;
192 		return 0;
193 	}
194 	if(c->nread > c->nc)
195 		c->state |= CNREAD;
196 	else
197 		c->state &= ~CNREAD;
198 	if(c->nc == 0)
199 		return 0;
200 	/* fprint(2, "rd: %F\n", r); */
201 	return 1;
202 }
203 
204 static int
205 wr(Client *c, Fcall *r)
206 {
207 	int n;
208 	char buf[MSGMAX];
209 
210 	n = convS2M(r, (uchar*)buf, sizeof(buf));
211 	if(n < 0){
212 		r->ename = "bad message type in wr";
213 		return -1;
214 	}
215 	/* fprint(2, "wr: %F\n", r); */
216 	return styxsend(c->server, c->fd, buf, n, 0);
217 }
218 
219 static void
220 sremove(Styxserver *server, Styxfile *f)
221 {
222 	Styxfile *s, *next, **p;
223 
224 	if(f == nil)
225 		return;
226 	if(Debug)
227 		fprint(2, "Remove file %s Qid=%llx\n", f->d.name, f->d.qid.path);
228 	if(f->d.qid.type&QTDIR)
229 		for(s = f->child; s != nil; s = next){
230 			next = s->sibling;
231 			sremove(server, s);
232 	}
233 	for(p = &server->ftab[hash(f->d.qid.path)]; *p; p = &(*p)->next)
234 		if(*p == f){
235 			*p = f->next;
236 			break;
237 		}
238 	for(p = &f->parent->child; *p; p = &(*p)->sibling)
239 		if(*p == f){
240 			*p = f->sibling;
241 			break;
242 		}
243 	styxfree(f->d.name);
244 	styxfree(f->d.uid);
245 	styxfree(f->d.gid);
246 	styxfree(f);
247 }
248 
249 int
250 styxrmfile(Styxserver *server, Path qid)
251 {
252 	Styxfile *f;
253 
254 	f = styxfindfile(server, qid);
255 	if(f != nil){
256 		if(f->parent == nil)
257 			return -1;
258 		sremove(server, f);
259 		return 0;
260 	}
261 	return -1;
262 }
263 
264 static void
265 incref(Styxfile *f)
266 {
267 	if(f != nil)
268 		f->ref++;
269 }
270 
271 static void
272 decref(Styxfile *f)
273 {
274 	if(f != nil)
275 		--f->ref;
276 }
277 
278 static void
279 increff(Fid *f)
280 {
281 	incref(styxfindfile(f->client->server, f->qid.path));
282 }
283 
284 static void
285 decreff(Fid *f)
286 {
287 	decref(styxfindfile(f->client->server, f->qid.path));
288 }
289 
290 static void
291 incopen(Fid *f)
292 {
293 	Styxfile *file;
294 
295 	if(f->open && (file = styxfindfile(f->client->server, f->qid.path)) != nil)
296 		file->open++;
297 }
298 
299 static void
300 decopen(Fid *f)
301 {
302 	Styxfile *file;
303 
304 	if(f->open && (file = styxfindfile(f->client->server, f->qid.path)) != nil)
305 		file->open--;
306 }
307 
308 int
309 styxperm(Styxfile *f, char *uid, int mode)
310 {
311 	int m, p;
312 
313 	p = 0;
314 	switch(mode&3){
315 	case OREAD:	p = AREAD;	break;
316 	case OWRITE:	p = AWRITE;	break;
317 	case ORDWR:	p = AREAD+AWRITE;	break;
318 	case OEXEC:	p = AEXEC;	break;
319 	}
320 	if(mode&OTRUNC)
321 		p |= AWRITE;
322 	m = f->d.mode&7;
323 	if((p&m) == p)
324 		return 1;
325 	if(strcmp(f->d.uid, uid) == 0){
326 		m |= (f->d.mode>>6)&7;
327 		if((p&m) == p)
328 			return 1;
329 	}
330 	if(strcmp(f->d.gid, uid) == 0){
331 		m |= (f->d.mode>>3)&7;
332 		if((p&m) == p)
333 			return 1;
334 	}
335 	return 0;
336 }
337 
338 static int
339 hash(Path path)
340 {
341 	return path&(TABSZ-1);
342 }
343 
344 Styxfile *
345 styxfindfile(Styxserver *server, Path path)
346 {
347 	Styxfile *f;
348 
349 	for(f = server->ftab[hash(path)]; f != nil; f = f->next){
350 		if(f->d.qid.path == path)
351 			return f;
352 	}
353 	return nil;
354 }
355 
356 static Fid *
357 findfid(Client *c, short fid)
358 {
359 	Fid *f;
360 	for(f = c->fids; f && f->fid != fid; f = f->next)
361 		;
362 	return f;
363 }
364 
365 static void
366 deletefid(Client *c, Fid *d)
367 {
368 	/* TODO: end any outstanding reads on this fid */
369 	Fid **f;
370 
371 	for(f = &c->fids; *f; f = &(*f)->next)
372 		if(*f == d){
373 			decreff(d);
374 			decopen(d);
375 			*f = d->next;
376 			styxfree(d);
377 			return;
378 		}
379 }
380 
381 static void
382 deletefids(Client *c)
383 {
384 	Fid *f, *g;
385 
386 	for(f = c->fids; f; f = g){
387 		decreff(f);
388 		decopen(f);
389 		g = f->next;
390 		styxfree(f);
391 	}
392 }
393 
394 Fid *
395 newfid(Client *c, short fid, Qid qid){
396 	Fid *f;
397 
398 	f = styxmalloc(sizeof(Fid));
399 	ASSERT(f, "newfid");
400 	f->client = c;
401 	f->fid = fid;
402 	f->open = 0;
403 	f->dri = 0;
404 	f->qid = qid;
405 	f->next = c->fids;
406 	c->fids = f;
407 	increff(f);
408 	return f;
409 }
410 
411 static void
412 flushtag(int oldtag)
413 {
414 	USED(oldtag);
415 }
416 
417 int
418 eqqid(Qid a, Qid b)
419 {
420 	return a.path == b.path && a.vers == b.vers;
421 }
422 
423 static Fid *
424 fidclone(Fid *old, short fid)
425 {
426 	Fid *new;
427 
428 	new = newfid(old->client, fid, old->qid);
429 	return new;
430 }
431 
432 static Walkqid*
433 devwalk(Client *c, Styxfile *file, Fid *fp, Fid *nfp, char **name, int nname, char **err)
434 {
435 	Styxserver *server;
436 	long j;
437 	Walkqid *wq;
438 	char *n;
439 	Styxfile *p, *f;
440 	Styxops *ops;
441 	Qid qid;
442 
443 	*err = nil;
444 	server = c->server;
445 	ops = server->ops;
446 
447 	wq = styxmalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
448 	wq->nqid = 0;
449 
450 	p = file;
451 	qid = p != nil ? p->d.qid : fp->qid;
452 	for(j = 0; j < nname; j++){
453 		if(!(qid.type&QTDIR)){
454 			if(j == 0)
455 				styxfatal("devwalk error");
456 			*err = Enotdir;
457 			goto Done;
458 		}
459 		if(p != nil && !styxperm(p, c->uname, OEXEC)){
460 			*err = Eperm;
461 			goto Done;
462 		}
463 		n = name[j];
464 		if(strcmp(n, ".") == 0){
465     Accept:
466 			wq->qid[wq->nqid++] = nfp->qid;
467 			continue;
468 		}
469 		if(p != nil && strcmp(n, "..") == 0 && p->parent){
470 			decref(p);
471 			nfp->qid.path = p->parent->d.qid.path;
472 			nfp->qid.type = p->parent->d.qid.type;
473 			nfp->qid.vers = 0;
474 			incref(p->parent);
475 			p = p->parent;
476 			qid = p->d.qid;
477 			goto Accept;
478 		}
479 
480 		if(ops->walk != nil){
481 			char *e;
482 
483 			e = ops->walk(&qid, n);
484 			if(e == nil){
485 				decreff(nfp);
486 				nfp->qid = qid;
487 				increff(nfp);
488 				p = styxfindfile(server, qid.path);
489 				if(server->needfile && p == nil)
490 					goto Done;
491 				qid = p != nil ? p->d.qid : nfp->qid;
492 				goto Accept;
493 			}
494 		}
495 
496 		if(p != nil)
497 		for(f = p->child; f != nil; f = f->sibling){
498 			if(strcmp(n, f->d.name) == 0){
499 				decref(p);
500 				nfp->qid.path = f->d.qid.path;
501 				nfp->qid.type = f->d.qid.type;
502 				nfp->qid.vers = 0;
503 				incref(f);
504 				p = f;
505 				qid = p->d.qid;
506 				goto Accept;
507 			}
508 		}
509 		if(j == 0 && *err == nil)
510 			*err = Enonexist;
511 		goto Done;
512 	}
513 Done:
514 	if(*err != nil){
515 		styxfree(wq);
516 		return nil;
517 	}
518 	return wq;
519 }
520 
521 static long
522 devdirread(Fid *fp, Styxfile *file, char *d, long n)
523 {
524 	long dsz, m;
525 	Styxfile *f;
526 	int i;
527 
528 	struct{
529 		Dir d;
530 		char slop[100];	/* TO DO */
531 	}dir;
532 
533 	f = file->child;
534 	for(i = 0; i < fp->dri; i++)
535 		if(f == 0)
536 			return 0;
537 		else
538 			f = f->sibling;
539 	for(m = 0; m < n; fp->dri++){
540 		if(f == nil)
541 			break;
542 		dir.d = f->d;
543 		dsz = convD2M(&dir.d, (uchar*)d, n-m);
544 		m += dsz;
545 		d += dsz;
546 		f = f->sibling;
547 	}
548 
549 	return m;
550 }
551 
552 static char*
553 nilconv(char *s)
554 {
555 	if(s != nil && s[0] == '\0')
556 		return nil;
557 	return s;
558 }
559 
560 static Styxfile *
561 newfile(Styxserver *server, Styxfile *parent, int isdir, Path qid, char *name, int mode, char *owner)
562 {
563 	Styxfile *file;
564 	Dir d;
565 	int h;
566 
567 	if(qid == -1)
568 		qid = server->qidgen++;
569 	file = styxfindfile(server, qid);
570 	if(file != nil)
571 		return nil;
572 	if(parent != nil){
573 		for(file = parent->child; file != nil; file = file->sibling)
574 			if(strcmp(name, file->d.name) == 0)
575 				return nil;
576 	}
577 	file = (Styxfile *)styxmalloc(sizeof(Styxfile));
578 	file->parent = parent;
579 	file->child = nil;
580 	h = hash(qid);
581 	file->next = server->ftab[h];
582 	server->ftab[h] = file;
583 	if(parent){
584 		file->sibling = parent->child;
585 		parent->child = file;
586 	}else
587 		file->sibling = nil;
588 
589 	d.type = 'X';
590 	d.dev = 'x';
591 	d.qid.path = qid;
592 	d.qid.type = 0;
593 	d.qid.vers = 0;
594 	d.mode = mode;
595 	d.atime = time(0);
596 	d.mtime = boottime;
597 	d.length = 0;
598 	d.name = strdup(name);
599 	d.uid = strdup(owner);
600 	d.gid = strdup(eve);
601 	d.muid = "";
602 
603 	if(isdir){
604 		d.qid.type |= QTDIR;
605 		d.mode |= DMDIR;
606 	}
607 	else{
608 		d.qid.type &= ~QTDIR;
609 		d.mode &= ~DMDIR;
610 	}
611 
612 	file->d = d;
613 	file->ref = 0;
614 	file->open = 0;
615 	if(Debug)
616 		fprint(2, "New file %s Qid=%llx\n", name, qid);
617 	return file;
618 }
619 
620 static void
621 run(Client *c)
622 {
623 	Fcall f;
624 	Fid *fp, *nfp;
625 	int i, open, mode;
626 	char ebuf[EMSGLEN];
627 	Walkqid *wq;
628 	Styxfile *file;
629 	Dir dir;
630 	Qid qid;
631 	Styxops *ops;
632 	char strs[128];
633 
634 	ebuf[0] = 0;
635 	if(rd(c, &f) <= 0)
636 		return;
637 	if(f.type == Tflush){
638 		flushtag(f.oldtag);
639 		f.type = Rflush;
640 		wr(c, &f);
641 		return;
642 	}
643 	ops = c->server->ops;
644 	file = nil;
645 	fp = findfid(c, f.fid);
646 	if(f.type != Tversion && f.type != Tauth && f.type != Tattach){
647 		if(fp == nil){
648 			f.type = Rerror;
649 			f.ename = Enofid;
650 			wr(c, &f);
651 			return;
652 		}
653 		else{
654 			file = styxfindfile(c->server, fp->qid.path);
655 			if(c->server->needfile && file == nil){
656 				f.type = Rerror;
657 				f.ename = Enonexist;
658 				wr(c, &f);
659 				return;
660 			}
661 		}
662 	}
663 	/* if(fp == nil) fprint(2, "fid not found for %d\n", f.fid); */
664 	switch(f.type){
665 	case	Twalk:
666 		if(Debug){
667 			fprint(2, "Twalk %d %d", f.fid, f.newfid);
668 			for(i = 0; i < f.nwname; i++)
669 				fprint(2, " %s", f.wname[i]);
670 			fprint(2, "\n");
671 		}
672 		nfp = findfid(c, f.newfid);
673 		f.type = Rerror;
674 		if(nfp){
675 			deletefid(c, nfp);
676 			nfp = nil;
677 		}
678 		if(nfp){
679 			f.ename = "fid in use";
680 			if(Debug) fprint(2, "walk: %s\n", f.ename);
681 			wr(c, &f);
682 			break;
683 		}else if(fp->open){
684 			f.ename = "can't clone";
685 			wr(c, &f);
686 			break;
687 		}
688 		if(f.newfid != f.fid)
689 			nfp = fidclone(fp, f.newfid);
690 		else
691 			nfp = fp;
692 		if((wq = devwalk(c, file, fp, nfp, f.wname, f.nwname, &f.ename)) == nil){
693 			if(nfp != fp)
694 				deletefid(c, nfp);
695 			f.type = Rerror;
696 		}else{
697 			if(nfp != fp){
698 				if(wq->nqid != f.nwname)
699 					deletefid(c, nfp);
700 			}
701 			f.type = Rwalk;
702 			f.nwqid = wq->nqid;
703 			for(i = 0; i < wq->nqid; i++)
704 				f.wqid[i] = wq->qid[i];
705 			styxfree(wq);
706 		}
707 		wr(c, &f);
708 		break;
709 	case	Topen:
710 		if(Debug)
711 			fprint(2, "Topen %d\n", f.fid);
712 		f.ename = nil;
713 		if(fp->open)
714 			f.ename = Eopen;
715 		else if((fp->qid.type&QTDIR) && (f.mode&(OWRITE|OTRUNC|ORCLOSE)))
716 			f.ename = Eperm;
717 		else if(file != nil && !styxperm(file, c->uname, f.mode))
718 			f.ename = Eperm;
719 		else if((f.mode&ORCLOSE) && file != nil && file->parent != nil && !styxperm(file->parent, c->uname, OWRITE))
720 			f.ename = Eperm;
721 		if(f.ename != nil){
722 			f.type = Rerror;
723 			wr(c, &f);
724 			break;
725 		}
726 		f.ename = Enonexist;
727 		decreff(fp);
728 		if(ops->open == nil || (f.ename = ops->open(&fp->qid, f.mode)) == nil){
729 			f.type = Ropen;
730 			f.qid = fp->qid;
731 			fp->mode = f.mode;
732 			fp->open = 1;
733 			fp->offset = 0;
734 			incopen(fp);
735 		}
736 		else
737 			f.type = Rerror;
738 		increff(fp);
739 		wr(c, &f);
740 		break;
741 	case	Tcreate:
742 		if(Debug)
743 			fprint(2, "Tcreate %d %s\n", f.fid, f.name);
744 		f.ename = nil;
745 		if(fp->open)
746 			f.ename = Eopen;
747 		else if(!(fp->qid.type&QTDIR))
748 			f.ename = Enotdir;
749 		else if((f.perm&DMDIR) && (f.mode&(OWRITE|OTRUNC|ORCLOSE)))
750 			f.ename = Eperm;
751 		else if(file != nil && !styxperm(file, c->uname, OWRITE))
752 			f.ename = Eperm;
753 		if(f.ename != nil){
754 			f.type = Rerror;
755 			wr(c, &f);
756 			break;
757 		}
758 		f.ename = Eperm;
759 		decreff(fp);
760 		if(file != nil){
761 			if(f.perm&DMDIR)
762 				f.perm = (f.perm&~0777) | (file->d.mode&f.perm&0777) | DMDIR;
763 			else
764 				f.perm = (f.perm&(~0777|0111)) | (file->d.mode&f.perm&0666);
765 		}
766 		if(ops->create && (f.ename = ops->create(&fp->qid, f.name, f.perm, f.mode)) == nil){
767 			f.type = Rcreate;
768 			f.qid = fp->qid;
769 			fp->mode = f.mode;
770 			fp->open = 1;
771 			fp->offset = 0;
772 			incopen(fp);
773 		}
774 		else
775 			f.type = Rerror;
776 		increff(fp);
777 		wr(c, &f);
778 		break;
779 	case	Tread:
780 		if(Debug)
781 			fprint(2, "Tread %d\n", f.fid);
782 		if(!fp->open){
783 			f.type = Rerror;
784 			f.ename = Ebadfid;
785 			wr(c, &f);
786 			break;
787 		}
788 		if(fp->qid.type&QTDIR || (file != nil && file->d.qid.type&QTDIR)){
789 			f.type = Rread;
790 			if(file == nil){
791 				f.ename = Eperm;
792 				if(ops->read && (f.ename = ops->read(fp->qid, c->data, (ulong*)(&f.count), fp->dri)) == nil){
793 					f.data = c->data;
794 				}
795 				else
796 					f.type = Rerror;
797 			}
798 			else{
799 				f.count = devdirread(fp, file, c->data, f.count);
800 				f.data = c->data;
801 			}
802 		}else{
803 			f.ename = Eperm;
804 			f.type = Rerror;
805 			if(ops->read && (f.ename = ops->read(fp->qid, c->data, (ulong*)(&f.count), f.offset)) == nil){
806 				f.type = Rread;
807 				f.data = c->data;
808 			}
809 		}
810 		wr(c, &f);
811 		break;
812 	case	Twrite:
813 		if(Debug)
814 			fprint(2, "Twrite %d\n", f.fid);
815 		if(!fp->open){
816 			f.type = Rerror;
817 			f.ename = Ebadfid;
818 			wr(c, &f);
819 			break;
820 		}
821 		f.ename = Eperm;
822 		f.type = Rerror;
823 		if(ops->write && (f.ename = ops->write(fp->qid, f.data, (ulong*)(&f.count), f.offset)) == nil){
824 			f.type = Rwrite;
825 		}
826 		wr(c, &f);
827 		break;
828 	case	Tclunk:
829 		if(Debug)
830 			fprint(2, "Tclunk %d\n", f.fid);
831 		open = fp->open;
832 		mode = fp->mode;
833 		qid = fp->qid;
834 		deletefid(c, fp);
835 		f.type = Rclunk;
836 		if(open && ops->close && (f.ename = ops->close(qid, mode)) != nil)
837 			f.type = Rerror;
838 		wr(c, &f);
839 		break;
840 	case	Tremove:
841 		if(Debug)
842 			fprint(2, "Tremove %d\n", f.fid);
843 		if(file != nil && file->parent != nil && !styxperm(file->parent, c->uname, OWRITE)){
844 			f.type = Rerror;
845 			f.ename = Eperm;
846 			deletefid(c, fp);
847 			wr(c, &f);
848 			break;
849 		}
850 		f.ename = Eperm;
851 		if(ops->remove && (f.ename = ops->remove(fp->qid)) == nil)
852 			f.type = Rremove;
853 		else
854 			f.type = Rerror;
855 		deletefid(c, fp);
856 		wr(c, &f);
857 		break;
858 	case	Tstat:
859 		if(Debug)
860 			fprint(2, "Tstat %d qid=%llx\n", f.fid, fp->qid.path);
861 		f.stat = styxmalloc(MAXSTAT);
862 		f.ename = "stat error";
863 		if(ops->stat == nil && file != nil){
864 			f.type = Rstat;
865 			f.nstat = convD2M(&file->d, f.stat, MAXSTAT);
866 		}
867 		else if(ops->stat && (f.ename = ops->stat(fp->qid, &dir)) == nil){
868 			f.type = Rstat;
869 			f.nstat = convD2M(&dir, f.stat, MAXSTAT);
870 		}
871 		else
872 			f.type = Rerror;
873 		wr(c, &f);
874 		styxfree(f.stat);
875 		break;
876 	case	Twstat:
877 		if(Debug)
878 			fprint(2, "Twstat %d\n", f.fid);
879 		f.ename = Eperm;
880 		convM2D(f.stat, f.nstat, &dir, strs);
881 		dir.name = nilconv(dir.name);
882 		dir.uid = nilconv(dir.uid);
883 		dir.gid = nilconv(dir.gid);
884 		dir.muid = nilconv(dir.muid);
885 		if(ops->wstat && (f.ename = ops->wstat(fp->qid, &dir)) == nil)
886 			f.type = Rwstat;
887 		else
888 			f.type = Rerror;
889 		wr(c, &f);
890 		break;
891 	case	Tversion:
892 		if(Debug)
893 			fprint(2, "Tversion\n");
894 		f.type = Rversion;
895 		f.tag = NOTAG;
896 		wr(c, &f);
897 		break;
898 	case Tauth:
899 		if(Debug)
900 			fprint(2, "Tauth\n");
901 		f.type = Rauth;
902 		wr(c, &f);
903 		break;
904 	case	Tattach:
905 		if(Debug)
906 			fprint(2, "Tattach %d %s %s\n", f.fid, f.uname[0] ? f.uname : c->uname, f.aname[0]? f.aname: c->aname);
907 		if(fp){
908 			f.type = Rerror;
909 			f.ename = "fid in use";
910 		}else{
911 			Qid q;
912 
913 			if(f.uname[0]){
914 				free(c->uname);
915 				c->uname = strdup(f.uname);
916 			}
917 			if(f.aname[0]){
918 				free(c->aname);
919 				c->aname = strdup(f.aname);
920 			}
921 			q.path = Qroot;
922 			q.type = QTDIR;
923 			q.vers = 0;
924 			fp = newfid(c, f.fid, q);
925 			f.type = Rattach;
926 			f.fid = fp->fid;
927 			f.qid = q;
928 			if(ops->attach && (f.ename = ops->attach(c->uname, c->aname)) != nil)
929 				f.type = Rerror;
930 		}
931 		wr(c, &f);
932 		break;
933 	}
934 }
935 
936 char *
937 styxinit(Styxserver *server, Styxops *ops, char *port, int perm, int needfile)
938 {
939 	int i;
940 
941 	if(Debug)
942 		fprint(2, "Initialising Styx server on port %s\n", port);
943 	if(perm == -1)
944 		perm = 0555;
945 	server->ops = ops;
946 	server->clients = nil;
947 	server->root = nil;
948 	server->ftab = (Styxfile**)malloc(TABSZ*sizeof(Styxfile*));
949 	for(i = 0; i < TABSZ; i++)
950 		server->ftab[i] = nil;
951 	server->qidgen = Qroot+1;
952 	if(styxinitsocket() < 0)
953 		return "styxinitsocket failed";
954 	server->connfd = styxannounce(server, port);
955 	if(server->connfd < 0)
956 		return "can't announce on network port";
957 	styxinitwait(server);
958 	server->root = newfile(server, nil, 1, Qroot, "/", perm|DMDIR, eve);
959 	server->needfile = needfile;
960 	return nil;
961 }
962 
963 char*
964 styxend(Styxserver *server)
965 {
966 	USED(server);
967 	styxendsocket();
968 	return nil;
969 }
970 
971 char *
972 styxwait(Styxserver *server)
973 {
974 	return styxwaitmsg(server);
975 }
976 
977 char *
978 styxprocess(Styxserver *server)
979 {
980 	Client *c;
981 	int s;
982 
983 	if(styxnewcall(server)){
984 		s = styxaccept(server);
985 		if(s >= 0){
986 			newclient(server, s);
987 			styxnewclient(server, s);
988 		}
989 	}
990 	for(c = server->clients; c != nil; ){
991 		Client *next = c->next;
992 
993 		server->curc = c;
994 		if(c->fd >= 0 && styxnewmsg(server, c->fd))
995 			c->state |= CRECV;
996 		if(c->state&(CNREAD|CRECV)){
997 			if(c->state&CDISC){
998 				styxfreeclient(server, c->fd);
999 				freeclient(c);
1000 			}else
1001 				do
1002 					run(c);
1003 				while(c->state&CNREAD);
1004 		}
1005 		c = next;
1006 	}
1007 
1008 	return nil;
1009 }
1010 
1011 Client*
1012 styxclient(Styxserver *server)
1013 {
1014 	return server->curc;
1015 }
1016 
1017 Styxfile*
1018 styxaddfile(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner)
1019 {
1020 	Styxfile *f, *parent;
1021 
1022 	parent = styxfindfile(server, pqid);
1023 	if(parent == nil || (parent->d.qid.type&QTDIR) == 0)
1024 		return nil;
1025 	f = newfile(server, parent, 0, qid, name, mode, owner);
1026 	return f;
1027 }
1028 
1029 Styxfile*
1030 styxadddir(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner)
1031 {
1032 	Styxfile *f, *parent;
1033 
1034 	parent = styxfindfile(server, pqid);
1035 	if(parent == nil || (parent->d.qid.type&QTDIR) == 0)
1036 		return nil;
1037 	f = newfile(server, parent, 1, qid, name, mode|DMDIR, owner);
1038 	return f;
1039 }
1040 
1041 long
1042 styxreadstr(ulong off, char *buf, ulong n, char *str)
1043 {
1044 	int size;
1045 
1046 	size = strlen(str);
1047 	if(off >= size)
1048 		return 0;
1049 	if(off+n > size)
1050 		n = size-off;
1051 	memmove(buf, str+off, n);
1052 	return n;
1053 }
1054 
1055 Qid
1056 styxqid(int path, int isdir)
1057 {
1058 	Qid q;
1059 
1060 	q.path = path;
1061 	q.vers = 0;
1062 	if(isdir)
1063 		q.type = QTDIR;
1064 	else
1065 		q.type = 0;
1066 	return q;
1067 }
1068 
1069 void
1070 styxsetowner(char *name)
1071 {
1072 	eve = name;
1073 }
1074