Exemplo n.º 1
0
static Cbuf*
ensurespace(Cdimg *cd, int n, Cbuf *co, Cbuf *cn, int dowrite)
{
	uvlong 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 = (vlong)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;
}
Exemplo n.º 2
0
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);	
}
Exemplo n.º 3
0
void
wrconform(Cdimg *cd, int n, ulong *pblock, uvlong *plength)
{
	char buf[1024];
	int i;
	Conform *c;

	c = map;
	*pblock = cd->nextblock;
	if(c==nil || n==c->nt){
		*plength = 0;
		return;
	}

	Cwseek(cd, (vlong)cd->nextblock * Blocksize);
	qsort(c->t, c->nt, sizeof(c->t[0]), goodcmp);
	for(i=n; i<c->nt; 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) - (vlong)*pblock * Blocksize;
	chat("write _conform.map at %lud+%llud\n", *pblock, *plength);
	Cpadblock(cd);
}
Exemplo n.º 4
0
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);
}
Exemplo n.º 5
0
/*
 * Patch the length field in a CE record.
 */
static void
setcelen(Cdimg *cd, vlong woffset, ulong len)
{
	vlong o;

	o = Cwoffset(cd);
	Cwseek(cd, woffset);
	Cputn(cd, len, 4);
	Cwseek(cd, o);
}
Exemplo n.º 6
0
void
Cupdatebootvol(Cdimg *cd)
{
	ulong o;

	o = Cwoffset(cd);
	Cwseek(cd, cd->bootcatptr);
	Cputnl(cd, cd->bootcatblock, 4);
	Cwseek(cd, o);
}
Exemplo n.º 7
0
/*
 * Patch the length field in a CE record.
 */
static void
setcelen(Cdimg *cd, int64_t woffset, uint32_t len)
{
	int64_t o;

	o = Cwoffset(cd);
	Cwseek(cd, woffset);
	Cputn(cd, len, 4);
	Cwseek(cd, o);
}
Exemplo n.º 8
0
static ulong
CputsuspCE(Cdimg *cd, vlong offset)
{
	vlong 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;
}
Exemplo n.º 9
0
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);
}
Exemplo n.º 10
0
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]));
}
Exemplo n.º 11
0
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);
}
Exemplo n.º 12
0
/*
 * 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);
}
Exemplo n.º 13
0
/*
 * 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;
}
Exemplo n.º 14
0
/*
 * 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;
	uvlong 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 %llud o+co.len-initlen %llud\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;
}
Exemplo n.º 15
0
void
main(int argc, char **argv)
{
	int fix;
	ulong block, newnull, cblock;
	vlong maxsize;
	uvlong length, clength;
	char buf[256], *dumpname, *proto, *s, *src, *status;
	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':
		info.flags |= CDbootnoemu;
		/* fall through */
	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 = strtoull(EARGF(usage()), 0, 0);
		break;
	case 'o':
		dataoffset = atoll(EARGF(usage()));
		blocksize = atoi(EARGF(usage()));
		if(blocksize%Blocksize)
			sysfatal("bad block size %d -- must be multiple of 2048", blocksize);
		blocksize /= Blocksize;
		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;
	case 'x':
		info.flags |= CDpbs;
		info.loader = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND

	if(info.flags & CDpbs && !(info.flags & CDbootnoemu))
		usage();

	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, (vlong)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", 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.
	 */
	if(dataoffset > (vlong)cd->nextblock * Blocksize)
		cd->nextblock = (dataoffset+Blocksize-1)/Blocksize;
	Cwseek(cd, (vlong)cd->nextblock * Blocksize);
	writefiles(dump, cd, &iroot);

	if(cd->bootimage){
		findbootimage(cd, &iroot);
		if(cd->loader)
			findloader(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);
	}
	if(info.flags & CDpbs)
		Cfillpbs(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);
		if(cd->flags & CDjoliet){
			setroot(cd, cd->jolietsvd, jroot.block, jroot.length);
			setvolsize(cd, cd->jolietsvd, cd->nextblock);
		}
	}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);
		if(cd->flags & CDjoliet){
			setroot(cd, cd->jolietsvd, jdumproot.block, jdumproot.length);
			setvolsize(cd, cd->jolietsvd, cd->nextblock);
		}
	}
	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, (vlong)cd->nextblock * Blocksize);
			goto Dofix;
		}
	
		/*
		 * Write old null header block; this commits all our changes.
		 */
		if(cd->nulldump){
			Cwseek(cd, (vlong)cd->nulldump * Blocksize);
			sprint(buf, "plan 9 dump cd\n");
			sprint(buf+strlen(buf), "%s %lud %lud %lud %llud %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, (vlong)cd->nextblock * Blocksize);
	exits(status);
}