xref: /plan9-contrib/sys/src/libdisk/proto.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
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(char*);
57 static int	mkfile(Mkaux*, File*);
58 static char*	mkpath(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 
71 int
72 rdproto(char *proto, char *root, Mkfsenum *mkenum, Mkfserr *mkerr, void *a)
73 {
74 	Mkaux mx, *m;
75 	File file;
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 	domkfs(m, &file, -1);
98 	free(m->oldfile.s);
99 	free(m->fullname.s);
100 	return 0;
101 }
102 
103 static void
104 domkfs(Mkaux *mkaux, File *me, int level)
105 {
106 	File *child;
107 	int rec;
108 
109 	child = getfile(mkaux, me);
110 	if(!child)
111 		return;
112 	if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){
113 		rec = child->elem[0] == '+';
114 		free(child->new);
115 		child->new = estrdup(me->new);
116 		setnames(mkaux, child);
117 		mktree(mkaux, child, rec);
118 		freefile(child);
119 		child = getfile(mkaux, me);
120 	}
121 	while(child && mkaux->indent > level){
122 		if(mkfile(mkaux, child))
123 			domkfs(mkaux, child, mkaux->indent);
124 		freefile(child);
125 		child = getfile(mkaux, me);
126 	}
127 	if(child){
128 		freefile(child);
129 		Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
130 		mkaux->lineno--;
131 	}
132 }
133 
134 static void
135 mktree(Mkaux *mkaux, File *me, int rec)
136 {
137 	File child;
138 	Dir d[HUNKS];
139 	int i, n, fd;
140 
141 	fd = open(mkaux->oldfile.s, OREAD);
142 	if(fd < 0){
143 		warn(mkaux, "can't open %s: %r", mkaux->oldfile.s);
144 		return;
145 	}
146 
147 	child = *me;
148 	while((n = dirread(fd, d, sizeof d)) > 0){
149 		n /= DIRLEN;
150 		for(i = 0; i < n; i++){
151 			child.new = mkpath(me->new, d[i].name);
152 			if(me->old)
153 				child.old = mkpath(me->old, d[i].name);
154 			child.elem = d[i].name;
155 			setnames(mkaux, &child);
156 			if(copyfile(mkaux, &child, &d[i], 1) && rec)
157 				mktree(mkaux, &child, rec);
158 			free(child.new);
159 			if(child.old)
160 				free(child.old);
161 		}
162 	}
163 	close(fd);
164 }
165 
166 static int
167 mkfile(Mkaux *mkaux, File *f)
168 {
169 	Dir dir;
170 
171 	if(dirstat(mkaux->oldfile.s, &dir) < 0){
172 		warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s);
173 		skipdir(mkaux);
174 		return 0;
175 	}
176 	return copyfile(mkaux, f, &dir, 0);
177 }
178 
179 enum {
180 	SLOP = 30
181 };
182 
183 static void
184 setname(Name *name, char *s1, char *s2)
185 {
186 	int l;
187 
188 	l = strlen(s1)+strlen(s2)+1;
189 	if(name->n < l) {
190 		free(name->s);
191 		name->s = emalloc(l+SLOP);
192 		name->n = l+SLOP;
193 	}
194 	snprint(name->s, name->n, "%s%s", s1, s2);
195 }
196 
197 static int
198 copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly)
199 {
200 	ulong xmode;
201 	char *p;
202 
203 	memmove(d->name, f->elem, NAMELEN);
204 	if(d->type != 'M'){
205 		strncpy(d->uid, "sys", NAMELEN);
206 		strncpy(d->gid, "sys", NAMELEN);
207 		xmode = (d->mode >> 6) & 7;
208 		d->mode |= xmode | (xmode << 3);
209 	}
210 	if(strcmp(f->uid, "-") != 0)
211 		strncpy(d->uid, f->uid, NAMELEN);
212 	if(strcmp(f->gid, "-") != 0)
213 		strncpy(d->gid, f->gid, NAMELEN);
214 	if(f->mode != ~0){
215 		if(permonly)
216 			d->mode = (d->mode & ~0666) | (f->mode & 0666);
217 		else if((d->mode&CHDIR) != (f->mode&CHDIR))
218 			warn(mkaux, "inconsistent mode for %s", f->new);
219 		else
220 			d->mode = f->mode;
221 	}
222 
223 	setname(&mkaux->fullname, mkaux->root, f->old ? f->old : f->new);
224 	if(p = strrchr(f->new, '/'))
225 		strcpy(d->name, p+1);
226 	else
227 		strcpy(d->name, f->new);
228 
229 	mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a);
230 	return (d->mode & CHDIR) != 0;
231 }
232 
233 static char *
234 mkpath(char *prefix, char *elem)
235 {
236 	char *p;
237 	int n;
238 
239 	n = strlen(prefix) + strlen(elem) + 2;
240 	p = emalloc(n);
241 	sprint(p, "%s/%s", prefix, elem);
242 	return p;
243 }
244 
245 static void
246 setnames(Mkaux *mkaux, File *f)
247 {
248 
249 	if(f->old){
250 		if(f->old[0] == '/')
251 			setname(&mkaux->oldfile, mkaux->root, f->old);
252 		else
253 			setname(&mkaux->oldfile, f->old, "");
254 	} else
255 		setname(&mkaux->oldfile, mkaux->root, f->new);
256 }
257 
258 static void
259 freefile(File *f)
260 {
261 	if(f->old)
262 		free(f->old);
263 	if(f->new)
264 		free(f->new);
265 	free(f);
266 }
267 
268 /*
269  * skip all files in the proto that
270  * could be in the current dir
271  */
272 static void
273 skipdir(Mkaux *mkaux)
274 {
275 	char *p, c;
276 	int level;
277 
278 	if(mkaux->indent < 0)
279 		return;
280 	level = mkaux->indent;
281 	for(;;){
282 		mkaux->indent = 0;
283 		p = Brdline(mkaux->b, '\n');
284 		mkaux->lineno++;
285 		if(!p){
286 			mkaux->indent = -1;
287 			return;
288 		}
289 		while((c = *p++) != '\n')
290 			if(c == ' ')
291 				mkaux->indent++;
292 			else if(c == '\t')
293 				mkaux->indent += 8;
294 			else
295 				break;
296 		if(mkaux->indent <= level){
297 			Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
298 			mkaux->lineno--;
299 			return;
300 		}
301 	}
302 }
303 
304 static File*
305 getfile(Mkaux *mkaux, File *old)
306 {
307 	File *f;
308 	char elem[NAMELEN];
309 	char *p;
310 	int c;
311 
312 	if(mkaux->indent < 0)
313 		return 0;
314 loop:
315 	mkaux->indent = 0;
316 	p = Brdline(mkaux->b, '\n');
317 	mkaux->lineno++;
318 	if(!p){
319 		mkaux->indent = -1;
320 		return 0;
321 	}
322 	while((c = *p++) != '\n')
323 		if(c == ' ')
324 			mkaux->indent++;
325 		else if(c == '\t')
326 			mkaux->indent += 8;
327 		else
328 			break;
329 	if(c == '\n' || c == '#')
330 		goto loop;
331 	p--;
332 	f = emalloc(sizeof *f);
333 	p = getname(mkaux, p, elem, sizeof elem);
334 	if(p == nil)
335 		return nil;
336 
337 	f->new = mkpath(old->new, elem);
338 	f->elem = utfrrune(f->new, L'/') + 1;
339 	p = getmode(mkaux, p, &f->mode);
340 	p = getname(mkaux, p, f->uid, sizeof f->uid);
341 	if(p == nil)
342 		return nil;
343 
344 	if(!*f->uid)
345 		strcpy(f->uid, "-");
346 	p = getname(mkaux, p, f->gid, sizeof f->gid);
347 	if(p == nil)
348 		return nil;
349 
350 	if(!*f->gid)
351 		strcpy(f->gid, "-");
352 	f->old = getpath(p);
353 	if(f->old && strcmp(f->old, "-") == 0){
354 		free(f->old);
355 		f->old = 0;
356 	}
357 	setnames(mkaux, f);
358 
359 	return f;
360 }
361 
362 static char*
363 getpath(char *p)
364 {
365 	char *q, *new;
366 	int c, n;
367 
368 	while((c = *p) == ' ' || c == '\t')
369 		p++;
370 	q = p;
371 	while((c = *q) != '\n' && c != ' ' && c != '\t')
372 		q++;
373 	if(q == p)
374 		return 0;
375 	n = q - p;
376 	new = emalloc(n + 1);
377 	memcpy(new, p, n);
378 	new[n] = 0;
379 	return new;
380 }
381 
382 static char*
383 getname(Mkaux *mkaux, char *p, char *buf, int len)
384 {
385 	char *s;
386 	int i, c;
387 
388 	while((c = *p) == ' ' || c == '\t')
389 		p++;
390 	i = 0;
391 	while((c = *p) != '\n' && c != ' ' && c != '\t'){
392 		if(i < len)
393 			buf[i++] = c;
394 		p++;
395 	}
396 	if(i == len){
397 		buf[len-1] = '\0';
398 		warn(mkaux, "name %s too long; truncated", buf);
399 	}else
400 		buf[i] = '\0';
401 
402 	if(buf[0] == '$'){
403 		s = getenv(buf+1);
404 		if(s == 0){
405 			warn(mkaux, "can't read environment variable %s", buf+1);
406 			skipdir(mkaux);
407 			return nil;
408 		}
409 		strncpy(buf, s, NAMELEN-1);
410 		buf[NAMELEN-1] = '\0';
411 		free(s);
412 	}
413 	return p;
414 }
415 
416 static char*
417 getmode(Mkaux *mkaux, char *p, ulong *xmode)
418 {
419 	char buf[7], *s;
420 	ulong m;
421 
422 	*xmode = ~0;
423 	p = getname(mkaux, p, buf, sizeof buf);
424 	s = buf;
425 	if(!*s || strcmp(s, "-") == 0)
426 		return p;
427 	m = 0;
428 	if(*s == 'd'){
429 		m |= CHDIR;
430 		s++;
431 	}
432 	if(*s == 'a'){
433 		m |= CHAPPEND;
434 		s++;
435 	}
436 	if(*s == 'l'){
437 		m |= CHEXCL;
438 		s++;
439 	}
440 	if(s[0] < '0' || s[0] > '7'
441 	|| s[1] < '0' || s[1] > '7'
442 	|| s[2] < '0' || s[2] > '7'
443 	|| s[3]){
444 		warn(mkaux, "bad mode specification %s", buf);
445 		return p;
446 	}
447 	*xmode = m | strtoul(s, 0, 8);
448 	return p;
449 }
450 
451 static void
452 warn(Mkaux *mkaux, char *fmt, ...)
453 {
454 	char buf[256];
455 	va_list va;
456 
457 	va_start(va, fmt);
458 	doprint(buf, buf+sizeof(buf), fmt, va);
459 	va_end(va);
460 
461 	if(mkaux->warn)
462 		mkaux->warn(buf, mkaux->a);
463 	else
464 		fprint(2, "warning: %s\n", buf);
465 }
466