xref: /plan9-contrib/sys/src/cmd/tapefs/fs.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <tapefs.h>
6 
7 Fid	*fids;
8 Ram	*ram;
9 int	mfd[2];
10 char	user[NAMELEN];
11 char	mdata[MAXMSG+MAXFDATA];
12 Fcall	rhdr;
13 Fcall	thdr;
14 ulong	path;
15 Idmap	*uidmap;
16 Idmap	*gidmap;
17 int	replete;
18 
19 Fid *	newfid(int);
20 void	ramstat(Ram*, char*);
21 void	io(void);
22 void	usage(void);
23 int	perm(Fid*, Ram*, int);
24 
25 char	*rflush(Fid*), *rnop(Fid*), *rsession(Fid*),
26 	*rattach(Fid*), *rclone(Fid*), *rwalk(Fid*),
27 	*rclwalk(Fid*), *ropen(Fid*), *rcreate(Fid*),
28 	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
29 	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
30 	*rauth(Fid*);
31 
32 char 	*(*fcalls[])(Fid*) = {
33 	[Tflush]	rflush,
34 	[Tsession]	rsession,
35 	[Tnop]		rnop,
36 	[Tattach]	rattach,
37 	[Tclone]	rclone,
38 	[Twalk]		rwalk,
39 	[Tclwalk]	rclwalk,
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[] =	"no authentication in ramfs";
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 
77 	defmnt = "/n/tapefs";
78 	ARGBEGIN{
79 	case 'm':
80 		defmnt = ARGF();
81 		break;
82 	case 'p':			/* password file */
83 		uidmap = getpass(ARGF());
84 		break;
85 	case 'g':			/* group file */
86 		gidmap = getpass(ARGF());
87 		break;
88 	default:
89 		usage();
90 	}ARGEND
91 
92 	if (argc==0)
93 		error("no file to mount");
94 	ram = r = (Ram *)emalloc(sizeof(Ram));
95 	r->busy = 1;
96 	r->data = 0;
97 	r->ndata = 0;
98 	r->perm = CHDIR | 0775;
99 	r->qid.path = CHDIR;
100 	r->qid.vers = 0;
101 	r->parent = 0;
102 	r->child = 0;
103 	r->next = 0;
104 	r->user = user;
105 	r->group = user;
106 	r->atime = time(0);
107 	r->mtime = r->atime;
108 	r->replete = 0;
109 	strcpy(r->name, ".");
110 	strcpy(user, getuser());
111 	populate(argv[0]);
112 	r->replete |= replete;
113 	if(pipe(p) < 0)
114 		error("pipe failed");
115 	mfd[0] = mfd[1] = p[0];
116 	notify(notifyf);
117 
118 	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
119 	case -1:
120 		error("fork");
121 	case 0:
122 		close(p[1]);
123 		notify(notifyf);
124 		io();
125 		break;
126 	default:
127 		close(p[0]);	/* don't deadlock if child fails */
128 		if(mount(p[1], defmnt, MREPL|MCREATE, "") < 0) {
129 			char buf[256];
130 			sprint(buf, "mount on `%s' failed", defmnt);
131 			error(buf);
132 		}
133 	}
134 	exits(0);
135 }
136 
137 char*
138 rnop(Fid *f)
139 {
140 	USED(f);
141 	return 0;
142 }
143 
144 char*
145 rsession(Fid *unused)
146 {
147 	Fid *f;
148 
149 	USED(unused);
150 
151 	memset(thdr.authid, 0, sizeof(thdr.authid));
152 	memset(thdr.authdom, 0, sizeof(thdr.authdom));
153 	memset(thdr.chal, 0, sizeof(thdr.chal));
154 	for(f = fids; f; f = f->next)
155 		if(f->busy)
156 			rclunk(f);
157 	return 0;
158 }
159 
160 char*
161 rflush(Fid *f)
162 {
163 	USED(f);
164 	return 0;
165 }
166 
167 char*
168 rattach(Fid *f)
169 {
170 	/* no authentication! */
171 	f->busy = 1;
172 	f->rclose = 0;
173 	f->ram = ram;
174 	thdr.qid = f->ram->qid;
175 	if(rhdr.uname[0])
176 		f->user = strdup(rhdr.uname);
177 	else
178 		f->user = "none";
179 	return 0;
180 }
181 
182 char*
183 rclone(Fid *f)
184 {
185 	Fid *nf;
186 
187 	if(f->open)
188 		return Eisopen;
189 	if(f->ram->busy == 0)
190 		return Enotexist;
191 	nf = newfid(rhdr.newfid);
192 	nf->busy = 1;
193 	nf->open = 0;
194 	nf->rclose = 0;
195 	nf->ram = f->ram;
196 	nf->user = f->user;	/* no ref count; the leakage is minor */
197 	return 0;
198 }
199 
200 char*
201 rwalk(Fid *f)
202 {
203 	Ram *r;
204 	char *name;
205 	Ram *dir;
206 
207 	if((f->ram->qid.path & CHDIR) == 0)
208 		return Enotdir;
209 	if(f->ram->busy == 0)
210 		return Enotexist;
211 	f->ram->atime = time(0);
212 	name = rhdr.name;
213 	if(strcmp(name, ".") == 0){
214 		thdr.qid = f->ram->qid;
215 		return 0;
216 	}
217 	dir = f->ram;
218 	if(!dir || !perm(f, dir, Pexec))
219 		return Eperm;
220 	if(strcmp(name, "..") == 0){
221 		f->ram = dir->parent;
222 		thdr.qid = f->ram->qid;
223 		return 0;
224 	}
225 	if (!dir->replete)
226 		popdir(dir);
227 	for(r=dir->child; r; r=r->next)
228 		if(r->busy && strcmp(name, r->name)==0){
229 			thdr.qid = r->qid;
230 			f->ram = r;
231 			return 0;
232 		}
233 	return Enotexist;
234 }
235 
236 char *
237 rclwalk(Fid *f)
238 {
239 	Fid *nf;
240 	char *err;
241 
242 	nf = newfid(rhdr.newfid);
243 	nf->busy = 1;
244 	nf->rclose = 0;
245 	nf->ram = f->ram;
246 	nf->user = f->user;
247 	if(err = rwalk(nf))
248 		rclunk(nf);
249 	return err;
250 }
251 
252 char *
253 ropen(Fid *f)
254 {
255 	Ram *r;
256 	int mode, trunc;
257 
258 	if(f->open)
259 		return Eisopen;
260 	r = f->ram;
261 	if(r->busy == 0)
262 		return Enotexist;
263 	if(r->perm & CHEXCL)
264 		if(r->open)
265 			return Excl;
266 	mode = rhdr.mode;
267 	if(r->qid.path & CHDIR){
268 		if(mode != OREAD)
269 			return Eperm;
270 		thdr.qid = r->qid;
271 		return 0;
272 	}
273 	if(mode & ORCLOSE)
274 		return Eperm;
275 	trunc = mode & OTRUNC;
276 	mode &= OPERM;
277 	if(mode==OWRITE || mode==ORDWR || trunc)
278 		if(!perm(f, r, Pwrite))
279 			return Eperm;
280 	if(mode==OREAD || mode==ORDWR)
281 		if(!perm(f, r, Pread))
282 			return Eperm;
283 	if(mode==OEXEC)
284 		if(!perm(f, r, Pexec))
285 			return Eperm;
286 	if(trunc && (r->perm&CHAPPEND)==0){
287 		r->ndata = 0;
288 		dotrunc(r);
289 		r->qid.vers++;
290 	}
291 	thdr.qid = r->qid;
292 	f->open = 1;
293 	r->open++;
294 	return 0;
295 }
296 
297 char *
298 rcreate(Fid *f)
299 {
300 	USED(f);
301 
302 	return Eperm;
303 }
304 
305 char*
306 rread(Fid *f)
307 {
308 	Ram *r;
309 	char *buf;
310 	long off;
311 	int n, cnt;
312 
313 	if(f->ram->busy == 0)
314 		return Enotexist;
315 	n = 0;
316 	thdr.count = 0;
317 	off = rhdr.offset;
318 	cnt = rhdr.count;
319 	buf = thdr.data;
320 	if(f->ram->qid.path & CHDIR){
321 		if (!f->ram->replete)
322 			popdir(f->ram);
323 		if(off%DIRLEN || cnt%DIRLEN)
324 			return "i/o error";
325 		for(r=f->ram->child; off; r=r->next){
326 			if (r==0)
327 				return 0;
328 			if(r->busy)
329 				off -= DIRLEN;
330 		}
331 		for(; r && n < cnt; r=r->next){
332 			if(!r->busy)
333 				continue;
334 			ramstat(r, buf+n);
335 			n += DIRLEN;
336 		}
337 		thdr.count = n;
338 		return 0;
339 	}
340 	r = f->ram;
341 	if(off >= r->ndata)
342 		return 0;
343 	r->atime = time(0);
344 	n = cnt;
345 	if(off+n > r->ndata)
346 		n = r->ndata - off;
347 	thdr.data = doread(r, off, n);
348 	thdr.count = n;
349 	return 0;
350 }
351 
352 char*
353 rwrite(Fid *f)
354 {
355 	Ram *r;
356 	ulong off;
357 	int cnt;
358 
359 	r = f->ram;
360 	if (dopermw(f->ram)==0)
361 		return Eperm;
362 	if(r->busy == 0)
363 		return Enotexist;
364 	off = rhdr.offset;
365 	if(r->perm & CHAPPEND)
366 		off = r->ndata;
367 	cnt = rhdr.count;
368 	if(r->qid.path & CHDIR)
369 		return "file is a directory";
370 	if(off > 100*1024*1024)		/* sanity check */
371 		return "write too big";
372 	dowrite(r, rhdr.data, off, cnt);
373 	r->qid.vers++;
374 	r->mtime = time(0);
375 	thdr.count = cnt;
376 	return 0;
377 }
378 
379 char *
380 rclunk(Fid *f)
381 {
382 	if(f->open)
383 		f->ram->open--;
384 	f->busy = 0;
385 	f->open = 0;
386 	f->ram = 0;
387 	return 0;
388 }
389 
390 char *
391 rremove(Fid *f)
392 {
393 	USED(f);
394 	return Eperm;
395 }
396 
397 char *
398 rstat(Fid *f)
399 {
400 	if(f->ram->busy == 0)
401 		return Enotexist;
402 	ramstat(f->ram, thdr.stat);
403 	return 0;
404 }
405 
406 char *
407 rwstat(Fid *f)
408 {
409 	if(f->ram->busy == 0)
410 		return Enotexist;
411 	return Eperm;
412 }
413 
414 void
415 ramstat(Ram *r, char *buf)
416 {
417 	Dir dir;
418 
419 	memmove(dir.name, r->name, NAMELEN);
420 	dir.qid = r->qid;
421 	dir.mode = r->perm;
422 	dir.length = r->ndata;
423 	dir.hlength = 0;
424 	strcpy(dir.uid, r->user);
425 	strcpy(dir.gid, r->group);
426 	dir.atime = r->atime;
427 	dir.mtime = r->mtime;
428 	convD2M(&dir, buf);
429 }
430 
431 Fid *
432 newfid(int fid)
433 {
434 	Fid *f, *ff;
435 
436 	ff = 0;
437 	for(f = fids; f; f = f->next)
438 		if(f->fid == fid)
439 			return f;
440 		else if(!ff && !f->busy)
441 			ff = f;
442 	if(ff){
443 		ff->fid = fid;
444 		ff->open = 0;
445 		ff->busy = 1;
446 	}
447 	f = emalloc(sizeof *f);
448 	f->ram = 0;
449 	f->fid = fid;
450 	f->busy = 1;
451 	f->open = 0;
452 	f->next = fids;
453 	fids = f;
454 	return f;
455 }
456 
457 void
458 io(void)
459 {
460 	char *err;
461 	int n, nerr;
462 	char buf[ERRLEN];
463 
464 	errstr(buf);
465 	for(nerr=0, buf[0]='\0'; nerr<100; nerr++){
466 		/*
467 		 * reading from a pipe or a network device
468 		 * will give an error after a few eof reads
469 		 * however, we cannot tell the difference
470 		 * between a zero-length read and an interrupt
471 		 * on the processes writing to us,
472 		 * so we wait for the error
473 		 */
474 		n = read(mfd[0], mdata, sizeof mdata);
475 		if (n==0)
476 			continue;
477 		if(n < 0){
478 			if (buf[0]=='\0')
479 				errstr(buf);
480 			continue;
481 		}
482 		nerr = 0;
483 		buf[0] = '\0';
484 		if(convM2S(mdata, &rhdr, n) == 0)
485 			continue;
486 
487 /*		fprint(2, "ramfs:%F\n", &rhdr);/**/
488 
489 		thdr.data = mdata + MAXMSG;
490 		if(!fcalls[rhdr.type])
491 			err = "bad fcall type";
492 		else
493 			err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
494 		if(err){
495 			thdr.type = Rerror;
496 			strncpy(thdr.ename, err, ERRLEN);
497 		}else{
498 			thdr.type = rhdr.type + 1;
499 			thdr.fid = rhdr.fid;
500 		}
501 		thdr.tag = rhdr.tag;
502 		n = convS2M(&thdr, mdata);
503 		if(write(mfd[1], mdata, n) != n)
504 			error("mount write");
505 	}
506 	if (buf[0]=='\0' || strncmp(buf, "write to hung", 13)==0)
507 		exits("");
508 	fprint(2, "%s: mount read: %s\n", argv0, buf);
509 	exits(buf);
510 }
511 
512 int
513 perm(Fid *f, Ram *r, int p)
514 {
515 	if (p==Pwrite && dopermw(r)==0)
516 		return 0;
517 	if((p*Pother) & r->perm)
518 		return 1;
519 	if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))
520 		return 1;
521 	if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))
522 		return 1;
523 	return 0;
524 }
525 
526 void
527 error(char *s)
528 {
529 	fprint(2, "%s: %s: ", argv0, s);
530 	perror("");
531 	exits(s);
532 }
533 
534 void *
535 emalloc(ulong n)
536 {
537 	void *p;
538 	p = malloc(n);
539 	if(!p)
540 		error("out of memory");
541 	return p;
542 }
543 
544 void *
545 erealloc(void *p, ulong n)
546 {
547 	p = realloc(p, n);
548 	if(!p)
549 		error("out of memory");
550 	return p;
551 }
552 
553 void
554 usage(void)
555 {
556 	fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);
557 	exits("usage");
558 }
559