]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ramfs.c
merge
[plan9front.git] / sys / src / cmd / ramfs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <9p.h>
7 #include <pool.h>
8
9 char Ebadoff[] = "bad file offset or count";
10 char Eexist[] = "file already exists";
11 char Enomem[] = "no memory";
12 char Eperm[] = "permission denied";
13 char Enotowner[] = "not owner";
14 char Elocked[] = "file is locked";
15
16 enum {
17         Tdat    = 0xbabababa,   
18         Tind    = 0xdadadada,
19
20         ESIZE   = 64*1024,
21 };
22
23 #define MAXFSIZE ((0x7fffffffll/sizeof(Ram*))*ESIZE)
24
25 typedef struct Ram Ram;
26 struct Ram
27 {
28         int     type;
29         int     size;
30         Ram     **link;
31         Ram     *ent[];
32 };
33
34 int private;
35
36 void*
37 ramalloc(ulong size)
38 {
39         void *v;
40
41         v = sbrk(size);
42         if(v == (void*)-1)
43                 return nil;
44         return v;
45 }
46
47 void
48 rammoved(void*, void *to)
49 {
50         Ram **link, **elink, *x = to;
51
52         *x->link = x;
53         if(x->type != Tind)
54                 return;
55
56         link = x->ent;
57         for(elink = link + (x->size / sizeof(Ram*)); link < elink; link++)
58                 if((x = *link) != nil)
59                         x->link = link;
60 }
61
62 void
63 ramnolock(Pool*)
64 {
65 }
66
67 Pool rampool = {
68         .name=          "ram",
69         .maxsize=       800*1024*1024,
70         .minarena=      4*1024,
71         .quantum=       32,
72         .alloc=         ramalloc,
73         .move=          rammoved,
74         .lock=          ramnolock,
75         .unlock=        ramnolock,
76         .flags=         0,
77 };
78
79 void
80 accessfile(File *f, int a)
81 {
82         f->atime = time(0);
83         if(a & AWRITE){
84                 f->mtime = f->atime;
85                 f->qid.vers++;
86         }
87 }
88
89 void
90 fsread(Req *r)
91 {
92         int o, n, i, count;
93         vlong top, off;
94         File *f;
95         Ram *x;
96         char *p;
97
98         f = r->fid->file;
99         off = r->ifcall.offset;
100         count = r->ifcall.count;
101
102         if(count == 0 || off >= f->length || f->aux == nil){
103                 r->ofcall.count = 0;
104                 respond(r, nil);
105                 return;
106         }
107
108         top = off + count;
109         if(top > MAXFSIZE){
110                 respond(r, Ebadoff);
111                 return;
112         }
113                 
114         if(top > f->length){
115                 top = f->length;
116                 count = top - off;
117         }
118         p = (char*)r->ofcall.data;
119         while(count > 0){
120                 i = off / ESIZE;
121                 o = off % ESIZE;
122
123                 x = (Ram*)f->aux;
124                 if(i < (x->size / sizeof(Ram*)))
125                         x = x->ent[i];
126                 else
127                         x = nil;
128                 if(x != nil && o < x->size){
129                         n = x->size - o;
130                         if(n > count)
131                                 n = count;
132                         memmove(p, (char*)&x[1] + o, n);
133                 } else {
134                         n = ESIZE - o;
135                         if(n > count)
136                                 n = count;
137                         memset(p, 0, n);
138                 }
139                 p += n;
140                 off += n;
141                 count -= n;
142         }
143         accessfile(f, AREAD);
144
145         r->ofcall.count = p - (char*)r->ofcall.data;
146         respond(r, nil);
147 }
148
149 void
150 fswrite(Req *r)
151 {
152         int o, n, i, count;
153         Ram *x, **link;
154         vlong top, off;
155         File *f;
156         char *p;
157
158         f = r->fid->file;
159         off = r->ifcall.offset;
160         count = r->ifcall.count;
161
162         if(f->mode & DMAPPEND)
163                 off = f->length;
164
165         if(count == 0){
166                 r->ofcall.count = 0;
167                 respond(r, nil);
168                 return;
169         }
170
171         top = off + count;
172         if(top > MAXFSIZE){
173                 respond(r, Ebadoff);
174                 return;
175         }
176
177         n = ((top + ESIZE-1)/ESIZE) * sizeof(Ram*);
178         x = (Ram*)f->aux;
179         if(x == nil || x->size < n){
180                 x = poolrealloc(&rampool, x, sizeof(Ram) + n);          
181                 if(x == nil){
182                         respond(r, Enomem);
183                         return;
184                 }
185                 link = (Ram**)&f->aux;
186                 if(*link == nil){
187                         memset(x, 0, sizeof(Ram));
188                         x->type = Tind;
189                         x->link = link;
190                         *link = x;
191                 } else if(x != *link)
192                         rammoved(*link, x);
193                 memset((char*)&x[1] + x->size, 0, n - x->size);
194                 x->size = n;
195         }
196
197         p = (char*)r->ifcall.data;
198         while(count > 0){
199                 i = off / ESIZE;
200                 o = off % ESIZE;
201
202                 n = ESIZE - o;
203                 if(n > count)
204                         n = count;
205
206                 x = ((Ram*)f->aux)->ent[i];
207                 if(x == nil || x->size < o+n){
208                         x = poolrealloc(&rampool, x, sizeof(Ram) + o+n);
209                         if(x == nil){
210                                 respond(r, Enomem);
211                                 return;
212                         }
213                         link = &((Ram*)f->aux)->ent[i];
214                         if(*link == nil){
215                                 memset(x, 0, sizeof(Ram));
216                                 x->type = Tdat;
217                         }
218                         if(o > x->size)
219                                 memset((char*)&x[1] + x->size, 0, o - x->size);
220                         x->size = o + n;
221                         x->link = link;
222                         *link = x;
223                 }
224
225                 memmove((char*)&x[1] + o, p, n);
226                 p += n;
227                 off += n;
228                 count -= n;
229         }
230
231         if(top > f->length)
232                 f->length = top;
233         accessfile(f, AWRITE);
234
235         r->ofcall.count = p - (char*)r->ifcall.data;
236         respond(r, nil);
237 }
238
239 void
240 truncfile(File *f, vlong l)
241 {
242         int i, o, n;
243         Ram *x;
244
245         x = (Ram*)f->aux;
246         if(x != nil){
247                 n = x->size / sizeof(Ram*);
248                 i = l / ESIZE;
249                 if(i < n){
250                         o = l % ESIZE;
251                         if(o != 0 && x->ent[i] != nil){
252                                 x->ent[i]->size = o * sizeof(Ram*);
253                                 i++;
254                         }
255                         while(i < n){
256                                 if(x->ent[i] != nil){
257                                         poolfree(&rampool, x->ent[i]);
258                                         x->ent[i] = nil;
259                                 }
260                                 i++;
261                         }
262                 }
263                 if(l == 0){
264                         poolfree(&rampool, (Ram*)f->aux);
265                         f->aux = nil;
266                 }
267         }
268         f->length = l;
269 }
270
271 void
272 fswstat(Req *r)
273 {
274         File *f, *w;
275         char *u;
276
277         f = r->fid->file;
278         u = r->fid->uid;
279
280         /*
281          * To change length, must have write permission on file.
282          */
283         if(r->d.length != ~0 && r->d.length != f->length){
284                 if(r->d.length > MAXFSIZE){
285                         respond(r, Ebadoff);
286                         return;
287                 }
288                 if(!hasperm(f, u, AWRITE) || (f->mode & DMDIR) != 0)
289                         goto Perm;
290         }
291
292         /*
293          * To change name, must have write permission in parent.
294          */
295         if(r->d.name[0] != '\0' && strcmp(r->d.name, f->name) != 0){
296                 if((w = f->parent) == nil)
297                         goto Perm;
298                 incref(w);
299                 if(!hasperm(w, u, AWRITE)){
300                         closefile(w);
301                         goto Perm;
302                 }
303                 if((w = walkfile(w, r->d.name)) != nil){
304                         closefile(w);
305                         respond(r, Eexist);
306                         return;
307                 }
308         }
309
310         /*
311          * To change mode, must be owner or group leader.
312          * Because of lack of users file, leader=>group itself.
313          */
314         if(r->d.mode != ~0 && f->mode != r->d.mode){
315                 if(strcmp(u, f->uid) != 0)
316                 if(strcmp(u, f->gid) != 0){
317                         respond(r, Enotowner);
318                         return;
319                 }
320         }
321
322         /*
323          * To change group, must be owner and member of new group,
324          * or leader of current group and leader of new group.
325          * Second case cannot happen, but we check anyway.
326          */
327         while(r->d.gid[0] != '\0' && strcmp(f->gid, r->d.gid) != 0){
328                 if(strcmp(u, f->uid) == 0)
329                         break;
330                 if(strcmp(u, f->gid) == 0)
331                 if(strcmp(u, r->d.gid) == 0)
332                         break;
333                 respond(r, Enotowner);
334                 return;
335         }
336
337         if(r->d.mode != ~0){
338                 f->mode = r->d.mode;
339                 f->qid.type = f->mode >> 24;
340         }
341         if(r->d.name[0] != '\0'){
342                 free(f->name);
343                 f->name = estrdup9p(r->d.name);
344         }
345         if(r->d.length != ~0 && r->d.length != f->length)
346                 truncfile(f, r->d.length);
347
348         accessfile(f, AWRITE);
349         if(r->d.mtime != ~0){
350                 f->mtime = r->d.mtime;
351         }
352
353         respond(r, nil);
354         return;
355
356 Perm:
357         respond(r, Eperm);
358 }
359
360 void
361 fscreate(Req *r)
362 {
363         File *f;
364         int p;
365
366         f = r->fid->file;
367         p = r->ifcall.perm;
368         if((p & DMDIR) != 0)
369                 p = (p & ~0777) | ((p & f->mode) & 0777);
370         else
371                 p = (p & ~0666) | ((p & f->mode) & 0666);
372         if((f = createfile(f, r->ifcall.name, r->fid->uid, p, nil)) == nil){
373                 responderror(r);
374                 return;
375         }
376         f->atime = f->mtime = time(0);
377         f->aux = nil;
378         r->fid->file = f;
379         r->ofcall.qid = f->qid;
380         respond(r, nil);
381 }
382
383 void
384 fsopen(Req *r)
385 {
386         File *f;
387
388         f = r->fid->file;
389         if((f->mode & DMEXCL) != 0){
390                 if(f->ref > 2 && (long)((ulong)time(0)-(ulong)f->atime) < 300){
391                         respond(r, Elocked);
392                         return;
393                 }
394         }
395         if((f->mode & DMAPPEND) == 0 && (r->ifcall.mode & OTRUNC) != 0){
396                 truncfile(f, 0);
397                 accessfile(f, AWRITE);
398         }
399         respond(r, nil);
400 }
401
402 void
403 fsdestroyfid(Fid *fid)
404 {
405         File *f;
406
407         f = fid->file;
408         if(fid->omode != -1 && (fid->omode & ORCLOSE) != 0 && f != nil && f->parent != nil)
409                 removefile(f);
410 }
411
412 void
413 fsdestroyfile(File *f)
414 {
415         truncfile(f, 0);
416 }
417
418 void
419 fsstart(Srv *)
420 {
421         char buf[40];
422         int ctl;
423
424         if(private){
425                 snprint(buf, sizeof buf, "/proc/%d/ctl", getpid());
426                 if((ctl = open(buf, OWRITE)) < 0)
427                         sysfatal("can't protect memory: %r");
428                 fprint(ctl, "noswap\n");
429                 fprint(ctl, "private\n");
430                 close(ctl);
431         }
432 }
433
434 Srv fs = {
435         .open=          fsopen,
436         .read=          fsread,
437         .write=         fswrite,
438         .wstat=         fswstat,
439         .create=        fscreate,
440         .destroyfid=    fsdestroyfid,
441
442         .start=         fsstart,
443 };
444
445 void
446 usage(void)
447 {
448         fprint(2, "usage: %s [-Dipsubac] [-m mountpoint] [-S srvname]\n", argv0);
449         exits("usage");
450 }
451
452 void
453 main(int argc, char **argv)
454 {
455         char *srvname = nil;
456         char *mtpt = "/tmp";
457         int mountflags, stdio;
458
459         fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile);
460
461         mountflags = stdio = 0;
462         ARGBEGIN{
463         case 'D':
464                 chatty9p++;
465                 break;
466         case 's':
467                 srvname = "ramfs";
468                 mtpt = nil;
469                 break;
470         case 'S':
471                 srvname = EARGF(usage());
472                 mtpt = nil;
473                 break;
474         case 'm':
475                 mtpt = EARGF(usage());
476                 break;
477         case 'i':
478                 stdio = 1;
479                 break;
480         case 'p':
481                 private = 1;
482                 break;
483         case 'u':
484                 rampool.maxsize = (uintptr)~0;
485                 break;
486         case 'b':
487                 mountflags |= MBEFORE;
488                 break;
489         case 'c':
490                 mountflags |= MCREATE;
491                 break;
492         case 'a':
493                 mountflags |= MAFTER;
494                 break;
495         default:
496                 usage();
497         }ARGEND;
498         if(argc > 0)
499                 usage();
500
501         if(stdio){
502                 fs.infd = 0;
503                 fs.outfd = 1;
504                 srv(&fs);
505                 exits(0);
506         }
507
508         if(srvname == nil && mtpt == nil)
509                 sysfatal("must specify -S, or -m option");
510
511         if(mountflags == 0)
512                 mountflags = MREPL | MCREATE;
513         postmountsrv(&fs, srvname, mtpt, mountflags);
514         exits(0);
515 }