Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <authsrv.h>
4 #include <fcall.h>
5 #include "tapefs.h"
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 */
22 Fid * newfid(int);
23 int ramstat(Ram*, uchar*, int);
24 void io(void);
25 void usage(void);
26 int perm(int);
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*);
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 };
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";
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 }
70 void
71 main(int argc, char *argv[])
72 {
73 Ram *r;
74 char *defmnt;
75 int p[2];
76 char buf[TICKREQLEN];
78 fmtinstall('F', fcallfmt);
80 defmnt = "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++;
94 case 'n':
95 newtap++;
96 break;
97 default:
98 usage();
99 }ARGEND
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);
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(post9pservice(p[1], defmnt) < 0) {
141 sprint(buf, "post on `%s' failed", defmnt);
142 error(buf);
145 exits(0);
148 char*
149 rversion(Fid *unused)
151 Fid *f;
153 USED(unused);
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";
166 for(f = fids; f; f = f->next)
167 if(f->busy)
168 rclunk(f);
169 return 0;
172 char*
173 rauth(Fid *unused)
175 USED(unused);
177 return Enoauth;
180 char*
181 rflush(Fid *f)
183 USED(f);
184 return 0;
187 char*
188 rattach(Fid *f)
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;
202 char*
203 rwalk(Fid *f)
205 Fid *nf;
206 Ram *r;
207 char *err;
208 char *name;
209 Ram *dir;
210 int i;
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;
227 thdr.nwqid = 0;
228 err = nil;
229 r = f->ram;
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;
237 if(r->busy == 0){
238 err = Enotexist;
239 break;
241 r->atime = time(0);
242 name = rhdr.wname[i];
243 dir = r;
244 if(!perm(Pexec)){
245 err = Eperm;
246 break;
248 if(strcmp(name, "..") == 0){
249 r = dir->parent;
250 Accept:
251 if(i == MAXWELEM){
252 err = "name too long";
253 break;
255 thdr.wqid[thdr.nwqid++] = r->qid;
256 continue;
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 */
266 if(i==0 && err == nil)
267 err = Enotexist;
270 if(err!=nil || thdr.nwqid<rhdr.nwname){
271 if(nf){
272 nf->busy = 0;
273 nf->open = 0;
274 nf->ram = 0;
276 }else if(thdr.nwqid == rhdr.nwname)
277 f->ram = r;
279 return err;
283 char *
284 ropen(Fid *f)
286 Ram *r;
287 int mode, trunc;
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;
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++;
322 thdr.qid = r->qid;
323 thdr.iounit = messagesize-IOHDRSZ;
324 f->open = 1;
325 r->open++;
326 return 0;
329 char *
330 rcreate(Fid *f)
332 USED(f);
334 return Eperm;
337 char*
338 rread(Fid *f)
340 int i, len;
341 Ram *r;
342 char *buf;
343 uvlong off, end;
344 int n, cnt;
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;
369 thdr.count = n;
370 return 0;
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;
384 char*
385 rwrite(Fid *f)
387 Ram *r;
388 ulong off;
389 int cnt;
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;
411 char *
412 rclunk(Fid *f)
414 if(f->open)
415 f->ram->open--;
416 f->busy = 0;
417 f->open = 0;
418 f->ram = 0;
419 return 0;
422 char *
423 rremove(Fid *f)
425 USED(f);
426 return Eperm;
429 char *
430 rstat(Fid *f)
432 if(f->ram->busy == 0)
433 return Enotexist;
434 thdr.nstat = ramstat(f->ram, thdr.stat, messagesize-IOHDRSZ);
435 return 0;
438 char *
439 rwstat(Fid *f)
441 if(f->ram->busy == 0)
442 return Enotexist;
443 return Eperm;
446 int
447 ramstat(Ram *r, uchar *buf, int nbuf)
449 Dir dir;
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);
463 Fid *
464 newfid(int fid)
466 Fid *f, *ff;
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;
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;
489 void
490 io(void)
492 char *err;
493 int n, nerr;
494 char buf[ERRMAX];
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;
514 nerr = 0;
515 buf[0] = '\0';
516 if(convM2S(mdata, n, &rhdr) != n)
517 error("convert error in convM2S");
519 if(verbose)
520 fprint(2, "tapefs: <=%F\n", &rhdr);/**/
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;
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");
544 if(buf[0]=='\0' || strstr(buf, "hungup"))
545 exits("");
546 fprint(2, "%s: mount read: %s\n", argv0, buf);
547 exits(buf);
550 int
551 perm(int p)
553 if(p==Pwrite)
554 return 0;
555 return 1;
558 void
559 error(char *s)
561 fprint(2, "%s: %s: ", argv0, s);
562 perror("");
563 exits(s);
566 char*
567 estrdup(char *s)
569 char *t;
571 t = emalloc(strlen(s)+1);
572 strcpy(t, s);
573 return t;
576 void *
577 emalloc(ulong n)
579 void *p;
580 p = mallocz(n, 1);
581 if(!p)
582 error("out of memory");
583 return p;
586 void *
587 erealloc(void *p, ulong n)
589 p = realloc(p, n);
590 if(!p)
591 error("out of memory");
592 return p;
595 void
596 usage(void)
598 fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);
599 exits("usage");