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