Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include <fcall.h>
6 #include <disk.h>
8 enum {
9 LEN = 8*1024,
10 HUNKS = 128
11 };
13 #undef warn
14 #define warn protowarn
16 #undef getmode
17 #define getmode protogetmode
19 typedef struct File File;
20 struct File{
21 char *new;
22 char *elem;
23 char *old;
24 char *uid;
25 char *gid;
26 ulong mode;
27 };
29 typedef void Mkfserr(char*, void*);
30 typedef void Mkfsenum(char*, char*, Dir*, void*);
32 typedef struct Name Name;
33 struct Name {
34 int n;
35 char *s;
36 };
38 typedef struct Mkaux Mkaux;
39 struct Mkaux {
40 Mkfserr *warn;
41 Mkfsenum *mkenum;
42 char *root;
43 char *proto;
44 jmp_buf jmp;
45 Biobuf *b;
47 Name oldfile;
48 Name fullname;
49 int lineno;
50 int indent;
52 void *a;
53 };
55 static void domkfs(Mkaux *mkaux, File *me, int level);
57 static int copyfile(Mkaux*, File*, Dir*, int);
58 static void freefile(File*);
59 static File* getfile(Mkaux*, File*);
60 static char* getmode(Mkaux*, char*, ulong*);
61 static char* getname(Mkaux*, char*, char**);
62 static char* getpath(Mkaux*, char*);
63 static int mkfile(Mkaux*, File*);
64 static char* mkpath(Mkaux*, char*, char*);
65 static void mktree(Mkaux*, File*, int);
66 static void setnames(Mkaux*, File*);
67 static void skipdir(Mkaux*);
68 static void warn(Mkaux*, char *, ...);
70 /*static void */
71 /*mprint(char *new, char *old, Dir *d, void*) */
72 /*{ */
73 /* print("%s %s %D\n", new, old, d); */
74 /*} */
76 int
77 rdproto(char *proto, char *root, Mkfsenum *mkenum, Mkfserr *mkerr, void *a)
78 {
79 Mkaux mx, *m;
80 File file;
81 volatile int rv;
83 m = &mx;
84 memset(&mx, 0, sizeof mx);
85 if(root == nil)
86 root = "/";
88 m->root = root;
89 m->warn = mkerr;
90 m->mkenum = mkenum;
91 m->a = a;
92 m->proto = proto;
93 m->lineno = 0;
94 m->indent = 0;
95 if((m->b = Bopen(proto, OREAD)) == nil) {
96 werrstr("open '%s': %r", proto);
97 return -1;
98 }
100 memset(&file, 0, sizeof file);
101 file.new = "";
102 file.old = nil;
104 rv = 0;
105 if(setjmp(m->jmp) == 0)
106 domkfs(m, &file, -1);
107 else
108 rv = -1;
109 free(m->oldfile.s);
110 free(m->fullname.s);
111 return rv;
114 static void*
115 emalloc(Mkaux *mkaux, ulong n)
117 void *v;
119 v = malloc(n);
120 if(v == nil)
121 longjmp(mkaux->jmp, 1); /* memory leak */
122 memset(v, 0, n);
123 return v;
126 static char*
127 estrdup(Mkaux *mkaux, char *s)
129 s = strdup(s);
130 if(s == nil)
131 longjmp(mkaux->jmp, 1); /* memory leak */
132 return s;
135 static void
136 domkfs(Mkaux *mkaux, File *me, int level)
138 File *child;
139 int rec;
141 child = getfile(mkaux, me);
142 if(!child)
143 return;
144 if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){
145 rec = child->elem[0] == '+';
146 free(child->new);
147 child->new = estrdup(mkaux, me->new);
148 setnames(mkaux, child);
149 mktree(mkaux, child, rec);
150 freefile(child);
151 child = getfile(mkaux, me);
153 while(child && mkaux->indent > level){
154 if(mkfile(mkaux, child))
155 domkfs(mkaux, child, mkaux->indent);
156 freefile(child);
157 child = getfile(mkaux, me);
159 if(child){
160 freefile(child);
161 Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
162 mkaux->lineno--;
166 static void
167 mktree(Mkaux *mkaux, File *me, int rec)
169 File child;
170 Dir *d;
171 int i, n, fd;
173 fd = open(mkaux->oldfile.s, OREAD);
174 if(fd < 0){
175 warn(mkaux, "can't open %s: %r", mkaux->oldfile.s);
176 return;
179 child = *me;
180 while((n = dirread(fd, &d)) > 0){
181 for(i = 0; i < n; i++){
182 child.new = mkpath(mkaux, me->new, d[i].name);
183 if(me->old)
184 child.old = mkpath(mkaux, me->old, d[i].name);
185 child.elem = d[i].name;
186 setnames(mkaux, &child);
187 if((!(d[i].mode&DMDIR) || rec) && copyfile(mkaux, &child, &d[i], 1) && rec)
188 mktree(mkaux, &child, rec);
189 free(child.new);
190 if(child.old)
191 free(child.old);
194 close(fd);
197 static int
198 mkfile(Mkaux *mkaux, File *f)
200 Dir *d;
202 if((d = dirstat(mkaux->oldfile.s)) == nil){
203 warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s);
204 skipdir(mkaux);
205 return 0;
207 return copyfile(mkaux, f, d, 0);
210 enum {
211 SLOP = 30
212 };
214 static void
215 setname(Mkaux *mkaux, Name *name, char *s1, char *s2)
217 int l;
219 l = strlen(s1)+strlen(s2)+1;
220 if(name->n < l+SLOP/2) {
221 free(name->s);
222 name->s = emalloc(mkaux, l+SLOP);
223 name->n = l+SLOP;
225 snprint(name->s, name->n, "%s%s%s", s1, s1[0]==0 || s1[strlen(s1)-1]!='/' ? "/" : "", s2);
228 static int
229 copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly)
231 Dir *nd;
232 ulong xmode;
233 char *p;
235 setname(mkaux, &mkaux->fullname, mkaux->root, f->old ? f->old : f->new);
236 /*
237 * Extra stat here is inefficient but accounts for binds.
238 */
239 if((nd = dirstat(mkaux->fullname.s)) != nil)
240 d = nd;
242 d->name = f->elem;
243 if(d->type != 'M'){
244 d->uid = "sys";
245 d->gid = "sys";
246 xmode = (d->mode >> 6) & 7;
247 d->mode |= xmode | (xmode << 3);
249 if(strcmp(f->uid, "-") != 0)
250 d->uid = f->uid;
251 if(strcmp(f->gid, "-") != 0)
252 d->gid = f->gid;
253 if(f->mode != ~0){
254 if(permonly)
255 d->mode = (d->mode & ~0666) | (f->mode & 0666);
256 else if((d->mode&DMDIR) != (f->mode&DMDIR))
257 warn(mkaux, "inconsistent mode for %s", f->new);
258 else
259 d->mode = f->mode;
262 if(p = strrchr(f->new, '/'))
263 d->name = p+1;
264 else
265 d->name = f->new;
267 mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a);
268 xmode = d->mode;
269 free(nd);
270 return (xmode&DMDIR) != 0;
273 static char *
274 mkpath(Mkaux *mkaux, char *prefix, char *elem)
276 char *p;
277 int n;
279 n = strlen(prefix) + strlen(elem) + 2;
280 p = emalloc(mkaux, n);
281 strcpy(p, prefix);
282 strcat(p, "/");
283 strcat(p, elem);
284 return p;
287 static void
288 setnames(Mkaux *mkaux, File *f)
291 if(f->old){
292 if(f->old[0] == '/')
293 setname(mkaux, &mkaux->oldfile, f->old, "");
294 else
295 setname(mkaux, &mkaux->oldfile, mkaux->root, f->old);
296 } else
297 setname(mkaux, &mkaux->oldfile, mkaux->root, f->new);
300 static void
301 freefile(File *f)
303 if(f->old)
304 free(f->old);
305 if(f->new)
306 free(f->new);
307 free(f);
310 /*
311 * skip all files in the proto that
312 * could be in the current dir
313 */
314 static void
315 skipdir(Mkaux *mkaux)
317 char *p, c;
318 int level;
320 if(mkaux->indent < 0)
321 return;
322 level = mkaux->indent;
323 for(;;){
324 mkaux->indent = 0;
325 p = Brdline(mkaux->b, '\n');
326 mkaux->lineno++;
327 if(!p){
328 mkaux->indent = -1;
329 return;
331 while((c = *p++) != '\n')
332 if(c == ' ')
333 mkaux->indent++;
334 else if(c == '\t')
335 mkaux->indent += 8;
336 else
337 break;
338 if(mkaux->indent <= level){
339 Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
340 mkaux->lineno--;
341 return;
346 static File*
347 getfile(Mkaux *mkaux, File *old)
349 File *f;
350 char *elem;
351 char *p;
352 int c;
354 if(mkaux->indent < 0)
355 return 0;
356 loop:
357 mkaux->indent = 0;
358 p = Brdline(mkaux->b, '\n');
359 mkaux->lineno++;
360 if(!p){
361 mkaux->indent = -1;
362 return 0;
364 while((c = *p++) != '\n')
365 if(c == ' ')
366 mkaux->indent++;
367 else if(c == '\t')
368 mkaux->indent += 8;
369 else
370 break;
371 if(c == '\n' || c == '#')
372 goto loop;
373 p--;
374 f = emalloc(mkaux, sizeof *f);
375 p = getname(mkaux, p, &elem);
376 if(p == nil)
377 return nil;
379 f->new = mkpath(mkaux, old->new, elem);
380 free(elem);
381 f->elem = utfrrune(f->new, '/') + 1;
382 p = getmode(mkaux, p, &f->mode);
383 p = getname(mkaux, p, &f->uid); /* LEAK */
384 if(p == nil)
385 return nil;
387 if(!*f->uid)
388 strcpy(f->uid, "-");
389 p = getname(mkaux, p, &f->gid); /* LEAK */
390 if(p == nil)
391 return nil;
393 if(!*f->gid)
394 strcpy(f->gid, "-");
395 f->old = getpath(mkaux, p);
396 if(f->old && strcmp(f->old, "-") == 0){
397 free(f->old);
398 f->old = 0;
400 setnames(mkaux, f);
402 return f;
405 static char*
406 getpath(Mkaux *mkaux, char *p)
408 char *q, *new;
409 int c, n;
411 while((c = *p) == ' ' || c == '\t')
412 p++;
413 q = p;
414 while((c = *q) != '\n' && c != ' ' && c != '\t')
415 q++;
416 if(q == p)
417 return 0;
418 n = q - p;
419 new = emalloc(mkaux, n + 1);
420 memcpy(new, p, n);
421 new[n] = 0;
422 return new;
425 static char*
426 getname(Mkaux *mkaux, char *p, char **buf)
428 char *s, *start;
429 int c;
431 while((c = *p) == ' ' || c == '\t')
432 p++;
434 start = p;
435 while((c = *p) != '\n' && c != ' ' && c != '\t')
436 p++;
438 *buf = malloc(p+2-start); /* +2: need at least 2 bytes; might strcpy "-" into buf */
439 if(*buf == nil)
440 return nil;
441 memmove(*buf, start, p-start);
443 (*buf)[p-start] = '\0';
445 if(**buf == '$'){
446 s = getenv(*buf+1);
447 if(s == 0){
448 warn(mkaux, "can't read environment variable %s", *buf+1);
449 skipdir(mkaux);
450 free(*buf);
451 return nil;
453 free(*buf);
454 *buf = s;
456 return p;
459 static char*
460 getmode(Mkaux *mkaux, char *p, ulong *xmode)
462 char *buf, *s;
463 ulong m;
465 *xmode = ~0;
466 p = getname(mkaux, p, &buf);
467 if(p == nil)
468 return nil;
470 s = buf;
471 if(!*s || strcmp(s, "-") == 0)
472 return p;
473 m = 0;
474 if(*s == 'd'){
475 m |= DMDIR;
476 s++;
478 if(*s == 'a'){
479 m |= DMAPPEND;
480 s++;
482 if(*s == 'l'){
483 m |= DMEXCL;
484 s++;
486 if(s[0] < '0' || s[0] > '7'
487 || s[1] < '0' || s[1] > '7'
488 || s[2] < '0' || s[2] > '7'
489 || s[3]){
490 warn(mkaux, "bad mode specification %s", buf);
491 free(buf);
492 return p;
494 *xmode = m | strtoul(s, 0, 8);
495 free(buf);
496 return p;
499 static void
500 warn(Mkaux *mkaux, char *fmt, ...)
502 char buf[256];
503 va_list va;
505 va_start(va, fmt);
506 vseprint(buf, buf+sizeof(buf), fmt, va);
507 va_end(va);
509 if(mkaux->warn)
510 mkaux->warn(buf, mkaux->a);
511 else
512 fprint(2, "warning: %s\n", buf);