From 7285a491c1ce1e630a0751b1011fd33e6b17234b Mon Sep 17 00:00:00 2001 From: wkj Date: Thu, 17 Jun 2004 01:47:21 +0000 Subject: 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. --- src/cmd/9660/boot.c | 189 +++++++++++++++ src/cmd/9660/cdrdwr.c | 632 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/9660/conform.c | 141 +++++++++++ src/cmd/9660/direc.c | 222 +++++++++++++++++ src/cmd/9660/dump.c | 511 +++++++++++++++++++++++++++++++++++++++ src/cmd/9660/dump9660.c | 402 ++++++++++++++++++++++++++++++ src/cmd/9660/ichar.c | 206 ++++++++++++++++ src/cmd/9660/iso9660.h | 424 ++++++++++++++++++++++++++++++++ src/cmd/9660/jchar.c | 138 +++++++++++ src/cmd/9660/mk9660.rc | 5 + src/cmd/9660/mk9660.sh | 5 + src/cmd/9660/mkfile | 34 +++ src/cmd/9660/path.c | 155 ++++++++++++ src/cmd/9660/plan9.c | 27 +++ src/cmd/9660/rune.c | 39 +++ src/cmd/9660/sysuse.c | 613 ++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/9660/uid.c | 41 ++++ src/cmd/9660/unix.c | 84 +++++++ src/cmd/9660/util.c | 98 ++++++++ src/cmd/9660/write.c | 409 +++++++++++++++++++++++++++++++ 20 files changed, 4375 insertions(+) create mode 100644 src/cmd/9660/boot.c create mode 100644 src/cmd/9660/cdrdwr.c create mode 100644 src/cmd/9660/conform.c create mode 100644 src/cmd/9660/direc.c create mode 100644 src/cmd/9660/dump.c create mode 100644 src/cmd/9660/dump9660.c create mode 100644 src/cmd/9660/ichar.c create mode 100644 src/cmd/9660/iso9660.h create mode 100644 src/cmd/9660/jchar.c create mode 100755 src/cmd/9660/mk9660.rc create mode 100755 src/cmd/9660/mk9660.sh create mode 100644 src/cmd/9660/mkfile create mode 100644 src/cmd/9660/path.c create mode 100644 src/cmd/9660/plan9.c create mode 100644 src/cmd/9660/rune.c create mode 100644 src/cmd/9660/sysuse.c create mode 100644 src/cmd/9660/uid.c create mode 100644 src/cmd/9660/unix.c create mode 100644 src/cmd/9660/util.c create mode 100644 src/cmd/9660/write.c (limited to 'src/cmd/9660') diff --git a/src/cmd/9660/boot.c b/src/cmd/9660/boot.c new file mode 100644 index 00000000..9cae9582 --- /dev/null +++ b/src/cmd/9660/boot.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include + +#include "iso9660.h" + +/* FreeBSD 4.5 installation CD for reference +g% cdsector 17 | xd -b +1+0 records in +1+0 records out +0000000 00 - volume descriptor type (0) + 43 44 30 30 31 - "CD001" + 01 - volume descriptor version (1) + 45 4c 20 54 4f 52 49 54 4f +0000010 20 53 50 45 43 49 46 49 43 41 54 49 4f 4e 00 00 +0000020 00 00 00 00 00 00 00 - 7-39 boot system identifier +"EL TORITO SPECIFICATION" + + 00 00 00 00 00 00 00 00 00 +0000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000040 00 00 00 00 00 00 00 - 39-71 boot identifier + +boot system use: + +absolute pointer to the boot catalog?? + + 4d 0c 00 00 00 00 00 00 00 +0000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000590 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00005a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +g% cdsector 3149 | xd -b # 0x0c4d +1+0 records in +1+0 records out +0000000 01 - header (1) + 00 - platform id (0 = 0x86) + 00 00 - reserved 0 + 00 00 00 00 00 00 00 00 00 00 00 00 +0000010 00 00 00 00 00 00 00 00 00 00 00 00 - id string + aa 55 - checksum + 55 aa - magic + +0000020 88 - 88 = bootable + 03 - 3 = 2.88MB diskette + 00 00 - load segment 0 means default 0x7C0 + 00 - system type (byte 5 of boot image) + 00 - unused (0) + 01 00 - 512-byte sector count for initial load + 4e 0c 00 00 - ptr to virtual disk + 00 00 00 00 +0000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +g% cdsector `{h2d 0c4e} | xd -b +1+0 records in +1+0 records out +0000000 eb 3c 00 00 00 00 00 00 00 00 00 00 02 00 00 00 +0000010 00 00 00 00 00 00 00 00 12 00 02 00 00 00 00 00 +0000020 00 00 00 00 00 16 1f 66 6a 00 51 50 06 53 + 31 c0 + +FREEBSD +0000000 eb 3c 00 00 00 00 00 00 00 00 00 00 02 00 00 00 +0000010 00 00 00 00 00 00 00 00 12 00 02 00 00 00 00 00 +0000020 00 00 00 00 00 16 1f 66 6a 00 51 50 06 53 + 31 c0 + +DOS 5 +0000000 eb 3c 90 4d 53 44 4f 53 35 2e 30 00 02 01 01 00 +0000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00 +0000020 00 00 00 00 00 00 29 6a 2c e0 16 4e 4f 20 4e 41 +0000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 fa 33 +0000040 c0 8e d0 bc 00 7c 16 07 bb 78 00 36 c5 37 1e 56 +0000050 16 53 bf 3e 7c b9 0b 00 fc f3 a4 06 1f c6 45 fe +0000060 0f 8b 0e 18 7c 88 4d f9 89 47 02 c7 07 3e 7c fb +0000070 cd 13 72 79 33 c0 39 06 13 7c 74 08 8b 0e 13 7c +0000080 89 0e 20 7c a0 10 7c f7 26 16 7c 03 06 1c 7c 13 +0000090 16 1e 7c 03 06 0e 7c 83 d2 00 a3 50 7c 89 16 52 +00000a0 7c a3 49 7c 89 16 4b 7c b8 20 00 f7 26 11 7c 8b + +NDISK +0000000 eb 3c 90 50 6c 61 6e 39 2e 30 30 00 02 01 01 00 +0000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00 +0000020 40 0b 00 00 00 00 29 13 00 00 00 43 59 4c 49 4e +0000030 44 52 49 43 41 4c 46 41 54 31 32 20 20 20 fa 31 +0000040 c0 8e d0 8e d8 8e c0 bc ec 7b 89 e5 88 56 12 fb +0000050 be ea 7d bf 90 7d ff d7 bf 82 7d ff d7 8b 06 27 +0000060 7c 8b 16 29 7c bb 00 7e bf 2c 7d ff d7 bb 10 00 + +PBSDISK +0000000 eb 3c 90 50 6c 61 6e 39 2e 30 30 00 02 01 01 00 +0000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00 +0000020 40 0b 00 00 00 00 29 13 00 00 00 43 59 4c 49 4e +0000030 44 52 49 43 41 4c 46 41 54 31 32 20 20 20 fa 31 +0000040 c0 8e d0 8e d8 8e c0 bc f8 7b 89 e5 88 56 00 fb +0000050 be f8 7d bf 00 7d ff d7 bf df 7c ff d7 8b 06 27 +0000060 7c 8b 16 29 7c bb 00 7e bf 89 7c ff d7 bf fb 7c +*/ + +void +Cputbootvol(Cdimg *cd) +{ + Cputc(cd, 0x00); + Cputs(cd, "CD001", 5); + Cputc(cd, 0x01); + Cputs(cd, "EL TORITO SPECIFICATION", 2+1+6+1+13); + Crepeat(cd, 0, 2+16+16+7); + cd->bootcatptr = Cwoffset(cd); + Cpadblock(cd); +} + +void +Cupdatebootvol(Cdimg *cd) +{ + ulong o; + + o = Cwoffset(cd); + Cwseek(cd, cd->bootcatptr); + Cputnl(cd, cd->bootcatblock, 4); + Cwseek(cd, o); +} + +void +Cputbootcat(Cdimg *cd) +{ + cd->bootcatblock = Cwoffset(cd) / Blocksize; + Cputc(cd, 0x01); + Cputc(cd, 0x00); + Cputc(cd, 0x00); + Cputc(cd, 0x00); + Crepeat(cd, 0, 12+12); + + /* + * either the checksum doesn't include the header word + * or it just doesn't matter. + */ + Cputc(cd, 0xAA); + Cputc(cd, 0x55); + Cputc(cd, 0x55); + Cputc(cd, 0xAA); + + cd->bootimageptr = Cwoffset(cd); + Cpadblock(cd); +} + +void +Cupdatebootcat(Cdimg *cd) +{ + ulong o; + + if(cd->bootdirec == nil) + return; + + o = Cwoffset(cd); + Cwseek(cd, cd->bootimageptr); + Cputc(cd, 0x88); + switch(cd->bootdirec->length){ + default: + fprint(2, "warning: boot image is not 1.44MB or 2.88MB; pretending 1.44MB\n"); + case 1440*1024: + Cputc(cd, 0x02); /* 1.44MB disk */ + break; + case 2880*1024: + Cputc(cd, 0x03); /* 2.88MB disk */ + break; + } + Cputnl(cd, 0, 2); /* load segment */ + Cputc(cd, 0); /* system type */ + Cputc(cd, 0); /* unused */ + Cputnl(cd, 1, 2); /* 512-byte sector count for load */ + Cputnl(cd, cd->bootdirec->block, 4); /* ptr to disk image */ + Cwseek(cd, o); +} + +void +findbootimage(Cdimg *cd, Direc *root) +{ + Direc *d; + + d = walkdirec(root, cd->bootimage); + if(d == nil){ + fprint(2, "warning: did not encounter boot image\n"); + return; + } + + cd->bootdirec = d; +} diff --git a/src/cmd/9660/cdrdwr.c b/src/cmd/9660/cdrdwr.c new file mode 100644 index 00000000..492e0289 --- /dev/null +++ b/src/cmd/9660/cdrdwr.c @@ -0,0 +1,632 @@ +#include +#include +#include +#include + +#include "iso9660.h" + +static int readisodesc(Cdimg*, Voldesc*); +static int readjolietdesc(Cdimg*, Voldesc*); + +/* + * It's not strictly conforming; instead it's enough to + * get us up and running; presumably the real CD writing + * will take care of being conforming. + * + * Things not conforming include: + * - no path table + * - root directories are of length zero + */ +Cdimg* +createcd(char *file, Cdinfo info) +{ + int fd, xfd; + Cdimg *cd; + + if(access(file, AEXIST) == 0){ + werrstr("file already exists"); + return nil; + } + + if((fd = create(file, ORDWR, 0666)) < 0) + return nil; + + cd = emalloc(sizeof *cd); + cd->file = atom(file); + + Binit(&cd->brd, fd, OREAD); + + if((xfd = open(file, ORDWR)) < 0) + sysfatal("can't open file again: %r"); + Binit(&cd->bwr, xfd, OWRITE); + + Crepeat(cd, 0, 16*Blocksize); + Cputisopvd(cd, info); + if(info.flags & CDbootable){ + cd->bootimage = info.bootimage; + cd->flags |= CDbootable; + Cputbootvol(cd); + } + + if(readisodesc(cd, &cd->iso) < 0) + assert(0); + if(info.flags & CDplan9) + cd->flags |= CDplan9; + else if(info.flags & CDrockridge) + cd->flags |= CDrockridge; + if(info.flags & CDjoliet) { + Cputjolietsvd(cd, info); + if(readjolietdesc(cd, &cd->joliet) < 0) + assert(0); + cd->flags |= CDjoliet; + } + Cputendvd(cd); + + if(info.flags & CDdump){ + cd->nulldump = Cputdumpblock(cd); + cd->flags |= CDdump; + } + if(cd->flags & CDbootable){ + Cputbootcat(cd); + Cupdatebootvol(cd); + } + + if(info.flags & CDconform) + cd->flags |= CDconform; + + cd->flags |= CDnew; + cd->nextblock = Cwoffset(cd) / Blocksize; + assert(cd->nextblock != 0); + + return cd; +} + +Cdimg* +opencd(char *file, Cdinfo info) +{ + int fd, xfd; + Cdimg *cd; + Dir *d; + + if((fd = open(file, ORDWR)) < 0) { + if(access(file, AEXIST) == 0) + return nil; + return createcd(file, info); + } + + if((d = dirfstat(fd)) == nil) { + close(fd); + return nil; + } + if(d->length == 0 || d->length % Blocksize) { + werrstr("bad length %lld", d->length); + close(fd); + free(d); + return nil; + } + + cd = emalloc(sizeof *cd); + cd->file = atom(file); + cd->nextblock = d->length / Blocksize; + assert(cd->nextblock != 0); + free(d); + + Binit(&cd->brd, fd, OREAD); + + if((xfd = open(file, ORDWR)) < 0) + sysfatal("can't open file again: %r"); + Binit(&cd->bwr, xfd, OWRITE); + + if(readisodesc(cd, &cd->iso) < 0) { + free(cd); + close(fd); + close(xfd); + return nil; + } + + /* lowercase because of isostring */ + if(strstr(cd->iso.systemid, "iso9660") == nil + && strstr(cd->iso.systemid, "utf8") == nil) { + werrstr("unknown systemid %s", cd->iso.systemid); + free(cd); + close(fd); + close(xfd); + return nil; + } + + if(strstr(cd->iso.systemid, "plan 9")) + cd->flags |= CDplan9; + if(strstr(cd->iso.systemid, "iso9660")) + cd->flags |= CDconform; + if(strstr(cd->iso.systemid, "rrip")) + cd->flags |= CDrockridge; + if(strstr(cd->iso.systemid, "boot")) + cd->flags |= CDbootable; + if(readjolietdesc(cd, &cd->joliet) == 0) + cd->flags |= CDjoliet; + if(hasdump(cd)) + cd->flags |= CDdump; + + return cd; +} + +ulong +big(void *a, int n) +{ + uchar *p; + ulong v; + int i; + + p = a; + v = 0; + for(i=0; ibwr); + if(Bseek(&cd->brd, block*Blocksize, 0) != block*Blocksize) + sysfatal("error seeking to block %lud", block); + if(Bread(&cd->brd, buf, len) != len) + sysfatal("error reading %lud bytes at block %lud: %r %lld", len, block, Bseek(&cd->brd, 0, 2)); +} + +int +parsedir(Cdimg *cd, Direc *d, uchar *buf, int len, char *(*cvtname)(uchar*, int)) +{ + enum { NAMELEN = 28 }; + char name[NAMELEN]; + uchar *p; + Cdir *c; + + memset(d, 0, sizeof *d); + + c = (Cdir*)buf; + + if(c->len > len) { + werrstr("buffer too small"); + return -1; + } + + if(c->namelen == 1 && c->name[0] == '\0') + d->name = atom("."); + else if(c->namelen == 1 && c->name[0] == '\001') + d->name = atom(".."); + else if(cvtname) + d->name = cvtname(c->name, c->namelen); + + d->block = little(c->dloc, 4); + d->length = little(c->dlen, 4); + + if(c->flags & 2) + d->mode |= DMDIR; + +/*BUG: do we really need to parse the plan 9 fields? */ + /* plan 9 use fields */ + if((cd->flags & CDplan9) && cvtname == isostring + && (c->namelen != 1 || c->name[0] > 1)) { + p = buf+33+c->namelen; + if((p-buf)&1) + p++; + assert(p < buf+c->len); + assert(*p < NAMELEN); + if(*p != 0) { + memmove(name, p+1, *p); + name[*p] = '\0'; + d->confname = d->name; + d->name = atom(name); + } + p += *p+1; + assert(*p < NAMELEN); + memmove(name, p+1, *p); + name[*p] = '\0'; + d->uid = atom(name); + p += *p+1; + assert(*p < NAMELEN); + memmove(name, p+1, *p); + name[*p] = '\0'; + d->gid = atom(name); + p += *p+1; + if((p-buf)&1) + p++; + d->mode = little(p, 4); + } + + // BUG: rock ridge extensions + return 0; +} + +void +setroot(Cdimg *cd, ulong block, ulong dloc, ulong dlen) +{ + assert(block != 0); + + Cwseek(cd, block*Blocksize+offsetof(Cvoldesc, rootdir[0])+offsetof(Cdir, dloc[0])); + Cputn(cd, dloc, 4); + Cputn(cd, dlen, 4); +} + +void +setvolsize(Cdimg *cd, ulong block, ulong size) +{ + assert(block != 0); + + Cwseek(cd, block*Blocksize+offsetof(Cvoldesc, volsize[0])); + Cputn(cd, size, 4); +} + +void +setpathtable(Cdimg *cd, ulong block, ulong sz, ulong lloc, ulong bloc) +{ + assert(block != 0); + + Cwseek(cd, block*Blocksize+offsetof(Cvoldesc, pathsize[0])); + Cputn(cd, sz, 4); + Cputnl(cd, lloc, 4); + Cputnl(cd, 0, 4); + Cputnm(cd, bloc, 4); + Cputnm(cd, 0, 4); + assert(Cwoffset(cd) == block*Blocksize+offsetof(Cvoldesc, rootdir[0])); +} + + +static void +parsedesc(Voldesc *v, Cvoldesc *cv, char *(*string)(uchar*, int)) +{ + v->systemid = string(cv->systemid, sizeof cv->systemid); + + v->pathsize = little(cv->pathsize, 4); + v->lpathloc = little(cv->lpathloc, 4); + v->mpathloc = little(cv->mpathloc, 4); + + v->volumeset = string(cv->volumeset, sizeof cv->volumeset); + v->publisher = string(cv->publisher, sizeof cv->publisher); + v->preparer = string(cv->preparer, sizeof cv->preparer); + v->application = string(cv->application, sizeof cv->application); + + v->abstract = string(cv->abstract, sizeof cv->abstract); + v->biblio = string(cv->biblio, sizeof cv->biblio); + v->notice = string(cv->notice, sizeof cv->notice); +} + +static int +readisodesc(Cdimg *cd, Voldesc *v) +{ + static uchar magic[] = { 0x01, 'C', 'D', '0', '0', '1', 0x01, 0x00 }; + Cvoldesc cv; + + memset(v, 0, sizeof *v); + + Creadblock(cd, &cv, 16, sizeof cv); + if(memcmp(cv.magic, magic, sizeof magic) != 0) { + werrstr("bad pvd magic"); + return -1; + } + + if(little(cv.blocksize, 2) != Blocksize) { + werrstr("block size not %d", Blocksize); + return -1; + } + + cd->iso9660pvd = 16; + parsedesc(v, &cv, isostring); + + return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, isostring); +} + +static int +readjolietdesc(Cdimg *cd, Voldesc *v) +{ + int i; + static uchar magic[] = { 0x02, 'C', 'D', '0', '0', '1', 0x01, 0x00 }; + Cvoldesc cv; + + memset(v, 0, sizeof *v); + + for(i=16; i<24; i++) { + Creadblock(cd, &cv, i, sizeof cv); + if(memcmp(cv.magic, magic, sizeof magic) != 0) + continue; + if(cv.charset[0] != 0x25 || cv.charset[1] != 0x2F + || (cv.charset[2] != 0x40 && cv.charset[2] != 0x43 && cv.charset[2] != 0x45)) + continue; + break; + } + + if(i==24) { + werrstr("could not find Joliet SVD"); + return -1; + } + + if(little(cv.blocksize, 2) != Blocksize) { + werrstr("block size not %d", Blocksize); + return -1; + } + + cd->jolietsvd = i; + parsedesc(v, &cv, jolietstring); + + return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, jolietstring); +} + +/* + * CD image buffering routines. + */ +void +Cputc(Cdimg *cd, int c) +{ + assert(Boffset(&cd->bwr) >= 16*Blocksize || c == 0); + +if(Boffset(&cd->bwr) == 0x9962) +if(c >= 256) abort(); + if(Bputc(&cd->bwr, c) < 0) + sysfatal("Bputc: %r"); + Bflush(&cd->brd); +} + +void +Cputnl(Cdimg *cd, ulong val, int size) +{ + switch(size) { + default: + sysfatal("bad size %d in bputnl", size); + case 2: + Cputc(cd, val); + Cputc(cd, val>>8); + break; + case 4: + Cputc(cd, val); + Cputc(cd, val>>8); + Cputc(cd, val>>16); + Cputc(cd, val>>24); + break; + } +} + +void +Cputnm(Cdimg *cd, ulong val, int size) +{ + switch(size) { + default: + sysfatal("bad size %d in bputnl", size); + case 2: + Cputc(cd, val>>8); + Cputc(cd, val); + break; + case 4: + Cputc(cd, val>>24); + Cputc(cd, val>>16); + Cputc(cd, val>>8); + Cputc(cd, val); + break; + } +} + +void +Cputn(Cdimg *cd, long val, int size) +{ + Cputnl(cd, val, size); + Cputnm(cd, val, size); +} + +/* + * ASCII/UTF string writing + */ +void +Crepeat(Cdimg *cd, int c, int n) +{ + while(n-- > 0) + Cputc(cd, c); +} + +void +Cputs(Cdimg *cd, char *s, int size) +{ + int n; + + if(s == nil) { + Crepeat(cd, ' ', size); + return; + } + + for(n=0; nbwr) >= 16*Blocksize); + + if(Bwrite(&cd->bwr, buf, n) != n) + sysfatal("Bwrite: %r"); + Bflush(&cd->brd); +} + +void +Cputr(Cdimg *cd, Rune r) +{ + Cputc(cd, r>>8); + Cputc(cd, r); +} + +void +Crepeatr(Cdimg *cd, Rune r, int n) +{ + int i; + + for(i=0; ibwr) % Blocksize); + if(n != Blocksize) + Crepeat(cd, 0, n); + + nb = Boffset(&cd->bwr)/Blocksize; + assert(nb != 0); + if(nb > cd->nextblock) + cd->nextblock = nb; +} + +void +Cputdate(Cdimg *cd, ulong ust) +{ + Tm *tm; + + if(ust == 0) { + Crepeat(cd, 0, 7); + return; + } + tm = gmtime(ust); + Cputc(cd, tm->year); + Cputc(cd, tm->mon+1); + Cputc(cd, tm->mday); + Cputc(cd, tm->hour); + Cputc(cd, tm->min); + Cputc(cd, tm->sec); + Cputc(cd, 0); +} + +void +Cputdate1(Cdimg *cd, ulong ust) +{ + Tm *tm; + char str[20]; + + if(ust == 0) { + Crepeat(cd, '0', 16); + Cputc(cd, 0); + return; + } + tm = gmtime(ust); + sprint(str, "%.4d%.2d%.2d%.2d%.2d%.4d", + tm->year+1900, + tm->mon+1, + tm->mday, + tm->hour, + tm->min, + tm->sec*100); + Cputs(cd, str, 16); + Cputc(cd, 0); +} + +void +Cwseek(Cdimg *cd, ulong offset) +{ + Bseek(&cd->bwr, offset, 0); +} + +ulong +Cwoffset(Cdimg *cd) +{ + return Boffset(&cd->bwr); +} + +void +Cwflush(Cdimg *cd) +{ + Bflush(&cd->bwr); +} + +ulong +Croffset(Cdimg *cd) +{ + return Boffset(&cd->brd); +} + +void +Crseek(Cdimg *cd, ulong offset) +{ + Bseek(&cd->brd, offset, 0); +} + +int +Cgetc(Cdimg *cd) +{ + int c; + + Cwflush(cd); + if((c = Bgetc(&cd->brd)) == Beof) { + fprint(2, "getc at %lud\n", Croffset(cd)); + assert(0); + //sysfatal("Bgetc: %r"); + } + return c; +} + +void +Cread(Cdimg *cd, void *buf, int n) +{ + Cwflush(cd); + if(Bread(&cd->brd, buf, n) != n) + sysfatal("Bread: %r"); +} + +char* +Crdline(Cdimg *cd, int c) +{ + Cwflush(cd); + return Brdline(&cd->brd, c); +} + +int +Clinelen(Cdimg *cd) +{ + return Blinelen(&cd->brd); +} + diff --git a/src/cmd/9660/conform.c b/src/cmd/9660/conform.c new file mode 100644 index 00000000..530b4d56 --- /dev/null +++ b/src/cmd/9660/conform.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include "iso9660.h" + +/* + * We keep an array sorted by bad atom pointer. + * The theory is that since we don't free memory very often, + * the array will be mostly sorted already and insertions will + * usually be near the end, so we won't spend much time + * keeping it sorted. + */ + +/* + * Binary search a Tx list. + * If no entry is found, return a pointer to + * where a new such entry would go. + */ +static Tx* +txsearch(char *atom, Tx *t, int n) +{ + while(n > 0) { + if(atom < t[n/2].bad) + n = n/2; + else if(atom > t[n/2].bad) { + t += n/2+1; + n -= (n/2+1); + } else + return &t[n/2]; + } + return t; +} + +void +addtx(char *b, char *g) +{ + Tx *t; + Conform *c; + + if(map == nil) + map = emalloc(sizeof(*map)); + c = map; + + if(c->nt%32 == 0) + c->t = erealloc(c->t, (c->nt+32)*sizeof(c->t[0])); + t = txsearch(b, c->t, c->nt); + if(t < c->t+c->nt && t->bad == b) { + fprint(2, "warning: duplicate entry for %s in _conform.map\n", b); + return; + } + + if(t != c->t+c->nt) + memmove(t+1, t, (c->t+c->nt - t)*sizeof(Tx)); + t->bad = b; + t->good = g; + c->nt++; +} + +char* +conform(char *s, int isdir) +{ + Tx *t; + char buf[10], *g; + Conform *c; + + c = map; + s = atom(s); + if(c){ + t = txsearch(s, c->t, c->nt); + if(t < c->t+c->nt && t->bad == s) + return t->good; + } + + sprint(buf, "%c%.6d", isdir ? 'D' : 'F', c ? c->nt : 0); + g = atom(buf); + addtx(s, g); + return g; +} + +#ifdef NOTUSED +static int +isalldigit(char *s) +{ + while(*s) + if(!isdigit(*s++)) + return 0; + return 1; +} +#endif + +static int +goodcmp(const void *va, const void *vb) +{ + Tx *a, *b; + + a = (Tx*)va; + b = (Tx*)vb; + return strcmp(a->good, b->good); +} + +static int +badatomcmp(const void *va, const void *vb) +{ + Tx *a, *b; + + a = (Tx*)va; + b = (Tx*)vb; + if(a->good < b->good) + return -1; + if(a->good > b->good) + return 1; + return 0; +} + +void +wrconform(Cdimg *cd, int n, ulong *pblock, ulong *plength) +{ + char buf[1024]; + int i; + Conform *c; + + c = map; + *pblock = cd->nextblock; + if(c==nil || n==c->nt){ + *plength = 0; + return; + } + + Cwseek(cd, cd->nextblock*Blocksize); + qsort(c->t, c->nt, sizeof(c->t[0]), goodcmp); + for(i=n; int; i++) { + snprint(buf, sizeof buf, "%s %s\n", c->t[i].good, c->t[i].bad); + Cwrite(cd, buf, strlen(buf)); + } + qsort(c->t, c->nt, sizeof(c->t[0]), badatomcmp); + *plength = Cwoffset(cd) - *pblock*Blocksize; + chat("write _conform.map at %lud+%lud\n", *pblock, *plength); + Cpadblock(cd); +} diff --git a/src/cmd/9660/direc.c b/src/cmd/9660/direc.c new file mode 100644 index 00000000..8185f680 --- /dev/null +++ b/src/cmd/9660/direc.c @@ -0,0 +1,222 @@ +#include +#include +#include +#include + +#include "iso9660.h" + +void +mkdirec(Direc *direc, XDir *d) +{ + memset(direc, 0, sizeof(Direc)); + direc->name = atom(d->name); + direc->uid = atom(d->uid); + direc->gid = atom(d->gid); + direc->uidno = d->uidno; + direc->gidno = d->gidno; + direc->mode = d->mode; + direc->length = d->length; + direc->mtime = d->mtime; + direc->atime = d->atime; + direc->ctime = d->ctime; + direc->symlink = d->symlink; +} + +static int +strecmp(char *a, char *ea, char *b) +{ + int r; + + if((r = strncmp(a, b, ea-a)) != 0) + return r; + + if(b[ea-a] == '\0') + return 0; + return 1; +} + +/* + * Binary search a list of directories for the + * entry with name name. + * If no entry is found, return a pointer to + * where a new such entry would go. + */ +static Direc* +dbsearch(char *name, int nname, Direc *d, int n) +{ + int i; + + while(n > 0) { + i = strecmp(name, name+nname, d[n/2].name); + if(i < 0) + n = n/2; + else if(i > 0) { + d += n/2+1; + n -= (n/2+1); + } else + return &d[n/2]; + } + return d; +} + +/* + * Walk to name, starting at d. + */ +Direc* +walkdirec(Direc *d, char *name) +{ + char *p, *nextp, *slashp; + Direc *nd; + + for(p=name; p && *p; p=nextp) { + if((slashp = strchr(p, '/')) != nil) + nextp = slashp+1; + else + nextp = slashp = p+strlen(p); + + nd = dbsearch(p, slashp-p, d->child, d->nchild); + if(nd >= d->child+d->nchild || strecmp(p, slashp, nd->name) != 0) + return nil; + d = nd; + } + return d; +} + +/* + * Add the file ``name'' with attributes d to the + * directory ``root''. Name may contain multiple + * elements; all but the last must exist already. + * + * The child lists are kept sorted by utfname. + */ +Direc* +adddirec(Direc *root, char *name, XDir *d) +{ + char *p; + Direc *nd; + int off; + + if(name[0] == '/') + name++; + if((p = strrchr(name, '/')) != nil) { + *p = '\0'; + root = walkdirec(root, name); + if(root == nil) { + sysfatal("error in proto file: no entry for /%s but /%s/%s\n", name, name, p+1); + return nil; + } + *p = '/'; + p++; + } else + p = name; + + nd = dbsearch(p, strlen(p), root->child, root->nchild); + off = nd - root->child; + if(off < root->nchild && strcmp(nd->name, p) == 0) { + if ((d->mode & DMDIR) == 0) + fprint(2, "warning: proto lists %s twice\n", name); + return nil; + } + + if(root->nchild%Ndirblock == 0) { + root->child = erealloc(root->child, (root->nchild+Ndirblock)*sizeof(Direc)); + nd = root->child + off; + } + + memmove(nd+1, nd, (root->nchild - off)*sizeof(Direc)); + mkdirec(nd, d); + nd->name = atom(p); + root->nchild++; + return nd; +} + +/* + * Copy the tree src into dst. + */ +void +copydirec(Direc *dst, Direc *src) +{ + int i, n; + + *dst = *src; + + if((src->mode & DMDIR) == 0) + return; + + n = (src->nchild + Ndirblock - 1); + n -= n%Ndirblock; + dst->child = emalloc(n*sizeof(Direc)); + + n = dst->nchild; + for(i=0; ichild[i], &src->child[i]); +} + +/* + * Turn the Dbadname flag on for any entries + * that have non-conforming names. + */ +static void +_checknames(Direc *d, int (*isbadname)(char*), int isroot) +{ + int i; + + if(!isroot && isbadname(d->name)) + d->flags |= Dbadname; + + if(strcmp(d->name, "_conform.map") == 0) + d->flags |= Dbadname; + + for(i=0; inchild; i++) + _checknames(&d->child[i], isbadname, 0); +} + +void +checknames(Direc *d, int (*isbadname)(char*)) +{ + _checknames(d, isbadname, 1); +} + +/* + * Set the names to conform to 8.3 + * by changing them to numbers. + * Plan 9 gets the right names from its + * own directory entry. + * + * We used to write a _conform.map file to translate + * names. Joliet should take care of most of the + * interoperability with other systems now. + */ +void +convertnames(Direc *d, char* (*cvt)(char*, char*)) +{ + int i; + char new[1024]; + + if(d->flags & Dbadname) + cvt(new, conform(d->name, d->mode & DMDIR)); + else + cvt(new, d->name); + d->confname = atom(new); + + for(i=0; inchild; i++) + convertnames(&d->child[i], cvt); +} + +/* + * Sort a directory with a given comparison function. + * After this is called on a tree, adddirec should not be, + * since the entries may no longer be sorted as adddirec expects. + */ +void +dsort(Direc *d, int (*cmp)(const void*, const void*)) +{ + int i, n; + + n = d->nchild; + qsort(d->child, n, sizeof(d[0]), cmp); + + for(i=0; ichild[i], cmp); +} + 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 +#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) +{ + 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; +} diff --git a/src/cmd/9660/dump9660.c b/src/cmd/9660/dump9660.c new file mode 100644 index 00000000..320e56d3 --- /dev/null +++ b/src/cmd/9660/dump9660.c @@ -0,0 +1,402 @@ +#include +#include +#include +#include +#include +#include "iso9660.h" + +ulong now; +int chatty; +int doabort; +int docolon; +int mk9660; +Conform *map; + +static void addprotofile(char *new, char *old, Dir *d, void *a); +void usage(void); + +char *argv0; + +void +usage(void) +{ + if(mk9660) + fprint(2, "usage: disk/mk9660 [-D:] [-9cjr] [-b bootfile] [-p proto] [-s src] cdimage\n"); + else + fprint(2, "usage: disk/dump9660 [-D:] [-9cjr] [-m maxsize] [-n now] [-p proto] [-s src] cdimage\n"); + exits("usage"); +} + +int +main(int argc, char **argv) +{ + int fix; + char buf[256], *dumpname, *proto, *s, *src, *status; + ulong block, length, newnull, cblock, clength, maxsize; + Cdimg *cd; + Cdinfo info; + XDir dir; + Direc *iconform, idumproot, iroot, *jconform, jdumproot, jroot, *r; + Dump *dump; + + fix = 0; + status = nil; + memset(&info, 0, sizeof info); + proto = "/sys/lib/sysconfig/proto/allproto"; + src = "./"; + + info.volumename = atom("9CD"); + info.volumeset = atom("9VolumeSet"); + info.publisher = atom("9Publisher"); + info.preparer = atom("dump9660"); + info.application = atom("dump9660"); + info.flags = CDdump; + maxsize = 0; + mk9660 = 0; + fmtinstall('H', encodefmt); + + ARGBEGIN{ + case 'D': + chatty++; + break; + case 'M': + mk9660 = 1; + argv0 = "disk/mk9660"; + info.flags &= ~CDdump; + break; + case '9': + info.flags |= CDplan9; + break; + case ':': + docolon = 1; + break; + case 'a': + doabort = 1; + break; + case 'b': + if(!mk9660) + usage(); + info.flags |= CDbootable; + info.bootimage = EARGF(usage()); + break; + case 'c': + info.flags |= CDconform; + break; + case 'f': + fix = 1; + break; + case 'j': + info.flags |= CDjoliet; + break; + case 'n': + now = atoi(EARGF(usage())); + break; + case 'm': + maxsize = strtoul(EARGF(usage()), 0, 0); + break; + case 'p': + proto = EARGF(usage()); + break; + case 'r': + info.flags |= CDrockridge; + break; + case 's': + src = EARGF(usage()); + break; + case 'v': + info.volumename = atom(EARGF(usage())); + break; + default: + usage(); + }ARGEND + + if(mk9660 && (fix || now || maxsize)) + usage(); + + if(argc != 1) + usage(); + + if(now == 0) + now = (ulong)time(0); + if(mk9660){ + if((cd = createcd(argv[0], info)) == nil) + sysfatal("cannot create '%s': %r", argv[0]); + }else{ + if((cd = opencd(argv[0], info)) == nil) + sysfatal("cannot open '%s': %r", argv[0]); + if(!(cd->flags & CDdump)) + sysfatal("not a dump cd"); + } + + /* create ISO9660/Plan 9 tree in memory */ + memset(&dir, 0, sizeof dir); + dir.name = atom(""); + dir.uid = atom("sys"); + dir.gid = atom("sys"); + dir.uidno = 0; + dir.gidno = 0; + dir.mode = DMDIR | 0755; + dir.mtime = now; + dir.atime = now; + dir.ctime = now; + + mkdirec(&iroot, &dir); + iroot.srcfile = src; + + /* + * Read new files into memory + */ + if(rdproto(proto, src, addprotofile, nil, &iroot) < 0) + sysfatal("rdproto: %r"); + + if(mk9660){ + dump = emalloc(sizeof *dump); + dumpname = nil; + }else{ + /* + * Read current dump tree and _conform.map. + */ + idumproot = readdumpdirs(cd, &dir, isostring); + readdumpconform(cd); + if(cd->flags & CDjoliet) + jdumproot = readdumpdirs(cd, &dir, jolietstring); + + if(fix){ + dumpname = nil; + cd->nextblock = cd->nulldump+1; + cd->nulldump = 0; + Cwseek(cd, cd->nextblock*Blocksize); + goto Dofix; + } + + dumpname = adddumpdir(&idumproot, now, &dir); + /* note that we assume all names are conforming and thus sorted */ + if(cd->flags & CDjoliet) { + s = adddumpdir(&jdumproot, now, &dir); + if(s != dumpname) + sysfatal("dumpnames don't match %s %s\n", dumpname, s); + } + dump = dumpcd(cd, &idumproot); + cd->nextblock = cd->nulldump+1; + } + + /* + * Write new files, starting where the dump tree was. + * Must be done before creation of the Joliet tree so that + * blocks and lengths are correct. + */ + Cwseek(cd, cd->nextblock*Blocksize); + writefiles(dump, cd, &iroot); + + if(cd->bootimage){ + findbootimage(cd, &iroot); + Cupdatebootcat(cd); + } + + /* create Joliet tree */ + if(cd->flags & CDjoliet) + copydirec(&jroot, &iroot); + + if(info.flags & CDconform) { + checknames(&iroot, isbadiso9660); + convertnames(&iroot, struprcpy); + } else + convertnames(&iroot, (void *) strcpy); + +// isoabstract = findconform(&iroot, abstract); +// isobiblio = findconform(&iroot, biblio); +// isonotice = findconform(&iroot, notice); + + dsort(&iroot, isocmp); + + if(cd->flags & CDjoliet) { + // jabstract = findconform(&jroot, abstract); + // jbiblio = findconform(&jroot, biblio); + // jnotice = findconform(&jroot, notice); + + checknames(&jroot, isbadjoliet); + convertnames(&jroot, (void *) strcpy); + dsort(&jroot, jolietcmp); + } + + /* + * Write directories. + */ + writedirs(cd, &iroot, Cputisodir); + if(cd->flags & CDjoliet) + writedirs(cd, &jroot, Cputjolietdir); + + if(mk9660){ + cblock = 0; + clength = 0; + newnull = 0; + }else{ + /* + * Write incremental _conform.map block. + */ + wrconform(cd, cd->nconform, &cblock, &clength); + + /* jump here if we're just fixing up the cd */ +Dofix: + /* + * Write null dump header block; everything after this will be + * overwritten at the next dump. Because of this, it needs to be + * reconstructable. We reconstruct the _conform.map and dump trees + * from the header blocks in dump.c, and we reconstruct the path + * tables by walking the cd. + */ + newnull = Cputdumpblock(cd); + } + + /* + * Write _conform.map. + */ + dir.mode = 0444; + if(cd->flags & (CDconform|CDjoliet)) { + if(!mk9660 && cd->nconform == 0){ + block = cblock; + length = clength; + }else + wrconform(cd, 0, &block, &length); + + if(mk9660) +{ + idumproot = iroot; + jdumproot = jroot; + } + if(length) { + /* The ISO9660 name will get turned into uppercase when written. */ + if((iconform = walkdirec(&idumproot, "_conform.map")) == nil) + iconform = adddirec(&idumproot, "_conform.map", &dir); + jconform = nil; + if(cd->flags & CDjoliet) { + if((jconform = walkdirec(&jdumproot, "_conform.map")) == nil) + jconform = adddirec(&jdumproot, "_conform.map", &dir); + } + iconform->block = block; + iconform->length = length; + if(cd->flags & CDjoliet) { + jconform->block = block; + jconform->length = length; + } + } + if(mk9660) { + iroot = idumproot; + jroot = jdumproot; + } + } + + if(mk9660){ + /* + * Patch in root directories. + */ + setroot(cd, cd->iso9660pvd, iroot.block, iroot.length); + setvolsize(cd, cd->iso9660pvd, cd->nextblock*Blocksize); + if(cd->flags & CDjoliet){ + setroot(cd, cd->jolietsvd, jroot.block, jroot.length); + setvolsize(cd, cd->jolietsvd, cd->nextblock*Blocksize); + } + }else{ + /* + * Write dump tree at end. We assume the name characters + * are all conforming, so everything is already sorted properly. + */ + convertnames(&idumproot, (info.flags & CDconform) ? (void *) struprcpy : (void *) strcpy); + if(cd->nulldump) { + r = walkdirec(&idumproot, dumpname); + assert(r != nil); + copybutname(r, &iroot); + } + if(cd->flags & CDjoliet) { + convertnames(&jdumproot, (void *) strcpy); + if(cd->nulldump) { + r = walkdirec(&jdumproot, dumpname); + assert(r != nil); + copybutname(r, &jroot); + } + } + + writedumpdirs(cd, &idumproot, Cputisodir); + if(cd->flags & CDjoliet) + writedumpdirs(cd, &jdumproot, Cputjolietdir); + + /* + * Patch in new root directory entry. + */ + setroot(cd, cd->iso9660pvd, idumproot.block, idumproot.length); + setvolsize(cd, cd->iso9660pvd, cd->nextblock*Blocksize); + if(cd->flags & CDjoliet){ + setroot(cd, cd->jolietsvd, jdumproot.block, jdumproot.length); + setvolsize(cd, cd->jolietsvd, cd->nextblock*Blocksize); + } + } + writepathtables(cd); + + if(!mk9660){ + /* + * If we've gotten too big, truncate back to what we started with, + * fix up the cd, and exit with a non-zero status. + */ + Cwflush(cd); + if(cd->nulldump && maxsize && Cwoffset(cd) > maxsize){ + fprint(2, "too big; writing old tree back\n"); + status = "cd too big; aborted"; + + rmdumpdir(&idumproot, dumpname); + rmdumpdir(&jdumproot, dumpname); + + cd->nextblock = cd->nulldump+1; + cd->nulldump = 0; + Cwseek(cd, cd->nextblock*Blocksize); + goto Dofix; + } + + /* + * Write old null header block; this commits all our changes. + */ + if(cd->nulldump){ + Cwseek(cd, cd->nulldump*Blocksize); + sprint(buf, "plan 9 dump cd\n"); + sprint(buf+strlen(buf), "%s %lud %lud %lud %lud %lud %lud", + dumpname, now, newnull, cblock, clength, iroot.block, + iroot.length); + if(cd->flags & CDjoliet) + sprint(buf+strlen(buf), " %lud %lud", + jroot.block, jroot.length); + strcat(buf, "\n"); + Cwrite(cd, buf, strlen(buf)); + Cpadblock(cd); + Cwflush(cd); + } + } + fdtruncate(cd->fd, cd->nextblock*Blocksize); + exits(status); + return 0; +} + +static void +addprotofile(char *new, char *old, Dir *d, void *a) +{ + char *name, *p; + Direc *direc; + XDir xd; + + dirtoxdir(&xd, d); + name = nil; + if(docolon && strchr(new, ':')) { + name = emalloc(strlen(new)+1); + strcpy(name, new); + while((p=strchr(name, ':'))) + *p=' '; + new = name; + } + if((direc = adddirec((Direc*)a, new, &xd))) { + direc->srcfile = atom(old); + + // BUG: abstract, biblio, notice + } + if(name) + free(name); + +} + diff --git a/src/cmd/9660/ichar.c b/src/cmd/9660/ichar.c new file mode 100644 index 00000000..35b0c056 --- /dev/null +++ b/src/cmd/9660/ichar.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include + +#include "iso9660.h" + +/* + * ISO 9660 file names must be uppercase, digits, or underscore. + * We use lowercase, digits, and underscore, translating lower to upper + * in mkisostring, and upper to lower in isostring. + * Files with uppercase letters in their names are thus nonconforming. + * Conforming files also must have a basename + * at most 8 letters and at most one suffix of at most 3 letters. + */ +char* +isostring(uchar *buf, int len) +{ + char *p, *q; + + p = emalloc(len+1); + memmove(p, buf, len); + p[len] = '\0'; + while(len > 0 && p[len-1] == ' ') + p[--len] = '\0'; + for(q=p; *q; q++) + *q = tolower(*q); + + q = atom(p); + free(p); + return q; +} + +int +isisofrog(char c) +{ + if(c >= '0' && c <= '9') + return 0; + if(c >= 'a' && c <= 'z') + return 0; + if(c == '_') + return 0; + + return 1; +} + +int +isbadiso9660(char *s) +{ + char *p, *q; + int i; + + if((p = strchr(s, '.')) != nil) { + if(p-s > 8) + return 1; + for(q=s; q 3) + return 1; + for(q=p+1; *q; q++) + if(isisofrog(*q)) + return 1; + } else { + if(strlen(s) > 8) + return 1; + for(q=s; *q; q++) + if(isisofrog(*q)) + return 1; + + /* + * we rename files of the form [FD]dddddd + * so they don't interfere with us. + */ + if(strlen(s) == 7 && (s[0] == 'D' || s[0] == 'F')) { + for(i=1; i<7; i++) + if(s[i] < '0' || s[i] > '9') + break; + if(i == 7) + return 1; + } + } + return 0; +} + +/* + * ISO9660 name comparison + * + * The standard algorithm is as follows: + * Take the filenames without extensions, pad the shorter with 0x20s (spaces), + * and do strcmp. If they are equal, go on. + * Take the extensions, pad the shorter with 0x20s (spaces), + * and do strcmp. If they are equal, go on. + * Compare the version numbers. + * + * Since Plan 9 names are not allowed to contain characters 0x00-0x1F, + * the padded comparisons are equivalent to using strcmp directly. + * We still need to handle the base and extension differently, + * so that .foo sorts before !foo.foo. + */ +int +isocmp(const void *va, const void *vb) +{ + int i; + char s1[32], s2[32], *b1, *b2, *e1, *e2; + const Direc *a, *b; + + a = va; + b = vb; + + strecpy(s1, s1+sizeof s1, a->confname); + b1 = s1; + strecpy(s2, s2+sizeof s2, b->confname); + b2 = s2; + if((e1 = strchr(b1, '.')) != nil) + *e1++ = '\0'; + else + e1 = ""; + if((e2 = strchr(b2, '.')) != nil) + *e2++ = '\0'; + else + e2 = ""; + + if((i = strcmp(b1, b2)) != 0) + return i; + + return strcmp(e1, e2); +} + +static char* +mkisostring(char *isobuf, int n, char *s) +{ + char *p, *q, *eq; + + eq = isobuf+n; + for(p=s, q=isobuf; *p && q < eq; p++) + if('a' <= *p && *p <= 'z') + *q++ = *p+'A'-'a'; + else + *q++ = *p; + + while(q < eq) + *q++ = ' '; + + return isobuf; +} + +void +Cputisopvd(Cdimg *cd, Cdinfo info) +{ + char buf[130]; + + Cputc(cd, 1); /* primary volume descriptor */ + Cputs(cd, "CD001", 5); /* standard identifier */ + Cputc(cd, 1); /* volume descriptor version */ + Cputc(cd, 0); /* unused */ + + assert(~info.flags & (CDplan9|CDrockridge)); + + /* system identifier */ + strcpy(buf, ""); + if(info.flags & CDplan9) + strcat(buf, "plan 9 "); + if(info.flags & CDrockridge) + strcat(buf, "rrip "); + if(info.flags & CDbootable) + strcat(buf, "boot "); + if(info.flags & CDconform) + strcat(buf, "iso9660"); + else + strcat(buf, "utf8"); + + struprcpy(buf, buf); + Cputs(cd, buf, 32); + + Cputs(cd, mkisostring(buf, 32, info.volumename), 32); /* volume identifier */ + + Crepeat(cd, 0, 8); /* unused */ + Cputn(cd, 0, 4); /* volume space size */ + Crepeat(cd, 0, 32); /* unused */ + Cputn(cd, 1, 2); /* volume set size */ + Cputn(cd, 1, 2); /* volume sequence number */ + Cputn(cd, Blocksize, 2); /* logical block size */ + Cputn(cd, 0, 4); /* path table size */ + Cputnl(cd, 0, 4); /* location of Lpath */ + Cputnl(cd, 0, 4); /* location of optional Lpath */ + Cputnm(cd, 0, 4); /* location of Mpath */ + Cputnm(cd, 0, 4); /* location of optional Mpath */ + Cputisodir(cd, nil, DTroot, 1, Cwoffset(cd)); /* root directory */ + + Cputs(cd, mkisostring(buf, 128, info.volumeset), 128); /* volume set identifier */ + Cputs(cd, mkisostring(buf, 128, info.publisher), 128); /* publisher identifier */ + Cputs(cd, mkisostring(buf, 128, info.preparer), 128); /* data preparer identifier */ + Cputs(cd, mkisostring(buf, 128, info.application), 128); /* application identifier */ + + Cputs(cd, "", 37); /* copyright notice */ + Cputs(cd, "", 37); /* abstract */ + Cputs(cd, "", 37); /* bibliographic file */ + Cputdate1(cd, now); /* volume creation date */ + Cputdate1(cd, now); /* volume modification date */ + Cputdate1(cd, 0); /* volume expiration date */ + Cputdate1(cd, 0); /* volume effective date */ + Cputc(cd, 1); /* file structure version */ + Cpadblock(cd); +} diff --git a/src/cmd/9660/iso9660.h b/src/cmd/9660/iso9660.h new file mode 100644 index 00000000..5eefae88 --- /dev/null +++ b/src/cmd/9660/iso9660.h @@ -0,0 +1,424 @@ +/* + * iso9660.h + * + * Routines and data structures to support reading and writing + * ISO 9660 CD images. See the ISO 9660 or ECMA 119 standards. + * + * Also supports Rock Ridge extensions for long file names and Unix stuff. + * Also supports Microsoft's Joliet extensions for Unicode and long file names. + * Also supports El Torito bootable CD spec. + */ + +typedef struct Cdimg Cdimg; +typedef struct Cdinfo Cdinfo; +typedef struct Conform Conform; +typedef struct Direc Direc; +typedef struct Dumproot Dumproot; +typedef struct Voldesc Voldesc; +typedef struct XDir XDir; + +#ifndef CHLINK +#define CHLINK 0 +#endif + +struct XDir { + char *name; + char *uid; + char *gid; + char *symlink; + ulong uidno; /* Numeric uid */ + ulong gidno; /* Numeric gid */ + + ulong mode; + ulong atime; + ulong mtime; + ulong ctime; + + vlong length; +}; + +/* + * A directory entry in a ISO9660 tree. + * The extra data (uid, etc.) here is put into the system use areas. + */ +struct Direc { + char *name; /* real name */ + char *confname; /* conformant name */ + char *srcfile; /* file to copy onto the image */ + + ulong block; + ulong length; + int flags; + + char *uid; + char *gid; + char *symlink; + ulong mode; + long atime; + long ctime; + long mtime; + + ulong uidno; + ulong gidno; + + Direc *child; + int nchild; +}; +enum { /* Direc flags */ + Dbadname = 1<<0, /* Non-conformant name */ +}; + +/* + * Data found in a volume descriptor. + */ +struct Voldesc { + char *systemid; + char *volumeset; + char *publisher; + char *preparer; + char *application; + + /* file names for various parameters */ + char *abstract; + char *biblio; + char *notice; + + /* path table */ + ulong pathsize; + ulong lpathloc; + ulong mpathloc; + + /* root of file tree */ + Direc root; +}; + +/* + * An ISO9660 CD image. Various parameters are kept in memory but the + * real image file is opened for reading and writing on fd. + * + * The bio buffers brd and bwr moderate reading and writing to the image. + * The routines we use are careful to flush one before or after using the other, + * as necessary. + */ +struct Cdimg { + char *file; + int fd; + ulong dumpblock; + ulong nextblock; + ulong iso9660pvd; + ulong jolietsvd; + ulong pathblock; + ulong rrcontin; /* rock ridge continuation offset */ + ulong nulldump; /* next dump block */ + ulong nconform; /* number of conform entries written already */ + ulong bootcatptr; + ulong bootcatblock; + ulong bootimageptr; + Direc *bootdirec; + char *bootimage; + + Biobuf brd; + Biobuf bwr; + + int flags; + + Voldesc iso; + Voldesc joliet; +}; +enum { /* Cdimg->flags, Cdinfo->flags */ + CDjoliet = 1<<0, + CDplan9 = 1<<1, + CDconform = 1<<2, + CDrockridge = 1<<3, + CDnew = 1<<4, + CDdump = 1<<5, + CDbootable = 1<<6, +}; + +typedef struct Tx Tx; +struct Tx { + char *bad; /* atoms */ + char *good; +}; + +struct Conform { + Tx *t; + int nt; /* delta = 32 */ +}; + +struct Cdinfo { + int flags; + + char *volumename; + + char *volumeset; + char *publisher; + char *preparer; + char *application; + char *bootimage; +}; + +enum { + Blocklen = 2048, +}; + +/* + * This is a doubly binary tree. + * We have a tree keyed on the MD5 values + * as well as a tree keyed on the block numbers. + */ +typedef struct Dump Dump; +typedef struct Dumpdir Dumpdir; + +struct Dump { + Cdimg *cd; + Dumpdir *md5root; + Dumpdir *blockroot; +}; + +struct Dumpdir { + char *name; + uchar md5[MD5dlen]; + ulong block; + ulong length; + Dumpdir *md5left; + Dumpdir *md5right; + Dumpdir *blockleft; + Dumpdir *blockright; +}; + +struct Dumproot { + char *name; + int nkid; + Dumproot *kid; + Direc root; + Direc jroot; +}; + +/* + * ISO9660 on-CD structures. + */ +typedef struct Cdir Cdir; +typedef struct Cpath Cpath; +typedef struct Cvoldesc Cvoldesc; + +/* a volume descriptor block */ +struct Cvoldesc { + uchar magic[8]; /* 0x01, "CD001", 0x01, 0x00 */ + uchar systemid[32]; /* system identifier */ + uchar volumeid[32]; /* volume identifier */ + uchar unused[8]; /* character set in secondary desc */ + uchar volsize[8]; /* volume size */ + uchar charset[32]; + uchar volsetsize[4]; /* volume set size = 1 */ + uchar volseqnum[4]; /* volume sequence number = 1 */ + uchar blocksize[4]; /* logical block size */ + uchar pathsize[8]; /* path table size */ + uchar lpathloc[4]; /* Lpath */ + uchar olpathloc[4]; /* optional Lpath */ + uchar mpathloc[4]; /* Mpath */ + uchar ompathloc[4]; /* optional Mpath */ + uchar rootdir[34]; /* directory entry for root */ + uchar volumeset[128]; /* volume set identifier */ + uchar publisher[128]; + uchar preparer[128]; /* data preparer identifier */ + uchar application[128]; /* application identifier */ + uchar notice[37]; /* copyright notice file */ + uchar abstract[37]; /* abstract file */ + uchar biblio[37]; /* bibliographic file */ + uchar cdate[17]; /* creation date */ + uchar mdate[17]; /* modification date */ + uchar xdate[17]; /* expiration date */ + uchar edate[17]; /* effective date */ + uchar fsvers; /* file system version = 1 */ +}; + +/* a directory entry */ +struct Cdir { + uchar len; + uchar xlen; + uchar dloc[8]; + uchar dlen[8]; + uchar date[7]; + uchar flags; + uchar unitsize; + uchar gapsize; + uchar volseqnum[4]; + uchar namelen; + uchar name[1]; /* chumminess */ +}; + +/* a path table entry */ +struct Cpath { + uchar namelen; + uchar xlen; + uchar dloc[4]; + uchar parent[2]; + uchar name[1]; /* chumminess */ +}; + +enum { /* Rockridge flags */ + RR_PX = 1<<0, + RR_PN = 1<<1, + RR_SL = 1<<2, + RR_NM = 1<<3, + RR_CL = 1<<4, + RR_PL = 1<<5, + RR_RE = 1<<6, + RR_TF = 1<<7, +}; + +enum { /* CputrripTF type argument */ + TFcreation = 1<<0, + TFmodify = 1<<1, + TFaccess = 1<<2, + TFattributes = 1<<3, + TFbackup = 1<<4, + TFexpiration = 1<<5, + TFeffective = 1<<6, + TFlongform = 1<<7, +}; + +enum { /* CputrripNM flag types */ + NMcontinue = 1<<0, + NMcurrent = 1<<1, + NMparent = 1<<2, + NMroot = 1<<3, + NMvolroot = 1<<4, + NMhost = 1<<5, +}; + +/* boot.c */ +void Cputbootvol(Cdimg*); +void Cputbootcat(Cdimg*); +void Cupdatebootvol(Cdimg*); +void Cupdatebootcat(Cdimg*); +void findbootimage(Cdimg*, Direc*); + +/* cdrdwr.c */ +Cdimg *createcd(char*, Cdinfo); +Cdimg *opencd(char*, Cdinfo); +void Creadblock(Cdimg*, void*, ulong, ulong); +ulong big(void*, int); +ulong little(void*, int); +int parsedir(Cdimg*, Direc*, uchar*, int, char *(*)(uchar*, int)); +void setroot(Cdimg*, ulong, ulong, ulong); +void setvolsize(Cdimg*, ulong, ulong); +void setpathtable(Cdimg*, ulong, ulong, ulong, ulong); +void Cputc(Cdimg*, int); +void Cputnl(Cdimg*, ulong, int); +void Cputnm(Cdimg*, ulong, int); +void Cputn(Cdimg*, long, int); +void Crepeat(Cdimg*, int, int); +void Cputs(Cdimg*, char*, int); +void Cwrite(Cdimg*, void*, int); +void Cputr(Cdimg*, Rune); +void Crepeatr(Cdimg*, Rune, int); +void Cputrs(Cdimg*, Rune*, int); +void Cputrscvt(Cdimg*, char*, int); +void Cpadblock(Cdimg*); +void Cputdate(Cdimg*, ulong); +void Cputdate1(Cdimg*, ulong); +void Cread(Cdimg*, void*, int); +void Cwflush(Cdimg*); +void Cwseek(Cdimg*, ulong); +ulong Cwoffset(Cdimg*); +ulong Croffset(Cdimg*); +int Cgetc(Cdimg*); +void Crseek(Cdimg*, ulong); +char *Crdline(Cdimg*, int); +int Clinelen(Cdimg*); + +/* conform.c */ +void rdconform(Cdimg*); +char *conform(char*, int); +void wrconform(Cdimg*, int, ulong*, ulong*); + +/* direc.c */ +void mkdirec(Direc*, XDir*); +Direc *walkdirec(Direc*, char*); +Direc *adddirec(Direc*, char*, XDir*); +void copydirec(Direc*, Direc*); +void checknames(Direc*, int (*)(char*)); +void convertnames(Direc*, char* (*)(char*, char*)); +void dsort(Direc*, int (*)(const void*, const void*)); +void setparents(Direc*); + +/* dump.c */ +ulong Cputdumpblock(Cdimg*); +int hasdump(Cdimg*); +Dump *dumpcd(Cdimg*, Direc*); +Dumpdir *lookupmd5(Dump*, uchar*); +void insertmd5(Dump*, char*, uchar*, ulong, ulong); + +Direc readdumpdirs(Cdimg*, XDir*, char*(*)(uchar*,int)); +char *adddumpdir(Direc*, ulong, XDir*); +void copybutname(Direc*, Direc*); + +void readkids(Cdimg*, Direc*, char*(*)(uchar*,int)); +void freekids(Direc*); +void readdumpconform(Cdimg*); +void rmdumpdir(Direc*, char*); + +/* ichar.c */ +char *isostring(uchar*, int); +int isbadiso9660(char*); +int isocmp(const void*, const void*); +int isisofrog(char); +void Cputisopvd(Cdimg*, Cdinfo); + +/* jchar.c */ +char *jolietstring(uchar*, int); +int isbadjoliet(char*); +int jolietcmp(const void*, const void*); +int isjolietfrog(Rune); +void Cputjolietsvd(Cdimg*, Cdinfo); + +/* path.c */ +void writepathtables(Cdimg*); + +/* util.c */ +void *emalloc(ulong); +void *erealloc(void*, ulong); +char *atom(char*); +char *struprcpy(char*, char*); +int chat(char*, ...); + +/* unix.c, plan9.c */ +void dirtoxdir(XDir*, Dir*); +void fdtruncate(int, ulong); +long uidno(char*); +long gidno(char*); + +/* rune.c */ +Rune *strtorune(Rune*, char*); +Rune *runechr(Rune*, Rune); +int runecmp(Rune*, Rune*); + +/* sysuse.c */ +int Cputsysuse(Cdimg*, Direc*, int, int, int); + +/* write.c */ +void writefiles(Dump*, Cdimg*, Direc*); +void writedirs(Cdimg*, Direc*, int(*)(Cdimg*, Direc*, int, int, int)); +void writedumpdirs(Cdimg*, Direc*, int(*)(Cdimg*, Direc*, int, int, int)); +int Cputisodir(Cdimg*, Direc*, int, int, int); +int Cputjolietdir(Cdimg*, Direc*, int, int, int); +void Cputendvd(Cdimg*); + +enum { + Blocksize = 2048, + Ndirblock = 16, /* directory blocks allocated at once */ + + DTdot = 0, + DTdotdot, + DTiden, + DTroot, + DTrootdot, +}; + +extern ulong now; +extern Conform *map; +extern int chatty; +extern int docolon; +extern int mk9660; diff --git a/src/cmd/9660/jchar.c b/src/cmd/9660/jchar.c new file mode 100644 index 00000000..c49da635 --- /dev/null +++ b/src/cmd/9660/jchar.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include + +#include "iso9660.h" + +char* +jolietstring(uchar *buf, int len) +{ + char *p, *q; + int i; + Rune *rp; + + rp = emalloc(sizeof(Rune)*(len/2+1)); + p = emalloc(UTFmax*(len/2+1)); + + for(i=0; i 64) + return 1; + strtorune(r, s); + for(p=r; *p; p++) + if(isjolietfrog(*p)) + return 1; + return 0; +} + +/* + * Joliet name comparison + * + * The standard algorithm is the ISO9660 algorithm but + * on the encoded Runes. Runes are encoded in big endian + * format, so we can just use runecmp. + * + * Padding is with zeros, but that still doesn't affect us. + */ + +static Rune emptystring[] = { (Rune)0 }; +int +jolietcmp(const void *va, const void *vb) +{ + int i; + Rune s1[256], s2[256], *b1, *b2, *e1, *e2; /*BUG*/ + const Direc *a, *b; + + a = va; + b = vb; + + b1 = strtorune(s1, a->confname); + b2 = strtorune(s2, b->confname); + if((e1 = runechr(b1, (Rune)'.')) != nil) + *e1++ = '\0'; + else + e1 = emptystring; + + if((e2 = runechr(b2, (Rune)'.')) != nil) + *e2++ = '\0'; + else + e2 = emptystring; + + if((i = runecmp(b1, b2)) != 0) + return i; + + return runecmp(e1, e2); +} + +/* + * Write a Joliet secondary volume descriptor. + */ +void +Cputjolietsvd(Cdimg *cd, Cdinfo info) +{ + Cputc(cd, 2); /* secondary volume descriptor */ + Cputs(cd, "CD001", 5); /* standard identifier */ + Cputc(cd, 1); /* volume descriptor version */ + Cputc(cd, 0); /* unused */ + + Cputrscvt(cd, "Joliet Plan 9", 32); /* system identifier */ + Cputrscvt(cd, info.volumename, 32); /* volume identifier */ + + Crepeat(cd, 0, 8); /* unused */ + Cputn(cd, 0, 4); /* volume space size */ + Cputc(cd, 0x25); /* escape sequences: UCS-2 Level 2 */ + Cputc(cd, 0x2F); + Cputc(cd, 0x43); + + Crepeat(cd, 0, 29); + Cputn(cd, 1, 2); /* volume set size */ + Cputn(cd, 1, 2); /* volume sequence number */ + Cputn(cd, Blocksize, 2); /* logical block size */ + Cputn(cd, 0, 4); /* path table size */ + Cputnl(cd, 0, 4); /* location of Lpath */ + Cputnl(cd, 0, 4); /* location of optional Lpath */ + Cputnm(cd, 0, 4); /* location of Mpath */ + Cputnm(cd, 0, 4); /* location of optional Mpath */ + Cputjolietdir(cd, nil, DTroot, 1, Cwoffset(cd)); /* root directory */ + Cputrscvt(cd, info.volumeset, 128); /* volume set identifier */ + Cputrscvt(cd, info.publisher, 128); /* publisher identifier */ + Cputrscvt(cd, info.preparer, 128); /* data preparer identifier */ + Cputrscvt(cd, info.application, 128); /* application identifier */ + Cputrscvt(cd, "", 37); /* copyright notice */ + Cputrscvt(cd, "", 37); /* abstract */ + Cputrscvt(cd, "", 37); /* bibliographic file */ + Cputdate1(cd, now); /* volume creation date */ + Cputdate1(cd, now); /* volume modification date */ + Cputdate1(cd, 0); /* volume expiration date */ + Cputdate1(cd, 0); /* volume effective date */ + Cputc(cd, 1); /* file structure version */ + Cpadblock(cd); +} + diff --git a/src/cmd/9660/mk9660.rc b/src/cmd/9660/mk9660.rc new file mode 100755 index 00000000..1824f8e7 --- /dev/null +++ b/src/cmd/9660/mk9660.rc @@ -0,0 +1,5 @@ +#!/bin/rc + +# the master copy of this file is /sys/src/cmd/disk/9660/mk9660.rc +# do NOT edit the copies in /*/bin/disk. +exec disk/dump9660 -M $* diff --git a/src/cmd/9660/mk9660.sh b/src/cmd/9660/mk9660.sh new file mode 100755 index 00000000..a127b829 --- /dev/null +++ b/src/cmd/9660/mk9660.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# the master copy of this file is /sys/src/cmd/disk/9660/mk9660.rc +# do NOT edit the copies in /*/bin/disk. +exec disk/dump9660 -M $* diff --git a/src/cmd/9660/mkfile b/src/cmd/9660/mkfile new file mode 100644 index 00000000..06d996f0 --- /dev/null +++ b/src/cmd/9660/mkfile @@ -0,0 +1,34 @@ +<$PLAN9/src/mkhdr + +TARG=dump9660 mk9660 + +OFILES= + +DFILES=\ + boot.$O\ + cdrdwr.$O\ + conform.$O\ + direc.$O\ + dump.$O\ + dump9660.$O\ + ichar.$O\ + jchar.$O\ + path.$O\ + unix.$O\ + rune.$O\ + sysuse.$O\ + util.$O\ + write.$O\ + +HFILES=iso9660.h + +SHORTLIB=sec disk bio 9 +<$PLAN9/src/mkmany + +$O.dump9660: $DFILES + +mk9660.$O:V: + # nothing + +$O.mk9660: mk9660.sh + cp mk9660.sh $target diff --git a/src/cmd/9660/path.c b/src/cmd/9660/path.c new file mode 100644 index 00000000..f2757dba --- /dev/null +++ b/src/cmd/9660/path.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include + +#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; inamelen == 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); +} diff --git a/src/cmd/9660/plan9.c b/src/cmd/9660/plan9.c new file mode 100644 index 00000000..25f04e92 --- /dev/null +++ b/src/cmd/9660/plan9.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include "iso9660.h" + +void +dirtoxdir(XDir *xd, Dir *d) +{ + xd->name = atom(d->name); + xd->uid = atom(d->uid); + xd->gid = atom(d->gid); + xd->uidno = 0; + xd->gidno = 0; + xd->mode = d->mode; + xd->atime = d->atime; + xd->mtime = d->mtime; + xd->ctime = 0; + xd->length = d->length; +}; + +void +fdtruncate(int fd, ulong size) +{ + USED(fd, size); +} diff --git a/src/cmd/9660/rune.c b/src/cmd/9660/rune.c new file mode 100644 index 00000000..3a436f4a --- /dev/null +++ b/src/cmd/9660/rune.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include "iso9660.h" + +Rune* +strtorune(Rune *r, char *s) +{ + Rune *or; + + if(s == nil) + return nil; + + or = r; + while(*s) + s += chartorune(r++, s); + *r = L'\0'; + return or; +} + +Rune* +runechr(Rune *s, Rune c) +{ + for(; *s; s++) + if(*s == c) + return s; + return nil; +} + +int +runecmp(Rune *s, Rune *t) +{ + while(*s && *t && *s == *t) + s++, t++; + return *s - *t; +} + diff --git a/src/cmd/9660/sysuse.c b/src/cmd/9660/sysuse.c new file mode 100644 index 00000000..dc326c6a --- /dev/null +++ b/src/cmd/9660/sysuse.c @@ -0,0 +1,613 @@ +/* + * To understand this code, see Rock Ridge Interchange Protocol + * standard 1.12 and System Use Sharing Protocol version 1.12 + * (search for rrip112.ps and susp112.ps on the web). + * + * Even better, go read something else. + */ + +#include +#include +#include +#include +#include "iso9660.h" + +static long mode(Direc*, int); +static long nlink(Direc*); +static ulong suspdirflags(Direc*, int); +static ulong CputsuspCE(Cdimg *cd, ulong offset); +static int CputsuspER(Cdimg*, int); +static int CputsuspRR(Cdimg*, int, int); +static int CputsuspSP(Cdimg*, int); +//static int CputsuspST(Cdimg*, int); +static int Cputrripname(Cdimg*, char*, int, char*, int); +static int CputrripSL(Cdimg*, int, int, char*, int); +static int CputrripPX(Cdimg*, Direc*, int, int); +static int CputrripTF(Cdimg*, Direc*, int, int); + +/* + * Patch the length field in a CE record. + */ +static void +setcelen(Cdimg *cd, ulong woffset, ulong len) +{ + ulong o; + + o = Cwoffset(cd); + Cwseek(cd, woffset); + Cputn(cd, len, 4); + Cwseek(cd, o); +} + +/* + * Rock Ridge data is put into little blockettes, which can be + * at most 256 bytes including a one-byte length. Some number + * of blockettes get packed together into a normal 2048-byte block. + * Blockettes cannot cross block boundaries. + * + * A Cbuf is a blockette buffer. Len contains + * the length of the buffer written so far, and we can + * write up to 254-28. + * + * We only have one active Cbuf at a time; cdimg.rrcontin is the byte + * offset of the beginning of that Cbuf. + * + * The blockette can be at most 255 bytes. The last 28 + * will be (in the worst case) a CE record pointing at + * a new blockette. If we do write 255 bytes though, + * we'll try to pad it out to be even, and overflow. + * So the maximum is 254-28. + * + * Ceoffset contains the offset to be used with setcelen + * to patch the CE pointing at the Cbuf once we know how + * long the Cbuf is. + */ +typedef struct Cbuf Cbuf; +struct Cbuf { + int len; /* written so far, of 254-28 */ + ulong ceoffset; +}; + +static int +freespace(Cbuf *cp) +{ + return (254-28) - cp->len; +} + +static Cbuf* +ensurespace(Cdimg *cd, int n, Cbuf *co, Cbuf *cn, int dowrite) +{ + ulong end; + + if(co->len+n <= 254-28) { + co->len += n; + return co; + } + + co->len += 28; + assert(co->len <= 254); + + if(dowrite == 0) { + cn->len = n; + return cn; + } + + /* + * the current blockette is full; update cd->rrcontin and then + * write a CE record to finish it. Unfortunately we need to + * figure out which block will be next before we write the CE. + */ + end = Cwoffset(cd)+28; + + /* + * if we're in a continuation blockette, update rrcontin. + * also, write our length into the field of the CE record + * that points at us. + */ + if(cd->rrcontin+co->len == end) { + assert(cd->rrcontin != 0); + assert(co == cn); + cd->rrcontin += co->len; + setcelen(cd, co->ceoffset, co->len); + } else + assert(co != cn); + + /* + * if the current continuation block can't fit another + * blockette, then start a new continuation block. + * rrcontin = 0 (mod Blocksize) means we just finished + * one, not that we've just started one. + */ + if(cd->rrcontin%Blocksize == 0 + || cd->rrcontin/Blocksize != (cd->rrcontin+256)/Blocksize) { + cd->rrcontin = cd->nextblock*Blocksize; + cd->nextblock++; + } + + cn->ceoffset = CputsuspCE(cd, cd->rrcontin); + + assert(Cwoffset(cd) == end); + + cn->len = n; + Cwseek(cd, cd->rrcontin); + assert(cd->rrcontin != 0); + + return cn; +} + +/* + * Put down the name, but we might need to break it + * into chunks so that each chunk fits in 254-28-5 bytes. + * What a crock. + * + * The new Plan 9 format uses strings of this form too, + * since they're already there. + */ +Cbuf* +Cputstring(Cdimg *cd, Cbuf *cp, Cbuf *cn, char *nm, char *p, int flags, int dowrite) +{ + char buf[256], *q; + int free; + + for(; p[0] != '\0'; p = q) { + cp = ensurespace(cd, 5+1, cp, cn, dowrite); + cp->len -= 5+1; + free = freespace(cp); + assert(5+1 <= free && free < 256); + + strncpy(buf, p, free-5); + buf[free-5] = '\0'; + q = p+strlen(buf); + p = buf; + + ensurespace(cd, 5+strlen(p), cp, nil, dowrite); /* nil: better not use this. */ + Cputrripname(cd, nm, flags | (q[0] ? NMcontinue : 0), p, dowrite); + } + return cp; +} + +/* + * Write a Rock Ridge SUSP set of records for a directory entry. + */ +int +Cputsysuse(Cdimg *cd, Direc *d, int dot, int dowrite, int initlen) +{ + char buf[256], buf0[256], *nextpath, *p, *path, *q; + int flags, free, m, what; + ulong o; + Cbuf cn, co, *cp; + + assert(cd != nil); + assert((initlen&1) == 0); + + if(dot == DTroot) + return 0; + + co.len = initlen; + + o = Cwoffset(cd); + + assert(dowrite==0 || Cwoffset(cd) == o+co.len-initlen); + cp = &co; + + if (dot == DTrootdot) { + m = CputsuspSP(cd, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputsuspSP(cd, dowrite); + + m = CputsuspER(cd, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputsuspER(cd, dowrite); + } + + /* + * In a perfect world, we'd be able to omit the NM + * entries when our name was all lowercase and conformant, + * but OpenBSD insists on uppercasing (really, not lowercasing) + * the ISO9660 names. + */ + what = RR_PX | RR_TF | RR_NM; + if(d != nil && (d->mode & CHLINK)) + what |= RR_SL; + + m = CputsuspRR(cd, what, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputsuspRR(cd, what, dowrite); + + if(what & RR_PX) { + m = CputrripPX(cd, d, dot, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputrripPX(cd, d, dot, dowrite); + } + + if(what & RR_NM) { + if(dot == DTiden) + p = d->name; + else if(dot == DTdotdot) + p = ".."; + else + p = "."; + + flags = suspdirflags(d, dot); + assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen); + cp = Cputstring(cd, cp, &cn, "NM", p, flags, dowrite); + } + + /* + * Put down the symbolic link. This is even more of a crock. + * Not only are the individual elements potentially split, + * but the whole path itself can be split across SL blocks. + * To keep the code simple as possible (really), we write + * only one element per SL block, wasting 6 bytes per element. + */ + if(what & RR_SL) { + for(path=d->symlink; path[0] != '\0'; path=nextpath) { + /* break off one component */ + if((nextpath = strchr(path, '/')) == nil) + nextpath = path+strlen(path); + strncpy(buf0, path, nextpath-path); + buf0[nextpath-path] = '\0'; + if(nextpath[0] == '/') + nextpath++; + p = buf0; + + /* write the name, perhaps broken into pieces */ + if(strcmp(p, "") == 0) + flags = NMroot; + else if(strcmp(p, ".") == 0) + flags = NMcurrent; + else if(strcmp(p, "..") == 0) + flags = NMparent; + else + flags = 0; + + /* the do-while handles the empty string properly */ + do { + /* must have room for at least 1 byte of name */ + cp = ensurespace(cd, 7+1, cp, &cn, dowrite); + cp->len -= 7+1; + free = freespace(cp); + assert(7+1 <= free && free < 256); + + strncpy(buf, p, free-7); + buf[free-7] = '\0'; + q = p+strlen(buf); + p = buf; + + /* nil: better not need to expand */ + assert(7+strlen(p) <= free); + ensurespace(cd, 7+strlen(p), cp, nil, dowrite); + CputrripSL(cd, nextpath[0], flags | (q[0] ? NMcontinue : 0), p, dowrite); + p = q; + } while(p[0] != '\0'); + } + } + + assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen); + + if(what & RR_TF) { + m = CputrripTF(cd, d, TFcreation|TFmodify|TFaccess|TFattributes, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputrripTF(cd, d, TFcreation|TFmodify|TFaccess|TFattributes, dowrite); + } + assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen); + + if(cp == &cn && dowrite) { + /* seek out of continuation, but mark our place */ + cd->rrcontin = Cwoffset(cd); + setcelen(cd, cn.ceoffset, cn.len); + Cwseek(cd, o+co.len-initlen); + } + + if(co.len & 1) { + co.len++; + if(dowrite) + Cputc(cd, 0); + } + + if(dowrite) { + if(Cwoffset(cd) != o+co.len-initlen) + fprint(2, "offset %lud o+co.len-initlen %lud\n", Cwoffset(cd), o+co.len-initlen); + assert(Cwoffset(cd) == o+co.len-initlen); + } else + assert(Cwoffset(cd) == o); + + assert(co.len <= 255); + return co.len - initlen; +} + +static char SUSPrrip[10] = "RRIP_1991A"; +static char SUSPdesc[84] = "RRIP "; +static char SUSPsrc[135] = "RRIP "; + +static ulong +CputsuspCE(Cdimg *cd, ulong offset) +{ + ulong o, x; + + chat("writing SUSP CE record pointing to %ld, %ld\n", offset/Blocksize, offset%Blocksize); + o = Cwoffset(cd); + Cputc(cd, 'C'); + Cputc(cd, 'E'); + Cputc(cd, 28); + Cputc(cd, 1); + Cputn(cd, offset/Blocksize, 4); + Cputn(cd, offset%Blocksize, 4); + x = Cwoffset(cd); + Cputn(cd, 0, 4); + assert(Cwoffset(cd) == o+28); + + return x; +} + +static int +CputsuspER(Cdimg *cd, int dowrite) +{ + assert(cd != nil); + + if(dowrite) { + chat("writing SUSP ER record\n"); + Cputc(cd, 'E'); /* ER field marker */ + Cputc(cd, 'R'); + Cputc(cd, 26); /* Length */ + Cputc(cd, 1); /* Version */ + Cputc(cd, 10); /* LEN_ID */ + Cputc(cd, 4); /* LEN_DESC */ + Cputc(cd, 4); /* LEN_SRC */ + Cputc(cd, 1); /* EXT_VER */ + Cputs(cd, SUSPrrip, 10); /* EXT_ID */ + Cputs(cd, SUSPdesc, 4); /* EXT_DESC */ + Cputs(cd, SUSPsrc, 4); /* EXT_SRC */ + } + return 8+10+4+4; +} + +static int +CputsuspRR(Cdimg *cd, int what, int dowrite) +{ + assert(cd != nil); + + if(dowrite) { + Cputc(cd, 'R'); /* RR field marker */ + Cputc(cd, 'R'); + Cputc(cd, 5); /* Length */ + Cputc(cd, 1); /* Version number */ + Cputc(cd, what); /* Flags */ + } + return 5; +} + +static int +CputsuspSP(Cdimg *cd, int dowrite) +{ + assert(cd!=0); + + if(dowrite) { +chat("writing SUSP SP record\n"); + Cputc(cd, 'S'); /* SP field marker */ + Cputc(cd, 'P'); + Cputc(cd, 7); /* Length */ + Cputc(cd, 1); /* Version */ + Cputc(cd, 0xBE); /* Magic */ + Cputc(cd, 0xEF); + Cputc(cd, 0); + } + + return 7; +} + +#ifdef NOTUSED +static int +CputsuspST(Cdimg *cd, int dowrite) +{ + assert(cd!=0); + + if(dowrite) { + Cputc(cd, 'S'); /* ST field marker */ + Cputc(cd, 'T'); + Cputc(cd, 4); /* Length */ + Cputc(cd, 1); /* Version */ + } + return 4; +} +#endif + +static ulong +suspdirflags(Direc *d, int dot) +{ + uchar flags; + + USED(d); + flags = 0; + switch(dot) { + default: + assert(0); + case DTdot: + case DTrootdot: + flags |= NMcurrent; + break; + case DTdotdot: + flags |= NMparent; + break; + case DTroot: + flags |= NMvolroot; + break; + case DTiden: + break; + } + return flags; +} + +static int +Cputrripname(Cdimg *cd, char *nm, int flags, char *name, int dowrite) +{ + int l; + + l = strlen(name); + if(dowrite) { + Cputc(cd, nm[0]); /* NM field marker */ + Cputc(cd, nm[1]); + Cputc(cd, l+5); /* Length */ + Cputc(cd, 1); /* Version */ + Cputc(cd, flags); /* Flags */ + Cputs(cd, name, l); /* Alternate name */ + } + return 5+l; +} + +static int +CputrripSL(Cdimg *cd, int contin, int flags, char *name, int dowrite) +{ + int l; + + l = strlen(name); + if(dowrite) { + Cputc(cd, 'S'); + Cputc(cd, 'L'); + Cputc(cd, l+7); + Cputc(cd, 1); + Cputc(cd, contin ? 1 : 0); + Cputc(cd, flags); + Cputc(cd, l); + Cputs(cd, name, l); + } + return 7+l; +} + +static int +CputrripPX(Cdimg *cd, Direc *d, int dot, int dowrite) +{ + assert(cd!=0); + + if(dowrite) { + Cputc(cd, 'P'); /* PX field marker */ + Cputc(cd, 'X'); + Cputc(cd, 36); /* Length */ + Cputc(cd, 1); /* Version */ + + Cputn(cd, mode(d, dot), 4); /* POSIX File mode */ + Cputn(cd, nlink(d), 4); /* POSIX st_nlink */ + Cputn(cd, d?d->uidno:0, 4); /* POSIX st_uid */ + Cputn(cd, d?d->gidno:0, 4); /* POSIX st_gid */ + } + + return 36; +} + +static int +CputrripTF(Cdimg *cd, Direc *d, int type, int dowrite) +{ + int i, length; + + assert(cd!=0); + assert(!(type & TFlongform)); + + length = 0; + for(i=0; i<7; i++) + if (type & (1<ctime:0); + if (type & TFmodify) + Cputdate(cd, d?d->mtime:0); + if (type & TFaccess) + Cputdate(cd, d?d->atime:0); + if (type & TFattributes) + Cputdate(cd, d?d->ctime:0); + + // if (type & TFbackup) + // Cputdate(cd, 0); + // if (type & TFexpiration) + // Cputdate(cd, 0); + // if (type & TFeffective) + // Cputdate(cd, 0); + } + return 5+7*length; +} + + +#define NONPXMODES (DMDIR & DMAPPEND & DMEXCL & DMMOUNT) +#define POSIXMODEMASK (0177777) +#ifndef S_IFMT +#define S_IFMT (0170000) +#endif +#ifndef S_IFDIR +#define S_IFDIR (0040000) +#endif +#ifndef S_IFREG +#define S_IFREG (0100000) +#endif +#ifndef S_IFLNK +#define S_IFLNK (0120000) +#endif +#undef ISTYPE +#define ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask)) +#ifndef S_ISDIR +#define S_ISDIR(mode) ISTYPE(mode, S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(mode) ISTYPE(mode, S_IREG) +#endif +#ifndef S_ISLNK +#define S_ISLNK(mode) ISTYPE(mode, S_ILNK) +#endif + + +static long +mode(Direc *d, int dot) +{ + long mode; + + if (!d) + return 0; + + if ((dot != DTroot) && (dot != DTrootdot)) { + mode = (d->mode & ~(NONPXMODES)); + if (d->mode & DMDIR) + mode |= S_IFDIR; + else if (d->mode & CHLINK) + mode |= S_IFLNK; + else + mode |= S_IFREG; + } else + mode = S_IFDIR | (0755); + + mode &= POSIXMODEMASK; + + /* Botch: not all POSIX types supported yet */ + assert(mode & (S_IFDIR|S_IFREG)); + +chat("writing PX record mode field %ulo with dot %d and name \"%s\"\n", mode, dot, d->name); + + return mode; +} + +static long +nlink(Direc *d) /* Trump up the nlink field for POSIX compliance */ +{ + int i; + long n; + + if (!d) + return 0; + + n = 1; + if (d->mode & DMDIR) /* One for "." and one more for ".." */ + n++; + + for(i=0; inchild; i++) + if (d->child[i].mode & DMDIR) + n++; + + return n; +} + diff --git a/src/cmd/9660/uid.c b/src/cmd/9660/uid.c new file mode 100644 index 00000000..a6348424 --- /dev/null +++ b/src/cmd/9660/uid.c @@ -0,0 +1,41 @@ +#include +#include + +/* + * /adm/users is + * id:user/group:head member:other members + * + * /etc/{passwd,group} + * name:x:nn:other stuff + */ + +static int isnumber(char *s); + +sniff(Biobuf *b) +{ + read first line of file into p; + + nf = getfields(p, f, nelem(f), ":"); + if(nf < 4) + return nil; + + if(isnumber(f[0]) && !isnumber(f[2])) + return _plan9; + + if(!isnumber(f[0]) && isnumber(f[2])) + return _unix; + + return nil; +} + + +int +isnumber(char *s) +{ + char *q; + + strtol(s, &q, 10); + return *q == '\0'; +} + +/* EOF */ diff --git a/src/cmd/9660/unix.c b/src/cmd/9660/unix.c new file mode 100644 index 00000000..99332af8 --- /dev/null +++ b/src/cmd/9660/unix.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include + +#include "iso9660.h" + +#include +#include + +typedef struct Xarg Xarg; +struct Xarg { + void (*enm)(char*,char*,XDir*,void*); + void (*warn)(char*,void*); + void *arg; +}; + +static long numericuid(char *user); +static long numericgid(char *gp); + +void +dirtoxdir(XDir *xd, Dir *d) +{ + // char buf[NAMELEN+1]; + memset(xd, 0, sizeof *xd); + + xd->name = atom(d->name); + xd->uid = atom(d->uid); + xd->gid = atom(d->gid); + xd->uidno = numericuid(d->uid); + xd->gidno = numericgid(d->gid); + xd->mode = d->mode; + xd->atime = d->atime; + xd->mtime = d->mtime; + xd->ctime = 0; + xd->length = d->length; + if(xd->mode & CHLINK) { + xd->mode |= 0777; + //xd->symlink = atom(d->symlink); + xd->symlink = atom("symlink"); // XXX: rsc + } +}; + +void +fdtruncate(int fd, ulong size) +{ + ftruncate(fd, size); + + return; +} + +static long +numericuid(char *user) +{ + struct passwd *pass; + static int warned = 0; + + if (! (pass = getpwnam(user))) { + if (!warned) + fprint(2, "Warning: getpwnam(3) failed for \"%s\"\n", user); + warned = 1; + return 0; + } + + return pass->pw_uid; +} + +static long +numericgid(char *gp) +{ + struct group *gr; + static int warned = 0; + + if (! (gr = getgrnam(gp))) { + if (!warned) + fprint(2, "Warning: getgrnam(3) failed for \"%s\"\n", gp); + warned = 1; + return 0; + } + + return gr->gr_gid; +} diff --git a/src/cmd/9660/util.c b/src/cmd/9660/util.c new file mode 100644 index 00000000..16fb21f3 --- /dev/null +++ b/src/cmd/9660/util.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include + +#include "iso9660.h" + +typedef struct Stringtab Stringtab; +struct Stringtab { + Stringtab *link; + char *str; +}; + +static Stringtab *stab[1024]; + +static uint +hash(char *s) +{ + uint h; + uchar *p; + + h = 0; + for(p=(uchar*)s; *p; p++) + h = h*37 + *p; + return h; +} + +static char* +estrdup(char *s) +{ + if((s = strdup(s)) == nil) + sysfatal("strdup(%.10s): out of memory", s); + return s; +} + +char* +atom(char *str) +{ + uint h; + Stringtab *tab; + + h = hash(str) % nelem(stab); + for(tab=stab[h]; tab; tab=tab->link) + if(strcmp(str, tab->str) == 0) + return tab->str; + + tab = emalloc(sizeof *tab); + tab->str = estrdup(str); + tab->link = stab[h]; + stab[h] = tab; + return tab->str; +} + +void* +emalloc(ulong n) +{ + void *p; + + if((p = malloc(n)) == nil) + sysfatal("malloc(%lud): out of memory", n); + memset(p, 0, n); + return p; +} + +void* +erealloc(void *v, ulong n) +{ + if((v = realloc(v, n)) == nil) + sysfatal("realloc(%p, %lud): out of memory", v, n); + return v; +} + +char* +struprcpy(char *p, char *s) +{ + char *op; + + op = p; + for(; *s; s++) + *p++ = toupper(*s); + *p = '\0'; + + return op; +} + +int +chat(char *fmt, ...) +{ + va_list arg; + + if(!chatty) + return 0; + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); + return 1; +} 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 +#include +#include +#include + +#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; inchild; 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; inchild; 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; inchild; 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; inchild; 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; inchild; 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; inchild; 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; inchild; 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; inchild; 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; inchild; 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); +} -- cgit v1.2.3