diff options
author | wkj <devnull@localhost> | 2004-06-17 01:47:21 +0000 |
---|---|---|
committer | wkj <devnull@localhost> | 2004-06-17 01:47:21 +0000 |
commit | 7285a491c1ce1e630a0751b1011fd33e6b17234b (patch) | |
tree | b2b2e24e333fa4660325a35f6c0f1d333e50e797 /src/cmd/9660/write.c | |
parent | e1dddc053287874e82e2b67f95ccee7d7bc63e22 (diff) | |
download | plan9port-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/write.c')
-rw-r--r-- | src/cmd/9660/write.c | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/src/cmd/9660/write.c b/src/cmd/9660/write.c new file mode 100644 index 00000000..94405317 --- /dev/null +++ b/src/cmd/9660/write.c @@ -0,0 +1,409 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +#include "iso9660.h" + +static void +writelittlebig4(uchar *buf, ulong x) +{ + buf[0] = buf[7] = x; + buf[1] = buf[6] = x>>8; + buf[2] = buf[5] = x>>16; + buf[3] = buf[4] = x>>24; +} + +void +rewritedot(Cdimg *cd, Direc *d) +{ + uchar buf[Blocksize]; + Cdir *c; + + Creadblock(cd, buf, d->block, Blocksize); + c = (Cdir*)buf; + assert(c->len != 0); + assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */ + writelittlebig4(c->dloc, d->block); + writelittlebig4(c->dlen, d->length); + + Cwseek(cd, d->block*Blocksize); + Cwrite(cd, buf, Blocksize); +} + +void +rewritedotdot(Cdimg *cd, Direc *d, Direc *dparent) +{ + uchar buf[Blocksize]; + Cdir *c; + + Creadblock(cd, buf, d->block, Blocksize); + c = (Cdir*)buf; + assert(c->len != 0); + assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */ + + c = (Cdir*)(buf+c->len); + assert(c->len != 0); + assert(c->namelen == 1 && c->name[0] == '\001'); /* dotdot*/ + + writelittlebig4(c->dloc, dparent->block); + writelittlebig4(c->dlen, dparent->length); + + Cwseek(cd, d->block*Blocksize); + Cwrite(cd, buf, Blocksize); +} + +/* + * Write each non-directory file. We copy the file to + * the cd image, and then if it turns out that we've + * seen this stream of bits before, we push the next block + * pointer back. This ensures consistency between the MD5s + * and the data on the CD image. MD5 summing on one pass + * and copying on another would not ensure this. + */ +void +writefiles(Dump *d, Cdimg *cd, Direc *direc) +{ + int i; + uchar buf[8192], digest[MD5dlen]; + ulong length, n, start; + Biobuf *b; + DigestState *s; + Dumpdir *dd; + + if(direc->mode & DMDIR) { + for(i=0; i<direc->nchild; i++) + writefiles(d, cd, &direc->child[i]); + return; + } + + assert(direc->block == 0); + + if((b = Bopen(direc->srcfile, OREAD)) == nil){ + fprint(2, "warning: cannot open '%s': %r\n", direc->srcfile); + direc->block = 0; + direc->length = 0; + return; + } + + start = cd->nextblock; + assert(start != 0); + + Cwseek(cd, start*Blocksize); + + s = md5(nil, 0, nil, nil); + length = 0; + while((n = Bread(b, buf, sizeof buf)) > 0) { + md5(buf, n, nil, s); + Cwrite(cd, buf, n); + length += n; + } + md5(nil, 0, digest, s); + Bterm(b); + Cpadblock(cd); + + if(length != direc->length) { + fprint(2, "warning: %s changed size underfoot\n", direc->srcfile); + direc->length = length; + } + + if(length == 0) + direc->block = 0; + else if((dd = lookupmd5(d, digest))) { + assert(dd->length == length); + assert(dd->block != 0); + direc->block = dd->block; + cd->nextblock = start; + } else { + direc->block = start; + if(chatty > 1) + fprint(2, "lookup %.16H %lud (%s) failed\n", digest, length, direc->name); + insertmd5(d, atom(direc->name), digest, start, length); + } +} + +/* + * Write a directory tree. We work from the leaves, + * and patch the dotdot pointers afterward. + */ +static void +_writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level) +{ + int i, l, ll; + ulong start, next; + + if((d->mode & DMDIR) == 0) + return; + + if(chatty) + fprint(2, "%*s%s\n", 4*level, "", d->name); + + for(i=0; i<d->nchild; i++) + _writedirs(cd, &d->child[i], put, level+1); + + l = 0; + l += put(cd, d, (level == 0) ? DTrootdot : DTdot, 0, l); + l += put(cd, nil, DTdotdot, 0, l); + for(i=0; i<d->nchild; i++) + l += put(cd, &d->child[i], DTiden, 0, l); + + start = cd->nextblock; + cd->nextblock += (l+Blocksize-1)/Blocksize; + next = cd->nextblock; + + Cwseek(cd, start*Blocksize); + ll = 0; + ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll); + ll += put(cd, nil, DTdotdot, 1, ll); + for(i=0; i<d->nchild; i++) + ll += put(cd, &d->child[i], DTiden, 1, ll); + assert(ll == l); + Cpadblock(cd); + assert(Cwoffset(cd) == next*Blocksize); + + d->block = start; + d->length = (next - start) * Blocksize; + rewritedot(cd, d); + rewritedotdot(cd, d, d); + + for(i=0; i<d->nchild; i++) + if(d->child[i].mode & DMDIR) + rewritedotdot(cd, &d->child[i], d); +} + +void +writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int)) +{ + /* + * If we're writing a mk9660 image, then the root really + * is the root, so start at level 0. If we're writing a dump image, + * then the "root" is really going to be two levels down once + * we patch in the dump hierarchy above it, so start at level non-zero. + */ + if(chatty) + fprint(2, ">>> writedirs\n"); + _writedirs(cd, d, put, mk9660 ? 0 : 1); +} + + +/* + * Write the dump tree. This is like writedirs but once we get to + * the roots of the individual days we just patch the parent dotdot blocks. + */ +static void +_writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level) +{ + int i; + ulong start; + + switch(level) { + case 0: + /* write root, list of years, also conform.map */ + for(i=0; i<d->nchild; i++) + if(d->child[i].mode & DMDIR) + _writedumpdirs(cd, &d->child[i], put, level+1); + chat("write dump root dir at %lud\n", cd->nextblock); + goto Writedir; + + case 1: /* write year, list of days */ + for(i=0; i<d->nchild; i++) + _writedumpdirs(cd, &d->child[i], put, level+1); + chat("write dump %s dir at %lud\n", d->name, cd->nextblock); + goto Writedir; + + Writedir: + start = cd->nextblock; + Cwseek(cd, start*Blocksize); + + put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd)); + put(cd, nil, DTdotdot, 1, Cwoffset(cd)); + for(i=0; i<d->nchild; i++) + put(cd, &d->child[i], DTiden, 1, Cwoffset(cd)); + Cpadblock(cd); + + d->block = start; + d->length = (cd->nextblock - start) * Blocksize; + + rewritedot(cd, d); + rewritedotdot(cd, d, d); + + for(i=0; i<d->nchild; i++) + if(d->child[i].mode & DMDIR) + rewritedotdot(cd, &d->child[i], d); + break; + + case 2: /* write day: already written, do nothing */ + break; + + default: + assert(0); + } +} + +void +writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int)) +{ + _writedumpdirs(cd, d, put, 0); +} + +static int +Cputplan9(Cdimg *cd, Direc *d, int dot, int dowrite) +{ + int l, n; + + if(dot != DTiden) + return 0; + + l = 0; + if(d->flags & Dbadname) { + n = strlen(d->name); + l += 1+n; + if(dowrite) { + Cputc(cd, n); + Cputs(cd, d->name, n); + } + } else { + l++; + if(dowrite) + Cputc(cd, 0); + } + + n = strlen(d->uid); + l += 1+n; + if(dowrite) { + Cputc(cd, n); + Cputs(cd, d->uid, n); + } + + n = strlen(d->gid); + l += 1+n; + if(dowrite) { + Cputc(cd, n); + Cputs(cd, d->gid, n); + } + + if(l & 1) { + l++; + if(dowrite) + Cputc(cd, 0); + } + l += 8; + if(dowrite) + Cputn(cd, d->mode, 4); + + return l; +} + +/* + * Write a directory entry. + */ +static int +genputdir(Cdimg *cd, Direc *d, int dot, int joliet, int dowrite, int offset) +{ + int f, n, l, lp; + long o; + + f = 0; + if(dot != DTiden || (d->mode & DMDIR)) + f |= 2; + + n = 1; + if(dot == DTiden) { + if(joliet) + n = 2*utflen(d->confname); + else + n = strlen(d->confname); + } + + l = 33+n; + if(l & 1) + l++; + assert(l <= 255); + + if(joliet == 0) { + if(cd->flags & CDplan9) + l += Cputplan9(cd, d, dot, 0); + else if(cd->flags & CDrockridge) + l += Cputsysuse(cd, d, dot, 0, l); + assert(l <= 255); + } + + if(dowrite == 0) { + if(Blocksize - offset%Blocksize < l) + l += Blocksize - offset%Blocksize; + return l; + } + + assert(offset%Blocksize == Cwoffset(cd)%Blocksize); + + o = Cwoffset(cd); + lp = 0; + if(Blocksize - Cwoffset(cd)%Blocksize < l) { + lp = Blocksize - Cwoffset(cd)%Blocksize; + Cpadblock(cd); + } + + Cputc(cd, l); /* length of directory record */ + Cputc(cd, 0); /* extended attribute record length */ + if(d) { + if((d->mode & DMDIR) == 0) + assert(d->length == 0 || d->block >= 18); + + Cputn(cd, d->block, 4); /* location of extent */ + Cputn(cd, d->length, 4); /* data length */ + } else { + Cputn(cd, 0, 4); + Cputn(cd, 0, 4); + } + Cputdate(cd, d ? d->mtime : now); /* recorded date */ + Cputc(cd, f); /* file flags */ + Cputc(cd, 0); /* file unit size */ + Cputc(cd, 0); /* interleave gap size */ + Cputn(cd, 1, 2); /* volume sequence number */ + Cputc(cd, n); /* length of file identifier */ + + if(dot == DTiden) { /* identifier */ + if(joliet) + Cputrscvt(cd, d->confname, n); + else + Cputs(cd, d->confname, n); + }else + if(dot == DTdotdot) + Cputc(cd, 1); + else + Cputc(cd, 0); + + if(Cwoffset(cd) & 1) /* pad */ + Cputc(cd, 0); + + if(joliet == 0) { + if(cd->flags & CDplan9) + Cputplan9(cd, d, dot, 1); + else if(cd->flags & CDrockridge) + Cputsysuse(cd, d, dot, 1, Cwoffset(cd)-(o+lp)); + } + + assert(o+lp+l == Cwoffset(cd)); + return lp+l; +} + +int +Cputisodir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset) +{ + return genputdir(cd, d, dot, 0, dowrite, offset); +} + +int +Cputjolietdir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset) +{ + return genputdir(cd, d, dot, 1, dowrite, offset); +} + +void +Cputendvd(Cdimg *cd) +{ + Cputc(cd, 255); /* volume descriptor set terminator */ + Cputs(cd, "CD001", 5); /* standard identifier */ + Cputc(cd, 1); /* volume descriptor version */ + Cpadblock(cd); +} |