blob: e9354f7b4f80daa4b10e09b11d736624deba022b [file] [log] [blame]
#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 u32int *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);
}