kernel: make the mntcache robust against fileserver like fossil that do not change the qid.vers on wstat

introducing new ctrunc() function that invalidates any caches
for the passed in chan, invoked when handling wstat with a
specified file length or on file creation/truncation.

test program to reproduce the problem:

#include <u.h>
#include <libc.h>
#include <libsec.h>

void
main(int argc, char *argv[])
{
	int fd;
	Dir *d, nd;

	fd = create("xxx", ORDWR, 0666);
	write(fd, "1234", 4);
	d = dirstat("xxx");
	assert(d->length == 4);
	nulldir(&nd);
	nd.length = 0;
	dirwstat("xxx", &nd);
	d = dirstat("xxx");
	assert(d->length == 0);
	fd = open("xxx", OREAD);
	assert(read(fd, (void*)&d, 4) == 0);
}
This commit is contained in:
cinap_lenrek 2017-01-12 20:13:20 +01:00
parent 4aeefba681
commit 47f07b2669
3 changed files with 47 additions and 13 deletions

View file

@ -187,7 +187,7 @@ ccache(Chan *c)
return nil; return nil;
} }
void int
copen(Chan *c) copen(Chan *c)
{ {
Mntcache *m, *f, **l; Mntcache *m, *f, **l;
@ -195,19 +195,20 @@ copen(Chan *c)
/* directories aren't cacheable */ /* directories aren't cacheable */
if(c->qid.type&QTDIR){ if(c->qid.type&QTDIR){
c->mcp = nil; c->mcp = nil;
return; return 0;
} }
lock(&cache); lock(&cache);
m = clookup(c, 1); m = clookup(c, 0);
if(m == nil) if(m != nil){
m = cache.head;
else if(m->qid.vers == c->qid.vers) {
ctail(m); ctail(m);
unlock(&cache); unlock(&cache);
c->mcp = m; c->mcp = m;
return; return 1;
} }
m = clookup(c, 1);
if(m == nil)
m = cache.head;
ctail(m); ctail(m);
l = &cache.hash[m->qid.path%NHASH]; l = &cache.hash[m->qid.path%NHASH];
@ -234,7 +235,7 @@ copen(Chan *c)
unlock(&cache); unlock(&cache);
cacheunlock(m); cacheunlock(m);
c->mcp = f; c->mcp = f;
return; return 1;
} }
} }
@ -251,10 +252,9 @@ copen(Chan *c)
m->rah.vers = m->qid.vers; m->rah.vers = m->qid.vers;
mntrahinit(&m->rah); mntrahinit(&m->rah);
cnodata(m); cnodata(m);
cacheunlock(m); cacheunlock(m);
c->mcp = m; c->mcp = m;
return 0;
} }
enum { enum {
@ -482,6 +482,31 @@ cwrite(Chan* c, uchar *buf, int len, vlong off)
cachedata(m, buf, len, off); cachedata(m, buf, len, off);
} }
void
ctrunc(Chan *c)
{
Mntcache *m;
if(c->qid.type&QTDIR)
return;
if((c->flag&COPEN) == 0){
lock(&cache);
c->mcp = clookup(c, 0);
unlock(&cache);
}
m = ccache(c);
if(m == nil)
return;
mntrahinit(&m->rah);
cnodata(m);
cacheunlock(m);
if((c->flag&COPEN) == 0)
c->mcp = nil;
}
void void
cclunk(Chan *c) cclunk(Chan *c)
{ {

View file

@ -521,8 +521,11 @@ mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
poperror(); poperror();
mntfree(r); mntfree(r);
if(c->flag & CCACHE) if(c->flag & CCACHE){
copen(c); if(copen(c))
if(type == Tcreate || (omode&OTRUNC) != 0)
ctrunc(c);
}
return c; return c;
} }
@ -620,6 +623,11 @@ mntwstat(Chan *c, uchar *dp, int n)
mountrpc(m, r); mountrpc(m, r);
poperror(); poperror();
mntfree(r); mntfree(r);
if(c->flag & CCACHE)
if(GBIT64(&dp[STATFIXLEN-4*BIT16SZ-BIT64SZ]) != ~0ULL)
ctrunc(c);
return n; return n;
} }

View file

@ -41,13 +41,14 @@ void confinit(void);
int consactive(void); int consactive(void);
void (*consdebug)(void); void (*consdebug)(void);
void cpushutdown(void); void cpushutdown(void);
void copen(Chan*); int copen(Chan*);
void cclunk(Chan*); void cclunk(Chan*);
Block* concatblock(Block*); Block* concatblock(Block*);
Block* copyblock(Block*, int); Block* copyblock(Block*, int);
void copypage(Page*, Page*); void copypage(Page*, Page*);
void countpagerefs(ulong*, int); void countpagerefs(ulong*, int);
int cread(Chan*, uchar*, int, vlong); int cread(Chan*, uchar*, int, vlong);
void ctrunc(Chan*);
void cunmount(Chan*, Chan*); void cunmount(Chan*, Chan*);
void cupdate(Chan*, uchar*, int, vlong); void cupdate(Chan*, uchar*, int, vlong);
void cwrite(Chan*, uchar*, int, vlong); void cwrite(Chan*, uchar*, int, vlong);