/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <bio.h>#include <libsec.h> #include "iso9660.h" static voidwritelittlebig4(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;} voidrewritedot(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, (vlong)d->block * Blocksize); Cwrite(cd, buf, Blocksize);} voidrewritedotdot(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, (vlong)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. */voidwritefiles(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; i<direc->nchild; 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); if(blocksize && start%blocksize) start += blocksize-start%blocksize; Cwseek(cd, (vlong)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; i<d->nchild; 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; i<d->nchild; i++) l += put(cd, &d->child[i], DTiden, 0, l); start = cd->nextblock; cd->nextblock += (l+Blocksize-1)/Blocksize; next = cd->nextblock; Cwseek(cd, (vlong)start * Blocksize); ll = 0; ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll); ll += put(cd, nil, DTdotdot, 1, ll); for(i=0; i<d->nchild; i++) ll += put(cd, &d->child[i], DTiden, 1, ll); assert(ll == l); Cpadblock(cd); assert(Cwoffset(cd) == (vlong)next * Blocksize); d->block = start; d->length = (vlong)(next - start) * Blocksize; rewritedot(cd, d); rewritedotdot(cd, d, d); for(i=0; i<d->nchild; i++) if(d->child[i].mode & DMDIR) rewritedotdot(cd, &d->child[i], d);} voidwritedirs(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; i<d->nchild; 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; i<d->nchild; 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, (vlong)start * Blocksize); put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd)); put(cd, nil, DTdotdot, 1, Cwoffset(cd)); for(i=0; i<d->nchild; i++) put(cd, &d->child[i], DTiden, 1, Cwoffset(cd)); Cpadblock(cd); d->block = start; d->length = (vlong)(cd->nextblock - start) * Blocksize; rewritedot(cd, d); rewritedotdot(cd, d, d); for(i=0; i<d->nchild; 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); }} voidwritedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int)){ _writedumpdirs(cd, d, put, 0);} static intCputplan9(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 intgenputdir(Cdimg *cd, Direc *d, int dot, int joliet, int dowrite, int offset){ int f, n, l, lp; vlong 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;} intCputisodir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset){ return genputdir(cd, d, dot, 0, dowrite, offset);} intCputjolietdir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset){ return genputdir(cd, d, dot, 1, dowrite, offset);} voidCputendvd(Cdimg *cd){ Cputc(cd, 255); /* volume descriptor set terminator */ Cputs(cd, "CD001", 5); /* standard identifier */ Cputc(cd, 1); /* volume descriptor version */ Cpadblock(cd);}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#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, (vlong)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, (vlong)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; i<direc->nchild; 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);
if(blocksize && start%blocksize)
start += blocksize-start%blocksize;
Cwseek(cd, (vlong)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; i<d->nchild; 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; i<d->nchild; i++)
l += put(cd, &d->child[i], DTiden, 0, l);
start = cd->nextblock;
cd->nextblock += (l+Blocksize-1)/Blocksize;
next = cd->nextblock;
Cwseek(cd, (vlong)start * Blocksize);
ll = 0;
ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll);
ll += put(cd, nil, DTdotdot, 1, ll);
for(i=0; i<d->nchild; i++)
ll += put(cd, &d->child[i], DTiden, 1, ll);
assert(ll == l);
Cpadblock(cd);
assert(Cwoffset(cd) == (vlong)next * Blocksize);
d->block = start;
d->length = (vlong)(next - start) * Blocksize;
rewritedot(cd, d);
rewritedotdot(cd, d, d);
for(i=0; i<d->nchild; 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; i<d->nchild; 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; i<d->nchild; 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, (vlong)start * Blocksize);
put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd));
put(cd, nil, DTdotdot, 1, Cwoffset(cd));
for(i=0; i<d->nchild; i++)
put(cd, &d->child[i], DTiden, 1, Cwoffset(cd));
Cpadblock(cd);
d->block = start;
d->length = (vlong)(cd->nextblock - start) * Blocksize;
rewritedot(cd, d);
rewritedotdot(cd, d, d);
for(i=0; i<d->nchild; 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;
vlong 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);
}