plan9fox/sys/src/cmd/ramfs.c
2016-11-17 02:09:57 +01:00

518 lines
8.3 KiB
C

#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <pool.h>
char Ebadoff[] = "bad file offset or count";
char Eexist[] = "file already exists";
char Enomem[] = "no memory";
char Eperm[] = "permission denied";
char Enotowner[] = "not owner";
char Elocked[] = "file locked";
enum {
Tdat = 0xbabababa,
Tind = 0xdadadada,
ESIZE = 64*1024,
};
#define MAXFSIZE ((0x7fffffffll/sizeof(Ram*))*ESIZE)
typedef struct Ram Ram;
struct Ram
{
int type;
int size;
Ram **link;
Ram *ent[];
};
int private;
void*
ramalloc(ulong size)
{
void *v;
v = sbrk(size);
if(v == (void*)-1)
return nil;
return v;
}
void
rammoved(void*, void *to)
{
Ram **link, **elink, *x = to;
*x->link = x;
if(x->type != Tind)
return;
link = x->ent;
for(elink = link + (x->size / sizeof(Ram*)); link < elink; link++)
if((x = *link) != nil)
x->link = link;
}
void
ramnolock(Pool*)
{
}
Pool rampool = {
.name= "ram",
.maxsize= 800*1024*1024,
.minarena= 4*1024,
.quantum= 32,
.alloc= ramalloc,
.move= rammoved,
.lock= ramnolock,
.unlock= ramnolock,
.flags= 0,
};
void
accessfile(File *f, int a)
{
f->atime = time(0);
if(a & AWRITE){
f->mtime = f->atime;
f->qid.vers++;
}
}
void
fsread(Req *r)
{
int o, n, i, count;
vlong top, off;
File *f;
Ram *x;
char *p;
f = r->fid->file;
off = r->ifcall.offset;
count = r->ifcall.count;
if(count == 0 || off >= f->length || f->aux == nil){
r->ofcall.count = 0;
respond(r, nil);
return;
}
top = off + count;
if(top > MAXFSIZE){
respond(r, Ebadoff);
return;
}
if(top > f->length){
top = f->length;
count = top - off;
}
p = (char*)r->ofcall.data;
while(count > 0){
i = off / ESIZE;
o = off % ESIZE;
x = (Ram*)f->aux;
if(i < (x->size / sizeof(Ram*)))
x = x->ent[i];
else
x = nil;
if(x != nil && o < x->size){
n = x->size - o;
if(n > count)
n = count;
memmove(p, (char*)&x[1] + o, n);
} else {
n = ESIZE - o;
if(n > count)
n = count;
memset(p, 0, n);
}
p += n;
off += n;
count -= n;
}
accessfile(f, AREAD);
r->ofcall.count = p - (char*)r->ofcall.data;
respond(r, nil);
}
void
fswrite(Req *r)
{
int o, n, i, count;
Ram *x, **link;
vlong top, off;
File *f;
char *p;
f = r->fid->file;
off = r->ifcall.offset;
count = r->ifcall.count;
if(f->mode & DMAPPEND)
off = f->length;
if(count == 0){
r->ofcall.count = 0;
respond(r, nil);
return;
}
top = off + count;
if(top > MAXFSIZE){
respond(r, Ebadoff);
return;
}
n = ((top + ESIZE-1)/ESIZE) * sizeof(Ram*);
x = (Ram*)f->aux;
if(x == nil || x->size < n){
x = poolrealloc(&rampool, x, sizeof(Ram) + n);
if(x == nil){
respond(r, Enomem);
return;
}
link = (Ram**)&f->aux;
if(*link == nil){
memset(x, 0, sizeof(Ram));
x->type = Tind;
x->link = link;
*link = x;
} else if(x != *link)
rammoved(*link, x);
memset((char*)&x[1] + x->size, 0, n - x->size);
x->size = n;
}
p = (char*)r->ifcall.data;
while(count > 0){
i = off / ESIZE;
o = off % ESIZE;
n = ESIZE - o;
if(n > count)
n = count;
x = ((Ram*)f->aux)->ent[i];
if(x == nil || x->size < o+n){
x = poolrealloc(&rampool, x, sizeof(Ram) + o+n);
if(x == nil){
respond(r, Enomem);
return;
}
link = &((Ram*)f->aux)->ent[i];
if(*link == nil){
memset(x, 0, sizeof(Ram));
x->type = Tdat;
}
if(o > x->size)
memset((char*)&x[1] + x->size, 0, o - x->size);
x->size = o + n;
x->link = link;
*link = x;
}
memmove((char*)&x[1] + o, p, n);
p += n;
off += n;
count -= n;
}
if(top > f->length)
f->length = top;
accessfile(f, AWRITE);
r->ofcall.count = p - (char*)r->ifcall.data;
respond(r, nil);
}
void
truncfile(File *f, vlong l)
{
int i, o, n;
Ram *x;
x = (Ram*)f->aux;
if(x != nil){
n = x->size / sizeof(Ram*);
i = l / ESIZE;
if(i < n){
o = l % ESIZE;
if(o != 0 && x->ent[i] != nil){
x->ent[i]->size = o * sizeof(Ram*);
i++;
}
while(i < n){
if(x->ent[i] != nil){
poolfree(&rampool, x->ent[i]);
x->ent[i] = nil;
}
i++;
}
}
if(l == 0){
poolfree(&rampool, (Ram*)f->aux);
f->aux = nil;
}
}
f->length = l;
}
void
fswstat(Req *r)
{
File *f, *w;
char *u;
f = r->fid->file;
u = r->fid->uid;
/*
* To change length, must have write permission on file.
*/
if(r->d.length != ~0 && r->d.length != f->length){
if(r->d.length > MAXFSIZE){
respond(r, Ebadoff);
return;
}
if(!hasperm(f, u, AWRITE) || (f->mode & DMDIR) != 0)
goto Perm;
}
/*
* To change name, must have write permission in parent.
*/
if(r->d.name[0] != '\0' && strcmp(r->d.name, f->name) != 0){
if((w = f->parent) == nil)
goto Perm;
incref(w);
if(!hasperm(w, u, AWRITE)){
closefile(w);
goto Perm;
}
if((w = walkfile(w, r->d.name)) != nil){
closefile(w);
respond(r, Eexist);
return;
}
}
/*
* To change mode, must be owner or group leader.
* Because of lack of users file, leader=>group itself.
*/
if(r->d.mode != ~0 && f->mode != r->d.mode){
if(strcmp(u, f->uid) != 0)
if(strcmp(u, f->gid) != 0){
respond(r, Enotowner);
return;
}
}
/*
* To change group, must be owner and member of new group,
* or leader of current group and leader of new group.
* Second case cannot happen, but we check anyway.
*/
while(r->d.gid[0] != '\0' && strcmp(f->gid, r->d.gid) != 0){
if(strcmp(u, f->uid) == 0)
break;
if(strcmp(u, f->gid) == 0)
if(strcmp(u, r->d.gid) == 0)
break;
respond(r, Enotowner);
return;
}
if(r->d.mode != ~0){
f->mode = r->d.mode;
f->qid.type = f->mode >> 24;
}
if(r->d.name[0] != '\0'){
free(f->name);
f->name = estrdup9p(r->d.name);
}
if(r->d.length != ~0 && r->d.length != f->length)
truncfile(f, r->d.length);
accessfile(f, AWRITE);
if(r->d.mtime != ~0){
f->mtime = r->d.mtime;
}
respond(r, nil);
return;
Perm:
respond(r, Eperm);
}
void
fscreate(Req *r)
{
File *f;
int p;
f = r->fid->file;
p = r->ifcall.perm;
if((p & DMDIR) != 0)
p = (p & ~0777) | ((p & f->mode) & 0777);
else
p = (p & ~0666) | ((p & f->mode) & 0666);
if((f = createfile(f, r->ifcall.name, r->fid->uid, p, nil)) == nil){
responderror(r);
return;
}
f->atime = f->mtime = time(0);
f->aux = nil;
r->fid->file = f;
r->ofcall.qid = f->qid;
respond(r, nil);
}
void
fsopen(Req *r)
{
File *f;
f = r->fid->file;
if((f->mode & DMEXCL) != 0){
if(f->ref > 2 && (long)((ulong)time(0)-(ulong)f->atime) < 300){
respond(r, Elocked);
return;
}
}
if((f->mode & DMAPPEND) == 0 && (r->ifcall.mode & OTRUNC) != 0){
truncfile(f, 0);
accessfile(f, AWRITE);
}
respond(r, nil);
}
void
fsdestroyfid(Fid *fid)
{
File *f;
f = fid->file;
if(fid->omode != -1 && (fid->omode & ORCLOSE) != 0 && f != nil && f->parent != nil)
removefile(f);
}
void
fsdestroyfile(File *f)
{
truncfile(f, 0);
}
void
fsstart(Srv *)
{
char buf[40];
int ctl;
if(private){
snprint(buf, sizeof buf, "/proc/%d/ctl", getpid());
if((ctl = open(buf, OWRITE)) < 0)
sysfatal("can't protect memory: %r");
fprint(ctl, "noswap\n");
fprint(ctl, "private\n");
close(ctl);
}
}
Srv fs = {
.open= fsopen,
.read= fsread,
.write= fswrite,
.wstat= fswstat,
.create= fscreate,
.destroyfid= fsdestroyfid,
.start= fsstart,
};
void
usage(void)
{
fprint(2, "usage: %s [-Dipsubac] [-m mountpoint] [-S srvname]\n", argv0);
exits("usage");
}
void
main(int argc, char **argv)
{
char *srvname = nil;
char *mtpt = "/tmp";
int mountflags;
fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile);
mountflags = 0;
ARGBEGIN{
case 'D':
chatty9p++;
break;
case 's':
srvname = "ramfs";
mtpt = nil;
break;
case 'S':
srvname = EARGF(usage());
mtpt = nil;
break;
case 'm':
mtpt = EARGF(usage());
break;
case 'i':
fs.nopipe = 1;
srvname = nil;
mtpt = nil;
break;
case 'p':
private = 1;
break;
case 'u':
rampool.maxsize = (uintptr)~0;
break;
case 'b':
mountflags |= MBEFORE;
break;
case 'c':
mountflags |= MCREATE;
break;
case 'a':
mountflags |= MAFTER;
break;
default:
usage();
}ARGEND;
if(argc > 0)
usage();
if(fs.nopipe){
fs.infd = 0;
fs.outfd = 1;
srv(&fs);
exits(0);
}
if(srvname == nil && mtpt == nil)
sysfatal("must specify -S, or -m option");
if(mountflags == 0)
mountflags = MREPL | MCREATE;
postmountsrv(&fs, srvname, mtpt, mountflags);
exits(0);
}