Imported Upstream version 3.2.0
[platform/upstream/libwebsockets.git] / lib / roles / http / server / fops-zip.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Original code used in this source file:
5  *
6  * https://github.com/PerBothner/DomTerm.git @912add15f3d0aec
7  *
8  * ./lws-term/io.c
9  * ./lws-term/junzip.c
10  *
11  * Copyright (C) 2017  Per Bothner <per@bothner.com>
12  *
13  * MIT License
14  *
15  * Permission is hereby granted, free of charge, to any person obtaining a copy
16  * of this software and associated documentation files (the "Software"), to deal
17  * in the Software without restriction, including without limitation the rights
18  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19  * ( copies of the Software, and to permit persons to whom the Software is
20  * furnished to do so, subject to the following conditions:
21  *
22  * The above copyright notice and this permission notice shall be included in
23  * all copies or substantial portions of the Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31  * SOFTWARE.
32  *
33  *
34  * lws rewrite:
35  *
36  * Copyright (C) 2017  Andy Green <andy@warmcat.com>
37  *
38  *  This library is free software; you can redistribute it and/or
39  *  modify it under the terms of the GNU Lesser General Public
40  *  License as published by the Free Software Foundation:
41  *  version 2.1 of the License.
42  *
43  *  This library is distributed in the hope that it will be useful,
44  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
45  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
46  *  Lesser General Public License for more details.
47  *
48  *  You should have received a copy of the GNU Lesser General Public
49  *  License along with this library; if not, write to the Free Software
50  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
51  *  MA  02110-1301  USA
52  */
53
54 #include "core/private.h"
55
56 #if defined(LWS_WITH_MINIZ)
57 #include <miniz.h>
58 #else
59 #include <zlib.h>
60 #endif
61
62 /*
63  * This code works with zip format containers which may have files compressed
64  * with gzip deflate (type 8) or store uncompressed (type 0).
65  *
66  * Linux zip produces such zipfiles by default, eg
67  *
68  *  $ zip ../myzip.zip file1 file2 file3
69  */
70
71 #define ZIP_COMPRESSION_METHOD_STORE 0
72 #define ZIP_COMPRESSION_METHOD_DEFLATE 8
73
74 typedef struct {
75         lws_filepos_t           filename_start;
76         uint32_t                crc32;
77         uint32_t                comp_size;
78         uint32_t                uncomp_size;
79         uint32_t                offset;
80         uint32_t                mod_time;
81         uint16_t                filename_len;
82         uint16_t                extra;
83         uint16_t                method;
84         uint16_t                file_com_len;
85 } lws_fops_zip_hdr_t;
86
87 typedef struct {
88         struct lws_fop_fd       fop_fd; /* MUST BE FIRST logical fop_fd into
89                                          * file inside zip: fops_zip fops */
90         lws_fop_fd_t            zip_fop_fd; /* logical fop fd on to zip file
91                                              * itself: using platform fops */
92         lws_fops_zip_hdr_t      hdr;
93         z_stream                inflate;
94         lws_filepos_t           content_start;
95         lws_filepos_t           exp_uncomp_pos;
96         union {
97                 uint8_t         trailer8[8];
98                 uint32_t        trailer32[2];
99         } u;
100         uint8_t                 rbuf[128]; /* decompression chunk size */
101         int                     entry_count;
102
103         unsigned int            decompress:1; /* 0 = direct from file */
104         unsigned int            add_gzip_container:1;
105 } *lws_fops_zip_t;
106
107 struct lws_plat_file_ops fops_zip;
108 #define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD))
109
110 static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 };
111
112 enum {
113         ZC_SIGNATURE                            = 0,
114         ZC_VERSION_MADE_BY                      = 4,
115         ZC_VERSION_NEEDED_TO_EXTRACT            = 6,
116         ZC_GENERAL_PURPOSE_BIT_FLAG             = 8,
117         ZC_COMPRESSION_METHOD                   = 10,
118         ZC_LAST_MOD_FILE_TIME                   = 12,
119         ZC_LAST_MOD_FILE_DATE                   = 14,
120         ZC_CRC32                                = 16,
121         ZC_COMPRESSED_SIZE                      = 20,
122         ZC_UNCOMPRESSED_SIZE                    = 24,
123         ZC_FILE_NAME_LENGTH                     = 28,
124         ZC_EXTRA_FIELD_LENGTH                   = 30,
125
126         ZC_FILE_COMMENT_LENGTH                  = 32,
127         ZC_DISK_NUMBER_START                    = 34,
128         ZC_INTERNAL_FILE_ATTRIBUTES             = 36,
129         ZC_EXTERNAL_FILE_ATTRIBUTES             = 38,
130         ZC_REL_OFFSET_LOCAL_HEADER              = 42,
131         ZC_DIRECTORY_LENGTH                     = 46,
132
133         ZE_SIGNATURE_OFFSET                     = 0,
134         ZE_DESK_NUMBER                          = 4,
135         ZE_CENTRAL_DIRECTORY_DISK_NUMBER        = 6,
136         ZE_NUM_ENTRIES_THIS_DISK                = 8,
137         ZE_NUM_ENTRIES                          = 10,
138         ZE_CENTRAL_DIRECTORY_SIZE               = 12,
139         ZE_CENTRAL_DIR_OFFSET                   = 16,
140         ZE_ZIP_COMMENT_LENGTH                   = 20,
141         ZE_DIRECTORY_LENGTH                     = 22,
142
143         ZL_REL_OFFSET_CONTENT                   = 28,
144         ZL_HEADER_LENGTH                        = 30,
145
146         LWS_FZ_ERR_SEEK_END_RECORD              = 1,
147         LWS_FZ_ERR_READ_END_RECORD,
148         LWS_FZ_ERR_END_RECORD_MAGIC,
149         LWS_FZ_ERR_END_RECORD_SANITY,
150         LWS_FZ_ERR_CENTRAL_SEEK,
151         LWS_FZ_ERR_CENTRAL_READ,
152         LWS_FZ_ERR_CENTRAL_SANITY,
153         LWS_FZ_ERR_NAME_TOO_LONG,
154         LWS_FZ_ERR_NAME_SEEK,
155         LWS_FZ_ERR_NAME_READ,
156         LWS_FZ_ERR_CONTENT_SANITY,
157         LWS_FZ_ERR_CONTENT_SEEK,
158         LWS_FZ_ERR_SCAN_SEEK,
159         LWS_FZ_ERR_NOT_FOUND,
160         LWS_FZ_ERR_ZLIB_INIT,
161         LWS_FZ_ERR_READ_CONTENT,
162         LWS_FZ_ERR_SEEK_COMPRESSED,
163 };
164
165 static uint16_t
166 get_u16(void *p)
167 {
168         const uint8_t *c = (const uint8_t *)p;
169
170         return (uint16_t)((c[0] | (c[1] << 8)));
171 }
172
173 static uint32_t
174 get_u32(void *p)
175 {
176         const uint8_t *c = (const uint8_t *)p;
177
178         return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
179 }
180
181 int
182 lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
183 {
184         lws_filepos_t amount;
185         uint8_t buf[96];
186         int i;
187
188         if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0)
189                 return LWS_FZ_ERR_SEEK_END_RECORD;
190
191         if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
192                               ZE_DIRECTORY_LENGTH))
193                 return LWS_FZ_ERR_READ_END_RECORD;
194
195         if (amount != ZE_DIRECTORY_LENGTH)
196                 return LWS_FZ_ERR_READ_END_RECORD;
197
198         /*
199          * We require the zip to have the last record right at the end
200          * Linux zip always does this if no zip comment.
201          */
202         if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6)
203                 return LWS_FZ_ERR_END_RECORD_MAGIC;
204
205         i = get_u16(buf + ZE_NUM_ENTRIES);
206
207         if (get_u16(buf + ZE_DESK_NUMBER) ||
208             get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
209             i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK))
210                 return LWS_FZ_ERR_END_RECORD_SANITY;
211
212         /* end record is OK... look for our file in the central dir */
213
214         if (lws_vfs_file_seek_set(priv->zip_fop_fd,
215                                   get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0)
216                 return LWS_FZ_ERR_CENTRAL_SEEK;
217
218         while (i--) {
219                 priv->content_start = lws_vfs_tell(priv->zip_fop_fd);
220
221                 if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
222                                       ZC_DIRECTORY_LENGTH))
223                         return LWS_FZ_ERR_CENTRAL_READ;
224
225                 if (amount != ZC_DIRECTORY_LENGTH)
226                         return LWS_FZ_ERR_CENTRAL_READ;
227
228                 if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50)
229                         return LWS_FZ_ERR_CENTRAL_SANITY;
230
231                lwsl_debug("cstart 0x%lx\n", (unsigned long)priv->content_start);
232
233                 priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH);
234                 priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH);
235                 priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd);
236
237                 priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD);
238                 priv->hdr.crc32 = get_u32(buf + ZC_CRC32);
239                 priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE);
240                 priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE);
241                 priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER);
242                 priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME);
243                 priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH);
244
245                 if (priv->hdr.filename_len != len)
246                         goto next;
247
248                 if (len >= (int)sizeof(buf) - 1)
249                         return LWS_FZ_ERR_NAME_TOO_LONG;
250
251                 if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
252                                                         &amount, buf, len))
253                         return LWS_FZ_ERR_NAME_READ;
254                 if ((int)amount != len)
255                         return LWS_FZ_ERR_NAME_READ;
256
257                 buf[len] = '\0';
258                 lwsl_debug("check %s vs %s\n", buf, name);
259
260                 if (strcmp((const char *)buf, name))
261                         goto next;
262
263                 /* we found a match */
264                 if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset) < 0)
265                         return LWS_FZ_ERR_NAME_SEEK;
266                 if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
267                                                         &amount, buf,
268                                                         ZL_HEADER_LENGTH))
269                         return LWS_FZ_ERR_NAME_READ;
270                 if (amount != ZL_HEADER_LENGTH)
271                         return LWS_FZ_ERR_NAME_READ;
272
273                 priv->content_start = priv->hdr.offset +
274                                       ZL_HEADER_LENGTH +
275                                       priv->hdr.filename_len +
276                                       get_u16(buf + ZL_REL_OFFSET_CONTENT);
277
278                 lwsl_debug("content supposed to start at 0x%lx\n",
279                           (unsigned long)priv->content_start);
280
281                 if (priv->content_start > priv->zip_fop_fd->len)
282                         return LWS_FZ_ERR_CONTENT_SANITY;
283
284                 if (lws_vfs_file_seek_set(priv->zip_fop_fd,
285                                           priv->content_start) < 0)
286                         return LWS_FZ_ERR_CONTENT_SEEK;
287
288                 /* we are aligned at the start of the content */
289
290                 priv->exp_uncomp_pos = 0;
291
292                 return 0;
293
294 next:
295                 if (i && lws_vfs_file_seek_set(priv->zip_fop_fd,
296                                                priv->content_start +
297                                                ZC_DIRECTORY_LENGTH +
298                                                priv->hdr.filename_len +
299                                                priv->hdr.extra +
300                                                priv->hdr.file_com_len) < 0)
301                         return LWS_FZ_ERR_SCAN_SEEK;
302         }
303
304         return LWS_FZ_ERR_NOT_FOUND;
305 }
306
307 static int
308 lws_fops_zip_reset_inflate(lws_fops_zip_t priv)
309 {
310         if (priv->decompress)
311                 inflateEnd(&priv->inflate);
312
313         priv->inflate.zalloc = Z_NULL;
314         priv->inflate.zfree = Z_NULL;
315         priv->inflate.opaque = Z_NULL;
316         priv->inflate.avail_in = 0;
317         priv->inflate.next_in = Z_NULL;
318
319         if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) {
320                 lwsl_err("inflate init failed\n");
321                 return LWS_FZ_ERR_ZLIB_INIT;
322         }
323
324         if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->content_start) < 0)
325                 return LWS_FZ_ERR_CONTENT_SEEK;
326
327         priv->exp_uncomp_pos = 0;
328
329         return 0;
330 }
331
332 static lws_fop_fd_t
333 lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
334                   const char *vpath, lws_fop_flags_t *flags)
335 {
336         lws_fop_flags_t local_flags = 0;
337         lws_fops_zip_t priv;
338         char rp[192];
339         int m;
340
341         /*
342          * vpath points at the / after the fops signature in vfs_path, eg
343          * with a vfs_path "/var/www/docs/manual.zip/index.html", vpath
344          * will come pointing at "/index.html"
345          */
346
347         priv = lws_zalloc(sizeof(*priv), "fops_zip priv");
348         if (!priv)
349                 return NULL;
350
351         priv->fop_fd.fops = &fops_zip;
352
353         m = sizeof(rp) - 1;
354         if ((vpath - vfs_path - 1) < m)
355                 m = lws_ptr_diff(vpath, vfs_path) - 1;
356         lws_strncpy(rp, vfs_path, m + 1);
357
358         /* open the zip file itself using the incoming fops, not fops_zip */
359
360         priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags);
361         if (!priv->zip_fop_fd) {
362                 lwsl_err("unable to open zip %s\n", rp);
363                 goto bail1;
364         }
365
366         if (*vpath == '/')
367                 vpath++;
368
369         m = lws_fops_zip_scan(priv, vpath, (int)strlen(vpath));
370         if (m) {
371                 lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
372                 goto bail2;
373         }
374
375         /* the directory metadata tells us modification time, so pass it on */
376         priv->fop_fd.mod_time = priv->hdr.mod_time;
377         *flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
378         priv->fop_fd.flags = *flags;
379
380         /* The zip fop_fd is left pointing at the start of the content.
381          *
382          * 1) Content could be uncompressed (STORE), and we can always serve
383          *    that directly
384          *
385          * 2) Content could be compressed (GZIP), and the client can handle
386          *    receiving GZIP... we can wrap it in a GZIP header and trailer
387          *    and serve the content part directly.  The flag indicating we
388          *    are providing GZIP directly is set so lws will send the right
389          *    headers.
390          *
391          * 3) Content could be compressed (GZIP) but the client can't handle
392          *    receiving GZIP... we can decompress it and serve as it is
393          *    inflated piecemeal.
394          *
395          * 4) Content may be compressed some unknown way... fail
396          *
397          */
398         if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
399                 /*
400                  * it is stored uncompressed, leave it indicated as
401                  * uncompressed, and just serve it from inside the
402                  * zip with no gzip container;
403                  */
404
405                 lwsl_info("direct zip serving (stored)\n");
406
407                 priv->fop_fd.len = priv->hdr.uncomp_size;
408
409                 return &priv->fop_fd;
410         }
411
412         if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
413             priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
414
415                 /*
416                  * We can serve the gzipped file contents directly as gzip
417                  * from inside the zip container; client says it is OK.
418                  *
419                  * To convert to standalone gzip, we have to add a 10-byte
420                  * constant header and a variable 8-byte trailer around the
421                  * content.
422                  *
423                  * The 8-byte trailer is prepared now and held in the priv.
424                  */
425
426                 lwsl_info("direct zip serving (gzipped)\n");
427
428                 priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
429                                    sizeof(priv->u);
430
431                 if (lws_is_be()) {
432                         uint8_t *p = priv->u.trailer8;
433
434                         *p++ = (uint8_t)priv->hdr.crc32;
435                         *p++ = (uint8_t)(priv->hdr.crc32 >> 8);
436                         *p++ = (uint8_t)(priv->hdr.crc32 >> 16);
437                         *p++ = (uint8_t)(priv->hdr.crc32 >> 24);
438                         *p++ = (uint8_t)priv->hdr.uncomp_size;
439                         *p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
440                         *p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
441                         *p   = (uint8_t)(priv->hdr.uncomp_size >> 24);
442                 } else {
443                         priv->u.trailer32[0] = priv->hdr.crc32;
444                         priv->u.trailer32[1] = priv->hdr.uncomp_size;
445                 }
446
447                 *flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
448                 priv->fop_fd.flags = *flags;
449                 priv->add_gzip_container = 1;
450
451                 return &priv->fop_fd;
452         }
453
454         if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
455
456                 /* we must decompress it to serve it */
457
458                 lwsl_info("decompressed zip serving\n");
459
460                 priv->fop_fd.len = priv->hdr.uncomp_size;
461
462                 if (lws_fops_zip_reset_inflate(priv)) {
463                         lwsl_err("inflate init failed\n");
464                         goto bail2;
465                 }
466
467                 priv->decompress = 1;
468
469                 return &priv->fop_fd;
470         }
471
472         /* we can't handle it ... */
473
474         lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
475                  priv->hdr.method);
476
477 bail2:
478         lws_vfs_file_close(&priv->zip_fop_fd);
479 bail1:
480         free(priv);
481
482         return NULL;
483 }
484
485 /* ie, we are closing the fop_fd for the file inside the gzip */
486
487 static int
488 lws_fops_zip_close(lws_fop_fd_t *fd)
489 {
490         lws_fops_zip_t priv = fop_fd_to_priv(*fd);
491
492         if (priv->decompress)
493                 inflateEnd(&priv->inflate);
494
495         lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
496
497         free(priv);
498         *fd = NULL;
499
500         return 0;
501 }
502
503 static lws_fileofs_t
504 lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
505 {
506         fd->pos += offset_from_cur_pos;
507
508         return fd->pos;
509 }
510
511 static int
512 lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
513                   lws_filepos_t len)
514 {
515         lws_fops_zip_t priv = fop_fd_to_priv(fd);
516         lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
517         int ret;
518
519         if (priv->decompress) {
520
521                 if (priv->exp_uncomp_pos != fd->pos) {
522                         /*
523                          *  there has been a seek in the uncompressed fop_fd
524                          * we have to restart the decompression and loop eating
525                          * the decompressed data up to the seek point
526                          */
527                         lwsl_info("seek in decompressed\n");
528
529                         lws_fops_zip_reset_inflate(priv);
530
531                         while (priv->exp_uncomp_pos != fd->pos) {
532                                 rlen = len;
533                                 if (rlen > fd->pos - priv->exp_uncomp_pos)
534                                         rlen = fd->pos - priv->exp_uncomp_pos;
535                                 if (lws_fops_zip_read(fd, amount, buf, rlen))
536                                         return LWS_FZ_ERR_SEEK_COMPRESSED;
537                         }
538                         *amount = 0;
539                 }
540
541                 priv->inflate.avail_out = (unsigned int)len;
542                 priv->inflate.next_out = buf;
543
544 spin:
545                 if (!priv->inflate.avail_in) {
546                         rlen = sizeof(priv->rbuf);
547                         if (rlen > priv->hdr.comp_size -
548                                    (cur - priv->content_start))
549                                 rlen = priv->hdr.comp_size -
550                                        (priv->hdr.comp_size -
551                                         priv->content_start);
552
553                         if (priv->zip_fop_fd->fops->LWS_FOP_READ(
554                                         priv->zip_fop_fd, &ramount, priv->rbuf,
555                                         rlen))
556                                 return LWS_FZ_ERR_READ_CONTENT;
557
558                         cur += ramount;
559
560                         priv->inflate.avail_in = (unsigned int)ramount;
561                         priv->inflate.next_in = priv->rbuf;
562                 }
563
564                 ret = inflate(&priv->inflate, Z_NO_FLUSH);
565                 if (ret == Z_STREAM_ERROR)
566                         return ret;
567
568                 switch (ret) {
569                 case Z_NEED_DICT:
570                         ret = Z_DATA_ERROR;
571                         /* fallthru */
572                 case Z_DATA_ERROR:
573                 case Z_MEM_ERROR:
574
575                         return ret;
576                 }
577
578                 if (!priv->inflate.avail_in && priv->inflate.avail_out &&
579                      cur != priv->content_start + priv->hdr.comp_size)
580                         goto spin;
581
582                 *amount = len - priv->inflate.avail_out;
583
584                 priv->exp_uncomp_pos += *amount;
585                 fd->pos += *amount;
586
587                 return 0;
588         }
589
590         if (priv->add_gzip_container) {
591
592                 lwsl_info("%s: gzip + container\n", __func__);
593                 *amount = 0;
594
595                 /* place the canned header at the start */
596
597                 if (len && fd->pos < sizeof(hd)) {
598                         rlen = sizeof(hd) - fd->pos;
599                         if (rlen > len)
600                                 rlen = len;
601                         /* provide stuff from canned header */
602                         memcpy(buf, hd + fd->pos, (size_t)rlen);
603                         fd->pos += rlen;
604                         buf += rlen;
605                         len -= rlen;
606                         *amount += rlen;
607                 }
608
609                 /* serve gzipped data direct from zipfile */
610
611                 if (len && fd->pos >= sizeof(hd) &&
612                     fd->pos < priv->hdr.comp_size + sizeof(hd)) {
613
614                         rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos -
615                                                       priv->content_start);
616                         if (rlen > len)
617                                 rlen = len;
618
619                         if (rlen &&
620                             priv->zip_fop_fd->pos < (priv->hdr.comp_size +
621                                                      priv->content_start)) {
622                                 if (lws_vfs_file_read(priv->zip_fop_fd,
623                                                       &ramount, buf, rlen))
624                                         return LWS_FZ_ERR_READ_CONTENT;
625                                 *amount += ramount;
626                                 fd->pos += ramount; // virtual pos
627                                 buf += ramount;
628                                 len -= ramount;
629                         }
630                 }
631
632                 /* place the prepared trailer at the end */
633
634                 if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) &&
635                     fd->pos < priv->hdr.comp_size + sizeof(hd) +
636                               sizeof(priv->u)) {
637                         cur = fd->pos - priv->hdr.comp_size - sizeof(hd);
638                         rlen = sizeof(priv->u) - cur;
639                         if (rlen > len)
640                                 rlen = len;
641
642                         memcpy(buf, priv->u.trailer8 + cur, (size_t)rlen);
643
644                         *amount += rlen;
645                         fd->pos += rlen;
646                 }
647
648                 return 0;
649         }
650
651         lwsl_info("%s: store\n", __func__);
652
653         if (len > priv->hdr.uncomp_size - (cur - priv->content_start))
654                 len = priv->hdr.comp_size - (priv->hdr.comp_size -
655                                              priv->content_start);
656
657         if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
658                                                  amount, buf, len))
659                 return LWS_FZ_ERR_READ_CONTENT;
660
661         return 0;
662 }
663
664 struct lws_plat_file_ops fops_zip = {
665         lws_fops_zip_open,
666         lws_fops_zip_close,
667         lws_fops_zip_seek_cur,
668         lws_fops_zip_read,
669         NULL,
670         { { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
671         NULL,
672 };