9 * Rather than reading /adm/users, which is a lot of work for
10 * a toy program, we assume all groups have the form
12 * meaning that each user is the leader of his own group.
17 OPERM = 0x3, /* mask of all permission types in open mode */
19 Maxsize = 768*1024*1024,
21 Maxulong= (1ULL << 32) - 1,
24 typedef struct Fid Fid;
25 typedef struct Ram Ram;
42 long parent; /* index in Ram array */
65 ulong path; /* incremented for each new file */
71 uchar mdata[IOHDRSZ+Maxfdata];
72 uchar rdata[Maxfdata]; /* buffer for data in reply */
73 uchar statbuf[STATMAX];
76 int messagesize = sizeof mdata;
79 uint ramstat(Ram*, uchar*, uint);
82 void *erealloc(void*, ulong);
86 int perm(Fid*, Ram*, int);
88 char *rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
89 *rattach(Fid*), *rwalk(Fid*),
90 *ropen(Fid*), *rcreate(Fid*),
91 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
92 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
110 char *(*fcalls[])(Fid*) = {
126 char Eperm[] = "permission denied";
127 char Enotdir[] = "not a directory";
128 char Enoauth[] = "ramfs: authentication not required";
129 char Enotexist[] = "file does not exist";
130 char Einuse[] = "file in use";
131 char Eexist[] = "file exists";
132 char Eisdir[] = "file is a directory";
133 char Enotowner[] = "not owner";
134 char Eisopen[] = "file already open for I/O";
135 char Excl[] = "exclusive use file already open";
136 char Ename[] = "illegal name";
137 char Eversion[] = "unknown 9P version";
138 char Enotempty[] = "directory not empty";
143 static int memlim = 1;
146 notifyf(void *a, char *s)
149 if(strncmp(s, "interrupt", 9) == 0)
155 main(int argc, char *argv[])
158 char *defmnt, *service;
175 defmnt = EARGF(usage());
184 memlim = 0; /* unlimited memory consumption */
185 mainmem->maxsize = (uintptr)~0;
192 service = EARGF(usage());
195 mountflags |= MBEFORE;
198 mountflags |= MCREATE;
201 mountflags |= MAFTER;
207 mountflags = MREPL | MCREATE;
210 error("pipe failed");
216 snprint(buf, sizeof buf, "#s/%s", service);
217 fd = create(buf, OWRITE|ORCLOSE, 0666);
219 error("create failed");
220 sprint(buf, "%d", p[1]);
221 if(write(fd, buf, strlen(buf)) < 0)
222 error("writing service file");
233 r->perm = DMDIR | 0775;
243 r->name = estrdup(".");
246 fmtinstall('F', fcallfmt);
247 fmtinstall('M', dirmodefmt);
249 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
257 close(p[0]); /* don't deadlock if child fails */
258 if(defmnt && mount(p[1], -1, defmnt, mountflags, "") < 0)
259 error("mount failed");
269 for(f = fids; f; f = f->next)
272 if(thdr.msize > sizeof mdata)
273 rhdr.msize = sizeof mdata;
275 rhdr.msize = thdr.msize;
276 messagesize = rhdr.msize;
277 if(strncmp(thdr.version, "9P2000", 6) != 0)
279 rhdr.version = "9P2000";
286 return "ramfs: no authentication required";
299 /* no authentication! */
303 rhdr.qid = f->ram->qid;
305 f->user = estrdup(thdr.uname);
308 if(strcmp(user, "none") == 0)
314 clone(Fid *f, Fid **nf)
318 if(f->ram->busy == 0)
320 *nf = newfid(thdr.newfid);
325 (*nf)->user = f->user; /* no ref count; the leakage is minor */
343 if(thdr.newfid != thdr.fid){
347 f = nf; /* walk the new fid */
352 for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
353 if((fram->qid.type & QTDIR) == 0){
362 name = thdr.wname[i];
363 if(strcmp(name, ".") == 0){
366 rhdr.wqid[i] = fram->qid;
369 parent = &ram[fram->parent];
370 if(!perm(f, parent, Pexec)){
374 if(strcmp(name, "..") == 0){
378 for(r=ram; r < &ram[nram]; r++)
379 if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){
385 if(i==0 && err == nil)
388 if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
389 /* clunk the new fid, which is the one we walked */
394 err = nil; /* didn't get everything in 9P2000 right! */
395 if(rhdr.nwqid == thdr.nwname) /* update the fid after a successful walk */
415 if(r->qid.type & QTDIR){
422 /* can't remove root; must be able to write parent */
423 if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite))
427 trunc = mode & OTRUNC;
429 if(mode==OWRITE || mode==ORDWR || trunc)
430 if(!perm(f, r, Pwrite))
432 if(mode==OREAD || mode==ORDWR)
433 if(!perm(f, r, Pread))
436 if(!perm(f, r, Pexec))
438 if(trunc && (r->perm&DMAPPEND)==0){
446 rhdr.iounit = messagesize-IOHDRSZ;
461 if(f->ram->busy == 0)
463 parent = f->ram - ram;
464 if((f->ram->qid.type&QTDIR) == 0)
466 /* must be able to write parent */
467 if(!perm(f, f->ram, Pwrite))
471 if(strcmp(name, ".")==0 || strcmp(name, "..")==0)
473 for(r=ram; r<&ram[nram]; r++)
474 if(r->busy && parent==r->parent)
475 if(strcmp((char*)name, r->name)==0)
477 for(r=ram; r->busy; r++)
478 if(r == &ram[Nram-1])
479 return "no free ram resources";
481 r->qid.path = ++path;
484 r->qid.type |= QTDIR;
487 r->name = estrdup(name);
489 r->group = f->ram->group;
490 r->muid = f->ram->muid;
492 prm = (prm&~0777) | (f->ram->perm&prm&0777);
494 prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666);
501 f->ram->mtime = r->atime;
504 rhdr.iounit = messagesize-IOHDRSZ;
506 if(thdr.mode & ORCLOSE)
520 if(f->ram->busy == 0)
524 rhdr.data = (char*)rdata;
526 return "negative seek offset";
530 if(cnt > messagesize) /* shouldn't happen, anyway */
533 return "negative read count";
534 if(f->ram->qid.type & QTDIR){
535 for(r=ram+1; off > 0; r++){
536 if(r->busy && r->parent==f->ram-ram)
537 off -= ramstat(r, statbuf, sizeof statbuf);
538 if(r == &ram[nram-1])
541 for(; r<&ram[nram] && n < cnt; r++){
542 if(!r->busy || r->parent!=f->ram-ram)
544 m = ramstat(r, buf+n, cnt-n);
549 rhdr.data = (char*)rdata;
560 rhdr.data = r->data+off;
577 return "negative seek offset";
579 if(r->perm & DMAPPEND)
583 return "negative write count";
584 if(r->qid.type & QTDIR)
586 if(memlim && off+cnt >= Maxsize) /* sanity check */
587 return "write too big";
588 if(off+cnt > r->ndata)
589 r->data = erealloc(r->data, off+cnt);
591 memset(r->data+r->ndata, 0, off-r->ndata);
592 if(off+cnt > r->ndata)
594 memmove(r->data+off, thdr.data, cnt);
604 long didx = dr - ram;
607 for(r=ram; r<&ram[nram]; r++)
608 if(r->busy && didx==r->parent)
616 if(r->qid.type & QTDIR && !emptydir(r))
623 memset(&r->qid, 0, sizeof r->qid);
638 e = realremove(f->ram);
656 if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite))
658 ram[r->parent].mtime = time(0);
659 return realremove(r);
665 if(f->ram->busy == 0)
667 rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf);
678 if(f->ram->busy == 0)
680 convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf);
684 * To change length, must have write permission on file.
686 if(dir.length!=~0 && dir.length!=r->ndata){
687 if(!perm(f, r, Pwrite))
692 * To change name, must have write permission in parent
693 * and name must be unique.
695 if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){
696 if(!perm(f, &ram[r->parent], Pwrite))
698 for(s=ram; s<&ram[nram]; s++)
699 if(s->busy && s->parent==r->parent)
700 if(strcmp(dir.name, s->name)==0)
705 * To change mode, must be owner or group leader.
706 * Because of lack of users file, leader=>group itself.
708 if(dir.mode!=~0 && r->perm!=dir.mode){
709 if(strcmp(f->user, r->user) != 0)
710 if(strcmp(f->user, r->group) != 0)
715 * To change group, must be owner and member of new group,
716 * or leader of current group and leader of new group.
717 * Second case cannot happen, but we check anyway.
719 if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){
720 if(strcmp(f->user, r->user) == 0)
721 // if(strcmp(f->user, dir.gid) == 0)
723 if(strcmp(f->user, r->group) == 0)
724 if(strcmp(f->user, dir.gid) == 0)
732 dir.mode &= ~DMDIR; /* cannot change dir bit */
733 dir.mode |= r->perm&DMDIR;
736 if(dir.name[0] != '\0'){
738 r->name = estrdup(dir.name);
740 if(dir.gid[0] != '\0')
741 r->group = estrdup(dir.gid);
742 if(dir.length!=~0 && dir.length!=r->ndata){
743 r->data = erealloc(r->data, dir.length);
744 if(r->ndata < dir.length)
745 memset(r->data+r->ndata, 0, dir.length-r->ndata);
746 r->ndata = dir.length;
748 ram[r->parent].mtime = time(0);
753 ramstat(Ram *r, uchar *buf, uint nbuf)
761 dir.length = r->ndata;
765 dir.atime = r->atime;
766 dir.mtime = r->mtime;
767 n = convD2M(&dir, buf, nbuf);
779 for(f = fids; f; f = f->next)
782 else if(!ff && !f->busy)
788 f = emalloc(sizeof *f);
805 snprint(buf, sizeof buf, "/proc/%d/ctl", pid);
806 ctl = open(buf, OWRITE);
808 fprint(2, "can't protect ramfs\n");
810 fprint(ctl, "noswap\n");
811 fprint(ctl, "private\n");
818 * reading from a pipe or a network device
819 * will give an error after a few eof reads.
820 * however, we cannot tell the difference
821 * between a zero-length read and an interrupt
822 * on the processes writing to us,
823 * so we wait for the error.
825 n = read9pmsg(mfd[0], mdata, messagesize);
827 rerrstr(buf, sizeof buf);
828 if(buf[0]=='\0' || strstr(buf, "hungup"))
834 if(convM2S(mdata, n, &thdr) == 0)
838 fprint(2, "ramfs %d:<-%F\n", pid, &thdr);
840 if(thdr.type<0 || thdr.type>=nelem(fcalls) || !fcalls[thdr.type])
841 err = "bad fcall type";
842 else if(((fid=newfid(thdr.fid))==nil || !fid->ram) && needfid[thdr.type])
843 err = "fid not in use";
845 err = (*fcalls[thdr.type])(fid);
850 rhdr.type = thdr.type + 1;
855 fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/
856 n = convS2M(&rhdr, mdata, messagesize);
858 error("convS2M error on write");
859 if(write(mfd[1], mdata, n) != n)
860 error("mount write");
865 perm(Fid *f, Ram *r, int p)
867 if((p*Pother) & r->perm)
869 if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))
871 if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))
879 fprint(2, "%s: %s: %r\n", argv0, s);
890 error("out of memory");
896 erealloc(void *p, ulong n)
900 error("out of memory");
913 error("out of memory");
921 fprint(2, "usage: %s [-Dipsubac] [-m mountpoint] [-S srvname]\n", argv0);