| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include <libsec.h> |
| |
| #include "iso9660.h" |
| |
| /* |
| * Add the requisite path tables to the CD image. |
| * They get put on the end once everything else is done. |
| * We use the path table itself as a queue in the breadth-first |
| * traversal of the tree. |
| * |
| * The only problem with this is that the path table does not |
| * store the lengths of the directories. So we keep an explicit |
| * map in an array in memory. |
| */ |
| |
| enum { |
| Big, |
| Little |
| }; |
| |
| static void |
| Crdpath(Cdimg *cd, Cpath *p) |
| { |
| p->namelen = Cgetc(cd); |
| if(p->namelen == 0) { |
| Crseek(cd, (Croffset(cd)+Blocksize-1)/Blocksize * Blocksize); |
| p->namelen = Cgetc(cd); |
| assert(p->namelen != 0); |
| } |
| |
| p->xlen = Cgetc(cd); |
| assert(p->xlen == 0); /* sanity, might not be true if we start using the extended fields */ |
| |
| Cread(cd, p->dloc, 4); |
| Cread(cd, p->parent, 2); |
| p->name[0] = '\0'; |
| Crseek(cd, Croffset(cd)+p->namelen+p->xlen+(p->namelen&1)); /* skip name, ext data */ |
| } |
| |
| static void |
| writepath(Cdimg *cd, Cdir *c, int parent, int size) |
| { |
| /* |
| DO NOT UNCOMMENT THIS CODE. |
| This commented-out code is here only so that no one comes |
| along and adds it later. |
| |
| The ISO 9660 spec is silent about whether path table entries |
| need to be padded so that they never cross block boundaries. |
| It would be reasonable to assume that they are like every other |
| data structure in the bloody spec; this code pads them out. |
| |
| Empirically, though, they're NOT padded. Windows NT and |
| derivatives are the only known current operating systems |
| that actually read these things. |
| |
| int l; |
| |
| l = 1+1+4+2+c->namelen; |
| if(Cwoffset(cd)/Blocksize != (Cwoffset(cd)+l)/Blocksize) |
| Cpadblock(cd); |
| */ |
| Cputc(cd, c->namelen); |
| Cputc(cd, 0); |
| Cwrite(cd, c->dloc + (size==Little ? 0 : 4), 4); |
| (size==Little ? Cputnl : Cputnm)(cd, parent, 2); |
| Cwrite(cd, c->name, c->namelen); |
| if(c->namelen & 1) |
| Cputc(cd, 0); |
| } |
| |
| static ulong* |
| addlength(ulong *a, ulong x, int n) |
| { |
| if(n%128==0) |
| a = erealloc(a, (n+128)*sizeof a[0]); |
| a[n] = x; |
| return a; |
| } |
| |
| static ulong |
| writepathtable(Cdimg *cd, ulong vdblock, int size) |
| { |
| int rp, wp; |
| uchar buf[Blocksize]; |
| ulong bk, end, i, *len, n, rdoff, start; |
| Cdir *c; |
| Cpath p; |
| |
| Creadblock(cd, buf, vdblock, Blocksize); |
| c = (Cdir*)(buf+offsetof(Cvoldesc, rootdir[0])); |
| |
| rp = 0; |
| wp = 0; |
| len = nil; |
| start = cd->nextblock*Blocksize; |
| Cwseek(cd, start); |
| Crseek(cd, start); |
| writepath(cd, c, 1, size); |
| len = addlength(len, little(c->dlen, 4), wp); |
| wp++; |
| |
| while(rp < wp) { |
| Crdpath(cd, &p); |
| n = (len[rp]+Blocksize-1)/Blocksize; |
| rp++; |
| bk = (size==Big ? big : little)(p.dloc, 4); |
| rdoff = Croffset(cd); |
| for(i=0; i<n; i++) { |
| Creadblock(cd, buf, bk+i, Blocksize); |
| c = (Cdir*)buf; |
| if(i != 0 && c->namelen == 1 && c->name[0] == '\0') /* hit another directory; stop */ |
| break; |
| while(c->len && c->namelen && (uchar*)c+c->len < buf+Blocksize) { |
| if((c->flags & 0x02) && (c->namelen > 1 || c->name[0] > '\001')) { /* directory */ |
| writepath(cd, c, rp, size); |
| len = addlength(len, little(c->dlen, 4), wp); |
| wp++; |
| } |
| c = (Cdir*)((uchar*)c+c->len); |
| } |
| } |
| Crseek(cd, rdoff); |
| } |
| end = Cwoffset(cd); |
| Cpadblock(cd); |
| return end-start; |
| } |
| |
| |
| static void |
| writepathtablepair(Cdimg *cd, ulong vdblock) |
| { |
| ulong bloc, lloc, sz, sz2; |
| |
| lloc = cd->nextblock; |
| sz = writepathtable(cd, vdblock, Little); |
| bloc = cd->nextblock; |
| sz2 = writepathtable(cd, vdblock, Big); |
| assert(sz == sz2); |
| setpathtable(cd, vdblock, sz, lloc, bloc); |
| } |
| |
| void |
| writepathtables(Cdimg *cd) |
| { |
| cd->pathblock = cd->nextblock; |
| |
| writepathtablepair(cd, cd->iso9660pvd); |
| if(cd->flags & CDjoliet) |
| writepathtablepair(cd, cd->jolietsvd); |
| } |