int vtfileflush(VtFile *f) { int ret; VtBlock *b; VtEntry e; assert(ISLOCKED(f)); b = fileload(f, &e); if(!(e.flags&VtEntryLocal)){ vtblockput(b); return 0; } ret = flushblock(f->c, nil, e.score, e.psize/VtScoreSize, e.dsize/VtEntrySize, e.type); if(ret < 0){ vtblockput(b); return -1; } vtentrypack(&e, b->data, f->offset % f->epb); vtblockput(b); return 0; }
VtBlock * vtfileblock(VtFile *r, u32int bn, int mode) { VtBlock *b, *bb; int index[VtPointerDepth+1]; VtEntry e; int i; int m; assert(ISLOCKED(r)); assert(bn != NilBlock); b = fileload(r, &e); if(b == nil) return nil; i = mkindices(&e, bn, index); if(i < 0) goto Err; if(i > DEPTH(e.type)){ if(mode == VtOREAD){ werrstr("bad address 0x%lux", (ulong)bn); goto Err; } index[i] = 0; if(growdepth(r, b, &e, i) < 0) goto Err; } assert(b->type == VtDirType); index[DEPTH(e.type)] = r->offset % r->epb; /* mode for intermediate block */ m = mode; /* why is this here? * if(m == VtOWRITE) m = VtORDWR; * well we disable it */ for(i=DEPTH(e.type); i>=0; i--){ bb = blockwalk(b, index[i], r->c, i==0 ? mode : m, &e); if(bb == nil) goto Err; vtblockput(b); b = bb; } b->pc = getcallerpc(&r); return b; Err: vtblockput(b); return nil; }
int vtfileblockscore(VtFile *r, u32int bn, uchar score[VtScoreSize]) { VtBlock *b, *bb; int index[VtPointerDepth+1]; VtEntry e; int i; assert(ISLOCKED(r)); assert(bn != NilBlock); b = fileload(r, &e); if(b == nil) return -1; if(DEPTH(e.type) == 0){ memmove(score, e.score, VtScoreSize); vtblockput(b); return 0; } i = mkindices(&e, bn, index); if(i < 0){ vtblockput(b); return -1; } if(i > DEPTH(e.type)){ memmove(score, vtzeroscore, VtScoreSize); vtblockput(b); return 0; } index[DEPTH(e.type)] = r->offset % r->epb; for(i=DEPTH(e.type); i>=1; i--){ bb = blockwalk(b, index[i], r->c, VtOREAD, &e); if(bb == nil) goto Err; vtblockput(b); b = bb; if(memcmp(b->score, vtzeroscore, VtScoreSize) == 0) break; } memmove(score, b->data+index[0]*VtScoreSize, VtScoreSize); vtblockput(b); return 0; Err: vtblockput(b); return -1; }
int vtfilegetentry(VtFile *r, VtEntry *e) { VtBlock *b; assert(ISLOCKED(r)); b = fileload(r, e); if(b == nil) return -1; vtblockput(b); return 0; }
int vtfilesetentry(VtFile *r, VtEntry *e) { VtBlock *b; VtEntry ee; assert(ISLOCKED(r)); b = fileload(r, &ee); if(b == nil) return -1; vtentrypack(e, b->data, r->offset % r->epb); vtblockput(b); return 0; }
uvlong vtfilegetsize(VtFile *r) { VtEntry e; VtBlock *b; assert(ISLOCKED(r)); b = fileload(r, &e); if(b == nil) return ~(uvlong)0; vtblockput(b); return e.size; }
int vtfilesetsize(VtFile *r, u64int size) { int depth, edepth; VtEntry e; VtBlock *b; assert(ISLOCKED(r)); if(size == 0) return vtfiletruncate(r); if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize){ werrstr(ETooBig); return -1; } b = fileload(r, &e); if(b == nil) return -1; /* quick out */ if(e.size == size){ vtblockput(b); return 0; } depth = sizetodepth(size, e.psize, e.dsize); edepth = DEPTH(e.type); if(depth < edepth){ if(shrinkdepth(r, b, &e, depth) < 0){ vtblockput(b); return -1; } }else if(depth > edepth){ if(growdepth(r, b, &e, depth) < 0){ vtblockput(b); return -1; } } if(size < e.size) shrinksize(r, &e, size); e.size = size; vtentrypack(&e, b->data, r->offset % r->epb); vtblockput(b); return 0; }
static int vtfilekill(VtFile *r, int doremove) { VtEntry e; VtBlock *b; assert(ISLOCKED(r)); b = fileload(r, &e); if(b == nil) return -1; if(doremove==0 && e.size == 0){ /* already truncated */ vtblockput(b); return 0; } if(doremove){ if(e.gen != ~0) e.gen++; e.dsize = 0; e.psize = 0; e.flags = 0; }else e.flags &= ~VtEntryLocal; e.type = 0; e.size = 0; memmove(e.score, vtzeroscore, VtScoreSize); vtentrypack(&e, b->data, r->offset % r->epb); vtblockput(b); if(doremove){ vtfileunlock(r); vtfileclose(r); } return 0; }
uint textload(Text *t, uint q0, char *file, int setqid) { Rune *rp; Dirlist *dl, **dlp; int fd, i, j, n, ndl, nulls; uint q, q1; Dir *d, *dbuf; char *tmp; Text *u; if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body) error("text.load"); if(t->w->isdir && t->file->nname==0){ warning(nil, "empty directory name\n"); return 0; } fd = open(file, OREAD); if(fd < 0){ warning(nil, "can't open %s: %r\n", file); return 0; } d = dirfstat(fd); if(d == nil){ warning(nil, "can't fstat %s: %r\n", file); goto Rescue; } nulls = FALSE; if(d->qid.type & QTDIR){ /* this is checked in get() but it's possible the file changed underfoot */ if(t->file->ntext > 1){ warning(nil, "%s is a directory; can't read with multiple windows on it\n", file); goto Rescue; } t->w->isdir = TRUE; t->w->filemenu = FALSE; if(t->file->name[t->file->nname-1] != '/'){ rp = runemalloc(t->file->nname+1); runemove(rp, t->file->name, t->file->nname); rp[t->file->nname] = '/'; winsetname(t->w, rp, t->file->nname+1); free(rp); } dlp = nil; ndl = 0; dbuf = nil; while((n=dirread(fd, &dbuf)) > 0){ for(i=0; i<n; i++){ dl = emalloc(sizeof(Dirlist)); j = strlen(dbuf[i].name); tmp = emalloc(j+1+1); memmove(tmp, dbuf[i].name, j); if(dbuf[i].qid.type & QTDIR) tmp[j++] = '/'; tmp[j] = '\0'; dl->r = bytetorune(tmp, &dl->nr); dl->wid = stringwidth(t->font, tmp); free(tmp); ndl++; dlp = realloc(dlp, ndl*sizeof(Dirlist*)); dlp[ndl-1] = dl; } free(dbuf); } qsort(dlp, ndl, sizeof(Dirlist*), dircmp); t->w->dlp = dlp; t->w->ndl = ndl; textcolumnate(t, dlp, ndl); q1 = t->file->nc; }else{ t->w->isdir = FALSE; t->w->filemenu = TRUE; q1 = q0 + fileload(t->file, q0, fd, &nulls); } if(setqid){ t->file->dev = d->dev; t->file->mtime = d->mtime; t->file->qidpath = d->qid.path; } close(fd); rp = fbufalloc(); for(q=q0; q<q1; q+=n){ n = q1-q; if(n > RBUFSIZE) n = RBUFSIZE; bufread(t->file, q, rp, n); if(q < t->org) t->org += n; else if(q <= t->org+t->nchars) frinsert(t, rp, rp+n, q-t->org); if(t->lastlinefull) break; } fbuffree(rp); for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u != t){ if(u->org > u->file->nc) /* will be 0 because of reset(), but safety first */ u->org = 0; textresize(u, u->all); textbacknl(u, u->org, 0); /* go to beginning of line */ } textsetselect(u, q0, q0); } if(nulls) warning(nil, "%s: NUL bytes elided\n", file); free(d); return q1-q0; Rescue: close(fd); return 0; }
int vtfileflushbefore(VtFile *r, u64int offset) { VtBlock *b, *bb; VtEntry e; int i, base, depth, ppb, epb, doflush; int index[VtPointerDepth+1], j, ret; VtBlock *bi[VtPointerDepth+2]; uchar *score; assert(ISLOCKED(r)); if(offset == 0) return 0; b = fileload(r, &e); if(b == nil) return -1; /* * compute path through tree for the last written byte and the next one. */ ret = -1; memset(bi, 0, sizeof bi); depth = DEPTH(e.type); bi[depth+1] = b; i = mkindices(&e, (offset-1)/e.dsize, index); if(i < 0) goto Err; if(i > depth) goto Err; ppb = e.psize / VtScoreSize; epb = e.dsize / VtEntrySize; /* * load the blocks along the last written byte */ index[depth] = r->offset % r->epb; for(i=depth; i>=0; i--){ bb = blockwalk(b, index[i], r->c, VtORDWR, &e); if(bb == nil) goto Err; bi[i] = bb; b = bb; } ret = 0; /* * walk up the path from leaf to root, flushing anything that * has been finished. */ base = e.type&~VtTypeDepthMask; for(i=0; i<=depth; i++){ doflush = 0; if(i == 0){ /* leaf: data or dir block */ if(offset%e.dsize == 0) doflush = 1; }else{ /* * interior node: pointer blocks. * specifically, b = bi[i] is a block whose index[i-1]'th entry * points at bi[i-1]. */ b = bi[i]; /* * the index entries up to but not including index[i-1] point at * finished blocks, so flush them for sure. */ for(j=0; j<index[i-1]; j++) if(flushblock(r->c, nil, b->data+j*VtScoreSize, ppb, epb, base+i-1) < 0) goto Err; /* * if index[i-1] is the last entry in the block and is global * (i.e. the kid is flushed), then we can flush this block. */ if(j==ppb-1 && vtglobaltolocal(b->data+j*VtScoreSize)==NilBlock) doflush = 1; } if(doflush){ if(i == depth) score = e.score; else score = bi[i+1]->data+index[i]*VtScoreSize; if(flushblock(r->c, bi[i], score, ppb, epb, base+i) < 0) goto Err; } } Err: /* top: entry. do this always so that the score is up-to-date */ vtentrypack(&e, bi[depth+1]->data, index[depth]); for(i=0; i<nelem(bi); i++) if(bi[i]) vtblockput(bi[i]); return ret; }