xref: /plan9/sys/src/cmd/vac/unvac.c (revision 3be74836e45a818042257560f5093e4f51d57220)
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