Blob


1 #include "stdinc.h"
2 #include <fcall.h> /* dirmodefmt */
3 #include "vac.h"
5 #ifndef PLAN9PORT
6 #pragma varargck type "t" ulong
7 #endif
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;
21 int mtimefmt(Fmt*);
22 void unvac(VacFile*, char*, VacDir*);
24 void
25 usage(void)
26 {
27 fprint(2, "usage: unvac [-TVcdtv] [-h host] file.vac [file ...]\n");
28 threadexitsall("usage");
29 }
31 struct
32 {
33 vlong data;
34 vlong skipdata;
35 } stats;
37 void
38 threadmain(int argc, char *argv[])
39 {
40 int i, printstats;
41 char *host;
42 VacFile *f;
44 fmtinstall('H', encodefmt);
45 fmtinstall('V', vtscorefmt);
46 fmtinstall('F', vtfcallfmt);
47 fmtinstall('t', mtimefmt);
48 fmtinstall('M', dirmodefmt);
50 host = nil;
51 printstats = 0;
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
82 if(argc < 1)
83 usage();
85 if(tostdout && diff){
86 fprint(2, "cannot use -c with -d\n");
87 usage();
88 }
90 conn = vtdial(host);
91 if(conn == nil)
92 sysfatal("could not connect to server: %r");
94 if(vtconnect(conn) < 0)
95 sysfatal("vtconnect: %r");
97 fs = vacfsopen(conn, argv[0], VtOREAD, 128<<20);
98 if(fs == nil)
99 sysfatal("vacfsopen: %r");
101 nwant = argc-1;
102 want = argv+1;
103 found = vtmallocz(nwant*sizeof found[0]);
105 if((f = vacfsgetroot(fs)) == nil)
106 sysfatal("vacfsgetroot: %r");
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++;
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);
123 int
124 writen(int fd, char *buf, int n)
126 int m;
127 int oldn;
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;
137 return oldn;
140 int
141 wantfile(char *name)
143 int i, namelen, n;
145 if(nwant == 0)
146 return 1;
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;
162 return 0;
165 void
166 unvac(VacFile *f, char *name, VacDir *vdir)
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;
179 if(vdir)
180 mode = vdir->mode;
181 else
182 mode = vacfilegetmode(f);
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) ? "/" : "");
212 else if(chatty)
213 fprint(2, "%s%s\n", name, (mode&ModeDir) ? "/" : "");
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;
233 if(mode&ModeDir){
234 if((vde = vdeopen(f)) == nil){
235 fprint(2, "vdeopen %s: %r", name);
236 errors++;
237 return;
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);
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;
255 free(dp);
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);
275 if(newname != newvdir.elem)
276 free(newname);
277 vdcleanup(&newvdir);
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;
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;
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;
309 break;
313 else if((fd = create(name, OWRITE, mode&0777)) < 0){
314 fprint(2, "create %s: %r\n", name);
315 errors++;
316 return;
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;
327 off += n;
328 stats.data += n;
330 close(fd);
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);
341 int
342 mtimefmt(Fmt *f)
344 Tm *tm;
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;