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 <flate.h>
#include <auth.h>
#include <fcall.h>
#include <ctype.h>
#include "tapefs.h"
#include "zip.h"

#define FORCE_LOWER     1       /* force filenames to lower case */
#define MUNGE_CR        1       /* replace '\r\n' with ' \n' */
#define High64 (1LL<<63)

/*
 * File system for zip archives (read-only)
 */

enum {
        IS_MSDOS = 0,   /* creator OS (interpretation of external flags) */
        IS_RDONLY = 1,  /* file was readonly (external flags) */
        IS_TEXT = 1,    /* file was text  (internal flags) */
};

typedef struct Block Block;
struct Block{
        uchar *pos;
        uchar *limit;
};

static Biobuf *bin;
static ulong *crctab;
static ulong crc;

static int findCDir(Biobuf *);
static int header(Biobuf *, ZipHead *);
static int cheader(Biobuf *, ZipHead *);
static void trailer(Biobuf *, ZipHead *);
static char *getname(Biobuf *, int);
static int blwrite(void *, void *, int);
static ulong get4(Biobuf *);
static int get2(Biobuf *);
static int get1(Biobuf *);
static long msdos2time(int, int);

void
populate(char *name)
{
        char *p;
        Fileinf f;
        ZipHead zh;
        int ok, entries;

        crctab = mkcrctab(ZCrcPoly);
        ok = inflateinit();
        if(ok != FlateOk)
                sysfatal("inflateinit failed: %s", flateerr(ok));

        bin = Bopen(name, OREAD);
        if (bin == nil)
                error("Can't open argument file");

        entries = findCDir(bin);
        if(entries < 0)
                sysfatal("empty file");

        while(entries-- > 0){
                memset(&zh, 0, sizeof(zh));
                if(!cheader(bin, &zh))
                        break;
                f.addr = zh.off;
                if(zh.iattr & IS_TEXT)
                        f.addr |= High64;
                f.mode = (zh.madevers == IS_MSDOS && zh.eattr & IS_RDONLY)? 0444: 0644;
                if (zh.meth == 0 && zh.uncsize == 0){
                        p = strchr(zh.file, '\0');
                        if(p > zh.file && p[-1] == '/')
                                f.mode |= (DMDIR | 0111);
                }
                f.uid = 0;
                f.gid = 0;
                f.size = zh.uncsize;
                f.mdate = msdos2time(zh.modtime, zh.moddate);
                f.name = zh.file + ((zh.file[0] == '/')? 1: 0);
                poppath(f, 1);
                free(zh.file);
        }
        return ;
}

void
dotrunc(Ram *r)
{
        USED(r);
}

void
docreate(Ram *r)
{
        USED(r);
}

char *
doread(Ram *r, vlong off, long cnt)
{
        int i, err;
        Block bs;
        ZipHead zh;
        static Qid oqid;
        static char buf[Maxbuf];
        static uchar *cache = nil;

        if (cnt > Maxbuf)
                sysfatal("file too big (>%d)", Maxbuf);

        if (Bseek(bin, r->addr & 0x7FFFFFFFFFFFFFFFLL, 0) < 0)
                sysfatal("seek failed");

        memset(&zh, 0, sizeof(zh));
        if (!header(bin, &zh))
                sysfatal("cannot get local header");

        switch(zh.meth){
        case 0:
                if (Bseek(bin, off, 1) < 0)
                        sysfatal("seek failed");
                if (Bread(bin, buf, cnt) != cnt)
                        sysfatal("read failed");
                break;
        case 8:
                if (r->qid.path != oqid.path){
                        oqid = r->qid;
                        if (cache)
                                free(cache);
                        cache = emalloc(r->ndata);

                        bs.pos = cache;
                        bs.limit = cache+r->ndata;
                        if ((err = inflate(&bs, blwrite, bin, (int(*)(void*))Bgetc)) != FlateOk)
                                sysfatal("inflate failed - %s", flateerr(err));

                        if (blockcrc(crctab, crc, cache, r->ndata) != zh.crc)
                                fprint(2, "%s - crc failed", r->name);

                        if ((r->addr & High64) && MUNGE_CR){
                                for (i = 0; i < r->ndata -1; i++)
                                        if (cache[i] == '\r' && cache[i +1] == '\n')
                                                cache[i] = ' ';
                        }
                }
                memcpy(buf, cache+off, cnt);
                break;
        default:
                sysfatal("%d - unsupported compression method", zh.meth);
                break;
        }
        
        return buf;
}

void
popdir(Ram *r)
{
        USED(r);
}

void
dowrite(Ram *r, char *buf, long off, long cnt)
{
        USED(r); USED(buf); USED(off); USED(cnt);
}

