xref: /plan9/sys/src/cmd/disk/mkext.c (revision c93608cc76758b2be624199c6208a0f90bad298d)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 enum{
6 	LEN	= 8*1024,
7 	NFLDS	= 6,		/* filename, modes, uid, gid, mtime, bytes */
8 };
9 
10 int	selected(char*, int, char*[]);
11 void	mkdirs(char*, char*);
12 void	mkdir(char*, ulong, ulong, char*, char*);
13 void	extract(char*, ulong, ulong, char*, char*, uvlong);
14 void	seekpast(uvlong);
15 void	error(char*, ...);
16 void	warn(char*, ...);
17 void	usage(void);
18 #pragma varargck argpos warn 1
19 #pragma varargck argpos error 1
20 
21 Biobufhdr bin;
22 uchar	binbuf[2*LEN];
23 char	linebuf[LEN];
24 int	uflag;
25 int	hflag;
26 int	vflag;
27 int	Tflag;
28 
29 void
main(int argc,char ** argv)30 main(int argc, char **argv)
31 {
32 	Biobuf bout;
33 	char *fields[NFLDS], name[2*LEN], *p, *namep;
34 	char *uid, *gid;
35 	ulong mode, mtime;
36 	uvlong bytes;
37 
38 	quotefmtinstall();
39 	namep = name;
40 	ARGBEGIN{
41 	case 'd':
42 		p = ARGF();
43 		if(strlen(p) >= LEN)
44 			error("destination fs name too long\n");
45 		strcpy(name, p);
46 		namep = name + strlen(name);
47 		break;
48 	case 'h':
49 		hflag = 1;
50 		Binit(&bout, 1, OWRITE);
51 		break;
52 	case 'u':
53 		uflag = 1;
54 		Tflag = 1;
55 		break;
56 	case 'T':
57 		Tflag = 1;
58 		break;
59 	case 'v':
60 		vflag = 1;
61 		break;
62 	default:
63 		usage();
64 	}ARGEND
65 
66 	Binits(&bin, 0, OREAD, binbuf, sizeof binbuf);
67 	while(p = Brdline(&bin, '\n')){
68 		p[Blinelen(&bin)-1] = '\0';
69 		strcpy(linebuf, p);
70 		p = linebuf;
71 		if(strcmp(p, "end of archive") == 0){
72 			Bterm(&bout);
73 			fprint(2, "done\n");
74 			exits(0);
75 		}
76 		if (gettokens(p, fields, NFLDS, " \t") != NFLDS){
77 			warn("too few fields in file header");
78 			continue;
79 		}
80 		p = unquotestrdup(fields[0]);
81 		strcpy(namep, p);
82 		free(p);
83 		mode = strtoul(fields[1], 0, 8);
84 		uid = fields[2];
85 		gid = fields[3];
86 		mtime = strtoul(fields[4], 0, 10);
87 		bytes = strtoull(fields[5], 0, 10);
88 		if(argc){
89 			if(!selected(namep, argc, argv)){
90 				if(bytes)
91 					seekpast(bytes);
92 				continue;
93 			}
94 			mkdirs(name, namep);
95 		}
96 		if(hflag){
97 			Bprint(&bout, "%q %luo %q %q %lud %llud\n",
98 				name, mode, uid, gid, mtime, bytes);
99 			if(bytes)
100 				seekpast(bytes);
101 			continue;
102 		}
103 		if(mode & DMDIR)
104 			mkdir(name, mode, mtime, uid, gid);
105 		else
106 			extract(name, mode, mtime, uid, gid, bytes);
107 	}
108 	fprint(2, "premature end of archive\n");
109 	exits("premature end of archive");
110 }
111 
112 int
fileprefix(char * prefix,char * s)113 fileprefix(char *prefix, char *s)
114 {
115 	while(*prefix)
116 		if(*prefix++ != *s++)
117 			return 0;
118 	if(*s && *s != '/')
119 		return 0;
120 	return 1;
121 }
122 
123 int
selected(char * s,int argc,char * argv[])124 selected(char *s, int argc, char *argv[])
125 {
126 	int i;
127 
128 	for(i=0; i<argc; i++)
129 		if(fileprefix(argv[i], s))
130 			return 1;
131 	return 0;
132 }
133 
134 void
mkdirs(char * name,char * namep)135 mkdirs(char *name, char *namep)
136 {
137 	char buf[2*LEN], *p;
138 	int fd;
139 
140 	strcpy(buf, name);
141 	for(p = &buf[namep - name]; p = utfrune(p, '/'); p++){
142 		if(p[1] == '\0')
143 			return;
144 		*p = 0;
145 		fd = create(buf, OREAD, 0775|DMDIR);
146 		close(fd);
147 		*p = '/';
148 	}
149 }
150 
151 void
mkdir(char * name,ulong mode,ulong mtime,char * uid,char * gid)152 mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid)
153 {
154 	Dir *d, xd;
155 	int fd;
156 	char *p;
157 	char olderr[256];
158 
159 	fd = create(name, OREAD, mode);
160 	if(fd < 0){
161 		rerrstr(olderr, sizeof(olderr));
162 		if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){
163 			free(d);
164 			warn("can't make directory %q, mode %luo: %s", name, mode, olderr);
165 			return;
166 		}
167 		free(d);
168 	}
169 	close(fd);
170 
171 	d = &xd;
172 	nulldir(d);
173 	p = utfrrune(name, L'/');
174 	if(p)
175 		p++;
176 	else
177 		p = name;
178 	d->name = p;
179 	if(uflag){
180 		d->uid = uid;
181 		d->gid = gid;
182 	}
183 	if(Tflag)
184 		d->mtime = mtime;
185 	d->mode = mode;
186 	if(dirwstat(name, d) < 0)
187 		warn("can't set modes for %q: %r", name);
188 
189 	if(uflag||Tflag){
190 		if((d = dirstat(name)) == nil){
191 			warn("can't reread modes for %q: %r", name);
192 			return;
193 		}
194 		if(Tflag && d->mtime != mtime)
195 			warn("%q: time mismatch %lud %lud\n", name, mtime, d->mtime);
196 		if(uflag && strcmp(uid, d->uid))
197 			warn("%q: uid mismatch %q %q", name, uid, d->uid);
198 		if(uflag && strcmp(gid, d->gid))
199 			warn("%q: gid mismatch %q %q", name, gid, d->gid);
200 	}
201 }
202 
203 void
extract(char * name,ulong mode,ulong mtime,char * uid,char * gid,uvlong bytes)204 extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, uvlong bytes)
205 {
206 	Dir d, *nd;
207 	Biobuf *b;
208 	char buf[LEN];
209 	char *p;
210 	ulong n;
211 	uvlong tot;
212 
213 	if(vflag)
214 		print("x %q %llud bytes\n", name, bytes);
215 
216 	b = Bopen(name, OWRITE);
217 	if(!b){
218 		warn("can't make file %q: %r", name);
219 		seekpast(bytes);
220 		return;
221 	}
222 	for(tot = 0; tot < bytes; tot += n){
223 		n = sizeof buf;
224 		if(tot + n > bytes)
225 			n = bytes - tot;
226 		n = Bread(&bin, buf, n);
227 		if(n <= 0)
228 			error("premature eof reading %q", name);
229 		if(Bwrite(b, buf, n) != n)
230 			warn("error writing %q: %r", name);
231 	}
232 
233 	nulldir(&d);
234 	p = utfrrune(name, '/');
235 	if(p)
236 		p++;
237 	else
238 		p = name;
239 	d.name = p;
240 	if(uflag){
241 		d.uid = uid;
242 		d.gid = gid;
243 	}
244 	if(Tflag)
245 		d.mtime = mtime;
246 	d.mode = mode;
247 	Bflush(b);
248 	if(dirfwstat(Bfildes(b), &d) < 0)
249 		warn("can't set modes for %q: %r", name);
250 	if(uflag||Tflag){
251 		if((nd = dirfstat(Bfildes(b))) == nil)
252 			warn("can't reread modes for %q: %r", name);
253 		else{
254 			if(Tflag && nd->mtime != mtime)
255 				warn("%q: time mismatch %lud %lud\n",
256 					name, mtime, nd->mtime);
257 			if(uflag && strcmp(uid, nd->uid))
258 				warn("%q: uid mismatch %q %q",
259 					name, uid, nd->uid);
260 			if(uflag && strcmp(gid, nd->gid))
261 				warn("%q: gid mismatch %q %q",
262 					name, gid, nd->gid);
263 			free(nd);
264 		}
265 	}
266 	Bterm(b);
267 }
268 
269 void
seekpast(uvlong bytes)270 seekpast(uvlong bytes)
271 {
272 	char buf[LEN];
273 	long n;
274 	uvlong tot;
275 
276 	for(tot = 0; tot < bytes; tot += n){
277 		n = sizeof buf;
278 		if(tot + n > bytes)
279 			n = bytes - tot;
280 		n = Bread(&bin, buf, n);
281 		if(n < 0)
282 			error("premature eof");
283 	}
284 }
285 
286 void
error(char * fmt,...)287 error(char *fmt, ...)
288 {
289 	char buf[1024];
290 	va_list arg;
291 
292 	sprint(buf, "%q: ", argv0);
293 	va_start(arg, fmt);
294 	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
295 	va_end(arg);
296 	fprint(2, "%s\n", buf);
297 	exits(0);
298 }
299 
300 void
warn(char * fmt,...)301 warn(char *fmt, ...)
302 {
303 	char buf[1024];
304 	va_list arg;
305 
306 	sprint(buf, "%q: ", argv0);
307 	va_start(arg, fmt);
308 	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
309 	va_end(arg);
310 	fprint(2, "%s\n", buf);
311 }
312 
313 void
usage(void)314 usage(void)
315 {
316 	fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n");
317 	exits("usage");
318 }
319