plan9fox/sys/src/cmd/ramfs.c
cinap_lenrek 51f4f46ae0 ramfs: fix truncfile() for non multiple of extend size (64k)
The calculation of the last block size is wrong and we can
only shrink the size of the last data block, not extend it.
2021-02-27 15:08:34 +01:00

517 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 is 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){
if(o < x->ent[i]->size)
x->ent[i]->size = o;
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, stdio;
fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile);
mountflags = stdio = 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':
stdio = 1;
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(stdio){
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);
}