void fidPut(Fid* fid) { vtLock(fid->con->fidlock); assert(fid->ref > 0); fid->ref--; vtUnlock(fid->con->fidlock); if(fid->ref == 0 && fid->fidno == NOFID){ fidFree(fid); return; } fidUnlock(fid); }
void archFree(Arch *a) { /* kill slave */ vtLock(a->lk); a->die = vtRendezAlloc(a->lk); vtWakeup(a->starve); while(a->ref > 1) vtSleep(a->die); vtUnlock(a->lk); vtRendezFree(a->starve); vtRendezFree(a->die); vtLockFree(a->lk); vtMemFree(a); }
int vtSetFd(VtSession *z, int fd) { vtLock(z->lk); if(z->cstate != VtStateAlloc) { vtSetError("bad state"); vtUnlock(z->lk); return 0; } if(z->fd >= 0) vtFdClose(z->fd); z->fd = fd; vtUnlock(z->lk); return 1; }
Packet* vtRecvPacket(VtSession *z) { uint8_t buf[10], *b; int n; Packet *p; int size, len; if(z->cstate != VtStateConnected) { vtSetError("session not connected"); return 0; } vtLock(z->inLock); p = z->part; /* get enough for head size */ size = packetSize(p); while(size < 2) { b = packetTrailer(p, MaxFragSize); assert(b != nil); n = vtFdRead(z->fd, b, MaxFragSize); if(n <= 0) goto Err; size += n; packetTrim(p, 0, size); } if(!packetConsume(p, buf, 2)) goto Err; len = (buf[0] << 8) | buf[1]; size -= 2; while(size < len) { n = len - size; if(n > MaxFragSize) n = MaxFragSize; b = packetTrailer(p, n); if(!vtFdReadFully(z->fd, b, n)) goto Err; size += n; } p = packetSplit(p, len); vtUnlock(z->inLock); return p; Err: vtUnlock(z->inLock); return nil; }
void snapGetTimes(Snap *s, uint32_t *arch, uint32_t *snap, uint32_t *snaplen) { if(s == nil){ *snap = -1; *arch = -1; *snaplen = -1; return; } vtLock(s->lk); *snap = s->snapMinutes; *arch = s->archMinute; *snaplen = s->snapLife; vtUnlock(s->lk); }
static Lstn* lstnAlloc(char* address, int flags) { int afd; Lstn *lstn; char dir[NETPATHLEN]; vtLock(lbox.lock); for(lstn = lbox.head; lstn != nil; lstn = lstn->next){ if(strcmp(lstn->address, address) != 0) continue; vtSetError("listen: already serving '%s'", address); vtUnlock(lbox.lock); return nil; } if((afd = announce(address, dir)) < 0){ vtSetError("listen: announce '%s': %r", address); vtUnlock(lbox.lock); return nil; } lstn = vtMemAllocZ(sizeof(Lstn)); lstn->afd = afd; lstn->address = vtStrDup(address); lstn->flags = flags; memmove(lstn->dir, dir, NETPATHLEN); if(lbox.tail != nil){ lstn->prev = lbox.tail; lbox.tail->next = lstn; } else{ lbox.head = lstn; lstn->prev = nil; } lbox.tail = lstn; vtUnlock(lbox.lock); if(vtThread(lstnListen, lstn) < 0){ vtSetError("listen: thread '%s': %r", lstn->address); lstnFree(lstn); return nil; } return lstn; }
void sourceClose(Source *r) { if(r == nil) return; vtLock(r->lk); r->ref--; if(r->ref) { vtUnlock(r->lk); return; } assert(r->ref == 0); vtUnlock(r->lk); if(r->parent) sourceClose(r->parent); vtLockFree(r->lk); memset(r, ~0, sizeof(*r)); vtMemFree(r); }
static void lstnFree(Lstn* lstn) { vtLock(lbox.lock); if(lstn->prev != nil) lstn->prev->next = lstn->next; else lbox.head = lstn->next; if(lstn->next != nil) lstn->next->prev = lstn->prev; else lbox.tail = lstn->prev; vtUnlock(lbox.lock); if(lstn->afd != -1) close(lstn->afd); vtMemFree(lstn->address); vtMemFree(lstn); }
void fidClunk(Fid* fid) { assert(fid->flags & FidFWlock); vtLock(fid->con->fidlock); assert(fid->ref > 0); fid->ref--; fidUnHash(fid); fid->fidno = NOFID; vtUnlock(fid->con->fidlock); if(fid->ref > 0){ /* not reached - fidUnHash requires ref == 0 */ fidUnlock(fid); return; } fidFree(fid); }
int exclUpdate(Fid* fid) { ulong t; Excl *excl; excl = fid->excl; t = time(0L); vtLock(ebox.lock); if(excl->time < t || excl->fsys != fid->fsys){ vtUnlock(ebox.lock); vtSetError("exclusive lock broken"); return 0; } excl->time = t+LifeTime; vtUnlock(ebox.lock); return 1; }
void exclFree(Fid* fid) { Excl *excl; if((excl = fid->excl) == nil) return; fid->excl = nil; vtLock(ebox.lock); if(excl->prev != nil) excl->prev->next = excl->next; else ebox.head = excl->next; if(excl->next != nil) excl->next->prev = excl->prev; else ebox.tail = excl->prev; vtUnlock(ebox.lock); vtMemFree(excl); }
void vtDisconnect(VtSession *z, int error) { Packet *p; uint8_t *b; vtDebug(z, "vtDisconnect\n"); vtLock(z->lk); if(z->cstate == VtStateConnected && !error && z->vtbl == nil) { /* clean shutdown */ p = packetAlloc(); b = packetHeader(p, 2); b[0] = VtQGoodbye; b[1] = 0; vtSendPacket(z, p); } if(z->fd >= 0) vtFdClose(z->fd); z->fd = -1; z->cstate = VtStateClosed; vtUnlock(z->lk); }
static Fid* fidAlloc(void) { Fid *fid; vtLock(fbox.lock); if(fbox.nfree > 0){ fid = fbox.free; fbox.free = fid->hash; fbox.nfree--; } else{ fid = vtMemAllocZ(sizeof(Fid)); fid->lock = vtLockAlloc(); fid->alock = vtLockAlloc(); } fbox.inuse++; vtUnlock(fbox.lock); fid->con = nil; fid->fidno = NOFID; fid->ref = 0; fid->flags = 0; fid->open = FidOCreate; assert(fid->fsys == nil); assert(fid->file == nil); fid->qid = (Qid){0, 0, 0}; assert(fid->uid == nil); assert(fid->uname == nil); assert(fid->db == nil); assert(fid->excl == nil); assert(fid->rpc == nil); assert(fid->cuname == nil); fid->hash = fid->next = fid->prev = nil; return fid; }
static int archWalk(Param *p, u32int addr, uchar type, u32int tag) { int ret, i, x, psize, dsize; uchar *data, score[VtScoreSize]; Block *b; Label l; Entry *e; WalkPtr w; p->nvisit++; b = cacheLocalData(p->c, addr, type, tag, OReadWrite,0); if(b == nil){ fprint(2, "archive(%ud, %#ux): cannot find block: %R\n", p->snapEpoch, addr); if(strcmp(vtGetError(), ELabelMismatch) == 0){ /* might as well plod on so we write _something_ to Venti */ memmove(p->score, vtZeroScore, VtScoreSize); return ArchFaked; } return ArchFailure; } if(DEBUG) fprint(2, "%*sarchive(%ud, %#ux): block label %L\n", p->depth*2, "", p->snapEpoch, b->addr, &b->l); p->depth++; if(p->depth > p->maxdepth) p->maxdepth = p->depth; data = b->data; if((b->l.state&BsVenti) == 0){ initWalk(&w, b, b->l.type==BtDir ? p->dsize : p->psize); for(i=0; nextWalk(&w, score, &type, &tag, &e); i++){ if(e){ if(!(e->flags&VtEntryActive)) continue; if((e->snap && !e->archive) || (e->flags&VtEntryNoArchive)){ if(0) fprint(2, "snap; faking %#ux\n", b->addr); if(data == b->data){ data = copyBlock(b, p->blockSize); if(data == nil){ ret = ArchFailure; goto Out; } w.data = data; } memmove(e->score, vtZeroScore, VtScoreSize); e->depth = 0; e->size = 0; e->tag = 0; e->flags &= ~VtEntryLocal; entryPack(e, data, w.n-1); continue; } } addr = globalToLocal(score); if(addr == NilBlock) continue; dsize = p->dsize; psize = p->psize; if(e){ p->dsize= e->dsize; p->psize = e->psize; } vtUnlock(b->lk); x = archWalk(p, addr, type, tag); vtLock(b->lk); if(e){ p->dsize = dsize; p->psize = psize; } while(b->iostate != BioClean && b->iostate != BioDirty) vtSleep(b->ioready); switch(x){ case ArchFailure: fprint(2, "archWalk %#ux failed; ptr is in %#ux offset %d\n", addr, b->addr, i); ret = ArchFailure; goto Out; case ArchFaked: /* * When we're writing the entry for an archive directory * (like /archive/2003/1215) then even if we've faked * any data, record the score unconditionally. * This way, we will always record the Venti score here. * Otherwise, temporary data or corrupted file system * would cause us to keep holding onto the on-disk * copy of the archive. */ if(e==nil || !e->archive) if(data == b->data){ if(0) fprint(2, "faked %#ux, faking %#ux (%V)\n", addr, b->addr, p->score); data = copyBlock(b, p->blockSize); if(data == nil){ ret = ArchFailure; goto Out; } w.data = data; } /* fall through */ if(0) fprint(2, "falling\n"); case ArchSuccess: if(e){ memmove(e->score, p->score, VtScoreSize); e->flags &= ~VtEntryLocal; entryPack(e, data, w.n-1); }else memmove(data+(w.n-1)*VtScoreSize, p->score, VtScoreSize); if(data == b->data){ blockDirty(b); /* * If b is in the active tree, then we need to note that we've * just removed addr from the active tree (replacing it with the * copy we just stored to Venti). If addr is in other snapshots, * this will close addr but not free it, since it has a non-empty * epoch range. * * If b is in the active tree but has been copied (this can happen * if we get killed at just the right moment), then we will * mistakenly leak its kids. * * The children of an archive directory (e.g., /archive/2004/0604) * are not treated as in the active tree. */ if((b->l.state&BsCopied)==0 && (e==nil || e->snap==0)) blockRemoveLink(b, addr, p->l.type, p->l.tag, 0); } break; } } if(!ventiSend(p->a, b, data)){ p->nfailsend++; ret = ArchFailure; goto Out; } p->nsend++; if(data != b->data) p->nfake++; if(data == b->data){ /* not faking it, so update state */ p->nreal++; l = b->l; l.state |= BsVenti; if(!blockSetLabel(b, &l, 0)){ ret = ArchFailure; goto Out; } } } shaBlock(p->score, b, data, p->blockSize); if(0) fprint(2, "ventisend %V %p %p %p\n", p->score, data, b->data, w.data); ret = data!=b->data ? ArchFaked : ArchSuccess; p->l = b->l; Out: if(data != b->data) vtMemFree(data); p->depth--; blockPut(b); return ret; }
static int cmdUname(int argc, char* argv[]) { User *u, *up; int d, dflag, i, r; char *p, *uid, *uname; char *createfmt = "fsys main create /active/usr/%s %s %s d775"; char *usage = "usage: uname [-d] uname [uid|:uid|%%newname|=leader|+member|-member]"; dflag = 0; ARGBEGIN{ default: return cliError(usage); case 'd': dflag = 1; break; }ARGEND if(argc < 1){ if(!dflag) return cliError(usage); vtRLock(ubox.lock); uboxDump(ubox.box); vtRUnlock(ubox.lock); return 1; } uname = argv[0]; argc--; argv++; if(argc == 0){ vtRLock(ubox.lock); if((u = _userByUname(ubox.box, uname)) == nil){ vtRUnlock(ubox.lock); return 0; } consPrint("\t%U\n", u); vtRUnlock(ubox.lock); return 1; } vtLock(ubox.lock); u = _userByUname(ubox.box, uname); while(argc--){ if(argv[0][0] == '%'){ if(u == nil){ vtUnlock(ubox.lock); return 0; } p = &argv[0][1]; if((up = _userByUname(ubox.box, p)) != nil){ vtSetError("uname: uname '%s' already exists", up->uname); vtUnlock(ubox.lock); return 0; } for(i = 0; usersMandatory[i] != nil; i++){ if(strcmp(usersMandatory[i], uname) != 0) continue; vtSetError("uname: uname '%s' is mandatory", uname); vtUnlock(ubox.lock); return 0; } d = strlen(p) - strlen(u->uname); for(up = ubox.box->head; up != nil; up = up->next){ if(up->leader != nil){ if(strcmp(up->leader, u->uname) == 0){ vtMemFree(up->leader); up->leader = vtStrDup(p); ubox.box->len += d; } } for(i = 0; i < up->ngroup; i++){ if(strcmp(up->group[i], u->uname) != 0) continue; vtMemFree(up->group[i]); up->group[i] = vtStrDup(p); ubox.box->len += d; break; } } uboxRemUser(ubox.box, u); vtMemFree(u->uname); u->uname = vtStrDup(p); uboxAddUser(ubox.box, u); } else if(argv[0][0] == '='){ if(u == nil){ vtUnlock(ubox.lock); return 0; } if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ if(argv[0][1] != '\0'){ vtUnlock(ubox.lock); return 0; } } if(u->leader != nil){ ubox.box->len -= strlen(u->leader); vtMemFree(u->leader); u->leader = nil; } if(up != nil){ u->leader = vtStrDup(up->uname); ubox.box->len += strlen(u->leader); } } else if(argv[0][0] == '+'){ if(u == nil){ vtUnlock(ubox.lock); return 0; } if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ vtUnlock(ubox.lock); return 0; } if(!_groupAddMember(ubox.box, u, up->uname)){ vtUnlock(ubox.lock); return 0; } } else if(argv[0][0] == '-'){ if(u == nil){ vtUnlock(ubox.lock); return 0; } if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ vtUnlock(ubox.lock); return 0; } if(!_groupRemMember(ubox.box, u, up->uname)){ vtUnlock(ubox.lock); return 0; } } else{ if(u != nil){ vtSetError("uname: uname '%s' already exists", u->uname); vtUnlock(ubox.lock); return 0; } uid = argv[0]; if(*uid == ':') uid++; if((u = _userByUid(ubox.box, uid)) != nil){ vtSetError("uname: uid '%s' already exists", u->uid); vtUnlock(ubox.lock); return 0; } u = userAlloc(uid, uname); uboxAddUser(ubox.box, u); if(argv[0][0] != ':'){ // should have an option for the mode and gid p = smprint(createfmt, uname, uname, uname); r = cliExec(p); vtMemFree(p); if(r == 0){ vtUnlock(ubox.lock); return 0; } } } argv++; } if(usersFileWrite(ubox.box) == 0){ vtUnlock(ubox.lock); return 0; } if(dflag) uboxDump(ubox.box); vtUnlock(ubox.lock); return 1; }
static int uboxInit(char* users, int len) { User *g, *u; Ubox *box, *obox; int blank, comment, i, nline, nuser; char *buf, *f[5], **line, *p, *q, *s; /* * Strip out whitespace and comments. * Note that comments are pointless, they disappear * when the server writes the database back out. */ blank = 1; comment = nline = 0; s = p = buf = vtMemAlloc(len+1); for(q = users; *q != '\0'; q++){ if(*q == '\r' || *q == '\t' || *q == ' ') continue; if(*q == '\n'){ if(!blank){ if(p != s){ *p++ = '\n'; nline++; s = p; } blank = 1; } comment = 0; continue; } if(*q == '#') comment = 1; blank = 0; if(!comment) *p++ = *q; } *p = '\0'; line = vtMemAllocZ((nline+2)*sizeof(char*)); if((i = gettokens(buf, line, nline+2, "\n")) != nline){ fprint(2, "nline %d (%d) botch\n", nline, i); vtMemFree(line); vtMemFree(buf); return 0; } /* * Everything is updated in a local Ubox until verified. */ box = vtMemAllocZ(sizeof(Ubox)); /* * First pass - check format, check for duplicates * and enter in hash buckets. */ nuser = 0; for(i = 0; i < nline; i++){ s = vtStrDup(line[i]); if(getfields(s, f, nelem(f), 0, ":") != 4){ fprint(2, "bad line '%s'\n", line[i]); vtMemFree(s); continue; } if(*f[0] == '\0' || *f[1] == '\0'){ fprint(2, "bad line '%s'\n", line[i]); vtMemFree(s); continue; } if(!validUserName(f[0])){ fprint(2, "invalid uid '%s'\n", f[0]); vtMemFree(s); continue; } if(_userByUid(box, f[0]) != nil){ fprint(2, "duplicate uid '%s'\n", f[0]); vtMemFree(s); continue; } if(!validUserName(f[1])){ fprint(2, "invalid uname '%s'\n", f[0]); vtMemFree(s); continue; } if(_userByUname(box, f[1]) != nil){ fprint(2, "duplicate uname '%s'\n", f[1]); vtMemFree(s); continue; } u = userAlloc(f[0], f[1]); uboxAddUser(box, u); line[nuser] = line[i]; nuser++; vtMemFree(s); } assert(box->nuser == nuser); /* * Second pass - fill in leader and group information. */ for(i = 0; i < nuser; i++){ s = vtStrDup(line[i]); getfields(s, f, nelem(f), 0, ":"); assert(g = _userByUname(box, f[1])); if(*f[2] != '\0'){ if((u = _userByUname(box, f[2])) == nil) g->leader = vtStrDup(g->uname); else g->leader = vtStrDup(u->uname); box->len += strlen(g->leader); } for(p = f[3]; p != nil; p = q){ if((q = utfrune(p, L',')) != nil) *q++ = '\0'; if(!_groupAddMember(box, g, p)){ // print/log error here } } vtMemFree(s); } vtMemFree(line); vtMemFree(buf); for(i = 0; usersMandatory[i] != nil; i++){ if((u = _userByUid(box, usersMandatory[i])) == nil){ vtSetError("user '%s' is mandatory", usersMandatory[i]); uboxFree(box); return 0; } if(strcmp(u->uid, u->uname) != 0){ vtSetError("uid/uname for user '%s' must match", usersMandatory[i]); uboxFree(box); return 0; } } vtLock(ubox.lock); obox = ubox.box; ubox.box = box; vtUnlock(ubox.lock); if(obox != nil) uboxFree(obox); return 1; }
static int cmdSrv(int argc, char* argv[]) { Con *con; Srv *srv; char *usage = "usage: srv [-APWdp] [service]"; int conflags, dflag, fd[2], mode, pflag, r; dflag = 0; pflag = 0; conflags = 0; mode = 0666; ARGBEGIN{ default: return cliError(usage); case 'A': conflags |= ConNoAuthCheck; break; case 'I': conflags |= ConIPCheck; break; case 'N': conflags |= ConNoneAllow; break; case 'P': conflags |= ConNoPermCheck; mode = 0600; break; case 'W': conflags |= ConWstatAllow; mode = 0600; break; case 'd': dflag = 1; break; case 'p': pflag = 1; mode = 0600; break; }ARGEND if(pflag && (conflags&ConNoPermCheck)){ vtSetError("srv: cannot use -P with -p"); return 0; } switch(argc){ default: return cliError(usage); case 0: vtRLock(sbox.lock); for(srv = sbox.head; srv != nil; srv = srv->next) consPrint("\t%s\t%d\n", srv->service, srv->srvfd); vtRUnlock(sbox.lock); return 1; case 1: if(!dflag) break; vtLock(sbox.lock); for(srv = sbox.head; srv != nil; srv = srv->next){ if(strcmp(srv->service, argv[0]) != 0) continue; srvFree(srv); break; } vtUnlock(sbox.lock); if(srv == nil){ vtSetError("srv: '%s' not found", argv[0]); return 0; } return 1; } if(pipe(fd) < 0){ vtSetError("srv pipe: %r"); return 0; } if((srv = srvAlloc(argv[0], mode, fd[0])) == nil){ close(fd[0]); close(fd[1]); return 0; } if(pflag) r = consOpen(fd[1], srv->srvfd, -1); else{ con = conAlloc(fd[1], srv->mntpnt, conflags); if(con == nil) r = 0; else r = 1; } if(r == 0){ close(fd[1]); vtLock(sbox.lock); srvFree(srv); vtUnlock(sbox.lock); } return r; }
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; }
static Source * sourceAlloc(Fs *fs, Block *b, Source *p, uint32_t offset, int mode, int issnapshot) { int epb; uint32_t epoch; char *pname = nil; Source *r; Entry e; assert(p==nil || sourceIsLocked(p)); if(p == nil) { assert(offset == 0); epb = 1; } else epb = p->dsize / VtEntrySize; if(b->l.type != BtDir) goto Bad; /* * a non-active entry is the only thing that * can legitimately happen here. all the others * get prints. */ if(!entryUnpack(&e, b->data, offset % epb)) { pname = sourceName(p); consPrint("%s: %s %V: sourceAlloc: entryUnpack failed\n", fs->name, pname, b->score); goto Bad; } if(!(e.flags & VtEntryActive)) { pname = sourceName(p); if(0) consPrint("%s: %s %V: sourceAlloc: not active\n", fs->name, pname, e.score); goto Bad; } if(e.psize < 256 || e.dsize < 256) { pname = sourceName(p); consPrint("%s: %s %V: sourceAlloc: psize %ud or dsize %ud < 256\n", fs->name, pname, e.score, e.psize, e.dsize); goto Bad; } if(e.depth < sizeToDepth(e.size, e.psize, e.dsize)) { pname = sourceName(p); consPrint("%s: %s %V: sourceAlloc: depth %ud size %llud " "psize %ud dsize %ud\n", fs->name, pname, e.score, e.depth, e.size, e.psize, e.dsize); goto Bad; } if((e.flags & VtEntryLocal) && e.tag == 0) { pname = sourceName(p); consPrint("%s: %s %V: sourceAlloc: flags %#ux tag %#ux\n", fs->name, pname, e.score, e.flags, e.tag); goto Bad; } if(e.dsize > fs->blockSize || e.psize > fs->blockSize) { pname = sourceName(p); consPrint("%s: %s %V: sourceAlloc: psize %ud or dsize %ud " "> blocksize %ud\n", fs->name, pname, e.score, e.psize, e.dsize, fs->blockSize); goto Bad; } epoch = b->l.epoch; if(mode == OReadWrite) { if(e.snap != 0) { vtSetError(ESnapRO); return nil; } } else if(e.snap != 0) { if(e.snap < fs->elo) { vtSetError(ESnapOld); return nil; } if(e.snap >= fs->ehi) goto Bad; epoch = e.snap; } r = vtMemAllocZ(sizeof(Source)); r->fs = fs; r->mode = mode; r->issnapshot = issnapshot; r->dsize = e.dsize; r->gen = e.gen; r->dir = (e.flags & VtEntryDir) != 0; r->lk = vtLockAlloc(); r->ref = 1; r->parent = p; if(p) { vtLock(p->lk); assert(mode == OReadOnly || p->mode == OReadWrite); p->ref++; vtUnlock(p->lk); } r->epoch = epoch; // consPrint("sourceAlloc: have %V be.%d fse.%d %s\n", b->score, // b->l.epoch, r->fs->ehi, mode == OReadWrite? "rw": "ro"); memmove(r->score, b->score, VtScoreSize); r->scoreEpoch = b->l.epoch; r->offset = offset; r->epb = epb; r->tag = b->l.tag; // consPrint("%s: sourceAlloc: %p -> %V %d\n", r, r->score, r->offset); return r; Bad: free(pname); vtSetError(EBadEntry); return nil; }
int vtConnect(VtSession *z, char *password) { char buf[VtMaxStringSize], *p, *ep, *prefix; int i; USED(password); vtLock(z->lk); if(z->cstate != VtStateAlloc) { vtSetError("bad session state"); vtUnlock(z->lk); return 0; } if(z->fd < 0){ vtSetError("%s", z->fderror); vtUnlock(z->lk); return 0; } /* be a little anal */ vtLock(z->inLock); vtLock(z->outLock); prefix = "venti-"; p = buf; ep = buf + sizeof(buf); p = seprint(p, ep, "%s", prefix); p += strlen(p); for(i=0; vtVersions[i].version; i++) { if(i != 0) *p++ = ':'; p = seprint(p, ep, "%s", vtVersions[i].s); } p = seprint(p, ep, "-libventi\n"); assert(p-buf < sizeof(buf)); if(z->outHash) vtSha1Update(z->outHash, (uint8_t*)buf, p-buf); if(!vtFdWrite(z->fd, (uint8_t*)buf, p-buf)) goto Err; vtDebug(z, "version string out: %s", buf); if(!vtVersionRead(z, prefix, &z->version)) goto Err; vtDebug(z, "version = %d: %s\n", z->version, vtGetVersion(z)); vtUnlock(z->inLock); vtUnlock(z->outLock); z->cstate = VtStateConnected; vtUnlock(z->lk); if(z->vtbl) return 1; if(!vtHello(z)) goto Err; return 1; Err: if(z->fd >= 0) vtFdClose(z->fd); z->fd = -1; vtUnlock(z->inLock); vtUnlock(z->outLock); z->cstate = VtStateClosed; vtUnlock(z->lk); return 0; }
static void archThread(void *v) { Arch *a = v; Block *b; Param p; Super super; int ret; u32int addr; uchar rbuf[VtRootSize]; VtRoot root; vtThreadSetName("arch"); for(;;){ /* look for work */ vtLock(a->fs->elk); b = superGet(a->c, &super); if(b == nil){ vtUnlock(a->fs->elk); fprint(2, "archThread: superGet: %R\n"); sleep(60*1000); continue; } addr = super.next; if(addr != NilBlock && super.current == NilBlock){ super.current = addr; super.next = NilBlock; superPack(&super, b->data); blockDirty(b); }else addr = super.current; blockPut(b); vtUnlock(a->fs->elk); if(addr == NilBlock){ /* wait for work */ vtLock(a->lk); vtSleep(a->starve); if(a->die != nil) goto Done; vtUnlock(a->lk); continue; } sleep(10*1000); /* window of opportunity to provoke races */ /* do work */ memset(&p, 0, sizeof p); p.blockSize = a->blockSize; p.dsize = 3*VtEntrySize; /* root has three Entries */ p.c = a->c; p.a = a; ret = archWalk(&p, addr, BtDir, RootTag); switch(ret){ default: abort(); case ArchFailure: fprint(2, "archiveBlock %#ux: %R\n", addr); sleep(60*1000); continue; case ArchSuccess: case ArchFaked: break; } if(0) fprint(2, "archiveSnapshot 0x%#ux: maxdepth %ud nfixed %ud" " send %ud nfailsend %ud nvisit %ud" " nreclaim %ud nfake %ud nreal %ud\n", addr, p.maxdepth, p.nfixed, p.nsend, p.nfailsend, p.nvisit, p.nreclaim, p.nfake, p.nreal); if(0) fprint(2, "archiveBlock %V (%ud)\n", p.score, p.blockSize); /* tie up vac root */ memset(&root, 0, sizeof root); root.version = VtRootVersion; strecpy(root.type, root.type+sizeof root.type, "vac"); strecpy(root.name, root.name+sizeof root.name, "fossil"); memmove(root.score, p.score, VtScoreSize); memmove(root.prev, super.last, VtScoreSize); root.blockSize = a->blockSize; vtRootPack(&root, rbuf); if(!vtWrite(a->z, p.score, VtRootType, rbuf, VtRootSize) || !vtSha1Check(p.score, rbuf, VtRootSize)){ fprint(2, "vtWriteBlock %#ux: %R\n", addr); sleep(60*1000); continue; } /* record success */ vtLock(a->fs->elk); b = superGet(a->c, &super); if(b == nil){ vtUnlock(a->fs->elk); fprint(2, "archThread: superGet: %R\n"); sleep(60*1000); continue; } super.current = NilBlock; memmove(super.last, p.score, VtScoreSize); superPack(&super, b->data); blockDirty(b); blockPut(b); vtUnlock(a->fs->elk); consPrint("archive vac:%V\n", p.score); } Done: a->ref--; vtWakeup(a->die); vtUnlock(a->lk); }
Fid* fidGet(Con* con, u32int fidno, int flags) { Fid *fid, **hash; if(fidno == NOFID) return nil; hash = &con->fidhash[fidno % NFidHash]; vtLock(con->fidlock); for(fid = *hash; fid != nil; fid = fid->hash){ if(fid->fidno != fidno) continue; /* * Already in use is an error * when called from attach, clone or walk. */ if(flags & FidFCreate){ vtUnlock(con->fidlock); vtSetError("%s: fid 0x%ud in use", argv0, fidno); return nil; } fid->ref++; vtUnlock(con->fidlock); fidLock(fid, flags); if((fid->open & FidOCreate) || fid->fidno == NOFID){ fidPut(fid); vtSetError("%s: fid invalid", argv0); return nil; } return fid; } if((flags & FidFCreate) && (fid = fidAlloc()) != nil){ assert(flags & FidFWlock); fid->con = con; fid->fidno = fidno; fid->ref = 1; fid->hash = *hash; *hash = fid; if(con->ftail != nil){ fid->prev = con->ftail; con->ftail->next = fid; } else{ con->fhead = fid; fid->prev = nil; } con->ftail = fid; fid->next = nil; con->nfid++; vtUnlock(con->fidlock); /* * The FidOCreate flag is used to prevent any * accidental access to the Fid between unlocking the * hash and acquiring the Fid lock for return. */ fidLock(fid, flags); fid->open &= ~FidOCreate; return fid; } vtUnlock(con->fidlock); vtSetError("%s: fid not found", argv0); return nil; }