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