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