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, *defsrv;79 int p[2];80 char buf[TICKREQLEN];82 fmtinstall('F', fcallfmt);83 initfcalls();85 defmnt = nil;86 defsrv = nil;87 ARGBEGIN{88 case 'm':89 defmnt = ARGF();90 break;91 case 's':92 defsrv = ARGF();93 break;94 case 'p': /* password file */95 uidmap = getpass(ARGF());96 break;97 case 'g': /* group file */98 gidmap = getpass(ARGF());99 break;100 case 'v':101 verbose++;103 case 'n':104 newtap++;105 break;106 default:107 usage();108 }ARGEND110 if(argc==0)111 error("no file to mount");112 user = getuser();113 if(user == nil)114 user = "dmr";115 ram = r = (Ram *)emalloc(sizeof(Ram));116 r->busy = 1;117 r->data = 0;118 r->ndata = 0;119 r->perm = DMDIR | 0775;120 r->qid.path = 0;121 r->qid.vers = 0;122 r->qid.type = QTDIR;123 r->parent = 0;124 r->child = 0;125 r->next = 0;126 r->user = user;127 r->group = user;128 r->atime = time(0);129 r->mtime = r->atime;130 r->replete = 0;131 r->name = estrdup(".");132 populate(argv[0]);133 r->replete |= replete;134 if(pipe(p) < 0)135 error("pipe failed");136 mfd[0] = mfd[1] = p[0];137 notify(notifyf);139 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){140 case -1:141 error("fork");142 case 0:143 close(p[1]);144 notify(notifyf);145 io();146 break;147 default:148 close(p[0]); /* don't deadlock if child fails */149 if(post9pservice(p[1], defsrv, defmnt) < 0){150 sprint(buf, "post9pservice: %r");151 error(buf);152 }153 }154 exits(0);155 }157 char*158 rversion(Fid *unused)159 {160 Fid *f;162 USED(unused);164 if(rhdr.msize < 256)165 return "version: message too small";166 if(rhdr.msize > messagesize)167 rhdr.msize = messagesize;168 else169 messagesize = rhdr.msize;170 thdr.msize = messagesize;171 if(strncmp(rhdr.version, "9P2000", 6) != 0)172 return "unrecognized 9P version";173 thdr.version = "9P2000";175 for(f = fids; f; f = f->next)176 if(f->busy)177 rclunk(f);178 return 0;179 }181 char*182 rauth(Fid *unused)183 {184 USED(unused);186 return Enoauth;187 }189 char*190 rflush(Fid *f)191 {192 USED(f);193 return 0;194 }196 char*197 rattach(Fid *f)198 {199 /* no authentication! */200 f->busy = 1;201 f->rclose = 0;202 f->ram = ram;203 thdr.qid = f->ram->qid;204 if(rhdr.uname[0])205 f->user = strdup(rhdr.uname);206 else207 f->user = "none";208 return 0;209 }211 char*212 rwalk(Fid *f)213 {214 Fid *nf;215 Ram *r;216 char *err;217 char *name;218 Ram *dir;219 int i;221 nf = nil;222 if(f->ram->busy == 0)223 return Enotexist;224 if(f->open)225 return Eisopen;226 if(rhdr.newfid != rhdr.fid){227 nf = newfid(rhdr.newfid);228 nf->busy = 1;229 nf->open = 0;230 nf->rclose = 0;231 nf->ram = f->ram;232 nf->user = f->user; /* no ref count; the leakage is minor */233 f = nf;234 }236 thdr.nwqid = 0;237 err = nil;238 r = f->ram;240 if(rhdr.nwname > 0){241 for(i=0; i<rhdr.nwname; i++){242 if((r->qid.type & QTDIR) == 0){243 err = Enotdir;244 break;245 }246 if(r->busy == 0){247 err = Enotexist;248 break;249 }250 r->atime = time(0);251 name = rhdr.wname[i];252 dir = r;253 if(!perm(Pexec)){254 err = Eperm;255 break;256 }257 if(strcmp(name, "..") == 0){258 r = dir->parent;259 Accept:260 if(i == MAXWELEM){261 err = "name too long";262 break;263 }264 thdr.wqid[thdr.nwqid++] = r->qid;265 continue;266 }267 if(!dir->replete)268 popdir(dir);269 for(r=dir->child; r; r=r->next)270 if(r->busy && strcmp(name, r->name)==0)271 goto Accept;272 break; /* file not found */273 }275 if(i==0 && err == nil)276 err = Enotexist;277 }279 if(err!=nil || thdr.nwqid<rhdr.nwname){280 if(nf){281 nf->busy = 0;282 nf->open = 0;283 nf->ram = 0;284 }285 }else if(thdr.nwqid == rhdr.nwname)286 f->ram = r;288 return err;290 }292 char *293 ropen(Fid *f)294 {295 Ram *r;296 int mode, trunc;298 if(f->open)299 return Eisopen;300 r = f->ram;301 if(r->busy == 0)302 return Enotexist;303 if(r->perm & DMEXCL)304 if(r->open)305 return Excl;306 mode = rhdr.mode;307 if(r->qid.type & QTDIR){308 if(mode != OREAD)309 return Eperm;310 thdr.qid = r->qid;311 return 0;312 }313 if(mode & ORCLOSE)314 return Eperm;315 trunc = mode & OTRUNC;316 mode &= OPERM;317 if(mode==OWRITE || mode==ORDWR || trunc)318 if(!perm(Pwrite))319 return Eperm;320 if(mode==OREAD || mode==ORDWR)321 if(!perm(Pread))322 return Eperm;323 if(mode==OEXEC)324 if(!perm(Pexec))325 return Eperm;326 if(trunc && (r->perm&DMAPPEND)==0){327 r->ndata = 0;328 dotrunc(r);329 r->qid.vers++;330 }331 thdr.qid = r->qid;332 thdr.iounit = messagesize-IOHDRSZ;333 f->open = 1;334 r->open++;335 return 0;336 }338 char *339 rcreate(Fid *f)340 {341 USED(f);343 return Eperm;344 }346 char*347 rread(Fid *f)348 {349 int i, len;350 Ram *r;351 char *buf;352 uvlong off, end;353 int n, cnt;355 if(f->ram->busy == 0)356 return Enotexist;357 n = 0;358 thdr.count = 0;359 off = rhdr.offset;360 end = rhdr.offset + rhdr.count;361 cnt = rhdr.count;362 if(cnt > messagesize-IOHDRSZ)363 cnt = messagesize-IOHDRSZ;364 buf = thdr.data;365 if(f->ram->qid.type & QTDIR){366 if(!f->ram->replete)367 popdir(f->ram);368 for(i=0,r=f->ram->child; r!=nil && i<end; r=r->next){369 if(!r->busy)370 continue;371 len = ramstat(r, (uchar*)buf+n, cnt-n);372 if(len <= BIT16SZ)373 break;374 if(i >= off)375 n += len;376 i += len;377 }378 thdr.count = n;379 return 0;380 }381 r = f->ram;382 if(off >= r->ndata)383 return 0;384 r->atime = time(0);385 n = cnt;386 if(off+n > r->ndata)387 n = r->ndata - off;388 thdr.data = doread(r, off, n);389 thdr.count = n;390 return 0;391 }393 char*394 rwrite(Fid *f)395 {396 Ram *r;397 ulong off;398 int cnt;400 r = f->ram;401 if(dopermw(f->ram)==0)402 return Eperm;403 if(r->busy == 0)404 return Enotexist;405 off = rhdr.offset;406 if(r->perm & DMAPPEND)407 off = r->ndata;408 cnt = rhdr.count;409 if(r->qid.type & QTDIR)410 return "file is a directory";411 if(off > 100*1024*1024) /* sanity check */412 return "write too big";413 dowrite(r, rhdr.data, off, cnt);414 r->qid.vers++;415 r->mtime = time(0);416 thdr.count = cnt;417 return 0;418 }420 char *421 rclunk(Fid *f)422 {423 if(f->open)424 f->ram->open--;425 f->busy = 0;426 f->open = 0;427 f->ram = 0;428 return 0;429 }431 char *432 rremove(Fid *f)433 {434 USED(f);435 return Eperm;436 }438 char *439 rstat(Fid *f)440 {441 if(f->ram->busy == 0)442 return Enotexist;443 thdr.nstat = ramstat(f->ram, thdr.stat, messagesize-IOHDRSZ);444 return 0;445 }447 char *448 rwstat(Fid *f)449 {450 if(f->ram->busy == 0)451 return Enotexist;452 return Eperm;453 }455 int456 ramstat(Ram *r, uchar *buf, int nbuf)457 {458 Dir dir;460 dir.name = r->name;461 dir.qid = r->qid;462 dir.mode = r->perm;463 dir.length = r->ndata;464 dir.uid = r->user;465 dir.gid = r->group;466 dir.muid = r->user;467 dir.atime = r->atime;468 dir.mtime = r->mtime;469 return convD2M(&dir, buf, nbuf);470 }472 Fid *473 newfid(int fid)474 {475 Fid *f, *ff;477 ff = 0;478 for(f = fids; f; f = f->next)479 if(f->fid == fid)480 return f;481 else if(!ff && !f->busy)482 ff = f;483 if(ff){484 ff->fid = fid;485 ff->open = 0;486 ff->busy = 1;487 }488 f = emalloc(sizeof *f);489 f->ram = 0;490 f->fid = fid;491 f->busy = 1;492 f->open = 0;493 f->next = fids;494 fids = f;495 return f;496 }498 void499 io(void)500 {501 char *err;502 int n, nerr;503 char buf[ERRMAX];505 errstr(buf, sizeof buf);506 for(nerr=0, buf[0]='\0'; nerr<100; nerr++){507 /*508 * reading from a pipe or a network device509 * will give an error after a few eof reads510 * however, we cannot tell the difference511 * between a zero-length read and an interrupt512 * on the processes writing to us,513 * so we wait for the error514 */515 n = read9pmsg(mfd[0], mdata, sizeof mdata);516 if(n==0)517 continue;518 if(n < 0){519 if(buf[0]=='\0')520 errstr(buf, sizeof buf);521 continue;522 }523 nerr = 0;524 buf[0] = '\0';525 if(convM2S(mdata, n, &rhdr) != n)526 error("convert error in convM2S");528 if(verbose)529 fprint(2, "tapefs: <=%F\n", &rhdr);/**/531 thdr.data = (char*)mdata + IOHDRSZ;532 thdr.stat = mdata + IOHDRSZ;533 if(!fcalls[rhdr.type])534 err = "bad fcall type";535 else536 err = (*fcalls[rhdr.type])(newfid(rhdr.fid));537 if(err){538 thdr.type = Rerror;539 thdr.ename = err;540 }else{541 thdr.type = rhdr.type + 1;542 thdr.fid = rhdr.fid;543 }544 thdr.tag = rhdr.tag;545 n = convS2M(&thdr, mdata, messagesize);546 if(n <= 0)547 error("convert error in convS2M");548 if(verbose)549 fprint(2, "tapefs: =>%F\n", &thdr);/**/550 if(write(mfd[1], mdata, n) != n)551 error("mount write");552 }553 if(buf[0]=='\0' || strstr(buf, "hungup"))554 exits("");555 fprint(2, "%s: mount read: %s\n", argv0, buf);556 exits(buf);557 }559 int560 perm(int p)561 {562 if(p==Pwrite)563 return 0;564 return 1;565 }567 void568 error(char *s)569 {570 fprint(2, "%s: %s: ", argv0, s);571 perror("");572 exits(s);573 }575 char*576 estrdup(char *s)577 {578 char *t;580 t = emalloc(strlen(s)+1);581 strcpy(t, s);582 return t;583 }585 void *586 emalloc(ulong n)587 {588 void *p;589 p = mallocz(n, 1);590 if(!p)591 error("out of memory");592 return p;593 }595 void *596 erealloc(void *p, ulong n)597 {598 p = realloc(p, n);599 if(!p)600 error("out of memory");601 return p;602 }604 void605 usage(void)606 {607 fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);608 exits("usage");609 }