VirtualBox

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

Last change on this file since 34027 was 34027, checked in by vboxsync, 14 years ago

zipgzip.cpp -> gzipvfs.cpp

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