xref: /plan9-contrib/sys/src/cmd/vac/vacfs.c (revision 1936bb650459bace06c38a45b60888b47e5cd459)
1 #include "stdinc.h"
2 #include <fcall.h>
3 #include "vac.h"
4 
5 #define convM2Su(a, b, c, d) convM2S(a, b, c)
6 #define convS2Mu(a, b, c, d) convS2M(a, b, c)
7 #define convM2Du(a, b, c, d) convM2D(a, b, c)
8 #define convD2Mu(a, b, c, d) convD2M(a, b, c)
9 
10 typedef struct Fid Fid;
11 
12 enum
13 {
14 	Stacksize = 320 * 1024,	/* was 32K */
15 	OPERM	= 0x3		/* mask of all permission types in open mode */
16 };
17 
18 struct Fid
19 {
20 	short busy;
21 	short open;
22 	int fid;
23 	char *user;
24 	Qid qid;
25 	VacFile *file;
26 	VacDirEnum *vde;
27 	Fid	*next;
28 };
29 
30 enum
31 {
32 	Pexec =		1,
33 	Pwrite = 	2,
34 	Pread = 	4,
35 	Pother = 	1,
36 	Pgroup = 	8,
37 	Powner =	64
38 };
39 
40 Fid	*fids;
41 uchar	*data;
42 int	mfd[2];
43 int	srvfd = -1;
44 char	*user;
45 uchar	mdata[8192+IOHDRSZ];
46 int messagesize = sizeof mdata;
47 Fcall	rhdr;
48 Fcall	thdr;
49 VacFs	*fs;
50 VtConn  *conn;
51 int	noperm;
52 int	dotu;
53 char *defmnt;
54 
55 Fid *	newfid(int);
56 void	error(char*);
57 void	io(void);
58 void	vacshutdown(void);
59 void	usage(void);
60 int	perm(Fid*, int);
61 int	permf(VacFile*, char*, int);
62 ulong	getl(void *p);
63 void	init(char*, char*, long, int);
64 int	vacdirread(Fid *f, char *p, long off, long cnt);
65 int	vacstat(VacFile *parent, VacDir *vd, uchar *p, int np);
66 void 	srv(void* a);
67 
68 
69 char	*rflush(Fid*), *rversion(Fid*),
70 	*rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
71 	*ropen(Fid*), *rcreate(Fid*),
72 	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
73 	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
74 
75 char 	*(*fcalls[Tmax])(Fid*);
76 
77 void
78 initfcalls(void)
79 {
80 	fcalls[Tflush]=	rflush;
81 	fcalls[Tversion]=	rversion;
82 	fcalls[Tattach]=	rattach;
83 	fcalls[Tauth]=		rauth;
84 	fcalls[Twalk]=		rwalk;
85 	fcalls[Topen]=		ropen;
86 	fcalls[Tcreate]=	rcreate;
87 	fcalls[Tread]=		rread;
88 	fcalls[Twrite]=	rwrite;
89 	fcalls[Tclunk]=	rclunk;
90 	fcalls[Tremove]=	rremove;
91 	fcalls[Tstat]=		rstat;
92 	fcalls[Twstat]=	rwstat;
93 }
94 
95 char	Eperm[] =	"permission denied";
96 char	Enotdir[] =	"not a directory";
97 char	Enotexist[] =	"file does not exist";
98 char	Einuse[] =	"file in use";
99 char	Eexist[] =	"file exists";
100 char	Enotowner[] =	"not owner";
101 char	Eisopen[] = 	"file already open for I/O";
102 char	Excl[] = 	"exclusive use file already open";
103 char	Ename[] = 	"illegal name";
104 char	Erdonly[] = 	"read only file system";
105 char	Eio[] = 	"i/o error";
106 char	Eempty[] = 	"directory is not empty";
107 char	Emode[] =	"illegal mode";
108 
109 int dflag;
110 
111 void
112 notifyf(void *a, char *s)
113 {
114 	USED(a);
115 	if(strncmp(s, "interrupt", 9) == 0)
116 		noted(NCONT);
117 	noted(NDFLT);
118 }
119 
120 void
121 threadmain(int argc, char *argv[])
122 {
123 	char *defsrv, *srvname;
124 	int p[2], fd;
125 	int stdio;
126 	char *host = nil;
127 	long ncache;
128 
129 	stdio = 0;
130 	ncache = 256;
131 	fmtinstall('H', encodefmt);
132 	fmtinstall('V', vtscorefmt);
133 	fmtinstall('F', vtfcallfmt);
134 
135 	defmnt = nil;
136 	defsrv = nil;
137 	ARGBEGIN{
138 	case 'd':
139 		fmtinstall('F', fcallfmt);
140 		dflag = 1;
141 		break;
142 	case 'c':
143 		ncache = atoi(EARGF(usage()));
144 		break;
145 	case 'i':
146 		defmnt = nil;
147 		stdio = 1;
148 		mfd[0] = 0;
149 		mfd[1] = 1;
150 		break;
151 	case 'h':
152 		host = EARGF(usage());
153 		break;
154 	case 'S':
155 		defsrv = EARGF(usage());
156 		break;
157 	case 's':
158 		defsrv = "vacfs";
159 		break;
160 	case 'm':
161 		defmnt = EARGF(usage());
162 		break;
163 	case 'p':
164 		noperm = 1;
165 		break;
166 	case 'V':
167 		chattyventi = 1;
168 		break;
169 	default:
170 		usage();
171 	}ARGEND
172 
173 	if(argc != 1)
174 		usage();
175 
176 	if(defsrv == nil && defmnt == nil && !stdio)
177 		defmnt = "/n/vac";
178 	if(stdio && defmnt)
179 		sysfatal("cannot use -m with -i");
180 
181 	initfcalls();
182 
183 	notify(notifyf);
184 	user = getuser();
185 
186 	conn = vtdial(host);
187 	if(conn == nil)
188 		sysfatal("could not connect to server: %r");
189 
190 	if(vtconnect(conn) < 0)
191 		sysfatal("vtconnect: %r");
192 
193 	fs = vacfsopen(conn, argv[0], VtOREAD, ncache);
194 	if(fs == nil)
195 		sysfatal("vacfsopen: %r");
196 
197 	if(!stdio){
198 		if(pipe(p) < 0)
199 			sysfatal("pipe failed: %r");
200 		mfd[0] = p[0];
201 		mfd[1] = p[0];
202 		srvfd = p[1];
203 		if(defsrv){
204 			srvname = smprint("/srv/%s", defsrv);
205 			fd = create(srvname, OWRITE|ORCLOSE, 0666);
206 			if(fd < 0)
207 				sysfatal("create %s: %r", srvname);
208 			if(fprint(fd, "%d", srvfd) < 0)
209 				sysfatal("write %s: %r", srvname);
210 			free(srvname);
211 		}
212 	}
213 
214 	procrfork(srv, 0, Stacksize, RFFDG|RFNAMEG|RFNOTEG);
215 
216 	if(!stdio){
217 		close(p[0]);
218 		if(defmnt){
219 			if(mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
220 				sysfatal("mount %s: %r", defmnt);
221 		}
222 	}
223 	threadexits(0);
224 }
225 
226 void
227 srv(void *a)
228 {
229 	USED(a);
230 	io();
231 	vacshutdown();
232 }
233 
234 void
235 usage(void)
236 {
237 	fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
238 	threadexitsall("usage");
239 }
240 
241 char*
242 rversion(Fid *unused)
243 {
244 	Fid *f;
245 
246 	USED(unused);
247 
248 	for(f = fids; f; f = f->next)
249 		if(f->busy)
250 			rclunk(f);
251 
252 	if(rhdr.msize < 256)
253 		return vtstrdup("version: message size too small");
254 	messagesize = rhdr.msize;
255 	if(messagesize > sizeof mdata)
256 		messagesize = sizeof mdata;
257 	thdr.msize = messagesize;
258 	if(strncmp(rhdr.version, "9P2000", 6) != 0)
259 		return vtstrdup("unrecognized 9P version");
260 	thdr.version = "9P2000";
261 	if(strncmp(rhdr.version, "9P2000.u", 8) == 0){
262 		dotu = 1;
263 		thdr.version = "9P2000.u";
264 	}
265 	return nil;
266 }
267 
268 char*
269 rflush(Fid *f)
270 {
271 	USED(f);
272 	return 0;
273 }
274 
275 char*
276 rauth(Fid *f)
277 {
278 	USED(f);
279 	return vtstrdup("vacfs: authentication not required");
280 }
281 
282 char*
283 rattach(Fid *f)
284 {
285 	/* no authentication for the momment */
286 	VacFile *file;
287 	char err[80];
288 
289 	file = vacfsgetroot(fs);
290 	if(file == nil) {
291 		rerrstr(err, sizeof err);
292 		return vtstrdup(err);
293 	}
294 
295 	f->busy = 1;
296 	f->file = file;
297 	f->qid.path = vacfilegetid(f->file);
298 	f->qid.vers = 0;
299 	f->qid.type = QTDIR;
300 	thdr.qid = f->qid;
301 	if(rhdr.uname[0])
302 		f->user = vtstrdup(rhdr.uname);
303 	else
304 		f->user = "none";
305 	return 0;
306 }
307 
308 char*
309 rwalk(Fid *f)
310 {
311 	VacFile *file, *nfile;
312 	Fid *nf;
313 	int nqid, nwname;
314 	Qid qid;
315 	char *err = nil;
316 
317 	if(f->busy == 0)
318 		return Enotexist;
319 	nf = nil;
320 	if(rhdr.fid != rhdr.newfid){
321 		if(f->open)
322 			return vtstrdup(Eisopen);
323 		if(f->busy == 0)
324 			return vtstrdup(Enotexist);
325 		nf = newfid(rhdr.newfid);
326 		if(nf->busy)
327 			return vtstrdup(Eisopen);
328 		nf->busy = 1;
329 		nf->open = 0;
330 		nf->qid = f->qid;
331 		nf->file = vacfileincref(f->file);
332 		nf->user = vtstrdup(f->user);
333 		f = nf;
334 	}
335 
336 	nwname = rhdr.nwname;
337 
338 	/* easy case */
339 	if(nwname == 0) {
340 		thdr.nwqid = 0;
341 		return 0;
342 	}
343 
344 	file = f->file;
345 	vacfileincref(file);
346 	qid = f->qid;
347 
348 	for(nqid = 0; nqid < nwname; nqid++){
349 		if((qid.type & QTDIR) == 0){
350 			err = Enotdir;
351 			break;
352 		}
353 		if(!permf(file, f->user, Pexec)) {
354 			err = Eperm;
355 			break;
356 		}
357 		nfile = vacfilewalk(file, rhdr.wname[nqid]);
358 		if(nfile == nil)
359 			break;
360 		vacfiledecref(file);
361 		file = nfile;
362 		qid.type = QTFILE;
363 		if(vacfileisdir(file))
364 			qid.type = QTDIR;
365 		qid.vers = vacfilegetmcount(file);
366 		qid.path = vacfilegetid(file);
367 		thdr.wqid[nqid] = qid;
368 	}
369 
370 	thdr.nwqid = nqid;
371 
372 	if(nqid == nwname){
373 		/* success */
374 		f->qid = thdr.wqid[nqid-1];
375 		vacfiledecref(f->file);
376 		f->file = file;
377 		return 0;
378 	}
379 
380 	vacfiledecref(file);
381 	if(nf != nil)
382 		rclunk(nf);
383 
384 	/* only error on the first element */
385 	if(nqid == 0)
386 		return vtstrdup(err);
387 
388 	return 0;
389 }
390 
391 char *
392 ropen(Fid *f)
393 {
394 	int mode, trunc;
395 
396 	if(f->open)
397 		return vtstrdup(Eisopen);
398 	if(!f->busy)
399 		return vtstrdup(Enotexist);
400 
401 	mode = rhdr.mode;
402 	thdr.iounit = messagesize - IOHDRSZ;
403 	if(f->qid.type & QTDIR){
404 		if(mode != OREAD)
405 			return vtstrdup(Eperm);
406 		if(!perm(f, Pread))
407 			return vtstrdup(Eperm);
408 		thdr.qid = f->qid;
409 		f->vde = nil;
410 		f->open = 1;
411 		return 0;
412 	}
413 	if(mode & ORCLOSE)
414 		return vtstrdup(Erdonly);
415 	trunc = mode & OTRUNC;
416 	mode &= OPERM;
417 	if(mode==OWRITE || mode==ORDWR || trunc)
418 		if(!perm(f, Pwrite))
419 			return vtstrdup(Eperm);
420 	if(mode==OREAD || mode==ORDWR)
421 		if(!perm(f, Pread))
422 			return vtstrdup(Eperm);
423 	if(mode==OEXEC)
424 		if(!perm(f, Pexec))
425 			return vtstrdup(Eperm);
426 	thdr.qid = f->qid;
427 	thdr.iounit = messagesize - IOHDRSZ;
428 	f->open = 1;
429 	return 0;
430 }
431 
432 char*
433 rcreate(Fid* fid)
434 {
435 	VacFile *vf;
436 	ulong mode;
437 
438 	if(fid->open)
439 		return vtstrdup(Eisopen);
440 	if(!fid->busy)
441 		return vtstrdup(Enotexist);
442 	if(fs->mode & ModeSnapshot)
443 		return vtstrdup(Erdonly);
444 	vf = fid->file;
445 	if(!vacfileisdir(vf))
446 		return vtstrdup(Enotdir);
447 	if(!permf(vf, fid->user, Pwrite))
448 		return vtstrdup(Eperm);
449 
450 	mode = rhdr.perm & 0777;
451 
452 	if(rhdr.perm & DMDIR){
453 		if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
454 			return vtstrdup(Emode);
455 		switch(rhdr.mode & OPERM){
456 		default:
457 			return vtstrdup(Emode);
458 		case OEXEC:
459 		case OREAD:
460 			break;
461 		case OWRITE:
462 		case ORDWR:
463 			return vtstrdup(Eperm);
464 		}
465 		mode |= ModeDir;
466 	}
467 	vf = vacfilecreate(vf, rhdr.name, mode);
468 	if(vf == nil) {
469 		char err[80];
470 		rerrstr(err, sizeof err);
471 
472 		return vtstrdup(err);
473 	}
474 
475 	vacfiledecref(fid->file);
476 
477 	fid->file = vf;
478 	fid->qid.type = QTFILE;
479 	if(vacfileisdir(vf))
480 		fid->qid.type = QTDIR;
481 	fid->qid.vers = vacfilegetmcount(vf);
482 	fid->qid.path = vacfilegetid(vf);
483 
484 	thdr.qid = fid->qid;
485 	thdr.iounit = messagesize - IOHDRSZ;
486 
487 	return 0;
488 }
489 
490 char*
491 rread(Fid *f)
492 {
493 	char *buf;
494 	vlong off;
495 	int cnt;
496 	VacFile *vf;
497 	char err[80];
498 	int n;
499 
500 	if(!f->busy)
501 		return vtstrdup(Enotexist);
502 	vf = f->file;
503 	thdr.count = 0;
504 	off = rhdr.offset;
505 	buf = thdr.data;
506 	cnt = rhdr.count;
507 	if(f->qid.type & QTDIR)
508 		n = vacdirread(f, buf, off, cnt);
509 	else if(vacfilegetmode(f->file)&ModeDevice)
510 		return vtstrdup("device");
511 	else if(vacfilegetmode(f->file)&ModeLink)
512 		return vtstrdup("symbolic link");
513 	else if(vacfilegetmode(f->file)&ModeNamedPipe)
514 		return vtstrdup("named pipe");
515 	else
516 		n = vacfileread(vf, buf, cnt, off);
517 	if(n < 0) {
518 		rerrstr(err, sizeof err);
519 		return vtstrdup(err);
520 	}
521 	thdr.count = n;
522 	return 0;
523 }
524 
525 char*
526 rwrite(Fid *f)
527 {
528 	USED(f);
529 	return vtstrdup(Erdonly);
530 }
531 
532 char *
533 rclunk(Fid *f)
534 {
535 	f->busy = 0;
536 	f->open = 0;
537 	vtfree(f->user);
538 	f->user = nil;
539 	if(f->file)
540 		vacfiledecref(f->file);
541 	f->file = nil;
542 	vdeclose(f->vde);
543 	f->vde = nil;
544 	return 0;
545 }
546 
547 char *
548 rremove(Fid *f)
549 {
550 	VacFile *vf, *vfp;
551 	char errbuf[80];
552 	char *err = nil;
553 
554 	if(!f->busy)
555 		return vtstrdup(Enotexist);
556 	vf = f->file;
557 	vfp = vacfilegetparent(vf);
558 
559 	if(!permf(vfp, f->user, Pwrite)) {
560 		err = Eperm;
561 		goto Exit;
562 	}
563 
564 	if(!vacfileremove(vf)) {
565 		rerrstr(errbuf, sizeof errbuf);
566 		err = errbuf;
567 	}
568 
569 Exit:
570 	vacfiledecref(vfp);
571 	rclunk(f);
572 	return vtstrdup(err);
573 }
574 
575 char *
576 rstat(Fid *f)
577 {
578 	VacDir dir;
579 	static uchar statbuf[1024];
580 	VacFile *parent;
581 
582 	if(!f->busy)
583 		return vtstrdup(Enotexist);
584 	parent = vacfilegetparent(f->file);
585 	vacfilegetdir(f->file, &dir);
586 	thdr.stat = statbuf;
587 	thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf);
588 	vdcleanup(&dir);
589 	vacfiledecref(parent);
590 	return 0;
591 }
592 
593 char *
594 rwstat(Fid *f)
595 {
596 	if(!f->busy)
597 		return vtstrdup(Enotexist);
598 	return vtstrdup(Erdonly);
599 }
600 
601 int
602 vacstat(VacFile *parent, VacDir *vd, uchar *p, int np)
603 {
604 	int ret;
605 	Dir dir;
606 
607 	memset(&dir, 0, sizeof(dir));
608 
609 	dir.qid.path = vd->qid + vacfilegetqidoffset(parent);
610 	if(vd->qidspace)
611 		dir.qid.path += vd->qidoffset;
612 	dir.qid.vers = vd->mcount;
613 	dir.mode = vd->mode & 0777;
614 	if(vd->mode & ModeAppend){
615 		dir.qid.type |= QTAPPEND;
616 		dir.mode |= DMAPPEND;
617 	}
618 	if(vd->mode & ModeExclusive){
619 		dir.qid.type |= QTEXCL;
620 		dir.mode |= DMEXCL;
621 	}
622 	if(vd->mode & ModeDir){
623 		dir.qid.type |= QTDIR;
624 		dir.mode |= DMDIR;
625 	}
626 
627 
628 	dir.atime = vd->atime;
629 	dir.mtime = vd->mtime;
630 	dir.length = vd->size;
631 
632 	dir.name = vd->elem;
633 	dir.uid = vd->uid;
634 	dir.gid = vd->gid;
635 	dir.muid = vd->mid;
636 
637 	ret = convD2Mu(&dir, p, np, dotu);
638 	return ret;
639 }
640 
641 int
642 vacdirread(Fid *f, char *p, long off, long cnt)
643 {
644 	int i, n, nb;
645 	VacDir vd;
646 
647 	/*
648 	 * special case of rewinding a directory
649 	 * otherwise ignore the offset
650 	 */
651 	if(off == 0 && f->vde){
652 		vdeclose(f->vde);
653 		f->vde = nil;
654 	}
655 
656 	if(f->vde == nil){
657 		f->vde = vdeopen(f->file);
658 		if(f->vde == nil)
659 			return -1;
660 	}
661 
662 	for(nb = 0; nb < cnt; nb += n) {
663 		i = vderead(f->vde, &vd);
664 		if(i < 0)
665 			return -1;
666 		if(i == 0)
667 			break;
668 		n = vacstat(f->file, &vd, (uchar*)p, cnt-nb);
669 		if(n <= BIT16SZ) {
670 			vdeunread(f->vde);
671 			break;
672 		}
673 		vdcleanup(&vd);
674 		p += n;
675 	}
676 	return nb;
677 }
678 
679 Fid *
680 newfid(int fid)
681 {
682 	Fid *f, *ff;
683 
684 	ff = 0;
685 	for(f = fids; f; f = f->next)
686 		if(f->fid == fid)
687 			return f;
688 		else if(!ff && !f->busy)
689 			ff = f;
690 	if(ff){
691 		ff->fid = fid;
692 		return ff;
693 	}
694 	f = vtmallocz(sizeof *f);
695 	f->fid = fid;
696 	f->next = fids;
697 	fids = f;
698 	return f;
699 }
700 
701 void
702 io(void)
703 {
704 	char *err;
705 	int n;
706 
707 	for(;;){
708 		n = read9pmsg(mfd[0], mdata, sizeof mdata);
709 		if(n <= 0)
710 			break;
711 		if(convM2Su(mdata, n, &rhdr, dotu) != n)
712 			sysfatal("convM2S conversion error");
713 
714 		if(dflag)
715 			fprint(2, "vacfs:<-%F\n", &rhdr);
716 
717 		thdr.data = (char*)mdata + IOHDRSZ;
718 		if(!fcalls[rhdr.type])
719 			err = "bad fcall type";
720 		else
721 			err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
722 		if(err){
723 			thdr.type = Rerror;
724 			thdr.ename = err;
725 		}else{
726 			thdr.type = rhdr.type + 1;
727 			thdr.fid = rhdr.fid;
728 		}
729 		thdr.tag = rhdr.tag;
730 		if(dflag)
731 			fprint(2, "vacfs:->%F\n", &thdr);
732 		n = convS2Mu(&thdr, mdata, messagesize, dotu);
733 		if(n <= BIT16SZ)
734 			sysfatal("convS2Mu conversion error");
735 		if(err)
736 			vtfree(err);
737 
738 		if(write(mfd[1], mdata, n) != n)
739 			sysfatal("mount write: %r");
740 	}
741 }
742 
743 int
744 permf(VacFile *vf, char *user, int p)
745 {
746 	VacDir dir;
747 	ulong perm;
748 
749 	if(vacfilegetdir(vf, &dir))
750 		return 0;
751 	perm = dir.mode & 0777;
752 
753 	if(noperm)
754 		goto Good;
755 	if((p*Pother) & perm)
756 		goto Good;
757 	if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
758 		goto Good;
759 	if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
760 		goto Good;
761 	vdcleanup(&dir);
762 	return 0;
763 Good:
764 	vdcleanup(&dir);
765 	return 1;
766 }
767 
768 int
769 perm(Fid *f, int p)
770 {
771 	return permf(f->file, f->user, p);
772 }
773 
774 void
775 vacshutdown(void)
776 {
777 	Fid *f;
778 
779 	for(f = fids; f; f = f->next) {
780 		if(!f->busy)
781 			continue;
782 		rclunk(f);
783 	}
784 
785 	vacfsclose(fs);
786 	vthangup(conn);
787 }
788 
789