diff options
author | wkj <devnull@localhost> | 2004-06-17 01:46:29 +0000 |
---|---|---|
committer | wkj <devnull@localhost> | 2004-06-17 01:46:29 +0000 |
commit | e1dddc053287874e82e2b67f95ccee7d7bc63e22 (patch) | |
tree | 15e76d20c16f5fc500a18a84ad905eadf28ca3ec /src/libdisk/proto.c | |
parent | 778df25e996f8344a917fc5d3cf1b962ab728ada (diff) | |
download | plan9port-e1dddc053287874e82e2b67f95ccee7d7bc63e22.tar.gz plan9port-e1dddc053287874e82e2b67f95ccee7d7bc63e22.tar.bz2 plan9port-e1dddc053287874e82e2b67f95ccee7d7bc63e22.zip |
Import proto file parser for dump9660.
Diffstat (limited to 'src/libdisk/proto.c')
-rw-r--r-- | src/libdisk/proto.c | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/src/libdisk/proto.c b/src/libdisk/proto.c new file mode 100644 index 00000000..8c94ddca --- /dev/null +++ b/src/libdisk/proto.c @@ -0,0 +1,513 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <auth.h> +#include <fcall.h> +#include <disk.h> + +enum { + LEN = 8*1024, + HUNKS = 128, +}; + +#undef warn +#define warn protowarn + +#undef getmode +#define getmode protogetmode + +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 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; + + void *a; +}; + +static void domkfs(Mkaux *mkaux, File *me, int level); + +static int copyfile(Mkaux*, File*, Dir*, int); +static void freefile(File*); +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 setnames(Mkaux*, File*); +static void skipdir(Mkaux*); +static void warn(Mkaux*, char *, ...); + +//static void +//mprint(char *new, char *old, Dir *d, void*) +//{ +// print("%s %s %D\n", new, old, d); +//} + +int +rdproto(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; + 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); + 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 void +domkfs(Mkaux *mkaux, File *me, int level) +{ + File *child; + int rec; + + child = getfile(mkaux, me); + if(!child) + 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); + setnames(mkaux, child); + mktree(mkaux, child, rec); + freefile(child); + child = getfile(mkaux, me); + } + while(child && mkaux->indent > level){ + if(mkfile(mkaux, child)) + domkfs(mkaux, child, mkaux->indent); + freefile(child); + child = getfile(mkaux, me); + } + if(child){ + freefile(child); + Bseek(mkaux->b, -Blinelen(mkaux->b), 1); + mkaux->lineno--; + } +} + +static void +mktree(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++){ + child.new = mkpath(mkaux, me->new, d[i].name); + if(me->old) + child.old = mkpath(mkaux, me->old, d[i].name); + child.elem = d[i].name; + setnames(mkaux, &child); + if((!(d[i].mode&DMDIR) || rec) && copyfile(mkaux, &child, &d[i], 1) && rec) + mktree(mkaux, &child, rec); + free(child.new); + if(child.old) + free(child.old); + } + } + close(fd); +} + +static int +mkfile(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 void +setname(Mkaux *mkaux, Name *name, char *s1, char *s2) +{ + int l; + + l = strlen(s1)+strlen(s2)+1; + if(name->n < l+SLOP/2) { + free(name->s); + name->s = emalloc(mkaux, l+SLOP); + name->n = l+SLOP; + } + snprint(name->s, name->n, "%s%s%s", s1, s1[0]==0 || s1[strlen(s1)-1]!='/' ? "/" : "", s2); +} + +static int +copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly) +{ + Dir *nd; + ulong xmode; + char *p; + + setname(mkaux, &mkaux->fullname, mkaux->root, f->old ? f->old : f->new); + /* + * Extra stat here is inefficient but accounts for binds. + */ + if((nd = dirstat(mkaux->fullname.s)) != nil) + d = nd; + + d->name = f->elem; + if(d->type != 'M'){ + d->uid = "sys"; + d->gid = "sys"; + xmode = (d->mode >> 6) & 7; + d->mode |= xmode | (xmode << 3); + } + if(strcmp(f->uid, "-") != 0) + d->uid = f->uid; + if(strcmp(f->gid, "-") != 0) + d->gid = f->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; + } + + if(p = strrchr(f->new, '/')) + 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); + strcpy(p, prefix); + strcat(p, "/"); + strcat(p, elem); + return p; +} + +static void +setnames(Mkaux *mkaux, File *f) +{ + + if(f->old){ + if(f->old[0] == '/') + setname(mkaux, &mkaux->oldfile, f->old, ""); + else + setname(mkaux, &mkaux->oldfile, mkaux->root, f->old); + } else + setname(mkaux, &mkaux->oldfile, mkaux->root, f->new); +} + +static void +freefile(File *f) +{ + if(f->old) + free(f->old); + if(f->new) + free(f->new); + free(f); +} + +/* + * skip all files in the proto that + * could be in the current dir + */ +static void +skipdir(Mkaux *mkaux) +{ + char *p, c; + int level; + + if(mkaux->indent < 0) + return; + level = mkaux->indent; + for(;;){ + mkaux->indent = 0; + p = Brdline(mkaux->b, '\n'); + mkaux->lineno++; + if(!p){ + mkaux->indent = -1; + return; + } + while((c = *p++) != '\n') + if(c == ' ') + mkaux->indent++; + else if(c == '\t') + mkaux->indent += 8; + else + break; + if(mkaux->indent <= level){ + Bseek(mkaux->b, -Blinelen(mkaux->b), 1); + mkaux->lineno--; + return; + } + } +} + +static File* +getfile(Mkaux *mkaux, File *old) +{ + File *f; + char *elem; + char *p; + int c; + + if(mkaux->indent < 0) + return 0; +loop: + mkaux->indent = 0; + p = Brdline(mkaux->b, '\n'); + mkaux->lineno++; + if(!p){ + mkaux->indent = -1; + return 0; + } + while((c = *p++) != '\n') + if(c == ' ') + mkaux->indent++; + else if(c == '\t') + mkaux->indent += 8; + else + break; + if(c == '\n' || c == '#') + goto loop; + p--; + f = emalloc(mkaux, sizeof *f); + p = getname(mkaux, p, &elem); + if(p == nil) + return nil; + + f->new = mkpath(mkaux, old->new, elem); + free(elem); + f->elem = utfrrune(f->new, L'/') + 1; + p = getmode(mkaux, p, &f->mode); + p = getname(mkaux, p, &f->uid); /* LEAK */ + if(p == nil) + return nil; + + if(!*f->uid) + strcpy(f->uid, "-"); + p = getname(mkaux, p, &f->gid); /* LEAK */ + if(p == nil) + return nil; + + if(!*f->gid) + strcpy(f->gid, "-"); + f->old = getpath(mkaux, p); + if(f->old && strcmp(f->old, "-") == 0){ + free(f->old); + f->old = 0; + } + setnames(mkaux, f); + + return f; +} + +static char* +getpath(Mkaux *mkaux, char *p) +{ + char *q, *new; + int c, n; + + while((c = *p) == ' ' || c == '\t') + p++; + q = p; + while((c = *q) != '\n' && c != ' ' && c != '\t') + q++; + if(q == p) + return 0; + n = q - p; + new = emalloc(mkaux, n + 1); + memcpy(new, p, n); + new[n] = 0; + return new; +} + +static char* +getname(Mkaux *mkaux, char *p, char **buf) +{ + char *s, *start; + int c; + + while((c = *p) == ' ' || c == '\t') + p++; + + start = p; + while((c = *p) != '\n' && c != ' ' && c != '\t') + p++; + + *buf = malloc(p+2-start); /* +2: need at least 2 bytes; might strcpy "-" into buf */ + if(*buf == nil) + return nil; + memmove(*buf, start, p-start); + + (*buf)[p-start] = '\0'; + + if(**buf == '$'){ + s = getenv(*buf+1); + if(s == 0){ + warn(mkaux, "can't read environment variable %s", *buf+1); + skipdir(mkaux); + free(*buf); + return nil; + } + free(*buf); + *buf = s; + } + return p; +} + +static char* +getmode(Mkaux *mkaux, char *p, ulong *xmode) +{ + char *buf, *s; + ulong m; + + *xmode = ~0; + p = getname(mkaux, p, &buf); + if(p == nil) + return nil; + + s = buf; + if(!*s || strcmp(s, "-") == 0) + return p; + m = 0; + if(*s == 'd'){ + m |= DMDIR; + s++; + } + if(*s == 'a'){ + m |= DMAPPEND; + s++; + } + if(*s == 'l'){ + m |= DMEXCL; + s++; + } + if(s[0] < '0' || s[0] > '7' + || s[1] < '0' || s[1] > '7' + || s[2] < '0' || s[2] > '7' + || s[3]){ + warn(mkaux, "bad mode specification %s", buf); + free(buf); + return p; + } + *xmode = m | strtoul(s, 0, 8); + free(buf); + return p; +} + +static void +warn(Mkaux *mkaux, char *fmt, ...) +{ + char buf[256]; + va_list va; + + va_start(va, fmt); + vseprint(buf, buf+sizeof(buf), fmt, va); + va_end(va); + + if(mkaux->warn) + mkaux->warn(buf, mkaux->a); + else + fprint(2, "warning: %s\n", buf); +} |