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; }
static Packet * vtRPC(VtSession *z, int op, Packet *p) { uint8_t *hdr, buf[2]; char *err; if(z == nil){ vtSetError(ENotConnected); return nil; } /* * single threaded for the momment */ vtLock(z->lk); if(z->cstate != VtStateConnected){ vtSetError(ENotConnected); goto Err; } hdr = packetHeader(p, 2); hdr[0] = op; /* op */ hdr[1] = 0; /* tid */ vtDebug(z, "client send: "); vtDebugMesg(z, p, "\n"); if(!vtSendPacket(z, p)) { p = nil; goto Err; } p = vtRecvPacket(z); if(p == nil) goto Err; vtDebug(z, "client recv: "); vtDebugMesg(z, p, "\n"); if(!packetConsume(p, buf, 2)) goto Err; if(buf[0] == VtRError) { if(!vtGetString(p, &err)) { vtSetError(EProtocolBotch); goto Err; } vtSetError(err); vtMemFree(err); packetFree(p); vtUnlock(z->lk); return nil; } if(buf[0] != op+1 || buf[1] != 0) { vtSetError(EProtocolBotch); goto Err; } vtUnlock(z->lk); return p; Err: vtDebug(z, "vtRPC failed: %s\n", vtGetError()); if(p != nil) packetFree(p); vtUnlock(z->lk); vtDisconnect(z, 1); return nil; }
static int _groupAddMember(Ubox* box, User* g, char* member) { User *u; if((u = _userByUname(box, member)) == nil) return 0; if(_groupMember(box, g->uid, u->uname, 0)){ if(strcmp(g->uname, member) == 0) vtSetError("uname: '%s' always in own group", member); else vtSetError("uname: '%s' already in group '%s'", member, g->uname); return 0; } g->group = vtMemRealloc(g->group, (g->ngroup+1)*sizeof(char*)); g->group[g->ngroup] = vtStrDup(member); box->len += strlen(member); g->ngroup++; if(g->ngroup > 1) box->len++; return 1; }
static int srvFd(char* name, int mode, int fd, char** mntpnt) { int n, srvfd; char *p, buf[10]; /* * Drop a file descriptor with given name and mode into /srv. * Create with ORCLOSE and don't close srvfd so it will be removed * automatically on process exit. */ p = smprint("/srv/%s", name); if((srvfd = create(p, ORCLOSE|OWRITE, mode)) < 0){ vtMemFree(p); p = smprint("#s/%s", name); if((srvfd = create(p, ORCLOSE|OWRITE, mode)) < 0){ vtSetError("create %s: %r", p); vtMemFree(p); return -1; } } n = snprint(buf, sizeof(buf), "%d", fd); if(write(srvfd, buf, n) < 0){ close(srvfd); vtSetError("write %s: %r", p); vtMemFree(p); return -1; } *mntpnt = p; return srvfd; }
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 packetTrim(Packet *p, int offset, int n) { Frag *f, *ff; NOTFREE(p); if(offset < 0 || offset > p->size) { vtSetError(EPacketOffset); return 0; } if(n < 0 || offset + n > p->size) { vtSetError(EPacketOffset); return 0; } p->size = n; /* easy case */ if(n == 0) { for(f=p->first; f != nil; f=ff) { ff = f->next; fragFree(f); } p->first = p->last = nil; p->asize = 0; return 1; } /* free before offset */ for(f=p->first; offset >= FRAGSIZE(f); f=ff) { p->asize -= FRAGASIZE(f); offset -= FRAGSIZE(f); ff = f->next; fragFree(f); } /* adjust frag */ f->rp += offset; p->first = f; /* skip middle */ for(; n > 0 && n > FRAGSIZE(f); f=f->next) n -= FRAGSIZE(f); /* adjust end */ f->wp = f->rp + n; p->last = f; ff = f->next; f->next = nil; /* free after */ for(f=ff; f != nil; f=ff) { p->asize -= FRAGASIZE(f); ff = f->next; fragFree(f); } return 1; }
/* hold z->inLock */ static int vtVersionRead(VtSession *z, char *prefix, int *ret) { char c; char buf[VtMaxStringSize]; char *q, *p, *pp; int i; q = prefix; p = buf; for(;;) { if(p >= buf + sizeof(buf)) { vtSetError(EBadVersion); return 0; } if(!vtFdReadFully(z->fd, (uint8_t*)&c, 1)) return 0; if(z->inHash) vtSha1Update(z->inHash, (uint8_t*)&c, 1); if(c == '\n') { *p = 0; break; } if(c < ' ' || *q && c != *q) { vtSetError(EBadVersion); return 0; } *p++ = c; if(*q) q++; } vtDebug(z, "version string in: %s\n", buf); p = buf + strlen(prefix); for(;;) { for(pp=p; *pp && *pp != ':' && *pp != '-'; pp++) ; for(i=0; vtVersions[i].version; i++) { if(strlen(vtVersions[i].s) != pp-p) continue; if(memcmp(vtVersions[i].s, p, pp-p) == 0) { *ret = vtVersions[i].version; return 1; } } p = pp; if(*p != ':') return 0; p++; } }
int vtSetCryptoStrength(VtSession *z, int c) { if(z->cstate != VtStateAlloc) { vtSetError("bad state"); return 0; } if(c != VtCryptoStrengthNone) { vtSetError("not supported yet"); return 0; } 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 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; }
uint8_t * packetHeader(Packet *p, int n) { Frag *f; Mem *m; NOTFREE(p); if(n <= 0 || n > MaxFragSize) { vtSetError(EPacketSize); return 0; } p->size += n; /* try and fix in current frag */ f = p->first; if(f != nil) { m = f->mem; if(n <= f->rp - m->bp) if(m->ref == 1 || memHead(m, f->rp, n)) { f->rp -= n; return f->rp; } } /* add frag to front */ f = fragAlloc(p, n, PEnd, p->first); p->asize += FRAGASIZE(f); if(p->first == nil) p->last = f; p->first = f; return f->rp; }
uint8_t * packetTrailer(Packet *p, int n) { Mem *m; Frag *f; NOTFREE(p); if(n <= 0 || n > MaxFragSize) { vtSetError(EPacketSize); return 0; } p->size += n; /* try and fix in current frag */ if(p->first != nil) { f = p->last; m = f->mem; if(n <= m->ep - f->wp) if(m->ref == 1 || memTail(m, f->wp, n)) { f->wp += n; return f->wp - n; } } /* add frag to end */ f = fragAlloc(p, n, (p->first == nil)?PMiddle:PFront, nil); p->asize += FRAGASIZE(f); if(p->first == nil) p->first = f; else p->last->next = f; p->last = f; return f->rp; }
VtSession * vtDial(char *host, int canfail) { VtSession *z; int fd; char *na; char e[ERRMAX]; if(host == nil) host = getenv("venti"); if(host == nil) host = "$venti"; if (host == nil) { if (!canfail) werrstr("no venti host set"); na = ""; fd = -1; } else { na = netmkaddr(host, 0, "venti"); fd = dial(na, 0, 0, 0); } if(fd < 0){ rerrstr(e, sizeof e); if(!canfail){ vtSetError("venti dialstring %s: %s", na, e); return nil; } } z = vtClientAlloc(); if(fd < 0) strcpy(z->fderror, e); vtSetFd(z, fd); return z; }
int fsEpochLow(Fs *fs, uint32_t low) { Block *bs; Super super; vtLock(fs->elk); if(low > fs->ehi){ vtSetError("bad low epoch (must be <= %ud)", fs->ehi); vtUnlock(fs->elk); return 0; } if((bs = superGet(fs->cache, &super)) == nil){ vtUnlock(fs->elk); return 0; } super.epochLow = low; fs->elo = low; superWrite(bs, &super, 1); blockPut(bs); vtUnlock(fs->elk); return 1; }
static int cmd9pTwstat(Fcall* f, int, char **argv) { Dir d; static uchar buf[DIRMAX]; memset(&d, 0, sizeof d); nulldir(&d); d.name = argv[1]; d.uid = argv[2]; d.gid = argv[3]; d.mode = cmd9pStrtoul(argv[4]); d.mtime = cmd9pStrtoul(argv[5]); d.length = cmd9pStrtoull(argv[6]); f->fid = strtol(argv[0], 0, 0); f->stat = buf; f->nstat = convD2M(&d, buf, sizeof buf); if(f->nstat < BIT16SZ){ vtSetError("Twstat: convD2M failed (internal error)"); return 0; } return 1; }
int packetFragments(Packet *p, IOchunk *io, int nio, int offset) { Frag *f; int size; IOchunk *eio; NOTFREE(p); if(p->size == 0 || nio <= 0) return 0; if(offset < 0 || offset > p->size) { vtSetError(EPacketOffset); return -1; } for(f=p->first; offset >= FRAGSIZE(f); f=f->next) offset -= FRAGSIZE(f); size = 0; eio = io + nio; for(; f != nil && io < eio; f=f->next) { io->addr = f->rp + offset; io->len = f->wp - (f->rp + offset); offset = 0; size += io->len; io++; } return size; }
static int cmdLstn(int argc, char* argv[]) { int dflag, flags; Lstn *lstn; char *usage = "usage: listen [-dIN] [address]"; dflag = 0; flags = 0; ARGBEGIN{ default: return cliError(usage); case 'd': dflag = 1; break; case 'I': flags |= ConIPCheck; break; case 'N': flags |= ConNoneAllow; break; }ARGEND switch(argc){ default: return cliError(usage); case 0: vtRLock(lbox.lock); for(lstn = lbox.head; lstn != nil; lstn = lstn->next) consPrint("\t%s\t%s\n", lstn->address, lstn->dir); vtRUnlock(lbox.lock); break; case 1: if(!dflag){ if(lstnAlloc(argv[0], flags) == nil) return 0; break; } vtLock(lbox.lock); for(lstn = lbox.head; lstn != nil; lstn = lstn->next){ if(strcmp(lstn->address, argv[0]) != 0) continue; if(lstn->afd != -1){ close(lstn->afd); lstn->afd = -1; } break; } vtUnlock(lbox.lock); if(lstn == nil){ vtSetError("listen: '%s' not found", argv[0]); return 0; } break; } return 1; }
int labelUnpack(Label *l, uchar *p, int i) { p += i*LabelSize; l->state = p[0]; l->type = p[1]; l->epoch = U32GET(p+2); l->epochClose = U32GET(p+6); l->tag = U32GET(p+10); if(l->type > BtMax){ Bad: vtSetError(EBadLabel); fprint(2, "%s: labelUnpack: bad label: 0x%.2ux 0x%.2ux 0x%.8ux " "0x%.8ux 0x%.8ux\n", argv0, l->state, l->type, l->epoch, l->epochClose, l->tag); return 0; } if(l->state != BsBad && l->state != BsFree){ if(!(l->state&BsAlloc) || l->state & ~BsMask) goto Bad; if(l->state&BsClosed){ if(l->epochClose == ~(u32int)0) goto Bad; }else{ if(l->epochClose != ~(u32int)0) goto Bad; } } return 1; }
/* 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 superUnpack(Super *s, uchar *p) { memset(s, 0, sizeof(*s)); if(U32GET(p) != SuperMagic) goto Err; s->version = U16GET(p+4); if(s->version != SuperVersion) goto Err; s->epochLow = U32GET(p+6); s->epochHigh = U32GET(p+10); s->qid = U64GET(p+14); if(s->epochLow == 0 || s->epochLow > s->epochHigh || s->qid == 0) goto Err; s->active = U32GET(p+22); s->next = U32GET(p+26); s->current = U32GET(p+30); memmove(s->last, p+34, VtScoreSize); memmove(s->name, p+54, sizeof(s->name)); s->name[sizeof(s->name)-1] = 0; return 1; Err: memset(s, 0, sizeof(*s)); vtSetError(EBadSuper); return 0; }
int vtSendPacket(VtSession *z, Packet *p) { IOchunk ioc; int n; uint8_t buf[2]; /* add framing */ n = packetSize(p); if(n >= (1<<16)) { vtSetError(EBigPacket); packetFree(p); return 0; } buf[0] = n>>8; buf[1] = n; packetPrefix(p, buf, 2); for(;;) { n = packetFragments(p, &ioc, 1, 0); if(n == 0) break; if(!vtFdWrite(z->fd, ioc.addr, ioc.len)) { packetFree(p); return 0; } packetConsume(p, nil, n); } packetFree(p); return 1; }
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; }
static Mem * memAlloc(int n, int pos) { Mem *m; int nn; if(n < 0 || n > MaxFragSize) { vtSetError(EPacketSize); return 0; } if(n <= SmallMemSize) { lock(&freeList.lk); m = freeList.smallMem; if(m != nil) freeList.smallMem = m->next; else freeList.nsmallMem++; unlock(&freeList.lk); nn = SmallMemSize; } else { lock(&freeList.lk); m = freeList.bigMem; if(m != nil) freeList.bigMem = m->next; else freeList.nbigMem++; unlock(&freeList.lk); nn = BigMemSize; } if(m == nil) { m = vtMemBrk(sizeof(Mem)); m->bp = vtMemBrk(nn); m->ep = m->bp + nn; } assert(m->ref == 0); m->ref = 1; switch(pos) { default: assert(0); case PFront: m->rp = m->bp; break; case PMiddle: /* leave a little bit at end */ m->rp = m->ep - n - 32; break; case PEnd: m->rp = m->ep - n; break; } /* check we did not blow it */ if(m->rp < m->bp) m->rp = m->bp; m->wp = m->rp + n; assert(m->rp >= m->bp && m->wp <= m->ep); return m; }
int headerUnpack(Header *h, uchar *p) { if(U32GET(p) != HeaderMagic){ vtSetError("vac header bad magic"); return 0; } h->version = U16GET(p+4); if(h->version != HeaderVersion){ vtSetError("vac header bad version"); return 0; } h->blockSize = U16GET(p+6); h->super = U32GET(p+8); h->label = U32GET(p+12); h->data = U32GET(p+16); h->end = U32GET(p+20); return 1; }
static int _groupRemMember(Ubox* box, User* g, char* member) { int i; if(_userByUname(box, member) == nil) return 0; for(i = 0; i < g->ngroup; i++){ if(strcmp(g->group[i], member) == 0) break; } if(i >= g->ngroup){ if(strcmp(g->uname, member) == 0) vtSetError("uname: '%s' always in own group", member); else vtSetError("uname: '%s' not in group '%s'", member, g->uname); return 0; } vtMemFree(g->group[i]); box->len -= strlen(member); if(g->ngroup > 1) box->len--; g->ngroup--; switch(g->ngroup){ case 0: vtMemFree(g->group); g->group = nil; break; default: for(; i < g->ngroup; i++) g->group[i] = g->group[i+1]; g->group[i] = nil; /* prevent accidents */ g->group = vtMemRealloc(g->group, g->ngroup * sizeof(char*)); break; } return 1; }
uint8_t * packetPeek(Packet *p, uint8_t *buf, int offset, int n) { Frag *f; int nn; uint8_t *b; NOTFREE(p); if(n == 0) return buf; if(offset < 0 || offset >= p->size) { vtSetError(EPacketOffset); return 0; } if(n < 0 || offset + n > p->size) { vtSetError(EPacketSize); return 0; } /* skip up to offset */ for(f=p->first; offset >= FRAGSIZE(f); f=f->next) offset -= FRAGSIZE(f); /* easy case */ if(offset + n <= FRAGSIZE(f)) return f->rp + offset; for(b=buf; n>0; n -= nn) { nn = FRAGSIZE(f) - offset; if(nn > n) nn = n; memmove(b, f->rp+offset, nn); offset = 0; f = f->next; b += nn; } return buf; }
int vtAddString(Packet *p, char *s) { uint8_t buf[2]; int n; if(s == nil) { vtSetError(ENullString); return 0; } n = strlen(s); if(n > VtMaxStringSize) { vtSetError(EBigString); return 0; } buf[0] = n>>8; buf[1] = n; packetAppend(p, buf, 2); packetAppend(p, (uint8_t*)s, n); return 1; }
static int cmd9pTversion(Fcall* f, int, char** argv) { f->msize = strtoul(argv[0], 0, 0); if(f->msize > cbox.con->msize){ vtSetError("msize too big"); return 0; } f->version = argv[1]; return 1; }
int vtHello(VtSession *z) { Packet *p; uint8_t buf[10]; char *sid; int crypto, codec; sid = nil; p = packetAlloc(); if(!vtAddString(p, vtGetVersion(z))) goto Err; if(!vtAddString(p, vtGetUid(z))) goto Err; buf[0] = vtGetCryptoStrength(z); buf[1] = 0; buf[2] = 0; packetAppend(p, buf, 3); p = vtRPC(z, VtQHello, p); if(p == nil) return 0; if(!vtGetString(p, &sid)) goto Err; if(!packetConsume(p, buf, 2)) goto Err; if(packetSize(p) != 0) { vtSetError(EProtocolBotch); goto Err; } crypto = buf[0]; codec = buf[1]; USED(crypto); USED(codec); packetFree(p); vtLock(z->lk); z->sid = sid; z->auth.state = VtAuthOK; vtSha1Free(z->inHash); z->inHash = nil; vtSha1Free(z->outHash); z->outHash = nil; vtUnlock(z->lk); return 1; Err: packetFree(p); vtMemFree(sid); return 0; }
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; }