| #include "stdinc.h" |
| #include <fcall.h> /* dirmodefmt */ |
| #include "vac.h" |
| |
| #ifndef PLAN9PORT |
| #pragma varargck type "t" ulong |
| #endif |
| |
| VacFs *fs; |
| int tostdout; |
| int diff; |
| int nwant; |
| char **want; |
| int *found; |
| int chatty; |
| VtConn *conn; |
| int errors; |
| int settimes; |
| int table; |
| |
| int mtimefmt(Fmt*); |
| void unvac(VacFile*, char*, VacDir*); |
| |
| void |
| usage(void) |
| { |
| fprint(2, "usage: unvac [-TVcdtv] [-h host] file.vac [file ...]\n"); |
| threadexitsall("usage"); |
| } |
| |
| struct |
| { |
| vlong data; |
| vlong skipdata; |
| } stats; |
| |
| void |
| threadmain(int argc, char *argv[]) |
| { |
| int i, printstats; |
| char *host; |
| VacFile *f; |
| |
| fmtinstall('H', encodefmt); |
| fmtinstall('V', vtscorefmt); |
| fmtinstall('F', vtfcallfmt); |
| fmtinstall('t', mtimefmt); |
| fmtinstall('M', dirmodefmt); |
| |
| host = nil; |
| printstats = 0; |
| |
| ARGBEGIN{ |
| case 'T': |
| settimes = 1; |
| break; |
| case 'V': |
| chattyventi = 1; |
| break; |
| case 'c': |
| tostdout++; |
| break; |
| case 'd': |
| diff++; |
| break; |
| case 'h': |
| host = EARGF(usage()); |
| break; |
| case 's': |
| printstats++; |
| break; |
| case 't': |
| table++; |
| break; |
| case 'v': |
| chatty++; |
| break; |
| default: |
| usage(); |
| }ARGEND |
| |
| if(argc < 1) |
| usage(); |
| |
| if(tostdout && diff){ |
| fprint(2, "cannot use -c with -d\n"); |
| usage(); |
| } |
| |
| conn = vtdial(host); |
| if(conn == nil) |
| sysfatal("could not connect to server: %r"); |
| |
| if(vtconnect(conn) < 0) |
| sysfatal("vtconnect: %r"); |
| |
| fs = vacfsopen(conn, argv[0], VtOREAD, 4<<20); |
| if(fs == nil) |
| sysfatal("vacfsopen: %r"); |
| |
| nwant = argc-1; |
| want = argv+1; |
| found = vtmallocz(nwant*sizeof found[0]); |
| |
| if((f = vacfsgetroot(fs)) == nil) |
| sysfatal("vacfsgetroot: %r"); |
| |
| unvac(f, nil, nil); |
| for(i=0; i<nwant; i++){ |
| if(want[i] && !found[i]){ |
| fprint(2, "warning: didn't find %s\n", want[i]); |
| errors++; |
| } |
| } |
| if(errors) |
| threadexitsall("errors"); |
| if(printstats) |
| fprint(2, "%lld bytes read, %lld bytes skipped\n", |
| stats.data, stats.skipdata); |
| threadexitsall(0); |
| } |
| |
| int |
| writen(int fd, char *buf, int n) |
| { |
| int m; |
| int oldn; |
| |
| oldn = n; |
| while(n > 0){ |
| m = write(fd, buf, n); |
| if(m <= 0) |
| return -1; |
| buf += m; |
| n -= m; |
| } |
| return oldn; |
| } |
| |
| int |
| wantfile(char *name) |
| { |
| int i, namelen, n; |
| |
| if(nwant == 0) |
| return 1; |
| |
| namelen = strlen(name); |
| for(i=0; i<nwant; i++){ |
| if(want[i] == nil) |
| continue; |
| n = strlen(want[i]); |
| if(n < namelen && name[n] == '/' && memcmp(name, want[i], n) == 0) |
| return 1; |
| if(namelen < n && want[i][namelen] == '/' && memcmp(want[i], name, namelen) == 0) |
| return 1; |
| if(n == namelen && memcmp(name, want[i], n) == 0){ |
| found[i] = 1; |
| return 1; |
| } |
| } |
| 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; |
| #ifdef PLAN9PORT |
| if(mode&ModeLink) |
| mode9 |= DMSYMLINK; |
| if(mode&ModeNamedPipe) |
| mode9 |= DMNAMEDPIPE; |
| if(mode&ModeSetUid) |
| mode9 |= DMSETUID; |
| if(mode&ModeSetGid) |
| mode9 |= DMSETGID; |
| if(mode&ModeDevice) |
| mode9 |= DMDEVICE; |
| #endif |
| 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|0700|(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 |
| mtimefmt(Fmt *f) |
| { |
| Tm *tm; |
| |
| tm = localtime(va_arg(f->args, ulong)); |
| fmtprint(f, "%04d-%02d-%02d %02d:%02d", |
| tm->year+1900, tm->mon+1, tm->mday, |
| tm->hour, tm->min); |
| return 0; |
| } |