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