/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * Manage tree of VtFiles stored in the block cache. * * The single point of truth for the info about the VtFiles themselves * is the block data. Because of this, there is no explicit locking of * VtFile structures, and indeed there may be more than one VtFile * structure for a given Venti file. They synchronize through the * block cache. * * This is a bit simpler than fossil because there are no epochs * or tags or anything else. Just mutable local blocks and immutable * Venti blocks. */ #include <u.h>#include <libc.h>#include <venti.h> #define MaxBlock (1UL<<31) static char ENotDir[] = "walk in non-directory";static char ETooBig[] = "file too big";/* static char EBadAddr[] = "bad address"; */static char ELabelMismatch[] = "label mismatch"; static int sizetodepth(uvlong s, int psize, int dsize);static VtBlock *fileload(VtFile *r, VtEntry *e);static int shrinkdepth(VtFile*, VtBlock*, VtEntry*, int);static int shrinksize(VtFile*, VtEntry*, uvlong);static int growdepth(VtFile*, VtBlock*, VtEntry*, int); #define ISLOCKED(r) ((r)->b != nil)#define DEPTH(t) ((t)&VtTypeDepthMask) static VtFile *vtfilealloc(VtCache *c, VtBlock *b, VtFile *p, u32int offset, int mode){ int epb; u32int size; VtEntry e; VtFile *r; assert(p==nil || ISLOCKED(p)); if(p == nil){ assert(offset == 0); epb = 1; }else epb = p->dsize / VtEntrySize; if(b->type != VtDirType){ werrstr("bad block type %#uo", b->type); return nil; } /* * a non-active entry is the only thing that * can legitimately happen here. all the others * get prints. */ if(vtentryunpack(&e, b->data, offset % epb) < 0){ fprint(2, "vtentryunpack failed: %r (%.*H)\n", VtEntrySize, b->data+VtEntrySize*(offset%epb)); return nil; } if(!(e.flags & VtEntryActive)){ werrstr("entry not active"); return nil; } if(DEPTH(e.type) < sizetodepth(e.size, e.psize, e.dsize)){ fprint(2, "depth %ud size %llud psize %ud dsize %ud\n", DEPTH(e.type), e.size, e.psize, e.dsize); werrstr("bad depth"); return nil; } size = vtcacheblocksize(c); if(e.dsize > size || e.psize > size){ werrstr("block sizes %ud, %ud bigger than cache block size %ud", e.psize, e.dsize, size); return nil; } r = vtmallocz(sizeof(VtFile)); r->c = c; r->mode = mode; r->dsize = e.dsize; r->psize = e.psize; r->gen = e.gen; r->dir = (e.type & VtTypeBaseMask) == VtDirType; r->ref = 1; r->parent = p; if(p){ qlock(&p->lk); assert(mode == VtOREAD || p->mode == VtORDWR); p->ref++; qunlock(&p->lk); }else{ assert(b->addr != NilBlock); r->local = 1; } memmove(r->score, b->score, VtScoreSize); r->offset = offset; r->epb = epb; return r;} VtFile *vtfileroot(VtCache *c, u32int addr, int mode){ VtFile *r; VtBlock *b; b = vtcachelocal(c, addr, VtDirType); if(b == nil) return nil; r = vtfilealloc(c, b, nil, 0, mode); vtblockput(b); return r;} VtFile*vtfileopenroot(VtCache *c, VtEntry *e){ VtBlock *b; VtFile *f; b = vtcacheallocblock(c, VtDirType); if(b == nil) return nil; vtentrypack(e, b->data, 0); f = vtfilealloc(c, b, nil, 0, VtORDWR); vtblockput(b); return f;} VtFile *vtfilecreateroot(VtCache *c, int psize, int dsize, int type){ VtEntry e; memset(&e, 0, sizeof e); e.flags = VtEntryActive; e.psize = psize; e.dsize = dsize; e.type = type; memmove(e.score, vtzeroscore, VtScoreSize); return vtfileopenroot(c, &e);} VtFile *vtfileopen(VtFile *r, u32int offset, int mode){ ulong bn; VtBlock *b; assert(ISLOCKED(r)); if(!r->dir){ werrstr(ENotDir); return nil; } bn = offset/(r->dsize/VtEntrySize); b = vtfileblock(r, bn, mode); if(b == nil) return nil; r = vtfilealloc(r->c, b, r, offset, mode); vtblockput(b); return r;} VtFile*vtfilecreate(VtFile *r, int psize, int dsize, int type){ return _vtfilecreate(r, -1, psize, dsize, type);} VtFile*_vtfilecreate(VtFile *r, int o, int psize, int dsize, int type){ int i; VtBlock *b; u32int bn, size; VtEntry e; int epb; VtFile *rr; u32int offset; assert(ISLOCKED(r)); assert(psize <= VtMaxLumpSize); assert(dsize <= VtMaxLumpSize); assert(type == VtDirType || type == VtDataType); if(!r->dir){ werrstr(ENotDir); return nil; } epb = r->dsize/VtEntrySize; size = vtfilegetdirsize(r); /* * look at a random block to see if we can find an empty entry */ if(o == -1){ offset = lnrand(size+1); offset -= offset % epb; }else offset = o; /* try the given block and then try the last block */ for(;;){ bn = offset/epb; b = vtfileblock(r, bn, VtORDWR); if(b == nil) return nil; for(i=offset%r->epb; i<epb; i++){ if(vtentryunpack(&e, b->data, i) < 0) continue; if((e.flags&VtEntryActive) == 0 && e.gen != ~0) goto Found; } vtblockput(b); if(offset == size){ fprint(2, "vtfilecreate: cannot happen\n"); werrstr("vtfilecreate: cannot happen"); return nil; } offset = size; } Found: /* found an entry - gen already set */ e.psize = psize; e.dsize = dsize; e.flags = VtEntryActive; e.type = type; e.size = 0; memmove(e.score, vtzeroscore, VtScoreSize); vtentrypack(&e, b->data, i); offset = bn*epb + i; if(offset+1 > size){ if(vtfilesetdirsize(r, offset+1) < 0){ vtblockput(b); return nil; } } rr = vtfilealloc(r->c, b, r, offset, VtORDWR); vtblockput(b); return rr;} static intvtfilekill(VtFile *r, int doremove){ VtEntry e; VtBlock *b; assert(ISLOCKED(r)); b = fileload(r, &e); if(b == nil) return -1; if(doremove==0 && e.size == 0){ /* already truncated */ vtblockput(b); return 0; } if(doremove){ if(e.gen != ~0) e.gen++; e.dsize = 0; e.psize = 0; e.flags = 0; }else e.flags &= ~VtEntryLocal; e.type = 0; e.size = 0; memmove(e.score, vtzeroscore, VtScoreSize); vtentrypack(&e, b->data, r->offset % r->epb); vtblockput(b); if(doremove){ vtfileunlock(r); vtfileclose(r); } return 0;} intvtfileremove(VtFile *r){ return vtfilekill(r, 1);} intvtfiletruncate(VtFile *r){ return vtfilekill(r, 0);} uvlongvtfilegetsize(VtFile *r){ VtEntry e; VtBlock *b; assert(ISLOCKED(r)); b = fileload(r, &e); if(b == nil) return ~(uvlong)0; vtblockput(b); return e.size;} static intshrinksize(VtFile *r, VtEntry *e, uvlong size){ int i, depth, type, isdir, ppb; uvlong ptrsz; uchar score[VtScoreSize]; VtBlock *b; b = vtcacheglobal(r->c, e->score, e->type); if(b == nil) return -1; ptrsz = e->dsize; ppb = e->psize/VtScoreSize; type = e->type; depth = DEPTH(type); for(i=0; i+1<depth; i++) ptrsz *= ppb; isdir = r->dir; while(depth > 0){ if(b->addr == NilBlock){ /* not worth copying the block just so we can zero some of it */ vtblockput(b); return -1; } /* * invariant: each pointer in the tree rooted at b accounts for ptrsz bytes */ /* zero the pointers to unnecessary blocks */ i = (size+ptrsz-1)/ptrsz; for(; i<ppb; i++) memmove(b->data+i*VtScoreSize, vtzeroscore, VtScoreSize); /* recurse (go around again) on the partially necessary block */ i = size/ptrsz; size = size%ptrsz; if(size == 0){ vtblockput(b); return 0; } ptrsz /= ppb; type--; memmove(score, b->data+i*VtScoreSize, VtScoreSize); vtblockput(b); b = vtcacheglobal(r->c, score, type); if(b == nil) return -1; } if(b->addr == NilBlock){ vtblockput(b); return -1; } /* * No one ever truncates BtDir blocks. */ if(depth==0 && !isdir && e->dsize > size) memset(b->data+size, 0, e->dsize-size); vtblockput(b); return 0;} intvtfilesetsize(VtFile *r, u64int size){ int depth, edepth; VtEntry e; VtBlock *b; assert(ISLOCKED(r)); if(size == 0) return vtfiletruncate(r); if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize){ werrstr(ETooBig); return -1; } b = fileload(r, &e); if(b == nil) return -1; /* quick out */ if(e.size == size){ vtblockput(b); return 0; } depth = sizetodepth(size, e.psize, e.dsize); edepth = DEPTH(e.type); if(depth < edepth){ if(shrinkdepth(r, b, &e, depth) < 0){ vtblockput(b); return -1; } }else if(depth > edepth){ if(growdepth(r, b, &e, depth) < 0){ vtblockput(b); return -1; } } if(size < e.size) shrinksize(r, &e, size); e.size = size; vtentrypack(&e, b->data, r->offset % r->epb); vtblockput(b); return 0;} intvtfilesetdirsize(VtFile *r, u32int ds){ uvlong size; int epb; assert(ISLOCKED(r)); epb = r->dsize/VtEntrySize; size = (uvlong)r->dsize*(ds/epb); size += VtEntrySize*(ds%epb); return vtfilesetsize(r, size);} u32intvtfilegetdirsize(VtFile *r){ ulong ds; uvlong size; int epb; assert(ISLOCKED(r)); epb = r->dsize/VtEntrySize; size = vtfilegetsize(r); ds = epb*(size/r->dsize); ds += (size%r->dsize)/VtEntrySize; return ds;} intvtfilegetentry(VtFile *r, VtEntry *e){ VtBlock *b; assert(ISLOCKED(r)); b = fileload(r, e); if(b == nil) return -1; vtblockput(b); return 0;} intvtfilesetentry(VtFile *r, VtEntry *e){ VtBlock *b; VtEntry ee; assert(ISLOCKED(r));Lines 1–500 of 1291. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * Manage tree of VtFiles stored in the block cache. * * The single point of truth for the info about the VtFiles themselves * is the block data. Because of this, there is no explicit locking of * VtFile structures, and indeed there may be more than one VtFile * structure for a given Venti file. They synchronize through the * block cache. * * This is a bit simpler than fossil because there are no epochs * or tags or anything else. Just mutable local blocks and immutable * Venti blocks. */ #include <u.h> #include <libc.h> #include <venti.h> #define MaxBlock (1UL<<31) static char ENotDir[] = "walk in non-directory"; static char ETooBig[] = "file too big"; /* static char EBadAddr[] = "bad address"; */ static char ELabelMismatch[] = "label mismatch"; static int sizetodepth(uvlong s, int psize, int dsize); static VtBlock *fileload(VtFile *r, VtEntry *e); static int shrinkdepth(VtFile*, VtBlock*, VtEntry*, int); static int shrinksize(VtFile*, VtEntry*, uvlong); static int growdepth(VtFile*, VtBlock*, VtEntry*, int); #define ISLOCKED(r) ((r)->b != nil) #define DEPTH(t) ((t)&VtTypeDepthMask) static VtFile * vtfilealloc(VtCache *c, VtBlock *b, VtFile *p, u32int offset, int mode) { int epb; u32int size; VtEntry e; VtFile *r; assert(p==nil || ISLOCKED(p)); if(p == nil){ assert(offset == 0); epb = 1; }else epb = p->dsize / VtEntrySize; if(b->type != VtDirType){ werrstr("bad block type %#uo", b->type); return nil; } /* * a non-active entry is the only thing that * can legitimately happen here. all the others * get prints. */ if(vtentryunpack(&e, b->data, offset % epb) < 0){ fprint(2, "vtentryunpack failed: %r (%.*H)\n", VtEntrySize, b->data+VtEntrySize*(offset%epb)); return nil; } if(!(e.flags & VtEntryActive)){ werrstr("entry not active"); return nil; } if(DEPTH(e.type) < sizetodepth(e.size, e.psize, e.dsize)){ fprint(2, "depth %ud size %llud psize %ud dsize %ud\n", DEPTH(e.type), e.size, e.psize, e.dsize); werrstr("bad depth"); return nil; } size = vtcacheblocksize(c); if(e.dsize > size || e.psize > size){ werrstr("block sizes %ud, %ud bigger than cache block size %ud", e.psize, e.dsize, size); return nil; } r = vtmallocz(sizeof(VtFile)); r->c = c; r->mode = mode; r->dsize = e.dsize; r->psize = e.psize; r->gen = e.gen; r->dir = (e.type & VtTypeBaseMask) == VtDirType; r->ref = 1; r->parent = p; if(p){ qlock(&p->lk); assert(mode == VtOREAD || p->mode == VtORDWR); p->ref++; qunlock(&p->lk); }else{ assert(b->addr != NilBlock); r->local = 1; } memmove(r->score, b->score, VtScoreSize); r->offset = offset; r->epb = epb; return r; } VtFile * vtfileroot(VtCache *c, u32int addr, int mode) { VtFile *r; VtBlock *b; b = vtcachelocal(c, addr, VtDirType); if(b == nil) return nil; r = vtfilealloc(c, b, nil, 0, mode); vtblockput(b); return r; } VtFile* vtfileopenroot(VtCache *c, VtEntry *e) { VtBlock *b; VtFile *f; b = vtcacheallocblock(c, VtDirType); if(b == nil) return nil; vtentrypack(e, b->data, 0); f = vtfilealloc(c, b, nil, 0, VtORDWR); vtblockput(b); return f; } VtFile * vtfilecreateroot(VtCache *c, int psize, int dsize, int type) { VtEntry e; memset(&e, 0, sizeof e); e.flags = VtEntryActive; e.psize = psize; e.dsize = dsize; e.type = type; memmove(e.score, vtzeroscore, VtScoreSize); return vtfileopenroot(c, &e); } VtFile * vtfileopen(VtFile *r, u32int offset, int mode) { ulong bn; VtBlock *b; assert(ISLOCKED(r)); if(!r->dir){ werrstr(ENotDir); return nil; } bn = offset/(r->dsize/VtEntrySize); b = vtfileblock(r, bn, mode); if(b == nil) return nil; r = vtfilealloc(r->c, b, r, offset, mode); vtblockput(b); return r; } VtFile* vtfilecreate(VtFile *r, int psize, int dsize, int type) { return _vtfilecreate(r, -1, psize, dsize, type); } VtFile* _vtfilecreate(VtFile *r, int o, int psize, int dsize, int type) { int i; VtBlock *b; u32int bn, size; VtEntry e; int epb; VtFile *rr; u32int offset; assert(ISLOCKED(r)); assert(psize <= VtMaxLumpSize); assert(dsize <= VtMaxLumpSize); assert(type == VtDirType || type == VtDataType); if(!r->dir){ werrstr(ENotDir); return nil; } epb = r->dsize/VtEntrySize; size = vtfilegetdirsize(r); /* * look at a random block to see if we can find an empty entry */ if(o == -1){ offset = lnrand(size+1); offset -= offset % epb; }else offset = o; /* try the given block and then try the last block */ for(;;){ bn = offset/epb; b = vtfileblock(r, bn, VtORDWR); if(b == nil) return nil; for(i=offset%r->epb; i<epb; i++){ if(vtentryunpack(&e, b->data, i) < 0) continue; if((e.flags&VtEntryActive) == 0 && e.gen != ~0) goto Found; } vtblockput(b); if(offset == size){ fprint(2, "vtfilecreate: cannot happen\n"); werrstr("vtfilecreate: cannot happen"); return nil; } offset = size; } Found: /* found an entry - gen already set */ e.psize = psize; e.dsize = dsize; e.flags = VtEntryActive; e.type = type; e.size = 0; memmove(e.score, vtzeroscore, VtScoreSize); vtentrypack(&e, b->data, i); offset = bn*epb + i; if(offset+1 > size){ if(vtfilesetdirsize(r, offset+1) < 0){ vtblockput(b); return nil; } } rr = vtfilealloc(r->c, b, r, offset, VtORDWR); vtblockput(b); return rr; } static int vtfilekill(VtFile *r, int doremove) { VtEntry e; VtBlock *b; assert(ISLOCKED(r)); b = fileload(r, &e); if(b == nil) return -1; if(doremove==0 && e.size == 0){ /* already truncated */ vtblockput(b); return 0; } if(doremove){ if(e.gen != ~0) e.gen++; e.dsize = 0; e.psize = 0; e.flags = 0; }else e.flags &= ~VtEntryLocal; e.type = 0; e.size = 0; memmove(e.score, vtzeroscore, VtScoreSize); vtentrypack(&e, b->data, r->offset % r->epb); vtblockput(b); if(doremove){ vtfileunlock(r); vtfileclose(r); } return 0; } int vtfileremove(VtFile *r) { return vtfilekill(r, 1); } int vtfiletruncate(VtFile *r) { return vtfilekill(r, 0); } uvlong vtfilegetsize(VtFile *r) { VtEntry e; VtBlock *b; assert(ISLOCKED(r)); b = fileload(r, &e); if(b == nil) return ~(uvlong)0; vtblockput(b); return e.size; } static int shrinksize(VtFile *r, VtEntry *e, uvlong size) { int i, depth, type, isdir, ppb; uvlong ptrsz; uchar score[VtScoreSize]; VtBlock *b; b = vtcacheglobal(r->c, e->score, e->type); if(b == nil) return -1; ptrsz = e->dsize; ppb = e->psize/VtScoreSize; type = e->type; depth = DEPTH(type); for(i=0; i+1<depth; i++) ptrsz *= ppb; isdir = r->dir; while(depth > 0){ if(b->addr == NilBlock){ /* not worth copying the block just so we can zero some of it */ vtblockput(b); return -1; } /* * invariant: each pointer in the tree rooted at b accounts for ptrsz bytes */ /* zero the pointers to unnecessary blocks */ i = (size+ptrsz-1)/ptrsz; for(; i<ppb; i++) memmove(b->data+i*VtScoreSize, vtzeroscore, VtScoreSize); /* recurse (go around again) on the partially necessary block */ i = size/ptrsz; size = size%ptrsz; if(size == 0){ vtblockput(b); return 0; } ptrsz /= ppb; type--; memmove(score, b->data+i*VtScoreSize, VtScoreSize); vtblockput(b); b = vtcacheglobal(r->c, score, type); if(b == nil) return -1; } if(b->addr == NilBlock){ vtblockput(b); return -1; } /* * No one ever truncates BtDir blocks. */ if(depth==0 && !isdir && e->dsize > size) memset(b->data+size, 0, e->dsize-size); vtblockput(b); return 0; } int vtfilesetsize(VtFile *r, u64int size) { int depth, edepth; VtEntry e; VtBlock *b; assert(ISLOCKED(r)); if(size == 0) return vtfiletruncate(r); if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize){ werrstr(ETooBig); return -1; } b = fileload(r, &e); if(b == nil) return -1; /* quick out */ if(e.size == size){ vtblockput(b); return 0; } depth = sizetodepth(size, e.psize, e.dsize); edepth = DEPTH(e.type); if(depth < edepth){ if(shrinkdepth(r, b, &e, depth) < 0){ vtblockput(b); return -1; } }else if(depth > edepth){ if(growdepth(r, b, &e, depth) < 0){ vtblockput(b); return -1; } } if(size < e.size) shrinksize(r, &e, size); e.size = size; vtentrypack(&e, b->data, r->offset % r->epb); vtblockput(b); return 0; } int vtfilesetdirsize(VtFile *r, u32int ds) { uvlong size; int epb; assert(ISLOCKED(r)); epb = r->dsize/VtEntrySize; size = (uvlong)r->dsize*(ds/epb); size += VtEntrySize*(ds%epb); return vtfilesetsize(r, size); } u32int vtfilegetdirsize(VtFile *r) { ulong ds; uvlong size; int epb; assert(ISLOCKED(r)); epb = r->dsize/VtEntrySize; size = vtfilegetsize(r); ds = epb*(size/r->dsize); ds += (size%r->dsize)/VtEntrySize; return ds; } int vtfilegetentry(VtFile *r, VtEntry *e) { VtBlock *b; assert(ISLOCKED(r)); b = fileload(r, e); if(b == nil) return -1; vtblockput(b); return 0; } int vtfilesetentry(VtFile *r, VtEntry *e) { VtBlock *b; VtEntry ee; assert(ISLOCKED(r));