/* * Walks elems starting at f. * Ok if nelems is 0. */ static Path* walkpath(Memblk *f, char *elems[], int nelems) { int i; Memblk *nf; Path *p; p = newpath(f); if(catcherror()){ putpath(p); error(nil); } isfile(f); for(i = 0; i < nelems; i++){ if((f->d.mode&DMDIR) == 0) error("not a directory"); rwlock(f, Rd); if(catcherror()){ rwunlock(f, Rd); error("walk: %r"); } nf = dfwalk(f, elems[i], Rd); rwunlock(f, Rd); addelem(&p, nf); mbput(nf); f = nf; USED(&f); /* in case of error() */ noerror(); } noerror(); return p; }
static void fsrm(int, char *argv[]) { Memblk *f, *pf; Path *p; p = walkto(argv[1], nil); if(catcherror()){ putpath(p); error(nil); } if(p->nf < 2) error("short path for rm"); dfmelt(&p, p->nf-1); f = p->f[p->nf-1]; pf = p->f[p->nf-2]; rwlock(f, Wr); if(catcherror()){ rwunlock(f, Wr); rwunlock(pf, Wr); error(nil); } dfremove(pf, f); p->f[p->nf-1] = nil; noerror(); noerror(); rwunlock(pf, Wr); putpath(p); }
static void fsget(int, char *argv[]) { Memblk *f; Mfile *m; char buf[4096], *nm; uvlong off; long nr; int fd; Path *p; fd = create(argv[1], OWRITE, 0664); if(fd < 0) error("create: %r\n"); nm = fsname(argv[2]); if(catcherror()){ free(nm); close(fd); error(nil); } p = walkto(nm, nil); f = p->f[p->nf-1]; rwlock(f, Rd); if(catcherror()){ rwunlock(f, Rd); putpath(p); error(nil); } m = f->mf; print("get %-30s\t%M\t%5ulld\t%s %ulld refs\n", m->name, (ulong)f->d.mode, f->d.length, m->uid, dbgetref(f->addr)); if((f->d.mode&DMDIR) == 0){ off = 0; for(;;){ if(fsmemfree() < Mminfree) fslru(); nr = dfpread(f, buf, sizeof buf, off); if(nr <= 0) break; if(write(fd, buf, nr) != nr){ fprint(2, "%s: error: %r\n", argv[0]); break; } off += nr; } } close(fd); noerror(); noerror(); rwunlock(f, Rd); putpath(p); free(nm); }
long dfcountrefs(Memblk *f) { Memblk *b; int i; long nfails; nfails = 0; isfile(f); if((f->addr&Fakeaddr) == 0 && f->addr >= fs->limit){ fprint(2, "fscheck: '%s' d%#010ullx: out of range\n", f->mf->name, f->addr); return 1; } if((f->addr&Fakeaddr) == 0) if(countref(f->addr) != 0) /* already visited */ return 0; /* skip children */ rwlock(f, Rd); if(catcherror()){ fprint(2, "fscheck: '%s' d%#010ullx: data: %r\n", f->mf->name, f->addr); rwunlock(f, Rd); return 1; } for(i = 0; i < nelem(f->d.dptr); i++) ptrmap(f->d.dptr[i], 0, bcountrefs, Disk); for(i = 0; i < nelem(f->d.iptr); i++) ptrmap(f->d.iptr[i], i+1, bcountrefs, Disk); if(f->d.mode&DMDIR) for(i = 0; i < f->d.length/Daddrsz; i++){ b = dfchild(f, i); if(catcherror()){ fprint(2, "fscheck: '%s' d%#010ullx:" " child[%d]: %r\n", f->mf->name, f->addr, i); nfails++; }else{ nfails += dfcountrefs(b); noerror(); } mbput(b); } noerror(); rwunlock(f, Rd); return nfails; }
/* * May be called with null parent, for root and ctl files. * The first call with a null parent is root, all others are ctl * files linked at root. */ Memblk* dfcreate(Memblk *parent, char *name, int uid, ulong mode) { Memblk *nf; Mfile *m; int isctl; if(fsfull()) error("file system full"); isctl = parent == nil; if(parent == nil) parent = fs->root; if(parent != nil){ dprint("dfcreate '%s' %M at\n%H\n", name, mode, parent); isdir(parent); isrwlocked(parent, Wr); ismelted(parent); }else dprint("dfcreate '%s' %M", name, mode); if(isctl) nf = dballoc(DBctl); else nf = dballoc(DBfile); if(catcherror()){ mbput(nf); if(parent != nil) rwunlock(parent, Wr); error(nil); } m = nf->mf; nf->d.id = nsec(); nf->d.mode = mode; nf->d.mtime = nf->d.id; nf->d.atime = nf->d.id; nf->d.length = 0; m->uid = usrname(uid); nf->d.uid = uid; m->gid = m->uid; nf->d.gid = nf->d.uid; m->muid = m->uid; nf->d.muid = nf->d.uid; m->name = name; nf->d.asize = pmeta(nf->d.embed, Embedsz, nf); changed(nf); if(parent != nil){ m->gid = parent->mf->gid; nf->d.gid = parent->d.gid; dflink(parent, nf); } noerror(); dprint("dfcreate-> %H\n within %H\n", nf, parent); return nf; }
static void fscat(int, char *argv[]) { Memblk *f; Mfile *m; char buf[4096], *nm; uvlong off; long nr; Path *p; nm = fsname(argv[2]); if(catcherror()){ free(nm); error(nil); } p = walkto(nm, nil); f = p->f[p->nf-1]; rwlock(f, Rd); if(catcherror()){ rwunlock(f, Rd); putpath(p); error(nil); } m = f->mf; print("cat %-30s\t%M\t%5ulld\t%s %ulld refs\n", m->name, (ulong)f->d.mode, f->d.length, m->uid, dbgetref(f->addr)); if((f->d.mode&DMDIR) == 0){ off = 0; for(;;){ if(fsmemfree() < Mminfree) fslru(); nr = dfpread(f, buf, sizeof buf, off); if(nr <= 0) break; write(1, buf, nr); off += nr; } } noerror(); noerror(); rwunlock(f, Rd); putpath(p); free(nm); }
void dfused(Path *p) { Memblk *f; f = p->f[p->nf-1]; isfile(f); rwlock(f, Wr); f->d.atime = nsec(); rwunlock(f, Wr); }
static void fsrm(int, char *argv[]) { Memblk *f, *pf; Path *p; char *nm; nm = fsname(argv[1]); if(catcherror()){ free(nm); error(nil); } p = walkto(nm, nil); if(catcherror()){ putpath(p); error(nil); } if(p->nf < 2) error("short path for rm"); meltedpath(&p, p->nf-1, 1); f = p->f[p->nf-1]; pf = p->f[p->nf-2]; rwlock(f, Wr); if(catcherror()){ rwunlock(f, Wr); rwunlock(pf, Wr); error(nil); } dfremove(pf, f); p->f[p->nf-1] = nil; noerror(); noerror(); noerror(); rwunlock(pf, Wr); putpath(p); free(nm); }
int dffreeze(Memblk *f) { int i; Memblk *b; long tot; isfile(f); if(f->frozen && f != fs->active && f != fs->archive) return 0; rwlock(f, Wr); if(catcherror()){ rwunlock(f, Wr); error(nil); } f->frozen = 1; tot = 1; for(i = 0; i < nelem(f->d.dptr); i++) tot += ptrmap(f->d.dptr[i], 0, bfreeze, Mem); for(i = 0; i < nelem(f->d.iptr); i++) tot += ptrmap(f->d.iptr[i], i+1, bfreeze, Mem); if((f->d.mode&DMDIR) != 0){ for(i = 0; i < f->d.length/Daddrsz; i++){ b = mfchild(f, i); if(b == nil) continue; if(!catcherror()){ tot += dffreeze(b); noerror(); } mbput(b); } } noerror(); rwunlock(f, Wr); return tot; }
/* * Drop one disk reference for f and reclaim its storage if it's gone. * The given memory reference is not released. * For directories, all files contained have their disk references adjusted, * and they are also reclaimed if no further references exist. * * NB: Time ago, directories were not in compact form (they had holes * due to removals) and this had a bug while reclaiming that could lead * to double frees of disk blocks. * The bug was fixed, but since then, directories have changed again to * have holes. If the a double free happens again, this is the place where * to look, besides dbdup and dfchdentry. */ int dfput(Memblk *f) { int i; Memblk *b; long tot; isfile(f); dKprint("dfput %H\n", f); /* * Remove children if it's the last disk ref before we drop data blocks. * No new disk refs may be added, so there's no race here. */ tot = 0; if(dbgetref(f->addr) == 1 && (f->d.mode&DMDIR) != 0){ rwlock(f, Wr); if(catcherror()){ rwunlock(f, Wr); error(nil); } for(i = 0; i < f->d.ndents; i++){ b = dfchild(f, i); if(!catcherror()){ tot += dfput(b); noerror(); } mbput(b); } noerror(); rwunlock(f, Wr); } if(dbput(f, f->type, f->addr) == 0) tot++; return tot; }
/* * Return the last version for *fp, wlocked, be it frozen or melted. */ void followmelted(Memblk **fp, int iswr) { Memblk *f; f = *fp; isfile(f); rwlock(f, iswr); while(f->mf->melted != nil){ incref(f->mf->melted); *fp = f->mf->melted; rwunlock(f, iswr); mbput(f); f = *fp; rwlock(f, iswr); if(!f->frozen) return; } }
/* * Report that a file has been modified. * Modification times propagate up to the root of the file tree. * But frozen files are never changed. */ void dfchanged(Path *p, int muid) { Memblk *f; u64int t, u; int i; t = nsec(); u = muid; for(i = 0; i < p->nf; i++){ f = p->f[i]; rwlock(f, Wr); if(f->frozen == 0) if(!catcherror()){ f->d.mtime = t; f->d.atime = t; f->d.muid = muid; changed(f); noerror(); } rwunlock(f, Wr); } }
void dfremove(Memblk *p, Memblk *f) { vlong n; /* funny as it seems, we may need extra blocks to melt */ if(fsfull()) error("file system full"); isrwlocked(f, Wr); isrwlocked(p, Wr); ismelted(p); if((f->d.mode&DMDIR) != 0 && f->d.ndents > 0) error("directory not empty"); incref(p); if(catcherror()){ mbput(p); error(nil); } dfunlink(p, f); /* shouldn't fail now. it's unlinked */ if(p->d.ndents == 0 && p->d.length > 0){ /* all gone, make it public */ p->d.length = 0; changed(p); } noerror(); rwunlock(f, Wr); if(!catcherror()){ n = dfput(f); dprint("dfput d%#ullx: %lld blks\n", f->addr, n); noerror(); } mbput(f); mbput(p); }
static void * taskq_thread(void *arg) { taskq_t *tq = arg; taskq_ent_t *t; bool prealloc; mxlock(&tq->tq_lock); while (tq->tq_flags & TASKQ_ACTIVE) { if ((t = tq->tq_task.tqent_next) == &tq->tq_task) { if (--tq->tq_active == 0) condbcast(&tq->tq_wait_cv); condwait(&tq->tq_dispatch_cv, &tq->tq_lock); tq->tq_active++; continue; } t->tqent_prev->tqent_next = t->tqent_next; t->tqent_next->tqent_prev = t->tqent_prev; t->tqent_next = NULL; t->tqent_prev = NULL; prealloc = t->tqent_flags & TQENT_FLAG_PREALLOC; mxunlock(&tq->tq_lock); rwlock(&tq->tq_threadlock, false); t->tqent_func(t->tqent_arg); rwunlock(&tq->tq_threadlock); mxlock(&tq->tq_lock); if (!prealloc) task_free(tq, t); } tq->tq_nthreads--; condbcast(&tq->tq_wait_cv); mxunlock(&tq->tq_lock); return (NULL); }
/* * Advance path to use the most recent version of each file. */ Path* dflast(Path **pp, int nth) { Memblk *f; Path *p; int i; p = *pp; for(i = 0; i < nth; i++){ f = p->f[i]; if(f != nil && f->mf != nil && f->mf->melted != nil) break; } if(i == nth) return p; /* all files have the last version */ ownpath(pp); p = *pp; for(i = 0; i < nth; i++){ followmelted(&p->f[i], Rd); rwunlock(p->f[i], Rd); } return p; }
/* * This is unrealistic in that it keeps the file locked * during the entire put. This means that we can only give * fslru() a chance before each put, and not before each * write, because everything is going to be in use and dirty if * we run out of memory. */ static void fsput(int, char *argv[]) { int fd; char *fn; Memblk *m, *f; Dir *d; char buf[4096]; uvlong off; long nw, nr; Path *p; char *nm; fd = open(argv[1], OREAD); if(fd < 0) error("open: %r\n"); d = dirfstat(fd); if(d == nil){ error("dirfstat: %r\n"); } nm = fsname(argv[2]); if(catcherror()){ free(nm); close(fd); free(d); error(nil); } p = walkto(nm, &fn); if(catcherror()){ putpath(p); error(nil); } meltedpath(&p, p->nf, 1); m = p->f[p->nf-1]; if(catcherror()){ rwunlock(m, Wr); error(nil); } f = dfcreate(m, fn, usrid(d->uid), d->mode&(DMDIR|0777)); noerror(); addelem(&p, f); decref(f); /* kept now in p */ rwlock(f, Wr); rwunlock(m, Wr); if(catcherror()){ rwunlock(f, Wr); error(nil); } if((d->mode&DMDIR) == 0){ off = 0; for(;;){ if(fsmemfree() < Mminfree) fslru(); nr = read(fd, buf, sizeof buf); if(nr <= 0) break; nw = dfpwrite(f, buf, nr, &off); dprint("wrote %ld of %ld bytes\n", nw, nr); off += nr; } } noerror(); noerror(); noerror(); if(verb) print("created %H\nat %H\n", f, m); rwunlock(f, Wr); free(nm); putpath(p); close(fd); free(d); }
/* * Caller walked down p, and now requires the nth element to be * melted, and wlocked for writing. (nth count starts at 1); * * Return the path with the version of f that we must use, * locked for writing and melted. * References kept in the path are traded for the ones returned. */ Path* dfmelt(Path **pp, int nth) { int i; Memblk *f, **fp, *nf; Path *p; ownpath(pp); p = *pp; assert(nth >= 1 && p->nf >= nth && p->nf >= 2); assert(p->f[0] == fs->root); fp = &p->f[nth-1]; /* * 1. Optimistic: Try to get a loaded melted version for f. */ followmelted(fp, Wr); f = *fp; if(!f->frozen) return p; ainc(&fs->nmelts); rwunlock(f, Wr); /* * 2. Realistic: * walk down the path, melting every frozen thing until we * reach f. Keep wlocks so melted files are not frozen while we walk. * /active is special, because it's only frozen temporarily while * creating a frozen version of the tree. Instead of melting it, * we should just wait for it. * p[0] is / * p[1] is /active */ for(;;){ followmelted(&p->f[1], Wr); if(p->f[1]->frozen == 0) break; rwunlock(p->f[1], Wr); yield(); } /* * At loop header, parent is p->f[i-1], melted and wlocked. * At the end of the loop, p->f[i] is melted and wlocked. */ for(i = 2; i < nth; i++){ followmelted(&p->f[i], Wr); if(!p->f[i]->frozen){ rwunlock(p->f[i-1], Wr); continue; } if(catcherror()){ rwunlock(p->f[i-1], Wr); rwunlock(p->f[i], Wr); error(nil); } nf = dbdup(p->f[i]); rwlock(nf, Wr); if(catcherror()){ rwunlock(nf, Wr); mbput(nf); error(nil); } dfchdentry(p->f[i-1], p->f[i]->addr, nf->addr, Mkit); noerror(); noerror(); /* committed */ rwunlock(p->f[i-1], Wr); /* parent */ rwunlock(p->f[i], Wr); /* old frozen version */ f = p->f[i]; p->f[i] = nf; assert(f->ref > 1); mbput(f); /* ref from path */ if(!catcherror()){ dbput(f, f->type, f->addr); /* p->f[i] ref from disk */ noerror(); } } return p; }