]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/replica/revproto.c
webfs(4): document -d and -D flags
[plan9front.git] / sys / src / cmd / replica / revproto.c
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
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*
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*
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
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
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
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
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
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 *
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
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
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
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*
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*
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*
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*
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
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 }