fops-zip.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  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. #include "private-libwebsockets.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[64];
  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 >= 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 (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));
  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 = vpath - vfs_path - 1;
  292. strncpy(rp, vfs_path, m);
  293. rp[m] = '\0';
  294. /* open the zip file itself using the incoming fops, not fops_zip */
  295. priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags);
  296. if (!priv->zip_fop_fd) {
  297. lwsl_err("unable to open zip %s\n", rp);
  298. goto bail1;
  299. }
  300. if (*vpath == '/')
  301. vpath++;
  302. m = lws_fops_zip_scan(priv, vpath, strlen(vpath));
  303. if (m) {
  304. lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
  305. goto bail2;
  306. }
  307. /* the directory metadata tells us modification time, so pass it on */
  308. priv->fop_fd.mod_time = priv->hdr.mod_time;
  309. *flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
  310. priv->fop_fd.flags = *flags;
  311. /* The zip fop_fd is left pointing at the start of the content.
  312. *
  313. * 1) Content could be uncompressed (STORE), and we can always serve
  314. * that directly
  315. *
  316. * 2) Content could be compressed (GZIP), and the client can handle
  317. * receiving GZIP... we can wrap it in a GZIP header and trailer
  318. * and serve the content part directly. The flag indicating we
  319. * are providing GZIP directly is set so lws will send the right
  320. * headers.
  321. *
  322. * 3) Content could be compressed (GZIP) but the client can't handle
  323. * receiving GZIP... we can decompress it and serve as it is
  324. * inflated piecemeal.
  325. *
  326. * 4) Content may be compressed some unknown way... fail
  327. *
  328. */
  329. if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
  330. /*
  331. * it is stored uncompressed, leave it indicated as
  332. * uncompressed, and just serve it from inside the
  333. * zip with no gzip container;
  334. */
  335. lwsl_info("direct zip serving (stored)\n");
  336. priv->fop_fd.len = priv->hdr.uncomp_size;
  337. return &priv->fop_fd;
  338. }
  339. if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
  340. priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
  341. /*
  342. * We can serve the gzipped file contents directly as gzip
  343. * from inside the zip container; client says it is OK.
  344. *
  345. * To convert to standalone gzip, we have to add a 10-byte
  346. * constant header and a variable 8-byte trailer around the
  347. * content.
  348. *
  349. * The 8-byte trailer is prepared now and held in the priv.
  350. */
  351. lwsl_info("direct zip serving (gzipped)\n");
  352. priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
  353. sizeof(priv->u);
  354. if (lws_is_be()) {
  355. uint8_t *p = priv->u.trailer8;
  356. *p++ = (uint8_t)priv->hdr.crc32;
  357. *p++ = (uint8_t)(priv->hdr.crc32 >> 8);
  358. *p++ = (uint8_t)(priv->hdr.crc32 >> 16);
  359. *p++ = (uint8_t)(priv->hdr.crc32 >> 24);
  360. *p++ = (uint8_t)priv->hdr.uncomp_size;
  361. *p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
  362. *p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
  363. *p = (uint8_t)(priv->hdr.uncomp_size >> 24);
  364. } else {
  365. priv->u.trailer32[0] = priv->hdr.crc32;
  366. priv->u.trailer32[1] = priv->hdr.uncomp_size;
  367. }
  368. *flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
  369. priv->fop_fd.flags = *flags;
  370. priv->add_gzip_container = 1;
  371. return &priv->fop_fd;
  372. }
  373. if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
  374. /* we must decompress it to serve it */
  375. lwsl_info("decompressed zip serving\n");
  376. priv->fop_fd.len = priv->hdr.uncomp_size;
  377. if (lws_fops_zip_reset_inflate(priv)) {
  378. lwsl_err("inflate init failed\n");
  379. goto bail2;
  380. }
  381. priv->decompress = 1;
  382. return &priv->fop_fd;
  383. }
  384. /* we can't handle it ... */
  385. lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
  386. priv->hdr.method);
  387. bail2:
  388. lws_vfs_file_close(&priv->zip_fop_fd);
  389. bail1:
  390. free(priv);
  391. return NULL;
  392. }
  393. /* ie, we are closing the fop_fd for the file inside the gzip */
  394. static int
  395. lws_fops_zip_close(lws_fop_fd_t *fd)
  396. {
  397. lws_fops_zip_t priv = fop_fd_to_priv(*fd);
  398. if (priv->decompress)
  399. inflateEnd(&priv->inflate);
  400. lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
  401. free(priv);
  402. *fd = NULL;
  403. return 0;
  404. }
  405. static lws_fileofs_t
  406. lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
  407. {
  408. fd->pos += offset_from_cur_pos;
  409. return fd->pos;
  410. }
  411. static int
  412. lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
  413. lws_filepos_t len)
  414. {
  415. lws_fops_zip_t priv = fop_fd_to_priv(fd);
  416. lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
  417. int ret;
  418. if (priv->decompress) {
  419. if (priv->exp_uncomp_pos != fd->pos) {
  420. /*
  421. * there has been a seek in the uncompressed fop_fd
  422. * we have to restart the decompression and loop eating
  423. * the decompressed data up to the seek point
  424. */
  425. lwsl_info("seek in decompressed\n");
  426. lws_fops_zip_reset_inflate(priv);
  427. while (priv->exp_uncomp_pos != fd->pos) {
  428. rlen = len;
  429. if (rlen > fd->pos - priv->exp_uncomp_pos)
  430. rlen = fd->pos - priv->exp_uncomp_pos;
  431. if (lws_fops_zip_read(fd, amount, buf, rlen))
  432. return LWS_FZ_ERR_SEEK_COMPRESSED;
  433. }
  434. *amount = 0;
  435. }
  436. priv->inflate.avail_out = len;
  437. priv->inflate.next_out = buf;
  438. spin:
  439. if (!priv->inflate.avail_in) {
  440. rlen = sizeof(priv->rbuf);
  441. if (rlen > priv->hdr.comp_size -
  442. (cur - priv->content_start))
  443. rlen = priv->hdr.comp_size -
  444. (priv->hdr.comp_size -
  445. priv->content_start);
  446. if (priv->zip_fop_fd->fops->LWS_FOP_READ(
  447. priv->zip_fop_fd, &ramount, priv->rbuf,
  448. rlen))
  449. return LWS_FZ_ERR_READ_CONTENT;
  450. cur += ramount;
  451. priv->inflate.avail_in = ramount;
  452. priv->inflate.next_in = priv->rbuf;
  453. }
  454. ret = inflate(&priv->inflate, Z_NO_FLUSH);
  455. if (ret == Z_STREAM_ERROR)
  456. return ret;
  457. switch (ret) {
  458. case Z_NEED_DICT:
  459. ret = Z_DATA_ERROR;
  460. /* and fall through */
  461. case Z_DATA_ERROR:
  462. case Z_MEM_ERROR:
  463. return ret;
  464. }
  465. if (!priv->inflate.avail_in && priv->inflate.avail_out &&
  466. cur != priv->content_start + priv->hdr.comp_size)
  467. goto spin;
  468. *amount = len - priv->inflate.avail_out;
  469. priv->exp_uncomp_pos += *amount;
  470. fd->pos += *amount;
  471. return 0;
  472. }
  473. if (priv->add_gzip_container) {
  474. lwsl_info("%s: gzip + container\n", __func__);
  475. *amount = 0;
  476. /* place the canned header at the start */
  477. if (len && fd->pos < sizeof(hd)) {
  478. rlen = sizeof(hd) - fd->pos;
  479. if (rlen > len)
  480. rlen = len;
  481. /* provide stuff from canned header */
  482. memcpy(buf, hd + fd->pos, rlen);
  483. fd->pos += rlen;
  484. buf += rlen;
  485. len -= rlen;
  486. *amount += rlen;
  487. }
  488. /* serve gzipped data direct from zipfile */
  489. if (len && fd->pos >= sizeof(hd) &&
  490. fd->pos < priv->hdr.comp_size + sizeof(hd)) {
  491. rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos -
  492. priv->content_start);
  493. if (rlen > len)
  494. rlen = len;
  495. if (rlen &&
  496. priv->zip_fop_fd->pos < (priv->hdr.comp_size +
  497. priv->content_start)) {
  498. if (lws_vfs_file_read(priv->zip_fop_fd,
  499. &ramount, buf, rlen))
  500. return LWS_FZ_ERR_READ_CONTENT;
  501. *amount += ramount;
  502. fd->pos += ramount; // virtual pos
  503. buf += ramount;
  504. len -= ramount;
  505. }
  506. }
  507. /* place the prepared trailer at the end */
  508. if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) &&
  509. fd->pos < priv->hdr.comp_size + sizeof(hd) +
  510. sizeof(priv->u)) {
  511. cur = fd->pos - priv->hdr.comp_size - sizeof(hd);
  512. rlen = sizeof(priv->u) - cur;
  513. if (rlen > len)
  514. rlen = len;
  515. memcpy(buf, priv->u.trailer8 + cur, rlen);
  516. *amount += rlen;
  517. fd->pos += rlen;
  518. }
  519. return 0;
  520. }
  521. lwsl_info("%s: store\n", __func__);
  522. if (len > priv->hdr.uncomp_size - (cur - priv->content_start))
  523. len = priv->hdr.comp_size - (priv->hdr.comp_size -
  524. priv->content_start);
  525. if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
  526. amount, buf, len))
  527. return LWS_FZ_ERR_READ_CONTENT;
  528. return 0;
  529. }
  530. struct lws_plat_file_ops fops_zip = {
  531. lws_fops_zip_open,
  532. lws_fops_zip_close,
  533. lws_fops_zip_seek_cur,
  534. lws_fops_zip_read,
  535. NULL,
  536. { { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
  537. NULL,
  538. };