Blob
1 #include <u.h>2 #include <libc.h>3 #include <fcall.h>5 int post9pservice(int, char*);7 /*8 * Rather than reading /adm/users, which is a lot of work for9 * a toy program, we assume all groups have the form10 * NNN:user:user:11 * meaning that each user is the leader of his own group.12 */14 enum15 {16 OPERM = 0x3, /* mask of all permission types in open mode */17 Nram = 2048,18 Maxsize = 512*1024*1024,19 Maxfdata = 8192,20 };22 typedef struct Fid Fid;23 typedef struct Ram Ram;25 struct Fid26 {27 short busy;28 short open;29 short rclose;30 int fid;31 Fid *next;32 char *user;33 Ram *ram;34 };36 struct Ram37 {38 short busy;39 short open;40 long parent; /* index in Ram array */41 Qid qid;42 long perm;43 char *name;44 ulong atime;45 ulong mtime;46 char *user;47 char *group;48 char *muid;49 char *data;50 long ndata;51 };53 enum54 {55 Pexec = 1,56 Pwrite = 2,57 Pread = 4,58 Pother = 1,59 Pgroup = 8,60 Powner = 64,61 };63 ulong path; /* incremented for each new file */64 Fid *fids;65 Ram ram[Nram];66 int nram;67 int mfd[2];68 char *user;69 uchar mdata[IOHDRSZ+Maxfdata];70 uchar rdata[Maxfdata]; /* buffer for data in reply */71 uchar statbuf[STATMAX];72 Fcall thdr;73 Fcall rhdr;74 int messagesize = sizeof mdata;76 Fid * newfid(int);77 uint ramstat(Ram*, uchar*, uint);78 void error(char*);79 void io(void);80 void *erealloc(void*, ulong);81 void *emalloc(ulong);82 char *estrdup(char*);83 void usage(void);84 int perm(Fid*, Ram*, int);86 char *rflush(Fid*), *rversion(Fid*), *rauth(Fid*),87 *rattach(Fid*), *rwalk(Fid*),88 *ropen(Fid*), *rcreate(Fid*),89 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),90 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);92 char *(*fcalls[])(Fid*) = {93 [Tversion] rversion,94 [Tflush] rflush,95 [Tauth] rauth,96 [Tattach] rattach,97 [Twalk] rwalk,98 [Topen] ropen,99 [Tcreate] rcreate,100 [Tread] rread,101 [Twrite] rwrite,102 [Tclunk] rclunk,103 [Tremove] rremove,104 [Tstat] rstat,105 [Twstat] rwstat,106 };108 char Eperm[] = "permission denied";109 char Enotdir[] = "not a directory";110 char Enoauth[] = "ramfs: authentication not required";111 char Enotexist[] = "file does not exist";112 char Einuse[] = "file in use";113 char Eexist[] = "file exists";114 char Eisdir[] = "file is a directory";115 char Enotowner[] = "not owner";116 char Eisopen[] = "file already open for I/O";117 char Excl[] = "exclusive use file already open";118 char Ename[] = "illegal name";119 char Eversion[] = "unknown 9P version";120 char Enotempty[] = "directory not empty";121 char Ebadfid[] = "bad fid";123 int debug;124 int private;126 void127 notifyf(void *a, char *s)128 {129 USED(a);130 if(strncmp(s, "interrupt", 9) == 0)131 noted(NCONT);132 noted(NDFLT);133 }135 void136 main(int argc, char *argv[])137 {138 Ram *r;139 char *defmnt;140 int p[2];141 int stdio = 0;142 char *service;144 service = "ramfs";145 defmnt = "/tmp";146 ARGBEGIN{147 case 'D':148 debug = 1;149 break;150 case 'i':151 defmnt = 0;152 stdio = 1;153 mfd[0] = 0;154 mfd[1] = 1;155 break;156 case 's':157 defmnt = 0;158 break;159 case 'm':160 defmnt = ARGF();161 break;162 case 'p':163 private++;164 break;165 case 'S':166 defmnt = 0;167 service = EARGF(usage());168 break;169 default:170 usage();171 }ARGEND173 if(defmnt)174 sysfatal("cannot mount -- not on plan 9");176 if(pipe(p) < 0)177 error("pipe failed");178 if(!stdio){179 mfd[0] = p[0];180 mfd[1] = p[0];181 if(post9pservice(p[1], service) < 0)182 sysfatal("post9pservice %s: %r", service);183 }185 user = getuser();186 notify(notifyf);187 nram = 2;188 r = &ram[0];189 r->busy = 1;190 r->data = 0;191 r->ndata = 0;192 r->perm = DMDIR | 0775;193 r->qid.type = QTDIR;194 r->qid.path = 0;195 r->qid.vers = 0;196 r->parent = 0;197 r->user = user;198 r->group = user;199 r->muid = user;200 r->atime = time(0);201 r->mtime = r->atime;202 r->name = estrdup(".");204 r = &ram[1];205 r->busy = 1;206 r->data = 0;207 r->ndata = 0;208 r->perm = 0666;209 r->qid.type = 0;210 r->qid.path = 1;211 r->qid.vers = 0;212 r->parent = 0;213 r->user = user;214 r->group = user;215 r->muid = user;216 r->atime = time(0);217 r->mtime = r->atime;218 r->name = estrdup("file");220 if(debug)221 fmtinstall('F', fcallfmt);222 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){223 case -1:224 error("fork");225 case 0:226 close(p[1]);227 io();228 break;229 default:230 close(p[0]); /* don't deadlock if child fails */231 }232 exits(0);233 }235 char*236 rversion(Fid *x)237 {238 Fid *f;240 USED(x);241 for(f = fids; f; f = f->next)242 if(f->busy)243 rclunk(f);244 if(thdr.msize > sizeof mdata)245 rhdr.msize = sizeof mdata;246 else247 rhdr.msize = thdr.msize;248 messagesize = rhdr.msize;249 if(strncmp(thdr.version, "9P2000", 6) != 0)250 return Eversion;251 rhdr.version = "9P2000";252 return 0;253 }255 char*256 rauth(Fid *x)257 {258 if(x->busy)259 return Ebadfid;260 return "ramfs: no authentication required";261 }263 char*264 rflush(Fid *f)265 {266 USED(f);267 return 0;268 }270 char*271 rattach(Fid *f)272 {273 /* no authentication! */274 if(f->busy)275 return Ebadfid;276 f->busy = 1;277 f->rclose = 0;278 f->ram = &ram[0];279 rhdr.qid = f->ram->qid;280 if(thdr.uname[0])281 f->user = estrdup(thdr.uname);282 else283 f->user = "none";284 if(strcmp(user, "none") == 0)285 user = f->user;286 return 0;287 }289 char*290 clone(Fid *f, Fid **nf)291 {292 if(!f->busy)293 return Ebadfid;294 if(f->open)295 return Eisopen;296 if(f->ram->busy == 0)297 return Enotexist;298 *nf = newfid(thdr.newfid);299 (*nf)->busy = 1;300 (*nf)->open = 0;301 (*nf)->rclose = 0;302 (*nf)->ram = f->ram;303 (*nf)->user = f->user; /* no ref count; the leakage is minor */304 return 0;305 }307 char*308 rwalk(Fid *f)309 {310 Ram *r, *fram;311 char *name;312 Ram *parent;313 Fid *nf;314 char *err;315 ulong t;316 int i;318 if(!f->busy)319 return Ebadfid;320 err = nil;321 nf = nil;322 rhdr.nwqid = 0;323 if(thdr.newfid != thdr.fid){324 err = clone(f, &nf);325 if(err)326 return err;327 f = nf; /* walk the new fid */328 }329 fram = f->ram;330 if(thdr.nwname > 0){331 t = time(0);332 for(i=0; i<thdr.nwname && i<MAXWELEM; i++){333 if((fram->qid.type & QTDIR) == 0){334 err = Enotdir;335 break;336 }337 if(fram->busy == 0){338 err = Enotexist;339 break;340 }341 fram->atime = t;342 name = thdr.wname[i];343 if(strcmp(name, ".") == 0){344 Found:345 rhdr.nwqid++;346 rhdr.wqid[i] = fram->qid;347 continue;348 }349 parent = &ram[fram->parent];350 if(!perm(f, parent, Pexec)){351 err = Eperm;352 break;353 }354 if(strcmp(name, "..") == 0){355 fram = parent;356 goto Found;357 }358 for(r=ram; r < &ram[nram]; r++)359 if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){360 fram = r;361 goto Found;362 }363 break;364 }365 if(i==0 && err == nil)366 err = Enotexist;367 }368 if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){369 /* clunk the new fid, which is the one we walked */370 fprint(2, "f %d zero busy\n", f->fid);371 f->busy = 0;372 f->ram = nil;373 }374 if(rhdr.nwqid == thdr.nwname) /* update the fid after a successful walk */375 f->ram = fram;376 assert(f->busy);377 return err;378 }380 char *381 ropen(Fid *f)382 {383 Ram *r;384 int mode, trunc;386 if(!f->busy)387 return Ebadfid;388 if(f->open)389 return Eisopen;390 r = f->ram;391 if(r->busy == 0)392 return Enotexist;393 if(r->perm & DMEXCL)394 if(r->open)395 return Excl;396 mode = thdr.mode;397 if(r->qid.type & QTDIR){398 if(mode != OREAD)399 return Eperm;400 rhdr.qid = r->qid;401 return 0;402 }403 if(mode & ORCLOSE){404 /* can't remove root; must be able to write parent */405 if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite))406 return Eperm;407 f->rclose = 1;408 }409 trunc = mode & OTRUNC;410 mode &= OPERM;411 if(mode==OWRITE || mode==ORDWR || trunc)412 if(!perm(f, r, Pwrite))413 return Eperm;414 if(mode==OREAD || mode==ORDWR)415 if(!perm(f, r, Pread))416 return Eperm;417 if(mode==OEXEC)418 if(!perm(f, r, Pexec))419 return Eperm;420 if(trunc && (r->perm&DMAPPEND)==0){421 r->ndata = 0;422 if(r->data)423 free(r->data);424 r->data = 0;425 r->qid.vers++;426 }427 rhdr.qid = r->qid;428 rhdr.iounit = messagesize-IOHDRSZ;429 f->open = 1;430 r->open++;431 return 0;432 }434 char *435 rcreate(Fid *f)436 {437 Ram *r;438 char *name;439 long parent, prm;441 if(!f->busy)442 return Ebadfid;443 if(f->open)444 return Eisopen;445 if(f->ram->busy == 0)446 return Enotexist;447 parent = f->ram - ram;448 if((f->ram->qid.type&QTDIR) == 0)449 return Enotdir;450 /* must be able to write parent */451 if(!perm(f, f->ram, Pwrite))452 return Eperm;453 prm = thdr.perm;454 name = thdr.name;455 if(strcmp(name, ".")==0 || strcmp(name, "..")==0)456 return Ename;457 for(r=ram; r<&ram[nram]; r++)458 if(r->busy && parent==r->parent)459 if(strcmp((char*)name, r->name)==0)460 return Einuse;461 for(r=ram; r->busy; r++)462 if(r == &ram[Nram-1])463 return "no free ram resources";464 r->busy = 1;465 r->qid.path = ++path;466 r->qid.vers = 0;467 if(prm & DMDIR)468 r->qid.type |= QTDIR;469 r->parent = parent;470 free(r->name);471 r->name = estrdup(name);472 r->user = f->user;473 r->group = f->ram->group;474 r->muid = f->ram->muid;475 if(prm & DMDIR)476 prm = (prm&~0777) | (f->ram->perm&prm&0777);477 else478 prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666);479 r->perm = prm;480 r->ndata = 0;481 if(r-ram >= nram)482 nram = r - ram + 1;483 r->atime = time(0);484 r->mtime = r->atime;485 f->ram->mtime = r->atime;486 f->ram = r;487 rhdr.qid = r->qid;488 rhdr.iounit = messagesize-IOHDRSZ;489 f->open = 1;490 if(thdr.mode & ORCLOSE)491 f->rclose = 1;492 r->open++;493 return 0;494 }496 char*497 rread(Fid *f)498 {499 Ram *r;500 uchar *buf;501 long off;502 int n, m, cnt;504 if(!f->busy)505 return Ebadfid;506 if(f->ram->busy == 0)507 return Enotexist;508 n = 0;509 rhdr.count = 0;510 off = thdr.offset;511 buf = rdata;512 cnt = thdr.count;513 if(cnt > messagesize) /* shouldn't happen, anyway */514 cnt = messagesize;515 if(f->ram->qid.type & QTDIR){516 for(r=ram+1; off > 0; r++){517 if(r->busy && r->parent==f->ram-ram)518 off -= ramstat(r, statbuf, sizeof statbuf);519 if(r == &ram[nram-1])520 return 0;521 }522 for(; r<&ram[nram] && n < cnt; r++){523 if(!r->busy || r->parent!=f->ram-ram)524 continue;525 m = ramstat(r, buf+n, cnt-n);526 if(m == 0)527 break;528 n += m;529 }530 rhdr.data = (char*)rdata;531 rhdr.count = n;532 return 0;533 }534 r = f->ram;535 if(off >= r->ndata)536 return 0;537 r->atime = time(0);538 n = cnt;539 if(off+n > r->ndata)540 n = r->ndata - off;541 rhdr.data = r->data+off;542 rhdr.count = n;543 return 0;544 }546 char*547 rwrite(Fid *f)548 {549 Ram *r;550 ulong off;551 int cnt;553 r = f->ram;554 if(!f->busy)555 return Ebadfid;556 if(r->busy == 0)557 return Enotexist;558 off = thdr.offset;559 if(r->perm & DMAPPEND)560 off = r->ndata;561 cnt = thdr.count;562 if(r->qid.type & QTDIR)563 return Eisdir;564 if(off+cnt >= Maxsize) /* sanity check */565 return "write too big";566 if(off+cnt > r->ndata)567 r->data = erealloc(r->data, off+cnt);568 if(off > r->ndata)569 memset(r->data+r->ndata, 0, off-r->ndata);570 if(off+cnt > r->ndata)571 r->ndata = off+cnt;572 memmove(r->data+off, thdr.data, cnt);573 r->qid.vers++;574 r->mtime = time(0);575 rhdr.count = cnt;576 return 0;577 }579 static int580 emptydir(Ram *dr)581 {582 long didx = dr - ram;583 Ram *r;585 for(r=ram; r<&ram[nram]; r++)586 if(r->busy && didx==r->parent)587 return 0;588 return 1;589 }591 char *592 realremove(Ram *r)593 {594 if(r->qid.type & QTDIR && !emptydir(r))595 return Enotempty;596 r->ndata = 0;597 if(r->data)598 free(r->data);599 r->data = 0;600 r->parent = 0;601 memset(&r->qid, 0, sizeof r->qid);602 free(r->name);603 r->name = nil;604 r->busy = 0;605 return nil;606 }608 char *609 rclunk(Fid *f)610 {611 char *e = nil;613 if(f->open)614 f->ram->open--;615 if(f->rclose)616 e = realremove(f->ram);617 fprint(2, "clunk fid %d busy=%d\n", f->fid, f->busy);618 fprint(2, "f %d zero busy\n", f->fid);619 f->busy = 0;620 f->open = 0;621 f->ram = 0;622 return e;623 }625 char *626 rremove(Fid *f)627 {628 Ram *r;630 if(f->open)631 f->ram->open--;632 fprint(2, "f %d zero busy\n", f->fid);633 f->busy = 0;634 f->open = 0;635 r = f->ram;636 f->ram = 0;637 if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite))638 return Eperm;639 ram[r->parent].mtime = time(0);640 return realremove(r);641 }643 char *644 rstat(Fid *f)645 {646 if(!f->busy)647 return Ebadfid;648 if(f->ram->busy == 0)649 return Enotexist;650 rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf);651 rhdr.stat = statbuf;652 return 0;653 }655 char *656 rwstat(Fid *f)657 {658 Ram *r, *s;659 Dir dir;661 if(!f->busy)662 return Ebadfid;663 if(f->ram->busy == 0)664 return Enotexist;665 convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf);666 r = f->ram;668 /*669 * To change length, must have write permission on file.670 */671 if(dir.length!=~0 && dir.length!=r->ndata){672 if(!perm(f, r, Pwrite))673 return Eperm;674 }676 /*677 * To change name, must have write permission in parent678 * and name must be unique.679 */680 if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){681 if(!perm(f, &ram[r->parent], Pwrite))682 return Eperm;683 for(s=ram; s<&ram[nram]; s++)684 if(s->busy && s->parent==r->parent)685 if(strcmp(dir.name, s->name)==0)686 return Eexist;687 }689 /*690 * To change mode, must be owner or group leader.691 * Because of lack of users file, leader=>group itself.692 */693 if(dir.mode!=~0 && r->perm!=dir.mode){694 if(strcmp(f->user, r->user) != 0)695 if(strcmp(f->user, r->group) != 0)696 return Enotowner;697 }699 /*700 * To change group, must be owner and member of new group,701 * or leader of current group and leader of new group.702 * Second case cannot happen, but we check anyway.703 */704 if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){705 if(strcmp(f->user, r->user) == 0)706 // if(strcmp(f->user, dir.gid) == 0)707 goto ok;708 if(strcmp(f->user, r->group) == 0)709 if(strcmp(f->user, dir.gid) == 0)710 goto ok;711 return Enotowner;712 ok:;713 }715 /* all ok; do it */716 if(dir.mode != ~0){717 dir.mode &= ~DMDIR; /* cannot change dir bit */718 dir.mode |= r->perm&DMDIR;719 r->perm = dir.mode;720 }721 if(dir.name[0] != '\0'){722 free(r->name);723 r->name = estrdup(dir.name);724 }725 if(dir.gid[0] != '\0')726 r->group = estrdup(dir.gid);727 if(dir.length!=~0 && dir.length!=r->ndata){728 r->data = erealloc(r->data, dir.length);729 if(r->ndata < dir.length)730 memset(r->data+r->ndata, 0, dir.length-r->ndata);731 r->ndata = dir.length;732 }733 ram[r->parent].mtime = time(0);734 return 0;735 }737 uint738 ramstat(Ram *r, uchar *buf, uint nbuf)739 {740 int n;741 Dir dir;743 dir.name = r->name;744 dir.qid = r->qid;745 dir.mode = r->perm;746 dir.length = r->ndata;747 dir.uid = r->user;748 dir.gid = r->group;749 dir.muid = r->muid;750 dir.atime = r->atime;751 dir.mtime = r->mtime;752 n = convD2M(&dir, buf, nbuf);753 if(n > 2)754 return n;755 return 0;756 }758 Fid *759 newfid(int fid)760 {761 Fid *f, *ff;763 ff = 0;764 for(f = fids; f; f = f->next)765 if(f->fid == fid){766 fprint(2, "got fid %d busy=%d\n", fid, f->busy);767 return f;768 }769 else if(!ff && !f->busy)770 ff = f;771 if(ff){772 ff->fid = fid;773 return ff;774 }775 f = emalloc(sizeof *f);776 f->ram = nil;777 f->fid = fid;778 f->next = fids;779 fids = f;780 return f;781 }783 void784 io(void)785 {786 char *err, buf[20];787 int n, pid, ctl;789 pid = getpid();790 if(private){791 snprint(buf, sizeof buf, "/proc/%d/ctl", pid);792 ctl = open(buf, OWRITE);793 if(ctl < 0){794 fprint(2, "can't protect ramfs\n");795 }else{796 fprint(ctl, "noswap\n");797 fprint(ctl, "private\n");798 close(ctl);799 }800 }802 for(;;){803 /*804 * reading from a pipe or a network device805 * will give an error after a few eof reads.806 * however, we cannot tell the difference807 * between a zero-length read and an interrupt808 * on the processes writing to us,809 * so we wait for the error.810 */811 n = read9pmsg(mfd[0], mdata, messagesize);812 if(n < 0)813 error("mount read");814 if(n == 0)815 error("mount eof");816 if(convM2S(mdata, n, &thdr) == 0)817 continue;819 if(debug)820 fprint(2, "ramfs %d:<-%F\n", pid, &thdr);822 if(!fcalls[thdr.type])823 err = "bad fcall type";824 else825 err = (*fcalls[thdr.type])(newfid(thdr.fid));826 if(err){827 rhdr.type = Rerror;828 rhdr.ename = err;829 }else{830 rhdr.type = thdr.type + 1;831 rhdr.fid = thdr.fid;832 }833 rhdr.tag = thdr.tag;834 if(debug)835 fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/836 n = convS2M(&rhdr, mdata, messagesize);837 if(n == 0)838 error("convS2M error on write");839 if(write(mfd[1], mdata, n) != n)840 error("mount write");841 }842 }844 int845 perm(Fid *f, Ram *r, int p)846 {847 if((p*Pother) & r->perm)848 return 1;849 if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))850 return 1;851 if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))852 return 1;853 return 0;854 }856 void857 error(char *s)858 {859 fprint(2, "%s: %s: %r\n", argv0, s);860 exits(s);861 }863 void *864 emalloc(ulong n)865 {866 void *p;868 p = malloc(n);869 if(!p)870 error("out of memory");871 memset(p, 0, n);872 return p;873 }875 void *876 erealloc(void *p, ulong n)877 {878 p = realloc(p, n);879 if(!p)880 error("out of memory");881 return p;882 }884 char *885 estrdup(char *q)886 {887 char *p;888 int n;890 n = strlen(q)+1;891 p = malloc(n);892 if(!p)893 error("out of memory");894 memmove(p, q, n);895 return p;896 }898 void899 usage(void)900 {901 fprint(2, "usage: %s [-is] [-m mountpoint]\n", argv0);902 exits("usage");903 }