xref: /plan9/sys/src/cmd/tapefs/fs.c (revision bbd061d49f2d27022e38642b8a1e5862b40931a7)
1 #include <u.h>
2 #include <libc.h>
3 #include <authsrv.h>
4 #include <fcall.h>
5 #include "tapefs.h"
6 
7 Fid	*fids;
8 Ram	*ram;
9 int	mfd[2];
10 char	*user;
11 uchar	mdata[Maxbuf+IOHDRSZ];
12 int	messagesize = Maxbuf+IOHDRSZ;
13 Fcall	rhdr;
14 Fcall	thdr;
15 ulong	path;
16 Idmap	*uidmap;
17 Idmap	*gidmap;
18 int	replete;
19 int	blocksize;		/* for 32v */
20 int	verbose;
21 int	newtap;		/* tap with time in sec */
22 int	blocksize;
23 
24 Fid *	newfid(int);
25 int	ramstat(Ram*, uchar*, int);
26 void	io(void);
27 void	usage(void);
28 int	perm(int);
29 
30 char	*rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
31 	*rattach(Fid*), *rwalk(Fid*),
32 	*ropen(Fid*), *rcreate(Fid*),
33 	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
34 	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
35 
36 char 	*(*fcalls[])(Fid*) = {
37 	[Tflush]	rflush,
38 	[Tversion]		rversion,
39 	[Tauth]	rauth,
40 	[Tattach]	rattach,
41 	[Twalk]		rwalk,
42 	[Topen]		ropen,
43 	[Tcreate]	rcreate,
44 	[Tread]		rread,
45 	[Twrite]	rwrite,
46 	[Tclunk]	rclunk,
47 	[Tremove]	rremove,
48 	[Tstat]		rstat,
49 	[Twstat]	rwstat,
50 };
51 
52 char	Eperm[] =	"permission denied";
53 char	Enotdir[] =	"not a directory";
54 char	Enoauth[] =	"tapefs: authentication not required";
55 char	Enotexist[] =	"file does not exist";
56 char	Einuse[] =	"file in use";
57 char	Eexist[] =	"file exists";
58 char	Enotowner[] =	"not owner";
59 char	Eisopen[] = 	"file already open for I/O";
60 char	Excl[] = 	"exclusive use file already open";
61 char	Ename[] = 	"illegal name";
62 
63 void
notifyf(void * a,char * s)64 notifyf(void *a, char *s)
65 {
66 	USED(a);
67 	if(strncmp(s, "interrupt", 9) == 0)
68 		noted(NCONT);
69 	noted(NDFLT);
70 }
71 
72 void
main(int argc,char * argv[])73 main(int argc, char *argv[])
74 {
75 	Ram *r;
76 	char *defmnt;
77 	int p[2];
78 	char buf[TICKREQLEN];
79 
80 	fmtinstall('F', fcallfmt);
81 
82 	defmnt = "/n/tapefs";
83 	ARGBEGIN{
84 	case 'm':
85 		defmnt = EARGF(usage());
86 		break;
87 	case 'p':			/* password file */
88 		uidmap = getpass(EARGF(usage()));
89 		break;
90 	case 'g':			/* group file */
91 		gidmap = getpass(EARGF(usage()));
92 		break;
93 	case 'v':
94 		verbose++;
95 		break;
96 	case 'n':
97 		newtap++;
98 		break;
99 	case 'b':
100 		blocksize = atoi(EARGF(usage()));
101 		break;
102 	default:
103 		usage();
104 	}ARGEND
105 
106 	if(argc==0)
107 		error("no file to mount");
108 	user = getuser();
109 	if(user == nil)
110 		user = "dmr";
111 	ram = r = (Ram *)emalloc(sizeof(Ram));
112 	r->busy = 1;
113 	r->data = 0;
114 	r->ndata = 0;
115 	r->perm = DMDIR | 0775;
116 	r->qid.path = 0;
117 	r->qid.vers = 0;
118 	r->qid.type = QTDIR;
119 	r->parent = 0;
120 	r->child = 0;
121 	r->next = 0;
122 	r->user = user;
123 	r->group = user;
124 	r->atime = time(0);
125 	r->mtime = r->atime;
126 	r->replete = 0;
127 	r->name = estrdup(".");
128 	populate(argv[0]);
129 	r->replete |= replete;
130 	if(pipe(p) < 0)
131 		error("pipe failed");
132 	mfd[0] = mfd[1] = p[0];
133 	notify(notifyf);
134 
135 	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
136 	case -1:
137 		error("fork");
138 	case 0:
139 		close(p[1]);
140 		notify(notifyf);
141 		io();
142 		break;
143 	default:
144 		close(p[0]);	/* don't deadlock if child fails */
145 		if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0) {
146 			sprint(buf, "mount on `%s' failed", defmnt);
147 			error(buf);
148 		}
149 	}
150 	exits(0);
151 }
152 
153 char*
rversion(Fid * unused)154 rversion(Fid *unused)
155 {
156 	Fid *f;
157 
158 	USED(unused);
159 
160 	if(rhdr.msize < 256)
161 		return "version: message too small";
162 	if(rhdr.msize > messagesize)
163 		rhdr.msize = messagesize;
164 	else
165 		messagesize = rhdr.msize;
166 	thdr.msize = messagesize;
167 	if(strncmp(rhdr.version, "9P2000", 6) != 0)
168 		return "unrecognized 9P version";
169 	thdr.version = "9P2000";
170 
171 	for(f = fids; f; f = f->next)
172 		if(f->busy)
173 			rclunk(f);
174 	return 0;
175 }
176 
177 char*
rauth(Fid * unused)178 rauth(Fid *unused)
179 {
180 	USED(unused);
181 
182 	return Enoauth;
183 }
184 
185 char*
rflush(Fid * f)186 rflush(Fid *f)
187 {
188 	USED(f);
189 	return 0;
190 }
191 
192 char*
rattach(Fid * f)193 rattach(Fid *f)
194 {
195 	/* no authentication! */
196 	f->busy = 1;
197 	f->rclose = 0;
198 	f->ram = ram;
199 	thdr.qid = f->ram->qid;
200 	if(rhdr.uname[0])
201 		f->user = strdup(rhdr.uname);
202 	else
203 		f->user = "none";
204 	return 0;
205 }
206 
207 char*
rwalk(Fid * f)208 rwalk(Fid *f)
209 {
210 	Fid *nf;
211 	Ram *r;
212 	char *err;
213 	char *name;
214 	Ram *dir;
215 	int i;
216 
217 	nf = nil;
218 	if(f->ram->busy == 0)
219 		return Enotexist;
220 	if(f->open)
221 		return Eisopen;
222 	if(rhdr.newfid != rhdr.fid){
223 		nf = newfid(rhdr.newfid);
224 		nf->busy = 1;
225 		nf->open = 0;
226 		nf->rclose = 0;
227 		nf->ram = f->ram;
228 		nf->user = f->user;	/* no ref count; the leakage is minor */
229 		f = nf;
230 	}
231 
232 	thdr.nwqid = 0;
233 	err = nil;
234 	r = f->ram;
235 
236 	if(rhdr.nwname > 0){
237 		for(i=0; i<rhdr.nwname; i++){
238 			if((r->qid.type & QTDIR) == 0){
239 				err = Enotdir;
240 				break;
241 			}
242 			if(r->busy == 0){
243 				err = Enotexist;
244 				break;
245 			}
246 			r->atime = time(0);
247 			name = rhdr.wname[i];
248 			dir = r;
249 			if(!perm(Pexec)){
250 				err = Eperm;
251 				break;
252 			}
253 			if(strcmp(name, "..") == 0){
254 				r = dir->parent;
255    Accept:
256 				if(i == MAXWELEM){
257 					err = "name too long";
258 					break;
259 				}
260  				thdr.wqid[thdr.nwqid++] = r->qid;
261 				continue;
262 			}
263 			if(!dir->replete)
264 				popdir(dir);
265 			for(r=dir->child; r; r=r->next)
266 				if(r->busy && strcmp(name, r->name)==0)
267 					goto Accept;
268 			break;	/* file not found */
269 		}
270 
271 		if(i==0 && err == nil)
272 			err = Enotexist;
273 	}
274 
275 	if(err!=nil || thdr.nwqid<rhdr.nwname){
276 		if(nf){
277 			nf->busy = 0;
278 			nf->open = 0;
279 			nf->ram = 0;
280 		}
281 	}else if(thdr.nwqid  == rhdr.nwname)
282 		f->ram = r;
283 
284 	return err;
285 
286 }
287 
288 char *
ropen(Fid * f)289 ropen(Fid *f)
290 {
291 	Ram *r;
292 	int mode, trunc;
293 
294 	if(f->open)
295 		return Eisopen;
296 	r = f->ram;
297 	if(r->busy == 0)
298 		return Enotexist;
299 	if(r->perm & DMEXCL)
300 		if(r->open)
301 			return Excl;
302 	mode = rhdr.mode;
303 	if(r->qid.type & QTDIR){
304 		if(mode != OREAD)
305 			return Eperm;
306 		thdr.qid = r->qid;
307 		return 0;
308 	}
309 	if(mode & ORCLOSE)
310 		return Eperm;
311 	trunc = mode & OTRUNC;
312 	mode &= OPERM;
313 	if(mode==OWRITE || mode==ORDWR || trunc)
314 		if(!perm(Pwrite))
315 			return Eperm;
316 	if(mode==OREAD || mode==ORDWR)
317 		if(!perm(Pread))
318 			return Eperm;
319 	if(mode==OEXEC)
320 		if(!perm(Pexec))
321 			return Eperm;
322 	if(trunc && (r->perm&DMAPPEND)==0){
323 		r->ndata = 0;
324 		dotrunc(r);
325 		r->qid.vers++;
326 	}
327 	thdr.qid = r->qid;
328 	thdr.iounit = messagesize-IOHDRSZ;
329 	f->open = 1;
330 	r->open++;
331 	return 0;
332 }
333 
334 char *
rcreate(Fid * f)335 rcreate(Fid *f)
336 {
337 	USED(f);
338 
339 	return Eperm;
340 }
341 
342 char*
rread(Fid * f)343 rread(Fid *f)
344 {
345 	int i, len;
346 	Ram *r;
347 	char *buf;
348 	uvlong off, end;
349 	int n, cnt;
350 
351 	if(f->ram->busy == 0)
352 		return Enotexist;
353 	n = 0;
354 	thdr.count = 0;
355 	off = rhdr.offset;
356 	end = rhdr.offset + rhdr.count;
357 	cnt = rhdr.count;
358 	if(cnt > messagesize-IOHDRSZ)
359 		cnt = messagesize-IOHDRSZ;
360 	buf = thdr.data;
361 	if(f->ram->qid.type & QTDIR){
362 		if(!f->ram->replete)
363 			popdir(f->ram);
364 		for(i=0,r=f->ram->child; r!=nil && i<end; r=r->next){
365 			if(!r->busy)
366 				continue;
367 			len = ramstat(r, (uchar*)buf+n, cnt-n);
368 			if(len <= BIT16SZ)
369 				break;
370 			if(i >= off)
371 				n += len;
372 			i += len;
373 		}
374 		thdr.count = n;
375 		return 0;
376 	}
377 	r = f->ram;
378 	if(off >= r->ndata)
379 		return 0;
380 	r->atime = time(0);
381 	n = cnt;
382 	if(off+n > r->ndata)
383 		n = r->ndata - off;
384 	thdr.data = doread(r, off, n);
385 	thdr.count = n;
386 	return 0;
387 }
388 
389 char*
rwrite(Fid * f)390 rwrite(Fid *f)
391 {
392 	Ram *r;
393 	ulong off;
394 	int cnt;
395 
396 	r = f->ram;
397 	if(dopermw(f->ram)==0)
398 		return Eperm;
399 	if(r->busy == 0)
400 		return Enotexist;
401 	off = rhdr.offset;
402 	if(r->perm & DMAPPEND)
403 		off = r->ndata;
404 	cnt = rhdr.count;
405 	if(r->qid.type & QTDIR)
406 		return "file is a directory";
407 	if(off > 100*1024*1024)		/* sanity check */
408 		return "write too big";
409 	dowrite(r, rhdr.data, off, cnt);
410 	r->qid.vers++;
411 	r->mtime = time(0);
412 	thdr.count = cnt;
413 	return 0;
414 }
415 
416 char *
rclunk(Fid * f)417 rclunk(Fid *f)
418 {
419 	if(f->open)
420 		f->ram->open--;
421 	f->busy = 0;
422 	f->open = 0;
423 	f->ram = 0;
424 	return 0;
425 }
426 
427 char *
rremove(Fid * f)428 rremove(Fid *f)
429 {
430 	USED(f);
431 	return Eperm;
432 }
433 
434 char *
rstat(Fid * f)435 rstat(Fid *f)
436 {
437 	if(f->ram->busy == 0)
438 		return Enotexist;
439 	thdr.nstat = ramstat(f->ram, thdr.stat, messagesize-IOHDRSZ);
440 	return 0;
441 }
442 
443 char *
rwstat(Fid * f)444 rwstat(Fid *f)
445 {
446 	if(f->ram->busy == 0)
447 		return Enotexist;
448 	return Eperm;
449 }
450 
451 int
ramstat(Ram * r,uchar * buf,int nbuf)452 ramstat(Ram *r, uchar *buf, int nbuf)
453 {
454 	Dir dir;
455 
456 	dir.name = r->name;
457 	dir.qid = r->qid;
458 	dir.mode = r->perm;
459 	dir.length = r->ndata;
460 	dir.uid = r->user;
461 	dir.gid = r->group;
462 	dir.muid = r->user;
463 	dir.atime = r->atime;
464 	dir.mtime = r->mtime;
465 	return convD2M(&dir, buf, nbuf);
466 }
467 
468 Fid *
newfid(int fid)469 newfid(int fid)
470 {
471 	Fid *f, *ff;
472 
473 	ff = 0;
474 	for(f = fids; f; f = f->next)
475 		if(f->fid == fid)
476 			return f;
477 		else if(!ff && !f->busy)
478 			ff = f;
479 	if(ff){
480 		ff->fid = fid;
481 		ff->open = 0;
482 		ff->busy = 1;
483 	}
484 	f = emalloc(sizeof *f);
485 	f->ram = 0;
486 	f->fid = fid;
487 	f->busy = 1;
488 	f->open = 0;
489 	f->next = fids;
490 	fids = f;
491 	return f;
492 }
493 
494 void
io(void)495 io(void)
496 {
497 	char *err;
498 	int n, nerr;
499 	char buf[ERRMAX];
500 
501 	errstr(buf, sizeof buf);
502 	for(nerr=0, buf[0]='\0'; nerr<100; nerr++){
503 		/*
504 		 * reading from a pipe or a network device
505 		 * will give an error after a few eof reads
506 		 * however, we cannot tell the difference
507 		 * between a zero-length read and an interrupt
508 		 * on the processes writing to us,
509 		 * so we wait for the error
510 		 */
511 		n = read9pmsg(mfd[0], mdata, sizeof mdata);
512 		if(n==0)
513 			continue;
514 		if(n < 0){
515 			if(buf[0]=='\0')
516 				errstr(buf, sizeof buf);
517 			continue;
518 		}
519 		nerr = 0;
520 		buf[0] = '\0';
521 		if(convM2S(mdata, n, &rhdr) != n)
522 			error("convert error in convM2S");
523 
524 		if(verbose)
525 			fprint(2, "tapefs: <=%F\n", &rhdr);/**/
526 
527 		thdr.data = (char*)mdata + IOHDRSZ;
528 		thdr.stat = mdata + IOHDRSZ;
529 		if(!fcalls[rhdr.type])
530 			err = "bad fcall type";
531 		else
532 			err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
533 		if(err){
534 			thdr.type = Rerror;
535 			thdr.ename = err;
536 		}else{
537 			thdr.type = rhdr.type + 1;
538 			thdr.fid = rhdr.fid;
539 		}
540 		thdr.tag = rhdr.tag;
541 		n = convS2M(&thdr, mdata, messagesize);
542 		if(n <= 0)
543 			error("convert error in convS2M");
544 		if(verbose)
545 			fprint(2, "tapefs: =>%F\n", &thdr);/**/
546 		if(write(mfd[1], mdata, n) != n)
547 			error("mount write");
548 	}
549 	if(buf[0]=='\0' || strstr(buf, "hungup"))
550 		exits("");
551 	fprint(2, "%s: mount read: %s\n", argv0, buf);
552 	exits(buf);
553 }
554 
555 int
perm(int p)556 perm(int p)
557 {
558 	if(p==Pwrite)
559 		return 0;
560 	return 1;
561 }
562 
563 void
error(char * s)564 error(char *s)
565 {
566 	fprint(2, "%s: %s: ", argv0, s);
567 	perror("");
568 	exits(s);
569 }
570 
571 char*
estrdup(char * s)572 estrdup(char *s)
573 {
574 	char *t;
575 
576 	t = emalloc(strlen(s)+1);
577 	strcpy(t, s);
578 	return t;
579 }
580 
581 void *
emalloc(ulong n)582 emalloc(ulong n)
583 {
584 	void *p;
585 	p = mallocz(n, 1);
586 	if(!p)
587 		error("out of memory");
588 	return p;
589 }
590 
591 void *
erealloc(void * p,ulong n)592 erealloc(void *p, ulong n)
593 {
594 	p = realloc(p, n);
595 	if(!p)
596 		error("out of memory");
597 	return p;
598 }
599 
600 void
usage(void)601 usage(void)
602 {
603 	fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);
604 	exits("usage");
605 }
606