Blob


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