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