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 static uint16_t
get_u16(void * p)148 get_u16(void *p)
149 {
150 const uint8_t *c = (const uint8_t *)p;
151
152 return (uint16_t)((c[0] | (c[1] << 8)));
153 }
154
155 static uint32_t
get_u32(void * p)156 get_u32(void *p)
157 {
158 const uint8_t *c = (const uint8_t *)p;
159
160 return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
161 }
162
163 int
lws_fops_zip_scan(lws_fops_zip_t priv,const char * name,int len)164 lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
165 {
166 lws_filepos_t amount;
167 uint8_t buf[96];
168 int i;
169
170 if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0)
171 return LWS_FZ_ERR_SEEK_END_RECORD;
172
173 if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
174 ZE_DIRECTORY_LENGTH))
175 return LWS_FZ_ERR_READ_END_RECORD;
176
177 if (amount != ZE_DIRECTORY_LENGTH)
178 return LWS_FZ_ERR_READ_END_RECORD;
179
180 /*
181 * We require the zip to have the last record right at the end
182 * Linux zip always does this if no zip comment.
183 */
184 if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6)
185 return LWS_FZ_ERR_END_RECORD_MAGIC;
186
187 i = get_u16(buf + ZE_NUM_ENTRIES);
188
189 if (get_u16(buf + ZE_DESK_NUMBER) ||
190 get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
191 i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK))
192 return LWS_FZ_ERR_END_RECORD_SANITY;
193
194 /* end record is OK... look for our file in the central dir */
195
196 if (lws_vfs_file_seek_set(priv->zip_fop_fd,
197 get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0)
198 return LWS_FZ_ERR_CENTRAL_SEEK;
199
200 while (i--) {
201 priv->content_start = lws_vfs_tell(priv->zip_fop_fd);
202
203 if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
204 ZC_DIRECTORY_LENGTH))
205 return LWS_FZ_ERR_CENTRAL_READ;
206
207 if (amount != ZC_DIRECTORY_LENGTH)
208 return LWS_FZ_ERR_CENTRAL_READ;
209
210 if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50)
211 return LWS_FZ_ERR_CENTRAL_SANITY;
212
213 lwsl_debug("cstart 0x%lx\n", (unsigned long)priv->content_start);
214
215 priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH);
216 priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH);
217 priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd);
218
219 priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD);
220 priv->hdr.crc32 = get_u32(buf + ZC_CRC32);
221 priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE);
222 priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE);
223 priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER);
224 priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME);
225 priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH);
226
227 if (priv->hdr.filename_len != len)
228 goto next;
229
230 if (len >= (int)sizeof(buf) - 1)
231 return LWS_FZ_ERR_NAME_TOO_LONG;
232
233 if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
234 &amount, buf, len))
235 return LWS_FZ_ERR_NAME_READ;
236 if ((int)amount != len)
237 return LWS_FZ_ERR_NAME_READ;
238
239 buf[len] = '\0';
240 lwsl_debug("check %s vs %s\n", buf, name);
241
242 if (strcmp((const char *)buf, name))
243 goto next;
244
245 /* we found a match */
246 if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset) < 0)
247 return LWS_FZ_ERR_NAME_SEEK;
248 if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
249 &amount, buf,
250 ZL_HEADER_LENGTH))
251 return LWS_FZ_ERR_NAME_READ;
252 if (amount != ZL_HEADER_LENGTH)
253 return LWS_FZ_ERR_NAME_READ;
254
255 priv->content_start = priv->hdr.offset +
256 ZL_HEADER_LENGTH +
257 priv->hdr.filename_len +
258 get_u16(buf + ZL_REL_OFFSET_CONTENT);
259
260 lwsl_debug("content supposed to start at 0x%lx\n",
261 (unsigned long)priv->content_start);
262
263 if (priv->content_start > priv->zip_fop_fd->len)
264 return LWS_FZ_ERR_CONTENT_SANITY;
265
266 if (lws_vfs_file_seek_set(priv->zip_fop_fd,
267 priv->content_start) < 0)
268 return LWS_FZ_ERR_CONTENT_SEEK;
269
270 /* we are aligned at the start of the content */
271
272 priv->exp_uncomp_pos = 0;
273
274 return 0;
275
276 next:
277 if (i && lws_vfs_file_seek_set(priv->zip_fop_fd,
278 priv->content_start +
279 ZC_DIRECTORY_LENGTH +
280 priv->hdr.filename_len +
281 priv->hdr.extra +
282 priv->hdr.file_com_len) < 0)
283 return LWS_FZ_ERR_SCAN_SEEK;
284 }
285
286 return LWS_FZ_ERR_NOT_FOUND;
287 }
288
289 static int
lws_fops_zip_reset_inflate(lws_fops_zip_t priv)290 lws_fops_zip_reset_inflate(lws_fops_zip_t priv)
291 {
292 if (priv->decompress)
293 inflateEnd(&priv->inflate);
294
295 priv->inflate.zalloc = Z_NULL;
296 priv->inflate.zfree = Z_NULL;
297 priv->inflate.opaque = Z_NULL;
298 priv->inflate.avail_in = 0;
299 priv->inflate.next_in = Z_NULL;
300
301 if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) {
302 lwsl_err("inflate init failed\n");
303 return LWS_FZ_ERR_ZLIB_INIT;
304 }
305
306 if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->content_start) < 0)
307 return LWS_FZ_ERR_CONTENT_SEEK;
308
309 priv->exp_uncomp_pos = 0;
310
311 return 0;
312 }
313
314 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)315 lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
316 const char *vpath, lws_fop_flags_t *flags)
317 {
318 lws_fop_flags_t local_flags = 0;
319 lws_fops_zip_t priv;
320 char rp[192];
321 int m;
322
323 /*
324 * vpath points at the / after the fops signature in vfs_path, eg
325 * with a vfs_path "/var/www/docs/manual.zip/index.html", vpath
326 * will come pointing at "/index.html"
327 */
328
329 priv = lws_zalloc(sizeof(*priv), "fops_zip priv");
330 if (!priv)
331 return NULL;
332
333 priv->fop_fd.fops = &fops_zip;
334
335 m = sizeof(rp) - 1;
336 if ((vpath - vfs_path - 1) < m)
337 m = lws_ptr_diff(vpath, vfs_path) - 1;
338 lws_strncpy(rp, vfs_path, m + 1);
339
340 /* open the zip file itself using the incoming fops, not fops_zip */
341
342 priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags);
343 if (!priv->zip_fop_fd) {
344 lwsl_err("unable to open zip %s\n", rp);
345 goto bail1;
346 }
347
348 if (*vpath == '/')
349 vpath++;
350
351 m = lws_fops_zip_scan(priv, vpath, (int)strlen(vpath));
352 if (m) {
353 lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
354 goto bail2;
355 }
356
357 /* the directory metadata tells us modification time, so pass it on */
358 priv->fop_fd.mod_time = priv->hdr.mod_time;
359 *flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
360 priv->fop_fd.flags = *flags;
361
362 /* The zip fop_fd is left pointing at the start of the content.
363 *
364 * 1) Content could be uncompressed (STORE), and we can always serve
365 * that directly
366 *
367 * 2) Content could be compressed (GZIP), and the client can handle
368 * receiving GZIP... we can wrap it in a GZIP header and trailer
369 * and serve the content part directly. The flag indicating we
370 * are providing GZIP directly is set so lws will send the right
371 * headers.
372 *
373 * 3) Content could be compressed (GZIP) but the client can't handle
374 * receiving GZIP... we can decompress it and serve as it is
375 * inflated piecemeal.
376 *
377 * 4) Content may be compressed some unknown way... fail
378 *
379 */
380 if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
381 /*
382 * it is stored uncompressed, leave it indicated as
383 * uncompressed, and just serve it from inside the
384 * zip with no gzip container;
385 */
386
387 lwsl_info("direct zip serving (stored)\n");
388
389 priv->fop_fd.len = priv->hdr.uncomp_size;
390
391 return &priv->fop_fd;
392 }
393
394 if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
395 priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
396
397 /*
398 * We can serve the gzipped file contents directly as gzip
399 * from inside the zip container; client says it is OK.
400 *
401 * To convert to standalone gzip, we have to add a 10-byte
402 * constant header and a variable 8-byte trailer around the
403 * content.
404 *
405 * The 8-byte trailer is prepared now and held in the priv.
406 */
407
408 lwsl_info("direct zip serving (gzipped)\n");
409
410 priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
411 sizeof(priv->u);
412
413 if (lws_is_be()) {
414 uint8_t *p = priv->u.trailer8;
415
416 *p++ = (uint8_t)priv->hdr.crc32;
417 *p++ = (uint8_t)(priv->hdr.crc32 >> 8);
418 *p++ = (uint8_t)(priv->hdr.crc32 >> 16);
419 *p++ = (uint8_t)(priv->hdr.crc32 >> 24);
420 *p++ = (uint8_t)priv->hdr.uncomp_size;
421 *p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
422 *p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
423 *p = (uint8_t)(priv->hdr.uncomp_size >> 24);
424 } else {
425 priv->u.trailer32[0] = priv->hdr.crc32;
426 priv->u.trailer32[1] = priv->hdr.uncomp_size;
427 }
428
429 *flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
430 priv->fop_fd.flags = *flags;
431 priv->add_gzip_container = 1;
432
433 return &priv->fop_fd;
434 }
435
436 if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
437
438 /* we must decompress it to serve it */
439
440 lwsl_info("decompressed zip serving\n");
441
442 priv->fop_fd.len = priv->hdr.uncomp_size;
443
444 if (lws_fops_zip_reset_inflate(priv)) {
445 lwsl_err("inflate init failed\n");
446 goto bail2;
447 }
448
449 priv->decompress = 1;
450
451 return &priv->fop_fd;
452 }
453
454 /* we can't handle it ... */
455
456 lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
457 priv->hdr.method);
458
459 bail2:
460 lws_vfs_file_close(&priv->zip_fop_fd);
461 bail1:
462 free(priv);
463
464 return NULL;
465 }
466
467 /* ie, we are closing the fop_fd for the file inside the gzip */
468
469 static int
lws_fops_zip_close(lws_fop_fd_t * fd)470 lws_fops_zip_close(lws_fop_fd_t *fd)
471 {
472 lws_fops_zip_t priv = fop_fd_to_priv(*fd);
473
474 if (priv->decompress)
475 inflateEnd(&priv->inflate);
476
477 lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
478
479 free(priv);
480 *fd = NULL;
481
482 return 0;
483 }
484
485 static lws_fileofs_t
lws_fops_zip_seek_cur(lws_fop_fd_t fd,lws_fileofs_t offset_from_cur_pos)486 lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
487 {
488 fd->pos += offset_from_cur_pos;
489
490 return fd->pos;
491 }
492
493 static int
lws_fops_zip_read(lws_fop_fd_t fd,lws_filepos_t * amount,uint8_t * buf,lws_filepos_t len)494 lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
495 lws_filepos_t len)
496 {
497 lws_fops_zip_t priv = fop_fd_to_priv(fd);
498 lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
499 int ret;
500
501 if (priv->decompress) {
502
503 if (priv->exp_uncomp_pos != fd->pos) {
504 /*
505 * there has been a seek in the uncompressed fop_fd
506 * we have to restart the decompression and loop eating
507 * the decompressed data up to the seek point
508 */
509 lwsl_info("seek in decompressed\n");
510
511 lws_fops_zip_reset_inflate(priv);
512
513 while (priv->exp_uncomp_pos != fd->pos) {
514 rlen = len;
515 if (rlen > fd->pos - priv->exp_uncomp_pos)
516 rlen = fd->pos - priv->exp_uncomp_pos;
517 if (lws_fops_zip_read(fd, amount, buf, rlen))
518 return LWS_FZ_ERR_SEEK_COMPRESSED;
519 }
520 *amount = 0;
521 }
522
523 priv->inflate.avail_out = (unsigned int)len;
524 priv->inflate.next_out = buf;
525
526 spin:
527 if (!priv->inflate.avail_in) {
528 rlen = sizeof(priv->rbuf);
529 if (rlen > priv->hdr.comp_size -
530 (cur - priv->content_start))
531 rlen = priv->hdr.comp_size -
532 (priv->hdr.comp_size -
533 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 > priv->hdr.uncomp_size - (cur - priv->content_start))
636 len = priv->hdr.comp_size - (priv->hdr.comp_size -
637 priv->content_start);
638
639 if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
640 amount, buf, len))
641 return LWS_FZ_ERR_READ_CONTENT;
642
643 return 0;
644 }
645
646 struct lws_plat_file_ops fops_zip = {
647 lws_fops_zip_open,
648 lws_fops_zip_close,
649 lws_fops_zip_seek_cur,
650 lws_fops_zip_read,
651 NULL,
652 { { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
653 NULL,
654 };
655