VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/zip/gzipvfs.cpp@ 64883

Last change on this file since 64883 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.0 KB
Line 
1/* $Id: gzipvfs.cpp 62477 2016-07-22 18:27:37Z vboxsync $ */
2/** @file
3 * IPRT - GZIP Compressor and Decompressor I/O Stream.
4 */
5
6/*
7 * Copyright (C) 2010-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Defined Constants And Macros *
30*********************************************************************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/zip.h>
33
34#include <iprt/assert.h>
35#include <iprt/file.h>
36#include <iprt/err.h>
37#include <iprt/poll.h>
38#include <iprt/string.h>
39#include <iprt/vfslowlevel.h>
40
41#include <zlib.h>
42
43#if defined(RT_OS_OS2) || defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS)
44/**
45 * Drag in the missing zlib symbols.
46 */
47PFNRT g_apfnRTZlibDeps[] =
48{
49 (PFNRT)gzrewind,
50 (PFNRT)gzread,
51 (PFNRT)gzopen,
52 (PFNRT)gzwrite,
53 (PFNRT)gzclose,
54 (PFNRT)gzdopen,
55 NULL
56};
57#endif /* RT_OS_OS2 || RT_OS_SOLARIS || RT_OS_WINDOWS */
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63#pragma pack(1)
64typedef struct RTZIPGZIPHDR
65{
66 /** RTZIPGZIPHDR_ID1. */
67 uint8_t bId1;
68 /** RTZIPGZIPHDR_ID2. */
69 uint8_t bId2;
70 /** CM - The compression method. */
71 uint8_t bCompressionMethod;
72 /** FLG - Flags. */
73 uint8_t fFlags;
74 /** Modification time of the source file or the timestamp at the time the
75 * compression took place. Can also be zero. Is the number of seconds since
76 * unix epoch. */
77 uint32_t u32ModTime;
78 /** Flags specific to the compression method. */
79 uint8_t bXtraFlags;
80 /** An ID indicating which OS or FS gzip ran on. */
81 uint8_t bOS;
82} RTZIPGZIPHDR;
83#pragma pack()
84AssertCompileSize(RTZIPGZIPHDR, 10);
85/** Pointer to a const gzip header. */
86typedef RTZIPGZIPHDR const *PCRTZIPGZIPHDR;
87
88/** gzip header identification no 1. */
89#define RTZIPGZIPHDR_ID1 0x1f
90/** gzip header identification no 2. */
91#define RTZIPGZIPHDR_ID2 0x8b
92/** gzip deflate compression method. */
93#define RTZIPGZIPHDR_CM_DEFLATE 8
94
95/** @name gzip header flags
96 * @{ */
97/** Probably a text file */
98#define RTZIPGZIPHDR_FLG_TEXT UINT8_C(0x01)
99/** Header CRC present (crc32 of header cast to uint16_t). */
100#define RTZIPGZIPHDR_FLG_HDR_CRC UINT8_C(0x02)
101/** Length prefixed xtra field is present. */
102#define RTZIPGZIPHDR_FLG_EXTRA UINT8_C(0x04)
103/** A name field is present (latin-1). */
104#define RTZIPGZIPHDR_FLG_NAME UINT8_C(0x08)
105/** A comment field is present (latin-1). */
106#define RTZIPGZIPHDR_FLG_COMMENT UINT8_C(0x10)
107/** Mask of valid flags. */
108#define RTZIPGZIPHDR_FLG_VALID_MASK UINT8_C(0x1f)
109/** @} */
110
111/** @name gzip default xtra flag values
112 * @{ */
113#define RTZIPGZIPHDR_XFL_DEFLATE_MAX UINT8_C(0x02)
114#define RTZIPGZIPHDR_XFL_DEFLATE_FASTEST UINT8_C(0x04)
115/** @} */
116
117/** @name Operating system / Filesystem IDs
118 * @{ */
119#define RTZIPGZIPHDR_OS_FAT UINT8_C(0x00)
120#define RTZIPGZIPHDR_OS_AMIGA UINT8_C(0x01)
121#define RTZIPGZIPHDR_OS_VMS UINT8_C(0x02)
122#define RTZIPGZIPHDR_OS_UNIX UINT8_C(0x03)
123#define RTZIPGZIPHDR_OS_VM_CMS UINT8_C(0x04)
124#define RTZIPGZIPHDR_OS_ATARIS_TOS UINT8_C(0x05)
125#define RTZIPGZIPHDR_OS_HPFS UINT8_C(0x06)
126#define RTZIPGZIPHDR_OS_MACINTOSH UINT8_C(0x07)
127#define RTZIPGZIPHDR_OS_Z_SYSTEM UINT8_C(0x08)
128#define RTZIPGZIPHDR_OS_CPM UINT8_C(0x09)
129#define RTZIPGZIPHDR_OS_TOPS_20 UINT8_C(0x0a)
130#define RTZIPGZIPHDR_OS_NTFS UINT8_C(0x0b)
131#define RTZIPGZIPHDR_OS_QDOS UINT8_C(0x0c)
132#define RTZIPGZIPHDR_OS_ACORN_RISCOS UINT8_C(0x0d)
133#define RTZIPGZIPHDR_OS_UNKNOWN UINT8_C(0xff)
134/** @} */
135
136
137/**
138 * The internal data of a GZIP I/O stream.
139 */
140typedef struct RTZIPGZIPSTREAM
141{
142 /** The stream we're reading or writing the compressed data from or to. */
143 RTVFSIOSTREAM hVfsIos;
144 /** Set if it's a decompressor, clear if it's a compressor. */
145 bool fDecompress;
146 /** Set if zlib reported a fatal error. */
147 bool fFatalError;
148 /** Set if we've reached the end of the zlib stream. */
149 bool fEndOfStream;
150 /** The stream offset for pfnTell, always the uncompressed data. */
151 RTFOFF offStream;
152 /** The zlib stream. */
153 z_stream Zlib;
154 /** The data buffer. */
155 uint8_t abBuffer[_64K];
156 /** Scatter gather segment describing abBuffer. */
157 RTSGSEG SgSeg;
158 /** Scatter gather buffer describing abBuffer. */
159 RTSGBUF SgBuf;
160 /** The original file name (decompressor only). */
161 char *pszOrgName;
162 /** The comment (decompressor only). */
163 char *pszComment;
164 /** The gzip header. */
165 RTZIPGZIPHDR Hdr;
166} RTZIPGZIPSTREAM;
167/** Pointer to a the internal data of a GZIP I/O stream. */
168typedef RTZIPGZIPSTREAM *PRTZIPGZIPSTREAM;
169
170
171/*********************************************************************************************************************************
172* Internal Functions *
173*********************************************************************************************************************************/
174static int rtZipGzip_FlushIt(PRTZIPGZIPSTREAM pThis, uint8_t fFlushType);
175
176
177/**
178 * Convert from zlib to IPRT status codes.
179 *
180 * This will also set the fFatalError flag when appropriate.
181 *
182 * @returns IPRT status code.
183 * @param pThis The gzip I/O stream instance data.
184 * @param rc Zlib error code.
185 */
186static int rtZipGzipConvertErrFromZlib(PRTZIPGZIPSTREAM pThis, int rc)
187{
188 switch (rc)
189 {
190 case Z_OK:
191 return VINF_SUCCESS;
192
193 case Z_BUF_ERROR:
194 /* This isn't fatal. */
195 return VINF_SUCCESS; /** @todo The code in zip.cpp treats Z_BUF_ERROR as fatal... */
196
197 case Z_STREAM_ERROR:
198 pThis->fFatalError = true;
199 return VERR_ZIP_CORRUPTED;
200
201 case Z_DATA_ERROR:
202 pThis->fFatalError = true;
203 return pThis->fDecompress ? VERR_ZIP_CORRUPTED : VERR_ZIP_ERROR;
204
205 case Z_MEM_ERROR:
206 pThis->fFatalError = true;
207 return VERR_ZIP_NO_MEMORY;
208
209 case Z_VERSION_ERROR:
210 pThis->fFatalError = true;
211 return VERR_ZIP_UNSUPPORTED_VERSION;
212
213 case Z_ERRNO: /* We shouldn't see this status! */
214 default:
215 AssertMsgFailed(("%d\n", rc));
216 if (rc >= 0)
217 return VINF_SUCCESS;
218 pThis->fFatalError = true;
219 return VERR_ZIP_ERROR;
220 }
221}
222
223
224/**
225 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
226 */
227static DECLCALLBACK(int) rtZipGzip_Close(void *pvThis)
228{
229 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
230
231 int rc;
232 if (pThis->fDecompress)
233 {
234 rc = inflateEnd(&pThis->Zlib);
235 if (rc != Z_OK)
236 rc = rtZipGzipConvertErrFromZlib(pThis, rc);
237 }
238 else
239 {
240 /* Flush the compression stream before terminating it. */
241 rc = VINF_SUCCESS;
242 if (!pThis->fFatalError)
243 rc = rtZipGzip_FlushIt(pThis, Z_FINISH);
244
245 int rc2 = deflateEnd(&pThis->Zlib);
246 if (RT_SUCCESS(rc) && rc2 != Z_OK)
247 rc = rtZipGzipConvertErrFromZlib(pThis, rc);
248 }
249
250 RTVfsIoStrmRelease(pThis->hVfsIos);
251 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
252 RTStrFree(pThis->pszOrgName);
253 pThis->pszOrgName = NULL;
254 RTStrFree(pThis->pszComment);
255 pThis->pszComment = NULL;
256
257 return rc;
258}
259
260
261/**
262 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
263 */
264static DECLCALLBACK(int) rtZipGzip_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
265{
266 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
267 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
268}
269
270
271/**
272 * Reads one segment.
273 *
274 * @returns IPRT status code.
275 * @param pThis The gzip I/O stream instance data.
276 * @param pvBuf Where to put the read bytes.
277 * @param cbToRead The number of bytes to read.
278 * @param fBlocking Whether to block or not.
279 * @param pcbRead Where to store the number of bytes actually read.
280 */
281static int rtZipGzip_ReadOneSeg(PRTZIPGZIPSTREAM pThis, void *pvBuf, size_t cbToRead, bool fBlocking, size_t *pcbRead)
282{
283 /*
284 * This simplifies life a wee bit below.
285 */
286 if (pThis->fEndOfStream)
287 return pcbRead ? VINF_EOF : VERR_EOF;
288
289 /*
290 * Set up the output buffer.
291 */
292 pThis->Zlib.next_out = (Bytef *)pvBuf;
293 pThis->Zlib.avail_out = (uInt)cbToRead;
294 AssertReturn(pThis->Zlib.avail_out == cbToRead, VERR_OUT_OF_RANGE);
295
296 /*
297 * Be greedy reading input, even if no output buffer is left. It's possible
298 * that it's just the end of stream marker which needs to be read. Happens
299 * for incompressible blocks just larger than the input buffer size.
300 */
301 int rc = VINF_SUCCESS;
302 while ( pThis->Zlib.avail_out > 0
303 || pThis->Zlib.avail_in == 0 /* greedy */)
304 {
305 /*
306 * Read more input?
307 *
308 * N.B. The assertions here validate the RTVfsIoStrmSgRead behavior
309 * since the API is new and untested. They could be removed later
310 * but, better leaving them in.
311 */
312 if (pThis->Zlib.avail_in == 0)
313 {
314 size_t cbReadIn = ~(size_t)0;
315 rc = RTVfsIoStrmSgRead(pThis->hVfsIos, -1 /*off*/, &pThis->SgBuf, fBlocking, &cbReadIn);
316 if (rc != VINF_SUCCESS)
317 {
318 AssertMsg(RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || rc == VINF_EOF, ("%Rrc\n", rc));
319 if (rc == VERR_INTERRUPTED)
320 {
321 Assert(cbReadIn == 0);
322 continue;
323 }
324 if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || cbReadIn == 0)
325 {
326 Assert(cbReadIn == 0);
327 break;
328 }
329 AssertMsg(rc == VINF_EOF, ("%Rrc\n", rc));
330 }
331 AssertMsgBreakStmt(cbReadIn > 0 && cbReadIn <= sizeof(pThis->abBuffer), ("%zu %Rrc\n", cbReadIn, rc),
332 rc = VERR_INTERNAL_ERROR_4);
333
334 pThis->Zlib.avail_in = (uInt)cbReadIn;
335 pThis->Zlib.next_in = &pThis->abBuffer[0];
336 }
337
338 /*
339 * Pass it on to zlib.
340 */
341 rc = inflate(&pThis->Zlib, Z_NO_FLUSH);
342 if (rc != Z_OK && rc != Z_BUF_ERROR)
343 {
344 if (rc == Z_STREAM_END)
345 {
346 pThis->fEndOfStream = true;
347 if (pThis->Zlib.avail_out == 0)
348 rc = VINF_SUCCESS;
349 else
350 rc = pcbRead ? VINF_EOF : VERR_EOF;
351 }
352 else
353 rc = rtZipGzipConvertErrFromZlib(pThis, rc);
354 break;
355 }
356 rc = VINF_SUCCESS;
357 }
358
359 /*
360 * Update the read counters before returning.
361 */
362 size_t const cbRead = cbToRead - pThis->Zlib.avail_out;
363 pThis->offStream += cbRead;
364 if (pcbRead)
365 *pcbRead = cbRead;
366
367 return rc;
368}
369
370
371/**
372 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
373 */
374static DECLCALLBACK(int) rtZipGzip_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
375{
376 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
377
378 Assert(pSgBuf->cSegs == 1);
379 if (!pThis->fDecompress)
380 return VERR_ACCESS_DENIED;
381 AssertReturn(off == -1 || off == pThis->offStream , VERR_INVALID_PARAMETER);
382
383 return rtZipGzip_ReadOneSeg(pThis, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, fBlocking, pcbRead);
384}
385
386
387/**
388 * Internal helper for rtZipGzip_Write, rtZipGzip_Flush and rtZipGzip_Close.
389 *
390 * @returns IPRT status code.
391 * @retval VINF_SUCCESS
392 * @retval VINF_TRY_AGAIN - the only informational status.
393 * @retval VERR_INTERRUPTED - call again.
394 *
395 * @param pThis The gzip I/O stream instance data.
396 * @param fBlocking Whether to block or not.
397 */
398static int rtZipGzip_WriteOutputBuffer(PRTZIPGZIPSTREAM pThis, bool fBlocking)
399{
400 /*
401 * Anything to write? No, then just return immediately.
402 */
403 size_t cbToWrite = sizeof(pThis->abBuffer) - pThis->Zlib.avail_out;
404 if (cbToWrite == 0)
405 {
406 Assert(pThis->Zlib.next_out == &pThis->abBuffer[0]);
407 return VINF_SUCCESS;
408 }
409 Assert(cbToWrite <= sizeof(pThis->abBuffer));
410
411 /*
412 * Loop write on VERR_INTERRUPTED.
413 *
414 * Note! Asserting a bit extra here to make sure the
415 * RTVfsIoStrmSgWrite works correctly.
416 */
417 int rc;
418 size_t cbWrittenOut;
419 for (;;)
420 {
421 /* Set up the buffer. */
422 pThis->SgSeg.cbSeg = cbToWrite;
423 Assert(pThis->SgSeg.pvSeg == &pThis->abBuffer[0]);
424 RTSgBufReset(&pThis->SgBuf);
425
426 cbWrittenOut = ~(size_t)0;
427 rc = RTVfsIoStrmSgWrite(pThis->hVfsIos, -1 /*off*/, &pThis->SgBuf, fBlocking, &cbWrittenOut);
428 if (rc != VINF_SUCCESS)
429 {
430 AssertMsg(RT_FAILURE(rc) || rc == VINF_TRY_AGAIN, ("%Rrc\n", rc));
431 if (rc == VERR_INTERRUPTED)
432 {
433 Assert(cbWrittenOut == 0);
434 continue;
435 }
436 if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || cbWrittenOut == 0)
437 {
438 AssertReturn(cbWrittenOut == 0, VERR_INTERNAL_ERROR_3);
439 AssertReturn(rc != VINF_SUCCESS, VERR_IPE_UNEXPECTED_INFO_STATUS);
440 return rc;
441 }
442 }
443 break;
444 }
445 AssertMsgReturn(cbWrittenOut > 0 && cbWrittenOut <= sizeof(pThis->abBuffer),
446 ("%zu %Rrc\n", cbWrittenOut, rc),
447 VERR_INTERNAL_ERROR_4);
448
449 /*
450 * Adjust the Zlib output buffer members.
451 */
452 if (cbWrittenOut == pThis->SgBuf.paSegs[0].cbSeg)
453 {
454 pThis->Zlib.avail_out = sizeof(pThis->abBuffer);
455 pThis->Zlib.next_out = &pThis->abBuffer[0];
456 }
457 else
458 {
459 Assert(cbWrittenOut <= pThis->SgBuf.paSegs[0].cbSeg);
460 size_t cbLeft = pThis->SgBuf.paSegs[0].cbSeg - cbWrittenOut;
461 memmove(&pThis->abBuffer[0], &pThis->abBuffer[cbWrittenOut], cbLeft);
462 pThis->Zlib.avail_out += (uInt)cbWrittenOut;
463 pThis->Zlib.next_out = &pThis->abBuffer[cbWrittenOut];
464 }
465
466 return VINF_SUCCESS;
467}
468
469
470/**
471 * Processes all available input.
472 *
473 * @returns IPRT status code.
474 *
475 * @param pThis The gzip I/O stream instance data.
476 * @param fBlocking Whether to block or not.
477 */
478static int rtZipGzip_CompressIt(PRTZIPGZIPSTREAM pThis, bool fBlocking)
479{
480 /*
481 * Processes all the intput currently lined up for us.
482 */
483 while (pThis->Zlib.avail_in > 0)
484 {
485 /* Make sure there is some space in the output buffer before calling
486 deflate() so we don't waste time filling up the corners. */
487 static const size_t s_cbFlushThreshold = 4096;
488 AssertCompile(sizeof(pThis->abBuffer) >= s_cbFlushThreshold * 4);
489 if (pThis->Zlib.avail_out < s_cbFlushThreshold)
490 {
491 int rc = rtZipGzip_WriteOutputBuffer(pThis, fBlocking);
492 if (rc != VINF_SUCCESS)
493 return rc;
494 Assert(pThis->Zlib.avail_out >= s_cbFlushThreshold);
495 }
496
497 int rcZlib = deflate(&pThis->Zlib, Z_NO_FLUSH);
498 if (rcZlib != Z_OK)
499 return rtZipGzipConvertErrFromZlib(pThis, rcZlib);
500 }
501 return VINF_SUCCESS;
502}
503
504
505/**
506 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
507 */
508static DECLCALLBACK(int) rtZipGzip_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
509{
510 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
511
512 Assert(pSgBuf->cSegs == 1); NOREF(fBlocking);
513 if (pThis->fDecompress)
514 return VERR_ACCESS_DENIED;
515 AssertReturn(off == -1 || off == pThis->offStream , VERR_INVALID_PARAMETER);
516
517 /*
518 * Write out the input buffer. Using a loop here because of potential
519 * integer type overflow since avail_in is uInt and cbSeg is size_t.
520 */
521 int rc = VINF_SUCCESS;
522 size_t cbWritten = 0;
523 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
524 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
525 if (cbLeft > 0)
526 for (;;)
527 {
528 size_t cbThis = cbLeft < ~(uInt)0 ? cbLeft : ~(uInt)0 / 2;
529 pThis->Zlib.next_in = (Bytef * )pbSrc;
530 pThis->Zlib.avail_in = (uInt)cbThis;
531 rc = rtZipGzip_CompressIt(pThis, fBlocking);
532
533 Assert(cbThis >= pThis->Zlib.avail_in);
534 cbThis -= pThis->Zlib.avail_in;
535 cbWritten += cbThis;
536 if (cbLeft == cbThis || rc != VINF_SUCCESS)
537 break;
538 pbSrc += cbThis;
539 cbLeft -= cbThis;
540 }
541
542 pThis->offStream += cbWritten;
543 if (pcbWritten)
544 *pcbWritten = cbWritten;
545 return rc;
546}
547
548
549/**
550 * Processes all available input.
551 *
552 * @returns IPRT status code.
553 *
554 * @param pThis The gzip I/O stream instance data.
555 * @param fFlushType The flush type to pass to deflate().
556 */
557static int rtZipGzip_FlushIt(PRTZIPGZIPSTREAM pThis, uint8_t fFlushType)
558{
559 /*
560 * Tell Zlib to flush until it stops producing more output.
561 */
562 int rc;
563 bool fMaybeMore = true;
564 for (;;)
565 {
566 /* Write the entire output buffer. */
567 do
568 {
569 rc = rtZipGzip_WriteOutputBuffer(pThis, true /*fBlocking*/);
570 if (RT_FAILURE(rc))
571 return rc;
572 Assert(rc == VINF_SUCCESS);
573 } while (pThis->Zlib.avail_out < sizeof(pThis->abBuffer));
574
575 if (!fMaybeMore)
576 return VINF_SUCCESS;
577
578 /* Do the flushing. */
579 pThis->Zlib.next_in = NULL;
580 pThis->Zlib.avail_in = 0;
581 int rcZlib = deflate(&pThis->Zlib, fFlushType);
582 if (rcZlib == Z_OK)
583 fMaybeMore = pThis->Zlib.avail_out < 64 || fFlushType == Z_FINISH;
584 else if (rcZlib == Z_STREAM_END)
585 fMaybeMore = false;
586 else
587 {
588 rtZipGzip_WriteOutputBuffer(pThis, true /*fBlocking*/);
589 return rtZipGzipConvertErrFromZlib(pThis, rcZlib);
590 }
591 }
592}
593
594
595/**
596 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
597 */
598static DECLCALLBACK(int) rtZipGzip_Flush(void *pvThis)
599{
600 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
601 if (!pThis->fDecompress)
602 {
603 int rc = rtZipGzip_FlushIt(pThis, Z_SYNC_FLUSH);
604 if (RT_FAILURE(rc))
605 return rc;
606 }
607
608 return RTVfsIoStrmFlush(pThis->hVfsIos);
609}
610
611
612/**
613 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
614 */
615static DECLCALLBACK(int) rtZipGzip_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
616 uint32_t *pfRetEvents)
617{
618 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
619
620 /*
621 * Collect our own events first and see if that satisfies the request. If
622 * not forward the call to the compressed stream.
623 */
624 uint32_t fRetEvents = 0;
625 if (pThis->fFatalError)
626 fRetEvents |= RTPOLL_EVT_ERROR;
627 if (pThis->fDecompress)
628 {
629 fEvents &= ~RTPOLL_EVT_WRITE;
630 if (pThis->Zlib.avail_in > 0)
631 fRetEvents = RTPOLL_EVT_READ;
632 }
633 else
634 {
635 fEvents &= ~RTPOLL_EVT_READ;
636 if (pThis->Zlib.avail_out > 0)
637 fRetEvents = RTPOLL_EVT_WRITE;
638 }
639
640 int rc = VINF_SUCCESS;
641 fRetEvents &= fEvents;
642 if (!fRetEvents)
643 rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
644 return rc;
645}
646
647
648/**
649 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
650 */
651static DECLCALLBACK(int) rtZipGzip_Tell(void *pvThis, PRTFOFF poffActual)
652{
653 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
654 *poffActual = pThis->offStream;
655 return VINF_SUCCESS;
656}
657
658
659/**
660 * The GZIP I/O stream vtable.
661 */
662static RTVFSIOSTREAMOPS g_rtZipGzipOps =
663{
664 { /* Obj */
665 RTVFSOBJOPS_VERSION,
666 RTVFSOBJTYPE_IO_STREAM,
667 "gzip",
668 rtZipGzip_Close,
669 rtZipGzip_QueryInfo,
670 RTVFSOBJOPS_VERSION
671 },
672 RTVFSIOSTREAMOPS_VERSION,
673 RTVFSIOSTREAMOPS_FEAT_NO_SG,
674 rtZipGzip_Read,
675 rtZipGzip_Write,
676 rtZipGzip_Flush,
677 rtZipGzip_PollOne,
678 rtZipGzip_Tell,
679 NULL /* Skip */,
680 NULL /*ZeroFill*/,
681 RTVFSIOSTREAMOPS_VERSION,
682};
683
684
685RTDECL(int) RTZipGzipDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSIOSTREAM phVfsIosOut)
686{
687 AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
688 AssertReturn(!(fFlags & ~RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR), VERR_INVALID_PARAMETER);
689 AssertPtrReturn(phVfsIosOut, VERR_INVALID_POINTER);
690
691 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
692 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
693
694 /*
695 * Create the decompression I/O stream.
696 */
697 RTVFSIOSTREAM hVfsIos;
698 PRTZIPGZIPSTREAM pThis;
699 int rc = RTVfsNewIoStream(&g_rtZipGzipOps, sizeof(RTZIPGZIPSTREAM), RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
700 &hVfsIos, (void **)&pThis);
701 if (RT_SUCCESS(rc))
702 {
703 pThis->hVfsIos = hVfsIosIn;
704 pThis->offStream = 0;
705 pThis->fDecompress = true;
706 pThis->SgSeg.pvSeg = &pThis->abBuffer[0];
707 pThis->SgSeg.cbSeg = sizeof(pThis->abBuffer);
708 RTSgBufInit(&pThis->SgBuf, &pThis->SgSeg, 1);
709
710 memset(&pThis->Zlib, 0, sizeof(pThis->Zlib));
711 pThis->Zlib.opaque = pThis;
712 rc = inflateInit2(&pThis->Zlib, MAX_WBITS | RT_BIT(5) /* autodetect gzip header */);
713 if (rc >= 0)
714 {
715 /*
716 * Read the gzip header from the input stream to check that it's
717 * a gzip stream as specified by the user.
718 *
719 * Note! Since we've told zlib to check for the gzip header, we
720 * prebuffer what we read in the input buffer so it can
721 * be handed on to zlib later on.
722 */
723 rc = RTVfsIoStrmRead(pThis->hVfsIos, pThis->abBuffer, sizeof(RTZIPGZIPHDR), true /*fBlocking*/, NULL /*pcbRead*/);
724 if (RT_SUCCESS(rc))
725 {
726 /* Validate the header and make a copy of it. */
727 PCRTZIPGZIPHDR pHdr = (PCRTZIPGZIPHDR)pThis->abBuffer;
728 if ( pHdr->bId1 == RTZIPGZIPHDR_ID1
729 && pHdr->bId2 == RTZIPGZIPHDR_ID2
730 && !(pHdr->fFlags & ~RTZIPGZIPHDR_FLG_VALID_MASK))
731 {
732 if (pHdr->bCompressionMethod == RTZIPGZIPHDR_CM_DEFLATE)
733 rc = VINF_SUCCESS;
734 else
735 rc = VERR_ZIP_UNSUPPORTED_METHOD;
736 }
737 else if ( (fFlags & RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR)
738 && (RT_MAKE_U16(pHdr->bId2, pHdr->bId1) % 31) == 0
739 && (pHdr->bId1 & 0xf) == RTZIPGZIPHDR_CM_DEFLATE )
740 {
741 pHdr = NULL;
742 rc = VINF_SUCCESS;
743 }
744 else
745 rc = VERR_ZIP_BAD_HEADER;
746 if (RT_SUCCESS(rc))
747 {
748 pThis->Zlib.avail_in = sizeof(RTZIPGZIPHDR);
749 pThis->Zlib.next_in = &pThis->abBuffer[0];
750 if (pHdr)
751 {
752 pThis->Hdr = *pHdr;
753 /* Parse on if there are names or comments. */
754 if (pHdr->fFlags & (RTZIPGZIPHDR_FLG_NAME | RTZIPGZIPHDR_FLG_COMMENT))
755 {
756 /** @todo Can implement this when someone needs the
757 * name or comment for something useful. */
758 }
759 }
760 if (RT_SUCCESS(rc))
761 {
762 *phVfsIosOut = hVfsIos;
763 return VINF_SUCCESS;
764 }
765 }
766 }
767 }
768 else
769 rc = rtZipGzipConvertErrFromZlib(pThis, rc); /** @todo cleaning up in this situation is going to go wrong. */
770 RTVfsIoStrmRelease(hVfsIos);
771 }
772 else
773 RTVfsIoStrmRelease(hVfsIosIn);
774 return rc;
775}
776
777
778RTDECL(int) RTZipGzipCompressIoStream(RTVFSIOSTREAM hVfsIosDst, uint32_t fFlags, uint8_t uLevel, PRTVFSIOSTREAM phVfsIosZip)
779{
780 AssertPtrReturn(hVfsIosDst, VERR_INVALID_HANDLE);
781 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
782 AssertPtrReturn(phVfsIosZip, VERR_INVALID_POINTER);
783 AssertReturn(uLevel > 0 && uLevel <= 9, VERR_INVALID_PARAMETER);
784
785 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosDst);
786 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
787
788 /*
789 * Create the compression I/O stream.
790 */
791 RTVFSIOSTREAM hVfsIos;
792 PRTZIPGZIPSTREAM pThis;
793 int rc = RTVfsNewIoStream(&g_rtZipGzipOps, sizeof(RTZIPGZIPSTREAM), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
794 &hVfsIos, (void **)&pThis);
795 if (RT_SUCCESS(rc))
796 {
797 pThis->hVfsIos = hVfsIosDst;
798 pThis->offStream = 0;
799 pThis->fDecompress = false;
800 pThis->SgSeg.pvSeg = &pThis->abBuffer[0];
801 pThis->SgSeg.cbSeg = sizeof(pThis->abBuffer);
802 RTSgBufInit(&pThis->SgBuf, &pThis->SgSeg, 1);
803
804 RT_ZERO(pThis->Zlib);
805 pThis->Zlib.opaque = pThis;
806 pThis->Zlib.next_out = &pThis->abBuffer[0];
807 pThis->Zlib.avail_out = sizeof(pThis->abBuffer);
808
809 rc = deflateInit2(&pThis->Zlib,
810 uLevel,
811 Z_DEFLATED,
812 15 /* Windows Size */ + 16 /* GZIP header */,
813 9 /* Max memory level for optimal speed */,
814 Z_DEFAULT_STRATEGY);
815
816 if (rc >= 0)
817 {
818 *phVfsIosZip = hVfsIos;
819 return VINF_SUCCESS;
820 }
821
822 rc = rtZipGzipConvertErrFromZlib(pThis, rc); /** @todo cleaning up in this situation is going to go wrong. */
823 RTVfsIoStrmRelease(hVfsIos);
824 }
825 else
826 RTVfsIoStrmRelease(hVfsIosDst);
827 return rc;
828}
829
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette