int fsVac(Fs *fs, char *name, uint8_t score[VtScoreSize]) { int r; DirEntry de; Entry e, ee; File *f; vtRLock(fs->elk); f = fileOpen(fs, name); if(f == nil){ vtRUnlock(fs->elk); return 0; } if(!fileGetSources(f, &e, &ee) || !fileGetDir(f, &de)){ fileDecRef(f); vtRUnlock(fs->elk); return 0; } fileDecRef(f); r = mkVac(fs->z, fs->blockSize, &e, &ee, &de, score); vtRUnlock(fs->elk); return r; }
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; }
int usersFileRead(char* path) { char *p; File *file; Fsys *fsys; int len, r; uvlong size; if((fsys = fsysGet("main")) == nil) return 0; fsysFsRlock(fsys); if(path == nil) path = "/active/adm/users"; r = 0; if((file = fileOpen(fsysGetFs(fsys), path)) != nil){ if(fileGetSize(file, &size)){ len = size; p = vtMemAlloc(size+1); if(fileRead(file, p, len, 0) == len){ p[len] = '\0'; r = uboxInit(p, len); } } fileDecRef(file); } fsysFsRUnlock(fsys); fsysPut(fsys); return r; }
static int fsNeedArch(Fs *fs, uint archMinute) { int need; File *f; char buf[100]; Tm now; uint32_t then; then = time(0); now = *localtime(then); /* back up to yesterday if necessary */ if(now.hour < archMinute/60 || now.hour == archMinute/60 && now.min < archMinute%60) now = *localtime(then-86400); snprint(buf, sizeof buf, "/archive/%d/%02d%02d", now.year+1900, now.mon+1, now.mday); need = 1; vtRLock(fs->elk); f = fileOpen(fs, buf); if(f){ need = 0; fileDecRef(f); } vtRUnlock(fs->elk); return need; }
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; }
/* return number of directory entries remaining */ static int fsRsearch1(File *f, char *s) { int n, r; DirEntry de; DirEntryEnum *dee; File *ff; char *t; dee = deeOpen(f); if(dee == nil) return 0; n = 0; for(;;){ r = deeRead(dee, &de); if(r <= 0) break; n++; if(de.mode & ModeSnapshot){ if((ff = fileWalk(f, de.elem)) != nil) fileDecRef(ff); else if(strcmp(vtGetError(), ESnapOld) == 0){ if(fileClri(f, de.elem, "adm")) n--; } } else if(de.mode & ModeDir){ if((ff = fileWalk(f, de.elem)) != nil){ t = smprint("%s/%s", s, de.elem); if(fsRsearch1(ff, t) == 0) if(fileRemove(ff, "adm")) n--; vtMemFree(t); fileDecRef(ff); } } deCleanup(&de); if(r < 0) break; } deeClose(dee); return n; }
static int fsEsearch1(File *f, char *path, uint32_t savetime, uint32_t *plo) { int n, r; DirEntry de; DirEntryEnum *dee; File *ff; Entry e, ee; char *t; dee = deeOpen(f); if(dee == nil) return 0; n = 0; for(;;){ r = deeRead(dee, &de); if(r <= 0) break; if(de.mode & ModeSnapshot){ if((ff = fileWalk(f, de.elem)) != nil){ if(fileGetSources(ff, &e, &ee)) if(de.mtime >= savetime && e.snap != 0) if(e.snap < *plo) *plo = e.snap; fileDecRef(ff); } } else if(de.mode & ModeDir){ if((ff = fileWalk(f, de.elem)) != nil){ t = smprint("%s/%s", path, de.elem); n += fsEsearch1(ff, t, savetime, plo); vtMemFree(t); fileDecRef(ff); } } deCleanup(&de); if(r < 0) break; } deeClose(dee); return n; }
static void addFile(File *root, char *name, uint mode) { File *f; f = fileCreate(root, name, mode | ModeDir, "adm"); if(f == nil) vtFatal("could not create file: %s: %r", name); fileDecRef(f); }
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); }
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; }
static void fidFree(Fid* fid) { if(fid->file != nil){ fileDecRef(fid->file); fid->file = nil; } if(fid->db != nil){ dirBufFree(fid->db); fid->db = nil; } fidUnlock(fid); if(fid->uid != nil){ vtMemFree(fid->uid); fid->uid = nil; } if(fid->uname != nil){ vtMemFree(fid->uname); fid->uname = nil; } if(fid->excl != nil) exclFree(fid); if(fid->rpc != nil){ close(fid->rpc->afd); auth_freerpc(fid->rpc); fid->rpc = nil; } if(fid->fsys != nil){ fsysPut(fid->fsys); fid->fsys = nil; } if(fid->cuname != nil){ vtMemFree(fid->cuname); fid->cuname = nil; } vtLock(fbox.lock); fbox.inuse--; if(fbox.nfree < 10){ fid->hash = fbox.free; fbox.free = fid; fbox.nfree++; } else{ vtLockFree(fid->alock); vtLockFree(fid->lock); vtMemFree(fid); } vtUnlock(fbox.lock); }
static int fsRsearch(Fs *fs, char *path) { File *f; DirEntry de; f = fileOpen(fs, path); if(f == nil) return 0; if(!fileGetDir(f, &de)){ fileDecRef(f); return 0; } if((de.mode & ModeDir) == 0){ fileDecRef(f); deCleanup(&de); return 0; } deCleanup(&de); fsRsearch1(f, path); fileDecRef(f); return 1; }
static int fsEsearch(Fs *fs, char *path, uint32_t savetime, uint32_t *plo) { int n; File *f; DirEntry de; f = fileOpen(fs, path); if(f == nil) return 0; if(!fileGetDir(f, &de)){ fileDecRef(f); return 0; } if((de.mode & ModeDir) == 0){ fileDecRef(f); deCleanup(&de); return 0; } deCleanup(&de); n = fsEsearch1(f, path, savetime, plo); fileDecRef(f); return n; }
void fsClose(Fs *fs) { vtRLock(fs->elk); periodicKill(fs->metaFlush); snapClose(fs->snap); if(fs->file){ fileMetaFlush(fs->file, 0); if(!fileDecRef(fs->file)) vtFatal("fsClose: files still in use: %r\n"); } fs->file = nil; sourceClose(fs->source); cacheFree(fs->cache); if(fs->arch) archFree(fs->arch); vtMemFree(fs->name); vtRUnlock(fs->elk); vtLockFree(fs->elk); memset(fs, ~0, sizeof(Fs)); vtMemFree(fs); }
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; }
static void topLevel(char *name) { Fs *fs; File *root; /* ok, now we can open as a fs */ fs = fsOpen(name, z, 100, OReadWrite); if(fs == nil) vtFatal("could not open file system: %r"); vtRLock(fs->elk); root = fsGetRoot(fs); if(root == nil) vtFatal("could not open root: %r"); addFile(root, "active", 0555); addFile(root, "archive", 0555); addFile(root, "snapshot", 0555); fileDecRef(root); if(iso9660file) iso9660copy(fs); vtRUnlock(fs->elk); fsClose(fs); }
/* * 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; }
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 usersFileWrite(Ubox* box) { Fs *fs; User *u; int i, r; Fsys *fsys; char *p, *q, *s; File *dir, *file; if((fsys = fsysGet("main")) == nil) return 0; fsysFsRlock(fsys); fs = fsysGetFs(fsys); /* * BUG: * the owner/group/permissions need to be thought out. */ r = 0; if((dir = fileOpen(fs, "/active")) == nil) goto tidy0; if((file = fileWalk(dir, uidadm)) == nil) file = fileCreate(dir, uidadm, ModeDir|0775, uidadm); fileDecRef(dir); if(file == nil) goto tidy; dir = file; if((file = fileWalk(dir, "users")) == nil) file = fileCreate(dir, "users", 0664, uidadm); fileDecRef(dir); if(file == nil) goto tidy; if(!fileTruncate(file, uidadm)) goto tidy; p = s = vtMemAlloc(box->len+1); q = p + box->len+1; for(u = box->head; u != nil; u = u->next){ p += snprint(p, q-p, "%s:%s:", u->uid, u->uname); if(u->leader != nil) p+= snprint(p, q-p, u->leader); p += snprint(p, q-p, ":"); if(u->ngroup){ p += snprint(p, q-p, u->group[0]); for(i = 1; i < u->ngroup; i++) p += snprint(p, q-p, ",%s", u->group[i]); } p += snprint(p, q-p, "\n"); } r = fileWrite(file, s, box->len, 0, uidadm); vtMemFree(s); tidy: if(file != nil) fileDecRef(file); tidy0: fsysFsRUnlock(fsys); fsysPut(fsys); return r; }
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; }
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 fsSnapshot(Fs *fs, char *srcpath, char *dstpath, int doarchive) { File *src, *dst; assert(fs->mode == OReadWrite); dst = nil; if(fs->halted){ vtSetError("file system is halted"); return 0; } /* * Freeze file system activity. */ vtLock(fs->elk); /* * Get the root of the directory we're going to save. */ if(srcpath == nil) srcpath = "/active"; src = fileOpen(fs, srcpath); if(src == nil) goto Err; /* * It is important that we maintain the invariant that: * if both b and bb are marked as Active with start epoch e * and b points at bb, then no other pointers to bb exist. * * When bb is unlinked from b, its close epoch is set to b's epoch. * A block with epoch == close epoch is * treated as free by cacheAllocBlock; this aggressively * reclaims blocks after they have been stored to Venti. * * Let's say src->source is block sb, and src->msource is block * mb. Let's also say that block b holds the Entry structures for * both src->source and src->msource (their Entry structures might * be in different blocks, but the argument is the same). * That is, right now we have: * * b Active w/ epoch e, holds ptrs to sb and mb. * sb Active w/ epoch e. * mb Active w/ epoch e. * * With things as they are now, the invariant requires that * b holds the only pointers to sb and mb. We want to record * pointers to sb and mb in new Entries corresponding to dst, * which breaks the invariant. Thus we need to do something * about b. Specifically, we bump the file system's epoch and * then rewalk the path from the root down to and including b. * This will copy-on-write as we walk, so now the state will be: * * b Snap w/ epoch e, holds ptrs to sb and mb. * new-b Active w/ epoch e+1, holds ptrs to sb and mb. * sb Active w/ epoch e. * mb Active w/ epoch e. * * In this state, it's perfectly okay to make more pointers to sb and mb. */ if(!bumpEpoch(fs, 0) || !fileWalkSources(src)) goto Err; /* * Sync to disk. I'm not sure this is necessary, but better safe than sorry. */ cacheFlush(fs->cache, 1); /* * Create the directory where we will store the copy of src. */ dst = fileOpenSnapshot(fs, dstpath, doarchive); if(dst == nil) goto Err; /* * Actually make the copy by setting dst's source and msource * to be src's. */ if(!fileSnapshot(dst, src, fs->ehi-1, doarchive)) goto Err; fileDecRef(src); fileDecRef(dst); src = nil; dst = nil; /* * Make another copy of the file system. This one is for the * archiver, so that the file system we archive has the recently * added snapshot both in /active and in /archive/yyyy/mmdd[.#]. */ if(doarchive){ if(!saveQid(fs)) goto Err; if(!bumpEpoch(fs, 1)) goto Err; } vtUnlock(fs->elk); /* BUG? can fs->arch fall out from under us here? */ if(doarchive && fs->arch) archKick(fs->arch); return 1; Err: fprint(2, "%s: fsSnapshot: %R\n", argv0); if(src) fileDecRef(src); if(dst) fileDecRef(dst); vtUnlock(fs->elk); return 0; }
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; }
/* * Prepare the directory to store a snapshot. * Temporary snapshots go into /snapshot/yyyy/mmdd/hhmm[.#] * Archival snapshots go into /archive/yyyy/mmdd[.#]. * * TODO This should be rewritten to eliminate most of the duplication. */ static File* fileOpenSnapshot(Fs *fs, char *dstpath, int doarchive) { int n; char buf[30], *s, *p, *elem; File *dir, *f; Tm now; if(dstpath){ if((p = strrchr(dstpath, '/')) != nil){ *p++ = '\0'; elem = p; p = dstpath; if(*p == '\0') p = "/"; }else{ p = "/"; elem = dstpath; } if((dir = fileOpen(fs, p)) == nil) return nil; f = fileCreate(dir, elem, ModeDir|ModeSnapshot|0555, "adm"); fileDecRef(dir); return f; }else if(doarchive){ /* * a snapshot intended to be archived to venti. */ dir = fileOpen(fs, "/archive"); if(dir == nil) return nil; now = *localtime(time(0)); /* yyyy */ snprint(buf, sizeof(buf), "%d", now.year+1900); f = fileWalk(dir, buf); if(f == nil) f = fileCreate(dir, buf, ModeDir|0555, "adm"); fileDecRef(dir); if(f == nil) return nil; dir = f; /* mmdd[#] */ snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday); s = buf+strlen(buf); for(n=0;; n++){ if(n) seprint(s, buf+sizeof(buf), ".%d", n); f = fileWalk(dir, buf); if(f != nil){ fileDecRef(f); continue; } f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm"); break; } fileDecRef(dir); return f; }else{ /* * Just a temporary snapshot * We'll use /snapshot/yyyy/mmdd/hhmm. * There may well be a better naming scheme. * (I'd have used hh:mm but ':' is reserved in Microsoft file systems.) */ dir = fileOpen(fs, "/snapshot"); if(dir == nil) return nil; now = *localtime(time(0)); /* yyyy */ snprint(buf, sizeof(buf), "%d", now.year+1900); f = fileWalk(dir, buf); if(f == nil) f = fileCreate(dir, buf, ModeDir|0555, "adm"); fileDecRef(dir); if(f == nil) return nil; dir = f; /* mmdd */ snprint(buf, sizeof(buf), "%02d%02d", now.mon+1, now.mday); f = fileWalk(dir, buf); if(f == nil) f = fileCreate(dir, buf, ModeDir|0555, "adm"); fileDecRef(dir); if(f == nil) return nil; dir = f; /* hhmm[.#] */ snprint(buf, sizeof buf, "%02d%02d", now.hour, now.min); s = buf+strlen(buf); for(n=0;; n++){ if(n) seprint(s, buf+sizeof(buf), ".%d", n); f = fileWalk(dir, buf); if(f != nil){ fileDecRef(f); continue; } f = fileCreate(dir, buf, ModeDir|ModeSnapshot|0555, "adm"); break; } fileDecRef(dir); return f; } }