VirtualBox

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

Last change on this file since 100594 was 98766, checked in by vboxsync, 2 years ago

Runtime/lzmavfs.cpp: Need to include asm.h for RT_LE2H_U32 to work, bugref:10254

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 KB
Line 
1/* $Id: lzmavfs.cpp 98766 2023-02-28 08:56:51Z 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 */
218static int rtZipLzma_ReadOneSeg(PRTZIPLZMASTREAM pThis, void *pvBuf, size_t cbToRead, bool fBlocking, size_t *pcbRead)
219{
220 /*
221 * This simplifies life a wee bit below.
222 */
223 if (pThis->fEndOfStream)
224 return pcbRead ? VINF_EOF : VERR_EOF;
225
226 /*
227 * Set up the output buffer.
228 */
229 pThis->Lzma.next_out = (uint8_t *)pvBuf;
230 pThis->Lzma.avail_out = cbToRead;
231
232 /*
233 * Be greedy reading input, even if no output buffer is left. It's possible
234 * that it's just the end of stream marker which needs to be read. Happens
235 * for incompressible blocks just larger than the input buffer size.
236 */
237 int rc = VINF_SUCCESS;
238 while ( pThis->Lzma.avail_out > 0
239 || pThis->Lzma.avail_in == 0 /* greedy */)
240 {
241 /* Read more input? */
242 if (pThis->Lzma.avail_in == 0)
243 {
244 size_t cbReadIn = ~(size_t)0;
245 rc = RTVfsIoStrmSgRead(pThis->hVfsIos, -1 /*off*/, &pThis->SgBuf, fBlocking, &cbReadIn);
246 if (rc != VINF_SUCCESS)
247 {
248 AssertMsg(RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || rc == VINF_EOF, ("%Rrc\n", rc));
249 if (rc == VERR_INTERRUPTED)
250 {
251 Assert(cbReadIn == 0);
252 continue;
253 }
254 if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || cbReadIn == 0)
255 {
256 Assert(cbReadIn == 0);
257 break;
258 }
259 else
260 {
261 AssertMsg(rc == VINF_EOF, ("%Rrc\n", rc));
262 pThis->enmLzmaAction = LZMA_FINISH;
263 }
264 }
265 AssertMsgBreakStmt(cbReadIn > 0 && cbReadIn <= sizeof(pThis->abBuffer), ("%zu %Rrc\n", cbReadIn, rc),
266 rc = VERR_INTERNAL_ERROR_4);
267
268 pThis->Lzma.avail_in = cbReadIn;
269 pThis->Lzma.next_in = &pThis->abBuffer[0];
270 }
271
272 /*
273 * Pass it on to lzma.
274 */
275 lzma_ret rcLzma = lzma_code(&pThis->Lzma, pThis->enmLzmaAction);
276 if (rcLzma != LZMA_OK && rcLzma != LZMA_BUF_ERROR)
277 {
278 if (rcLzma == LZMA_STREAM_END)
279 {
280 pThis->fEndOfStream = true;
281 if (pThis->Lzma.avail_out == 0)
282 rc = VINF_SUCCESS;
283 else
284 rc = pcbRead ? VINF_EOF : VERR_EOF;
285 }
286 else
287 rc = rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
288 break;
289 }
290 rc = VINF_SUCCESS;
291 }
292
293 /*
294 * Update the read counters before returning.
295 */
296 size_t const cbRead = cbToRead - pThis->Lzma.avail_out;
297 pThis->offStream += cbRead;
298 if (pcbRead)
299 *pcbRead = cbRead;
300
301 return rc;
302}
303
304
305/**
306 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
307 */
308static DECLCALLBACK(int) rtZipLzma_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
309{
310 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
311
312 Assert(pSgBuf->cSegs == 1);
313 if (!pThis->fDecompress)
314 return VERR_ACCESS_DENIED;
315 AssertReturn(off == -1 || off == pThis->offStream , VERR_INVALID_PARAMETER);
316
317 return rtZipLzma_ReadOneSeg(pThis, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, fBlocking, pcbRead);
318}
319
320
321/**
322 * Internal helper for rtZipLzma_Write, rtZipLzma_Flush and rtZipLzma_Close.
323 *
324 * @returns IPRT status code.
325 * @retval VINF_SUCCESS
326 * @retval VINF_TRY_AGAIN - the only informational status.
327 * @retval VERR_INTERRUPTED - call again.
328 *
329 * @param pThis The lzma I/O stream instance data.
330 * @param fBlocking Whether to block or not.
331 */
332static int rtZipLzma_WriteOutputBuffer(PRTZIPLZMASTREAM pThis, bool fBlocking)
333{
334 /*
335 * Anything to write? No, then just return immediately.
336 */
337 size_t cbToWrite = sizeof(pThis->abBuffer) - pThis->Lzma.avail_out;
338 if (cbToWrite == 0)
339 {
340 Assert(pThis->Lzma.next_out == &pThis->abBuffer[0]);
341 return VINF_SUCCESS;
342 }
343 Assert(cbToWrite <= sizeof(pThis->abBuffer));
344
345 /*
346 * Loop write on VERR_INTERRUPTED.
347 *
348 * Note! Asserting a bit extra here to make sure the
349 * RTVfsIoStrmSgWrite works correctly.
350 */
351 int rc;
352 size_t cbWrittenOut;
353 for (;;)
354 {
355 /* Set up the buffer. */
356 pThis->SgSeg.cbSeg = cbToWrite;
357 Assert(pThis->SgSeg.pvSeg == &pThis->abBuffer[0]);
358 RTSgBufReset(&pThis->SgBuf);
359
360 cbWrittenOut = ~(size_t)0;
361 rc = RTVfsIoStrmSgWrite(pThis->hVfsIos, -1 /*off*/, &pThis->SgBuf, fBlocking, &cbWrittenOut);
362 if (rc != VINF_SUCCESS)
363 {
364 AssertMsg(RT_FAILURE(rc) || rc == VINF_TRY_AGAIN, ("%Rrc\n", rc));
365 if (rc == VERR_INTERRUPTED)
366 {
367 Assert(cbWrittenOut == 0);
368 continue;
369 }
370 if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || cbWrittenOut == 0)
371 {
372 AssertReturn(cbWrittenOut == 0, VERR_INTERNAL_ERROR_3);
373 AssertReturn(rc != VINF_SUCCESS, VERR_IPE_UNEXPECTED_INFO_STATUS);
374 return rc;
375 }
376 }
377 break;
378 }
379 AssertMsgReturn(cbWrittenOut > 0 && cbWrittenOut <= sizeof(pThis->abBuffer),
380 ("%zu %Rrc\n", cbWrittenOut, rc),
381 VERR_INTERNAL_ERROR_4);
382
383 /*
384 * Adjust the Lzma output buffer members.
385 */
386 if (cbWrittenOut == pThis->SgBuf.paSegs[0].cbSeg)
387 {
388 pThis->Lzma.avail_out = sizeof(pThis->abBuffer);
389 pThis->Lzma.next_out = &pThis->abBuffer[0];
390 }
391 else
392 {
393 Assert(cbWrittenOut <= pThis->SgBuf.paSegs[0].cbSeg);
394 size_t cbLeft = pThis->SgBuf.paSegs[0].cbSeg - cbWrittenOut;
395 memmove(&pThis->abBuffer[0], &pThis->abBuffer[cbWrittenOut], cbLeft);
396 pThis->Lzma.avail_out += cbWrittenOut;
397 pThis->Lzma.next_out = &pThis->abBuffer[cbWrittenOut];
398 }
399
400 return VINF_SUCCESS;
401}
402
403
404/**
405 * Processes all available input.
406 *
407 * @returns IPRT status code.
408 *
409 * @param pThis The lzma I/O stream instance data.
410 * @param fBlocking Whether to block or not.
411 */
412static int rtZipLzma_CompressIt(PRTZIPLZMASTREAM pThis, bool fBlocking)
413{
414 /*
415 * Processes all the input currently lined up for us.
416 */
417 while (pThis->Lzma.avail_in > 0)
418 {
419 /* Make sure there is some space in the output buffer before calling
420 deflate() so we don't waste time filling up the corners. */
421 static const size_t s_cbFlushThreshold = 4096;
422 AssertCompile(sizeof(pThis->abBuffer) >= s_cbFlushThreshold * 4);
423 if (pThis->Lzma.avail_out < s_cbFlushThreshold)
424 {
425 int rc = rtZipLzma_WriteOutputBuffer(pThis, fBlocking);
426 if (rc != VINF_SUCCESS)
427 return rc;
428 Assert(pThis->Lzma.avail_out >= s_cbFlushThreshold);
429 }
430
431 lzma_ret rcLzma = lzma_code(&pThis->Lzma, LZMA_RUN);
432 if (rcLzma != LZMA_OK)
433 return rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
434 }
435 return VINF_SUCCESS;
436}
437
438
439/**
440 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
441 */
442static DECLCALLBACK(int) rtZipLzma_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
443{
444 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
445
446 Assert(pSgBuf->cSegs == 1); NOREF(fBlocking);
447 if (pThis->fDecompress)
448 return VERR_ACCESS_DENIED;
449 AssertReturn(off == -1 || off == pThis->offStream , VERR_INVALID_PARAMETER);
450
451 /*
452 * Write out the input buffer. Using a loop here because of potential
453 * integer type overflow since avail_in is uInt and cbSeg is size_t.
454 */
455 int rc = VINF_SUCCESS;
456 size_t cbWritten = 0;
457 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
458 size_t cbLeft = pSgBuf->paSegs[0].cbSeg;
459 if (cbLeft > 0)
460 for (;;)
461 {
462 size_t cbThis = cbLeft < ~(size_t)0 ? cbLeft : ~(size_t)0 / 2;
463 pThis->Lzma.next_in = pbSrc;
464 pThis->Lzma.avail_in = cbThis;
465 rc = rtZipLzma_CompressIt(pThis, fBlocking);
466
467 Assert(cbThis >= pThis->Lzma.avail_in);
468 cbThis -= pThis->Lzma.avail_in;
469 cbWritten += cbThis;
470 if (cbLeft == cbThis || rc != VINF_SUCCESS)
471 break;
472 pbSrc += cbThis;
473 cbLeft -= cbThis;
474 }
475
476 pThis->offStream += cbWritten;
477 if (pcbWritten)
478 *pcbWritten = cbWritten;
479 return rc;
480}
481
482
483/**
484 * Processes all available input.
485 *
486 * @returns IPRT status code.
487 *
488 * @param pThis The lzma I/O stream instance data.
489 * @param enmFlushType The flush type to pass to lzma_run().
490 */
491static int rtZipLzma_FlushIt(PRTZIPLZMASTREAM pThis, lzma_action enmFlushType)
492{
493 /*
494 * Tell Zlib to flush until it stops producing more output.
495 */
496 int rc;
497 bool fMaybeMore = true;
498 for (;;)
499 {
500 /* Write the entire output buffer. */
501 do
502 {
503 rc = rtZipLzma_WriteOutputBuffer(pThis, true /*fBlocking*/);
504 if (RT_FAILURE(rc))
505 return rc;
506 Assert(rc == VINF_SUCCESS);
507 } while (pThis->Lzma.avail_out < sizeof(pThis->abBuffer));
508
509 if (!fMaybeMore)
510 return VINF_SUCCESS;
511
512 /* Do the flushing. */
513 pThis->Lzma.next_in = NULL;
514 pThis->Lzma.avail_in = 0;
515 lzma_ret rcLzma = lzma_code(&pThis->Lzma, enmFlushType);
516 if (rcLzma == LZMA_OK)
517 fMaybeMore = pThis->Lzma.avail_out < 64 || enmFlushType == LZMA_FINISH;
518 else if (rcLzma == LZMA_STREAM_END)
519 fMaybeMore = false;
520 else
521 {
522 rtZipLzma_WriteOutputBuffer(pThis, true /*fBlocking*/);
523 return rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
524 }
525 }
526}
527
528
529/**
530 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
531 */
532static DECLCALLBACK(int) rtZipLzma_Flush(void *pvThis)
533{
534 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
535 if (!pThis->fDecompress)
536 {
537 int rc = rtZipLzma_FlushIt(pThis, LZMA_SYNC_FLUSH);
538 if (RT_FAILURE(rc))
539 return rc;
540 }
541
542 return RTVfsIoStrmFlush(pThis->hVfsIos);
543}
544
545
546/**
547 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
548 */
549static DECLCALLBACK(int) rtZipLzma_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
550 uint32_t *pfRetEvents)
551{
552 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
553
554 /*
555 * Collect our own events first and see if that satisfies the request. If
556 * not forward the call to the compressed stream.
557 */
558 uint32_t fRetEvents = 0;
559 if (pThis->fFatalError)
560 fRetEvents |= RTPOLL_EVT_ERROR;
561 if (pThis->fDecompress)
562 {
563 fEvents &= ~RTPOLL_EVT_WRITE;
564 if (pThis->Lzma.avail_in > 0)
565 fRetEvents = RTPOLL_EVT_READ;
566 }
567 else
568 {
569 fEvents &= ~RTPOLL_EVT_READ;
570 if (pThis->Lzma.avail_out > 0)
571 fRetEvents = RTPOLL_EVT_WRITE;
572 }
573
574 int rc = VINF_SUCCESS;
575 fRetEvents &= fEvents;
576 if (!fRetEvents)
577 rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
578 return rc;
579}
580
581
582/**
583 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
584 */
585static DECLCALLBACK(int) rtZipLzma_Tell(void *pvThis, PRTFOFF poffActual)
586{
587 PRTZIPLZMASTREAM pThis = (PRTZIPLZMASTREAM)pvThis;
588 *poffActual = pThis->offStream;
589 return VINF_SUCCESS;
590}
591
592
593/**
594 * The LZMA I/O stream vtable.
595 */
596static RTVFSIOSTREAMOPS g_rtZipLzmaOps =
597{
598 { /* Obj */
599 RTVFSOBJOPS_VERSION,
600 RTVFSOBJTYPE_IO_STREAM,
601 "lzma",
602 rtZipLzma_Close,
603 rtZipLzma_QueryInfo,
604 NULL,
605 RTVFSOBJOPS_VERSION
606 },
607 RTVFSIOSTREAMOPS_VERSION,
608 RTVFSIOSTREAMOPS_FEAT_NO_SG,
609 rtZipLzma_Read,
610 rtZipLzma_Write,
611 rtZipLzma_Flush,
612 rtZipLzma_PollOne,
613 rtZipLzma_Tell,
614 NULL /* Skip */,
615 NULL /*ZeroFill*/,
616 RTVFSIOSTREAMOPS_VERSION,
617};
618
619
620RTDECL(int) RTZipXzDecompressIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSIOSTREAM phVfsIosOut)
621{
622 AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
623 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
624 AssertPtrReturn(phVfsIosOut, VERR_INVALID_POINTER);
625
626 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
627 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
628
629 /*
630 * Create the decompression I/O stream.
631 */
632 RTVFSIOSTREAM hVfsIos;
633 PRTZIPLZMASTREAM pThis;
634 int rc = RTVfsNewIoStream(&g_rtZipLzmaOps, sizeof(*pThis), RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
635 &hVfsIos, (void **)&pThis);
636 if (RT_SUCCESS(rc))
637 {
638 pThis->hVfsIos = hVfsIosIn;
639 pThis->offStream = 0;
640 pThis->fDecompress = true;
641 pThis->SgSeg.pvSeg = &pThis->abBuffer[0];
642 pThis->SgSeg.cbSeg = sizeof(pThis->abBuffer);
643 pThis->enmLzmaAction = LZMA_RUN;
644 RTSgBufInit(&pThis->SgBuf, &pThis->SgSeg, 1);
645
646 memset(&pThis->Lzma, 0, sizeof(pThis->Lzma));
647 lzma_ret rcLzma = lzma_stream_decoder(&pThis->Lzma, UINT64_MAX, LZMA_CONCATENATED);
648 if (rcLzma == LZMA_OK)
649 {
650 /*
651 * Read the XZ header from the input stream to check that it's
652 * a xz stream as specified by the user.
653 */
654 rc = RTVfsIoStrmRead(pThis->hVfsIos, pThis->abBuffer, sizeof(RTZIPXZHDR), true /*fBlocking*/, NULL /*pcbRead*/);
655 if (RT_SUCCESS(rc))
656 {
657 /* Validate the header. */
658 static const uint8_t s_abXzMagic[6] = RTZIPXZHDR_MAGIC;
659 PCRTZIPXZHDR pHdr = (PCRTZIPXZHDR)pThis->abBuffer;
660 uint8_t bChkMethod = pHdr->bStrmFlags2 & 0xf;
661
662 if ( !memcmp(&pHdr->abMagic[0], &s_abXzMagic[0], sizeof(s_abXzMagic))
663 && pHdr->bStrmFlags1 == 0
664 && (pHdr->bStrmFlags2 & 0xf0) == 0
665 && ( bChkMethod == RTZIPXZHDR_CHECK_NONE
666 || bChkMethod == RTZIPXZHDR_CHECK_CRC32
667 || bChkMethod == RTZIPXZHDR_CHECK_CRC64
668 || bChkMethod == RTZIPXZHDR_CHECK_SHA256)
669 && RTCrc32(&pHdr->bStrmFlags1, 2) == RT_LE2H_U32(pHdr->u32Crc))
670 {
671 pThis->Lzma.avail_in = sizeof(RTZIPXZHDR);
672 pThis->Lzma.next_in = &pThis->abBuffer[0];
673 *phVfsIosOut = hVfsIos;
674 return VINF_SUCCESS;
675 }
676 else
677 rc = VERR_ZIP_BAD_HEADER;
678 }
679 }
680 else
681 rc = rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
682 RTVfsIoStrmRelease(hVfsIos);
683 }
684 else
685 RTVfsIoStrmRelease(hVfsIosIn);
686 return rc;
687}
688
689
690RTDECL(int) RTZipXzCompressIoStream(RTVFSIOSTREAM hVfsIosDst, uint32_t fFlags, uint8_t uLevel, PRTVFSIOSTREAM phVfsIosXz)
691{
692 AssertPtrReturn(hVfsIosDst, VERR_INVALID_HANDLE);
693 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
694 AssertPtrReturn(phVfsIosXz, VERR_INVALID_POINTER);
695 AssertReturn(uLevel > 0 && uLevel <= 9, VERR_INVALID_PARAMETER);
696
697 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosDst);
698 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
699
700 /*
701 * Create the compression I/O stream.
702 */
703 RTVFSIOSTREAM hVfsIos;
704 PRTZIPLZMASTREAM pThis;
705 int rc = RTVfsNewIoStream(&g_rtZipLzmaOps, sizeof(RTZIPLZMASTREAM), RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
706 &hVfsIos, (void **)&pThis);
707 if (RT_SUCCESS(rc))
708 {
709 pThis->hVfsIos = hVfsIosDst;
710 pThis->offStream = 0;
711 pThis->fDecompress = false;
712 pThis->SgSeg.pvSeg = &pThis->abBuffer[0];
713 pThis->SgSeg.cbSeg = sizeof(pThis->abBuffer);
714 RTSgBufInit(&pThis->SgBuf, &pThis->SgSeg, 1);
715
716 RT_ZERO(pThis->Lzma);
717 pThis->Lzma.next_out = &pThis->abBuffer[0];
718 pThis->Lzma.avail_out = sizeof(pThis->abBuffer);
719
720 lzma_ret rcLzma = lzma_easy_encoder(&pThis->Lzma, uLevel, LZMA_CHECK_CRC64);
721 if (rcLzma == LZMA_OK)
722 {
723 *phVfsIosXz = hVfsIos;
724 return VINF_SUCCESS;
725 }
726
727 rc = rtZipLzmaConvertErrFromLzma(pThis, rcLzma);
728 RTVfsIoStrmRelease(hVfsIos);
729 }
730 else
731 RTVfsIoStrmRelease(hVfsIosDst);
732 return rc;
733}
734
735
736
737/**
738 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
739 */
740static DECLCALLBACK(int) rtVfsChainXzDec_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
741 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
742{
743 RT_NOREF(pProviderReg, poffError, pErrInfo);
744
745 if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
746 return VERR_VFS_CHAIN_ONLY_IOS;
747 if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
748 return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
749 if ( pElement->enmTypeIn != RTVFSOBJTYPE_FILE
750 && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM)
751 return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS;
752 if (pSpec->fOpenFile & RTFILE_O_WRITE)
753 return VERR_VFS_CHAIN_READ_ONLY_IOS;
754 if (pElement->cArgs != 0)
755 return VERR_VFS_CHAIN_NO_ARGS;
756
757 return VINF_SUCCESS;
758}
759
760
761/**
762 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
763 */
764static DECLCALLBACK(int) rtVfsChainXzDec_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
765 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
766 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
767{
768 RT_NOREF(pProviderReg, pSpec, pElement, poffError, pErrInfo);
769 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
770
771 RTVFSIOSTREAM hVfsIosIn = RTVfsObjToIoStream(hPrevVfsObj);
772 if (hVfsIosIn == NIL_RTVFSIOSTREAM)
773 return VERR_VFS_CHAIN_CAST_FAILED;
774
775 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
776 int rc = RTZipXzDecompressIoStream(hVfsIosIn, 0 /*fFlags*/, &hVfsIos);
777 RTVfsObjFromIoStream(hVfsIosIn);
778 if (RT_SUCCESS(rc))
779 {
780 *phVfsObj = RTVfsObjFromIoStream(hVfsIos);
781 RTVfsIoStrmRelease(hVfsIos);
782 if (*phVfsObj != NIL_RTVFSOBJ)
783 return VINF_SUCCESS;
784 rc = VERR_VFS_CHAIN_CAST_FAILED;
785 }
786 return rc;
787}
788
789
790/**
791 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
792 */
793static DECLCALLBACK(bool) rtVfsChainXzDec_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
794 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
795 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
796{
797 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
798 return false;
799}
800
801
802/** VFS chain element 'lzmadec'. */
803static RTVFSCHAINELEMENTREG g_rtVfsChainXzDecReg =
804{
805 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
806 /* fReserved = */ 0,
807 /* pszName = */ "xzdec",
808 /* ListEntry = */ { NULL, NULL },
809 /* pszHelp = */ "Takes an xz compressed I/O stream and decompresses it. No arguments.",
810 /* pfnValidate = */ rtVfsChainXzDec_Validate,
811 /* pfnInstantiate = */ rtVfsChainXzDec_Instantiate,
812 /* pfnCanReuseElement = */ rtVfsChainXzDec_CanReuseElement,
813 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
814};
815
816RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainXzDecReg, rtVfsChainXzDecReg);
817
818
819
820/**
821 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
822 */
823static DECLCALLBACK(int) rtVfsChainXz_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
824 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
825{
826 RT_NOREF(pProviderReg);
827
828 /*
829 * Basics.
830 */
831 if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
832 return VERR_VFS_CHAIN_ONLY_IOS;
833 if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
834 return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
835 if ( pElement->enmTypeIn != RTVFSOBJTYPE_FILE
836 && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM)
837 return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS;
838 if (pSpec->fOpenFile & RTFILE_O_READ)
839 return VERR_VFS_CHAIN_WRITE_ONLY_IOS;
840 if (pElement->cArgs > 1)
841 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
842
843 /*
844 * Optional argument 1..9 indicating the compression level.
845 * We store it in pSpec->uProvider.
846 */
847 if (pElement->cArgs > 0)
848 {
849 const char *psz = pElement->paArgs[0].psz;
850 if (!*psz || !strcmp(psz, "default"))
851 pElement->uProvider = 6;
852 else if (!strcmp(psz, "fast"))
853 pElement->uProvider = 3;
854 else if ( RT_C_IS_DIGIT(*psz)
855 && *psz != '0'
856 && *RTStrStripL(psz + 1) == '\0')
857 pElement->uProvider = *psz - '0';
858 else
859 {
860 *poffError = pElement->paArgs[0].offSpec;
861 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected compression level: 1-9, default, or fast");
862 }
863 }
864 else
865 pElement->uProvider = 6;
866
867 return VINF_SUCCESS;
868}
869
870
871/**
872 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
873 */
874static DECLCALLBACK(int) rtVfsChainXz_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
875 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
876 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
877{
878 RT_NOREF(pProviderReg, pSpec, pElement, poffError, pErrInfo);
879 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
880
881 RTVFSIOSTREAM hVfsIosOut = RTVfsObjToIoStream(hPrevVfsObj);
882 if (hVfsIosOut == NIL_RTVFSIOSTREAM)
883 return VERR_VFS_CHAIN_CAST_FAILED;
884
885 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
886 int rc = RTZipXzCompressIoStream(hVfsIosOut, 0 /*fFlags*/, pElement->uProvider, &hVfsIos);
887 RTVfsObjFromIoStream(hVfsIosOut);
888 if (RT_SUCCESS(rc))
889 {
890 *phVfsObj = RTVfsObjFromIoStream(hVfsIos);
891 RTVfsIoStrmRelease(hVfsIos);
892 if (*phVfsObj != NIL_RTVFSOBJ)
893 return VINF_SUCCESS;
894 rc = VERR_VFS_CHAIN_CAST_FAILED;
895 }
896 return rc;
897}
898
899
900/**
901 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
902 */
903static DECLCALLBACK(bool) rtVfsChainXz_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
904 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
905 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
906{
907 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
908 return false;
909}
910
911
912/** VFS chain element 'xz'. */
913static RTVFSCHAINELEMENTREG g_rtVfsChainXzReg =
914{
915 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
916 /* fReserved = */ 0,
917 /* pszName = */ "xz",
918 /* ListEntry = */ { NULL, NULL },
919 /* pszHelp = */ "Takes an I/O stream and compresses it with xz.\n"
920 "Optional argument specifying compression level: 1-9, default, fast",
921 /* pfnValidate = */ rtVfsChainXz_Validate,
922 /* pfnInstantiate = */ rtVfsChainXz_Instantiate,
923 /* pfnCanReuseElement = */ rtVfsChainXz_CanReuseElement,
924 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
925};
926
927RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainXzReg, rtVfsChainXzReg);
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