2008-06-14 13:28:49 -04:00
|
|
|
#include "stdinc.h"
|
2008-06-14 14:29:24 -04:00
|
|
|
#include <fcall.h> /* dirmodefmt */
|
2008-06-14 13:28:49 -04:00
|
|
|
#include "vac.h"
|
|
|
|
|
|
2008-06-14 23:12:20 -04:00
|
|
|
#ifndef PLAN9PORT
|
|
|
|
|
#pragma varargck type "t" ulong
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-06-14 13:28:49 -04:00
|
|
|
VacFs *fs;
|
|
|
|
|
int tostdout;
|
2008-12-06 16:14:10 -08:00
|
|
|
int diff;
|
2008-06-14 13:28:49 -04:00
|
|
|
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)
|
|
|
|
|
{
|
2008-12-06 16:14:10 -08:00
|
|
|
fprint(2, "usage: unvac [-TVcdtv] [-h host] file.vac [file ...]\n");
|
2008-06-14 13:28:49 -04:00
|
|
|
threadexitsall("usage");
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-06 16:14:10 -08:00
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
vlong data;
|
|
|
|
|
vlong skipdata;
|
|
|
|
|
} stats;
|
|
|
|
|
|
2008-06-14 13:28:49 -04:00
|
|
|
void
|
|
|
|
|
threadmain(int argc, char *argv[])
|
|
|
|
|
{
|
2008-12-06 16:14:10 -08:00
|
|
|
int i, printstats;
|
2008-06-14 13:28:49 -04:00
|
|
|
char *host;
|
|
|
|
|
VacFile *f;
|
|
|
|
|
|
|
|
|
|
fmtinstall('H', encodefmt);
|
|
|
|
|
fmtinstall('V', vtscorefmt);
|
|
|
|
|
fmtinstall('F', vtfcallfmt);
|
2008-06-14 14:29:24 -04:00
|
|
|
fmtinstall('t', mtimefmt);
|
2008-06-14 13:28:49 -04:00
|
|
|
fmtinstall('M', dirmodefmt);
|
|
|
|
|
|
|
|
|
|
host = nil;
|
2008-12-06 16:14:10 -08:00
|
|
|
printstats = 0;
|
|
|
|
|
|
2008-06-14 13:28:49 -04:00
|
|
|
ARGBEGIN{
|
|
|
|
|
case 'T':
|
|
|
|
|
settimes = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 'V':
|
|
|
|
|
chattyventi = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 'c':
|
|
|
|
|
tostdout++;
|
|
|
|
|
break;
|
2008-12-06 16:14:10 -08:00
|
|
|
case 'd':
|
|
|
|
|
diff++;
|
|
|
|
|
break;
|
2008-06-14 13:28:49 -04:00
|
|
|
case 'h':
|
|
|
|
|
host = EARGF(usage());
|
|
|
|
|
break;
|
2008-12-06 16:14:10 -08:00
|
|
|
case 's':
|
|
|
|
|
printstats++;
|
|
|
|
|
break;
|
2008-06-14 13:28:49 -04:00
|
|
|
case 't':
|
|
|
|
|
table++;
|
|
|
|
|
break;
|
|
|
|
|
case 'v':
|
|
|
|
|
chatty++;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
usage();
|
|
|
|
|
}ARGEND
|
|
|
|
|
|
|
|
|
|
if(argc < 1)
|
|
|
|
|
usage();
|
|
|
|
|
|
2008-12-06 16:14:10 -08:00
|
|
|
if(tostdout && diff){
|
|
|
|
|
fprint(2, "cannot use -c with -d\n");
|
|
|
|
|
usage();
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-14 13:28:49 -04:00
|
|
|
conn = vtdial(host);
|
|
|
|
|
if(conn == nil)
|
|
|
|
|
sysfatal("could not connect to server: %r");
|
|
|
|
|
|
|
|
|
|
if(vtconnect(conn) < 0)
|
|
|
|
|
sysfatal("vtconnect: %r");
|
|
|
|
|
|
2009-05-25 02:11:27 -07:00
|
|
|
fs = vacfsopen(conn, argv[0], VtOREAD, 4<<20);
|
2008-06-14 13:28:49 -04:00
|
|
|
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");
|
2008-12-06 16:14:10 -08:00
|
|
|
if(printstats)
|
|
|
|
|
fprint(2, "%lld bytes read, %lld bytes skipped\n",
|
|
|
|
|
stats.data, stats.skipdata);
|
2008-06-14 13:28:49 -04:00
|
|
|
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;
|
2008-12-06 14:42:14 -08:00
|
|
|
if(namelen < n && want[i][namelen] == '/' && memcmp(want[i], name, namelen) == 0)
|
2008-06-14 13:28:49 -04:00
|
|
|
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];
|
2008-12-06 16:14:10 -08:00
|
|
|
int fd, n, m, bsize;
|
2008-06-14 14:29:24 -04:00
|
|
|
ulong mode, mode9;
|
2008-06-14 13:28:49 -04:00
|
|
|
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;
|
2008-06-14 23:12:20 -04:00
|
|
|
#ifdef PLAN9PORT
|
|
|
|
|
if(mode&ModeLink)
|
|
|
|
|
mode9 |= DMSYMLINK;
|
2008-06-14 13:28:49 -04:00
|
|
|
if(mode&ModeNamedPipe)
|
|
|
|
|
mode9 |= DMNAMEDPIPE;
|
|
|
|
|
if(mode&ModeSetUid)
|
|
|
|
|
mode9 |= DMSETUID;
|
|
|
|
|
if(mode&ModeSetGid)
|
|
|
|
|
mode9 |= DMSETGID;
|
|
|
|
|
if(mode&ModeDevice)
|
|
|
|
|
mode9 |= DMDEVICE;
|
2008-06-14 23:12:20 -04:00
|
|
|
#endif
|
2008-06-14 14:29:24 -04:00
|
|
|
print("%M %-10s %-10s %11lld %t %s\n",
|
2008-06-14 13:28:49 -04:00
|
|
|
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){
|
2008-12-06 16:14:10 -08:00
|
|
|
off = 0;
|
2008-06-14 13:28:49 -04:00
|
|
|
if(tostdout)
|
|
|
|
|
fd = dup(1, -1);
|
2008-12-06 16:14:10 -08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-06-14 13:28:49 -04:00
|
|
|
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);
|
2008-12-06 16:14:10 -08:00
|
|
|
Err:
|
2008-06-14 13:28:49 -04:00
|
|
|
errors++;
|
|
|
|
|
close(fd);
|
|
|
|
|
remove(name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
off += n;
|
2008-12-06 16:14:10 -08:00
|
|
|
stats.data += n;
|
2008-06-14 13:28:49 -04:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|