#include #include #include #include #include #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; bblock + 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; bnchild); 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; inchild; 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; inchild; 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; inchild; 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; inchild; 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 * \ * * * 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) { uvlong x; Cwseek(cd, (vlong)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]; int nf; ulong cb, nc, db; uvlong m; 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, (vlong)cb * Blocksize); m = (vlong)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; }