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