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
#include <auth.h>
5
#include <fcall.h>
6
#include <disk.h>
7
 
8
enum {
9
	LEN	= 8*1024,
10
	HUNKS	= 128,
11
};
12
 
13
typedef struct File File;
14
struct File{
15
	char	*new;
16
	char	*elem;
17
	char	*old;
18
	char	*uid;
19
	char	*gid;
20
	ulong	mode;
21
};
22
 
23
typedef void Mkfserr(char*, void*);
24
typedef void Mkfsenum(char*, char*, Dir*, void*);
25
 
26
typedef struct Name Name;
27
struct Name {
28
	int n;
29
	char *s;
30
};
31
 
32
typedef struct Mkaux Mkaux;
33
struct Mkaux {
34
	Mkfserr *warn;
35
	Mkfsenum *mkenum;
36
	char *root;
37
	char *xroot;
38
	char *proto;
39
	jmp_buf jmp;
40
	Biobuf *b;
41
 
42
	Name oldfile;
43
	Name fullname;
44
	int	lineno;
45
	int	indent;
46
 
47
	void *a;
48
};
49
 
50
static void domkfs(Mkaux *mkaux, File *me, int level);
51
 
52
static int	copyfile(Mkaux*, File*, Dir*, int);
53
static void	freefile(File*);
54
static File*	getfile(Mkaux*, File*);
55
static char*	getmode(Mkaux*, char*, ulong*);
56
static char*	getname(Mkaux*, char*, char**);
57
static char*	getpath(Mkaux*, char*);
58
static int	mkfile(Mkaux*, File*);
59
static char*	mkpath(Mkaux*, char*, char*);
60
static void	mktree(Mkaux*, File*, int);
61
static void	setnames(Mkaux*, File*);
62
static void	skipdir(Mkaux*);
63
static void	warn(Mkaux*, char *, ...);
64
 
65
//static void
66
//mprint(char *new, char *old, Dir *d, void*)
67
//{
68
//	print("%s %s %D\n", new, old, d);
69
//}
70
 
71
int
72
revrdproto(char *proto, char *root, char *xroot, Mkfsenum *mkenum, Mkfserr *mkerr, void *a)
73
{
74
	Mkaux mx, *m;
75
	File file;
76
	int rv;
77
 
78
	m = &mx;
79
	memset(&mx, 0, sizeof mx);
80
	if(root == nil)
81
		root = "/";
82
 
83
	m->root = root;
84
	m->xroot = xroot;
85
	m->warn = mkerr;
86
	m->mkenum = mkenum;
87
	m->a = a;
88
	m->proto = proto;
89
	m->lineno = 0;
90
	m->indent = 0;
91
	if((m->b = Bopen(proto, OREAD)) == nil) {
92
		werrstr("open '%s': %r", proto);
93
		return -1;
94
	}
95
 
96
	memset(&file, 0, sizeof file);
97
	file.new = "";
98
	file.old = nil;
99
 
100
	rv = 0;
101
	if(setjmp(m->jmp) == 0)
102
		domkfs(m, &file, -1);
103
	else
104
		rv = -1;
105
	free(m->oldfile.s);
106
	free(m->fullname.s);
107
	return rv;
108
}
109
 
110
static void*
111
emalloc(Mkaux *mkaux, ulong n)
112
{
113
	void *v;
114
 
115
	v = malloc(n);
116
	if(v == nil)
117
		longjmp(mkaux->jmp, 1);	/* memory leak */
118
	memset(v, 0, n);
119
	return v;
120
}
121
 
122
static char*
123
estrdup(Mkaux *mkaux, char *s)
124
{
125
	s = strdup(s);
126
	if(s == nil)
127
		longjmp(mkaux->jmp, 1);	/* memory leak */
128
	return s;
129
}
130
 
131
static void
132
domkfs(Mkaux *mkaux, File *me, int level)
133
{
134
	File *child;
135
	int rec;
136
 
137
	child = getfile(mkaux, me);
138
	if(!child)
139
		return;
140
	if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){
141
		rec = child->elem[0] == '+';
142
		free(child->new);
143
		child->new = estrdup(mkaux, me->new);
144
		setnames(mkaux, child);
145
		mktree(mkaux, child, rec);
146
		freefile(child);
147
		child = getfile(mkaux, me);
148
	}
149
	while(child && mkaux->indent > level){
150
		if(mkfile(mkaux, child))
151
			domkfs(mkaux, child, mkaux->indent);
152
		freefile(child);
153
		child = getfile(mkaux, me);
154
	}
155
	if(child){
156
		freefile(child);
157
		Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
158
		mkaux->lineno--;
159
	}
160
}
161
 
162
enum {
163
	SLOP = 30
164
};
165
 
166
static void
167
setname(Mkaux *mkaux, Name *name, char *s1, char *s2)
168
{
169
	int l;
170
 
171
	l = strlen(s1)+strlen(s2)+1;
172
	if(name->n < l) {
173
		free(name->s);
174
		name->s = emalloc(mkaux, l+SLOP);
175
		name->n = l+SLOP;
176
	}
177
	snprint(name->s, name->n, "%s%s%s", s1, s2[0] && s2[0]!='/' ? "/" : "", s2);
178
}
179
 
