ref: 333ae58f37c2c8f79f7d7078283a30e42c4d7a27
dir: /flfmt9660.c/
/* * Initialize a fossil file system from an ISO9660 image already in the * file system. This is a fairly bizarre thing to do, but it lets us generate * installation CDs that double as valid Plan 9 disk partitions. * People having trouble booting the CD can just copy it into a disk * partition and you've got a working Plan 9 system. * * I've tried hard to keep all the associated cruft in this file. * If you deleted this file and cut out the three calls into it from flfmt.c, * no traces would remain. */ #include "stdinc.h" #include "dat.h" #include "fns.h" #include "flfmt9660.h" #include <bio.h> #include <ctype.h> static Biobuf *b; enum{ Tag = 0x96609660, Blocksize = 2048, }; #pragma varargck type "s" uchar* #pragma varargck type "L" uchar* #pragma varargck type "B" uchar* #pragma varargck type "N" uchar* #pragma varargck type "C" uchar* #pragma varargck type "D" uchar* typedef struct Voldesc Voldesc; struct Voldesc { 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]; /* root directory */ uchar volsetid[128]; /* volume set identifier */ uchar publisher[128]; uchar prepid[128]; /* data preparer identifier */ uchar applid[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 */ }; static void dumpbootvol(void *a) { Voldesc *v; v = a; print("magic %.2ux %.5s %.2ux %2ux\n", v->magic[0], v->magic+1, v->magic[6], v->magic[7]); if(v->magic[0] == 0xFF) return; print("system %.32C\n", v->systemid); print("volume %.32C\n", v->volumeid); print("volume size %.4N\n", v->volsize); print("charset %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n", v->charset[0], v->charset[1], v->charset[2], v->charset[3], v->charset[4], v->charset[5], v->charset[6], v->charset[7]); print("volume set size %.2N\n", v->volsetsize); print("volume sequence number %.2N\n", v->volseqnum); print("logical block size %.2N\n", v->blocksize); print("path size %.4L\n", v->pathsize); print("lpath loc %.4L\n", v->lpathloc); print("opt lpath loc %.4L\n", v->olpathloc); print("mpath loc %.4B\n", v->mpathloc); print("opt mpath loc %.4B\n", v->ompathloc); print("rootdir %D\n", v->rootdir); print("volume set identifier %.128C\n", v->volsetid); print("publisher %.128C\n", v->publisher); print("preparer %.128C\n", v->prepid); print("application %.128C\n", v->applid); print("notice %.37C\n", v->notice); print("abstract %.37C\n", v->abstract); print("biblio %.37C\n", v->biblio); print("creation date %.17s\n", v->cdate); print("modification date %.17s\n", v->mdate); print("expiration date %.17s\n", v->xdate); print("effective date %.17s\n", v->edate); print("fs version %d\n", v->fsvers); } typedef struct Cdir Cdir; 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 */ }; #pragma varargck type "D" Cdir* static int Dfmt(Fmt *fmt) { char buf[128]; Cdir *c; c = va_arg(fmt->args, Cdir*); if(c->namelen == 1 && c->name[0] == '\0' || c->name[0] == '\001') { snprint(buf, sizeof buf, ".%s dloc %.4N dlen %.4N", c->name[0] ? "." : "", c->dloc, c->dlen); } else { snprint(buf, sizeof buf, "%.*C dloc %.4N dlen %.4N", c->namelen, c->name, c->dloc, c->dlen); } fmtstrcpy(fmt, buf); return 0; } char longc, shortc; static void bigend(void) { longc = 'B'; } static void littleend(void) { longc = 'L'; } static ulong big(void *a, int n) { uchar *p; ulong v; int i; p = a; v = 0; for(i=0; i<n; i++) v = (v<<8) | *p++; return v; } static ulong little(void *a, int n) { uchar *p; ulong v; int i; p = a; v = 0; for(i=0; i<n; i++) v |= (*p++<<(i*8)); return v; } /* numbers in big or little endian. */ static int BLfmt(Fmt *fmt) { ulong v; uchar *p; char buf[20]; p = va_arg(fmt->args, uchar*); if(!(fmt->flags&FmtPrec)) { fmtstrcpy(fmt, "*BL*"); return 0; } if(fmt->r == 'B') v = big(p, fmt->prec); else v = little(p, fmt->prec); sprint(buf, "0x%.*lux", fmt->prec*2, v); fmt->flags &= ~FmtPrec; fmtstrcpy(fmt, buf); return 0; } /* numbers in both little and big endian */ static int Nfmt(Fmt *fmt) { char buf[100]; uchar *p; p = va_arg(fmt->args, uchar*); sprint(buf, "%.*L %.*B", fmt->prec, p, fmt->prec, p+fmt->prec); fmt->flags &= ~FmtPrec; fmtstrcpy(fmt, buf); return 0; } static int asciiTfmt(Fmt *fmt) { char *p, buf[256]; int i; p = va_arg(fmt->args, char*); for(i=0; i<fmt->prec; i++) buf[i] = *p++; buf[i] = '\0'; for(p=buf+strlen(buf); p>buf && p[-1]==' '; p--) ; p[0] = '\0'; fmt->flags &= ~FmtPrec; fmtstrcpy(fmt, buf); return 0; } static void ascii(void) { fmtinstall('C', asciiTfmt); } static int runeTfmt(Fmt *fmt) { Rune buf[256], *r; int i; uchar *p; p = va_arg(fmt->args, uchar*); for(i=0; i*2+2<=fmt->prec; i++, p+=2) buf[i] = (p[0]<<8)|p[1]; buf[i] = L'\0'; for(r=buf+i; r>buf && r[-1]==L' '; r--) ; r[0] = L'\0'; fmt->flags &= ~FmtPrec; return fmtprint(fmt, "%S", buf); } static void getsect(uchar *buf, int n) { if(Bseek(b, n*2048, 0) != n*2048 || Bread(b, buf, 2048) != 2048) { abort(); sysfatal("reading block at %,d: %r", n*2048); } } static Header *h; static int fd; static char *file9660; static int off9660; static ulong startoff; static ulong endoff; static ulong fsoff; static uchar root[2048]; static Voldesc *v; static ulong iso9660start(Cdir*); static void iso9660copydir(Fs*, File*, Cdir*); static void iso9660copyfile(Fs*, File*, Cdir*); void iso9660init(int xfd, Header *xh, char *xfile9660, int xoff9660) { uchar sect[2048], sect2[2048]; fmtinstall('L', BLfmt); fmtinstall('B', BLfmt); fmtinstall('N', Nfmt); fmtinstall('D', Dfmt); fd = xfd; h = xh; file9660 = xfile9660; off9660 = xoff9660; if((b = Bopen(file9660, OREAD)) == nil) sysfatal("Bopen %s: %r", file9660); getsect(root, 16); ascii(); v = (Voldesc*)root; if(memcmp(v->magic, "\x01CD001\x01\x00", 8) != 0) sysfatal("%s not a cd image", file9660); startoff = iso9660start((Cdir*)v->rootdir)*Blocksize; endoff = little(v->volsize, 4); /* already in bytes */ fsoff = off9660 + h->data*h->blockSize; if(fsoff > startoff) sysfatal("fossil data starts after cd data"); if(off9660 + (vlong)h->end*h->blockSize < endoff) sysfatal("fossil data ends before cd data"); if(fsoff%h->blockSize) sysfatal("cd offset not a multiple of fossil block size"); /* Read "same" block via CD image and via Fossil image */ getsect(sect, startoff/Blocksize); if(seek(fd, startoff-off9660, 0) < 0) sysfatal("cannot seek to first data sector on cd via fossil"); fprint(2, "look for %lud at %lud\n", startoff, startoff-off9660); if(readn(fd, sect2, Blocksize) != Blocksize) sysfatal("cannot read first data sector on cd via fossil"); if(memcmp(sect, sect2, Blocksize) != 0) sysfatal("iso9660 offset is a lie %08lux %08lux", *(long*)sect, *(long*)sect2); } void iso9660labels(Disk *disk, uchar *buf, void (*write)(int, u32int)) { ulong sb, eb, bn, lb, llb; Label l; int lpb; uchar sect[Blocksize]; if(!diskReadRaw(disk, PartData, (startoff-fsoff)/h->blockSize, buf)) sysfatal("disk read failed: %r"); getsect(sect, startoff/Blocksize); if(memcmp(buf, sect, Blocksize) != 0) sysfatal("fsoff is wrong"); sb = (startoff-fsoff)/h->blockSize; eb = (endoff-fsoff+h->blockSize-1)/h->blockSize; lpb = h->blockSize/LabelSize; /* for each reserved block, mark label */ llb = ~0; l.type = BtData; l.state = BsAlloc; l.tag = Tag; l.epoch = 1; l.epochClose = ~(u32int)0; for(bn=sb; bn<eb; bn++){ lb = bn/lpb; if(lb != llb){ if(llb != ~0) (*write)(PartLabel, llb); memset(buf, 0, h->blockSize); } llb = lb; labelPack(&l, buf, bn%lpb); } if(llb != ~0) (*write)(PartLabel, llb); } void iso9660copy(Fs *fs) { File *root; root = fileOpen(fs, "/active"); iso9660copydir(fs, root, (Cdir*)v->rootdir); fileDecRef(root); runlock(&fs->elk); if(!fsSnapshot(fs, nil, nil, 0)) sysfatal("snapshot failed: %r"); rlock(&fs->elk); } /* * The first block used is the first data block of the leftmost file in the tree. * (Just an artifact of how mk9660 works.) */ static ulong iso9660start(Cdir *c) { uchar sect[Blocksize]; while(c->flags&2){ getsect(sect, little(c->dloc, 4)); c = (Cdir*)sect; c = (Cdir*)((uchar*)c+c->len); /* skip dot */ c = (Cdir*)((uchar*)c+c->len); /* skip dotdot */ /* oops: might happen if leftmost directory is empty or leftmost file is zero length! */ if(little(c->dloc, 4) == 0) sysfatal("error parsing cd image or unfortunate cd image"); } return little(c->dloc, 4); } static void iso9660copydir(Fs *fs, File *dir, Cdir *cd) { ulong off, end, len; uchar sect[Blocksize], *esect, *p; Cdir *c; len = little(cd->dlen, 4); off = little(cd->dloc, 4)*Blocksize; end = off+len; esect = sect+Blocksize; for(; off<end; off+=Blocksize){ getsect(sect, off/Blocksize); p = sect; while(p < esect){ c = (Cdir*)p; if(c->len <= 0) break; if(c->namelen!=1 || c->name[0]>1) iso9660copyfile(fs, dir, c); p += c->len; } } } static char* getname(uchar **pp) { uchar *p; int l; p = *pp; l = *p; *pp = p+1+l; if(l == 0) return ""; memmove(p, p+1, l); p[l] = 0; return (char*)p; } static char* getcname(Cdir *c) { uchar *up; char *p, *q; up = &c->namelen; p = getname(&up); for(q=p; *q; q++) *q = tolower(*q); return p; } static char dmsize[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, }; static ulong getcdate(uchar *p) /* yMdhmsz */ { Tm tm; int y, M, d, h, m, s, tz; y=p[0]; M=p[1]; d=p[2]; h=p[3]; m=p[4]; s=p[5]; tz=p[6]; USED(tz); if (y < 70) return 0; if (M < 1 || M > 12) return 0; if (d < 1 || d > dmsize[M-1]) return 0; if (h > 23) return 0; if (m > 59) return 0; if (s > 59) return 0; memset(&tm, 0, sizeof tm); tm.sec = s; tm.min = m; tm.hour = h; tm.mday = d; tm.mon = M-1; tm.year = 1900+y; tm.zone[0] = 0; return tm2sec(&tm); } static int ind; static void iso9660copyfile(Fs *fs, File *dir, Cdir *c) { Dir d; DirEntry de; int sysl; uchar score[VtScoreSize]; ulong off, foff, len, mode; uchar *p; File *f; ind++; memset(&d, 0, sizeof d); p = c->name + c->namelen; if(((uintptr)p) & 1) p++; sysl = (uchar*)c + c->len - p; if(sysl <= 0) sysfatal("missing plan9 directory entry on %d/%d/%.*s", c->namelen, c->name[0], c->namelen, c->name); d.name = getname(&p); d.uid = getname(&p); d.gid = getname(&p); if((uintptr)p & 1) p++; d.mode = little(p, 4); if(d.name[0] == 0) d.name = getcname(c); d.mtime = getcdate(c->date); d.atime = d.mtime; if(d.mode&DMDIR) print("%*scopy %s %s %s %luo\n", ind*2, "", d.name, d.uid, d.gid, d.mode); mode = d.mode&0777; if(d.mode&DMDIR) mode |= ModeDir; if((f = fileCreate(dir, d.name, mode, d.uid)) == nil) sysfatal("could not create file '%s': %r", d.name); if(d.mode&DMDIR) iso9660copydir(fs, f, c); else{ len = little(c->dlen, 4); off = little(c->dloc, 4)*Blocksize; for(foff=0; foff<len; foff+=h->blockSize){ localToGlobal((off+foff-fsoff)/h->blockSize, score); if(!fileMapBlock(f, foff/h->blockSize, score, Tag)) sysfatal("fileMapBlock: %r"); } if(!fileSetSize(f, len)) sysfatal("fileSetSize: %r"); } if(!fileGetDir(f, &de)) sysfatal("fileGetDir: %r"); de.uid = d.uid; de.gid = d.gid; de.mtime = d.mtime; de.atime = d.atime; de.mode = d.mode&0777; if(!fileSetDir(f, &de, "sys")) sysfatal("fileSetDir: %r"); fileDecRef(f); ind--; }