aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/fossil/flfmt9660.c
diff options
context:
space:
mode:
authorDavid du Colombier <0intro@gmail.com>2013-09-23 23:00:39 +0200
committerDavid du Colombier <0intro@gmail.com>2013-09-23 23:00:39 +0200
commit6f4d00ee45693290fae042b27536b54f77b96acd (patch)
tree60ad31bf16ed2000661c02345dd2a63851588a5d /src/cmd/fossil/flfmt9660.c
parentfea86f063930ea187f1c77e93207ac8d39125520 (diff)
downloadplan9port-6f4d00ee45693290fae042b27536b54f77b96acd.tar.gz
plan9port-6f4d00ee45693290fae042b27536b54f77b96acd.tar.bz2
plan9port-6f4d00ee45693290fae042b27536b54f77b96acd.zip
fossil: import from plan 9
R=rsc https://codereview.appspot.com/7988047
Diffstat (limited to 'src/cmd/fossil/flfmt9660.c')
-rw-r--r--src/cmd/fossil/flfmt9660.c565
1 files changed, 565 insertions, 0 deletions
diff --git a/src/cmd/fossil/flfmt9660.c b/src/cmd/fossil/flfmt9660.c
new file mode 100644
index 00000000..4464c9fb
--- /dev/null
+++ b/src/cmd/fossil/flfmt9660.c
@@ -0,0 +1,565 @@
+/*
+ * 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 "T" 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 %.32T\n", v->systemid);
+ print("volume %.32T\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 %.128T\n", v->volsetid);
+ print("publisher %.128T\n", v->publisher);
+ print("preparer %.128T\n", v->prepid);
+ print("application %.128T\n", v->applid);
+ print("notice %.37T\n", v->notice);
+ print("abstract %.37T\n", v->abstract);
+ print("biblio %.37T\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, "%.*T 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('T', 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)
+ vtFatal("Bopen %s: %r", file9660);
+
+ getsect(root, 16);
+ ascii();
+
+ v = (Voldesc*)root;
+ if(memcmp(v->magic, "\x01CD001\x01\x00", 8) != 0)
+ vtFatal("%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)
+ vtFatal("fossil data starts after cd data");
+ if(off9660 + (vlong)h->end*h->blockSize < endoff)
+ vtFatal("fossil data ends before cd data");
+ if(fsoff%h->blockSize)
+ vtFatal("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)
+ vtFatal("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)
+ vtFatal("cannot read first data sector on cd via fossil");
+ if(memcmp(sect, sect2, Blocksize) != 0)
+ vtFatal("iso9660 offset is a lie %08ux %08ux", *(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))
+ vtFatal("disk read failed: %r");
+ getsect(sect, startoff/Blocksize);
+ if(memcmp(buf, sect, Blocksize) != 0)
+ vtFatal("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);
+ vtRUnlock(fs->elk);
+ if(!fsSnapshot(fs, nil, nil, 0))
+ vtFatal("snapshot failed: %R");
+ vtRLock(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)
+ vtFatal("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)
+ vtFatal("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)
+ vtFatal("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))
+ vtFatal("fileMapBlock: %R");
+ }
+ if(!fileSetSize(f, len))
+ vtFatal("fileSetSize: %R");
+ }
+ if(!fileGetDir(f, &de))
+ vtFatal("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"))
+ vtFatal("fileSetDir: %R");
+ fileDecRef(f);
+ ind--;
+}