xref: /plan9/sys/src/cmd/rio/fsys.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include "dat.h"
11 #include "fns.h"
12 
13 char Eperm[] = "permission denied";
14 char Eexist[] = "file does not exist";
15 char Enotdir[] = "not a directory";
16 char	Ebadfcall[] = "bad fcall type";
17 char	Eoffset[] = "illegal offset";
18 
19 int	messagesize = 8192+IOHDRSZ;	/* good start */
20 
21 enum{
22 	DEBUG = 0
23 };
24 
25 Dirtab dirtab[]=
26 {
27 	{ ".",			QTDIR,	Qdir,			0500|DMDIR },
28 	{ "cons",		QTFILE,	Qcons,		0600 },
29 	{ "cursor",		QTFILE,	Qcursor,		0600 },
30 	{ "consctl",	QTFILE,	Qconsctl,		0200 },
31 	{ "winid",		QTFILE,	Qwinid,		0400 },
32 	{ "winname",	QTFILE,	Qwinname,	0400 },
33 	{ "kbdin",		QTFILE,	Qkbdin,		0200 },
34 	{ "label",		QTFILE,	Qlabel,		0600 },
35 	{ "mouse",	QTFILE,	Qmouse,		0600 },
36 	{ "screen",		QTFILE,	Qscreen,		0400 },
37 	{ "snarf",		QTFILE,	Qsnarf,		0600 },
38 	{ "text",		QTFILE,	Qtext,		0400 },
39 	{ "wdir",		QTFILE,	Qwdir,		0600 },
40 	{ "wctl",		QTFILE,	Qwctl,		0600 },
41 	{ "window",	QTFILE,	Qwindow,		0400 },
42 	{ "wsys",		QTDIR,	Qwsys,		0500|DMDIR },
43 	{ nil, }
44 };
45 
46 static uint		getclock(void);
47 static void		filsysproc(void*);
48 static Fid*		newfid(Filsys*, int);
49 static int		dostat(Filsys*, int, Dirtab*, uchar*, int, uint);
50 
51 int	clockfd;
52 int	firstmessage = 1;
53 
54 char	srvpipe[64];
55 char	srvwctl[64];
56 
57 static	Xfid*	filsysflush(Filsys*, Xfid*, Fid*);
58 static	Xfid*	filsysversion(Filsys*, Xfid*, Fid*);
59 static	Xfid*	filsysauth(Filsys*, Xfid*, Fid*);
60 static	Xfid*	filsysnop(Filsys*, Xfid*, Fid*);
61 static	Xfid*	filsysattach(Filsys*, Xfid*, Fid*);
62 static	Xfid*	filsyswalk(Filsys*, Xfid*, Fid*);
63 static	Xfid*	filsysopen(Filsys*, Xfid*, Fid*);
64 static	Xfid*	filsyscreate(Filsys*, Xfid*, Fid*);
65 static	Xfid*	filsysread(Filsys*, Xfid*, Fid*);
66 static	Xfid*	filsyswrite(Filsys*, Xfid*, Fid*);
67 static	Xfid*	filsysclunk(Filsys*, Xfid*, Fid*);
68 static	Xfid*	filsysremove(Filsys*, Xfid*, Fid*);
69 static	Xfid*	filsysstat(Filsys*, Xfid*, Fid*);
70 static	Xfid*	filsyswstat(Filsys*, Xfid*, Fid*);
71 
72 Xfid* 	(*fcall[Tmax])(Filsys*, Xfid*, Fid*) =
73 {
74 	[Tflush]	= filsysflush,
75 	[Tversion]	= filsysversion,
76 	[Tauth]	= filsysauth,
77 	[Tattach]	= filsysattach,
78 	[Twalk]	= filsyswalk,
79 	[Topen]	= filsysopen,
80 	[Tcreate]	= filsyscreate,
81 	[Tread]	= filsysread,
82 	[Twrite]	= filsyswrite,
83 	[Tclunk]	= filsysclunk,
84 	[Tremove]= filsysremove,
85 	[Tstat]	= filsysstat,
86 	[Twstat]	= filsyswstat,
87 };
88 
89 void
post(char * name,char * envname,int srvfd)90 post(char *name, char *envname, int srvfd)
91 {
92 	int fd;
93 	char buf[32];
94 
95 	fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600);
96 	if(fd < 0)
97 		error(name);
98 	sprint(buf, "%d",srvfd);
99 	if(write(fd, buf, strlen(buf)) != strlen(buf))
100 		error("srv write");
101 	putenv(envname, name);
102 }
103 
104 /*
105  * Build pipe with OCEXEC set on second fd.
106  * Can't put it on both because we want to post one in /srv.
107  */
108 int
cexecpipe(int * p0,int * p1)109 cexecpipe(int *p0, int *p1)
110 {
111 	/* pipe the hard way to get close on exec */
112 	if(bind("#|", "/mnt/temp", MREPL) < 0)
113 		return -1;
114 	*p0 = open("/mnt/temp/data", ORDWR);
115 	*p1 = open("/mnt/temp/data1", ORDWR|OCEXEC);
116 	unmount(nil, "/mnt/temp");
117 	if(*p0<0 || *p1<0)
118 		return -1;
119 	return 0;
120 }
121 
122 Filsys*
filsysinit(Channel * cxfidalloc)123 filsysinit(Channel *cxfidalloc)
124 {
125 	int n, fd, pid, p0;
126 	Filsys *fs;
127 	Channel *c;
128 	char buf[128];
129 
130 	fs = emalloc(sizeof(Filsys));
131 	if(cexecpipe(&fs->cfd, &fs->sfd) < 0)
132 		goto Rescue;
133 	fmtinstall('F', fcallfmt);
134 	clockfd = open("/dev/time", OREAD|OCEXEC);
135 	fd = open("/dev/user", OREAD);
136 	strcpy(buf, "Jean-Paul_Belmondo");
137 	if(fd >= 0){
138 		n = read(fd, buf, sizeof buf-1);
139 		if(n > 0)
140 			buf[n] = 0;
141 		close(fd);
142 	}
143 	fs->user = estrdup(buf);
144 	fs->cxfidalloc = cxfidalloc;
145 	pid = getpid();
146 
147 	/*
148 	 * Create and post wctl pipe
149 	 */
150 	if(cexecpipe(&p0, &wctlfd) < 0)
151 		goto Rescue;
152 	sprint(srvwctl, "/srv/riowctl.%s.%d", fs->user, pid);
153 	post(srvwctl, "wctl", p0);
154 	close(p0);
155 
156 	/*
157 	 * Start server processes
158 	 */
159 	c = chancreate(sizeof(char*), 0);
160 	if(c == nil)
161 		error("wctl channel");
162 	proccreate(wctlproc, c, 4096);
163 	threadcreate(wctlthread, c, 4096);
164 	proccreate(filsysproc, fs, 10000);
165 
166 	/*
167 	 * Post srv pipe
168 	 */
169 	sprint(srvpipe, "/srv/rio.%s.%d", fs->user, pid);
170 	post(srvpipe, "wsys", fs->cfd);
171 
172 	return fs;
173 
174 Rescue:
175 	free(fs);
176 	return nil;
177 }
178 
179 static
180 void
filsysproc(void * arg)181 filsysproc(void *arg)
182 {
183 	int n;
184 	Xfid *x;
185 	Fid *f;
186 	Fcall t;
187 	uchar *buf;
188 	Filsys *fs;
189 
190 	threadsetname("FILSYSPROC");
191 	fs = arg;
192 	fs->pid = getpid();
193 	x = nil;
194 	for(;;){
195 		buf = emalloc(messagesize+UTFmax);	/* UTFmax for appending partial rune in xfidwrite */
196 		n = read9pmsg(fs->sfd, buf, messagesize);
197 		if(n <= 0){
198 			yield();	/* if threadexitsall'ing, will not return */
199 			fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n);
200 			errorshouldabort = 0;
201 			error("eof or i/o error on server channel");
202 		}
203 		if(x == nil){
204 			send(fs->cxfidalloc, nil);
205 			recv(fs->cxfidalloc, &x);
206 			x->fs = fs;
207 		}
208 		x->buf = buf;
209 		if(convM2S(buf, n, x) != n)
210 			error("convert error in convM2S");
211 		if(DEBUG)
212 			fprint(2, "rio:<-%F\n", &x->Fcall);
213 		if(fcall[x->type] == nil)
214 			x = filsysrespond(fs, x, &t, Ebadfcall);
215 		else{
216 			if(x->type==Tversion || x->type==Tauth)
217 				f = nil;
218 			else
219 				f = newfid(fs, x->fid);
220 			x->f = f;
221 			x  = (*fcall[x->type])(fs, x, f);
222 		}
223 		firstmessage = 0;
224 	}
225 }
226 
227 /*
228  * Called only from a different FD group
229  */
230 int
filsysmount(Filsys * fs,int id)231 filsysmount(Filsys *fs, int id)
232 {
233 	char buf[32];
234 
235 	close(fs->sfd);	/* close server end so mount won't hang if exiting */
236 	sprint(buf, "%d", id);
237 	if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) < 0){
238 		fprint(2, "mount failed: %r\n");
239 		return -1;
240 	}
241 	if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){
242 		fprint(2, "bind failed: %r\n");
243 		return -1;
244 	}
245 	return 0;
246 }
247 
248 Xfid*
filsysrespond(Filsys * fs,Xfid * x,Fcall * t,char * err)249 filsysrespond(Filsys *fs, Xfid *x, Fcall *t, char *err)
250 {
251 	int n;
252 
253 	if(err){
254 		t->type = Rerror;
255 		t->ename = err;
256 	}else
257 		t->type = x->type+1;
258 	t->fid = x->fid;
259 	t->tag = x->tag;
260 	if(x->buf == nil)
261 		x->buf = malloc(messagesize);
262 	n = convS2M(t, x->buf, messagesize);
263 	if(n <= 0)
264 		error("convert error in convS2M");
265 	if(write(fs->sfd, x->buf, n) != n)
266 		error("write error in respond");
267 	if(DEBUG)
268 		fprint(2, "rio:->%F\n", t);
269 	free(x->buf);
270 	x->buf = nil;
271 	return x;
272 }
273 
274 void
filsyscancel(Xfid * x)275 filsyscancel(Xfid *x)
276 {
277 	if(x->buf){
278 		free(x->buf);
279 		x->buf = nil;
280 	}
281 }
282 
283 static
284 Xfid*
filsysversion(Filsys * fs,Xfid * x,Fid *)285 filsysversion(Filsys *fs, Xfid *x, Fid*)
286 {
287 	Fcall t;
288 
289 	if(!firstmessage)
290 		return filsysrespond(x->fs, x, &t, "version request not first message");
291 	if(x->msize < 256)
292 		return filsysrespond(x->fs, x, &t, "version: message size too small");
293 	messagesize = x->msize;
294 	t.msize = messagesize;
295 	if(strncmp(x->version, "9P2000", 6) != 0)
296 		return filsysrespond(x->fs, x, &t, "unrecognized 9P version");
297 	t.version = "9P2000";
298 	return filsysrespond(fs, x, &t, nil);
299 }
300 
301 static
302 Xfid*
filsysauth(Filsys * fs,Xfid * x,Fid *)303 filsysauth(Filsys *fs, Xfid *x, Fid*)
304 {
305 	Fcall t;
306 
307 		return filsysrespond(fs, x, &t, "rio: authentication not required");
308 }
309 
310 static
311 Xfid*
filsysflush(Filsys *,Xfid * x,Fid *)312 filsysflush(Filsys*, Xfid *x, Fid*)
313 {
314 	sendp(x->c, xfidflush);
315 	return nil;
316 }
317 
318 static
319 Xfid*
filsysattach(Filsys *,Xfid * x,Fid * f)320 filsysattach(Filsys *, Xfid *x, Fid *f)
321 {
322 	Fcall t;
323 
324 	if(strcmp(x->uname, x->fs->user) != 0)
325 		return filsysrespond(x->fs, x, &t, Eperm);
326 	f->busy = TRUE;
327 	f->open = FALSE;
328 	f->qid.path = Qdir;
329 	f->qid.type = QTDIR;
330 	f->qid.vers = 0;
331 	f->dir = dirtab;
332 	f->nrpart = 0;
333 	sendp(x->c, xfidattach);
334 	return nil;
335 }
336 
337 static
338 int
numeric(char * s)339 numeric(char *s)
340 {
341 	for(; *s!='\0'; s++)
342 		if(*s<'0' || '9'<*s)
343 			return 0;
344 	return 1;
345 }
346 
347 static
348 Xfid*
filsyswalk(Filsys * fs,Xfid * x,Fid * f)349 filsyswalk(Filsys *fs, Xfid *x, Fid *f)
350 {
351 	Fcall t;
352 	Fid *nf;
353 	int i, id;
354 	uchar type;
355 	ulong path;
356 	Dirtab *d, *dir;
357 	Window *w;
358 	char *err;
359 	Qid qid;
360 
361 	if(f->open)
362 		return filsysrespond(fs, x, &t, "walk of open file");
363 	nf = nil;
364 	if(x->fid  != x->newfid){
365 		/* BUG: check exists */
366 		nf = newfid(fs, x->newfid);
367 		if(nf->busy)
368 			return filsysrespond(fs, x, &t, "clone to busy fid");
369 		nf->busy = TRUE;
370 		nf->open = FALSE;
371 		nf->dir = f->dir;
372 		nf->qid = f->qid;
373 		nf->w = f->w;
374 		incref(f->w);
375 		nf->nrpart = 0;	/* not open, so must be zero */
376 		f = nf;	/* walk f */
377 	}
378 
379 	t.nwqid = 0;
380 	err = nil;
381 
382 	/* update f->qid, f->dir only if walk completes */
383 	qid = f->qid;
384 	dir = f->dir;
385 
386 	if(x->nwname > 0){
387 		for(i=0; i<x->nwname; i++){
388 			if((qid.type & QTDIR) == 0){
389 				err = Enotdir;
390 				break;
391 			}
392 			if(strcmp(x->wname[i], "..") == 0){
393 				type = QTDIR;
394 				path = Qdir;
395 				dir = dirtab;
396 				if(FILE(qid) == Qwsysdir)
397 					path = Qwsys;
398 				id = 0;
399     Accept:
400 				if(i == MAXWELEM){
401 					err = "name too long";
402 					break;
403 				}
404 				qid.type = type;
405 				qid.vers = 0;
406 				qid.path = QID(id, path);
407 				t.wqid[t.nwqid++] = qid;
408 				continue;
409 			}
410 
411 			if(qid.path == Qwsys){
412 				/* is it a numeric name? */
413 				if(!numeric(x->wname[i]))
414 					break;
415 				/* yes: it's a directory */
416 				id = atoi(x->wname[i]);
417 				qlock(&all);
418 				w = wlookid(id);
419 				if(w == nil){
420 					qunlock(&all);
421 					break;
422 				}
423 				path = Qwsysdir;
424 				type = QTDIR;
425 				qunlock(&all);
426 				incref(w);
427 				sendp(winclosechan, f->w);
428 				f->w = w;
429 				dir = dirtab;
430 				goto Accept;
431 			}
432 
433 			if(snarffd>=0 && strcmp(x->wname[i], "snarf")==0)
434 				break;	/* don't serve /dev/snarf if it's provided in the environment */
435 			id = WIN(f->qid);
436 			d = dirtab;
437 			d++;	/* skip '.' */
438 			for(; d->name; d++)
439 				if(strcmp(x->wname[i], d->name) == 0){
440 					path = d->qid;
441 					type = d->type;
442 					dir = d;
443 					goto Accept;
444 				}
445 
446 			break;	/* file not found */
447 		}
448 
449 		if(i==0 && err==nil)
450 			err = Eexist;
451 	}
452 
453 	if(err!=nil || t.nwqid<x->nwname){
454 		if(nf){
455 			if(nf->w)
456 				sendp(winclosechan, nf->w);
457 			nf->open = FALSE;
458 			nf->busy = FALSE;
459 		}
460 	}else if(t.nwqid == x->nwname){
461 		f->dir = dir;
462 		f->qid = qid;
463 	}
464 
465 	return filsysrespond(fs, x, &t, err);
466 }
467 
468 static
469 Xfid*
filsysopen(Filsys * fs,Xfid * x,Fid * f)470 filsysopen(Filsys *fs, Xfid *x, Fid *f)
471 {
472 	Fcall t;
473 	int m;
474 
475 	/* can't truncate anything, so just disregard */
476 	x->mode &= ~(OTRUNC|OCEXEC);
477 	/* can't execute or remove anything */
478 	if(x->mode==OEXEC || (x->mode&ORCLOSE))
479 		goto Deny;
480 	switch(x->mode){
481 	default:
482 		goto Deny;
483 	case OREAD:
484 		m = 0400;
485 		break;
486 	case OWRITE:
487 		m = 0200;
488 		break;
489 	case ORDWR:
490 		m = 0600;
491 		break;
492 	}
493 	if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
494 		goto Deny;
495 
496 	sendp(x->c, xfidopen);
497 	return nil;
498 
499     Deny:
500 	return filsysrespond(fs, x, &t, Eperm);
501 }
502 
503 static
504 Xfid*
filsyscreate(Filsys * fs,Xfid * x,Fid *)505 filsyscreate(Filsys *fs, Xfid *x, Fid*)
506 {
507 	Fcall t;
508 
509 	return filsysrespond(fs, x, &t, Eperm);
510 }
511 
512 static
513 int
idcmp(void * a,void * b)514 idcmp(void *a, void *b)
515 {
516 	return *(int*)a - *(int*)b;
517 }
518 
519 static
520 Xfid*
filsysread(Filsys * fs,Xfid * x,Fid * f)521 filsysread(Filsys *fs, Xfid *x, Fid *f)
522 {
523 	Fcall t;
524 	uchar *b;
525 	int i, n, o, e, len, j, k, *ids;
526 	Dirtab *d, dt;
527 	uint clock;
528 	char buf[16];
529 
530 	if((f->qid.type & QTDIR) == 0){
531 		sendp(x->c, xfidread);
532 		return nil;
533 	}
534 	o = x->offset;
535 	e = x->offset+x->count;
536 	clock = getclock();
537 	b = malloc(messagesize-IOHDRSZ);	/* avoid memset of emalloc */
538 	if(b == nil)
539 		return filsysrespond(fs, x, &t, "out of memory");
540 	n = 0;
541 	switch(FILE(f->qid)){
542 	case Qdir:
543 	case Qwsysdir:
544 		d = dirtab;
545 		d++;	/* first entry is '.' */
546 		for(i=0; d->name!=nil && i<e; i+=len){
547 			len = dostat(fs, WIN(x->f->qid), d, b+n, x->count-n, clock);
548 			if(len <= BIT16SZ)
549 				break;
550 			if(i >= o)
551 				n += len;
552 			d++;
553 		}
554 		break;
555 	case Qwsys:
556 		qlock(&all);
557 		ids = emalloc(nwindow*sizeof(int));
558 		for(j=0; j<nwindow; j++)
559 			ids[j] = window[j]->id;
560 		qunlock(&all);
561 		qsort(ids, nwindow, sizeof ids[0], idcmp);
562 		dt.name = buf;
563 		for(i=0, j=0; j<nwindow && i<e; i+=len){
564 			k = ids[j];
565 			sprint(dt.name, "%d", k);
566 			dt.qid = QID(k, Qdir);
567 			dt.type = QTDIR;
568 			dt.perm = DMDIR|0700;
569 			len = dostat(fs, k, &dt, b+n, x->count-n, clock);
570 			if(len == 0)
571 				break;
572 			if(i >= o)
573 				n += len;
574 			j++;
575 		}
576 		free(ids);
577 		break;
578 	}
579 	t.data = (char*)b;
580 	t.count = n;
581 	filsysrespond(fs, x, &t, nil);
582 	free(b);
583 	return x;
584 }
585 
586 static
587 Xfid*
filsyswrite(Filsys *,Xfid * x,Fid *)588 filsyswrite(Filsys*, Xfid *x, Fid*)
589 {
590 	sendp(x->c, xfidwrite);
591 	return nil;
592 }
593 
594 static
595 Xfid*
filsysclunk(Filsys * fs,Xfid * x,Fid * f)596 filsysclunk(Filsys *fs, Xfid *x, Fid *f)
597 {
598 	Fcall t;
599 
600 	if(f->open){
601 		f->busy = FALSE;
602 		f->open = FALSE;
603 		sendp(x->c, xfidclose);
604 		return nil;
605 	}
606 	if(f->w)
607 		sendp(winclosechan, f->w);
608 	f->busy = FALSE;
609 	f->open = FALSE;
610 	return filsysrespond(fs, x, &t, nil);
611 }
612 
613 static
614 Xfid*
filsysremove(Filsys * fs,Xfid * x,Fid *)615 filsysremove(Filsys *fs, Xfid *x, Fid*)
616 {
617 	Fcall t;
618 
619 	return filsysrespond(fs, x, &t, Eperm);
620 }
621 
622 static
623 Xfid*
filsysstat(Filsys * fs,Xfid * x,Fid * f)624 filsysstat(Filsys *fs, Xfid *x, Fid *f)
625 {
626 	Fcall t;
627 
628 	t.stat = emalloc(messagesize-IOHDRSZ);
629 	t.nstat = dostat(fs, WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
630 	x = filsysrespond(fs, x, &t, nil);
631 	free(t.stat);
632 	return x;
633 }
634 
635 static
636 Xfid*
filsyswstat(Filsys * fs,Xfid * x,Fid *)637 filsyswstat(Filsys *fs, Xfid *x, Fid*)
638 {
639 	Fcall t;
640 
641 	return filsysrespond(fs, x, &t, Eperm);
642 }
643 
644 static
645 Fid*
newfid(Filsys * fs,int fid)646 newfid(Filsys *fs, int fid)
647 {
648 	Fid *f, *ff, **fh;
649 
650 	ff = nil;
651 	fh = &fs->fids[fid&(Nhash-1)];
652 	for(f=*fh; f; f=f->next)
653 		if(f->fid == fid)
654 			return f;
655 		else if(ff==nil && f->busy==FALSE)
656 			ff = f;
657 	if(ff){
658 		ff->fid = fid;
659 		return ff;
660 	}
661 	f = emalloc(sizeof *f);
662 	f->fid = fid;
663 	f->next = *fh;
664 	*fh = f;
665 	return f;
666 }
667 
668 static
669 uint
getclock(void)670 getclock(void)
671 {
672 	char buf[32];
673 
674 	seek(clockfd, 0, 0);
675 	read(clockfd, buf, sizeof buf);
676 	return atoi(buf);
677 }
678 
679 static
680 int
dostat(Filsys * fs,int id,Dirtab * dir,uchar * buf,int nbuf,uint clock)681 dostat(Filsys *fs, int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
682 {
683 	Dir d;
684 
685 	d.qid.path = QID(id, dir->qid);
686 	if(dir->qid == Qsnarf)
687 		d.qid.vers = snarfversion;
688 	else
689 		d.qid.vers = 0;
690 	d.qid.type = dir->type;
691 	d.mode = dir->perm;
692 	d.length = 0;	/* would be nice to do better */
693 	d.name = dir->name;
694 	d.uid = fs->user;
695 	d.gid = fs->user;
696 	d.muid = fs->user;
697 	d.atime = clock;
698 	d.mtime = clock;
699 	return convD2M(&d, buf, nbuf);
700 }
701