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 56typedef 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 69typedef 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 89struct lws_plat_file_ops fops_zip; 90#define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD)) 91 92static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 }; 93 94enum { 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 150static uint16_t 151get_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 158static uint32_t 159get_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 166int 167lws_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 279next: 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 292static int 293lws_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 317static lws_fop_fd_t 318lws_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 462bail2: 463 lws_vfs_file_close(&priv->zip_fop_fd); 464bail1: 465 free(priv); 466 467 return NULL; 468} 469 470/* ie, we are closing the fop_fd for the file inside the gzip */ 471 472static int 473lws_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 488static lws_fileofs_t 489lws_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 496static int 497lws_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 529spin: 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 647struct 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