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