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
176 if(pipe(p) < 0)
177 error("pipe failed");
178 if(!stdio){
179 mfd[0] = p[0];
180 mfd[1] = p[0];
181 if(post9pservice(p[1], service) < 0)
182 sysfatal("post9pservice %s: %r", service);
185 user = getuser();
186 notify(notifyf);
187 nram = 2;
188 r = &ram[0];
189 r->busy = 1;
190 r->data = 0;
191 r->ndata = 0;
192 r->perm = DMDIR | 0775;
193 r->qid.type = QTDIR;
194 r->qid.path = 0;
195 r->qid.vers = 0;
196 r->parent = 0;
197 r->user = user;
198 r->group = user;
199 r->muid = user;
200 r->atime = time(0);
201 r->mtime = r->atime;
202 r->name = estrdup(".");
204 r = &ram[1];
205 r->busy = 1;
206 r->data = 0;
207 r->ndata = 0;
208 r->perm = 0666;
209 r->qid.type = 0;
210 r->qid.path = 1;
211 r->qid.vers = 0;
212 r->parent = 0;
213 r->user = user;
214 r->group = user;
215 r->muid = user;
216 r->atime = time(0);
217 r->mtime = r->atime;
218 r->name = estrdup("file");
220 if(debug)
221 fmtinstall('F', fcallfmt);
222 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
223 case -1:
224 error("fork");
225 case 0:
226 close(p[1]);
227 io();
228 break;
229 default:
230 close(p[0]); /* don't deadlock if child fails */
232 exits(0);
235 char*
236 rversion(Fid *x)
238 Fid *f;
240 USED(x);
241 for(f = fids; f; f = f->next)
242 if(f->busy)
243 rclunk(f);
244 if(thdr.msize > sizeof mdata)
245 rhdr.msize = sizeof mdata;
246 else
247 rhdr.msize = thdr.msize;
248 messagesize = rhdr.msize;
249 if(strncmp(thdr.version, "9P2000", 6) != 0)
250 return Eversion;
251 rhdr.version = "9P2000";
252 return 0;
255 char*
256 rauth(Fid *x)
258 if(x->busy)
259 return Ebadfid;
260 return "ramfs: no authentication required";
263 char*
264 rflush(Fid *f)
266 USED(f);
267 return 0;
270 char*
271 rattach(Fid *f)
273 /* no authentication! */
274 if(f->busy)
275 return Ebadfid;
276 f->busy = 1;
277 f->rclose = 0;
278 f->ram = &ram[0];
279 rhdr.qid = f->ram->qid;
280 if(thdr.uname[0])
281 f->user = estrdup(thdr.uname);
282 else
283 f->user = "none";
284 if(strcmp(user, "none") == 0)
285 user = f->user;
286 return 0;
289 char*
290 xclone(Fid *f, Fid **nf)
292 if(!f->busy)
293 return Ebadfid;
294 if(f->open)
295 return Eisopen;
296 if(f->ram->busy == 0)
297 return Enotexist;
298 *nf = newfid(thdr.newfid);
299 (*nf)->busy = 1;
300 (*nf)->open = 0;
301 (*nf)->rclose = 0;
302 (*nf)->ram = f->ram;
303 (*nf)->user = f->user; /* no ref count; the leakage is minor */
304 return 0;
307 char*
308 rwalk(Fid *f)
310 Ram *r, *fram;
311 char *name;
312 Ram *parent;
313 Fid *nf;
314 char *err;
315 ulong t;
316 int i;
318 if(!f->busy)
319 return Ebadfid;
320 err = nil;
321 nf = nil;
322 rhdr.nwqid = 0;
323 if(thdr.newfid != thdr.fid){
324 err = xclone(f, &nf);
325 if(err)
326 return err;
327 f = nf; /* walk the new fid */
329 fram = f->ram;
330 if(thdr.nwname > 0){
331 t = time(0);
332 for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
333 if((fram->qid.type & QTDIR) == 0){
334 err = Enotdir;
335 break;
337 if(fram->busy == 0){
338 err = Enotexist;
339 break;
341 fram->atime = t;
342 name = thdr.wname[i];
343 if(strcmp(name, ".") == 0){
344 Found:
345 rhdr.nwqid++;
346 rhdr.wqid[i] = fram->qid;
347 continue;
349 parent = &ram[fram->parent];
350 if(!perm(f, parent, Pexec)){
351 err = Eperm;
352 break;
354 if(strcmp(name, "..") == 0){
355 fram = parent;
356 goto Found;
358 for(r=ram; r < &ram[nram]; r++)
359 if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){
360 fram = r;
361 goto Found;
363 break;
365 if(i==0 && err == nil)
366 err = Enotexist;
368 if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
369 /* clunk the new fid, which is the one we walked */
370 fprint(2, "f %d zero busy\n", f->fid);
371 f->busy = 0;
372 f->ram = nil;
374 if(rhdr.nwqid == thdr.nwname) /* update the fid after a successful walk */
375 f->ram = fram;
376 assert(f->busy);
377 return err;
380 char *
381 ropen(Fid *f)
383 Ram *r;
384 int mode, trunc;
386 if(!f->busy)
387 return Ebadfid;
388 if(f->open)
389 return Eisopen;
390 r = f->ram;
391 if(r->busy == 0)
392 return Enotexist;
393 if(r->perm & DMEXCL)
394 if(r->open)
395 return Excl;
396 mode = thdr.mode;
397 if(r->qid.type & QTDIR){
398 if(mode != OREAD)
399 return Eperm;
400 rhdr.qid = r->qid;
401 return 0;
403 if(mode & ORCLOSE){
404 /* can't remove root; must be able to write parent */
405 if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite))
406 return Eperm;
407 f->rclose = 1;
409 trunc = mode & OTRUNC;
410 mode &= OPERM;
411 if(mode==OWRITE || mode==ORDWR || trunc)
412 if(!perm(f, r, Pwrite))
413 return Eperm;
414 if(mode==OREAD || mode==ORDWR)
415 if(!perm(f, r, Pread))
416 return Eperm;
417 if(mode==OEXEC)
418 if(!perm(f, r, Pexec))
419 return Eperm;
420 if(trunc && (r->perm&DMAPPEND)==0){
421 r->ndata = 0;
422 if(r->data)
423 free(r->data);
424 r->data = 0;
425 r->qid.vers++;
427 rhdr.qid = r->qid;
428 rhdr.iounit = messagesize-IOHDRSZ;
429 f->open = 1;
430 r->open++;
431 return 0;
434 char *
435 rcreate(Fid *f)
437 Ram *r;
438 char *name;
439 long parent, prm;
441 if(!f->busy)
442 return Ebadfid;
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;
496 char*
497 rread(Fid *f)
499 Ram *r;
500 uchar *buf;
501 long off;
502 int n, m, cnt;
504 if(!f->busy)
505 return Ebadfid;
506 if(f->ram->busy == 0)
507 return Enotexist;
508 n = 0;
509 rhdr.count = 0;
510 off = thdr.offset;
511 buf = rdata;
512 cnt = thdr.count;
513 if(cnt > messagesize) /* shouldn't happen, anyway */
514 cnt = messagesize;
515 if(f->ram->qid.type & QTDIR){
516 for(r=ram+1; off > 0; r++){
517 if(r->busy && r->parent==f->ram-ram)
518 off -= ramstat(r, statbuf, sizeof statbuf);
519 if(r == &ram[nram-1])
520 return 0;
522 for(; r<&ram[nram] && n < cnt; r++){
523 if(!r->busy || r->parent!=f->ram-ram)
524 continue;
525 m = ramstat(r, buf+n, cnt-n);
526 if(m == 0)
527 break;
528 n += m;
530 rhdr.data = (char*)rdata;
531 rhdr.count = n;
532 return 0;
534 r = f->ram;
535 if(off >= r->ndata)
536 return 0;
537 r->atime = time(0);
538 n = cnt;
539 if(off+n > r->ndata)
540 n = r->ndata - off;
541 rhdr.data = r->data+off;
542 rhdr.count = n;
543 return 0;
546 char*
547 rwrite(Fid *f)
549 Ram *r;
550 ulong off;
551 int cnt;
553 r = f->ram;
554 if(!f->busy)
555 return Ebadfid;
556 if(r->busy == 0)
557 return Enotexist;
558 off = thdr.offset;
559 if(r->perm & DMAPPEND)
560 off = r->ndata;
561 cnt = thdr.count;
562 if(r->qid.type & QTDIR)
563 return Eisdir;
564 if(off+cnt >= Maxsize) /* sanity check */
565 return "write too big";
566 if(off+cnt > r->ndata)
567 r->data = erealloc(r->data, off+cnt);
568 if(off > r->ndata)
569 memset(r->data+r->ndata, 0, off-r->ndata);
570 if(off+cnt > r->ndata)
571 r->ndata = off+cnt;
572 memmove(r->data+off, thdr.data, cnt);
573 r->qid.vers++;
574 r->mtime = time(0);
575 rhdr.count = cnt;
576 return 0;
579 static int
580 emptydir(Ram *dr)
582 long didx = dr - ram;
583 Ram *r;
585 for(r=ram; r<&ram[nram]; r++)
586 if(r->busy && didx==r->parent)
587 return 0;
588 return 1;
591 char *
592 realremove(Ram *r)
594 if(r->qid.type & QTDIR && !emptydir(r))
595 return Enotempty;
596 r->ndata = 0;
597 if(r->data)
598 free(r->data);
599 r->data = 0;
600 r->parent = 0;
601 memset(&r->qid, 0, sizeof r->qid);
602 free(r->name);
603 r->name = nil;
604 r->busy = 0;
605 return nil;
608 char *
609 rclunk(Fid *f)
611 char *e = nil;
613 if(f->open)
614 f->ram->open--;
615 if(f->rclose)
616 e = realremove(f->ram);
617 fprint(2, "clunk fid %d busy=%d\n", f->fid, f->busy);
618 fprint(2, "f %d zero busy\n", f->fid);
619 f->busy = 0;
620 f->open = 0;
621 f->ram = 0;
622 return e;
625 char *
626 rremove(Fid *f)
628 Ram *r;
630 if(f->open)
631 f->ram->open--;
632 fprint(2, "f %d zero busy\n", f->fid);
633 f->busy = 0;
634 f->open = 0;
635 r = f->ram;
636 f->ram = 0;
637 if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite))
638 return Eperm;
639 ram[r->parent].mtime = time(0);
640 return realremove(r);
643 char *
644 rstat(Fid *f)
646 if(!f->busy)
647 return Ebadfid;
648 if(f->ram->busy == 0)
649 return Enotexist;
650 rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf);
651 rhdr.stat = statbuf;
652 return 0;
655 char *
656 rwstat(Fid *f)
658 Ram *r, *s;
659 Dir dir;
661 if(!f->busy)
662 return Ebadfid;
663 if(f->ram->busy == 0)
664 return Enotexist;
665 convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf);
666 r = f->ram;
668 /*
669 * To change length, must have write permission on file.
670 */
671 if(dir.length!=~0 && dir.length!=r->ndata){
672 if(!perm(f, r, Pwrite))
673 return Eperm;
676 /*
677 * To change name, must have write permission in parent
678 * and name must be unique.
679 */
680 if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){
681 if(!perm(f, &ram[r->parent], Pwrite))
682 return Eperm;
683 for(s=ram; s<&ram[nram]; s++)
684 if(s->busy && s->parent==r->parent)
685 if(strcmp(dir.name, s->name)==0)
686 return Eexist;
689 /*
690 * To change mode, must be owner or group leader.
691 * Because of lack of users file, leader=>group itself.
692 */
693 if(dir.mode!=~0 && r->perm!=dir.mode){
694 if(strcmp(f->user, r->user) != 0)
695 if(strcmp(f->user, r->group) != 0)
696 return Enotowner;
699 /*
700 * To change group, must be owner and member of new group,
701 * or leader of current group and leader of new group.
702 * Second case cannot happen, but we check anyway.
703 */
704 if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){
705 if(strcmp(f->user, r->user) == 0)
706 // if(strcmp(f->user, dir.gid) == 0)
707 goto ok;
708 if(strcmp(f->user, r->group) == 0)
709 if(strcmp(f->user, dir.gid) == 0)
710 goto ok;
711 return Enotowner;
712 ok:;
715 /* all ok; do it */
716 if(dir.mode != ~0){
717 dir.mode &= ~DMDIR; /* cannot change dir bit */
718 dir.mode |= r->perm&DMDIR;
719 r->perm = dir.mode;
721 if(dir.name[0] != '\0'){
722 free(r->name);
723 r->name = estrdup(dir.name);
725 if(dir.gid[0] != '\0')
726 r->group = estrdup(dir.gid);
727 if(dir.length!=~0 && dir.length!=r->ndata){
728 r->data = erealloc(r->data, dir.length);
729 if(r->ndata < dir.length)
730 memset(r->data+r->ndata, 0, dir.length-r->ndata);
731 r->ndata = dir.length;
733 ram[r->parent].mtime = time(0);
734 return 0;
737 uint
738 ramstat(Ram *r, uchar *buf, uint nbuf)
740 int n;
741 Dir dir;
743 dir.name = r->name;
744 dir.qid = r->qid;
745 dir.mode = r->perm;
746 dir.length = r->ndata;
747 dir.uid = r->user;
748 dir.gid = r->group;
749 dir.muid = r->muid;
750 dir.atime = r->atime;
751 dir.mtime = r->mtime;
752 n = convD2M(&dir, buf, nbuf);
753 if(n > 2)
754 return n;
755 return 0;
758 Fid *
759 newfid(int fid)
761 Fid *f, *ff;
763 ff = 0;
764 for(f = fids; f; f = f->next)
765 if(f->fid == fid){
766 fprint(2, "got fid %d busy=%d\n", fid, f->busy);
767 return f;
769 else if(!ff && !f->busy)
770 ff = f;
771 if(ff){
772 ff->fid = fid;
773 return ff;
775 f = emalloc(sizeof *f);
776 f->ram = nil;
777 f->fid = fid;
778 f->next = fids;
779 fids = f;
780 return f;
783 void
784 io(void)
786 char *err, buf[20];
787 int n, pid, ctl;
789 pid = getpid();
790 if(private){
791 snprint(buf, sizeof buf, "/proc/%d/ctl", pid);
792 ctl = open(buf, OWRITE);
793 if(ctl < 0){
794 fprint(2, "can't protect ramfs\n");
795 }else{
796 fprint(ctl, "noswap\n");
797 fprint(ctl, "private\n");
798 close(ctl);
802 for(;;){
803 /*
804 * reading from a pipe or a network device
805 * will give an error after a few eof reads.
806 * however, we cannot tell the difference
807 * between a zero-length read and an interrupt
808 * on the processes writing to us,
809 * so we wait for the error.
810 */
811 n = read9pmsg(mfd[0], mdata, messagesize);
812 if(n < 0)
813 error("mount read");
814 if(n == 0)
815 error("mount eof");
816 if(convM2S(mdata, n, &thdr) == 0)
817 continue;
819 if(debug)
820 fprint(2, "ramfs %d:<-%F\n", pid, &thdr);
822 if(!fcalls[thdr.type])
823 err = "bad fcall type";
824 else
825 err = (*fcalls[thdr.type])(newfid(thdr.fid));
826 if(err){
827 rhdr.type = Rerror;
828 rhdr.ename = err;
829 }else{
830 rhdr.type = thdr.type + 1;
831 rhdr.fid = thdr.fid;
833 rhdr.tag = thdr.tag;
834 if(debug)
835 fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/
836 n = convS2M(&rhdr, mdata, messagesize);
837 if(n == 0)
838 error("convS2M error on write");
839 if(write(mfd[1], mdata, n) != n)
840 error("mount write");
844 int
845 perm(Fid *f, Ram *r, int p)
847 if((p*Pother) & r->perm)
848 return 1;
849 if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))
850 return 1;
851 if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))
852 return 1;
853 return 0;
856 void
857 error(char *s)
859 fprint(2, "%s: %s: %r\n", argv0, s);
860 exits(s);
863 void *
864 emalloc(ulong n)
866 void *p;
868 p = malloc(n);
869 if(!p)
870 error("out of memory");
871 memset(p, 0, n);
872 return p;
875 void *
876 erealloc(void *p, ulong n)
878 p = realloc(p, n);
879 if(!p)
880 error("out of memory");
881 return p;
884 char *
885 estrdup(char *q)
887 char *p;
888 int n;
890 n = strlen(q)+1;
891 p = malloc(n);
892 if(!p)
893 error("out of memory");
894 memmove(p, q, n);
895 return p;
898 void
899 usage(void)
901 fprint(2, "usage: %s [-is] [-m mountpoint]\n", argv0);
902 exits("usage");