aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/9660/dump.c
diff options
context:
space:
mode:
authorwkj <devnull@localhost>2004-06-17 01:47:21 +0000
committerwkj <devnull@localhost>2004-06-17 01:47:21 +0000
commit7285a491c1ce1e630a0751b1011fd33e6b17234b (patch)
treeb2b2e24e333fa4660325a35f6c0f1d333e50e797 /src/cmd/9660/dump.c
parente1dddc053287874e82e2b67f95ccee7d7bc63e22 (diff)
downloadplan9port-7285a491c1ce1e630a0751b1011fd33e6b17234b.tar.gz
plan9port-7285a491c1ce1e630a0751b1011fd33e6b17234b.tar.bz2
plan9port-7285a491c1ce1e630a0751b1011fd33e6b17234b.zip
Dump9660 (and mk9660). Until we either do something
intelligent with symlinks or put in a switch for things like dump9660, this is of rather limited utility under Unix.
Diffstat (limited to 'src/cmd/9660/dump.c')
-rw-r--r--src/cmd/9660/dump.c511
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;
+}