unvac.c (6517B)
1 #include "stdinc.h" 2 #include <fcall.h> /* dirmodefmt */ 3 #include "vac.h" 4 5 #ifndef PLAN9PORT 6 #pragma varargck type "t" ulong 7 #endif 8 9 VacFs *fs; 10 int tostdout; 11 int diff; 12 int nwant; 13 char **want; 14 int *found; 15 int chatty; 16 VtConn *conn; 17 int errors; 18 int settimes; 19 int table; 20 21 int mtimefmt(Fmt*); 22 void unvac(VacFile*, char*, VacDir*); 23 24 void 25 usage(void) 26 { 27 fprint(2, "usage: unvac [-TVcdtv] [-h host] file.vac [file ...]\n"); 28 threadexitsall("usage"); 29 } 30 31 struct 32 { 33 vlong data; 34 vlong skipdata; 35 } stats; 36 37 void 38 threadmain(int argc, char *argv[]) 39 { 40 int i, printstats; 41 char *host; 42 VacFile *f; 43 44 fmtinstall('H', encodefmt); 45 fmtinstall('V', vtscorefmt); 46 fmtinstall('F', vtfcallfmt); 47 fmtinstall('t', mtimefmt); 48 fmtinstall('M', dirmodefmt); 49 50 host = nil; 51 printstats = 0; 52 53 ARGBEGIN{ 54 case 'T': 55 settimes = 1; 56 break; 57 case 'V': 58 chattyventi = 1; 59 break; 60 case 'c': 61 tostdout++; 62 break; 63 case 'd': 64 diff++; 65 break; 66 case 'h': 67 host = EARGF(usage()); 68 break; 69 case 's': 70 printstats++; 71 break; 72 case 't': 73 table++; 74 break; 75 case 'v': 76 chatty++; 77 break; 78 default: 79 usage(); 80 }ARGEND 81 82 if(argc < 1) 83 usage(); 84 85 if(tostdout && diff){ 86 fprint(2, "cannot use -c with -d\n"); 87 usage(); 88 } 89 90 conn = vtdial(host); 91 if(conn == nil) 92 sysfatal("could not connect to server: %r"); 93 94 if(vtconnect(conn) < 0) 95 sysfatal("vtconnect: %r"); 96 97 fs = vacfsopen(conn, argv[0], VtOREAD, 128<<20); 98 if(fs == nil) 99 sysfatal("vacfsopen: %r"); 100 101 nwant = argc-1; 102 want = argv+1; 103 found = vtmallocz(nwant*sizeof found[0]); 104 105 if((f = vacfsgetroot(fs)) == nil) 106 sysfatal("vacfsgetroot: %r"); 107 108 unvac(f, nil, nil); 109 for(i=0; i<nwant; i++){ 110 if(want[i] && !found[i]){ 111 fprint(2, "warning: didn't find %s\n", want[i]); 112 errors++; 113 } 114 } 115 if(errors) 116 threadexitsall("errors"); 117 if(printstats) 118 fprint(2, "%lld bytes read, %lld bytes skipped\n", 119 stats.data, stats.skipdata); 120 threadexitsall(0); 121 } 122 123 int 124 writen(int fd, char *buf, int n) 125 { 126 int m; 127 int oldn; 128 129 oldn = n; 130 while(n > 0){ 131 m = write(fd, buf, n); 132 if(m <= 0) 133 return -1; 134 buf += m; 135 n -= m; 136 } 137 return oldn; 138 } 139 140 int 141 wantfile(char *name) 142 { 143 int i, namelen, n; 144 145 if(nwant == 0) 146 return 1; 147 148 namelen = strlen(name); 149 for(i=0; i<nwant; i++){ 150 if(want[i] == nil) 151 continue; 152 n = strlen(want[i]); 153 if(n < namelen && name[n] == '/' && memcmp(name, want[i], n) == 0) 154 return 1; 155 if(namelen < n && want[i][namelen] == '/' && memcmp(want[i], name, namelen) == 0) 156 return 1; 157 if(n == namelen && memcmp(name, want[i], n) == 0){ 158 found[i] = 1; 159 return 1; 160 } 161 } 162 return 0; 163 } 164 165 void 166 unvac(VacFile *f, char *name, VacDir *vdir) 167 { 168 static char buf[65536]; 169 int fd, n, m, bsize; 170 ulong mode, mode9; 171 char *newname; 172 char *what; 173 vlong off; 174 Dir d, *dp; 175 VacDirEnum *vde; 176 VacDir newvdir; 177 VacFile *newf; 178 179 if(vdir) 180 mode = vdir->mode; 181 else 182 mode = vacfilegetmode(f); 183 184 if(vdir){ 185 if(table){ 186 if(chatty){ 187 mode9 = vdir->mode&0777; 188 if(mode&ModeDir) 189 mode9 |= DMDIR; 190 if(mode&ModeAppend) 191 mode9 |= DMAPPEND; 192 if(mode&ModeExclusive) 193 mode9 |= DMEXCL; 194 #ifdef PLAN9PORT 195 if(mode&ModeLink) 196 mode9 |= DMSYMLINK; 197 if(mode&ModeNamedPipe) 198 mode9 |= DMNAMEDPIPE; 199 if(mode&ModeSetUid) 200 mode9 |= DMSETUID; 201 if(mode&ModeSetGid) 202 mode9 |= DMSETGID; 203 if(mode&ModeDevice) 204 mode9 |= DMDEVICE; 205 #endif 206 print("%M %-10s %-10s %11lld %t %s\n", 207 mode9, vdir->uid, vdir->gid, vdir->size, 208 vdir->mtime, name); 209 }else 210 print("%s%s\n", name, (mode&ModeDir) ? "/" : ""); 211 } 212 else if(chatty) 213 fprint(2, "%s%s\n", name, (mode&ModeDir) ? "/" : ""); 214 } 215 216 if(mode&(ModeDevice|ModeLink|ModeNamedPipe|ModeExclusive)){ 217 if(table) 218 return; 219 if(mode&ModeDevice) 220 what = "device"; 221 else if(mode&ModeLink) 222 what = "link"; 223 else if(mode&ModeNamedPipe) 224 what = "named pipe"; 225 else if(mode&ModeExclusive) 226 what = "lock"; 227 else 228 what = "unknown type of file"; 229 fprint(2, "warning: ignoring %s %s\n", what, name); 230 return; 231 } 232 233 if(mode&ModeDir){ 234 if((vde = vdeopen(f)) == nil){ 235 fprint(2, "vdeopen %s: %r", name); 236 errors++; 237 return; 238 } 239 if(!table && !tostdout && vdir){ 240 // create directory 241 if((dp = dirstat(name)) == nil){ 242 if((fd = create(name, OREAD, DMDIR|0700|(mode&0777))) < 0){ 243 fprint(2, "mkdir %s: %r\n", name); 244 vdeclose(vde); 245 } 246 close(fd); 247 }else{ 248 if(!(dp->mode&DMDIR)){ 249 fprint(2, "%s already exists and is not a directory\n", name); 250 errors++; 251 free(dp); 252 vdeclose(vde); 253 return; 254 } 255 free(dp); 256 } 257 } 258 while(vderead(vde, &newvdir) > 0){ 259 if(name == nil) 260 newname = newvdir.elem; 261 else 262 newname = smprint("%s/%s", name, newvdir.elem); 263 if(wantfile(newname)){ 264 if((newf = vacfilewalk(f, newvdir.elem)) == nil){ 265 fprint(2, "walk %s: %r\n", name); 266 errors++; 267 }else if(newf == f){ 268 fprint(2, "walk loop: %s\n", newname); 269 vacfiledecref(newf); 270 }else{ 271 unvac(newf, newname, &newvdir); 272 vacfiledecref(newf); 273 } 274 } 275 if(newname != newvdir.elem) 276 free(newname); 277 vdcleanup(&newvdir); 278 } 279 vdeclose(vde); 280 }else{ 281 if(!table){ 282 off = 0; 283 if(tostdout) 284 fd = dup(1, -1); 285 else if(diff && (fd = open(name, ORDWR)) >= 0){ 286 bsize = vacfiledsize(f); 287 while((n = readn(fd, buf, bsize)) > 0){ 288 if(sha1matches(f, off/bsize, (uchar*)buf, n)){ 289 off += n; 290 stats.skipdata += n; 291 continue; 292 } 293 seek(fd, off, 0); 294 if((m = vacfileread(f, buf, n, off)) < 0) 295 break; 296 if(writen(fd, buf, m) != m){ 297 fprint(2, "write %s: %r\n", name); 298 goto Err; 299 } 300 off += m; 301 stats.data += m; 302 if(m < n){ 303 nulldir(&d); 304 d.length = off; 305 if(dirfwstat(fd, &d) < 0){ 306 fprint(2, "dirfwstat %s: %r\n", name); 307 goto Err; 308 } 309 break; 310 } 311 } 312 } 313 else if((fd = create(name, OWRITE, mode&0777)) < 0){ 314 fprint(2, "create %s: %r\n", name); 315 errors++; 316 return; 317 } 318 while((n = vacfileread(f, buf, sizeof buf, off)) > 0){ 319 if(writen(fd, buf, n) != n){ 320 fprint(2, "write %s: %r\n", name); 321 Err: 322 errors++; 323 close(fd); 324 remove(name); 325 return; 326 } 327 off += n; 328 stats.data += n; 329 } 330 close(fd); 331 } 332 } 333 if(vdir && settimes && !tostdout){ 334 nulldir(&d); 335 d.mtime = vdir->mtime; 336 if(dirwstat(name, &d) < 0) 337 fprint(2, "warning: setting mtime on %s: %r", name); 338 } 339 } 340 341 int 342 mtimefmt(Fmt *f) 343 { 344 Tm *tm; 345 346 tm = localtime(va_arg(f->args, ulong)); 347 fmtprint(f, "%04d-%02d-%02d %02d:%02d", 348 tm->year+1900, tm->mon+1, tm->mday, 349 tm->hour, tm->min); 350 return 0; 351 }