xref: /inferno-os/os/port/devsrv.c (revision 6cde411a8ffd477459336cedf48034e46f56f913)
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