void vacstdin(VacFile *fp, char *name) { int64_t off; VacFile *f; static char buf[8192]; int n; if((f = vacfilecreate(fp, name, 0666)) == nil){ warn("vacfilecreate %s: %r", name); return; } off = 0; while((n = read(0, buf, sizeof buf)) > 0){ if(vacfilewrite(f, buf, n, off) < 0){ warn("venti write %s: %r", name); vacfiledecref(f); return; } off += n; } vacfileflush(f, 1); vacfiledecref(f); }
char * rremove(Fid *f) { VacFile *vf, *vfp; char errbuf[80]; char *err = nil; if(!f->busy) return vtstrdup(Enotexist); vf = f->file; vfp = vacfilegetparent(vf); if(!permf(vfp, f->user, Pwrite)) { err = Eperm; goto Exit; } if(!vacfileremove(vf)) { rerrstr(errbuf, sizeof errbuf); err = errbuf; } Exit: vacfiledecref(vfp); rclunk(f); return vtstrdup(err); }
char* rcreate(Fid* fid) { VacFile *vf; uint32_t mode; if(fid->open) return vtstrdup(Eisopen); if(!fid->busy) return vtstrdup(Enotexist); if(fs->mode & ModeSnapshot) return vtstrdup(Erdonly); vf = fid->file; if(!vacfileisdir(vf)) return vtstrdup(Enotdir); if(!permf(vf, fid->user, Pwrite)) return vtstrdup(Eperm); mode = rhdr.perm & 0777; if(rhdr.perm & DMDIR){ if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND)) return vtstrdup(Emode); switch(rhdr.mode & OPERM){ default: return vtstrdup(Emode); case OEXEC: case OREAD: break; case OWRITE: case ORDWR: return vtstrdup(Eperm); } mode |= ModeDir; } vf = vacfilecreate(vf, rhdr.name, mode); if(vf == nil) { char err[80]; rerrstr(err, sizeof err); return vtstrdup(err); } vacfiledecref(fid->file); fid->file = vf; fid->qid.type = QTFILE; if(vacfileisdir(vf)) fid->qid.type = QTDIR; fid->qid.vers = vacfilegetmcount(vf); fid->qid.path = vacfilegetid(vf); thdr.qid = fid->qid; thdr.iounit = messagesize - IOHDRSZ; return 0; }
char * rclunk(Fid *f) { f->busy = 0; f->open = 0; vtfree(f->user); f->user = nil; if(f->file) vacfiledecref(f->file); f->file = nil; vdeclose(f->vde); f->vde = nil; return 0; }
char * rstat(Fid *f) { VacDir dir; static uint8_t statbuf[1024]; VacFile *parent; if(!f->busy) return vtstrdup(Enotexist); parent = vacfilegetparent(f->file); vacfilegetdir(f->file, &dir); thdr.stat = statbuf; thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf); vdcleanup(&dir); vacfiledecref(parent); return 0; }
int vacmerge(VacFile *fp, char *name) { VacFs *mfs; VacDir vd; VacDirEnum *de; VacFile *mp; uint64_t maxqid, offset; if(strlen(name) < 4 || strcmp(name+strlen(name)-4, ".vac") != 0) return -1; if((mfs = vacfsopen(z, name, VtOREAD, 100)) == nil) return -1; if(verbose) fprint(2, "merging %s\n", name); mp = vacfsgetroot(mfs); de = vdeopen(mp); if(de){ offset = 0; if(vacfsgetmaxqid(mfs, &maxqid) >= 0){ _vacfsnextqid(fs, &offset); vacfsjumpqid(fs, maxqid+1); } while(vderead(de, &vd) > 0){ if(vd.qid > maxqid){ warn("vacmerge %s: maxqid=%lld but %s has %lld", name, maxqid, vd.elem, vd.qid); vacfsjumpqid(fs, vd.qid - maxqid); maxqid = vd.qid; } vacmergefile(fp, mp, &vd, name, offset, maxqid); vdcleanup(&vd); } vdeclose(de); } vacfiledecref(mp); vacfsclose(mfs); return 0; }
/* * fp is the directory we're writing. * mp is the directory whose contents we're merging in. * d is the directory entry of the file from mp that we want to add to fp. * vacfile is the name of the .vac file, for error messages. * offset is the qid that qid==0 in mp should correspond to. * max is the maximum qid we expect to see (not really needed). */ int vacmergefile(VacFile *fp, VacFile *mp, VacDir *d, char *vacfile, int64_t offset, int64_t max) { VtEntry ed, em; VacFile *mf; VacFile *f; mf = vacfilewalk(mp, d->elem); if(mf == nil){ warn("could not walk %s in %s", d->elem, vacfile); return -1; } if(vacfilegetentries(mf, &ed, &em) < 0){ warn("could not get entries for %s in %s", d->elem, vacfile); vacfiledecref(mf); return -1; } if((f = vacfilecreate(fp, d->elem, d->mode)) == nil){ warn("vacfilecreate %s: %r", d->elem); vacfiledecref(mf); return -1; } if(d->qidspace){ d->qidoffset += offset; d->qidmax += offset; }else{ d->qidspace = 1; d->qidoffset = offset; d->qidmax = max; } if(vacfilesetdir(f, d) < 0 || vacfilesetentries(f, &ed, &em) < 0 || vacfilesetqidspace(f, d->qidoffset, d->qidmax) < 0){ warn("vacmergefile %s: %r", d->elem); vacfiledecref(mf); vacfiledecref(f); return -1; } vacfiledecref(mf); vacfiledecref(f); return 0; }
void threadmain(int argc, char **argv) { int i, j, fd, n, printstats; Dir *d; char *s; uint64_t u; VacFile *f, *fdiff; VacFs *fsdiff; int blocksize; int outfd; char *stdinname; char *diffvac; uint64_t qid; fmtinstall('F', vtfcallfmt); fmtinstall('H', encodefmt); fmtinstall('V', vtscorefmt); blocksize = BlockSize; stdinname = nil; printstats = 0; fsdiff = nil; diffvac = nil; ARGBEGIN{ case 'V': chattyventi++; break; case 'a': archivefile = EARGF(usage()); break; case 'b': u = unittoull(EARGF(usage())); if(u < 512) u = 512; if(u > VtMaxLumpSize) u = VtMaxLumpSize; blocksize = u; break; case 'd': diffvac = EARGF(usage()); break; case 'e': excludepattern(EARGF(usage())); break; case 'f': vacfile = EARGF(usage()); break; case 'h': host = EARGF(usage()); break; case 'i': stdinname = EARGF(usage()); break; case 'm': merge++; break; case 'q': qdiff++; break; case 's': printstats++; break; case 'v': verbose++; break; case 'x': loadexcludefile(EARGF(usage())); break; default: usage(); }ARGEND if(argc == 0 && !stdinname) usage(); if(archivefile && (vacfile || diffvac)){ fprint(2, "cannot use -a with -f, -d\n"); usage(); } z = vtdial(host); if(z == nil) sysfatal("could not connect to server: %r"); if(vtconnect(z) < 0) sysfatal("vtconnect: %r"); // Setup: // fs is the output vac file system // f is directory in output vac to write new files // fdiff is corresponding directory in existing vac if(archivefile){ VacFile *fp; char yyyy[5]; char mmdd[10]; char oldpath[40]; Tm tm; fdiff = nil; if((outfd = open(archivefile, ORDWR)) < 0){ if(access(archivefile, 0) >= 0) sysfatal("open %s: %r", archivefile); if((outfd = create(archivefile, OWRITE, 0666)) < 0) sysfatal("create %s: %r", archivefile); atexit(removevacfile); // because it is new if((fs = vacfscreate(z, blocksize, 512)) == nil) sysfatal("vacfscreate: %r"); }else{ if((fs = vacfsopen(z, archivefile, VtORDWR, 512)) == nil) sysfatal("vacfsopen %s: %r", archivefile); if((fdiff = recentarchive(fs, oldpath)) != nil){ if(verbose) fprint(2, "diff %s\n", oldpath); }else if(verbose) fprint(2, "no recent archive to diff against\n"); } // Create yyyy/mmdd. tm = *localtime(time(0)); snprint(yyyy, sizeof yyyy, "%04d", tm.year+1900); fp = vacfsgetroot(fs); if((f = vacfilewalk(fp, yyyy)) == nil && (f = vacfilecreate(fp, yyyy, ModeDir|0555)) == nil) sysfatal("vacfscreate %s: %r", yyyy); vacfiledecref(fp); fp = f; snprint(mmdd, sizeof mmdd, "%02d%02d", tm.mon+1, tm.mday); n = 0; while((f = vacfilewalk(fp, mmdd)) != nil){ vacfiledecref(f); n++; snprint(mmdd+4, sizeof mmdd-4, ".%d", n); } f = vacfilecreate(fp, mmdd, ModeDir|0555); if(f == nil) sysfatal("vacfscreate %s/%s: %r", yyyy, mmdd); vacfiledecref(fp); if(verbose) fprint(2, "archive %s/%s\n", yyyy, mmdd); }else{ if(vacfile == nil) outfd = 1; else if((outfd = create(vacfile, OWRITE, 0666)) < 0) sysfatal("create %s: %r", vacfile); atexit(removevacfile); if((fs = vacfscreate(z, blocksize, 512)) == nil) sysfatal("vacfscreate: %r"); f = vacfsgetroot(fs); fdiff = nil; if(diffvac){ if((fsdiff = vacfsopen(z, diffvac, VtOREAD, 128)) == nil) warn("vacfsopen %s: %r", diffvac); else fdiff = vacfsgetroot(fsdiff); } } if(stdinname) vacstdin(f, stdinname); for(i=0; i<argc; i++){ // We can't use / and . and .. and ../.. as valid archive // names, so expand to the list of files in the directory. if(argv[i][0] == 0){ warn("empty string given as command-line argument"); continue; } cleanname(argv[i]); if(strcmp(argv[i], "/") == 0 || strcmp(argv[i], ".") == 0 || strcmp(argv[i], "..") == 0 || (strlen(argv[i]) > 3 && strcmp(argv[i]+strlen(argv[i])-3, "/..") == 0)){ if((fd = open(argv[i], OREAD)) < 0){ warn("open %s: %r", argv[i]); continue; } while((n = dirread(fd, &d)) > 0){ for(j=0; j<n; j++){ s = vtmalloc(strlen(argv[i])+1+strlen(d[j].name)+1); strcpy(s, argv[i]); strcat(s, "/"); strcat(s, d[j].name); cleanname(s); vac(f, fdiff, s, &d[j]); } free(d); } close(fd); continue; } if((d = dirstat(argv[i])) == nil){ warn("stat %s: %r", argv[i]); continue; } vac(f, fdiff, argv[i], d); free(d); } if(fdiff) vacfiledecref(fdiff); /* * Record the maximum qid so that vacs can be merged * without introducing overlapping qids. Older versions * of vac arranged that the root would have the largest * qid in the file system, but we can't do that anymore * (the root gets created first!). */ if(_vacfsnextqid(fs, &qid) >= 0) vacfilesetqidspace(f, 0, qid); vacfiledecref(f); /* * Copy fsdiff's root block score into fs's slot for that, * so that vacfssync will copy it into root.prev for us. * Just nice documentation, no effect. */ if(fsdiff) memmove(fs->score, fsdiff->score, VtScoreSize); if(vacfssync(fs) < 0) fprint(2, "vacfssync: %r\n"); fprint(outfd, "vac:%V\n", fs->score); atexitdont(removevacfile); vacfsclose(fs); vthangup(z); if(printstats){ fprint(2, "%d files, %d files skipped, %d directories\n" "%lld data bytes written, %lld data bytes skipped\n", stats.nfile, stats.skipfiles, stats.ndir, stats.data, stats.skipdata); dup(2, 1); packetstats(); } threadexitsall(0); }
/* * Archive the file named name, which has stat info d, * into the vac directory fp (p = parent). * * If we're doing a vac -d against another archive, the * equivalent directory to fp in that archive is diffp. */ void vac(VacFile *fp, VacFile *diffp, char *name, Dir *d) { char *elem, *s; static char buf[65536]; int fd, i, n, bsize; int64_t off; Dir *dk; // kids VacDir vd, vddiff; VacFile *f, *fdiff; VtEntry e; if(!includefile(name)){ warn("excluding %s%s", name, (d->mode&DMDIR) ? "/" : ""); return; } if(d->mode&DMDIR) stats.ndir++; else stats.nfile++; if(merge && vacmerge(fp, name) >= 0) return; if(verbose) fprint(2, "%s%s\n", name, (d->mode&DMDIR) ? "/" : ""); if((fd = open(name, OREAD)) < 0){ warn("open %s: %r", name); return; } elem = strrchr(name, '/'); if(elem) elem++; else elem = name; plan9tovacdir(&vd, d); if((f = vacfilecreate(fp, elem, vd.mode)) == nil){ warn("vacfilecreate %s: %r", name); return; } if(diffp) fdiff = vacfilewalk(diffp, elem); else fdiff = nil; if(vacfilesetdir(f, &vd) < 0) warn("vacfilesetdir %s: %r", name); if(d->mode&DMDIR){ while((n = dirread(fd, &dk)) > 0){ for(i=0; i<n; i++){ s = vtmalloc(strlen(name)+1+strlen(dk[i].name)+1); strcpy(s, name); strcat(s, "/"); strcat(s, dk[i].name); vac(f, fdiff, s, &dk[i]); free(s); } free(dk); } }else{ off = 0; bsize = fs->bsize; if(fdiff){ /* * Copy fdiff's contents into f by moving the score. * We'll diff and update below. */ if(vacfilegetentries(fdiff, &e, nil) >= 0) if(vacfilesetentries(f, &e, nil) >= 0){ bsize = e.dsize; /* * Or if -q is set, and the metadata looks the same, * don't even bother reading the file. */ if(qdiff && vacfilegetdir(fdiff, &vddiff) >= 0){ if(vddiff.mtime == vd.mtime) if(vddiff.size == vd.size) if(!vddiff.plan9 || (/* vddiff.p9path == vd.p9path && */ vddiff.p9version == vd.p9version)){ stats.skipfiles++; stats.nfile--; vdcleanup(&vddiff); goto Out; } /* * Skip over presumably-unchanged prefix * of an append-only file. */ if(vd.mode&ModeAppend) if(vddiff.size < vd.size) if(vddiff.plan9 && vd.plan9) if(vddiff.p9path == vd.p9path){ off = vd.size/bsize*bsize; if(seek(fd, off, 0) >= 0) stats.skipdata += off; else{ seek(fd, 0, 0); // paranoia off = 0; } } vdcleanup(&vddiff); // XXX different verbose chatty prints for kaminsky? } } } if(qdiff && verbose) fprint(2, "+%s\n", name); while((n = readn(fd, buf, bsize)) > 0){ if(fdiff && sha1matches(f, off/bsize, (uint8_t*)buf, n)){ off += n; stats.skipdata += n; continue; } if(vacfilewrite(f, buf, n, off) < 0){ warn("venti write %s: %r", name); goto Out; } stats.data += n; off += n; } /* * Since we started with fdiff's contents, * set the size in case fdiff was bigger. */ if(fdiff && vacfilesetsize(f, off) < 0) warn("vtfilesetsize %s: %r", name); } Out: vacfileflush(f, 1); vacfiledecref(f); if(fdiff) vacfiledecref(fdiff); close(fd); }
VacFile* recentarchive(VacFs *fs, char *path) { VacFile *fp, *f; VacDirEnum *de; VacDir vd; char buf[10]; int year, mmdd, nn, n, n1; char *p; fp = vacfsgetroot(fs); de = vdeopen(fp); year = 0; if(de){ for(; vderead(de, &vd) > 0; vdcleanup(&vd)){ if(strlen(vd.elem) != 4) continue; if((n = strtol(vd.elem, &p, 10)) < 1900 || *p != 0) continue; if(year < n) year = n; } } vdeclose(de); if(year == 0){ vacfiledecref(fp); return nil; } snprint(buf, sizeof buf, "%04d", year); if((f = vacfilewalk(fp, buf)) == nil){ fprint(2, "warning: dirread %s but cannot walk", buf); vacfiledecref(fp); return nil; } fp = f; de = vdeopen(fp); mmdd = 0; nn = 0; if(de){ for(; vderead(de, &vd) > 0; vdcleanup(&vd)){ if(strlen(vd.elem) < 4) continue; if((n = strtol(vd.elem, &p, 10)) < 100 || n > 1231 || p != vd.elem+4) continue; if(*p == '.'){ if(p[1] == '0' || (n1 = strtol(p+1, &p, 10)) == 0 || *p != 0) continue; }else{ if(*p != 0) continue; n1 = 0; } if(n < mmdd || (n == mmdd && n1 < nn)) continue; mmdd = n; nn = n1; } } vdeclose(de); if(mmdd == 0){ vacfiledecref(fp); return nil; } if(nn == 0) snprint(buf, sizeof buf, "%04d", mmdd); else snprint(buf, sizeof buf, "%04d.%d", mmdd, nn); if((f = vacfilewalk(fp, buf)) == nil){ fprint(2, "warning: dirread %s but cannot walk", buf); vacfiledecref(fp); return nil; } vacfiledecref(fp); sprint(path, "%04d/%s", year, buf); return f; }
char* rwalk(Fid *f) { VacFile *file, *nfile; Fid *nf; int nqid, nwname; Qid qid; char *err = nil; if(f->busy == 0) return Enotexist; nf = nil; if(rhdr.fid != rhdr.newfid){ if(f->open) return vtstrdup(Eisopen); if(f->busy == 0) return vtstrdup(Enotexist); nf = newfid(rhdr.newfid); if(nf->busy) return vtstrdup(Eisopen); nf->busy = 1; nf->open = 0; nf->qid = f->qid; nf->file = vacfileincref(f->file); nf->user = vtstrdup(f->user); f = nf; } nwname = rhdr.nwname; /* easy case */ if(nwname == 0) { thdr.nwqid = 0; return 0; } file = f->file; vacfileincref(file); qid = f->qid; for(nqid = 0; nqid < nwname; nqid++){ if((qid.type & QTDIR) == 0){ err = Enotdir; break; } if(!permf(file, f->user, Pexec)) { err = Eperm; break; } nfile = vacfilewalk(file, rhdr.wname[nqid]); if(nfile == nil) break; vacfiledecref(file); file = nfile; qid.type = QTFILE; if(vacfileisdir(file)) qid.type = QTDIR; qid.vers = vacfilegetmcount(file); qid.path = vacfilegetid(file); thdr.wqid[nqid] = qid; } thdr.nwqid = nqid; if(nqid == nwname){ /* success */ f->qid = thdr.wqid[nqid-1]; vacfiledecref(f->file); f->file = file; return 0; } vacfiledecref(file); if(nf != nil) rclunk(nf); /* only error on the first element */ if(nqid == 0) return vtstrdup(err); return 0; }
void unvac(VacFile *f, char *name, VacDir *vdir) { static char buf[65536]; int fd, n, m, bsize; ulong mode, mode9; char *newname; char *what; vlong off; Dir d, *dp; VacDirEnum *vde; VacDir newvdir; VacFile *newf; if(vdir) mode = vdir->mode; else mode = vacfilegetmode(f); if(vdir){ if(table){ if(chatty){ mode9 = vdir->mode&0777; if(mode&ModeDir) mode9 |= DMDIR; if(mode&ModeAppend) mode9 |= DMAPPEND; if(mode&ModeExclusive) mode9 |= DMEXCL; print("%M %-10s %-10s %11lld %t %s\n", mode9, vdir->uid, vdir->gid, vdir->size, vdir->mtime, name); }else print("%s%s\n", name, (mode&ModeDir) ? "/" : ""); } else if(chatty) fprint(2, "%s%s\n", name, (mode&ModeDir) ? "/" : ""); } if(mode&(ModeDevice|ModeLink|ModeNamedPipe|ModeExclusive)){ if(table) return; if(mode&ModeDevice) what = "device"; else if(mode&ModeLink) what = "link"; else if(mode&ModeNamedPipe) what = "named pipe"; else if(mode&ModeExclusive) what = "lock"; else what = "unknown type of file"; fprint(2, "warning: ignoring %s %s\n", what, name); return; } if(mode&ModeDir){ if((vde = vdeopen(f)) == nil){ fprint(2, "vdeopen %s: %r", name); errors++; return; } if(!table && !tostdout && vdir){ // create directory if((dp = dirstat(name)) == nil){ if((fd = create(name, OREAD, DMDIR|(mode&0777))) < 0){ fprint(2, "mkdir %s: %r\n", name); vdeclose(vde); } close(fd); }else{ if(!(dp->mode&DMDIR)){ fprint(2, "%s already exists and is not a directory\n", name); errors++; free(dp); vdeclose(vde); return; } free(dp); } } while(vderead(vde, &newvdir) > 0){ if(name == nil) newname = newvdir.elem; else newname = smprint("%s/%s", name, newvdir.elem); if(wantfile(newname)){ if((newf = vacfilewalk(f, newvdir.elem)) == nil){ fprint(2, "walk %s: %r\n", name); errors++; }else if(newf == f){ fprint(2, "walk loop: %s\n", newname); vacfiledecref(newf); }else{ unvac(newf, newname, &newvdir); vacfiledecref(newf); } } if(newname != newvdir.elem) free(newname); vdcleanup(&newvdir); } vdeclose(vde); }else{ if(!table){ off = 0; if(tostdout) fd = dup(1, -1); else if(diff && (fd = open(name, ORDWR)) >= 0){ bsize = vacfiledsize(f); while((n = readn(fd, buf, bsize)) > 0){ if(sha1matches(f, off/bsize, (uchar*)buf, n)){ off += n; stats.skipdata += n; continue; } seek(fd, off, 0); if((m = vacfileread(f, buf, n, off)) < 0) break; if(writen(fd, buf, m) != m){ fprint(2, "write %s: %r\n", name); goto Err; } off += m; stats.data += m; if(m < n){ nulldir(&d); d.length = off; if(dirfwstat(fd, &d) < 0){ fprint(2, "dirfwstat %s: %r\n", name); goto Err; } break; } } } else if((fd = create(name, OWRITE, mode&0777)) < 0){ fprint(2, "create %s: %r\n", name); errors++; return; } while((n = vacfileread(f, buf, sizeof buf, off)) > 0){ if(writen(fd, buf, n) != n){ fprint(2, "write %s: %r\n", name); Err: errors++; close(fd); remove(name); return; } off += n; stats.data += n; } close(fd); } } if(vdir && settimes && !tostdout){ nulldir(&d); d.mtime = vdir->mtime; if(dirwstat(name, &d) < 0) fprint(2, "warning: setting mtime on %s: %r", name); } }
int vacstat(VacFile *parent, VacDir *vd, uchar *p, int np) { int ret; Dir dir; #ifdef PLAN9PORT int n; VacFile *vf; uvlong size; char *ext = nil; #endif memset(&dir, 0, sizeof(dir)); dir.qid.path = vd->qid + vacfilegetqidoffset(parent); if(vd->qidspace) dir.qid.path += vd->qidoffset; dir.qid.vers = vd->mcount; dir.mode = vd->mode & 0777; if(vd->mode & ModeAppend){ dir.qid.type |= QTAPPEND; dir.mode |= DMAPPEND; } if(vd->mode & ModeExclusive){ dir.qid.type |= QTEXCL; dir.mode |= DMEXCL; } if(vd->mode & ModeDir){ dir.qid.type |= QTDIR; dir.mode |= DMDIR; } #ifdef PLAN9PORT if(vd->mode & (ModeLink|ModeDevice|ModeNamedPipe)){ vf = vacfilewalk(parent, vd->elem); if(vf == nil) return 0; vacfilegetsize(vf, &size); ext = malloc(size+1); if(ext == nil) return 0; n = vacfileread(vf, ext, size, 0); USED(n); ext[size] = 0; vacfiledecref(vf); if(vd->mode & ModeLink){ dir.qid.type |= QTSYMLINK; dir.mode |= DMSYMLINK; } if(vd->mode & ModeDevice) dir.mode |= DMDEVICE; if(vd->mode & ModeNamedPipe) dir.mode |= DMNAMEDPIPE; } #endif dir.atime = vd->atime; dir.mtime = vd->mtime; dir.length = vd->size; dir.name = vd->elem; dir.uid = vd->uid; dir.gid = vd->gid; dir.muid = vd->mid; ret = convD2M(&dir, p, np); #ifdef PLAN9PORT free(ext); #endif return ret; }