xref: /plan9/sys/src/cmd/replica/revproto.c (revision e0d6d19cdffb15d5c5f1e7337cee05064ead1fd0)
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
revrdproto(char * proto,char * root,char * xroot,Mkfsenum * mkenum,Mkfserr * mkerr,void * a)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*
emalloc(Mkaux * mkaux,ulong n)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*
estrdup(Mkaux * mkaux,char * s)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
domkfs(Mkaux * mkaux,File * me,int level)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
setname(Mkaux * mkaux,Name * name,char * s1,char * s2)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
mktree(Mkaux * mkaux,File * me,int rec)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
mkfile(Mkaux * mkaux,File * f)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
copyfile(Mkaux * mkaux,File * f,Dir * d,int permonly)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 *
mkpath(Mkaux * mkaux,char * prefix,char * elem)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
setnames(Mkaux * mkaux,File * f)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
freefile(File * f)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
skipdir(Mkaux * mkaux)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*
getfile(Mkaux * mkaux,File * old)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*
getpath(Mkaux * mkaux,char * p)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*
getname(Mkaux * mkaux,char * p,char ** buf)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*
getmode(Mkaux * mkaux,char * p,ulong * xmode)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
warn(Mkaux * mkaux,char * fmt,...)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 }
513