Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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);
}