diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/fossil/file.c | |
download | plan9front-e5888a1ffdae813d7575f5fb02275c6bb07e5199.tar.xz |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/fossil/file.c')
-rwxr-xr-x | sys/src/cmd/fossil/file.c | 1842 |
1 files changed, 1842 insertions, 0 deletions
diff --git a/sys/src/cmd/fossil/file.c b/sys/src/cmd/fossil/file.c new file mode 100755 index 000000000..cb96ed197 --- /dev/null +++ b/sys/src/cmd/fossil/file.c @@ -0,0 +1,1842 @@ +#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; bo<nb; bo++){ + b = sourceBlock(meta, bo, OReadOnly); + if(b == nil) + goto Err; + if(!mbUnpack(&mb, b->data, 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<dsize?OReadWrite:OOverWrite); + if(b == nil){ + if(offset > 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 +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; i<nkids; i++){ + rv |= fileMetaFlush(kids[i], 1); + fileDecRef(kids[i]); + } + vtMemFree(kids); + return rv; +} + +/* assumes metaLock is held */ +static int +fileMetaFlush2(File *f, char *oelem) +{ + File *fp; + Block *b, *bb; + MetaBlock mb; + MetaEntry me, me2; + int i, n; + u32int boff; + + if(!f->dirty) + 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; i<n; i++){ + b = sourceBlock(r, i, OReadOnly); + if(b == nil) + goto Err; + if(!mbUnpack(&mb, b->data, 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; i<dee->n; 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; i<n; i++){ + de = dee->buf + 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; i<dee->n; 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; bo<nb; bo++){ + b = sourceBlock(ms, bo, OReadWrite); + if(b == nil) + goto Err; + if(!mbUnpack(&mb, b->data, 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) + 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 strdup("/**GOK**"); + + p = fileGetParent(f); + if (p == f) + name = strdup(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; +} |