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