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
usage(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
threadmain(int argc,char * argv[])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
writen(int fd,char * buf,int n)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
wantfile(char * name)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
unvac(VacFile * f,char * name,VacDir * vdir)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
mtimefmt(Fmt * f)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 }
352