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 blocksize; /* for 32v */20 int verbose;21 int newtap; /* tap with time in sec */23 Fid * newfid(int);24 int ramstat(Ram*, uchar*, int);25 void io(void);26 void usage(void);27 int perm(int);29 char *rflush(Fid*), *rversion(Fid*), *rauth(Fid*),30 *rattach(Fid*), *rwalk(Fid*),31 *ropen(Fid*), *rcreate(Fid*),32 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),33 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);35 char *(*fcalls[Tmax])(Fid*);36 void37 initfcalls(void)38 {39 fcalls[Tflush]= rflush;40 fcalls[Tversion]= rversion;41 fcalls[Tauth]= rauth;42 fcalls[Tattach]= rattach;43 fcalls[Twalk]= rwalk;44 fcalls[Topen]= ropen;45 fcalls[Tcreate]= rcreate;46 fcalls[Tread]= rread;47 fcalls[Twrite]= rwrite;48 fcalls[Tclunk]= rclunk;49 fcalls[Tremove]= rremove;50 fcalls[Tstat]= rstat;51 fcalls[Twstat]= rwstat;52 }54 char Eperm[] = "permission denied";55 char Enotdir[] = "not a directory";56 char Enoauth[] = "tapefs: authentication not required";57 char Enotexist[] = "file does not exist";58 char Einuse[] = "file in use";59 char Eexist[] = "file exists";60 char Enotowner[] = "not owner";61 char Eisopen[] = "file already open for I/O";62 char Excl[] = "exclusive use file already open";63 char Ename[] = "illegal name";65 void66 notifyf(void *a, char *s)67 {68 USED(a);69 if(strncmp(s, "interrupt", 9) == 0)70 noted(NCONT);71 noted(NDFLT);72 }74 void75 main(int argc, char *argv[])76 {77 Ram *r;78 char *defmnt;79 int p[2];80 char buf[TICKREQLEN];82 fmtinstall('F', fcallfmt);83 initfcalls();85 defmnt = "tapefs";86 ARGBEGIN{87 case 'm':88 defmnt = ARGF();89 break;90 case 'p': /* password file */91 uidmap = getpass(ARGF());92 break;93 case 'g': /* group file */94 gidmap = getpass(ARGF());95 break;96 case 'v':97 verbose++;99 case 'n':100 newtap++;101 break;102 default:103 usage();104 }ARGEND106 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);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(post9pservice(p[1], defmnt) < 0) {146 sprint(buf, "post on `%s' failed", defmnt);147 error(buf);148 }149 }150 exits(0);151 }153 char*154 rversion(Fid *unused)155 {156 Fid *f;158 USED(unused);160 if(rhdr.msize < 256)161 return "version: message too small";162 if(rhdr.msize > messagesize)163 rhdr.msize = messagesize;164 else165 messagesize = rhdr.msize;166 thdr.msize = messagesize;167 if(strncmp(rhdr.version, "9P2000", 6) != 0)168 return "unrecognized 9P version";169 thdr.version = "9P2000";171 for(f = fids; f; f = f->next)172 if(f->busy)173 rclunk(f);174 return 0;175 }177 char*178 rauth(Fid *unused)179 {180 USED(unused);182 return Enoauth;183 }185 char*186 rflush(Fid *f)187 {188 USED(f);189 return 0;190 }192 char*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 else203 f->user = "none";204 return 0;205 }207 char*208 rwalk(Fid *f)209 {210 Fid *nf;211 Ram *r;212 char *err;213 char *name;214 Ram *dir;215 int i;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 }232 thdr.nwqid = 0;233 err = nil;234 r = f->ram;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 }271 if(i==0 && err == nil)272 err = Enotexist;273 }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;284 return err;286 }288 char *289 ropen(Fid *f)290 {291 Ram *r;292 int mode, trunc;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 }334 char *335 rcreate(Fid *f)336 {337 USED(f);339 return Eperm;340 }342 char*343 rread(Fid *f)344 {345 int i, len;346 Ram *r;347 char *buf;348 uvlong off, end;349 int n, cnt;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 }389 char*390 rwrite(Fid *f)391 {392 Ram *r;393 ulong off;394 int cnt;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 }416 char *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 }427 char *428 rremove(Fid *f)429 {430 USED(f);431 return Eperm;432 }434 char *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 }443 char *444 rwstat(Fid *f)445 {446 if(f->ram->busy == 0)447 return Enotexist;448 return Eperm;449 }451 int452 ramstat(Ram *r, uchar *buf, int nbuf)453 {454 Dir dir;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 }468 Fid *469 newfid(int fid)470 {471 Fid *f, *ff;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 }494 void495 io(void)496 {497 char *err;498 int n, nerr;499 char buf[ERRMAX];501 errstr(buf, sizeof buf);502 for(nerr=0, buf[0]='\0'; nerr<100; nerr++){503 /*504 * reading from a pipe or a network device505 * will give an error after a few eof reads506 * however, we cannot tell the difference507 * between a zero-length read and an interrupt508 * on the processes writing to us,509 * so we wait for the error510 */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");524 if(verbose)525 fprint(2, "tapefs: <=%F\n", &rhdr);/**/527 thdr.data = (char*)mdata + IOHDRSZ;528 thdr.stat = mdata + IOHDRSZ;529 if(!fcalls[rhdr.type])530 err = "bad fcall type";531 else532 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 }555 int556 perm(int p)557 {558 if(p==Pwrite)559 return 0;560 return 1;561 }563 void564 error(char *s)565 {566 fprint(2, "%s: %s: ", argv0, s);567 perror("");568 exits(s);569 }571 char*572 estrdup(char *s)573 {574 char *t;576 t = emalloc(strlen(s)+1);577 strcpy(t, s);578 return t;579 }581 void *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 }591 void *592 erealloc(void *p, ulong n)593 {594 p = realloc(p, n);595 if(!p)596 error("out of memory");597 return p;598 }600 void601 usage(void)602 {603 fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);604 exits("usage");605 }