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