URI:
       tproto.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tproto.c (8910B)
       ---
            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 #undef warn
           14 #define warn protowarn
           15 
           16 #undef getmode
           17 #define getmode protogetmode
           18 
           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 };
           28 
           29 typedef void Mkfserr(char*, void*);
           30 typedef void Mkfsenum(char*, char*, Dir*, void*);
           31 
           32 typedef struct Name Name;
           33 struct Name {
           34         int n;
           35         char *s;
           36 };
           37 
           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;
           46 
           47         Name oldfile;
           48         Name fullname;
           49         int        lineno;
           50         int        indent;
           51 
           52         void *a;
           53 };
           54 
           55 static void domkfs(Mkaux *mkaux, File *me, int level);
           56 
           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 *, ...);
           69 
           70 /*static void */
           71 /*mprint(char *new, char *old, Dir *d, void*) */
           72 /*{ */
           73 /*        print("%s %s %D\n", new, old, d); */
           74 /*} */
           75 
           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;
           82 
           83         m = &mx;
           84         memset(&mx, 0, sizeof mx);
           85         if(root == nil)
           86                 root = "/";
           87 
           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         }
           99 
          100         memset(&file, 0, sizeof file);
          101         file.new = "";
          102         file.old = nil;
          103 
          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;
          112 }
          113 
          114 static void*
          115 emalloc(Mkaux *mkaux, ulong n)
          116 {
          117         void *v;
          118 
          119         v = malloc(n);
          120         if(v == nil)
          121                 longjmp(mkaux->jmp, 1);        /* memory leak */
          122         memset(v, 0, n);
          123         return v;
          124 }
          125 
          126 static char*
          127 estrdup(Mkaux *mkaux, char *s)
          128 {
          129         s = strdup(s);
          130         if(s == nil)
          131                 longjmp(mkaux->jmp, 1);        /* memory leak */
          132         return s;
          133 }
          134 
          135 static void
          136 domkfs(Mkaux *mkaux, File *me, int level)
          137 {
          138         File *child;
          139         int rec;
          140 
          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);
          152         }
          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);
          158         }
          159         if(child){
          160                 freefile(child);
          161                 Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
          162                 mkaux->lineno--;
          163         }
          164 }
          165 
          166 static void
          167 mktree(Mkaux *mkaux, File *me, int rec)
          168 {
          169         File child;
          170         Dir *d;
          171         int i, n, fd;
          172 
          173         fd = open(mkaux->oldfile.s, OREAD);
          174         if(fd < 0){
          175                 warn(mkaux, "can't open %s: %r", mkaux->oldfile.s);
          176                 return;
          177         }
          178 
          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);
          192                 }
          193         }
          194         close(fd);
          195 }
          196 
          197 static int
          198 mkfile(Mkaux *mkaux, File *f)
          199 {
          200         Dir *d;
          201 
          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;
          206         }
          207         return copyfile(mkaux, f, d, 0);
          208 }
          209 
          210 enum {
          211         SLOP = 30
          212 };
          213 
          214 static void
          215 setname(Mkaux *mkaux, Name *name, char *s1, char *s2)
          216 {
          217         int l;
          218 
          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;
          224         }
          225         snprint(name->s, name->n, "%s%s%s", s1, s1[0]==0 || s1[strlen(s1)-1]!='/' ? "/" : "", s2);
          226 }
          227 
          228 static int
          229 copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly)
          230 {
          231         Dir *nd;
          232         ulong xmode;
          233         char *p;
          234 
          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;
          241 
          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);
          248         }
          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;
          260         }
          261 
          262         if(p = strrchr(f->new, '/'))
          263                 d->name = p+1;
          264         else
          265                 d->name = f->new;
          266 
          267         mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a);
          268         xmode = d->mode;
          269         free(nd);
          270         return (xmode&DMDIR) != 0;
          271 }
          272 
          273 static char *
          274 mkpath(Mkaux *mkaux, char *prefix, char *elem)
          275 {
          276         char *p;
          277         int n;
          278 
          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;
          285 }
          286 
          287 static void
          288 setnames(Mkaux *mkaux, File *f)
          289 {
          290 
          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);
          298 }
          299 
          300 static void
          301 freefile(File *f)
          302 {
          303         if(f->old)
          304                 free(f->old);
          305         if(f->new)
          306                 free(f->new);
          307         free(f);
          308 }
          309 
          310 /*
          311  * skip all files in the proto that
          312  * could be in the current dir
          313  */
          314 static void
          315 skipdir(Mkaux *mkaux)
          316 {
          317         char *p, c;
          318         int level;
          319 
          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;
          330                 }
          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;
          342                 }
          343         }
          344 }
          345 
          346 static File*
          347 getfile(Mkaux *mkaux, File *old)
          348 {
          349         File *f;
          350         char *elem;
          351         char *p;
          352         int c;
          353 
          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;
          363         }
          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;
          378 
          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;
          386 
          387         if(!*f->uid)
          388                 strcpy(f->uid, "-");
          389         p = getname(mkaux, p, &f->gid);        /* LEAK */
          390         if(p == nil)
          391                 return nil;
          392 
          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;
          399         }
          400         setnames(mkaux, f);
          401 
          402         return f;
          403 }
          404 
          405 static char*
          406 getpath(Mkaux *mkaux, char *p)
          407 {
          408         char *q, *new;
          409         int c, n;
          410 
          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;
          423 }
          424 
          425 static char*
          426 getname(Mkaux *mkaux, char *p, char **buf)
          427 {
          428         char *s, *start;
          429         int c;
          430 
          431         while((c = *p) == ' ' || c == '\t')
          432                 p++;
          433 
          434         start = p;
          435         while((c = *p) != '\n' && c != ' ' && c != '\t')
          436                 p++;
          437 
          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);
          442 
          443         (*buf)[p-start] = '\0';
          444 
          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;
          452                 }
          453                 free(*buf);
          454                 *buf = s;
          455         }
          456         return p;
          457 }
          458 
          459 static char*
          460 getmode(Mkaux *mkaux, char *p, ulong *xmode)
          461 {
          462         char *buf, *s;
          463         ulong m;
          464 
          465         *xmode = ~0;
          466         p = getname(mkaux, p, &buf);
          467         if(p == nil)
          468                 return nil;
          469 
          470         s = buf;
          471         if(!*s || strcmp(s, "-") == 0)
          472                 return p;
          473         m = 0;
          474         if(*s == 'd'){
          475                 m |= DMDIR;
          476                 s++;
          477         }
          478         if(*s == 'a'){
          479                 m |= DMAPPEND;
          480                 s++;
          481         }
          482         if(*s == 'l'){
          483                 m |= DMEXCL;
          484                 s++;
          485         }
          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;
          493         }
          494         *xmode = m | strtoul(s, 0, 8);
          495         free(buf);
          496         return p;
          497 }
          498 
          499 static void
          500 warn(Mkaux *mkaux, char *fmt, ...)
          501 {
          502         char buf[256];
          503         va_list va;
          504 
          505         va_start(va, fmt);
          506         vseprint(buf, buf+sizeof(buf), fmt, va);
          507         va_end(va);
          508 
          509         if(mkaux->warn)
          510                 mkaux->warn(buf, mkaux->a);
          511         else
          512                 fprint(2, "warning: %s\n", buf);
          513 }