Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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
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
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
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
314
usage(void)
315
{
316
	fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n");
317
	exits("usage");
318
}