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