xref: /inferno-os/emu/port/devsrv.c (revision fd7058f9a883832e948d667b63c56178e37b1e15)
1 #include	"dat.h"
2 #include	"fns.h"
3 #include	"error.h"
4 #include	"interp.h"
5 #include	<isa.h>
6 #include	"runt.h"
7 
8 typedef struct SrvFile SrvFile;
9 typedef struct Pending Pending;
10 
11 /* request pending to a server, in case a server vanishes */
12 struct Pending
13 {
14 	Pending*	next;
15 	Pending*	prev;
16 	int fid;
17 	Channel*	rc;
18 	Channel*	wc;
19 };
20 
21 struct SrvFile
22 {
23 	char*	name;
24 	char*	user;
25 	ulong		perm;
26 	Qid		qid;
27 	int		ref;
28 
29 	/* root directory */
30 	char*	spec;
31 	SrvFile*	devlist;
32 	SrvFile*	entry;
33 
34 	/* file */
35 	int		opens;
36 	int		flags;
37 	vlong	length;
38 	Channel*	read;
39 	Channel*	write;
40 	SrvFile*	dir;		/* parent directory */
41 	Pending	waitlist;	/* pending requests from client opens */
42 };
43 
44 enum
45 {
46 	SORCLOSE	= (1<<0),
47 	SRDCLOSE	= (1<<1),
48 	SWRCLOSE	= (1<<2),
49 	SREMOVED	= (1<<3),
50 };
51 
52 typedef struct SrvDev SrvDev;
53 struct SrvDev
54 {
55 	Type*	Rread;
56 	Type*	Rwrite;
57 	QLock	l;
58 	ulong	pathgen;
59 	SrvFile*	devices;
60 };
61 
62 static SrvDev dev;
63 
64 void	freechan(Heap*, int);
65 static void	freerdchan(Heap*, int);
66 static void	freewrchan(Heap*, int);
67 static void delwaiting(Pending*);
68 
69 Type	*Trdchan;
70 Type	*Twrchan;
71 
72 static int
73 srvgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
74 {
75 	SrvFile *f;
76 
77 	USED(name);
78 	USED(tab);
79 	USED(ntab);
80 
81 	if(s == DEVDOTDOT){
82 		devdir(c, c->qid, "#s", 0, eve, 0555, dp);
83 		return 1;
84 	}
85 	f = c->aux;
86 	if((c->qid.type & QTDIR) == 0){
87 		if(s > 0)
88 			return -1;
89 		devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
90 		return 1;
91 	}
92 
93 	for(f = f->entry; f != nil; f = f->entry){
94 		if(s-- == 0)
95 			break;
96 	}
97 	if(f == nil)
98 		return -1;
99 
100 	devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
101 	return 1;
102 }
103 
104 static void
105 srvinit(void)
106 {
107 	static uchar rmap[] = Sys_Rread_map;
108 	static uchar wmap[] = Sys_Rwrite_map;
109 
110 	Trdchan = dtype(freerdchan, sizeof(Channel), Tchannel.map, Tchannel.np);
111 	Twrchan = dtype(freewrchan, sizeof(Channel), Tchannel.map, Tchannel.np);
112 
113 	dev.pathgen = 1;
114 	dev.Rread = dtype(freeheap, Sys_Rread_size, rmap, sizeof(rmap));
115 	dev.Rwrite = dtype(freeheap, Sys_Rwrite_size, wmap, sizeof(wmap));
116 }
117 
118 static int
119 srvcanattach(SrvFile *d)
120 {
121 	if(strcmp(d->user, up->env->user) == 0)
122 		return 1;
123 
124 	/*
125 	 * Need write permission in other to allow attaches if
126 	 * we are not the owner
127 	 */
128 	if(d->perm & 2)
129 		return 1;
130 
131 	return 0;
132 }
133 
134 static Chan*
135 srvattach(char *spec)
136 {
137 	Chan *c;
138 	SrvFile *d;
139 	char srvname[16];
140 
141 	qlock(&dev.l);
142 	if(waserror()){
143 		qunlock(&dev.l);
144 		nexterror();
145 	}
146 
147 	if(spec[0] != '\0'){
148 		for(d = dev.devices; d != nil; d = d->devlist){
149 			if(strcmp(spec, d->spec) == 0){
150 				if(!srvcanattach(d))
151 					error(Eperm);
152 				c = devattach('s', spec);
153 				c->aux = d;
154 				c->qid = d->qid;
155 				d->ref++;
156 				poperror();
157 				qunlock(&dev.l);
158 				return c;
159 			}
160 		}
161 	}
162 
163 	d = malloc(sizeof(SrvFile));
164 	if(d == nil)
165 		error(Enomem);
166 
167 	d->ref = 1;
168 	kstrdup(&d->spec, spec);
169 	kstrdup(&d->user, up->env->user);
170 	snprint(srvname, sizeof(srvname), "srv%ld", up->env->pgrp->pgrpid);
171 	kstrdup(&d->name, srvname);
172 	d->perm = DMDIR|0770;
173 	mkqid(&d->qid, dev.pathgen++, 0, QTDIR);
174 
175 	d->devlist = dev.devices;
176 	dev.devices = d;
177 
178 	poperror();
179 	qunlock(&dev.l);
180 
181 	c = devattach('s', spec);
182 	c->aux = d;
183 	c->qid = d->qid;
184 
185 	return c;
186 }
187 
188 static Walkqid*
189 srvwalk(Chan *c, Chan *nc, char **name, int nname)
190 {
191 	SrvFile *d, *pd;
192 	Walkqid *w;
193 
194 	pd = c->aux;
195 	qlock(&dev.l);
196 	if(waserror()){
197 		qunlock(&dev.l);
198 		nexterror();
199 	}
200 
201 	w = devwalk(c, nc, name, nname, nil, 0, srvgen);
202 	if(w != nil && w->clone != nil){
203 		if(nname != 0){
204 			for(d = pd->entry; d != nil; d = d->entry)
205 				if(d->qid.path == w->clone->qid.path)
206 					break;
207 			if(d == nil)
208 				panic("srvwalk");
209 			if(w->clone == c)
210 				pd->ref--;
211 		}else
212 			d = pd;
213 		w->clone->aux = d;
214 		d->ref++;
215 	}
216 
217 	poperror();
218 	qunlock(&dev.l);
219 	return w;
220 }
221 
222 static int
223 srvstat(Chan *c, uchar *db, int n)
224 {
225 	qlock(&dev.l);
226 	if(waserror()){
227 		qunlock(&dev.l);
228 		nexterror();
229 	}
230 	n = devstat(c, db, n, 0, 0, srvgen);
231 	poperror();
232 	qunlock(&dev.l);
233 	return n;
234 }
235 
236 static Chan*
237 srvopen(Chan *c, int omode)
238 {
239 	SrvFile *sf;
240 
241 	openmode(omode);	/* check it */
242 	if(c->qid.type & QTDIR){
243 		if(omode != OREAD)
244 			error(Eisdir);
245 		c->mode = omode;
246 		c->flag |= COPEN;
247 		c->offset = 0;
248 		return c;
249 	}
250 
251 	sf = c->aux;
252 
253 	qlock(&dev.l);
254 	if(waserror()){
255 		qunlock(&dev.l);
256 		nexterror();
257 	}
258 	devpermcheck(sf->user, sf->perm, omode);
259 	if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0)
260 		error(Eperm);
261 	if(sf->perm & DMEXCL && sf->opens != 0)
262 		error(Einuse);
263 	sf->opens++;
264 	if(omode&ORCLOSE)
265 		sf->flags |= SORCLOSE;
266 	poperror();
267 	qunlock(&dev.l);
268 
269 	c->offset = 0;
270 	c->flag |= COPEN;
271 	c->mode = openmode(omode);
272 
273 	return c;
274 }
275 
276 static int
277 srvwstat(Chan *c, uchar *dp, int n)
278 {
279 	Dir *d;
280 	SrvFile *sf, *f;
281 
282 	sf = c->aux;
283 	if(strcmp(up->env->user, sf->user) != 0)
284 		error(Eperm);
285 
286 	d = smalloc(sizeof(*d)+n);
287 	if(waserror()){
288 		free(d);
289 		nexterror();
290 	}
291 	n = convM2D(dp, n, d, (char*)&d[1]);
292 	if(n == 0)
293 		error(Eshortstat);
294 	if(!emptystr(d->name)){
295 		if(sf->dir == nil)
296 			error(Eperm);
297 		validwstatname(d->name);
298 		qlock(&dev.l);
299 		for(f = sf->dir; f != nil; f = f->entry)
300 			if(strcmp(f->name, d->name) == 0){
301 				qunlock(&dev.l);
302 				error(Eexist);
303 			}
304 		kstrdup(&sf->name, d->name);
305 		qunlock(&dev.l);
306 	}
307 	if(d->mode != ~0UL)
308 		sf->perm = d->mode & (DMEXCL|DMAPPEND|0777);
309 	if(d->length != (vlong)-1)
310 		sf->length = d->length;
311 	poperror();
312 	free(d);
313 	return n;
314 }
315 
316 static void
317 srvputdir(SrvFile *dir)
318 {
319 	SrvFile **l, *d;
320 
321 	dir->ref--;
322 	if(dir->ref != 0)
323 		return;
324 
325 	for(l = &dev.devices; (d = *l) != nil; l = &d->devlist)
326 		if(d == dir){
327 			*l = d->devlist;
328 			break;
329 		}
330 	free(dir->spec);
331 	free(dir->user);
332 	free(dir->name);
333 	free(dir);
334 }
335 
336 static void
337 srvunblock(SrvFile *sf, int fid)
338 {
339 	Channel *d;
340 	Sys_FileIO_read rreq;
341 	Sys_FileIO_write wreq;
342 
343 	acquire();
344 	if(waserror()){
345 		release();
346 		nexterror();
347 	}
348 	d = sf->read;
349 	if(d != H){
350 		rreq.t0 = 0;
351 		rreq.t1 = 0;
352 		rreq.t2 = fid;
353 		rreq.t3 = H;
354 		csendalt(d, &rreq, d->mid.t, -1);
355 	}
356 
357 	d = sf->write;
358 	if(d != H){
359 		wreq.t0 = 0;
360 		wreq.t1 = H;
361 		wreq.t2 = fid;
362 		wreq.t3 = H;
363 		csendalt(d, &wreq, d->mid.t, -1);
364 	}
365 	poperror();
366 	release();
367 }
368 
369 static void
370 srvcancelreqs(SrvFile *sf)
371 {
372 	Pending *w, *ws;
373 	Sys_Rread rreply;
374 	Sys_Rwrite wreply;
375 
376 	acquire();
377 	ws = &sf->waitlist;
378 	while((w = ws->next) != ws){
379 		delwaiting(w);
380 		if(waserror() == 0){
381 			if(w->rc != nil){
382 				rreply.t0 = H;
383 				rreply.t1 = c2string(Ehungup, strlen(Ehungup));
384 				csend(w->rc, &rreply);
385 			}
386 			if(w->wc != nil){
387 				wreply.t0 = 0;
388 				wreply.t1 = c2string(Ehungup, strlen(Ehungup));
389 				csend(w->wc, &wreply);
390 			}
391 			poperror();
392 		}
393 	}
394 	release();
395 }
396 
397 static void
398 srvdelete(SrvFile *sf)
399 {
400 	SrvFile *f, **l;
401 
402 	if((sf->flags & SREMOVED) == 0){
403 		for(l = &sf->dir->entry; (f = *l) != nil; l = &f->entry){
404 			if(sf == f){
405 				*l = f->entry;
406 				break;
407 			}
408 		}
409 		sf->ref--;
410 		sf->flags |= SREMOVED;
411 	}
412 }
413 
414 static void
415 srvchkref(SrvFile *sf)
416 {
417 	if(sf->ref != 0)
418 		return;
419 
420 	if(sf->dir != nil)
421 		srvputdir(sf->dir);
422 
423 	free(sf->user);
424 	free(sf->name);
425 	free(sf);
426 }
427 
428 static void
429 srvfree(SrvFile *sf, int flag)
430 {
431 	sf->flags |= flag;
432 	if((sf->flags & (SRDCLOSE | SWRCLOSE)) == (SRDCLOSE | SWRCLOSE)){
433 		sf->ref--;
434 		srvdelete(sf);
435 		/* no further requests can arrive; return error to pending requests */
436 		srvcancelreqs(sf);
437 		srvchkref(sf);
438 	}
439 }
440 
441 static void
442 freerdchan(Heap *h, int swept)
443 {
444 	SrvFile *sf;
445 
446 	release();
447 	qlock(&dev.l);
448 	sf = H2D(Channel*, h)->aux;
449 	sf->read = H;
450 	srvfree(sf, SRDCLOSE);
451 	qunlock(&dev.l);
452 	acquire();
453 
454 	freechan(h, swept);
455 }
456 
457 static void
458 freewrchan(Heap *h, int swept)
459 {
460 	SrvFile *sf;
461 
462 	release();
463 	qlock(&dev.l);
464 	sf = H2D(Channel*, h)->aux;
465 	sf->write = H;
466 	srvfree(sf, SWRCLOSE);
467 	qunlock(&dev.l);
468 	acquire();
469 
470 	freechan(h, swept);
471 }
472 
473 static void
474 srvclunk(Chan *c, int remove)
475 {
476 	int opens, noperm;
477 	SrvFile *sf;
478 
479 	sf = c->aux;
480 	qlock(&dev.l);
481 	if(c->qid.type & QTDIR){
482 		srvputdir(sf);
483 		qunlock(&dev.l);
484 		if(remove)
485 			error(Eperm);
486 		return;
487 	}
488 	opens = 0;
489 	if(c->flag & COPEN){
490 		opens = sf->opens--;
491 		if(sf->read != H || sf->write != H)
492 			srvunblock(sf, c->fid);
493 	}
494 
495 	sf->ref--;
496 	if(opens == 1){
497 		if(sf->flags & SORCLOSE)
498 			remove = 1;
499 	}
500 
501 	noperm = 0;
502 	if(remove && strcmp(sf->dir->user, up->env->user) != 0){
503 		noperm = 1;
504 		remove = 0;
505 	}
506 	if(remove)
507 		srvdelete(sf);
508 	srvchkref(sf);
509 	qunlock(&dev.l);
510 
511 	if(noperm)
512 		error(Eperm);
513 }
514 
515 static void
516 srvclose(Chan *c)
517 {
518 	srvclunk(c, 0);
519 }
520 
521 static void
522 srvremove(Chan *c)
523 {
524 	srvclunk(c, 1);
525 }
526 
527 static void
528 addwaiting(SrvFile *sp, Pending *w)
529 {
530 	Pending *sw;
531 
532 	sw = &sp->waitlist;
533 	w->next = sw;
534 	w->prev = sw->prev;
535 	sw->prev->next = w;
536 	sw->prev = w;
537 }
538 
539 static void
540 delwaiting(Pending *w)
541 {
542 	w->next->prev = w->prev;
543 	w->prev->next = w->next;
544 }
545 
546 static long
547 srvread(Chan *c, void *va, long count, vlong offset)
548 {
549 	int l;
550 	Heap * volatile h;
551 	Array *a;
552 	SrvFile *sp;
553 	Channel *rc;
554 	Channel *rd;
555 	Pending wait;
556 	Sys_Rread * volatile r;
557 	Sys_FileIO_read req;
558 
559 	if(c->qid.type & QTDIR){
560 		qlock(&dev.l);
561 		if(waserror()){
562 			qunlock(&dev.l);
563 			nexterror();
564 		}
565 		l = devdirread(c, va, count, 0, 0, srvgen);
566 		poperror();
567 		qunlock(&dev.l);
568 		return l;
569 	}
570 
571 	sp = c->aux;
572 
573 	acquire();
574 	if(waserror()){
575 		release();
576 		nexterror();
577 	}
578 
579 	rd = sp->read;
580 	if(rd == H)
581 		error(Ehungup);
582 
583 	rc = cnewc(dev.Rread, movtmp, 1);
584 	ptradd(D2H(rc));
585 	if(waserror()){
586 		ptrdel(D2H(rc));
587 		destroy(rc);
588 		nexterror();
589 	}
590 
591 	req.t0 = offset;
592 	req.t1 = count;
593 	req.t2 = c->fid;
594 	req.t3 = rc;
595 	csend(rd, &req);
596 
597 	h = heap(dev.Rread);
598 	r = H2D(Sys_Rread *, h);
599 	ptradd(h);
600 	if(waserror()){
601 		ptrdel(h);
602 		destroy(r);
603 		nexterror();
604 	}
605 
606 	wait.fid = c->fid;
607 	wait.rc = rc;
608 	wait.wc = nil;
609 	addwaiting(sp, &wait);
610 	if(waserror()){
611 		delwaiting(&wait);
612 		nexterror();
613 	}
614 	crecv(rc, r);
615 	poperror();
616 	delwaiting(&wait);
617 
618 	if(r->t1 != H)
619 		error(string2c(r->t1));
620 
621 	a = r->t0;
622 	l = 0;
623 	if(a != H){
624 		l = a->len;
625 		if(l > count)
626 			l = count;
627 		memmove(va, a->data, l);
628 	}
629 
630 	poperror();
631 	ptrdel(h);
632 	destroy(r);
633 
634 	poperror();
635 	ptrdel(D2H(rc));
636 	destroy(rc);
637 
638 	poperror();
639 	release();
640 
641 	return l;
642 }
643 
644 static long
645 srvwrite(Chan *c, void *va, long count, vlong offset)
646 {
647 	long l;
648 	Heap * volatile h;
649 	SrvFile *sp;
650 	Channel *wc;
651 	Channel *wr;
652 	Pending wait;
653 	Sys_Rwrite * volatile w;
654 	Sys_FileIO_write req;
655 
656 	if(c->qid.type & QTDIR)
657 		error(Eperm);
658 
659 	acquire();
660 	if(waserror()){
661 		release();
662 		nexterror();
663 	}
664 
665 	sp = c->aux;
666 	wr = sp->write;
667 	if(wr == H)
668 		error(Ehungup);
669 
670 	wc = cnewc(dev.Rwrite, movtmp, 1);
671 	ptradd(D2H(wc));
672 	if(waserror()){
673 		ptrdel(D2H(wc));
674 		destroy(wc);
675 		nexterror();
676 	}
677 
678 	req.t0 = offset;
679 	req.t1 = mem2array(va, count);
680 	req.t2 = c->fid;
681 	req.t3 = wc;
682 
683 	ptradd(D2H(req.t1));
684 
685 	if(waserror()){
686 		ptrdel(D2H(req.t1));
687 		destroy(req.t1);
688 		nexterror();
689 	}
690 
691 	csend(wr, &req);
692 
693 	poperror();
694 	ptrdel(D2H(req.t1));
695 	destroy(req.t1);
696 
697 	h = heap(dev.Rwrite);
698 	w = H2D(Sys_Rwrite *, h);
699 	ptradd(h);
700 
701 	if(waserror()){
702 		ptrdel(h);
703 		destroy(w);
704 		nexterror();
705 	}
706 
707 	wait.fid = c->fid;
708 	wait.rc = nil;
709 	wait.wc = wc;
710 	addwaiting(sp, &wait);
711 	if(waserror()){
712 		delwaiting(&wait);
713 		nexterror();
714 	}
715 	crecv(wc, w);
716 	poperror();
717 	delwaiting(&wait);
718 
719 	if(w->t1 != H)
720 		error(string2c(w->t1));
721 	poperror();
722 	ptrdel(h);
723 	l = w->t0;
724 	destroy(w);
725 
726 	poperror();
727 	ptrdel(D2H(wc));
728 	destroy(wc);
729 
730 	poperror();
731 	release();
732 	if(l < 0)
733 		l = 0;
734 	return l;
735 }
736 
737 static void
738 srvretype(Channel *c, SrvFile *f, Type *t)
739 {
740 	Heap *h;
741 
742 	h = D2H(c);
743 	freetype(h->t);
744 	h->t = t;
745 	t->ref++;
746 	c->aux = f;
747 }
748 
749 int
750 srvf2c(char *dir, char *file, Sys_FileIO *io)
751 {
752 	SrvFile *s, *f;
753 	volatile struct { Chan *c; } c;
754 
755 	c.c = nil;
756 	if(waserror()){
757 		cclose(c.c);
758 		return -1;
759 	}
760 
761 	if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
762 		error(Efilename);
763 
764 	c.c = namec(dir, Aaccess, 0, 0);
765 	if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's')
766 		error("directory not a srv device");
767 
768 	s = c.c->aux;
769 
770 	qlock(&dev.l);
771 	if(waserror()){
772 		qunlock(&dev.l);
773 		nexterror();
774 	}
775 	for(f = s->entry; f != nil; f = f->entry){
776 		if(strcmp(f->name, file) == 0)
777 			error(Eexist);
778 	}
779 
780 	f = malloc(sizeof(SrvFile));
781 	if(f == nil)
782 		error(Enomem);
783 
784 	srvretype(io->read, f, Trdchan);
785 	srvretype(io->write, f, Twrchan);
786 	f->read = io->read;
787 	f->write = io->write;
788 
789 	f->waitlist.next = &f->waitlist;
790 	f->waitlist.prev = &f->waitlist;
791 
792 	kstrdup(&f->name, file);
793 	kstrdup(&f->user, up->env->user);
794 	f->perm = 0666 & (~0666 | (s->perm & 0666));
795 	f->length = 0;
796 	f->ref = 2;
797 	mkqid(&f->qid, dev.pathgen++, 0, QTFILE);
798 
799 	f->entry = s->entry;
800 	s->entry = f;
801 	s->ref++;
802 	f->dir = s;
803 	poperror();
804 	qunlock(&dev.l);
805 
806 	cclose(c.c);
807 	poperror();
808 
809 	return 0;
810 }
811 
812 Dev srvdevtab = {
813 	's',
814 	"srv",
815 
816 	srvinit,
817 	srvattach,
818 	srvwalk,
819 	srvstat,
820 	srvopen,
821 	devcreate,
822 	srvclose,
823 	srvread,
824 	devbread,
825 	srvwrite,
826 	devbwrite,
827 	srvremove,
828 	srvwstat
829 };
830