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