diff options
Diffstat (limited to 'src/cmd/9660/dump.c')
-rw-r--r-- | src/cmd/9660/dump.c | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/src/cmd/9660/dump.c b/src/cmd/9660/dump.c new file mode 100644 index 00000000..b77e9eb9 --- /dev/null +++ b/src/cmd/9660/dump.c @@ -0,0 +1,511 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> +#include <ctype.h> +#include "iso9660.h" + +static void +md5cd(Cdimg *cd, ulong block, ulong length, uchar *digest) +{ + int n; + uchar buf[Blocksize]; + DigestState *s; + + s = md5(nil, 0, nil, nil); + while(length > 0) { + n = length; + if(n > Blocksize) + n = Blocksize; + + Creadblock(cd, buf, block, n); + + md5(buf, n, nil, s); + + block++; + length -= n; + } + md5(nil, 0, digest, s); +} + +static Dumpdir* +mkdumpdir(char *name, uchar *md5, ulong block, ulong length) +{ + Dumpdir *d; + + assert(block != 0); + + d = emalloc(sizeof *d); + d->name = name; + memmove(d->md5, md5, sizeof d->md5); + d->block = block; + d->length = length; + + return d; +} + +static Dumpdir** +ltreewalkmd5(Dumpdir **l, uchar *md5) +{ + int i; + + while(*l) { + i = memcmp(md5, (*l)->md5, MD5dlen); + if(i < 0) + l = &(*l)->md5left; + else if(i == 0) + return l; + else + l = &(*l)->md5right; + } + return l; +} + +static Dumpdir** +ltreewalkblock(Dumpdir **l, ulong block) +{ + while(*l) { + if(block < (*l)->block) + l = &(*l)->blockleft; + else if(block == (*l)->block) + return l; + else + l = &(*l)->blockright; + } + return l; +} + +/* + * Add a particular file to our binary tree. + */ +static void +addfile(Cdimg *cd, Dump *d, char *name, Direc *dir) +{ + uchar md5[MD5dlen]; + Dumpdir **lblock; + + assert((dir->mode & DMDIR) == 0); + + if(dir->length == 0) + return; + + lblock = ltreewalkblock(&d->blockroot, dir->block); + if(*lblock != nil) { + if((*lblock)->length == dir->length) + return; + fprint(2, "block %lud length %lud %s %lud %s\n", dir->block, (*lblock)->length, (*lblock)->name, + dir->length, dir->name); + assert(0); + } + + md5cd(cd, dir->block, dir->length, md5); + if(chatty > 1) + fprint(2, "note file %.16H %lud (%s)\n", md5, dir->length, dir->name); + insertmd5(d, name, md5, dir->block, dir->length); +} + +void +insertmd5(Dump *d, char *name, uchar *md5, ulong block, ulong length) +{ + Dumpdir **lmd5; + Dumpdir **lblock; + + lblock = ltreewalkblock(&d->blockroot, block); + if(*lblock != nil) { + if((*lblock)->length == length) + return; + fprint(2, "block %lud length %lud %lud\n", block, (*lblock)->length, length); + assert(0); + } + + assert(length != 0); + *lblock = mkdumpdir(name, md5, block, length); + + lmd5 = ltreewalkmd5(&d->md5root, md5); + if(*lmd5 != nil) + fprint(2, "warning: data duplicated on CD\n"); + else + *lmd5 = *lblock; +} + +/* + * Fill all the children entries for a particular directory; + * all we care about is block, length, and whether it is a directory. + */ +void +readkids(Cdimg *cd, Direc *dir, char *(*cvt)(uchar*, int)) +{ + char *dot, *dotdot; + int m, n; + uchar buf[Blocksize], *ebuf, *p; + ulong b, nb; + Cdir *c; + Direc dx; + + assert(dir->mode & DMDIR); + + dot = atom("."); + dotdot = atom(".."); + ebuf = buf+Blocksize; + nb = (dir->length+Blocksize-1) / Blocksize; + + n = 0; + for(b=0; b<nb; b++) { + Creadblock(cd, buf, dir->block + b, Blocksize); + p = buf; + while(p < ebuf) { + c = (Cdir*)p; + if(c->len == 0) + break; + if(p+c->len > ebuf) + break; + if(parsedir(cd, &dx, p, ebuf-p, cvt) == 0 && dx.name != dot && dx.name != dotdot) + n++; + p += c->len; + } + } + + m = (n+Ndirblock-1)/Ndirblock * Ndirblock; + dir->child = emalloc(m*sizeof dir->child[0]); + dir->nchild = n; + + n = 0; + for(b=0; b<nb; b++) { + assert(n <= dir->nchild); + Creadblock(cd, buf, dir->block + b, Blocksize); + p = buf; + while(p < ebuf) { + c = (Cdir*)p; + if(c->len == 0) + break; + if(p+c->len > ebuf) + break; + if(parsedir(cd, &dx, p, ebuf-p, cvt) == 0 && dx.name != dot && dx.name != dotdot) { + assert(n < dir->nchild); + dir->child[n++] = dx; + } + p += c->len; + } + } +} + +/* + * Free the children. Make sure their children are free too. + */ +void +freekids(Direc *dir) +{ + int i; + + for(i=0; i<dir->nchild; i++) + assert(dir->child[i].nchild == 0); + + free(dir->child); + dir->child = nil; + dir->nchild = 0; +} + +/* + * Add a whole directory and all its children to our binary tree. + */ +static void +adddir(Cdimg *cd, Dump *d, Direc *dir) +{ + int i; + + readkids(cd, dir, isostring); + for(i=0; i<dir->nchild; i++) { + if(dir->child[i].mode & DMDIR) + adddir(cd, d, &dir->child[i]); + else + addfile(cd, d, atom(dir->name), &dir->child[i]); + } + freekids(dir); +} + +Dumpdir* +lookupmd5(Dump *d, uchar *md5) +{ + return *ltreewalkmd5(&d->md5root, md5); +} + +void +adddirx(Cdimg *cd, Dump *d, Direc *dir, int lev) +{ + int i; + Direc dd; + + if(lev == 2){ + dd = *dir; + adddir(cd, d, &dd); + return; + } + for(i=0; i<dir->nchild; i++) + adddirx(cd, d, &dir->child[i], lev+1); +} + +Dump* +dumpcd(Cdimg *cd, Direc *dir) +{ + Dump *d; + + d = emalloc(sizeof *d); + d->cd = cd; + adddirx(cd, d, dir, 0); + return d; +} + +/* +static ulong +minblock(Direc *root, int lev) +{ + int i; + ulong m, n; + + m = root->block; + for(i=0; i<root->nchild; i++) { + n = minblock(&root->child[i], lev-1); + if(m > n) + m = n; + } + return m; +} +*/ + +void +copybutname(Direc *d, Direc *s) +{ + Direc x; + + x = *d; + *d = *s; + d->name = x.name; + d->confname = x.confname; +} + +Direc* +createdumpdir(Direc *root, XDir *dir, char *utfname) +{ + char *p; + Direc *d; + + if(utfname[0]=='/') + sysfatal("bad dump name '%s'", utfname); + p = strchr(utfname, '/'); + if(p == nil || strchr(p+1, '/')) + sysfatal("bad dump name '%s'", utfname); + *p++ = '\0'; + if((d = walkdirec(root, utfname)) == nil) + d = adddirec(root, utfname, dir); + if(walkdirec(d, p)) + sysfatal("duplicate dump name '%s/%s'", utfname, p); + d = adddirec(d, p, dir); + return d; +} + +static void +rmdirec(Direc *d, Direc *kid) +{ + Direc *ekid; + + ekid = d->child+d->nchild; + assert(d->child <= kid && kid < ekid); + if(ekid != kid+1) + memmove(kid, kid+1, (ekid-(kid+1))*sizeof(*kid)); + d->nchild--; +} + +void +rmdumpdir(Direc *root, char *utfname) +{ + char *p; + Direc *d, *dd; + + if(utfname[0]=='/') + sysfatal("bad dump name '%s'", utfname); + p = strchr(utfname, '/'); + if(p == nil || strchr(p+1, '/')) + sysfatal("bad dump name '%s'", utfname); + *p++ = '\0'; + if((d = walkdirec(root, utfname)) == nil) + sysfatal("cannot remove %s/%s: %s does not exist", utfname, p, utfname); + p[-1] = '/'; + + if((dd = walkdirec(d, p)) == nil) + sysfatal("cannot remove %s: does not exist", utfname); + + rmdirec(d, dd); + if(d->nchild == 0) + rmdirec(root, d); +} + +char* +adddumpdir(Direc *root, ulong now, XDir *dir) +{ + char buf[40], *p; + int n; + Direc *dday, *dyear; + Tm tm; + + tm = *localtime(now); + + sprint(buf, "%d", tm.year+1900); + if((dyear = walkdirec(root, buf)) == nil) { + dyear = adddirec(root, buf, dir); + assert(dyear != nil); + } + + n = 0; + sprint(buf, "%.2d%.2d", tm.mon+1, tm.mday); + p = buf+strlen(buf); + while(walkdirec(dyear, buf)) + sprint(p, "%d", ++n); + + dday = adddirec(dyear, buf, dir); + assert(dday != nil); + + sprint(buf, "%s/%s", dyear->name, dday->name); +assert(walkdirec(root, buf)==dday); + return atom(buf); +} + +/* + * The dump directory tree is inferred from a linked list of special blocks. + * One block is written at the end of each dump. + * The blocks have the form + * + * plan 9 dump cd + * <dump-name> <dump-time> <next-block> <conform-block> <conform-length> \ + * <iroot-block> <iroot-length> <jroot-block> <jroot-length> + * + * If only the first line is present, this is the end of the chain. + */ +static char magic[] = "plan 9 dump cd\n"; +ulong +Cputdumpblock(Cdimg *cd) +{ + ulong x; + + Cwseek(cd, cd->nextblock*Blocksize); + x = Cwoffset(cd); + Cwrite(cd, magic, sizeof(magic)-1); + Cpadblock(cd); + return x/Blocksize; +} + +int +hasdump(Cdimg *cd) +{ + int i; + char buf[128]; + + for(i=16; i<24; i++) { + Creadblock(cd, buf, i, sizeof buf); + if(memcmp(buf, magic, sizeof(magic)-1) == 0) + return i; + } + return 0; +} + +Direc +readdumpdirs(Cdimg *cd, XDir *dir, char *(*cvt)(uchar*, int)) +{ + char buf[Blocksize]; + char *p, *q, *f[16]; + int i, nf; + ulong db, t; + Direc *nr, root; + XDir xd; + + mkdirec(&root, dir); + db = hasdump(cd); + xd = *dir; + for(;;){ + if(db == 0) + sysfatal("error in dump blocks"); + + Creadblock(cd, buf, db, sizeof buf); + if(memcmp(buf, magic, sizeof(magic)-1) != 0) + break; + p = buf+sizeof(magic)-1; + if(p[0] == '\0') + break; + if((q = strchr(p, '\n')) != nil) + *q = '\0'; + + nf = tokenize(p, f, nelem(f)); + i = 5; + if(nf < i || (cvt==jolietstring && nf < i+2)) + sysfatal("error in dump block %lud: nf=%d; p='%s'", db, nf, p); + nr = createdumpdir(&root, &xd, f[0]); + t = strtoul(f[1], 0, 0); + xd.mtime = xd.ctime = xd.atime = t; + db = strtoul(f[2], 0, 0); + if(cvt == jolietstring) + i += 2; + nr->block = strtoul(f[i], 0, 0); + nr->length = strtoul(f[i+1], 0, 0); + } + cd->nulldump = db; + return root; +} + +extern void addtx(char*, char*); + +static int +isalldigit(char *s) +{ + while(*s) + if(!isdigit(*s++)) + return 0; + return 1; +} + +void +readdumpconform(Cdimg *cd) +{ + char buf[Blocksize]; + char *p, *q, *f[10]; + ulong cb, nc, m, db; + int nf; + + db = hasdump(cd); + assert(map==nil || map->nt == 0); + + for(;;){ + if(db == 0) + sysfatal("error0 in dump blocks"); + + Creadblock(cd, buf, db, sizeof buf); + if(memcmp(buf, magic, sizeof(magic)-1) != 0) + break; + p = buf+sizeof(magic)-1; + if(p[0] == '\0') + break; + if((q = strchr(p, '\n')) != nil) + *q = '\0'; + + nf = tokenize(p, f, nelem(f)); + if(nf < 5) + sysfatal("error0 in dump block %lud", db); + + db = strtoul(f[2], 0, 0); + cb = strtoul(f[3], 0, 0); + nc = strtoul(f[4], 0, 0); + + Crseek(cd, cb*Blocksize); + m = cb*Blocksize+nc; + while(Croffset(cd) < m && (p = Crdline(cd, '\n')) != nil){ + p[Clinelen(cd)-1] = '\0'; + if(tokenize(p, f, 2) != 2 || (f[0][0] != 'D' && f[0][0] != 'F') + || strlen(f[0]) != 7 || !isalldigit(f[0]+1)) + break; + + addtx(atom(f[1]), atom(f[0])); + } + } + if(map) + cd->nconform = map->nt; + else + cd->nconform = 0; +} |