| #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); |
| } |