fops-zip.c 17 KB

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