#include "stdinc.h"#include "9h.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 voidfileFree(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 voidfileSetTmp(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;Lines 1–500 of 1842. Load more
#include "stdinc.h" #include "9h.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;