692 lines · 11.7 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <bio.h>#include <auth.h>#include <fcall.h>#include <disk.h>#include <regexp.h> enum {	LEN	= 8*1024,	HUNKS	= 128,}; typedef struct File File;struct File{	char	*new;	char	*elem;	char	*old;	char	*uid;	char	*gid;	ulong	mode;}; typedef void Mkfserr(char*, void*);typedef void Mkfsenum(char*, char*, Dir*, void*); typedef struct Name Name;struct Name {	int n;	char *s;}; typedef struct Opt Opt;struct Opt {	int level;	long mode;	long mask;	Reprog *skip;	char *uid;	char *gid;	Opt *prev;}; typedef struct Mkaux Mkaux;struct Mkaux {	Mkfserr *warn;	Mkfsenum *mkenum;	char *root;	char *proto;	jmp_buf jmp;	Biobuf *b; 	Name oldfile;	Name fullname;	int	lineno;	int	indent; 	Opt *opt; 	void *a;}; static void domkfs(Mkaux *mkaux, File *me, int level); static int	copyfile(Mkaux*, File*, Dir*, int);static void	freefile(File*);static void	freeoptptr(Opt*, void*);static char*	getline(Mkaux*);static File*	getfile(Mkaux*, File*);static char*	getmode(Mkaux*, char*, ulong*);static char*	getname(Mkaux*, char*, char**);static char*	getpath(Mkaux*, char*);static int	mkfile(Mkaux*, File*);static char*	mkpath(Mkaux*, char*, char*);static void	mktree(Mkaux*, File*, int);static void	setname(Mkaux*, Name*, File*);static void	setopt(Mkaux*, char*, char*);static void	skipdir(Mkaux*);static void	warn(Mkaux*, char *, ...);static void	popopt(Mkaux *mkaux); intrdproto(char *proto, char *root, Mkfsenum *mkenum, Mkfserr *mkerr, void *a){	Mkaux mx, *m;	File file;	int rv; 	m = &mx;	memset(&mx, 0, sizeof mx);	if(root == nil)		root = "/"; 	m->root = root;	m->warn = mkerr;	m->mkenum = mkenum;	m->a = a;	m->proto = proto;	m->lineno = 0;	m->indent = 0;	m->opt = nil;	if((m->b = Bopen(proto, OREAD)) == nil) {		werrstr("open '%s': %r", proto);		return -1;	} 	memset(&file, 0, sizeof file);	file.new = "";	file.old = nil; 	rv = 0;	if(setjmp(m->jmp) == 0)		domkfs(m, &file, -1);	else		rv = -1;	free(m->oldfile.s);	free(m->fullname.s);	m->indent = -1;	popopt(m);	return rv;} static void*emalloc(Mkaux *mkaux, ulong n){	void *v; 	v = malloc(n);	if(v == nil)		longjmp(mkaux->jmp, 1);	/* memory leak */	memset(v, 0, n);	return v;} static char*estrdup(Mkaux *mkaux, char *s){	s = strdup(s);	if(s == nil)		longjmp(mkaux->jmp, 1);	/* memory leak */	return s;} static voiddomkfs(Mkaux *mkaux, File *me, int level){	File *child;	int rec; 	child = getfile(mkaux, me);	if(child == nil)		return;	if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){		rec = child->elem[0] == '+';		free(child->new);		child->new = estrdup(mkaux, me->new);		setname(mkaux, &mkaux->oldfile, child);		mktree(mkaux, child, rec);		freefile(child);		child = getfile(mkaux, me);	}	while(child != nil && mkaux->indent > level){		if(mkfile(mkaux, child))			domkfs(mkaux, child, mkaux->indent);		freefile(child);		child = getfile(mkaux, me);	}	if(child != nil){		freefile(child);		Bseek(mkaux->b, -Blinelen(mkaux->b), 1);		mkaux->lineno--;	}} static voidmktree(Mkaux *mkaux, File *me, int rec){	File child;	Dir *d;	int i, n, fd; 	fd = open(mkaux->oldfile.s, OREAD);	if(fd < 0){		warn(mkaux, "can't open %s: %r", mkaux->oldfile.s);		return;	}	child = *me;	while((n = dirread(fd, &d)) > 0){		for(i = 0; i < n; i++){			if(mkaux->opt && mkaux->opt->skip){				Resub m[8]; 				memset(m, 0, sizeof(m));				if(regexec(mkaux->opt->skip, d[i].name, m, nelem(m)))					continue;			}			child.new = mkpath(mkaux, me->new, d[i].name);			if(me->old != nil)				child.old = mkpath(mkaux, me->old, d[i].name);			child.elem = d[i].name;			setname(mkaux, &mkaux->oldfile, &child);			if((!(d[i].mode&DMDIR) || rec) && copyfile(mkaux, &child, &d[i], 1) && rec)				mktree(mkaux, &child, rec);			free(child.new);			free(child.old);		}		free(d);	}	close(fd);} static intmkfile(Mkaux *mkaux, File *f){	Dir *d; 	if((d = dirstat(mkaux->oldfile.s)) == nil){		warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s);		skipdir(mkaux);		return 0;	}	return copyfile(mkaux, f, d, 0);} enum {	SLOP = 30}; static voidsetname(Mkaux *mkaux, Name *name, File *f){	char *s1, *s2;	int n;		s1 = mkaux->root;	s2 = "";	if(f->old){		/* if old is not a absolute path, dont append root to it */		if(f->old[0] != '/')			s1 = f->old;		else			s2 = f->old;	}else		s2 = f->new;	n = strlen(s1) + strlen(s2) + 2;	if(name->n < n+SLOP/2) {		free(name->s);		name->s = emalloc(mkaux, n+SLOP);		name->n = n+SLOP;	}	snprint(name->s, name->n, "%s/%s", s1, s2);	cleanname(name->s);}  static intcopyfile(Mkaux *mkaux, File *f, Dir *d, int permonly){	Dir *nd;	Opt *o;	ulong xmode;	char *p; 	setname(mkaux, &mkaux->fullname, f); 	/*	 * Extra stat here is inefficient but accounts for binds.	 */	if((nd = dirstat(mkaux->fullname.s)) != nil)		d = nd; 	d->name = f->elem;	o = mkaux->opt;	if(strcmp(f->uid, "-") != 0)		d->uid = f->uid;	else if(o != nil && o->uid != nil)		d->uid = o->uid;	if(strcmp(f->gid, "-") != 0)		d->gid = f->gid;	else if(o != nil && o->gid != nil)		d->gid = o->gid;	if(f->mode != ~0){		if(permonly)			d->mode = (d->mode & ~0666) | (f->mode & 0666);		else if((d->mode&DMDIR) != (f->mode&DMDIR))			warn(mkaux, "inconsistent mode for %s", f->new);		else			d->mode = f->mode;	} else if(o != nil && o->mask)		d->mode = (d->mode & ~o->mask) | (o->mode & o->mask); 	if((p = strrchr(f->new, '/')) != nil)		d->name = p+1;	else		d->name = f->new;	mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a);	xmode = d->mode;	free(nd);	return (xmode&DMDIR) != 0;} static char *mkpath(Mkaux *mkaux, char *prefix, char *elem){	char *p;	int n; 	n = strlen(prefix) + strlen(elem) + 2;	p = emalloc(mkaux, n);	snprint(p, n, "%s/%s", prefix, elem);	return cleanname(p);} static intparsemode(char *spec, long *pmask, long *pmode){	char op, set, *s;	long mode;	long mask; 	s = spec;	op = set = 0;	mode = 0;	mask = DMAPPEND | DMEXCL | DMTMP; 	if(*s >= '0' && *s <= '7'){		mask = 0666;		mode = strtoul(s, 0, 8);		op = '=';		s = "!";	} 	for(; *s && op == 0; s++){		switch(*s){		case 'a':			mask |= 0666;			break;		case 'u':			mask |= 0600;			break;		case 'g':			mask |= 060;			break;		case 'o':			mask |= 06;			break;		case '-':		case '+':		case '=':			op = *s;			break;		default:			return 0;		}	}	if(s == spec)		mask |= 0666; 	for(; *s; s++){		switch(*s){		case 'r':			mode |= 0444;			break;		case 'w':			mode |= 0222;			break;		case 'x':			mode |= 0111;			break;		case 'a':			mode |= DMAPPEND;			break;		case 'l':			mode |= DMEXCL;			break;		case 't':			mode |= DMTMP;			break;		case '!':			set = 1;			break;		default:			return 0;		}	} 	if(op == '+' || op == '-')		mask &= mode;	if(op == '-')		mode = ~mode;	if(set)		*pmask = 0; 	*pmask |= mask;	*pmode = (*pmode & ~mask) | (mode & mask); 	return 1;} static voidsetopt(Mkaux *mkaux, char *key, char *val){	Opt *o; 	o = mkaux->opt;	if(o == nil || mkaux->indent > o->level){		o = emalloc(mkaux, sizeof(*o));		if(mkaux->opt != nil)			*o = *mkaux->opt;		o->level = mkaux->indent;		o->prev = mkaux->opt;		mkaux->opt = o;	} else if(mkaux->indent < o->level)		return;	if(strcmp(key, "skip") == 0){		freeoptptr(o, &o->skip);		if((o->skip = regcomp(val)) == nil)			warn(mkaux, "bad regular expression %s", val);	} else if(strcmp(key, "uid") == 0){		freeoptptr(o, &o->uid);		o->uid = *val ? estrdup(mkaux, val) : nil;	} else if(strcmp(key, "gid") == 0){		freeoptptr(o, &o->gid);		o->gid = *val ? estrdup(mkaux, val) : nil;	} else if(strcmp(key, "mode") == 0){		if(!parsemode(val, &o->mask, &o->mode))			warn(mkaux, "bad mode specification %s", val);	} else {		warn(mkaux, "bad option %s=%s", key, val);	}} static voidpopopt(Mkaux *mkaux){	Opt *o; 	while((o = mkaux->opt) != nil){		if(o->level <= mkaux->indent)			break;		mkaux->opt = o->prev;		freeoptptr(o, &o->skip);		freeoptptr(o, &o->uid);		freeoptptr(o, &o->gid);		free(o);	}} static voidfreeoptptr(Opt *o, void *p){	int x = (void**)p - (void**)o;	void *v = ((void**)o)[x];	if(v == nil)		return;	((void**)o)[x] = nil;	if((o = o->prev) != nil)		if(((void**)o)[x] == v)			return;	free(v);}  static voidfreefile(File *f){	free(f->old);	free(f->new);	free(f);} /* * skip all files in the proto that * could be in the current dir */static voidskipdir(Mkaux *mkaux){	int level; 	level = mkaux->indent;	while(getline(mkaux) != nil){		if(mkaux->indent <= level){			popopt(mkaux);			Bseek(mkaux->b, -Blinelen(mkaux->b), 1);			mkaux->lineno--;			return;		}	}} static char*getline(Mkaux *mkaux){	char *p;	int c; 	if(mkaux->indent < 0)		return nil;loop:

Lines 1–500 of 692.