#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->loader = info.loader; cd->flags |= info.flags & (CDbootable|CDbootnoemu); 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, (vlong)block * Blocksize, 0) != (vlong)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, (vlong)block * Blocksize + offsetof(Cvoldesc, rootdir[0]) + offsetof(Cdir, dloc[0])); Cputn(cd, dloc, 4); Cputn(cd, dlen, 4); } void setvolsize(Cdimg *cd, uvlong block, ulong size) { assert(block != 0); Cwseek(cd, block * Blocksize + offsetof(Cvoldesc, volsize[0])); Cputn(cd, size, 4); /* size in blocks */ } void setpathtable(Cdimg *cd, ulong block, ulong sz, ulong lloc, ulong bloc) { assert(block != 0); Cwseek(cd, (vlong)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) == (vlong)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, uvlong val, int size) { switch(size) { default: sysfatal("bad size %d in Cputnl", size); case 2: if(val >= (1<<16)) sysfatal("value %llud too big for size %d in Cputnl", val, size); Cputc(cd, val); Cputc(cd, val>>8); break; case 4: if(val >= (1ULL<<32)) sysfatal("value %llud too big for size %d in Cputnl", val, size); Cputc(cd, val); Cputc(cd, val>>8); Cputc(cd, val>>16); Cputc(cd, val>>24); break; case 8: Cputc(cd, val); Cputc(cd, val>>8); Cputc(cd, val>>16); Cputc(cd, val>>24); Cputc(cd, val>>32); Cputc(cd, val>>40); Cputc(cd, val>>48); Cputc(cd, val>>56); break; } } void Cputnm(Cdimg *cd, uvlong val, int size) { switch(size) { default: sysfatal("bad size %d in Cputnm", size); case 2: if(val >= (1<<16)) sysfatal("value %llud too big for size %d in Cputnl", val, size); Cputc(cd, val>>8); Cputc(cd, val); break; case 4: if(val >= (1ULL<<32)) sysfatal("value %llud too big for size %d in Cputnl", val, size); Cputc(cd, val>>24); Cputc(cd, val>>16); Cputc(cd, val>>8); Cputc(cd, val); break; case 8: Cputc(cd, val>>56); Cputc(cd, val>>48); Cputc(cd, val>>40); Cputc(cd, val>>32); Cputc(cd, val>>24); Cputc(cd, val>>16); Cputc(cd, val>>8); Cputc(cd, val); break; } } void Cputn(Cdimg *cd, uvlong 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, vlong offset) { Bseek(&cd->bwr, offset, 0); } uvlong Cwoffset(Cdimg *cd) { return Boffset(&cd->bwr); } void Cwflush(Cdimg *cd) { Bflush(&cd->bwr); } uvlong Croffset(Cdimg *cd) { return Boffset(&cd->brd); } void Crseek(Cdimg *cd, vlong offset) { Bseek(&cd->brd, offset, 0); } int Cgetc(Cdimg *cd) { int c; Cwflush(cd); if((c = Bgetc(&cd->brd)) == Beof) { fprint(2, "getc at %llud\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); }