8 * Rather than reading /adm/users, which is a lot of work for
9 * a toy program, we assume all groups have the form
11 * meaning that each user is the leader of his own group.
16 OPERM = 0x3, /* mask of all permission types in open mode */
18 Maxsize = 512*1024*1024,
22 typedef struct Fid Fid;
23 typedef struct Ram Ram;
40 long parent; /* index in Ram array */
63 ulong path; /* incremented for each new file */
69 uchar mdata[IOHDRSZ+Maxfdata];
70 uchar rdata[Maxfdata]; /* buffer for data in reply */
71 uchar statbuf[STATMAX];
74 int messagesize = sizeof mdata;
77 uint ramstat(Ram*, uchar*, uint);
79 void *erealloc(void*, ulong);
82 void ramfsusage(void);
83 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*) = {
108 char Eperm[] = "permission denied";
109 char Enotdir[] = "not a directory";
110 char Enoauth[] = "no authentication in ramfs";
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";
124 notifyf(void *a, char *s)
127 if(strncmp(s, "interrupt", 9) == 0)
133 ramfsmain(int argc, char *argv[])
148 case 'i': /* this is DIFFERENT from normal ramfs; use 1 for both for kernel */
167 error("pipe failed");
172 fd = create("#s/ramfs", OWRITE, 0666);
174 error("create of /srv/ramfs failed");
175 sprint(buf, "%d", p[1]);
176 if(write(fd, buf, strlen(buf)) < 0)
177 error("writing /srv/ramfs");
181 user = atom(getuser());
188 r->perm = DMDIR | 0775;
198 r->name = estrdup(".");
201 fmtinstall('F', fcallfmt);
202 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
210 close(mfd[0]); /* don't deadlock if child fails */
211 if(defmnt && mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
212 error("mount failed: %r");
221 for(f = fids; f; f = f->next)
224 if(thdr.msize > sizeof mdata)
225 rhdr.msize = sizeof mdata;
227 rhdr.msize = thdr.msize;
228 messagesize = rhdr.msize;
229 if(strncmp(thdr.version, "9P2000", 6) != 0)
231 rhdr.version = "9P2000";
238 return "ramfs: no authentication required";
251 /* no authentication! */
255 rhdr.qid = f->ram->qid;
257 f->user = atom(thdr.uname);
259 f->user = atom("none");
260 if(strcmp(user, "none") == 0)
266 clone(Fid *f, Fid **nf)
270 if(f->ram->busy == 0)
272 *nf = newfid(thdr.newfid);
277 (*nf)->user = f->user; /* no ref count; the leakage is minor */
295 if(rhdr.newfid != rhdr.fid){
299 f = nf; /* walk the new fid */
304 for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
305 if((fram->qid.type & QTDIR) == 0){
314 name = thdr.wname[i];
315 if(strcmp(name, ".") == 0){
318 rhdr.wqid[i] = fram->qid;
321 parent = &ram[fram->parent];
323 if(!perm(f, parent, Pexec)){
328 if(strcmp(name, "..") == 0){
332 for(r=ram; r < &ram[nram]; r++)
333 if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){
339 if(i==0 && err == nil)
342 if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
343 /* clunk the new fid, which is the one we walked */
347 if(rhdr.nwqid == thdr.nwname) /* update the fid after a successful walk */
367 if(r->qid.type & QTDIR){
374 /* can't remove root; must be able to write parent */
375 if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite))
379 trunc = mode & OTRUNC;
381 if(mode==OWRITE || mode==ORDWR || trunc)
382 if(!perm(f, r, Pwrite))
384 if(mode==OREAD || mode==ORDWR)
385 if(!perm(f, r, Pread))
388 if(!perm(f, r, Pexec))
390 if(trunc && (r->perm&DMAPPEND)==0){
398 rhdr.iounit = messagesize-IOHDRSZ;
413 if(f->ram->busy == 0)
415 parent = f->ram - ram;
416 if((f->ram->qid.type&QTDIR) == 0)
418 /* must be able to write parent */
420 if(!perm(f, f->ram, Pwrite))
425 if(strcmp(name, ".")==0 || strcmp(name, "..")==0)
427 for(r=ram; r<&ram[nram]; r++)
428 if(r->busy && parent==r->parent)
429 if(strcmp((char*)name, r->name)==0)
431 for(r=ram; r->busy; r++)
432 if(r == &ram[Nram-1])
433 return "no free ram resources";
435 r->qid.path = ++path;
438 r->qid.type |= QTDIR;
441 r->name = estrdup(name);
443 r->group = f->ram->group;
444 r->muid = f->ram->muid;
446 prm = (prm&~0777) | (f->ram->perm&prm&0777);
448 prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666);
455 f->ram->mtime = r->atime;
458 rhdr.iounit = messagesize-IOHDRSZ;
460 if(thdr.mode & ORCLOSE)
474 if(f->ram->busy == 0)
481 if(cnt > messagesize) /* shouldn't happen, anyway */
483 if(f->ram->qid.type & QTDIR){
484 for(r=ram+1; off > 0; r++){
485 if(r->busy && r->parent==f->ram-ram)
486 off -= ramstat(r, statbuf, sizeof statbuf);
487 if(r == &ram[nram-1])
490 for(; r<&ram[nram] && n < cnt; r++){
491 if(!r->busy || r->parent!=f->ram-ram)
493 m = ramstat(r, buf+n, cnt-n);
498 rhdr.data = (char*)rdata;
509 rhdr.data = r->data+off;
525 if(r->perm & DMAPPEND)
528 if(r->qid.type & QTDIR)
530 if(off+cnt >= Maxsize) /* sanity check */
531 return "write too big";
532 if(off+cnt > r->ndata)
533 r->data = erealloc(r->data, off+cnt);
535 memset(r->data+r->ndata, 0, off-r->ndata);
536 if(off+cnt > r->ndata)
538 memmove(r->data+off, thdr.data, cnt);
553 memset(&r->qid, 0, sizeof r->qid);
584 if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite))
587 ram[r->parent].mtime = time(0);
595 if(f->ram->busy == 0)
597 rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf);
608 if(f->ram->busy == 0)
610 convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf);
614 * To change length, must have write permission on file.
617 if(dir.length!=~0 && dir.length!=r->ndata){
618 if(!perm(f, r, Pwrite))
624 * To change name, must have write permission in parent
625 * and name must be unique.
627 if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){
629 if(!perm(f, &ram[r->parent], Pwrite))
632 for(s=ram; s<&ram[nram]; s++)
633 if(s->busy && s->parent==r->parent)
634 if(strcmp(dir.name, s->name)==0)
640 * To change mode, must be owner or group leader.
641 * Because of lack of users file, leader=>group itself.
643 if(dir.mode!=~0 && r->perm!=dir.mode){
644 if(strcmp(f->user, r->user) != 0)
645 if(strcmp(f->user, r->group) != 0)
650 * To change group, must be owner and member of new group,
651 * or leader of current group and leader of new group.
652 * Second case cannot happen, but we check anyway.
654 if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){
655 if(strcmp(f->user, r->user) == 0)
656 if(strcmp(f->user, dir.gid) == 0)
658 if(strcmp(f->user, r->group) == 0)
659 if(strcmp(f->user, dir.gid) == 0)
668 dir.mode &= ~DMDIR; /* cannot change dir bit */
669 dir.mode |= r->perm&DMDIR;
672 if(dir.name[0] != '\0'){
674 r->name = estrdup(dir.name);
676 if(dir.gid[0] != '\0')
677 r->group = atom(dir.gid);
679 if(dir.uid[0] != '\0')
680 r->user = atom(dir.uid);
682 if(dir.length!=~0 && dir.length!=r->ndata){
683 r->data = erealloc(r->data, dir.length);
684 if(r->ndata < dir.length)
685 memset(r->data+r->ndata, 0, dir.length-r->ndata);
686 r->ndata = dir.length;
690 r->mtime = dir.mtime;
692 ram[r->parent].mtime = time(0);
697 ramstat(Ram *r, uchar *buf, uint nbuf)
704 dir.length = r->ndata;
708 dir.atime = r->atime;
709 dir.mtime = r->mtime;
710 return convD2M(&dir, buf, nbuf);
719 for(f = fids; f; f = f->next)
722 else if(!ff && !f->busy)
728 f = emalloc(sizeof *f);
746 * reading from a pipe or a network device
747 * will give an error after a few eof reads.
748 * however, we cannot tell the difference
749 * between a zero-length read and an interrupt
750 * on the processes writing to us,
751 * so we wait for the error.
753 n = read9pmsg(mfd[0], mdata, messagesize);
755 error("mount read: %r");
758 if(convM2S(mdata, n, &thdr) == 0)
762 fprint(2, "ramfs %d:<-%F\n", pid, &thdr);
764 if(!fcalls[thdr.type])
765 err = "bad fcall type";
767 err = (*fcalls[thdr.type])(newfid(thdr.fid));
772 rhdr.type = thdr.type + 1;
777 fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/
778 n = convS2M(&rhdr, mdata, messagesize);
780 error("convS2M error on write");
781 if(write(mfd[1], mdata, n) != n)
782 error("mount write");
787 perm(Fid *f, Ram *r, int p)
789 if((p*Pother) & r->perm)
791 if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))
793 if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))
805 error("out of memory");
811 erealloc(void *p, ulong n)
815 error("out of memory");
828 error("out of memory");
836 fprint(2, "usage: %s [-is] [-m mountpoint]\n", argv0);
841 * Custom allocators to avoid malloc overheads on small objects.
842 * We never free these. (See below.)
844 typedef struct Stringtab Stringtab;
856 t = malloc(64*sizeof(Stringtab));
858 sysfatal("out of memory");
875 sysfatal("strdup big string");
880 sysfatal("out of memory");
891 * Return a uniquely allocated copy of a string.
892 * Don't free these -- they stay in the table for the
893 * next caller who wants that particular string.
894 * String comparison can be done with pointer comparison
895 * if you know both strings are atoms.
897 static Stringtab *stab[1024];
906 for(p=(uchar*)s; *p; p++)
917 h = hash(str) % nelem(stab);
918 for(tab=stab[h]; tab; tab=tab->link)
919 if(strcmp(str, tab->str) == 0)
923 tab->str = xstrdup(str);