180
static void
181
mktree(Mkaux *mkaux, File *me, int rec)
182
{
183
	File child;
184
	Dir *d;
185
	int i, n, fd;
186
 
187
	setname(mkaux, &mkaux->fullname, mkaux->root, me->new);
188
	fd = open(mkaux->fullname.s, OREAD);
189
	if(fd < 0){
190
		warn(mkaux, "can't open %s: %r", mkaux->fullname.s);
191
		return;
192
	}
193
 
194
	child = *me;
195
	while((n = dirread(fd, &d)) > 0){
196
		for(i = 0; i < n; i++){
197
			child.new = mkpath(mkaux, me->new, d[i].name);
198
			if(me->old)
199
				child.old = mkpath(mkaux, me->old, d[i].name);
200
			child.elem = d[i].name;
201
			setnames(mkaux, &child);
202
			if(copyfile(mkaux, &child, &d[i], 1) && rec)
203
				mktree(mkaux, &child, rec);
204
			free(child.new);
205
			if(child.old)
206
				free(child.old);
207
		}
208
	}
209
	close(fd);
210
}
211
 
212
static int
213
mkfile(Mkaux *mkaux, File *f)
214
{
215
	Dir *d;
216
 
217
	setname(mkaux, &mkaux->fullname, mkaux->root, f->new);
218
	if((d = dirstat(mkaux->fullname.s)) == nil){
219
		warn(mkaux, "can't stat file %s: %r", mkaux->fullname.s);
220
		skipdir(mkaux);
221
		return 0;
222
	}
223
	return copyfile(mkaux, f, d, 0);
224
}
225
 
226
static int
227
copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly)
228
{
229
	Dir *nd;
230
	ulong xmode;
231
	char *p;
232
 
233
	/*
234
	 * Extra stat here is inefficient but accounts for binds.
235
	 */
236
	setname(mkaux, &mkaux->fullname, mkaux->root, f->new);
237
	if((nd = dirstat(mkaux->fullname.s)) != nil)
238
		d = nd;
239
 
240
	setname(mkaux, &mkaux->fullname, mkaux->xroot, f->old ? f->old : f->new);
241
	d->name = f->elem;
242
	if(d->type != 'M'){
243
		d->uid = "sys";
244
		d->gid = "sys";
245
		xmode = (d->mode >> 6) & 7;
246
		d->mode |= xmode | (xmode << 3);
247
	}
248
	if(strcmp(f->uid, "-") != 0)
249
		d->uid = f->uid;
250
	if(strcmp(f->gid, "-") != 0)
251
		d->gid = f->gid;
252
	if(f->mode != ~0){
253
		if(permonly)
254
			d->mode = (d->mode & ~0666) | (f->mode & 0666);
255
		else if((d->mode&DMDIR) != (f->mode&DMDIR))
256
			warn(mkaux, "inconsistent mode for %s", f->new);
257
		else
258
			d->mode = f->mode;
259
	}
260
 
261
	if(p = strrchr(f->new, '/'))
262
		d->name = p+1;
263
	else
264
		d->name = f->new;
265
 
266
	mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a);
267
	xmode = d->mode;
268
	free(nd);
269
	return (xmode&DMDIR) != 0;
270
}
271
 
272
static char *
273
mkpath(Mkaux *mkaux, char *prefix, char *elem)
274
{
275
	char *p;
276
	int n;
277
 
278
	n = strlen(prefix) + strlen(elem) + 2;
279
	p = emalloc(mkaux, n);
280
	strcpy(p, prefix);
281
	strcat(p, "/");
282
	strcat(p, elem);
283
	return p;
284
}
285
 
286
static void
287
setnames(Mkaux *mkaux, File *f)
288
{
289
 
290
	if(f->old){
291
		if(f->old[0] == '/')
292
			setname(mkaux, &mkaux->oldfile, f->old, "");
293
		else
294
			setname(mkaux, &mkaux->oldfile, mkaux->xroot, f->old);
295
	} else
296
		setname(mkaux, &mkaux->oldfile, mkaux->xroot, f->new);
297
}
298
 
299
static void
300
freefile(File *f)
301
{
302
	if(f->old)
303
		free(f->old);
304
	if(f->new)
305
		free(f->new);
306
	free(f);
307
}
308
 
309
/*
310
 * skip all files in the proto that
311
 * could be in the current dir
312
 */
313
static void
314
skipdir(Mkaux *mkaux)
315
{
316
	char *p, c;
317
	int level;
318
 
319
	if(mkaux->indent < 0)
320
		return;
321
	level = mkaux->indent;
322
	for(;;){
323
		mkaux->indent = 0;
324
		p = Brdline(mkaux->b, '\n');
325
		mkaux->lineno++;
326
		if(!p){
327
			mkaux->indent = -1;
328
			return;
329
		}
330
		while((c = *p++) != '\n')
331
			if(c == ' ')
332
				mkaux->indent++;
333
			else if(c == '\t')
334
				mkaux->indent += 8;
335
			else
336
				break;
337
		if(mkaux->indent <= level){
338
			Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
339
			mkaux->lineno--;
340
			return;
341
		}
342
	}
343
}
344
 
