plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

proto.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 }