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