int
dopermw(Ram *r)
{
        USED(r);
        return 0;
}

/*************************************************/

static int
findCDir(Biobuf *bin)
{
        vlong ecoff;
        long off;
        int entries, zclen;

        ecoff = Bseek(bin, -ZECHeadSize, 2);
        if(ecoff < 0)
                sysfatal("can't seek to header");

        if(get4(bin) != ZECHeader)
                sysfatal("bad magic number on directory");

        get2(bin);
        get2(bin);
        get2(bin);
        entries = get2(bin);
        get4(bin);
        off = get4(bin);
        zclen = get2(bin);
        while(zclen-- > 0)
                get1(bin);

        if(Bseek(bin, off, 0) != off)
                sysfatal("can't seek to contents");

        return entries;
}


static int
header(Biobuf *bin, ZipHead *zh)
{
        ulong v;
        int flen, xlen;

        v = get4(bin);
        if(v != ZHeader){
                if(v == ZCHeader)
                        return 0;
                sysfatal("bad magic on local header");
        }
        zh->extvers = get1(bin);
        zh->extos = get1(bin);
        zh->flags = get2(bin);
        zh->meth = get2(bin);
        zh->modtime = get2(bin);
        zh->moddate = get2(bin);
        zh->crc = get4(bin);
        zh->csize = get4(bin);
        zh->uncsize = get4(bin);
        flen = get2(bin);
        xlen = get2(bin);

        zh->file = getname(bin, flen);

        while(xlen-- > 0)
                get1(bin);
        return 1;
}

static int
cheader(Biobuf *bin, ZipHead *zh)
{
        ulong v;
        int flen, xlen, fclen;

        v = get4(bin);
        if(v != ZCHeader){
                if(v == ZECHeader)
                        return 0;
                sysfatal("bad magic number in file");
        }
        zh->madevers = get1(bin);
        zh->madeos = get1(bin);
        zh->extvers = get1(bin);
        zh->extos = get1(bin);
        zh->flags = get2(bin);
        zh->meth = get2(bin);
        zh->modtime = get2(bin);
        zh->moddate = get2(bin);
        zh->crc = get4(bin);
        zh->csize = get4(bin);
        zh->uncsize = get4(bin);
        flen = get2(bin);
        xlen = get2(bin);
        fclen = get2(bin);
        get2(bin);              /* disk number start */
        zh->iattr = get2(bin);  /* 1 == is-text-file */
        zh->eattr = get4(bin);  /* 1 == readonly-file */
        zh->off = get4(bin);

        zh->file = getname(bin, flen);

        while(xlen-- > 0)
                get1(bin);

        while(fclen-- > 0)
                get1(bin);

        return 1;
}

static int
blwrite(void *vb, void *buf, int n)
{
        Block *b = vb;
        if(n > b->limit - b->pos)
                n = b->limit - b->pos;
        memmove(b->pos, buf, n);
        b->pos += n;
        return n;
}


static void
trailer(Biobuf *bin, ZipHead *zh)
{
        if(zh->flags & ZTrailInfo){
                zh->crc = get4(bin);
                zh->csize = get4(bin);
                zh->uncsize = get4(bin);
        }
}

static char*
getname(Biobuf *bin, int len)
{
        char *s;
        int i, c;

        s = emalloc(len + 1);
        for(i = 0; i < len; i++){
                c = get1(bin);
                if(FORCE_LOWER)
                        c = tolower(c);
                s[i] = c;
        }
        s[i] = '\0';
        return s;
}


static ulong
get4(Biobuf *b)
{
        ulong v;
        int i, c;

        v = 0;
        for(i = 0; i < 4; i++){
                c = Bgetc(b);
                if(c < 0)
                        sysfatal("unexpected eof");
                v |= c << (i * 8);
        }
        return v;
}

static int
get2(Biobuf *b)
{
        int i, c, v;

        v = 0;
        for(i = 0; i < 2; i++){
                c = Bgetc(b);
                if(c < 0)
                        sysfatal("unexpected eof");
                v |= c << (i * 8);
        }
        return v;
}

static int
get1(Biobuf *b)
{
        int c;

        c = Bgetc(b);
        if(c < 0)
                sysfatal("unexpected eof");
        return c;
}

static long
msdos2time(int time, int date)
{
        Tm tm;

        tm.hour = time >> 11;
        tm.min = (time >> 5) & 63;
        tm.sec = (time & 31) << 1;
        tm.year = 80 + (date >> 9);
        tm.mon = ((date >> 5) & 15) - 1;
        tm.mday = date & 31;
        tm.zone[0] = '\0';
        tm.yday = 0;

        return tm2sec(&tm);
}