xref: /plan9/sys/src/cmd/tapefs/zipfs.c (revision 41fe996ad1ef7168ab61531223f4e75c6cf225c2)
1a8453668SDavid du Colombier #include <u.h>
2a8453668SDavid du Colombier #include <libc.h>
3a8453668SDavid du Colombier #include <bio.h>
4a8453668SDavid du Colombier #include <flate.h>
5a8453668SDavid du Colombier #include <auth.h>
6a8453668SDavid du Colombier #include <fcall.h>
7a8453668SDavid du Colombier #include <ctype.h>
8a8453668SDavid du Colombier #include "tapefs.h"
9a8453668SDavid du Colombier #include "zip.h"
10a8453668SDavid du Colombier 
11a8453668SDavid du Colombier #define FORCE_LOWER	1	/* force filenames to lower case */
12a8453668SDavid du Colombier #define MUNGE_CR	1	/* replace '\r\n' with ' \n' */
13*41fe996aSDavid du Colombier #define High64 (1LL<<63)
14a8453668SDavid du Colombier 
15a8453668SDavid du Colombier /*
16a8453668SDavid du Colombier  * File system for zip archives (read-only)
17a8453668SDavid du Colombier  */
18a8453668SDavid du Colombier 
19a8453668SDavid du Colombier enum {
20a8453668SDavid du Colombier 	IS_MSDOS = 0,	/* creator OS (interpretation of external flags) */
21a8453668SDavid du Colombier 	IS_RDONLY = 1,	/* file was readonly (external flags) */
22a8453668SDavid du Colombier 	IS_TEXT = 1,	/* file was text  (internal flags) */
23a8453668SDavid du Colombier };
24a8453668SDavid du Colombier 
25a8453668SDavid du Colombier typedef struct Block Block;
26a8453668SDavid du Colombier struct Block{
27a8453668SDavid du Colombier 	uchar *pos;
28a8453668SDavid du Colombier 	uchar *limit;
29a8453668SDavid du Colombier };
30a8453668SDavid du Colombier 
31a8453668SDavid du Colombier static Biobuf *bin;
32a8453668SDavid du Colombier static ulong *crctab;
33a8453668SDavid du Colombier static ulong crc;
34a8453668SDavid du Colombier 
35a8453668SDavid du Colombier static int findCDir(Biobuf *);
36a8453668SDavid du Colombier static int header(Biobuf *, ZipHead *);
37a8453668SDavid du Colombier static int cheader(Biobuf *, ZipHead *);
38a8453668SDavid du Colombier static void trailer(Biobuf *, ZipHead *);
39a8453668SDavid du Colombier static char *getname(Biobuf *, int);
40a8453668SDavid du Colombier static int blwrite(void *, void *, int);
41a8453668SDavid du Colombier static ulong get4(Biobuf *);
42a8453668SDavid du Colombier static int get2(Biobuf *);
43a8453668SDavid du Colombier static int get1(Biobuf *);
44a8453668SDavid du Colombier static long msdos2time(int, int);
45a8453668SDavid du Colombier 
46a8453668SDavid du Colombier void
populate(char * name)47a8453668SDavid du Colombier populate(char *name)
48a8453668SDavid du Colombier {
49a8453668SDavid du Colombier 	char *p;
50a8453668SDavid du Colombier 	Fileinf f;
51a8453668SDavid du Colombier 	ZipHead zh;
52a8453668SDavid du Colombier 	int ok, entries;
53a8453668SDavid du Colombier 
54a8453668SDavid du Colombier 	crctab = mkcrctab(ZCrcPoly);
55a8453668SDavid du Colombier 	ok = inflateinit();
56a8453668SDavid du Colombier 	if(ok != FlateOk)
57a8453668SDavid du Colombier 		sysfatal("inflateinit failed: %s", flateerr(ok));
58a8453668SDavid du Colombier 
59a8453668SDavid du Colombier 	bin = Bopen(name, OREAD);
60a8453668SDavid du Colombier 	if (bin == nil)
61a8453668SDavid du Colombier 		error("Can't open argument file");
62a8453668SDavid du Colombier 
63a8453668SDavid du Colombier 	entries = findCDir(bin);
64a8453668SDavid du Colombier 	if(entries < 0)
65a8453668SDavid du Colombier 		sysfatal("empty file");
66a8453668SDavid du Colombier 
67a8453668SDavid du Colombier 	while(entries-- > 0){
68a8453668SDavid du Colombier 		memset(&zh, 0, sizeof(zh));
69a8453668SDavid du Colombier 		if(!cheader(bin, &zh))
70a8453668SDavid du Colombier 			break;
71*41fe996aSDavid du Colombier 		f.addr = zh.off;
72*41fe996aSDavid du Colombier 		if(zh.iattr & IS_TEXT)
73*41fe996aSDavid du Colombier 			f.addr |= High64;
74a8453668SDavid du Colombier 		f.mode = (zh.madevers == IS_MSDOS && zh.eattr & IS_RDONLY)? 0444: 0644;
75a8453668SDavid du Colombier 		if (zh.meth == 0 && zh.uncsize == 0){
76a8453668SDavid du Colombier 			p = strchr(zh.file, '\0');
77a8453668SDavid du Colombier 			if(p > zh.file && p[-1] == '/')
78a8453668SDavid du Colombier 				f.mode |= (DMDIR | 0111);
79a8453668SDavid du Colombier 		}
80a8453668SDavid du Colombier 		f.uid = 0;
81a8453668SDavid du Colombier 		f.gid = 0;
82a8453668SDavid du Colombier 		f.size = zh.uncsize;
83a8453668SDavid du Colombier 		f.mdate = msdos2time(zh.modtime, zh.moddate);
84a8453668SDavid du Colombier 		f.name = zh.file + ((zh.file[0] == '/')? 1: 0);
85a8453668SDavid du Colombier 		poppath(f, 1);
86a8453668SDavid du Colombier 		free(zh.file);
87a8453668SDavid du Colombier 	}
88a8453668SDavid du Colombier 	return ;
89a8453668SDavid du Colombier }
90a8453668SDavid du Colombier 
91a8453668SDavid du Colombier void
dotrunc(Ram * r)92a8453668SDavid du Colombier dotrunc(Ram *r)
93a8453668SDavid du Colombier {
94a8453668SDavid du Colombier 	USED(r);
95a8453668SDavid du Colombier }
96a8453668SDavid du Colombier 
97a8453668SDavid du Colombier void
docreate(Ram * r)98a8453668SDavid du Colombier docreate(Ram *r)
99a8453668SDavid du Colombier {
100a8453668SDavid du Colombier 	USED(r);
101a8453668SDavid du Colombier }
102a8453668SDavid du Colombier 
103a8453668SDavid du Colombier char *
doread(Ram * r,vlong off,long cnt)104*41fe996aSDavid du Colombier doread(Ram *r, vlong off, long cnt)
105a8453668SDavid du Colombier {
106a8453668SDavid du Colombier 	int i, err;
107a8453668SDavid du Colombier 	Block bs;
108a8453668SDavid du Colombier 	ZipHead zh;
109a8453668SDavid du Colombier 	static Qid oqid;
110a8453668SDavid du Colombier 	static char buf[Maxbuf];
111a8453668SDavid du Colombier 	static uchar *cache = nil;
112a8453668SDavid du Colombier 
113a8453668SDavid du Colombier 	if (cnt > Maxbuf)
114a8453668SDavid du Colombier 		sysfatal("file too big (>%d)", Maxbuf);
115a8453668SDavid du Colombier 
116*41fe996aSDavid du Colombier 	if (Bseek(bin, r->addr & 0x7FFFFFFFFFFFFFFFLL, 0) < 0)
117a8453668SDavid du Colombier 		sysfatal("seek failed");
118a8453668SDavid du Colombier 
119a8453668SDavid du Colombier 	memset(&zh, 0, sizeof(zh));
120a8453668SDavid du Colombier 	if (!header(bin, &zh))
121a8453668SDavid du Colombier 		sysfatal("cannot get local header");
122a8453668SDavid du Colombier 
123a8453668SDavid du Colombier 	switch(zh.meth){
124a8453668SDavid du Colombier 	case 0:
125a8453668SDavid du Colombier 		if (Bseek(bin, off, 1) < 0)
126a8453668SDavid du Colombier 			sysfatal("seek failed");
127a8453668SDavid du Colombier 		if (Bread(bin, buf, cnt) != cnt)
128a8453668SDavid du Colombier 			sysfatal("read failed");
129a8453668SDavid du Colombier 		break;
130a8453668SDavid du Colombier 	case 8:
131a8453668SDavid du Colombier 		if (r->qid.path != oqid.path){
132a8453668SDavid du Colombier 			oqid = r->qid;
133a8453668SDavid du Colombier 			if (cache)
134a8453668SDavid du Colombier 				free(cache);
135a8453668SDavid du Colombier 			cache = emalloc(r->ndata);
136a8453668SDavid du Colombier 
137a8453668SDavid du Colombier 			bs.pos = cache;
138a8453668SDavid du Colombier 			bs.limit = cache+r->ndata;
139a8453668SDavid du Colombier 			if ((err = inflate(&bs, blwrite, bin, (int(*)(void*))Bgetc)) != FlateOk)
140a8453668SDavid du Colombier 				sysfatal("inflate failed - %s", flateerr(err));
141a8453668SDavid du Colombier 
142a8453668SDavid du Colombier 			if (blockcrc(crctab, crc, cache, r->ndata) != zh.crc)
143a8453668SDavid du Colombier 				fprint(2, "%s - crc failed", r->name);
144a8453668SDavid du Colombier 
145*41fe996aSDavid du Colombier 			if ((r->addr & High64) && MUNGE_CR){
146a8453668SDavid du Colombier 				for (i = 0; i < r->ndata -1; i++)
147a8453668SDavid du Colombier 					if (cache[i] == '\r' && cache[i +1] == '\n')
148a8453668SDavid du Colombier 						cache[i] = ' ';
149a8453668SDavid du Colombier 			}
150a8453668SDavid du Colombier 		}
151a8453668SDavid du Colombier 		memcpy(buf, cache+off, cnt);
152a8453668SDavid du Colombier 		break;
153a8453668SDavid du Colombier 	default:
154a8453668SDavid du Colombier 		sysfatal("%d - unsupported compression method", zh.meth);
155a8453668SDavid du Colombier 		break;
156a8453668SDavid du Colombier 	}
157a8453668SDavid du Colombier 
158a8453668SDavid du Colombier 	return buf;
159a8453668SDavid du Colombier }
160a8453668SDavid du Colombier 
161a8453668SDavid du Colombier void
popdir(Ram * r)162a8453668SDavid du Colombier popdir(Ram *r)
163a8453668SDavid du Colombier {
164a8453668SDavid du Colombier 	USED(r);
165a8453668SDavid du Colombier }
166a8453668SDavid du Colombier 
167a8453668SDavid du Colombier void
dowrite(Ram * r,char * buf,long off,long cnt)168a8453668SDavid du Colombier dowrite(Ram *r, char *buf, long off, long cnt)
169a8453668SDavid du Colombier {
170a8453668SDavid du Colombier 	USED(r); USED(buf); USED(off); USED(cnt);
171a8453668SDavid du Colombier }
172a8453668SDavid du Colombier 
173a8453668SDavid du Colombier int
dopermw(Ram * r)174a8453668SDavid du Colombier dopermw(Ram *r)
175a8453668SDavid du Colombier {
176a8453668SDavid du Colombier 	USED(r);
177a8453668SDavid du Colombier 	return 0;
178a8453668SDavid du Colombier }
179a8453668SDavid du Colombier 
180a8453668SDavid du Colombier /*************************************************/
181a8453668SDavid du Colombier 
182a8453668SDavid du Colombier static int
findCDir(Biobuf * bin)183a8453668SDavid du Colombier findCDir(Biobuf *bin)
184a8453668SDavid du Colombier {
185a8453668SDavid du Colombier 	vlong ecoff;
186a8453668SDavid du Colombier 	long off;
187a8453668SDavid du Colombier 	int entries, zclen;
188a8453668SDavid du Colombier 
189a8453668SDavid du Colombier 	ecoff = Bseek(bin, -ZECHeadSize, 2);
190a8453668SDavid du Colombier 	if(ecoff < 0)
191a8453668SDavid du Colombier 		sysfatal("can't seek to header");
192a8453668SDavid du Colombier 
193a8453668SDavid du Colombier 	if(get4(bin) != ZECHeader)
194a8453668SDavid du Colombier 		sysfatal("bad magic number on directory");
195a8453668SDavid du Colombier 
196a8453668SDavid du Colombier 	get2(bin);
197a8453668SDavid du Colombier 	get2(bin);
198a8453668SDavid du Colombier 	get2(bin);
199a8453668SDavid du Colombier 	entries = get2(bin);
200a8453668SDavid du Colombier 	get4(bin);
201a8453668SDavid du Colombier 	off = get4(bin);
202a8453668SDavid du Colombier 	zclen = get2(bin);
203a8453668SDavid du Colombier 	while(zclen-- > 0)
204a8453668SDavid du Colombier 		get1(bin);
205a8453668SDavid du Colombier 
206a8453668SDavid du Colombier 	if(Bseek(bin, off, 0) != off)
207a8453668SDavid du Colombier 		sysfatal("can't seek to contents");
208a8453668SDavid du Colombier 
209a8453668SDavid du Colombier 	return entries;
210a8453668SDavid du Colombier }
211a8453668SDavid du Colombier 
212a8453668SDavid du Colombier 
213a8453668SDavid du Colombier static int
header(Biobuf * bin,ZipHead * zh)214a8453668SDavid du Colombier header(Biobuf *bin, ZipHead *zh)
215a8453668SDavid du Colombier {
216a8453668SDavid du Colombier 	ulong v;
217a8453668SDavid du Colombier 	int flen, xlen;
218a8453668SDavid du Colombier 
219a8453668SDavid du Colombier 	v = get4(bin);
220a8453668SDavid du Colombier 	if(v != ZHeader){
221a8453668SDavid du Colombier 		if(v == ZCHeader)
222a8453668SDavid du Colombier 			return 0;
223a8453668SDavid du Colombier 		sysfatal("bad magic on local header");
224a8453668SDavid du Colombier 	}
225a8453668SDavid du Colombier 	zh->extvers = get1(bin);
226a8453668SDavid du Colombier 	zh->extos = get1(bin);
227a8453668SDavid du Colombier 	zh->flags = get2(bin);
228a8453668SDavid du Colombier 	zh->meth = get2(bin);
229a8453668SDavid du Colombier 	zh->modtime = get2(bin);
230a8453668SDavid du Colombier 	zh->moddate = get2(bin);
231a8453668SDavid du Colombier 	zh->crc = get4(bin);
232a8453668SDavid du Colombier 	zh->csize = get4(bin);
233a8453668SDavid du Colombier 	zh->uncsize = get4(bin);
234a8453668SDavid du Colombier 	flen = get2(bin);
235a8453668SDavid du Colombier 	xlen = get2(bin);
236a8453668SDavid du Colombier 
237a8453668SDavid du Colombier 	zh->file = getname(bin, flen);
238a8453668SDavid du Colombier 
239a8453668SDavid du Colombier 	while(xlen-- > 0)
240a8453668SDavid du Colombier 		get1(bin);
241a8453668SDavid du Colombier 	return 1;
242a8453668SDavid du Colombier }
243a8453668SDavid du Colombier 
244a8453668SDavid du Colombier static int
cheader(Biobuf * bin,ZipHead * zh)245a8453668SDavid du Colombier cheader(Biobuf *bin, ZipHead *zh)
246a8453668SDavid du Colombier {
247a8453668SDavid du Colombier 	ulong v;
248a8453668SDavid du Colombier 	int flen, xlen, fclen;
249a8453668SDavid du Colombier 
250a8453668SDavid du Colombier 	v = get4(bin);
251a8453668SDavid du Colombier 	if(v != ZCHeader){
252a8453668SDavid du Colombier 		if(v == ZECHeader)
253a8453668SDavid du Colombier 			return 0;
254a8453668SDavid du Colombier 		sysfatal("bad magic number in file");
255a8453668SDavid du Colombier 	}
256a8453668SDavid du Colombier 	zh->madevers = get1(bin);
257a8453668SDavid du Colombier 	zh->madeos = get1(bin);
258a8453668SDavid du Colombier 	zh->extvers = get1(bin);
259a8453668SDavid du Colombier 	zh->extos = get1(bin);
260a8453668SDavid du Colombier 	zh->flags = get2(bin);
261a8453668SDavid du Colombier 	zh->meth = get2(bin);
262a8453668SDavid du Colombier 	zh->modtime = get2(bin);
263a8453668SDavid du Colombier 	zh->moddate = get2(bin);
264a8453668SDavid du Colombier 	zh->crc = get4(bin);
265a8453668SDavid du Colombier 	zh->csize = get4(bin);
266a8453668SDavid du Colombier 	zh->uncsize = get4(bin);
267a8453668SDavid du Colombier 	flen = get2(bin);
268a8453668SDavid du Colombier 	xlen = get2(bin);
269a8453668SDavid du Colombier 	fclen = get2(bin);
270a8453668SDavid du Colombier 	get2(bin);		/* disk number start */
271a8453668SDavid du Colombier 	zh->iattr = get2(bin);	/* 1 == is-text-file */
272a8453668SDavid du Colombier 	zh->eattr = get4(bin);	/* 1 == readonly-file */
273a8453668SDavid du Colombier 	zh->off = get4(bin);
274a8453668SDavid du Colombier 
275a8453668SDavid du Colombier 	zh->file = getname(bin, flen);
276a8453668SDavid du Colombier 
277a8453668SDavid du Colombier 	while(xlen-- > 0)
278a8453668SDavid du Colombier 		get1(bin);
279a8453668SDavid du Colombier 
280a8453668SDavid du Colombier 	while(fclen-- > 0)
281a8453668SDavid du Colombier 		get1(bin);
282a8453668SDavid du Colombier 
283a8453668SDavid du Colombier 	return 1;
284a8453668SDavid du Colombier }
285a8453668SDavid du Colombier 
286a8453668SDavid du Colombier static int
blwrite(void * vb,void * buf,int n)287a8453668SDavid du Colombier blwrite(void *vb, void *buf, int n)
288a8453668SDavid du Colombier {
289a8453668SDavid du Colombier 	Block *b = vb;
290a8453668SDavid du Colombier 	if(n > b->limit - b->pos)
291a8453668SDavid du Colombier 		n = b->limit - b->pos;
292a8453668SDavid du Colombier 	memmove(b->pos, buf, n);
293a8453668SDavid du Colombier 	b->pos += n;
294a8453668SDavid du Colombier 	return n;
295a8453668SDavid du Colombier }
296a8453668SDavid du Colombier 
297a8453668SDavid du Colombier 
298a8453668SDavid du Colombier static void
trailer(Biobuf * bin,ZipHead * zh)299a8453668SDavid du Colombier trailer(Biobuf *bin, ZipHead *zh)
300a8453668SDavid du Colombier {
301a8453668SDavid du Colombier 	if(zh->flags & ZTrailInfo){
302a8453668SDavid du Colombier 		zh->crc = get4(bin);
303a8453668SDavid du Colombier 		zh->csize = get4(bin);
304a8453668SDavid du Colombier 		zh->uncsize = get4(bin);
305a8453668SDavid du Colombier 	}
306a8453668SDavid du Colombier }
307a8453668SDavid du Colombier 
308a8453668SDavid du Colombier static char*
getname(Biobuf * bin,int len)309a8453668SDavid du Colombier getname(Biobuf *bin, int len)
310a8453668SDavid du Colombier {
311a8453668SDavid du Colombier 	char *s;
312a8453668SDavid du Colombier 	int i, c;
313a8453668SDavid du Colombier 
314a8453668SDavid du Colombier 	s = emalloc(len + 1);
315a8453668SDavid du Colombier 	for(i = 0; i < len; i++){
316a8453668SDavid du Colombier 		c = get1(bin);
317a8453668SDavid du Colombier 		if(FORCE_LOWER)
318a8453668SDavid du Colombier 			c = tolower(c);
319a8453668SDavid du Colombier 		s[i] = c;
320a8453668SDavid du Colombier 	}
321a8453668SDavid du Colombier 	s[i] = '\0';
322a8453668SDavid du Colombier 	return s;
323a8453668SDavid du Colombier }
324a8453668SDavid du Colombier 
325a8453668SDavid du Colombier 
326a8453668SDavid du Colombier static ulong
get4(Biobuf * b)327a8453668SDavid du Colombier get4(Biobuf *b)
328a8453668SDavid du Colombier {
329a8453668SDavid du Colombier 	ulong v;
330a8453668SDavid du Colombier 	int i, c;
331a8453668SDavid du Colombier 
332a8453668SDavid du Colombier 	v = 0;
333a8453668SDavid du Colombier 	for(i = 0; i < 4; i++){
334a8453668SDavid du Colombier 		c = Bgetc(b);
335a8453668SDavid du Colombier 		if(c < 0)
336a8453668SDavid du Colombier 			sysfatal("unexpected eof");
337a8453668SDavid du Colombier 		v |= c << (i * 8);
338a8453668SDavid du Colombier 	}
339a8453668SDavid du Colombier 	return v;
340a8453668SDavid du Colombier }
341a8453668SDavid du Colombier 
342a8453668SDavid du Colombier static int
get2(Biobuf * b)343a8453668SDavid du Colombier get2(Biobuf *b)
344a8453668SDavid du Colombier {
345a8453668SDavid du Colombier 	int i, c, v;
346a8453668SDavid du Colombier 
347a8453668SDavid du Colombier 	v = 0;
348a8453668SDavid du Colombier 	for(i = 0; i < 2; i++){
349a8453668SDavid du Colombier 		c = Bgetc(b);
350a8453668SDavid du Colombier 		if(c < 0)
351a8453668SDavid du Colombier 			sysfatal("unexpected eof");
352a8453668SDavid du Colombier 		v |= c << (i * 8);
353a8453668SDavid du Colombier 	}
354a8453668SDavid du Colombier 	return v;
355a8453668SDavid du Colombier }
356a8453668SDavid du Colombier 
357a8453668SDavid du Colombier static int
get1(Biobuf * b)358a8453668SDavid du Colombier get1(Biobuf *b)
359a8453668SDavid du Colombier {
360a8453668SDavid du Colombier 	int c;
361a8453668SDavid du Colombier 
362a8453668SDavid du Colombier 	c = Bgetc(b);
363a8453668SDavid du Colombier 	if(c < 0)
364a8453668SDavid du Colombier 		sysfatal("unexpected eof");
365a8453668SDavid du Colombier 	return c;
366a8453668SDavid du Colombier }
367a8453668SDavid du Colombier 
368a8453668SDavid du Colombier static long
msdos2time(int time,int date)369a8453668SDavid du Colombier msdos2time(int time, int date)
370a8453668SDavid du Colombier {
371a8453668SDavid du Colombier 	Tm tm;
372a8453668SDavid du Colombier 
373a8453668SDavid du Colombier 	tm.hour = time >> 11;
374a8453668SDavid du Colombier 	tm.min = (time >> 5) & 63;
375a8453668SDavid du Colombier 	tm.sec = (time & 31) << 1;
376a8453668SDavid du Colombier 	tm.year = 80 + (date >> 9);
377a8453668SDavid du Colombier 	tm.mon = ((date >> 5) & 15) - 1;
378a8453668SDavid du Colombier 	tm.mday = date & 31;
379a8453668SDavid du Colombier 	tm.zone[0] = '\0';
380a8453668SDavid du Colombier 	tm.yday = 0;
381a8453668SDavid du Colombier 
382a8453668SDavid du Colombier 	return tm2sec(&tm);
383a8453668SDavid du Colombier }
384a8453668SDavid du Colombier 
385