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