#include "stdinc.h" #include "9.h" /* for consPrint */ #include "dat.h" #include "fns.h" #include "error.h" /* * locking order is upwards. A thread can hold the lock for a File * and then acquire the lock of its parent */ struct File { Fs *fs; /* immutable */ /* meta data for file: protected by the lk in the parent */ int ref; /* holds this data structure up */ int partial; /* file was never really open */ int removed; /* file has been removed */ int dirty; /* dir is dirty with respect to meta data in block */ u32int boff; /* block offset within msource for this file's meta data */ DirEntry dir; /* meta data for this file, including component name */ File *up; /* parent file (directory) */ File *next; /* sibling */ /* data for file */ VtLock *lk; /* lock for the following */ Source *source; Source *msource; /* for directories: meta data for children */ File *down; /* children */ int mode; int issnapshot; }; static int fileMetaFlush2(File*, char*); static u32int fileMetaAlloc(File*, DirEntry*, u32int); static int fileRLock(File*); static void fileRUnlock(File*); static int fileLock(File*); static void fileUnlock(File*); static void fileMetaLock(File*); static void fileMetaUnlock(File*); static void fileRAccess(File*); static void fileWAccess(File*, char*); static File * fileAlloc(Fs *fs) { File *f; f = vtMemAllocZ(sizeof(File)); f->lk = vtLockAlloc(); f->ref = 1; f->fs = fs; f->boff = NilBlock; f->mode = fs->mode; return f; } static void fileFree(File *f) { sourceClose(f->source); vtLockFree(f->lk); sourceClose(f->msource); deCleanup(&f->dir); memset(f, ~0, sizeof(File)); vtMemFree(f); } /* * the file is locked already * f->msource is unlocked */ static File * dirLookup(File *f, char *elem) { int i; MetaBlock mb; MetaEntry me; Block *b; Source *meta; File *ff; u32int bo, nb; meta = f->msource; b = nil; if(!sourceLock(meta, -1)) return nil; nb = (sourceGetSize(meta)+meta->dsize-1)/meta->dsize; for(bo=0; bodata, meta->dsize)) goto Err; if(mbSearch(&mb, elem, &i, &me)){ ff = fileAlloc(f->fs); if(!deUnpack(&ff->dir, &me)){ fileFree(ff); goto Err; } sourceUnlock(meta); blockPut(b); ff->boff = bo; ff->mode = f->mode; ff->issnapshot = f->issnapshot; return ff; } blockPut(b); b = nil; } vtSetError(ENoFile); /* fall through */ Err: sourceUnlock(meta); blockPut(b); return nil; } File * fileRoot(Source *r) { Block *b; Source *r0, *r1, *r2; MetaBlock mb; MetaEntry me; File *root, *mr; Fs *fs; b = nil; root = nil; mr = nil; r1 = nil; r2 = nil; fs = r->fs; if(!sourceLock(r, -1)) return nil; r0 = sourceOpen(r, 0, fs->mode, 0); if(r0 == nil) goto Err; r1 = sourceOpen(r, 1, fs->mode, 0); if(r1 == nil) goto Err; r2 = sourceOpen(r, 2, fs->mode, 0); if(r2 == nil) goto Err; mr = fileAlloc(fs); mr->msource = r2; r2 = nil; root = fileAlloc(fs); root->boff = 0; root->up = mr; root->source = r0; r0->file = root; /* point back to source */ r0 = nil; root->msource = r1; r1 = nil; mr->down = root; if(!sourceLock(mr->msource, -1)) goto Err; b = sourceBlock(mr->msource, 0, OReadOnly); sourceUnlock(mr->msource); if(b == nil) goto Err; if(!mbUnpack(&mb, b->data, mr->msource->dsize)) goto Err; meUnpack(&me, &mb, 0); if(!deUnpack(&root->dir, &me)) goto Err; blockPut(b); sourceUnlock(r); fileRAccess(root); return root; Err: blockPut(b); if(r0) sourceClose(r0); if(r1) sourceClose(r1); if(r2) sourceClose(r2); if(mr) fileFree(mr); if(root) fileFree(root); sourceUnlock(r); return nil; } static Source * fileOpenSource(File *f, u32int offset, u32int gen, int dir, uint mode, int issnapshot) { char *rname, *fname; Source *r; if(!sourceLock(f->source, mode)) return nil; r = sourceOpen(f->source, offset, mode, issnapshot); sourceUnlock(f->source); if(r == nil) return nil; if(r->gen != gen){ vtSetError(ERemoved); goto Err; } if(r->dir != dir && r->mode != -1){ /* this hasn't been as useful as we hoped it would be. */ rname = sourceName(r); fname = fileName(f); consPrint("%s: source %s for file %s: fileOpenSource: " "dir mismatch %d %d\n", f->source->fs->name, rname, fname, r->dir, dir); free(rname); free(fname); vtSetError(EBadMeta); goto Err; } return r; Err: sourceClose(r); return nil; } File * _fileWalk(File *f, char *elem, int partial) { File *ff; fileRAccess(f); if(elem[0] == 0){ vtSetError(EBadPath); return nil; } if(!fileIsDir(f)){ vtSetError(ENotDir); return nil; } if(strcmp(elem, ".") == 0){ return fileIncRef(f); } if(strcmp(elem, "..") == 0){ if(fileIsRoot(f)) return fileIncRef(f); return fileIncRef(f->up); } if(!fileLock(f)) return nil; for(ff = f->down; ff; ff=ff->next){ if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){ ff->ref++; goto Exit; } } ff = dirLookup(f, elem); if(ff == nil) goto Err; if(ff->dir.mode & ModeSnapshot){ ff->mode = OReadOnly; ff->issnapshot = 1; } if(partial){ /* * Do nothing. We're opening this file only so we can clri it. * Usually the sources can't be opened, hence we won't even bother. * Be VERY careful with the returned file. If you hand it to a routine * expecting ff->source and/or ff->msource to be non-nil, we're * likely to dereference nil. FileClri should be the only routine * setting partial. */ ff->partial = 1; }else if(ff->dir.mode & ModeDir){ ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode, ff->issnapshot); ff->msource = fileOpenSource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode, ff->issnapshot); if(ff->source == nil || ff->msource == nil) goto Err; }else{ ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode, ff->issnapshot); if(ff->source == nil) goto Err; } /* link in and up parent ref count */ if (ff->source) ff->source->file = ff; /* point back */ ff->next = f->down; f->down = ff; ff->up = f; fileIncRef(f); Exit: fileUnlock(f); return ff; Err: fileUnlock(f); if(ff != nil) fileDecRef(ff); return nil; } File * fileWalk(File *f, char *elem) { return _fileWalk(f, elem, 0); } File * _fileOpen(Fs *fs, char *path, int partial) { File *f, *ff; char *p, elem[VtMaxStringSize], *opath; int n; f = fs->file; fileIncRef(f); opath = path; while(*path != 0){ for(p = path; *p && *p != '/'; p++) ; n = p - path; if(n > 0){ if(n > VtMaxStringSize){ vtSetError("%s: element too long", EBadPath); goto Err; } memmove(elem, path, n); elem[n] = 0; ff = _fileWalk(f, elem, partial && *p=='\0'); if(ff == nil){ vtSetError("%.*s: %R", utfnlen(opath, p-opath), opath); goto Err; } fileDecRef(f); f = ff; } if(*p == '/') p++; path = p; } return f; Err: fileDecRef(f); return nil; } File* fileOpen(Fs *fs, char *path) { return _fileOpen(fs, path, 0); } static void fileSetTmp(File *f, int istmp) { int i; Entry e; Source *r; for(i=0; i<2; i++){ if(i==0) r = f->source; else r = f->msource; if(r == nil) continue; if(!sourceGetEntry(r, &e)){ fprint(2, "sourceGetEntry failed (cannot happen): %r\n"); continue; } if(istmp) e.flags |= VtEntryNoArchive; else e.flags &= ~VtEntryNoArchive; if(!sourceSetEntry(r, &e)){ fprint(2, "sourceSetEntry failed (cannot happen): %r\n"); continue; } } } File * fileCreate(File *f, char *elem, ulong mode, char *uid) { File *ff; DirEntry *dir; Source *pr, *r, *mr; int isdir; if(!fileLock(f)) return nil; r = nil; mr = nil; for(ff = f->down; ff; ff=ff->next){ if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){ ff = nil; vtSetError(EExists); goto Err1; } } ff = dirLookup(f, elem); if(ff != nil){ vtSetError(EExists); goto Err1; } pr = f->source; if(pr->mode != OReadWrite){ vtSetError(EReadOnly); goto Err1; } if(!sourceLock2(f->source, f->msource, -1)) goto Err1; ff = fileAlloc(f->fs); isdir = mode & ModeDir; r = sourceCreate(pr, pr->dsize, isdir, 0); if(r == nil) goto Err; if(isdir){ mr = sourceCreate(pr, pr->dsize, 0, r->offset); if(mr == nil) goto Err; } dir = &ff->dir; dir->elem = vtStrDup(elem); dir->entry = r->offset; dir->gen = r->gen; if(isdir){ dir->mentry = mr->offset; dir->mgen = mr->gen; } dir->size = 0; if(!fsNextQid(f->fs, &dir->qid)) goto Err; dir->uid = vtStrDup(uid); dir->gid = vtStrDup(f->dir.gid); dir->mid = vtStrDup(uid); dir->mtime = time(0L); dir->mcount = 0; dir->ctime = dir->mtime; dir->atime = dir->mtime; dir->mode = mode; ff->boff = fileMetaAlloc(f, dir, 0); if(ff->boff == NilBlock) goto Err; sourceUnlock(f->source); sourceUnlock(f->msource); ff->source = r; r->file = ff; /* point back */ ff->msource = mr; if(mode&ModeTemporary){ if(!sourceLock2(r, mr, -1)) goto Err1; fileSetTmp(ff, 1); sourceUnlock(r); if(mr) sourceUnlock(mr); } /* committed */ /* link in and up parent ref count */ ff->next = f->down; f->down = ff; ff->up = f; fileIncRef(f); fileWAccess(f, uid); fileUnlock(f); return ff; Err: sourceUnlock(f->source); sourceUnlock(f->msource); Err1: if(r){ sourceLock(r, -1); sourceRemove(r); } if(mr){ sourceLock(mr, -1); sourceRemove(mr); } if(ff) fileDecRef(ff); fileUnlock(f); return 0; } int fileRead(File *f, void *buf, int cnt, vlong offset) { Source *s; uvlong size; u32int bn; int off, dsize, n, nn; Block *b; uchar *p; if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset); if(!fileRLock(f)) return -1; if(offset < 0){ vtSetError(EBadOffset); goto Err1; } fileRAccess(f); if(!sourceLock(f->source, OReadOnly)) goto Err1; s = f->source; dsize = s->dsize; size = sourceGetSize(s); if(offset >= size) offset = size; if(cnt > size-offset) cnt = size-offset; bn = offset/dsize; off = offset%dsize; p = buf; while(cnt > 0){ b = sourceBlock(s, bn, OReadOnly); if(b == nil) goto Err; n = cnt; if(n > dsize-off) n = dsize-off; nn = dsize-off; if(nn > n) nn = n; memmove(p, b->data+off, nn); memset(p+nn, 0, nn-n); off = 0; bn++; cnt -= n; p += n; blockPut(b); } sourceUnlock(s); fileRUnlock(f); return p-(uchar*)buf; Err: sourceUnlock(s); Err1: fileRUnlock(f); return -1; } /* * Changes the file block bn to be the given block score. * Very sneaky. Only used by flfmt. */ int fileMapBlock(File *f, ulong bn, uchar score[VtScoreSize], ulong tag) { Block *b; Entry e; Source *s; if(!fileLock(f)) return 0; s = nil; if(f->dir.mode & ModeDir){ vtSetError(ENotFile); goto Err; } if(f->source->mode != OReadWrite){ vtSetError(EReadOnly); goto Err; } if(!sourceLock(f->source, -1)) goto Err; s = f->source; b = _sourceBlock(s, bn, OReadWrite, 1, tag); if(b == nil) goto Err; if(!sourceGetEntry(s, &e)) goto Err; if(b->l.type == BtDir){ memmove(e.score, score, VtScoreSize); assert(e.tag == tag || e.tag == 0); e.tag = tag; e.flags |= VtEntryLocal; entryPack(&e, b->data, f->source->offset % f->source->epb); }else memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize); blockDirty(b); blockPut(b); sourceUnlock(s); fileUnlock(f); return 1; Err: if(s) sourceUnlock(s); fileUnlock(f); return 0; } int fileSetSize(File *f, uvlong size) { int r; if(!fileLock(f)) return 0; r = 0; if(f->dir.mode & ModeDir){ vtSetError(ENotFile); goto Err; } if(f->source->mode != OReadWrite){ vtSetError(EReadOnly); goto Err; } if(!sourceLock(f->source, -1)) goto Err; r = sourceSetSize(f->source, size); sourceUnlock(f->source); Err: fileUnlock(f); return r; } int fileWrite(File *f, void *buf, int cnt, vlong offset, char *uid) { Source *s; ulong bn; int off, dsize, n; Block *b; uchar *p; vlong eof; if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset); if(!fileLock(f)) return -1; s = nil; if(f->dir.mode & ModeDir){ vtSetError(ENotFile); goto Err; } if(f->source->mode != OReadWrite){ vtSetError(EReadOnly); goto Err; } if(offset < 0){ vtSetError(EBadOffset); goto Err; } fileWAccess(f, uid); if(!sourceLock(f->source, -1)) goto Err; s = f->source; dsize = s->dsize; eof = sourceGetSize(s); if(f->dir.mode & ModeAppend) offset = eof; bn = offset/dsize; off = offset%dsize; p = buf; while(cnt > 0){ n = cnt; if(n > dsize-off) n = dsize-off; b = sourceBlock(s, bn, n eof) sourceSetSize(s, offset); goto Err; } memmove(b->data+off, p, n); off = 0; cnt -= n; p += n; offset += n; bn++; blockDirty(b); blockPut(b); } if(offset > eof && !sourceSetSize(s, offset)) goto Err; sourceUnlock(s); fileUnlock(f); return p-(uchar*)buf; Err: if(s) sourceUnlock(s); fileUnlock(f); return -1; } int fileGetDir(File *f, DirEntry *dir) { if(!fileRLock(f)) return 0; fileMetaLock(f); deCopy(dir, &f->dir); fileMetaUnlock(f); if(!fileIsDir(f)){ if(!sourceLock(f->source, OReadOnly)){ fileRUnlock(f); return 0; } dir->size = sourceGetSize(f->source); sourceUnlock(f->source); } fileRUnlock(f); return 1; } int fileTruncate(File *f, char *uid) { if(fileIsDir(f)){ vtSetError(ENotFile); return 0; } if(!fileLock(f)) return 0; if(f->source->mode != OReadWrite){ vtSetError(EReadOnly); fileUnlock(f); return 0; } if(!sourceLock(f->source, -1)){ fileUnlock(f); return 0; } if(!sourceTruncate(f->source)){ sourceUnlock(f->source); fileUnlock(f); return 0; } sourceUnlock(f->source); fileUnlock(f); fileWAccess(f, uid); return 1; } int fileSetDir(File *f, DirEntry *dir, char *uid) { File *ff; char *oelem; u32int mask; u64int size; /* can not set permissions for the root */ if(fileIsRoot(f)){ vtSetError(ERoot); return 0; } if(!fileLock(f)) return 0; if(f->source->mode != OReadWrite){ vtSetError(EReadOnly); fileUnlock(f); return 0; } fileMetaLock(f); /* check new name does not already exist */ if(strcmp(f->dir.elem, dir->elem) != 0){ for(ff = f->up->down; ff; ff=ff->next){ if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){ vtSetError(EExists); goto Err; } } ff = dirLookup(f->up, dir->elem); if(ff != nil){ fileDecRef(ff); vtSetError(EExists); goto Err; } } if(!sourceLock2(f->source, f->msource, -1)) goto Err; if(!fileIsDir(f)){ size = sourceGetSize(f->source); if(size != dir->size){ if(!sourceSetSize(f->source, dir->size)){ sourceUnlock(f->source); if(f->msource) sourceUnlock(f->msource); goto Err; } /* commited to changing it now */ } } /* commited to changing it now */ if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary)) fileSetTmp(f, dir->mode&ModeTemporary); sourceUnlock(f->source); if(f->msource) sourceUnlock(f->msource); oelem = nil; if(strcmp(f->dir.elem, dir->elem) != 0){ oelem = f->dir.elem; f->dir.elem = vtStrDup(dir->elem); } if(strcmp(f->dir.uid, dir->uid) != 0){ vtMemFree(f->dir.uid); f->dir.uid = vtStrDup(dir->uid); } if(strcmp(f->dir.gid, dir->gid) != 0){ vtMemFree(f->dir.gid); f->dir.gid = vtStrDup(dir->gid); } f->dir.mtime = dir->mtime; f->dir.atime = dir->atime; //fprint(2, "mode %x %x ", f->dir.mode, dir->mode); mask = ~(ModeDir|ModeSnapshot); f->dir.mode &= ~mask; f->dir.mode |= mask & dir->mode; f->dirty = 1; //fprint(2, "->%x\n", f->dir.mode); fileMetaFlush2(f, oelem); vtMemFree(oelem); fileMetaUnlock(f); fileUnlock(f); fileWAccess(f->up, uid); return 1; Err: fileMetaUnlock(f); fileUnlock(f); return 0; } int fileSetQidSpace(File *f, u64int offset, u64int max) { int ret; if(!fileLock(f)) return 0; fileMetaLock(f); f->dir.qidSpace = 1; f->dir.qidOffset = offset; f->dir.qidMax = max; ret = fileMetaFlush2(f, nil)>=0; fileMetaUnlock(f); fileUnlock(f); return ret; } uvlong fileGetId(File *f) { /* immutable */ return f->dir.qid; } ulong fileGetMcount(File *f) { ulong mcount; fileMetaLock(f); mcount = f->dir.mcount; fileMetaUnlock(f); return mcount; } ulong fileGetMode(File *f) { ulong mode; fileMetaLock(f); mode = f->dir.mode; fileMetaUnlock(f); return mode; } int fileIsDir(File *f) { /* immutable */ return (f->dir.mode & ModeDir) != 0; } int fileIsAppend(File *f) { return (f->dir.mode & ModeAppend) != 0; } int fileIsExclusive(File *f) { return (f->dir.mode & ModeExclusive) != 0; } int fileIsTemporary(File *f) { return (f->dir.mode & ModeTemporary) != 0; } int fileIsRoot(File *f) { return f == f->fs->file; } int fileIsRoFs(File *f) { return f->fs->mode == OReadOnly; } int fileGetSize(File *f, uvlong *size) { if(!fileRLock(f)) return 0; if(!sourceLock(f->source, OReadOnly)){ fileRUnlock(f); return 0; } *size = sourceGetSize(f->source); sourceUnlock(f->source); fileRUnlock(f); return 1; } int fileMetaFlush(File *f, int rec) { File **kids, *p; int nkids; int i, rv; fileMetaLock(f); rv = fileMetaFlush2(f, nil); fileMetaUnlock(f); if(!rec || !fileIsDir(f)) return rv; if(!fileLock(f)) return rv; nkids = 0; for(p=f->down; p; p=p->next) nkids++; kids = vtMemAlloc(nkids*sizeof(File*)); i = 0; for(p=f->down; p; p=p->next){ kids[i++] = p; p->ref++; } fileUnlock(f); for(i=0; idirty) return 0; if(oelem == nil) oelem = f->dir.elem; //print("fileMetaFlush %s->%s\n", oelem, f->dir.elem); fp = f->up; if(!sourceLock(fp->msource, -1)) return -1; /* can happen if source is clri'ed out from under us */ if(f->boff == NilBlock) goto Err1; b = sourceBlock(fp->msource, f->boff, OReadWrite); if(b == nil) goto Err1; if(!mbUnpack(&mb, b->data, fp->msource->dsize)) goto Err; if(!mbSearch(&mb, oelem, &i, &me)) goto Err; n = deSize(&f->dir); if(0)fprint(2, "old size %d new size %d\n", me.size, n); if(mbResize(&mb, &me, n)){ /* fits in the block */ mbDelete(&mb, i); if(strcmp(f->dir.elem, oelem) != 0) mbSearch(&mb, f->dir.elem, &i, &me2); dePack(&f->dir, &me); mbInsert(&mb, i, &me); mbPack(&mb); blockDirty(b); blockPut(b); sourceUnlock(fp->msource); f->dirty = 0; return 1; } /* * moving entry to another block * it is feasible for the fs to crash leaving two copies * of the directory entry. This is just too much work to * fix. Given that entries are only allocated in a block that * is less than PercentageFull, most modifications of meta data * will fit within the block. i.e. this code should almost * never be executed. */ boff = fileMetaAlloc(fp, &f->dir, f->boff+1); if(boff == NilBlock){ /* mbResize might have modified block */ mbPack(&mb); blockDirty(b); goto Err; } fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff); f->boff = boff; /* make sure deletion goes to disk after new entry */ bb = sourceBlock(fp->msource, f->boff, OReadWrite); mbDelete(&mb, i); mbPack(&mb); blockDependency(b, bb, -1, nil, nil); blockPut(bb); blockDirty(b); blockPut(b); sourceUnlock(fp->msource); f->dirty = 0; return 1; Err: blockPut(b); Err1: sourceUnlock(fp->msource); return -1; } static int fileMetaRemove(File *f, char *uid) { Block *b; MetaBlock mb; MetaEntry me; int i; File *up; up = f->up; fileWAccess(up, uid); fileMetaLock(f); sourceLock(up->msource, OReadWrite); b = sourceBlock(up->msource, f->boff, OReadWrite); if(b == nil) goto Err; if(!mbUnpack(&mb, b->data, up->msource->dsize)) { fprint(2, "U\n"); goto Err; } if(!mbSearch(&mb, f->dir.elem, &i, &me)) { fprint(2, "S\n"); goto Err; } mbDelete(&mb, i); mbPack(&mb); sourceUnlock(up->msource); blockDirty(b); blockPut(b); f->removed = 1; f->boff = NilBlock; f->dirty = 0; fileMetaUnlock(f); return 1; Err: sourceUnlock(up->msource); blockPut(b); fileMetaUnlock(f); return 0; } /* assume file is locked, assume f->msource is locked */ static int fileCheckEmpty(File *f) { u32int i, n; Block *b; MetaBlock mb; Source *r; r = f->msource; n = (sourceGetSize(r)+r->dsize-1)/r->dsize; for(i=0; idata, r->dsize)) goto Err; if(mb.nindex > 0){ vtSetError(ENotEmpty); goto Err; } blockPut(b); } return 1; Err: blockPut(b); return 0; } int fileRemove(File *f, char *uid) { File *ff; /* can not remove the root */ if(fileIsRoot(f)){ vtSetError(ERoot); return 0; } if(!fileLock(f)) return 0; if(f->source->mode != OReadWrite){ vtSetError(EReadOnly); goto Err1; } if(!sourceLock2(f->source, f->msource, -1)) goto Err1; if(fileIsDir(f) && !fileCheckEmpty(f)) goto Err; for(ff=f->down; ff; ff=ff->next) assert(ff->removed); sourceRemove(f->source); f->source->file = nil; /* erase back pointer */ f->source = nil; if(f->msource){ sourceRemove(f->msource); f->msource = nil; } fileUnlock(f); if(!fileMetaRemove(f, uid)) return 0; return 1; Err: sourceUnlock(f->source); if(f->msource) sourceUnlock(f->msource); Err1: fileUnlock(f); return 0; } static int clri(File *f, char *uid) { int r; if(f == nil) return 0; if(f->up->source->mode != OReadWrite){ vtSetError(EReadOnly); fileDecRef(f); return 0; } r = fileMetaRemove(f, uid); fileDecRef(f); return r; } int fileClriPath(Fs *fs, char *path, char *uid) { return clri(_fileOpen(fs, path, 1), uid); } int fileClri(File *dir, char *elem, char *uid) { return clri(_fileWalk(dir, elem, 1), uid); } File * fileIncRef(File *vf) { fileMetaLock(vf); assert(vf->ref > 0); vf->ref++; fileMetaUnlock(vf); return vf; } int fileDecRef(File *f) { File *p, *q, **qq; if(f->up == nil){ /* never linked in */ assert(f->ref == 1); fileFree(f); return 1; } fileMetaLock(f); f->ref--; if(f->ref > 0){ fileMetaUnlock(f); return 0; } assert(f->ref == 0); assert(f->down == nil); fileMetaFlush2(f, nil); p = f->up; qq = &p->down; for(q = *qq; q; q = *qq){ if(q == f) break; qq = &q->next; } assert(q != nil); *qq = f->next; fileMetaUnlock(f); fileFree(f); fileDecRef(p); return 1; } File * fileGetParent(File *f) { if(fileIsRoot(f)) return fileIncRef(f); return fileIncRef(f->up); } DirEntryEnum * deeOpen(File *f) { DirEntryEnum *dee; File *p; if(!fileIsDir(f)){ vtSetError(ENotDir); fileDecRef(f); return nil; } /* flush out meta data */ if(!fileLock(f)) return nil; for(p=f->down; p; p=p->next) fileMetaFlush2(p, nil); fileUnlock(f); dee = vtMemAllocZ(sizeof(DirEntryEnum)); dee->file = fileIncRef(f); return dee; } static int dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size) { Block *b; ulong bn; Entry e; int epb; epb = s->dsize/VtEntrySize; bn = elem/epb; elem -= bn*epb; b = sourceBlock(s, bn, OReadOnly); if(b == nil) goto Err; if(!entryUnpack(&e, b->data, elem)) goto Err; /* hanging entries are returned as zero size */ if(!(e.flags & VtEntryActive) || e.gen != gen) *size = 0; else *size = e.size; blockPut(b); return 1; Err: blockPut(b); return 0; } static int deeFill(DirEntryEnum *dee) { int i, n; Source *meta, *source; MetaBlock mb; MetaEntry me; File *f; Block *b; DirEntry *de; /* clean up first */ for(i=dee->i; in; i++) deCleanup(dee->buf+i); vtMemFree(dee->buf); dee->buf = nil; dee->i = 0; dee->n = 0; f = dee->file; source = f->source; meta = f->msource; b = sourceBlock(meta, dee->boff, OReadOnly); if(b == nil) goto Err; if(!mbUnpack(&mb, b->data, meta->dsize)) goto Err; n = mb.nindex; dee->buf = vtMemAlloc(n * sizeof(DirEntry)); for(i=0; ibuf + i; meUnpack(&me, &mb, i); if(!deUnpack(de, &me)) goto Err; dee->n++; if(!(de->mode & ModeDir)) if(!dirEntrySize(source, de->entry, de->gen, &de->size)) goto Err; } dee->boff++; blockPut(b); return 1; Err: blockPut(b); return 0; } int deeRead(DirEntryEnum *dee, DirEntry *de) { int ret, didread; File *f; u32int nb; if(dee == nil){ vtSetError("cannot happen in deeRead"); return -1; } f = dee->file; if(!fileRLock(f)) return -1; if(!sourceLock2(f->source, f->msource, OReadOnly)){ fileRUnlock(f); return -1; } nb = (sourceGetSize(f->msource)+f->msource->dsize-1)/f->msource->dsize; didread = 0; while(dee->i >= dee->n){ if(dee->boff >= nb){ ret = 0; goto Return; } didread = 1; if(!deeFill(dee)){ ret = -1; goto Return; } } memmove(de, dee->buf + dee->i, sizeof(DirEntry)); dee->i++; ret = 1; Return: sourceUnlock(f->source); sourceUnlock(f->msource); fileRUnlock(f); if(didread) fileRAccess(f); return ret; } void deeClose(DirEntryEnum *dee) { int i; if(dee == nil) return; for(i=dee->i; in; i++) deCleanup(dee->buf+i); vtMemFree(dee->buf); fileDecRef(dee->file); vtMemFree(dee); } /* * caller must lock f->source and f->msource * caller must NOT lock the source and msource * referenced by dir. */ static u32int fileMetaAlloc(File *f, DirEntry *dir, u32int start) { u32int nb, bo; Block *b, *bb; MetaBlock mb; int nn; uchar *p; int i, n, epb; MetaEntry me; Source *s, *ms; s = f->source; ms = f->msource; n = deSize(dir); nb = (sourceGetSize(ms)+ms->dsize-1)/ms->dsize; b = nil; if(start > nb) start = nb; for(bo=start; bodata, ms->dsize)) goto Err; nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free; if(n <= nn && mb.nindex < mb.maxindex) break; blockPut(b); b = nil; } /* add block to meta file */ if(b == nil){ b = sourceBlock(ms, bo, OReadWrite); if(b == nil) goto Err; sourceSetSize(ms, (nb+1)*ms->dsize); mbInit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry); } p = mbAlloc(&mb, n); if(p == nil){ /* mbAlloc might have changed block */ mbPack(&mb); blockDirty(b); vtSetError(EBadMeta); goto Err; } mbSearch(&mb, dir->elem, &i, &me); assert(me.p == nil); me.p = p; me.size = n; dePack(dir, &me); mbInsert(&mb, i, &me); mbPack(&mb); /* meta block depends on super block for qid ... */ bb = cacheLocal(b->c, PartSuper, 0, OReadOnly); blockDependency(b, bb, -1, nil, nil); blockPut(bb); /* ... and one or two dir entries */ epb = s->dsize/VtEntrySize; bb = sourceBlock(s, dir->entry/epb, OReadOnly); blockDependency(b, bb, -1, nil, nil); blockPut(bb); if(dir->mode & ModeDir){ bb = sourceBlock(s, dir->mentry/epb, OReadOnly); blockDependency(b, bb, -1, nil, nil); blockPut(bb); } blockDirty(b); blockPut(b); return bo; Err: blockPut(b); return NilBlock; } static int chkSource(File *f) { if(f->partial) return 1; if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){ vtSetError(ERemoved); return 0; } return 1; } static int fileRLock(File *f) { assert(!vtCanLock(f->fs->elk)); vtRLock(f->lk); if(!chkSource(f)){ fileRUnlock(f); return 0; } return 1; } static void fileRUnlock(File *f) { vtRUnlock(f->lk); } static int fileLock(File *f) { assert(!vtCanLock(f->fs->elk)); vtLock(f->lk); if(!chkSource(f)){ fileUnlock(f); return 0; } return 1; } static void fileUnlock(File *f) { vtUnlock(f->lk); } /* * f->source and f->msource must NOT be locked. * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2). * We have to respect that ordering. */ static void fileMetaLock(File *f) { if(f->up == nil) fprint(2, "f->elem = %s\n", f->dir.elem); assert(f->up != nil); assert(!vtCanLock(f->fs->elk)); vtLock(f->up->lk); } static void fileMetaUnlock(File *f) { vtUnlock(f->up->lk); } /* * f->source and f->msource must NOT be locked. * see fileMetaLock. */ static void fileRAccess(File* f) { if(f->mode == OReadOnly || f->fs->noatimeupd) return; fileMetaLock(f); f->dir.atime = time(0L); f->dirty = 1; fileMetaUnlock(f); } /* * f->source and f->msource must NOT be locked. * see fileMetaLock. */ static void fileWAccess(File* f, char *mid) { if(f->mode == OReadOnly) return; fileMetaLock(f); f->dir.atime = f->dir.mtime = time(0L); if(strcmp(f->dir.mid, mid) != 0){ vtMemFree(f->dir.mid); f->dir.mid = vtStrDup(mid); } f->dir.mcount++; f->dirty = 1; fileMetaUnlock(f); /*RSC: let's try this */ /*presotto - lets not if(f->up) fileWAccess(f->up, mid); */ } static int getEntry(Source *r, Entry *e, int checkepoch) { u32int epoch; Block *b; if(r == nil){ memset(&e, 0, sizeof e); return 1; } b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly); if(b == nil) return 0; if(!entryUnpack(e, b->data, r->offset % r->epb)){ blockPut(b); return 0; } epoch = b->l.epoch; blockPut(b); if(checkepoch){ b = cacheGlobal(r->fs->cache, e->score, entryType(e), e->tag, OReadOnly); if(b){ if(b->l.epoch >= epoch) fprint(2, "warning: entry %p epoch not older %#.8ux/%d %V/%d in getEntry\n", r, b->addr, b->l.epoch, r->score, epoch); blockPut(b); } } return 1; } static int setEntry(Source *r, Entry *e) { Block *b; Entry oe; b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite); if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score); if(b == nil) return 0; if(!entryUnpack(&oe, b->data, r->offset % r->epb)){ blockPut(b); return 0; } e->gen = oe.gen; entryPack(e, b->data, r->offset % r->epb); /* BUG b should depend on the entry pointer */ blockDirty(b); blockPut(b); return 1; } /* assumes hold elk */ int fileSnapshot(File *dst, File *src, u32int epoch, int doarchive) { Entry e, ee; /* add link to snapshot */ if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1)) return 0; e.snap = epoch; e.archive = doarchive; ee.snap = epoch; ee.archive = doarchive; if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee)) return 0; return 1; } int fileGetSources(File *f, Entry *e, Entry *ee) { if(!getEntry(f->source, e, 0) || !getEntry(f->msource, ee, 0)) return 0; return 1; } /* * Walk down to the block(s) containing the Entries * for f->source and f->msource, copying as we go. */ int fileWalkSources(File *f) { if(f->mode == OReadOnly){ fprint(2, "readonly in fileWalkSources\n"); return 1; } if(!sourceLock2(f->source, f->msource, OReadWrite)){ fprint(2, "sourceLock2 failed in fileWalkSources\n"); return 0; } sourceUnlock(f->source); sourceUnlock(f->msource); return 1; } /* * convert File* to full path name in malloced string. * this hasn't been as useful as we hoped it would be. */ char * fileName(File *f) { char *name, *pname; File *p; static char root[] = "/"; if (f == nil) return vtStrDup("/**GOK**"); p = fileGetParent(f); if (p == f) name = vtStrDup(root); else { pname = fileName(p); if (strcmp(pname, root) == 0) name = smprint("/%s", f->dir.elem); else name = smprint("%s/%s", pname, f->dir.elem); free(pname); } fileDecRef(p); return name; }