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