VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/zip/lzmavfs.cpp

Last change on this file was 100908, checked in by vboxsync, 9 months ago

IPRT,Storage,Puel: Changed the pfnRead and pfnWrite VFS methods and the RTVfsIoStrmSgRead, RTVfsIoStrmSgWrite, RTVfsFileSgRead and RTVfsFileSgWrite APIs to advance pSgBuf and respect the incoming position just like RTFileSgRead & RTFileSgWrite.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 KB
Line 
1/* $Id: lzmavfs.cpp 100908 2023-08-19 02:57:05Z vboxsync $ */
2/** @file
3 * IPRT - XZ/LZMA Compressor and Decompressor I/O Stream.
4 */
5
6/*
7 * Copyright (C) 2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Defined Constants And Macros *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/zip.h>
43
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/crc.h>
47#include <iprt/ctype.h>
48#include <iprt/file.h>
49#include <iprt/err.h>
50#include <iprt/poll.h>
51#include <iprt/string.h>
52#include <iprt/vfslowlevel.h>
53
54#define LZMA_API_STATIC
55#include <lzma.h>
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61typedef struct RTZIPXZHDR
62{
63 /** The magic. */
64 uint8_t abMagic[6];
65 /** Stream flag byte 1 (always 0). */
66 uint8_t bStrmFlags1;
67 /** Stream flag byte 2 (high nibble always 0 for now). */
68 uint8_t bStrmFlags2;
69 /** CRC32 checksum of RTZIPXZHDR::bStrmFlags1 and RTZIPXZHDR::bStrmFlags2. */
70 uint32_t u32Crc;
71} RTZIPXZHDR;
72AssertCompileSize(RTZIPXZHDR, 12);
73/** Pointer to an XZ stream header. */
74typedef RTZIPXZHDR *PRTZIPXZHDR;
75/** Pointer to a constant XZ stream header. */
76typedef const RTZIPXZHDR *PCRTZIPXZHDR;
77
78/** The header magic. */
79#define RTZIPXZHDR_MAGIC { 0xfd, '7', 'z', 'X', 'Z', 0x00 }
80/** None check */
81#define RTZIPXZHDR_CHECK_NONE 0x00
82/** CRC32 check */
83#define RTZIPXZHDR_CHECK_CRC32 0x01
84/** CRC64 check */
85#define RTZIPXZHDR_CHECK_CRC64 0x04
86/** SHA-256 check */
87#define RTZIPXZHDR_CHECK_SHA256 0x0a
88
89
90/**
91 * The internal data of a XZ/LZMA I/O stream.
92 */
93typedef struct RTZIPLZMASTREAM
94{
95 /** The stream we're reading or writing the compressed data from or to. */
96 RTVFSIOSTREAM hVfsIos;
97 /** Set if it's a decompressor, clear if it's a compressor. */
98 bool fDecompress;
99 /** Set if liblzma reported a fatal error. */
100 bool fFatalError;
101 /** Set if we've reached the end of the stream. */
102 bool fEndOfStream;
103 /** The stream offset for pfnTell, always the uncompressed data. */
104 RTFOFF offStream;
105 /** The lzma decoder stream. */
106 lzma_stream Lzma;
107 /** The current lzma action to use as a parameter to lzma_code(). */
108 lzma_action enmLzmaAction;
109 /** The data buffer. */
110 uint8_t abBuffer[_64K];
111 /** Scatter gather segment describing abBuffer. */
112 RTSGSEG SgSeg;
113 /** Scatter gather buffer describing abBuffer. */
114 RTSGBUF SgBuf;
115} RTZIPLZMASTREAM;
116/** Pointer to a the internal data of a LZMA I/O stream. */
117typedef RTZIPLZMASTREAM *PRTZIPLZMASTREAM;
118
119
120/*********************************************************************************************************************************
121* Internal Functions *
122*********************************************************************************************************************************/
123static int rtZipLzma_FlushIt(PRTZIPLZMASTREAM pThis, lzma_action enmFlushType);
124
125
126/**
127 * Convert from liblzma to IPRT status codes.
128 *
129 * This will also set the fFatalError flag when appropriate.
130 *
131 * @returns IPRT status code.
132 * @param pThis The gzip I/O stream instance data.
133 * @param rc Zlib error code.
134 */
135static int rtZipLzmaConvertErrFromLzma(PRTZIPLZMASTREAM pThis, lzma_ret rc)
136{
137 switch (rc)
138 {
139 case LZMA_OK:
140 return VINF_SUCCESS;
141
142 case LZMA_BUF_ERROR:
143 /* This isn't fatal. */
144 return VINF_SUCCESS;
145
146 case LZMA_FORMAT_ERROR:
147 pThis->fFatalError = true;
148 return VERR_ZIP_CORRUPTED;
149
150 case LZMA_DATA_ERROR:
151 pThis->fFatalError = true;
152 return pThis->fDecompress ? VERR_ZIP_CORRUPTED : VERR_ZIP_ERROR;
153
154 case LZMA_MEM_ERROR:
155 pThis->fFatalError = true;
156 return VERR_ZIP_NO_MEMORY;
157
158 case LZMA_PROG_ERROR:
159 pThis->fFatalError = true;
160 return VERR_INTERNAL_ERROR;
161
162 default:
163 AssertMsgFailed(("%d\n", rc));
164 if (rc >= 0)
165 return VINF_SUCCESS;
166 pThis->fFatalError = true;
167 return VERR_ZIP_ERROR;
168 }
169}
170
171
172/**
173 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
174 */
175static DECLCALLBACK(int) rtZipLzma_Close(void *pvThis)
176{
177 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
178
179 int rc = VINF_SUCCESS;
180 if (pThis->fDecompress)
181 lzma_end(&pThis->Lzma);
182 else
183 {
184 /* Flush the compression stream before terminating it. */
185 if (!pThis->fFatalError)
186 rc = rtZipLzma_FlushIt(pThis, LZMA_FINISH);
187
188 lzma_end(&pThis->Lzma);
189 }
190
191 RTVfsIoStrmRelease(pThis->hVfsIos);
192 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
193
194 return rc;
195}
196
197
198/**
199 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
200 */
201static DECLCALLBACK(int) rtZipLzma_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
202{
203 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
204 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
205}
206
207
208/**
209 * Reads one segment.
210 *
211 * @returns IPRT status code.
212 * @param pThis The lzma I/O stream instance data.
213 * @param pvBuf Where to put the read bytes.
214 * @param cbToRead The number of bytes to read.
215 * @param fBlocking Whether to block or not.
216 * @param pcbRead Where to store the number of bytes actually read.
217 * @param pSgBuf The S/G buffer descriptor, for advancing.
218 */
219static int rtZipLzma_ReadOneSeg(PRTZIPLZMASTREAM pThis, void *pvBuf, size_t cbToRead, bool fBlocking,
220 size_t *pcbRead, PRTSGBUF pSgBuf)
221{
222 /*
223 * This simplifies life a wee bit below.
224 */
225 if (pThis->fEndOfStream)
226 return pcbRead ? VINF_EOF : VERR_EOF;
227
228 /*
229 * Set up the output buffer.
230 */
231 pThis->Lzma.next_out = (uint8_t *)pvBuf;
232 pThis->Lzma.avail_out = cbToRead;
233
234 /*
235 * Be greedy reading input, even if no output buffer is left. It's possible
236 * that it's just the end of stream marker which needs to be read. Happens
237 * for incompressible blocks just larger than the input buffer size.
238 */
239 int rc = VINF_SUCCESS;
240 while ( pThis->Lzma.avail_out > 0
241 || pThis->Lzma.avail_in == 0 /* greedy */)
242 {
243 /* Read more input? */
244 if (pThis->Lzma.avail_in == 0)
245 {
246 size_t cbReadIn = ~(size_t)0;
247 RTSgBufReset(&pThis->SgBuf);
248 rc = RTVfsIoStrmSgRead(pThis->hVfsIos, -1 /*off*/, &pThis->SgBuf, fBlocking, &cbReadIn);
249 if (rc != VINF_SUCCESS)
250 {
251 AssertMsg(RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || rc == VINF_EOF, ("%Rrc\n", rc));
252 if (rc == VERR_INTERRUPTED)
253 {
254 Assert(cbReadIn == 0);
255 continue;
256 }
257 if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || cbReadIn == 0)
258 {
259 Assert(cbReadIn == 0);
260 break;
261 }
262 else
263 {
264 AssertMsg(rc == VINF_EOF, ("%Rrc\n", rc));
265 pThis->enmLzmaAction = LZMA_FINISH;
266 }
267 }
268 AssertMsgBreakStmt(cbReadIn > 0 && cbReadIn <= sizeof(pThis->abBuffer), ("%zu %Rrc\n", cbReadIn, rc),
269 rc = VERR_INTERNAL_ERROR_4);
270
271 pThis->Lzma.avail_in = cbReadIn;
272 pThis->Lzma.next_in = &pThis->abBuffer[0];
273 }
274
275 /*
276 * Pass it on to lzma.
277 */
278 lzma_ret rcLzma = lzma_code(&pThis->Lzma, pThis->enmLzmaAction);
279 if (rcLzma != LZMA_OK && rcLzma != LZMA_BUF_ERROR)
280 {
281 if (rcLzma == LZMA_STREAM_END)
282 {
283 pThis->fEndOfStream = true;
284 if (pThis->Lzma.avail_out == 0)
285 rc = VINF_SUCCESS;
286 else
287 rc = pcbRead ? VINF_EOF : VERR_EOF;
288 }
289 else
290 rc = rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
291 break;
292 }
293 rc = VINF_SUCCESS;
294 }
295
296 /*
297 * Update the read counters before returning.
298 */
299 size_t const cbRead = cbToRead - pThis->Lzma.avail_out;
300 pThis->offStream += cbRead;
301 if (pcbRead)
302 *pcbRead = cbRead;
303 RTSgBufAdvance(pSgBuf, cbRead);
304
305 return rc;
306}
307
308
309/**
310 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
311 */
312static DECLCALLBACK(int) rtZipLzma_Read(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
313{
314 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
315
316 Assert(pSgBuf->cSegs == 1);
317 if (!pThis->fDecompress)
318 return VERR_ACCESS_DENIED;
319 AssertReturn(off == -1 || off == pThis->offStream , VERR_INVALID_PARAMETER);
320
321 return rtZipLzma_ReadOneSeg(pThis, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, fBlocking, pcbRead, pSgBuf);
322}
323
324
325/**
326 * Internal helper for rtZipLzma_Write, rtZipLzma_Flush and rtZipLzma_Close.
327 *
328 * @returns IPRT status code.
329 * @retval VINF_SUCCESS
330 * @retval VINF_TRY_AGAIN - the only informational status.
331 * @retval VERR_INTERRUPTED - call again.
332 *
333 * @param pThis The lzma I/O stream instance data.
334 * @param fBlocking Whether to block or not.
335 */
336static int rtZipLzma_WriteOutputBuffer(PRTZIPLZMASTREAM pThis, bool fBlocking)
337{
338 /*
339 * Anything to write? No, then just return immediately.
340 */
341 size_t cbToWrite = sizeof(pThis->abBuffer) - pThis->Lzma.avail_out;
342 if (cbToWrite == 0)
343 {
344 Assert(pThis->Lzma.next_out == &pThis->abBuffer[0]);
345 return VINF_SUCCESS;
346 }
347 Assert(cbToWrite <= sizeof(pThis->abBuffer));
348
349 /*
350 * Loop write on VERR_INTERRUPTED.
351 *
352 * Note! Asserting a bit extra here to make sure the
353 * RTVfsIoStrmSgWrite works correctly.
354 */
355 int rc;
356 size_t cbWrittenOut;
357 for (;;)
358 {
359 /* Set up the buffer. */
360 pThis->SgSeg.cbSeg = cbToWrite;
361 Assert(pThis->SgSeg.pvSeg == &pThis->abBuffer[0]);
362 RTSgBufReset(&pThis->SgBuf);
363
364 cbWrittenOut = ~(size_t)0;
365 rc = RTVfsIoStrmSgWrite(pThis->hVfsIos, -1 /*off*/, &pThis->SgBuf, fBlocking, &cbWrittenOut);
366 if (rc != VINF_SUCCESS)
367 {
368 AssertMsg(RT_FAILURE(rc) || rc == VINF_TRY_AGAIN, ("%Rrc\n", rc));
369 if (rc == VERR_INTERRUPTED)
370 {
371 Assert(cbWrittenOut == 0);
372 continue;
373 }
374 if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || cbWrittenOut == 0)
375 {
376 AssertReturn(cbWrittenOut == 0, VERR_INTERNAL_ERROR_3);
377 AssertReturn(rc != VINF_SUCCESS, VERR_IPE_UNEXPECTED_INFO_STATUS);
378 return rc;
379 }
380 }
381 break;
382 }
383 AssertMsgReturn(cbWrittenOut > 0 && cbWrittenOut <= sizeof(pThis->abBuffer),
384 ("%zu %Rrc\n", cbWrittenOut, rc),
385 VERR_INTERNAL_ERROR_4);
386
387 /*
388 * Adjust the Lzma output buffer members.
389 */
390 if (cbWrittenOut == pThis->SgBuf.paSegs[0].cbSeg)
391 {
392 pThis->Lzma.avail_out = sizeof(pThis->abBuffer);
393 pThis->Lzma.next_out = &pThis->abBuffer[0];
394 }
395 else
396 {
397 Assert(cbWrittenOut <= pThis->SgBuf.paSegs[0].cbSeg);
398 size_t cbLeft = pThis->SgBuf.paSegs[0].cbSeg - cbWrittenOut;
399 memmove(&pThis->abBuffer[0], &pThis->abBuffer[cbWrittenOut], cbLeft);
400 pThis->Lzma.avail_out += cbWrittenOut;
401 pThis->Lzma.next_out = &pThis->abBuffer[cbWrittenOut];
402 }
403
404 return VINF_SUCCESS;
405}
406
407
408/**
409 * Processes all available input.
410 *
411 * @returns IPRT status code.
412 *
413 * @param pThis The lzma I/O stream instance data.
414 * @param fBlocking Whether to block or not.
415 */
416static int rtZipLzma_CompressIt(PRTZIPLZMASTREAM pThis, bool fBlocking)
417{
418 /*
419 * Processes all the input currently lined up for us.
420 */
421 while (pThis->Lzma.avail_in > 0)
422 {
423 /* Make sure there is some space in the output buffer before calling
424 deflate() so we don't waste time filling up the corners. */
425 static const size_t s_cbFlushThreshold = 4096;
426 AssertCompile(sizeof(pThis->abBuffer) >= s_cbFlushThreshold * 4);
427 if (pThis->Lzma.avail_out < s_cbFlushThreshold)
428 {
429 int rc = rtZipLzma_WriteOutputBuffer(pThis, fBlocking);
430 if (rc != VINF_SUCCESS)
431 return rc;
432 Assert(pThis->Lzma.avail_out >= s_cbFlushThreshold);
433 }
434
435 lzma_ret rcLzma = lzma_code(&pThis->Lzma, LZMA_RUN);
436 if (rcLzma != LZMA_OK)
437 return rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
438 }
439 return VINF_SUCCESS;
440}
441
442
443/**
444 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
445 */
446static DECLCALLBACK(int) rtZipLzma_Write(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
447{
448 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
449
450 Assert(pSgBuf->cSegs == 1); NOREF(fBlocking);
451 if (pThis->fDecompress)
452 return VERR_ACCESS_DENIED;
453 AssertReturn(off == -1 || off == pThis->offStream , VERR_INVALID_PARAMETER);
454
455 /*
456 * Write out the input buffer. Using a loop here because of potential
457 * integer type overflow since avail_in is uInt and cbSeg is size_t.
458 */
459 int rc = VINF_SUCCESS;
460 size_t cbWritten = 0;
461 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
462 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
463 if (cbLeft > 0)
464 for (;;)
465 {
466 size_t cbThis = cbLeft < ~(size_t)0 ? cbLeft : ~(size_t)0 / 2;
467 pThis->Lzma.next_in = pbSrc;
468 pThis->Lzma.avail_in = cbThis;
469 rc = rtZipLzma_CompressIt(pThis, fBlocking);
470
471 Assert(cbThis >= pThis->Lzma.avail_in);
472 cbThis -= pThis->Lzma.avail_in;
473 cbWritten += cbThis;
474 if (cbLeft == cbThis || rc != VINF_SUCCESS)
475 break;
476 pbSrc += cbThis;
477 cbLeft -= cbThis;
478 }
479
480 pThis->offStream += cbWritten;
481 if (pcbWritten)
482 *pcbWritten = cbWritten;
483 RTSgBufAdvance(pSgBuf, cbWritten);
484
485 return rc;
486}
487
488
489/**
490 * Processes all available input.
491 *
492 * @returns IPRT status code.
493 *
494 * @param pThis The lzma I/O stream instance data.
495 * @param enmFlushType The flush type to pass to lzma_run().
496 */
497static int rtZipLzma_FlushIt(PRTZIPLZMASTREAM pThis, lzma_action enmFlushType)
498{
499 /*
500 * Tell Zlib to flush until it stops producing more output.
501 */
502 int rc;
503 bool fMaybeMore = true;
504 for (;;)
505 {
506 /* Write the entire output buffer. */
507 do
508 {
509 rc = rtZipLzma_WriteOutputBuffer(pThis, true /*fBlocking*/);
510 if (RT_FAILURE(rc))
511 return rc;
512 Assert(rc == VINF_SUCCESS);
513 } while (pThis->Lzma.avail_out < sizeof(pThis->abBuffer));
514
515 if (!fMaybeMore)
516 return VINF_SUCCESS;
517
518 /* Do the flushing. */
519 pThis->Lzma.next_in = NULL;
520 pThis->Lzma.avail_in = 0;
521 lzma_ret rcLzma = lzma_code(&pThis->Lzma, enmFlushType);
522 if (rcLzma == LZMA_OK)
523 fMaybeMore = pThis->Lzma.avail_out < 64 || enmFlushType == LZMA_FINISH;
524 else if (rcLzma == LZMA_STREAM_END)
525 fMaybeMore = false;
526 else
527 {
528 rtZipLzma_WriteOutputBuffer(pThis, true /*fBlocking*/);
529 return rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
530 }
531 }
532}
533
534
535/**
536 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
537 */
538static DECLCALLBACK(int) rtZipLzma_Flush(void *pvThis)
539{
540 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
541 if (!pThis->fDecompress)
542 {
543 int rc = rtZipLzma_FlushIt(pThis, LZMA_SYNC_FLUSH);
544 if (RT_FAILURE(rc))
545 return rc;
546 }
547
548 return RTVfsIoStrmFlush(pThis->hVfsIos);
549}
550
551
552/**
553 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
554 */
555static DECLCALLBACK(int) rtZipLzma_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
556 uint32_t *pfRetEvents)
557{
558 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
559
560 /*
561 * Collect our own events first and see if that satisfies the request. If
562 * not forward the call to the compressed stream.
563 */
564 uint32_t fRetEvents = 0;
565 if (pThis->fFatalError)
566 fRetEvents |= RTPOLL_EVT_ERROR;
567 if (pThis->fDecompress)
568 {
569 fEvents &= ~RTPOLL_EVT_WRITE;
570 if (pThis->Lzma.avail_in > 0)
571 fRetEvents = RTPOLL_EVT_READ;
572 }
573 else
574 {
575 fEvents &= ~RTPOLL_EVT_READ;
576 if (pThis->Lzma.avail_out > 0)
577 fRetEvents = RTPOLL_EVT_WRITE;
578 }
579
580 int rc = VINF_SUCCESS;
581 fRetEvents &= fEvents;
582 if (!fRetEvents)
583 rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
584 return rc;
585}
586
587
588/**
589 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
590 */
591static DECLCALLBACK(int) rtZipLzma_Tell(void *pvThis, PRTFOFF poffActual)
592{
593 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
594 *poffActual = pThis->offStream;
595 return VINF_SUCCESS;
596}
597
598
599/**
600 * The LZMA I/O stream vtable.
601 */
602static RTVFSIOSTREAMOPS g_rtZipLzmaOps =
603{
604 { /* Obj */
605 RTVFSOBJOPS_VERSION,
606 RTVFSOBJTYPE_IO_STREAM,
607 "lzma",
608 rtZipLzma_Close,
609 rtZipLzma_QueryInfo,
610 NULL,
611 RTVFSOBJOPS_VERSION
612 },
613 RTVFSIOSTREAMOPS_VERSION,
614 RTVFSIOSTREAMOPS_FEAT_NO_SG,
615 rtZipLzma_Read,
616 rtZipLzma_Write,
617 rtZipLzma_Flush,
618 rtZipLzma_PollOne,
619 rtZipLzma_Tell,
620 NULL /* Skip */,
621 NULL /*ZeroFill*/,
622 RTVFSIOSTREAMOPS_VERSION,
623};
624
625
626RTDECL(int) RTZipXzDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSIOSTREAM phVfsIosOut)
627{
628 AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
629 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
630 AssertPtrReturn(phVfsIosOut, VERR_INVALID_POINTER);
631
632 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
633 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
634
635 /*
636 * Create the decompression I/O stream.
637 */
638 RTVFSIOSTREAM hVfsIos;
639 PRTZIPLZMASTREAM pThis;
640 int rc = RTVfsNewIoStream(&g_rtZipLzmaOps, sizeof(*pThis), RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
641 &hVfsIos, (void **)&pThis);
642 if (RT_SUCCESS(rc))
643 {
644 pThis->hVfsIos = hVfsIosIn;
645 pThis->offStream = 0;
646 pThis->fDecompress = true;
647 pThis->SgSeg.pvSeg = &pThis->abBuffer[0];
648 pThis->SgSeg.cbSeg = sizeof(pThis->abBuffer);
649 pThis->enmLzmaAction = LZMA_RUN;
650 RTSgBufInit(&pThis->SgBuf, &pThis->SgSeg, 1);
651
652 memset(&pThis->Lzma, 0, sizeof(pThis->Lzma));
653 lzma_ret rcLzma = lzma_stream_decoder(&pThis->Lzma, UINT64_MAX, LZMA_CONCATENATED);
654 if (rcLzma == LZMA_OK)
655 {
656 /*
657 * Read the XZ header from the input stream to check that it's
658 * a xz stream as specified by the user.
659 */
660 rc = RTVfsIoStrmRead(pThis->hVfsIos, pThis->abBuffer, sizeof(RTZIPXZHDR), true /*fBlocking*/, NULL /*pcbRead*/);
661 if (RT_SUCCESS(rc))
662 {
663 /* Validate the header. */
664 static const uint8_t s_abXzMagic[6] = RTZIPXZHDR_MAGIC;
665 PCRTZIPXZHDR pHdr = (PCRTZIPXZHDR)pThis->abBuffer;
666 uint8_t bChkMethod = pHdr->bStrmFlags2 & 0xf;
667
668 if ( !memcmp(&pHdr->abMagic[0], &s_abXzMagic[0], sizeof(s_abXzMagic))
669 && pHdr->bStrmFlags1 == 0
670 && (pHdr->bStrmFlags2 & 0xf0) == 0
671 && ( bChkMethod == RTZIPXZHDR_CHECK_NONE
672 || bChkMethod == RTZIPXZHDR_CHECK_CRC32
673 || bChkMethod == RTZIPXZHDR_CHECK_CRC64
674 || bChkMethod == RTZIPXZHDR_CHECK_SHA256)
675 && RTCrc32(&pHdr->bStrmFlags1, 2) == RT_LE2H_U32(pHdr->u32Crc))
676 {
677 pThis->Lzma.avail_in = sizeof(RTZIPXZHDR);
678 pThis->Lzma.next_in = &pThis->abBuffer[0];
679 *phVfsIosOut = hVfsIos;
680 return VINF_SUCCESS;
681 }
682 else
683 rc = VERR_ZIP_BAD_HEADER;
684 }
685 }
686 else
687 rc = rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
688 RTVfsIoStrmRelease(hVfsIos);
689 }
690 else
691 RTVfsIoStrmRelease(hVfsIosIn);
692 return rc;
693}
694
695
696RTDECL(int) RTZipXzCompressIoStream(RTVFSIOSTREAM hVfsIosDst, uint32_t fFlags, uint8_t uLevel, PRTVFSIOSTREAM phVfsIosXz)
697{
698 AssertPtrReturn(hVfsIosDst, VERR_INVALID_HANDLE);
699 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
700 AssertPtrReturn(phVfsIosXz, VERR_INVALID_POINTER);
701 AssertReturn(uLevel > 0 && uLevel <= 9, VERR_INVALID_PARAMETER);
702
703 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosDst);
704 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
705
706 /*
707 * Create the compression I/O stream.
708 */
709 RTVFSIOSTREAM hVfsIos;
710 PRTZIPLZMASTREAM pThis;
711 int rc = RTVfsNewIoStream(&g_rtZipLzmaOps, sizeof(RTZIPLZMASTREAM), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
712 &hVfsIos, (void **)&pThis);
713 if (RT_SUCCESS(rc))
714 {
715 pThis->hVfsIos = hVfsIosDst;
716 pThis->offStream = 0;
717 pThis->fDecompress = false;
718 pThis->SgSeg.pvSeg = &pThis->abBuffer[0];
719 pThis->SgSeg.cbSeg = sizeof(pThis->abBuffer);
720 RTSgBufInit(&pThis->SgBuf, &pThis->SgSeg, 1);
721
722 RT_ZERO(pThis->Lzma);
723 pThis->Lzma.next_out = &pThis->abBuffer[0];
724 pThis->Lzma.avail_out = sizeof(pThis->abBuffer);
725
726 lzma_ret rcLzma = lzma_easy_encoder(&pThis->Lzma, uLevel, LZMA_CHECK_CRC64);
727 if (rcLzma == LZMA_OK)
728 {
729 *phVfsIosXz = hVfsIos;
730 return VINF_SUCCESS;
731 }
732
733 rc = rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
734 RTVfsIoStrmRelease(hVfsIos);
735 }
736 else
737 RTVfsIoStrmRelease(hVfsIosDst);
738 return rc;
739}
740
741
742
743/**
744 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
745 */
746static DECLCALLBACK(int) rtVfsChainXzDec_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
747 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
748{
749 RT_NOREF(pProviderReg, poffError, pErrInfo);
750
751 if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
752 return VERR_VFS_CHAIN_ONLY_IOS;
753 if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
754 return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
755 if ( pElement->enmTypeIn != RTVFSOBJTYPE_FILE
756 && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM)
757 return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS;
758 if (pSpec->fOpenFile & RTFILE_O_WRITE)
759 return VERR_VFS_CHAIN_READ_ONLY_IOS;
760 if (pElement->cArgs != 0)
761 return VERR_VFS_CHAIN_NO_ARGS;
762
763 return VINF_SUCCESS;
764}
765
766
767/**
768 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
769 */
770static DECLCALLBACK(int) rtVfsChainXzDec_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
771 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
772 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
773{
774 RT_NOREF(pProviderReg, pSpec, pElement, poffError, pErrInfo);
775 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
776
777 RTVFSIOSTREAM hVfsIosIn = RTVfsObjToIoStream(hPrevVfsObj);
778 if (hVfsIosIn == NIL_RTVFSIOSTREAM)
779 return VERR_VFS_CHAIN_CAST_FAILED;
780
781 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
782 int rc = RTZipXzDecompressIoStream(hVfsIosIn, 0 /*fFlags*/, &hVfsIos);
783 RTVfsObjFromIoStream(hVfsIosIn);
784 if (RT_SUCCESS(rc))
785 {
786 *phVfsObj = RTVfsObjFromIoStream(hVfsIos);
787 RTVfsIoStrmRelease(hVfsIos);
788 if (*phVfsObj != NIL_RTVFSOBJ)
789 return VINF_SUCCESS;
790 rc = VERR_VFS_CHAIN_CAST_FAILED;
791 }
792 return rc;
793}
794
795
796/**
797 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
798 */
799static DECLCALLBACK(bool) rtVfsChainXzDec_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
800 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
801 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
802{
803 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
804 return false;
805}
806
807
808/** VFS chain element 'lzmadec'. */
809static RTVFSCHAINELEMENTREG g_rtVfsChainXzDecReg =
810{
811 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
812 /* fReserved = */ 0,
813 /* pszName = */ "xzdec",
814 /* ListEntry = */ { NULL, NULL },
815 /* pszHelp = */ "Takes an xz compressed I/O stream and decompresses it. No arguments.",
816 /* pfnValidate = */ rtVfsChainXzDec_Validate,
817 /* pfnInstantiate = */ rtVfsChainXzDec_Instantiate,
818 /* pfnCanReuseElement = */ rtVfsChainXzDec_CanReuseElement,
819 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
820};
821
822RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainXzDecReg, rtVfsChainXzDecReg);
823
824
825
826/**
827 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
828 */
829static DECLCALLBACK(int) rtVfsChainXz_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
830 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
831{
832 RT_NOREF(pProviderReg);
833
834 /*
835 * Basics.
836 */
837 if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
838 return VERR_VFS_CHAIN_ONLY_IOS;
839 if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
840 return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
841 if ( pElement->enmTypeIn != RTVFSOBJTYPE_FILE
842 && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM)
843 return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS;
844 if (pSpec->fOpenFile & RTFILE_O_READ)
845 return VERR_VFS_CHAIN_WRITE_ONLY_IOS;
846 if (pElement->cArgs > 1)
847 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
848
849 /*
850 * Optional argument 1..9 indicating the compression level.
851 * We store it in pSpec->uProvider.
852 */
853 if (pElement->cArgs > 0)
854 {
855 const char *psz = pElement->paArgs[0].psz;
856 if (!*psz || !strcmp(psz, "default"))
857 pElement->uProvider = 6;
858 else if (!strcmp(psz, "fast"))
859 pElement->uProvider = 3;
860 else if ( RT_C_IS_DIGIT(*psz)
861 && *psz != '0'
862 && *RTStrStripL(psz + 1) == '\0')
863 pElement->uProvider = *psz - '0';
864 else
865 {
866 *poffError = pElement->paArgs[0].offSpec;
867 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected compression level: 1-9, default, or fast");
868 }
869 }
870 else
871 pElement->uProvider = 6;
872
873 return VINF_SUCCESS;
874}
875
876
877/**
878 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
879 */
880static DECLCALLBACK(int) rtVfsChainXz_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
881 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
882 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
883{
884 RT_NOREF(pProviderReg, pSpec, pElement, poffError, pErrInfo);
885 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
886
887 RTVFSIOSTREAM hVfsIosOut = RTVfsObjToIoStream(hPrevVfsObj);
888 if (hVfsIosOut == NIL_RTVFSIOSTREAM)
889 return VERR_VFS_CHAIN_CAST_FAILED;
890
891 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
892 int rc = RTZipXzCompressIoStream(hVfsIosOut, 0 /*fFlags*/, pElement->uProvider, &hVfsIos);
893 RTVfsObjFromIoStream(hVfsIosOut);
894 if (RT_SUCCESS(rc))
895 {
896 *phVfsObj = RTVfsObjFromIoStream(hVfsIos);
897 RTVfsIoStrmRelease(hVfsIos);
898 if (*phVfsObj != NIL_RTVFSOBJ)
899 return VINF_SUCCESS;
900 rc = VERR_VFS_CHAIN_CAST_FAILED;
901 }
902 return rc;
903}
904
905
906/**
907 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
908 */
909static DECLCALLBACK(bool) rtVfsChainXz_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
910 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
911 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
912{
913 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
914 return false;
915}
916
917
918/** VFS chain element 'xz'. */
919static RTVFSCHAINELEMENTREG g_rtVfsChainXzReg =
920{
921 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
922 /* fReserved = */ 0,
923 /* pszName = */ "xz",
924 /* ListEntry = */ { NULL, NULL },
925 /* pszHelp = */ "Takes an I/O stream and compresses it with xz.\n"
926 "Optional argument specifying compression level: 1-9, default, fast",
927 /* pfnValidate = */ rtVfsChainXz_Validate,
928 /* pfnInstantiate = */ rtVfsChainXz_Instantiate,
929 /* pfnCanReuseElement = */ rtVfsChainXz_CanReuseElement,
930 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
931};
932
933RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainXzReg, rtVfsChainXzReg);
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use