]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/tapefs/zipfs.c
cc: use 7 octal digits for 21 bit runes
[plan9front.git] / sys / src / cmd / tapefs / zipfs.c
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
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
92 dotrunc(Ram *r)
93 {
94         USED(r);
95 }
96
97 void
98 docreate(Ram *r)
99 {
100         USED(r);
101 }
102
103 char *
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
162 popdir(Ram *r)
163 {
164         USED(r);
165 }
166
167 void
168 dowrite(Ram *r, char *buf, long off, long cnt)
169 {
170         USED(r); USED(buf); USED(off); USED(cnt);
171 }
172
173 int
174 dopermw(Ram *r)
175 {
176         USED(r);
177         return 0;
178 }
179
180 /*************************************************/
181
182 static int
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         off = 0;
193         while(get4(bin) != ZECHeader){
194                 if(ecoff <= 0 || off >= 1024)
195                         sysfatal("bad magic number");
196                 off++;
197                 ecoff--;
198                 Bseek(bin, ecoff, 0);
199         }
200         get2(bin);
201         get2(bin);
202         get2(bin);
203         entries = get2(bin);
204         get4(bin);
205         off = get4(bin);
206         zclen = get2(bin);
207         while(zclen-- > 0)
208                 get1(bin);
209
210         if(Bseek(bin, off, 0) != off)
211                 sysfatal("can't seek to contents");
212
213         return entries;
214 }
215
216
217 static int
218 header(Biobuf *bin, ZipHead *zh)
219 {
220         ulong v;
221         int flen, xlen;
222
223         v = get4(bin);
224         if(v != ZHeader){
225                 if(v == ZCHeader)
226                         return 0;
227                 sysfatal("bad magic on local header");
228         }
229         zh->extvers = get1(bin);
230         zh->extos = get1(bin);
231         zh->flags = get2(bin);
232         zh->meth = get2(bin);
233         zh->modtime = get2(bin);
234         zh->moddate = get2(bin);
235         zh->crc = get4(bin);
236         zh->csize = get4(bin);
237         zh->uncsize = get4(bin);
238         flen = get2(bin);
239         xlen = get2(bin);
240
241         zh->file = getname(bin, flen);
242
243         while(xlen-- > 0)
244                 get1(bin);
245         return 1;
246 }
247
248 static int
249 cheader(Biobuf *bin, ZipHead *zh)
250 {
251         ulong v;
252         int flen, xlen, fclen;
253
254         v = get4(bin);
255         if(v != ZCHeader){
256                 if(v == ZECHeader)
257                         return 0;
258                 sysfatal("bad magic number in file");
259         }
260         zh->madevers = get1(bin);
261         zh->madeos = get1(bin);
262         zh->extvers = get1(bin);
263         zh->extos = get1(bin);
264         zh->flags = get2(bin);
265         zh->meth = get2(bin);
266         zh->modtime = get2(bin);
267         zh->moddate = get2(bin);
268         zh->crc = get4(bin);
269         zh->csize = get4(bin);
270         zh->uncsize = get4(bin);
271         flen = get2(bin);
272         xlen = get2(bin);
273         fclen = get2(bin);
274         get2(bin);              /* disk number start */
275         zh->iattr = get2(bin);  /* 1 == is-text-file */
276         zh->eattr = get4(bin);  /* 1 == readonly-file */
277         zh->off = get4(bin);
278
279         zh->file = getname(bin, flen);
280
281         while(xlen-- > 0)
282                 get1(bin);
283
284         while(fclen-- > 0)
285                 get1(bin);
286
287         return 1;
288 }
289
290 static int
291 blwrite(void *vb, void *buf, int n)
292 {
293         Block *b = vb;
294         if(n > b->limit - b->pos)
295                 n = b->limit - b->pos;
296         memmove(b->pos, buf, n);
297         b->pos += n;
298         return n;
299 }
300
301
302 static void
303 trailer(Biobuf *bin, ZipHead *zh)
304 {
305         if(zh->flags & ZTrailInfo){
306                 zh->crc = get4(bin);
307                 if(zh->crc == 0x08074b50)       /* thanks apple */
308                         zh->crc = get4(bin);
309                 zh->csize = get4(bin);
310                 zh->uncsize = get4(bin);
311         }
312 }
313
314 static char*
315 getname(Biobuf *bin, int len)
316 {
317         char *s;
318         int i, c;
319
320         s = emalloc(len + 1);
321         for(i = 0; i < len; i++){
322                 c = get1(bin);
323                 if(FORCE_LOWER)
324                         c = tolower(c);
325                 s[i] = c;
326         }
327         s[i] = '\0';
328         return s;
329 }
330
331
332 static ulong
333 get4(Biobuf *b)
334 {
335         ulong v;
336         int i, c;
337
338         v = 0;
339         for(i = 0; i < 4; i++){
340                 c = Bgetc(b);
341                 if(c < 0)
342                         sysfatal("unexpected eof");
343                 v |= c << (i * 8);
344         }
345         return v;
346 }
347
348 static int
349 get2(Biobuf *b)
350 {
351         int i, c, v;
352
353         v = 0;
354         for(i = 0; i < 2; i++){
355                 c = Bgetc(b);
356                 if(c < 0)
357                         sysfatal("unexpected eof");
358                 v |= c << (i * 8);
359         }
360         return v;
361 }
362
363 static int
364 get1(Biobuf *b)
365 {
366         int c;
367
368         c = Bgetc(b);
369         if(c < 0)
370                 sysfatal("unexpected eof");
371         return c;
372 }
373
374 static long
375 msdos2time(int time, int date)
376 {
377         Tm tm;
378
379         tm.hour = time >> 11;
380         tm.min = (time >> 5) & 63;
381         tm.sec = (time & 31) << 1;
382         tm.year = 80 + (date >> 9);
383         tm.mon = ((date >> 5) & 15) - 1;
384         tm.mday = date & 31;
385         tm.zone[0] = '\0';
386         tm.yday = 0;
387
388         return tm2sec(&tm);
389 }
390