345
static File*
346
getfile(Mkaux *mkaux, File *old)
347
{
348
	File *f;
349
	char *elem;
350
	char *p;
351
	int c;
352
 
353
	if(mkaux->indent < 0)
354
		return 0;
355
loop:
356
	mkaux->indent = 0;
357
	p = Brdline(mkaux->b, '\n');
358
	mkaux->lineno++;
359
	if(!p){
360
		mkaux->indent = -1;
361
		return 0;
362
	}
363
	while((c = *p++) != '\n')
364
		if(c == ' ')
365
			mkaux->indent++;
366
		else if(c == '\t')
367
			mkaux->indent += 8;
368
		else
369
			break;
370
	if(c == '\n' || c == '#')
371
		goto loop;
372
	p--;
373
	f = emalloc(mkaux, sizeof *f);
374
	p = getname(mkaux, p, &elem);
375
	if(p == nil)
376
		return nil;
377
 
378
	f->new = mkpath(mkaux, old->new, elem);
379
	free(elem);
380
	f->elem = utfrrune(f->new, L'/') + 1;
381
	p = getmode(mkaux, p, &f->mode);
382
	p = getname(mkaux, p, &f->uid);	/* LEAK */
383
	if(p == nil)
384
		return nil;
385
 
386
	if(!*f->uid)
387
		f->uid = "-";		/* LEAK */
388
	p = getname(mkaux, p, &f->gid);	/* LEAK */
389
	if(p == nil)
390
		return nil;
391
 
392
	if(!*f->gid)
393
		f->gid = "-";		/* LEAK */
394
	f->old = getpath(mkaux, p);
395
	if(f->old && strcmp(f->old, "-") == 0){
396
		free(f->old);
397
		f->old = 0;
398
	}
399
	setnames(mkaux, f);
400
 
401
	return f;
402
}
403
 
404
static char*
405
getpath(Mkaux *mkaux, char *p)
406
{
407
	char *q, *new;
408
	int c, n;
409
 
410
	while((c = *p) == ' ' || c == '\t')
411
		p++;
412
	q = p;
413
	while((c = *q) != '\n' && c != ' ' && c != '\t')
414
		q++;
415
	if(q == p)
416
		return 0;
417
	n = q - p;
418
	new = emalloc(mkaux, n + 1);
419
	memcpy(new, p, n);
420
	new[n] = 0;
421
	return new;
422
}
423
 
424
static char*
425
getname(Mkaux *mkaux, char *p, char **buf)
426
{
427
	char *s, *start;
428
	int c;
429
 
430
	while((c = *p) == ' ' || c == '\t')
431
		p++;
432
 
433
	start = p;
434
	while((c = *p) != '\n' && c != ' ' && c != '\t')
435
		p++;
436
 
437
	*buf = malloc(p+1-start);
438
	if(*buf == nil)
439
		return nil;
440
	memmove(*buf, start, p-start);
441
 
442
	(*buf)[p-start] = '\0';
443
 
444
	if(**buf == '$'){
445
		s = getenv(*buf+1);
446
		if(s == 0){
447
			warn(mkaux, "can't read environment variable %s", *buf+1);
448
			skipdir(mkaux);
449
			free(*buf);
450
			return nil;
451
		}
452
		free(*buf);
453
		*buf = s;
454
	}
455
	return p;
456
}
457
 
458
static char*
459
getmode(Mkaux *mkaux, char *p, ulong *xmode)
460
{
461
	char *buf, *s;
462
	ulong m;
463
 
464
	*xmode = ~0;
465
	p = getname(mkaux, p, &buf);
466
	if(p == nil)
467
		return nil;
468
 
469
	s = buf;
470
	if(!*s || strcmp(s, "-") == 0)
471
		return p;
472
	m = 0;
473
	if(*s == 'd'){
474
		m |= DMDIR;
475
		s++;
476
	}
477
	if(*s == 'a'){
478
		m |= DMAPPEND;
479
		s++;
480
	}
481
	if(*s == 'l'){
482
		m |= DMEXCL;
483
		s++;
484
	}
485
	if(s[0] < '0' || s[0] > '7'
486
	|| s[1] < '0' || s[1] > '7'
487
	|| s[2] < '0' || s[2] > '7'
488
	|| s[3]){
489
		warn(mkaux, "bad mode specification %s", buf);
490
		free(buf);
491
		return p;
492
	}
493
	*xmode = m | strtoul(s, 0, 8);
494
	free(buf);
495
	return p;
496
}
497
 
498
static void
499
warn(Mkaux *mkaux, char *fmt, ...)
500
{
501
	char buf[256];
502
	va_list va;
503
 
504
	va_start(va, fmt);
505
	vseprint(buf, buf+sizeof(buf), fmt, va);
506
	va_end(va);
507
 
508
	if(mkaux->warn)
509
		mkaux->warn(buf, mkaux->a);
510
	else
511
		fprint(2, "warning: %s\n", buf);
512
}