xref: /inferno-os/os/port/devsrv.c (revision ddf161d27871e47d85fd56e8403c715af8ce43c8)
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 	h = heap(dev.Rread);
601 	r = H2D(Sys_Rread *, h);
602 	ptradd(h);
603 	if(waserror()){
604 		ptrdel(h);
605 		destroy(r);
606 		nexterror();
607 	}
608 
609 	wait.fid = c->fid;
610 	wait.rc = rc;
611 	wait.wc = nil;
612 	addwaiting(sp, &wait);
613 	if(waserror()){
614 		delwaiting(&wait);
615 		nexterror();
616 	}
617 	crecv(rc, r);
618 	poperror();
619 	delwaiting(&wait);
620 
621 	if(r->t1 != H)
622 		error(string2c(r->t1));
623 
624 	a = r->t0;
625 	l = 0;
626 	if(a != H){
627 		l = a->len;
628 		if(l > count)
629 			l = count;
630 		memmove(va, a->data, l);
631 	}
632 
633 	poperror();
634 	ptrdel(h);
635 	destroy(r);
636 
637 	poperror();
638 	ptrdel(D2H(rc));
639 	destroy(rc);
640 
641 	poperror();
642 	release();
643 
644 	return l;
645 }
646 
647 static long
648 srvwrite(Chan *c, void *va, long count, vlong offset)
649 {
650 	long l;
651 	Heap * volatile h;
652 	SrvFile *sp;
653 	Channel *wc;
654 	Channel *wr;
655 	Pending wait;
656 	Sys_Rwrite * volatile w;
657 	Sys_FileIO_write req;
658 
659 	if(c->qid.type & QTDIR)
660 		error(Eperm);
661 
662 	acquire();
663 	if(waserror()){
664 		release();
665 		nexterror();
666 	}
667 
668 	sp = c->aux;
669 	wr = sp->write;
670 	if(wr == H)
671 		error(Ehungup);
672 
673 	wc = cnewc(dev.Rwrite, movtmp, 1);
674 	ptradd(D2H(wc));
675 	if(waserror()){
676 		ptrdel(D2H(wc));
677 		destroy(wc);
678 		nexterror();
679 	}
680 
681 	req.t0 = offset;
682 	req.t1 = mem2array(va, count);
683 	req.t2 = c->fid;
684 	req.t3 = wc;
685 
686 	ptradd(D2H(req.t1));
687 
688 	if(waserror()){
689 		ptrdel(D2H(req.t1));
690 		destroy(req.t1);
691 		nexterror();
692 	}
693 
694 	csend(wr, &req);
695 
696 	poperror();
697 	ptrdel(D2H(req.t1));
698 	destroy(req.t1);
699 
700 	h = heap(dev.Rwrite);
701 	w = H2D(Sys_Rwrite *, h);
702 	ptradd(h);
703 
704 	if(waserror()){
705 		ptrdel(h);
706 		destroy(w);
707 		nexterror();
708 	}
709 
710 	wait.fid = c->fid;
711 	wait.rc = nil;
712 	wait.wc = wc;
713 	addwaiting(sp, &wait);
714 	if(waserror()){
715 		delwaiting(&wait);
716 		nexterror();
717 	}
718 	crecv(wc, w);
719 	poperror();
720 	delwaiting(&wait);
721 
722 	if(w->t1 != H)
723 		error(string2c(w->t1));
724 	poperror();
725 	ptrdel(h);
726 	l = w->t0;
727 	destroy(w);
728 
729 	poperror();
730 	ptrdel(D2H(wc));
731 	destroy(wc);
732 
733 	poperror();
734 	release();
735 	if(l < 0)
736 		l = 0;
737 	return l;
738 }
739 
740 static void
741 srvretype(Channel *c, SrvFile *f, Type *t)
742 {
743 	Heap *h;
744 
745 	h = D2H(c);
746 	freetype(h->t);
747 	h->t = t;
748 	t->ref++;
749 	c->aux = f;
750 }
751 
752 int
753 srvf2c(char *dir, char *file, Sys_FileIO *io)
754 {
755 	SrvFile *s, *f;
756 	volatile struct { Chan *c; } c;
757 
758 	c.c = nil;
759 	if(waserror()){
760 		cclose(c.c);
761 		return -1;
762 	}
763 
764 	if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
765 		error(Efilename);
766 
767 	c.c = namec(dir, Aaccess, 0, 0);
768 	if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's')
769 		error("directory not a srv device");
770 
771 	s = c.c->aux;
772 
773 	qlock(&dev.l);
774 	if(waserror()){
775 		qunlock(&dev.l);
776 		nexterror();
777 	}
778 	for(f = s->entry; f != nil; f = f->entry){
779 		if(strcmp(f->name, file) == 0)
780 			error(Eexist);
781 	}
782 
783 	f = malloc(sizeof(SrvFile));
784 	if(f == nil)
785 		error(Enomem);
786 
787 	srvretype(io->read, f, Trdchan);
788 	srvretype(io->write, f, Twrchan);
789 	f->read = io->read;
790 	f->write = io->write;
791 
792 	f->waitlist.next = &f->waitlist;
793 	f->waitlist.prev = &f->waitlist;
794 
795 	kstrdup(&f->name, file);
796 	kstrdup(&f->user, up->env->user);
797 	f->perm = 0666 & (~0666 | (s->perm & 0666));
798 	f->length = 0;
799 	f->ref = 2;
800 	mkqid(&f->qid, dev.pathgen++, 0, QTFILE);
801 
802 	f->entry = s->entry;
803 	s->entry = f;
804 	s->ref++;
805 	f->dir = s;
806 	poperror();
807 	qunlock(&dev.l);
808 
809 	cclose(c.c);
810 	poperror();
811 
812 	return 0;
813 }
814 
815 Dev srvdevtab = {
816 	's',
817 	"srv",
818 
819 	devreset,
820 	srvinit,
821 	devshutdown,
822 	srvattach,
823 	srvwalk,
824 	srvstat,
825 	srvopen,
826 	devcreate,
827 	srvclose,
828 	srvread,
829 	devbread,
830 	srvwrite,
831 	devbwrite,
832 	srvremove,
833 	srvwstat
834 };
835