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