Subversion Repositories planix.SVN

Rev

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