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