URI: 
       tramfs - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 57ccfb9e8f51138c33ad5f58e14c0e54246cf5c4
   DIR parent b3994ec5c78e6c18885079b58abb7fb997899c3f
  HTML Author: rsc <devnull@localhost>
       Date:   Thu, 11 Dec 2003 17:50:50 +0000
       
       ramfs
       
       Diffstat:
         A src/cmd/ramfs.c                     |     904 +++++++++++++++++++++++++++++++
       
       1 file changed, 904 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/src/cmd/ramfs.c b/src/cmd/ramfs.c
       t@@ -0,0 +1,904 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +
       +int post9pservice(int, char*);
       +
       +/*
       + * Rather than reading /adm/users, which is a lot of work for
       + * a toy program, we assume all groups have the form
       + *        NNN:user:user:
       + * meaning that each user is the leader of his own group.
       + */
       +
       +enum
       +{
       +        OPERM        = 0x3,                /* mask of all permission types in open mode */
       +        Nram        = 2048,
       +        Maxsize        = 512*1024*1024,
       +        Maxfdata        = 8192,
       +};
       +
       +typedef struct Fid Fid;
       +typedef struct Ram Ram;
       +
       +struct Fid
       +{
       +        short        busy;
       +        short        open;
       +        short        rclose;
       +        int        fid;
       +        Fid        *next;
       +        char        *user;
       +        Ram        *ram;
       +};
       +
       +struct Ram
       +{
       +        short        busy;
       +        short        open;
       +        long        parent;                /* index in Ram array */
       +        Qid        qid;
       +        long        perm;
       +        char        *name;
       +        ulong        atime;
       +        ulong        mtime;
       +        char        *user;
       +        char        *group;
       +        char        *muid;
       +        char        *data;
       +        long        ndata;
       +};
       +
       +enum
       +{
       +        Pexec =                1,
       +        Pwrite =         2,
       +        Pread =         4,
       +        Pother =         1,
       +        Pgroup =         8,
       +        Powner =        64,
       +};
       +
       +ulong        path;                /* incremented for each new file */
       +Fid        *fids;
       +Ram        ram[Nram];
       +int        nram;
       +int        mfd[2];
       +char        *user;
       +uchar        mdata[IOHDRSZ+Maxfdata];
       +uchar        rdata[Maxfdata];        /* buffer for data in reply */
       +uchar statbuf[STATMAX];
       +Fcall thdr;
       +Fcall        rhdr;
       +int        messagesize = sizeof mdata;
       +
       +Fid *        newfid(int);
       +uint        ramstat(Ram*, uchar*, uint);
       +void        error(char*);
       +void        io(void);
       +void        *erealloc(void*, ulong);
       +void        *emalloc(ulong);
       +char        *estrdup(char*);
       +void        usage(void);
       +int        perm(Fid*, Ram*, int);
       +
       +char        *rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
       +        *rattach(Fid*), *rwalk(Fid*),
       +        *ropen(Fid*), *rcreate(Fid*),
       +        *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
       +        *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
       +
       +char         *(*fcalls[])(Fid*) = {
       +        [Tversion]        rversion,
       +        [Tflush]        rflush,
       +        [Tauth]        rauth,
       +        [Tattach]        rattach,
       +        [Twalk]                rwalk,
       +        [Topen]                ropen,
       +        [Tcreate]        rcreate,
       +        [Tread]                rread,
       +        [Twrite]        rwrite,
       +        [Tclunk]        rclunk,
       +        [Tremove]        rremove,
       +        [Tstat]                rstat,
       +        [Twstat]        rwstat,
       +};
       +
       +char        Eperm[] =        "permission denied";
       +char        Enotdir[] =        "not a directory";
       +char        Enoauth[] =        "ramfs: authentication not required";
       +char        Enotexist[] =        "file does not exist";
       +char        Einuse[] =        "file in use";
       +char        Eexist[] =        "file exists";
       +char        Eisdir[] =        "file is a directory";
       +char        Enotowner[] =        "not owner";
       +char        Eisopen[] =         "file already open for I/O";
       +char        Excl[] =         "exclusive use file already open";
       +char        Ename[] =         "illegal name";
       +char        Eversion[] =        "unknown 9P version";
       +char        Enotempty[] =        "directory not empty";
       +char        Ebadfid[] =        "bad fid";
       +
       +int debug;
       +int private;
       +
       +void
       +notifyf(void *a, char *s)
       +{
       +        USED(a);
       +        if(strncmp(s, "interrupt", 9) == 0)
       +                noted(NCONT);
       +        noted(NDFLT);
       +}
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        Ram *r;
       +        char *defmnt;
       +        int p[2];
       +        int stdio = 0;
       +        char *service;
       +
       +        service = "ramfs";
       +        defmnt = "/tmp";
       +        ARGBEGIN{
       +        case 'D':
       +                debug = 1;
       +                break;
       +        case 'i':
       +                defmnt = 0;
       +                stdio = 1;
       +                mfd[0] = 0;
       +                mfd[1] = 1;
       +                break;
       +        case 's':
       +                defmnt = 0;
       +                break;
       +        case 'm':
       +                defmnt = ARGF();
       +                break;
       +        case 'p':
       +                private++;
       +                break;
       +        case 'S':
       +                defmnt = 0;
       +                service = EARGF(usage());
       +                break;
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        if(defmnt)
       +                sysfatal("cannot mount -- not on plan 9");
       +
       +        if(pipe(p) < 0)
       +                error("pipe failed");
       +        if(!stdio){
       +                mfd[0] = p[0];
       +                mfd[1] = p[0];
       +                if(post9pservice(p[1], service) < 0)
       +                        sysfatal("post9pservice %s: %r", service);
       +        }
       +
       +        user = getuser();
       +        notify(notifyf);
       +        nram = 2;
       +        r = &ram[0];
       +        r->busy = 1;
       +        r->data = 0;
       +        r->ndata = 0;
       +        r->perm = DMDIR | 0775;
       +        r->qid.type = QTDIR;
       +        r->qid.path = 0;
       +        r->qid.vers = 0;
       +        r->parent = 0;
       +        r->user = user;
       +        r->group = user;
       +        r->muid = user;
       +        r->atime = time(0);
       +        r->mtime = r->atime;
       +        r->name = estrdup(".");
       +
       +        r = &ram[1];
       +        r->busy = 1;
       +        r->data = 0;
       +        r->ndata = 0;
       +        r->perm = 0666;
       +        r->qid.type = 0;
       +        r->qid.path = 1;
       +        r->qid.vers = 0;
       +        r->parent = 0;
       +        r->user = user;
       +        r->group = user;
       +        r->muid = user;
       +        r->atime = time(0);
       +        r->mtime = r->atime;
       +        r->name = estrdup("file");
       +
       +        if(debug)
       +                fmtinstall('F', fcallfmt);
       +        switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
       +        case -1:
       +                error("fork");
       +        case 0:
       +                close(p[1]);
       +                io();
       +                break;
       +        default:
       +                close(p[0]);        /* don't deadlock if child fails */
       +        }
       +        exits(0);
       +}
       +
       +char*
       +rversion(Fid *x)
       +{
       +        Fid *f;
       +
       +        USED(x);
       +        for(f = fids; f; f = f->next)
       +                if(f->busy)
       +                        rclunk(f);
       +        if(thdr.msize > sizeof mdata)
       +                rhdr.msize = sizeof mdata;
       +        else
       +                rhdr.msize = thdr.msize;
       +        messagesize = rhdr.msize;
       +        if(strncmp(thdr.version, "9P2000", 6) != 0)
       +                return Eversion;
       +        rhdr.version = "9P2000";
       +        return 0;
       +}
       +
       +char*
       +rauth(Fid *x)
       +{
       +        if(x->busy)
       +                return Ebadfid;
       +        return "ramfs: no authentication required";
       +}
       +
       +char*
       +rflush(Fid *f)
       +{
       +        USED(f);
       +        return 0;
       +}
       +
       +char*
       +rattach(Fid *f)
       +{
       +        /* no authentication! */
       +        if(f->busy)
       +                return Ebadfid;
       +        f->busy = 1;
       +        f->rclose = 0;
       +        f->ram = &ram[0];
       +        rhdr.qid = f->ram->qid;
       +        if(thdr.uname[0])
       +                f->user = estrdup(thdr.uname);
       +        else
       +                f->user = "none";
       +        if(strcmp(user, "none") == 0)
       +                user = f->user;
       +        return 0;
       +}
       +
       +char*
       +clone(Fid *f, Fid **nf)
       +{
       +        if(!f->busy)
       +                return Ebadfid;
       +        if(f->open)
       +                return Eisopen;
       +        if(f->ram->busy == 0)
       +                return Enotexist;
       +        *nf = newfid(thdr.newfid);
       +        (*nf)->busy = 1;
       +        (*nf)->open = 0;
       +        (*nf)->rclose = 0;
       +        (*nf)->ram = f->ram;
       +        (*nf)->user = f->user;        /* no ref count; the leakage is minor */
       +        return 0;
       +}
       +
       +char*
       +rwalk(Fid *f)
       +{
       +        Ram *r, *fram;
       +        char *name;
       +        Ram *parent;
       +        Fid *nf;
       +        char *err;
       +        ulong t;
       +        int i;
       +
       +        if(!f->busy)
       +                return Ebadfid;
       +        err = nil;
       +        nf = nil;
       +        rhdr.nwqid = 0;
       +        if(thdr.newfid != thdr.fid){
       +                err = clone(f, &nf);
       +                if(err)
       +                        return err;
       +                f = nf;        /* walk the new fid */
       +        }
       +        fram = f->ram;
       +        if(thdr.nwname > 0){
       +                t = time(0);
       +                for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
       +                        if((fram->qid.type & QTDIR) == 0){
       +                                err = Enotdir;
       +                                 break;
       +                        }
       +                        if(fram->busy == 0){
       +                                err = Enotexist;
       +                                break;
       +                        }
       +                        fram->atime = t;
       +                        name = thdr.wname[i];
       +                        if(strcmp(name, ".") == 0){
       +    Found:
       +                                rhdr.nwqid++;
       +                                rhdr.wqid[i] = fram->qid;
       +                                continue;
       +                        }
       +                        parent = &ram[fram->parent];
       +                        if(!perm(f, parent, Pexec)){
       +                                err = Eperm;
       +                                break;
       +                        }
       +                        if(strcmp(name, "..") == 0){
       +                                fram = parent;
       +                                goto Found;
       +                        }
       +                        for(r=ram; r < &ram[nram]; r++)
       +                                if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){
       +                                        fram = r;
       +                                        goto Found;
       +                                }
       +                        break;
       +                }
       +                if(i==0 && err == nil)
       +                        err = Enotexist;
       +        }
       +        if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
       +                /* clunk the new fid, which is the one we walked */
       +fprint(2, "f %d zero busy\n", f->fid);
       +                f->busy = 0;
       +                f->ram = nil;
       +        }
       +        if(rhdr.nwqid == thdr.nwname)        /* update the fid after a successful walk */
       +                f->ram = fram;
       +        assert(f->busy);
       +        return err;
       +}
       +
       +char *
       +ropen(Fid *f)
       +{
       +        Ram *r;
       +        int mode, trunc;
       +
       +        if(!f->busy)
       +                return Ebadfid;
       +        if(f->open)
       +                return Eisopen;
       +        r = f->ram;
       +        if(r->busy == 0)
       +                return Enotexist;
       +        if(r->perm & DMEXCL)
       +                if(r->open)
       +                        return Excl;
       +        mode = thdr.mode;
       +        if(r->qid.type & QTDIR){
       +                if(mode != OREAD)
       +                        return Eperm;
       +                rhdr.qid = r->qid;
       +                return 0;
       +        }
       +        if(mode & ORCLOSE){
       +                /* can't remove root; must be able to write parent */
       +                if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite))
       +                        return Eperm;
       +                f->rclose = 1;
       +        }
       +        trunc = mode & OTRUNC;
       +        mode &= OPERM;
       +        if(mode==OWRITE || mode==ORDWR || trunc)
       +                if(!perm(f, r, Pwrite))
       +                        return Eperm;
       +        if(mode==OREAD || mode==ORDWR)
       +                if(!perm(f, r, Pread))
       +                        return Eperm;
       +        if(mode==OEXEC)
       +                if(!perm(f, r, Pexec))
       +                        return Eperm;
       +        if(trunc && (r->perm&DMAPPEND)==0){
       +                r->ndata = 0;
       +                if(r->data)
       +                        free(r->data);
       +                r->data = 0;
       +                r->qid.vers++;
       +        }
       +        rhdr.qid = r->qid;
       +        rhdr.iounit = messagesize-IOHDRSZ;
       +        f->open = 1;
       +        r->open++;
       +        return 0;
       +}
       +
       +char *
       +rcreate(Fid *f)
       +{
       +        Ram *r;
       +        char *name;
       +        long parent, prm;
       +
       +        if(!f->busy)
       +                return Ebadfid;
       +        if(f->open)
       +                return Eisopen;
       +        if(f->ram->busy == 0)
       +                return Enotexist;
       +        parent = f->ram - ram;
       +        if((f->ram->qid.type&QTDIR) == 0)
       +                return Enotdir;
       +        /* must be able to write parent */
       +        if(!perm(f, f->ram, Pwrite))
       +                return Eperm;
       +        prm = thdr.perm;
       +        name = thdr.name;
       +        if(strcmp(name, ".")==0 || strcmp(name, "..")==0)
       +                return Ename;
       +        for(r=ram; r<&ram[nram]; r++)
       +                if(r->busy && parent==r->parent)
       +                if(strcmp((char*)name, r->name)==0)
       +                        return Einuse;
       +        for(r=ram; r->busy; r++)
       +                if(r == &ram[Nram-1])
       +                        return "no free ram resources";
       +        r->busy = 1;
       +        r->qid.path = ++path;
       +        r->qid.vers = 0;
       +        if(prm & DMDIR)
       +                r->qid.type |= QTDIR;
       +        r->parent = parent;
       +        free(r->name);
       +        r->name = estrdup(name);
       +        r->user = f->user;
       +        r->group = f->ram->group;
       +        r->muid = f->ram->muid;
       +        if(prm & DMDIR)
       +                prm = (prm&~0777) | (f->ram->perm&prm&0777);
       +        else
       +                prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666);
       +        r->perm = prm;
       +        r->ndata = 0;
       +        if(r-ram >= nram)
       +                nram = r - ram + 1;
       +        r->atime = time(0);
       +        r->mtime = r->atime;
       +        f->ram->mtime = r->atime;
       +        f->ram = r;
       +        rhdr.qid = r->qid;
       +        rhdr.iounit = messagesize-IOHDRSZ;
       +        f->open = 1;
       +        if(thdr.mode & ORCLOSE)
       +                f->rclose = 1;
       +        r->open++;
       +        return 0;
       +}
       +
       +char*
       +rread(Fid *f)
       +{
       +        Ram *r;
       +        uchar *buf;
       +        long off;
       +        int n, m, cnt;
       +
       +        if(!f->busy)
       +                return Ebadfid;
       +        if(f->ram->busy == 0)
       +                return Enotexist;
       +        n = 0;
       +        rhdr.count = 0;
       +        off = thdr.offset;
       +        buf = rdata;
       +        cnt = thdr.count;
       +        if(cnt > messagesize)        /* shouldn't happen, anyway */
       +                cnt = messagesize;
       +        if(f->ram->qid.type & QTDIR){
       +                for(r=ram+1; off > 0; r++){
       +                        if(r->busy && r->parent==f->ram-ram)
       +                                off -= ramstat(r, statbuf, sizeof statbuf);
       +                        if(r == &ram[nram-1])
       +                                return 0;
       +                }
       +                for(; r<&ram[nram] && n < cnt; r++){
       +                        if(!r->busy || r->parent!=f->ram-ram)
       +                                continue;
       +                        m = ramstat(r, buf+n, cnt-n);
       +                        if(m == 0)
       +                                break;
       +                        n += m;
       +                }
       +                rhdr.data = (char*)rdata;
       +                rhdr.count = n;
       +                return 0;
       +        }
       +        r = f->ram;
       +        if(off >= r->ndata)
       +                return 0;
       +        r->atime = time(0);
       +        n = cnt;
       +        if(off+n > r->ndata)
       +                n = r->ndata - off;
       +        rhdr.data = r->data+off;
       +        rhdr.count = n;
       +        return 0;
       +}
       +
       +char*
       +rwrite(Fid *f)
       +{
       +        Ram *r;
       +        ulong off;
       +        int cnt;
       +
       +        r = f->ram;
       +        if(!f->busy)
       +                return Ebadfid;
       +        if(r->busy == 0)
       +                return Enotexist;
       +        off = thdr.offset;
       +        if(r->perm & DMAPPEND)
       +                off = r->ndata;
       +        cnt = thdr.count;
       +        if(r->qid.type & QTDIR)
       +                return Eisdir;
       +        if(off+cnt >= Maxsize)                /* sanity check */
       +                return "write too big";
       +        if(off+cnt > r->ndata)
       +                r->data = erealloc(r->data, off+cnt);
       +        if(off > r->ndata)
       +                memset(r->data+r->ndata, 0, off-r->ndata);
       +        if(off+cnt > r->ndata)
       +                r->ndata = off+cnt;
       +        memmove(r->data+off, thdr.data, cnt);
       +        r->qid.vers++;
       +        r->mtime = time(0);
       +        rhdr.count = cnt;
       +        return 0;
       +}
       +
       +static int
       +emptydir(Ram *dr)
       +{
       +        long didx = dr - ram;
       +        Ram *r;
       +
       +        for(r=ram; r<&ram[nram]; r++)
       +                if(r->busy && didx==r->parent)
       +                        return 0;
       +        return 1;
       +}
       +
       +char *
       +realremove(Ram *r)
       +{
       +        if(r->qid.type & QTDIR && !emptydir(r))
       +                return Enotempty;
       +        r->ndata = 0;
       +        if(r->data)
       +                free(r->data);
       +        r->data = 0;
       +        r->parent = 0;
       +        memset(&r->qid, 0, sizeof r->qid);
       +        free(r->name);
       +        r->name = nil;
       +        r->busy = 0;
       +        return nil;
       +}
       +
       +char *
       +rclunk(Fid *f)
       +{
       +        char *e = nil;
       +
       +        if(f->open)
       +                f->ram->open--;
       +        if(f->rclose)
       +                e = realremove(f->ram);
       +fprint(2, "clunk fid %d busy=%d\n", f->fid, f->busy);
       +fprint(2, "f %d zero busy\n", f->fid);
       +        f->busy = 0;
       +        f->open = 0;
       +        f->ram = 0;
       +        return e;
       +}
       +
       +char *
       +rremove(Fid *f)
       +{
       +        Ram *r;
       +
       +        if(f->open)
       +                f->ram->open--;
       +fprint(2, "f %d zero busy\n", f->fid);
       +        f->busy = 0;
       +        f->open = 0;
       +        r = f->ram;
       +        f->ram = 0;
       +        if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite))
       +                return Eperm;
       +        ram[r->parent].mtime = time(0);
       +        return realremove(r);
       +}
       +
       +char *
       +rstat(Fid *f)
       +{
       +        if(!f->busy)
       +                return Ebadfid;
       +        if(f->ram->busy == 0)
       +                return Enotexist;
       +        rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf);
       +        rhdr.stat = statbuf;
       +        return 0;
       +}
       +
       +char *
       +rwstat(Fid *f)
       +{
       +        Ram *r, *s;
       +        Dir dir;
       +
       +        if(!f->busy)
       +                return Ebadfid;
       +        if(f->ram->busy == 0)
       +                return Enotexist;
       +        convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf);
       +        r = f->ram;
       +
       +        /*
       +         * To change length, must have write permission on file.
       +         */
       +        if(dir.length!=~0 && dir.length!=r->ndata){
       +                 if(!perm(f, r, Pwrite))
       +                        return Eperm;
       +        }
       +
       +        /*
       +         * To change name, must have write permission in parent
       +         * and name must be unique.
       +         */
       +        if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){
       +                 if(!perm(f, &ram[r->parent], Pwrite))
       +                        return Eperm;
       +                for(s=ram; s<&ram[nram]; s++)
       +                        if(s->busy && s->parent==r->parent)
       +                        if(strcmp(dir.name, s->name)==0)
       +                                return Eexist;
       +        }
       +
       +        /*
       +         * To change mode, must be owner or group leader.
       +         * Because of lack of users file, leader=>group itself.
       +         */
       +        if(dir.mode!=~0 && r->perm!=dir.mode){
       +                if(strcmp(f->user, r->user) != 0)
       +                if(strcmp(f->user, r->group) != 0)
       +                        return Enotowner;
       +        }
       +
       +        /*
       +         * To change group, must be owner and member of new group,
       +         * or leader of current group and leader of new group.
       +         * Second case cannot happen, but we check anyway.
       +         */
       +        if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){
       +                if(strcmp(f->user, r->user) == 0)
       +        //        if(strcmp(f->user, dir.gid) == 0)
       +                        goto ok;
       +                if(strcmp(f->user, r->group) == 0)
       +                if(strcmp(f->user, dir.gid) == 0)
       +                        goto ok;
       +                return Enotowner;
       +                ok:;
       +        }
       +
       +        /* all ok; do it */
       +        if(dir.mode != ~0){
       +                dir.mode &= ~DMDIR;        /* cannot change dir bit */
       +                dir.mode |= r->perm&DMDIR;
       +                r->perm = dir.mode;
       +        }
       +        if(dir.name[0] != '\0'){
       +                free(r->name);
       +                r->name = estrdup(dir.name);
       +        }
       +        if(dir.gid[0] != '\0')
       +                r->group = estrdup(dir.gid);
       +        if(dir.length!=~0 && dir.length!=r->ndata){
       +                r->data = erealloc(r->data, dir.length);
       +                if(r->ndata < dir.length)
       +                        memset(r->data+r->ndata, 0, dir.length-r->ndata);
       +                r->ndata = dir.length;
       +        }
       +        ram[r->parent].mtime = time(0);
       +        return 0;
       +}
       +
       +uint
       +ramstat(Ram *r, uchar *buf, uint nbuf)
       +{
       +        int n;
       +        Dir dir;
       +
       +        dir.name = r->name;
       +        dir.qid = r->qid;
       +        dir.mode = r->perm;
       +        dir.length = r->ndata;
       +        dir.uid = r->user;
       +        dir.gid = r->group;
       +        dir.muid = r->muid;
       +        dir.atime = r->atime;
       +        dir.mtime = r->mtime;
       +        n = convD2M(&dir, buf, nbuf);
       +        if(n > 2)
       +                return n;
       +        return 0;
       +}
       +
       +Fid *
       +newfid(int fid)
       +{
       +        Fid *f, *ff;
       +
       +        ff = 0;
       +        for(f = fids; f; f = f->next)
       +                if(f->fid == fid){
       +fprint(2, "got fid %d busy=%d\n", fid, f->busy);
       +                        return f;
       +                }
       +                else if(!ff && !f->busy)
       +                        ff = f;
       +        if(ff){
       +                ff->fid = fid;
       +                return ff;
       +        }
       +        f = emalloc(sizeof *f);
       +        f->ram = nil;
       +        f->fid = fid;
       +        f->next = fids;
       +        fids = f;
       +        return f;
       +}
       +
       +void
       +io(void)
       +{
       +        char *err, buf[20];
       +        int n, pid, ctl;
       +
       +        pid = getpid();
       +        if(private){
       +                snprint(buf, sizeof buf, "/proc/%d/ctl", pid);
       +                ctl = open(buf, OWRITE);
       +                if(ctl < 0){
       +                        fprint(2, "can't protect ramfs\n");
       +                }else{
       +                        fprint(ctl, "noswap\n");
       +                        fprint(ctl, "private\n");
       +                        close(ctl);
       +                }
       +        }
       +
       +        for(;;){
       +                /*
       +                 * reading from a pipe or a network device
       +                 * will give an error after a few eof reads.
       +                 * however, we cannot tell the difference
       +                 * between a zero-length read and an interrupt
       +                 * on the processes writing to us,
       +                 * so we wait for the error.
       +                 */
       +                n = read9pmsg(mfd[0], mdata, messagesize);
       +                if(n < 0)
       +                        error("mount read");
       +                if(n == 0)
       +                        error("mount eof");
       +                if(convM2S(mdata, n, &thdr) == 0)
       +                        continue;
       +
       +                if(debug)
       +                        fprint(2, "ramfs %d:<-%F\n", pid, &thdr);
       +
       +                if(!fcalls[thdr.type])
       +                        err = "bad fcall type";
       +                else
       +                        err = (*fcalls[thdr.type])(newfid(thdr.fid));
       +                if(err){
       +                        rhdr.type = Rerror;
       +                        rhdr.ename = err;
       +                }else{
       +                        rhdr.type = thdr.type + 1;
       +                        rhdr.fid = thdr.fid;
       +                }
       +                rhdr.tag = thdr.tag;
       +                if(debug)
       +                        fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/
       +                n = convS2M(&rhdr, mdata, messagesize);
       +                if(n == 0)
       +                        error("convS2M error on write");
       +                if(write(mfd[1], mdata, n) != n)
       +                        error("mount write");
       +        }
       +}
       +
       +int
       +perm(Fid *f, Ram *r, int p)
       +{
       +        if((p*Pother) & r->perm)
       +                return 1;
       +        if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))
       +                return 1;
       +        if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))
       +                return 1;
       +        return 0;
       +}
       +
       +void
       +error(char *s)
       +{
       +        fprint(2, "%s: %s: %r\n", argv0, s);
       +        exits(s);
       +}
       +
       +void *
       +emalloc(ulong n)
       +{
       +        void *p;
       +
       +        p = malloc(n);
       +        if(!p)
       +                error("out of memory");
       +        memset(p, 0, n);
       +        return p;
       +}
       +
       +void *
       +erealloc(void *p, ulong n)
       +{
       +        p = realloc(p, n);
       +        if(!p)
       +                error("out of memory");
       +        return p;
       +}
       +
       +char *
       +estrdup(char *q)
       +{
       +        char *p;
       +        int n;
       +
       +        n = strlen(q)+1;
       +        p = malloc(n);
       +        if(!p)
       +                error("out of memory");
       +        memmove(p, q, n);
       +        return p;
       +}
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: %s [-is] [-m mountpoint]\n", argv0);
       +        exits("usage");
       +}
       +