VirtualBox

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

Last change on this file since 46274 was 39083, checked in by vboxsync, 13 years ago

IPRT: -Wunused-parameter.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/* $Id: gzipvfs.cpp 39083 2011-10-22 00:28:46Z vboxsync $ */
2/** @file
3 * IPRT - GZIP Compressor and Decompressor I/O Stream.
4 */
5
6/*
7 * Copyright (C) 2010-2011 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* Structures and Typedefs *
61*******************************************************************************/
62#pragma pack(1)
63typedef struct RTZIPGZIPHDR
64{
65 /** RTZIPGZIPHDR_ID1. */
66 uint8_t bId1;
67 /** RTZIPGZIPHDR_ID2. */
68 uint8_t bId2;
69 /** CM - The compression method. */
70 uint8_t bCompressionMethod;
71 /** FLG - Flags. */
72 uint8_t fFlags;
73 /** Modification time of the source file or the timestamp at the time the
74 * compression took place. Can also be zero. Is the number of seconds since
75 * unix epoch. */
76 uint32_t u32ModTime;
77 /** Flags specific to the compression method. */
78 uint8_t bXtraFlags;
79 /** An ID indicating which OS or FS gzip ran on. */
80 uint8_t bOS;
81} RTZIPGZIPHDR;
82#pragma pack()
83AssertCompileSize(RTZIPGZIPHDR, 10);
84/** Pointer to a const gzip header. */
85typedef RTZIPGZIPHDR const *PCRTZIPGZIPHDR;
86
87/** gzip header identification no 1. */
88#define RTZIPGZIPHDR_ID1 0x1f
89/** gzip header identification no 2. */
90#define RTZIPGZIPHDR_ID2 0x8b
91/** gzip deflate compression method. */
92#define RTZIPGZIPHDR_CM_DEFLATE 8
93
94/** @name gzip header flags
95 * @{ */
96/** Probably a text file */
97#define RTZIPGZIPHDR_FLG_TEXT UINT8_C(0x01)
98/** Header CRC present (crc32 of header cast to uint16_t). */
99#define RTZIPGZIPHDR_FLG_HDR_CRC UINT8_C(0x02)
100/** Length prefixed xtra field is present. */
101#define RTZIPGZIPHDR_FLG_EXTRA UINT8_C(0x04)
102/** A name field is present (latin-1). */
103#define RTZIPGZIPHDR_FLG_NAME UINT8_C(0x08)
104/** A comment field is present (latin-1). */
105#define RTZIPGZIPHDR_FLG_COMMENT UINT8_C(0x10)
106/** Mask of valid flags. */
107#define RTZIPGZIPHDR_FLG_VALID_MASK UINT8_C(0x1f)
108/** @} */
109
110/** @name gzip default xtra flag values
111 * @{ */
112#define RTZIPGZIPHDR_XFL_DEFLATE_MAX UINT8_C(0x02)
113#define RTZIPGZIPHDR_XFL_DEFLATE_FASTEST UINT8_C(0x04)
114/** @} */
115
116/** @name Operating system / Filesystem IDs
117 * @{ */
118#define RTZIPGZIPHDR_OS_FAT UINT8_C(0x00)
119#define RTZIPGZIPHDR_OS_AMIGA UINT8_C(0x01)
120#define RTZIPGZIPHDR_OS_VMS UINT8_C(0x02)
121#define RTZIPGZIPHDR_OS_UNIX UINT8_C(0x03)
122#define RTZIPGZIPHDR_OS_VM_CMS UINT8_C(0x04)
123#define RTZIPGZIPHDR_OS_ATARIS_TOS UINT8_C(0x05)
124#define RTZIPGZIPHDR_OS_HPFS UINT8_C(0x06)
125#define RTZIPGZIPHDR_OS_MACINTOSH UINT8_C(0x07)
126#define RTZIPGZIPHDR_OS_Z_SYSTEM UINT8_C(0x08)
127#define RTZIPGZIPHDR_OS_CPM UINT8_C(0x09)
128#define RTZIPGZIPHDR_OS_TOPS_20 UINT8_C(0x0a)
129#define RTZIPGZIPHDR_OS_NTFS UINT8_C(0x0b)
130#define RTZIPGZIPHDR_OS_QDOS UINT8_C(0x0c)
131#define RTZIPGZIPHDR_OS_ACORN_RISCOS UINT8_C(0x0d)
132#define RTZIPGZIPHDR_OS_UNKNOWN UINT8_C(0xff)
133/** @} */
134
135
136/**
137 * The internal data of a GZIP I/O stream.
138 */
139typedef struct RTZIPGZIPSTREAM
140{
141 /** The stream we're reading or writing the compressed data from or to. */
142 RTVFSIOSTREAM hVfsIos;
143 /** Set if it's a decompressor, clear if it's a compressor. */
144 bool fDecompress;
145 /** Set if zlib reported a fatal error. */
146 bool fFatalError;
147 /** Set if we've reached the end of the zlib stream. */
148 bool fEndOfStream;
149 /** The stream offset for pfnTell. */
150 RTFOFF offStream;
151 /** The zlib stream. */
152 z_stream Zlib;
153 /** The data buffer. */
154 uint8_t abBuffer[_64K];
155 /** Scatter gather segment describing abBuffer. */
156 RTSGSEG SgSeg;
157 /** Scatter gather buffer describing abBuffer. */
158 RTSGBUF SgBuf;
159 /** The original file name (decompressor only). */
160 char *pszOrgName;
161 /** The comment (decompressor only). */
162 char *pszComment;
163 /** The gzip header. */
164 RTZIPGZIPHDR Hdr;
165} RTZIPGZIPSTREAM;
166/** Pointer to a the internal data of a GZIP I/O stream. */
167typedef RTZIPGZIPSTREAM *PRTZIPGZIPSTREAM;
168
169
170/**
171 * Convert from zlib to IPRT status codes.
172 *
173 * This will also set the fFatalError flag when appropriate.
174 *
175 * @returns IPRT status code.
176 * @param pThis The gzip I/O stream instance data.
177 * @param rc Zlib error code.
178 */
179static int rtZipGzipConvertErrFromZlib(PRTZIPGZIPSTREAM pThis, int rc)
180{
181 switch (rc)
182 {
183 case Z_OK:
184 return VINF_SUCCESS;
185
186 case Z_BUF_ERROR:
187 /* This isn't fatal. */
188 return VINF_SUCCESS; /** @todo The code in zip.cpp treats Z_BUF_ERROR as fatal... */
189
190 case Z_STREAM_ERROR:
191 pThis->fFatalError = true;
192 return VERR_ZIP_CORRUPTED;
193
194 case Z_DATA_ERROR:
195 pThis->fFatalError = true;
196 return pThis->fDecompress ? VERR_ZIP_CORRUPTED : VERR_ZIP_ERROR;
197
198 case Z_MEM_ERROR:
199 pThis->fFatalError = true;
200 return VERR_ZIP_NO_MEMORY;
201
202 case Z_VERSION_ERROR:
203 pThis->fFatalError = true;
204 return VERR_ZIP_UNSUPPORTED_VERSION;
205
206 case Z_ERRNO: /* We shouldn't see this status! */
207 default:
208 AssertMsgFailed(("%d\n", rc));
209 if (rc >= 0)
210 return VINF_SUCCESS;
211 pThis->fFatalError = true;
212 return VERR_ZIP_ERROR;
213 }
214}
215
216
217/**
218 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
219 */
220static DECLCALLBACK(int) rtZipGzip_Close(void *pvThis)
221{
222 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
223
224 int rc;
225 if (pThis->fDecompress)
226 rc = inflateEnd(&pThis->Zlib);
227 else
228 rc = deflateEnd(&pThis->Zlib);
229 if (rc != Z_OK)
230 rc = rtZipGzipConvertErrFromZlib(pThis, rc);
231
232 RTVfsIoStrmRelease(pThis->hVfsIos);
233 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
234 RTStrFree(pThis->pszOrgName);
235 pThis->pszOrgName = NULL;
236 RTStrFree(pThis->pszComment);
237 pThis->pszComment = NULL;
238
239 return rc;
240}
241
242
243/**
244 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
245 */
246static DECLCALLBACK(int) rtZipGzip_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
247{
248 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
249 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
250}
251
252
253/**
254 * Reads one segment.
255 *
256 * @returns IPRT status code.
257 * @param pThis The gzip I/O stream instance data.
258 * @param pvBuf Where to put the read bytes.
259 * @param cbToRead The number of bytes to read.
260 * @param fBlocking Whether to block or not.
261 * @param pcbRead Where to store the number of bytes actually read.
262 */
263static int rtZipGzip_ReadOneSeg(PRTZIPGZIPSTREAM pThis, void *pvBuf, size_t cbToRead, bool fBlocking, size_t *pcbRead)
264{
265 /*
266 * This simplifies life a wee bit below.
267 */
268 if (pThis->fEndOfStream)
269 return pcbRead ? VINF_EOF : VERR_EOF;
270
271 /*
272 * Set up the output buffer.
273 */
274 pThis->Zlib.next_out = (Bytef *)pvBuf;
275 pThis->Zlib.avail_out = (uInt)cbToRead;
276 AssertReturn(pThis->Zlib.avail_out == cbToRead, VERR_OUT_OF_RANGE);
277
278 /*
279 * Be greedy reading input, even if no output buffer is left. It's possible
280 * that it's just the end of stream marker which needs to be read. Happens
281 * for incompressible blocks just larger than the input buffer size.
282 */
283 int rc = VINF_SUCCESS;
284 while ( pThis->Zlib.avail_out > 0
285 || pThis->Zlib.avail_in == 0 /* greedy */)
286 {
287 /*
288 * Read more input?
289 *
290 * N.B. The assertions here validate the RTVfsIoStrmSgRead behavior
291 * since the API is new and untested. They could be removed later
292 * but, better leaving them in.
293 */
294 if (pThis->Zlib.avail_in == 0)
295 {
296 size_t cbReadIn = ~(size_t)0;
297 rc = RTVfsIoStrmSgRead(pThis->hVfsIos, &pThis->SgBuf, fBlocking, &cbReadIn);
298 if (rc != VINF_SUCCESS)
299 {
300 AssertMsg(RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || rc == VINF_EOF, ("%Rrc\n", rc));
301 if (rc == VERR_INTERRUPTED)
302 {
303 Assert(cbReadIn == 0);
304 continue;
305 }
306 if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || cbReadIn == 0)
307 {
308 Assert(cbReadIn == 0);
309 break;
310 }
311 AssertMsg(rc == VINF_EOF, ("%Rrc\n", rc));
312 }
313 AssertMsgBreakStmt(cbReadIn > 0 && cbReadIn <= sizeof(pThis->abBuffer), ("%zu %Rrc\n", cbReadIn, rc),
314 rc = VERR_INTERNAL_ERROR_4);
315
316 pThis->Zlib.avail_in = (uInt)cbReadIn;
317 pThis->Zlib.next_in = &pThis->abBuffer[0];
318 }
319
320 /*
321 * Pass it on to zlib.
322 */
323 rc = inflate(&pThis->Zlib, Z_NO_FLUSH);
324 if (rc != Z_OK && rc != Z_BUF_ERROR)
325 {
326 if (rc == Z_STREAM_END)
327 {
328 pThis->fEndOfStream = true;
329 if (pThis->Zlib.avail_out == 0)
330 rc = VINF_SUCCESS;
331 else
332 rc = pcbRead ? VINF_EOF : VERR_EOF;
333 }
334 else
335 rc = rtZipGzipConvertErrFromZlib(pThis, rc);
336 break;
337 }
338 rc = VINF_SUCCESS;
339 }
340
341 /*
342 * Update the read counters before returning.
343 */
344 size_t const cbRead = cbToRead - pThis->Zlib.avail_out;
345 pThis->offStream += cbRead;
346 if (pcbRead)
347 *pcbRead = cbRead;
348
349 return rc;
350}
351
352/**
353 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
354 */
355static DECLCALLBACK(int) rtZipGzip_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
356{
357 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
358 int rc;
359
360 AssertReturn(off == -1, VERR_INVALID_PARAMETER);
361 if (!pThis->fDecompress)
362 return VERR_ACCESS_DENIED;
363
364 if (pSgBuf->cSegs == 1)
365 rc = rtZipGzip_ReadOneSeg(pThis, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, fBlocking, pcbRead);
366 else
367 {
368 rc = VINF_SUCCESS;
369 size_t cbRead = 0;
370 size_t cbReadSeg;
371 size_t *pcbReadSeg = pcbRead ? &cbReadSeg : NULL;
372 for (uint32_t iSeg = 0; iSeg < pSgBuf->cSegs; iSeg++)
373 {
374 cbReadSeg = 0;
375 rc = rtZipGzip_ReadOneSeg(pThis, pSgBuf->paSegs[iSeg].pvSeg, pSgBuf->paSegs[iSeg].cbSeg, fBlocking, pcbReadSeg);
376 if (RT_FAILURE(rc))
377 break;
378 if (pcbRead)
379 {
380 cbRead += cbReadSeg;
381 if (cbReadSeg != pSgBuf->paSegs[iSeg].cbSeg)
382 break;
383 }
384 }
385 if (pcbRead)
386 *pcbRead = cbRead;
387 }
388
389 return rc;
390}
391
392
393/**
394 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
395 */
396static DECLCALLBACK(int) rtZipGzip_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
397{
398 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
399 //int rc;
400
401 AssertReturn(off == -1, VERR_INVALID_PARAMETER);
402 NOREF(fBlocking);
403 if (pThis->fDecompress)
404 return VERR_ACCESS_DENIED;
405
406 /** @todo implement compression. */
407 NOREF(pSgBuf); NOREF(pcbWritten);
408 return VERR_NOT_IMPLEMENTED;
409}
410
411
412/**
413 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
414 */
415static DECLCALLBACK(int) rtZipGzip_Flush(void *pvThis)
416{
417 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
418 return RTVfsIoStrmFlush(pThis->hVfsIos);
419}
420
421
422/**
423 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
424 */
425static DECLCALLBACK(int) rtZipGzip_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
426 uint32_t *pfRetEvents)
427{
428 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
429
430 /*
431 * Collect our own events first and see if that satisfies the request. If
432 * not forward the call to the compressed stream.
433 */
434 uint32_t fRetEvents = 0;
435 if (pThis->fFatalError)
436 fRetEvents |= RTPOLL_EVT_ERROR;
437 if (pThis->fDecompress)
438 {
439 fEvents &= ~RTPOLL_EVT_WRITE;
440 if (pThis->Zlib.avail_in > 0)
441 fRetEvents = RTPOLL_EVT_READ;
442 }
443 else
444 {
445 fEvents &= ~RTPOLL_EVT_READ;
446 if (pThis->Zlib.avail_out > 0)
447 fRetEvents = RTPOLL_EVT_WRITE;
448 }
449
450 int rc = VINF_SUCCESS;
451 fRetEvents &= fEvents;
452 if (!fRetEvents)
453 rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
454 return rc;
455}
456
457
458/**
459 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
460 */
461static DECLCALLBACK(int) rtZipGzip_Tell(void *pvThis, PRTFOFF poffActual)
462{
463 PRTZIPGZIPSTREAM pThis = (PRTZIPGZIPSTREAM)pvThis;
464 *poffActual = pThis->offStream;
465 return VINF_SUCCESS;
466}
467
468
469/**
470 * The GZIP I/O stream vtable.
471 */
472static RTVFSIOSTREAMOPS g_rtZipGzipOps =
473{
474 { /* Obj */
475 RTVFSOBJOPS_VERSION,
476 RTVFSOBJTYPE_IO_STREAM,
477 "gzip",
478 rtZipGzip_Close,
479 rtZipGzip_QueryInfo,
480 RTVFSOBJOPS_VERSION
481 },
482 RTVFSIOSTREAMOPS_VERSION,
483 0,
484 rtZipGzip_Read,
485 rtZipGzip_Write,
486 rtZipGzip_Flush,
487 rtZipGzip_PollOne,
488 rtZipGzip_Tell,
489 NULL /* Skip */,
490 NULL /*ZeroFill*/,
491 RTVFSIOSTREAMOPS_VERSION,
492};
493
494
495RTDECL(int) RTZipGzipDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSIOSTREAM phVfsIosOut)
496{
497 AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
498 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
499 AssertPtrReturn(phVfsIosOut, VERR_INVALID_POINTER);
500
501 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
502 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
503
504 /*
505 * Create the decompression I/O stream.
506 */
507 RTVFSIOSTREAM hVfsIos;
508 PRTZIPGZIPSTREAM pThis;
509 int rc = RTVfsNewIoStream(&g_rtZipGzipOps, sizeof(RTZIPGZIPSTREAM), RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
510 &hVfsIos, (void **)&pThis);
511 if (RT_SUCCESS(rc))
512 {
513 pThis->hVfsIos = hVfsIosIn;
514 pThis->offStream = 0;
515 pThis->fDecompress = true;
516 pThis->SgSeg.pvSeg = &pThis->abBuffer[0];
517 pThis->SgSeg.cbSeg = sizeof(pThis->abBuffer);
518 RTSgBufInit(&pThis->SgBuf, &pThis->SgSeg, 1);
519
520 memset(&pThis->Zlib, 0, sizeof(pThis->Zlib));
521 pThis->Zlib.opaque = pThis;
522 rc = inflateInit2(&pThis->Zlib, MAX_WBITS + 16 /* autodetect gzip header */);
523 if (rc >= 0)
524 {
525 /*
526 * Read the gzip header from the input stream to check that it's
527 * a gzip stream.
528 *
529 * Note!. Since we've told zlib to check for the gzip header, we
530 * prebuffer what we read in the input buffer so it can
531 * be handed on to zlib later on.
532 */
533 rc = RTVfsIoStrmRead(pThis->hVfsIos, pThis->abBuffer, sizeof(RTZIPGZIPHDR), true /*fBlocking*/, NULL /*pcbRead*/);
534 if (RT_SUCCESS(rc))
535 {
536 /* Validate the header and make a copy of it. */
537 PCRTZIPGZIPHDR pHdr = (PCRTZIPGZIPHDR)pThis->abBuffer;
538 if ( pHdr->bId1 != RTZIPGZIPHDR_ID1
539 || pHdr->bId2 != RTZIPGZIPHDR_ID2
540 || pHdr->fFlags & ~RTZIPGZIPHDR_FLG_VALID_MASK)
541 rc = VERR_ZIP_BAD_HEADER;
542 else if (pHdr->bCompressionMethod != RTZIPGZIPHDR_CM_DEFLATE)
543 rc = VERR_ZIP_UNSUPPORTED_METHOD;
544 else
545 {
546 pThis->Hdr = *pHdr;
547 pThis->Zlib.avail_in = sizeof(RTZIPGZIPHDR);
548 pThis->Zlib.next_in = &pThis->abBuffer[0];
549
550 /* Parse on if there are names or comments. */
551 if (pHdr->fFlags & (RTZIPGZIPHDR_FLG_NAME | RTZIPGZIPHDR_FLG_COMMENT))
552 {
553 /** @todo Can implement this when someone needs the
554 * name or comment for something useful. */
555 }
556 if (RT_SUCCESS(rc))
557 {
558 *phVfsIosOut = hVfsIos;
559 return VINF_SUCCESS;
560 }
561 }
562 }
563 }
564 else
565 rc = rtZipGzipConvertErrFromZlib(pThis, rc); /** @todo cleaning up in this situation is going to go wrong. */
566 RTVfsIoStrmRelease(hVfsIos);
567 }
568 else
569 RTVfsIoStrmRelease(hVfsIosIn);
570 return rc;
571}
572
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