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