xref: /plan9/sys/src/libdisk/proto.c (revision 59cc4ca53493a3c6d2349fe2b7f7c40f7dce7294)
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[NAMELEN];
19 	char	gid[NAMELEN];
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*, int);
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[HUNKS];
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, sizeof d)) > 0){
175 		n /= DIRLEN;
176 		for(i = 0; i < n; i++){
177 			child.new = mkpath(mkaux, me->new, d[i].name);
178 			if(me->old)
179 				child.old = mkpath(mkaux, me->old, d[i].name);
180 			child.elem = d[i].name;
181 			setnames(mkaux, &child);
182 			if(copyfile(mkaux, &child, &d[i], 1) && rec)
183 				mktree(mkaux, &child, rec);
184 			free(child.new);
185 			if(child.old)
186 				free(child.old);
187 		}
188 	}
189 	close(fd);
190 }
191 
192 static int
193 mkfile(Mkaux *mkaux, File *f)
194 {
195 	Dir dir;
196 
197 	if(dirstat(mkaux->oldfile.s, &dir) < 0){
198 		warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s);
199 		skipdir(mkaux);
200 		return 0;
201 	}
202 	return copyfile(mkaux, f, &dir, 0);
203 }
204 
205 enum {
206 	SLOP = 30
207 };
208 
209 static void
210 setname(Mkaux *mkaux, Name *name, char *s1, char *s2)
211 {
212 	int l;
213 
214 	l = strlen(s1)+strlen(s2)+1;
215 	if(name->n < l) {
216 		free(name->s);
217 		name->s = emalloc(mkaux, l+SLOP);
218 		name->n = l+SLOP;
219 	}
220 	snprint(name->s, name->n, "%s%s", s1, s2);
221 }
222 
223 static int
224 copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly)
225 {
226 	ulong xmode;
227 	char *p;
228 
229 	memmove(d->name, f->elem, NAMELEN);
230 	if(d->type != 'M'){
231 		strncpy(d->uid, "sys", NAMELEN);
232 		strncpy(d->gid, "sys", NAMELEN);
233 		xmode = (d->mode >> 6) & 7;
234 		d->mode |= xmode | (xmode << 3);
235 	}
236 	if(strcmp(f->uid, "-") != 0)
237 		strncpy(d->uid, f->uid, NAMELEN);
238 	if(strcmp(f->gid, "-") != 0)
239 		strncpy(d->gid, f->gid, NAMELEN);
240 	if(f->mode != ~0){
241 		if(permonly)
242 			d->mode = (d->mode & ~0666) | (f->mode & 0666);
243 		else if((d->mode&CHDIR) != (f->mode&CHDIR))
244 			warn(mkaux, "inconsistent mode for %s", f->new);
245 		else
246 			d->mode = f->mode;
247 	}
248 
249 	setname(mkaux, &mkaux->fullname, mkaux->root, f->old ? f->old : f->new);
250 	if(p = strrchr(f->new, '/'))
251 		strcpy(d->name, p+1);
252 	else
253 		strcpy(d->name, f->new);
254 
255 	mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a);
256 	return (d->mode & CHDIR) != 0;
257 }
258 
259 static char *
260 mkpath(Mkaux *mkaux, char *prefix, char *elem)
261 {
262 	char *p;
263 	int n;
264 
265 	n = strlen(prefix) + strlen(elem) + 2;
266 	p = emalloc(mkaux, n);
267 	sprint(p, "%s/%s", prefix, elem);
268 	return p;
269 }
270 
271 static void
272 setnames(Mkaux *mkaux, File *f)
273 {
274 
275 	if(f->old){
276 		if(f->old[0] == '/')
277 			setname(mkaux, &mkaux->oldfile, mkaux->root, f->old);
278 		else
279 			setname(mkaux, &mkaux->oldfile, f->old, "");
280 	} else
281 		setname(mkaux, &mkaux->oldfile, mkaux->root, f->new);
282 }
283 
284 static void
285 freefile(File *f)
286 {
287 	if(f->old)
288 		free(f->old);
289 	if(f->new)
290 		free(f->new);
291 	free(f);
292 }
293 
294 /*
295  * skip all files in the proto that
296  * could be in the current dir
297  */
298 static void
299 skipdir(Mkaux *mkaux)
300 {
301 	char *p, c;
302 	int level;
303 
304 	if(mkaux->indent < 0)
305 		return;
306 	level = mkaux->indent;
307 	for(;;){
308 		mkaux->indent = 0;
309 		p = Brdline(mkaux->b, '\n');
310 		mkaux->lineno++;
311 		if(!p){
312 			mkaux->indent = -1;
313 			return;
314 		}
315 		while((c = *p++) != '\n')
316 			if(c == ' ')
317 				mkaux->indent++;
318 			else if(c == '\t')
319 				mkaux->indent += 8;
320 			else
321 				break;
322 		if(mkaux->indent <= level){
323 			Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
324 			mkaux->lineno--;
325 			return;
326 		}
327 	}
328 }
329 
330 static File*
331 getfile(Mkaux *mkaux, File *old)
332 {
333 	File *f;
334 	char elem[NAMELEN];
335 	char *p;
336 	int c;
337 
338 	if(mkaux->indent < 0)
339 		return 0;
340 loop:
341 	mkaux->indent = 0;
342 	p = Brdline(mkaux->b, '\n');
343 	mkaux->lineno++;
344 	if(!p){
345 		mkaux->indent = -1;
346 		return 0;
347 	}
348 	while((c = *p++) != '\n')
349 		if(c == ' ')
350 			mkaux->indent++;
351 		else if(c == '\t')
352 			mkaux->indent += 8;
353 		else
354 			break;
355 	if(c == '\n' || c == '#')
356 		goto loop;
357 	p--;
358 	f = emalloc(mkaux, sizeof *f);
359 	p = getname(mkaux, p, elem, sizeof elem);
360 	if(p == nil)
361 		return nil;
362 
363 	f->new = mkpath(mkaux, old->new, elem);
364 	f->elem = utfrrune(f->new, L'/') + 1;
365 	p = getmode(mkaux, p, &f->mode);
366 	p = getname(mkaux, p, f->uid, sizeof f->uid);
367 	if(p == nil)
368 		return nil;
369 
370 	if(!*f->uid)
371 		strcpy(f->uid, "-");
372 	p = getname(mkaux, p, f->gid, sizeof f->gid);
373 	if(p == nil)
374 		return nil;
375 
376 	if(!*f->gid)
377 		strcpy(f->gid, "-");
378 	f->old = getpath(mkaux, p);
379 	if(f->old && strcmp(f->old, "-") == 0){
380 		free(f->old);
381 		f->old = 0;
382 	}
383 	setnames(mkaux, f);
384 
385 	return f;
386 }
387 
388 static char*
389 getpath(Mkaux *mkaux, char *p)
390 {
391 	char *q, *new;
392 	int c, n;
393 
394 	while((c = *p) == ' ' || c == '\t')
395 		p++;
396 	q = p;
397 	while((c = *q) != '\n' && c != ' ' && c != '\t')
398 		q++;
399 	if(q == p)
400 		return 0;
401 	n = q - p;
402 	new = emalloc(mkaux, n + 1);
403 	memcpy(new, p, n);
404 	new[n] = 0;
405 	return new;
406 }
407 
408 static char*
409 getname(Mkaux *mkaux, char *p, char *buf, int len)
410 {
411 	char *s;
412 	int i, c;
413 
414 	while((c = *p) == ' ' || c == '\t')
415 		p++;
416 	i = 0;
417 	while((c = *p) != '\n' && c != ' ' && c != '\t'){
418 		if(i < len)
419 			buf[i++] = c;
420 		p++;
421 	}
422 	if(i == len){
423 		buf[len-1] = '\0';
424 		warn(mkaux, "name %s too long; truncated", buf);
425 	}else
426 		buf[i] = '\0';
427 
428 	if(buf[0] == '$'){
429 		s = getenv(buf+1);
430 		if(s == 0){
431 			warn(mkaux, "can't read environment variable %s", buf+1);
432 			skipdir(mkaux);
433 			return nil;
434 		}
435 		strncpy(buf, s, NAMELEN-1);
436 		buf[NAMELEN-1] = '\0';
437 		free(s);
438 	}
439 	return p;
440 }
441 
442 static char*
443 getmode(Mkaux *mkaux, char *p, ulong *xmode)
444 {
445 	char buf[7], *s;
446 	ulong m;
447 
448 	*xmode = ~0;
449 	p = getname(mkaux, p, buf, sizeof buf);
450 	s = buf;
451 	if(!*s || strcmp(s, "-") == 0)
452 		return p;
453 	m = 0;
454 	if(*s == 'd'){
455 		m |= CHDIR;
456 		s++;
457 	}
458 	if(*s == 'a'){
459 		m |= CHAPPEND;
460 		s++;
461 	}
462 	if(*s == 'l'){
463 		m |= CHEXCL;
464 		s++;
465 	}
466 	if(s[0] < '0' || s[0] > '7'
467 	|| s[1] < '0' || s[1] > '7'
468 	|| s[2] < '0' || s[2] > '7'
469 	|| s[3]){
470 		warn(mkaux, "bad mode specification %s", buf);
471 		return p;
472 	}
473 	*xmode = m | strtoul(s, 0, 8);
474 	return p;
475 }
476 
477 static void
478 warn(Mkaux *mkaux, char *fmt, ...)
479 {
480 	char buf[256];
481 	va_list va;
482 
483 	va_start(va, fmt);
484 	doprint(buf, buf+sizeof(buf), fmt, va);
485 	va_end(va);
486 
487 	if(mkaux->warn)
488 		mkaux->warn(buf, mkaux->a);
489 	else
490 		fprint(2, "warning: %s\n", buf);
491 }
492