VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/ApplianceImplIO.cpp@ 47469

Last change on this file since 47469 was 47357, checked in by vboxsync, 11 years ago

This is how I meant you should do decompression.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.6 KB
Line 
1/* $Id: ApplianceImplIO.cpp 47357 2013-07-23 17:45:24Z vboxsync $ */
2/** @file
3 * IO helper for IAppliance COM class implementations.
4 */
5
6/*
7 * Copyright (C) 2010-2013 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
18/******************************************************************************
19 * Header Files *
20 ******************************************************************************/
21
22#include "ProgressImpl.h"
23#include "ApplianceImpl.h"
24#include "ApplianceImplPrivate.h"
25#include "VirtualBoxImpl.h"
26
27#include <iprt/zip.h>
28#include <iprt/tar.h>
29#include <iprt/sha.h>
30#include <iprt/path.h>
31#include <iprt/asm.h>
32#include <iprt/stream.h>
33#include <iprt/circbuf.h>
34#include <iprt/vfs.h>
35#include <VBox/vd.h>
36#include <zlib.h>
37//#define VBOX_MAIN_USE_VFS /** @todo Replace as much as possible with IPRT VFS. */
38
39/******************************************************************************
40 * Structures and Typedefs *
41 ******************************************************************************/
42typedef struct FILESTORAGEINTERNAL
43{
44 /** File handle. */
45 RTFILE file;
46 /** Completion callback. */
47 PFNVDCOMPLETED pfnCompleted;
48} FILESTORAGEINTERNAL, *PFILESTORAGEINTERNAL;
49
50typedef struct TARSTORAGEINTERNAL
51{
52 /** Tar handle. */
53 RTTARFILE file;
54 /** Completion callback. */
55 PFNVDCOMPLETED pfnCompleted;
56} TARSTORAGEINTERNAL, *PTARSTORAGEINTERNAL;
57
58typedef struct SHASTORAGEINTERNAL
59{
60 /** Completion callback. */
61 PFNVDCOMPLETED pfnCompleted;
62 /** Storage handle for the next callback in chain. */
63 void *pvStorage;
64 /** Current file open mode. */
65 uint32_t fOpenMode;
66 /** Our own storage handle. */
67 PSHASTORAGE pShaStorage;
68 /** Circular buffer used for transferring data from/to the worker thread. */
69 PRTCIRCBUF pCircBuf;
70 /** Current absolute position (regardless of the real read/written data). */
71 uint64_t cbCurAll;
72 /** Current real position in the file. */
73 uint64_t cbCurFile;
74 /** Handle of the worker thread. */
75 RTTHREAD pWorkerThread;
76 /** Status of the worker thread. */
77 volatile uint32_t u32Status;
78 /** Event for signaling a new status. */
79 RTSEMEVENT newStatusEvent;
80 /** Event for signaling a finished task of the worker thread. */
81 RTSEMEVENT workFinishedEvent;
82 /** SHA1/SHA256 calculation context. */
83 union
84 {
85 RTSHA1CONTEXT Sha1;
86 RTSHA256CONTEXT Sha256;
87 } ctx;
88 /** Write mode only: Memory buffer for writing zeros. */
89 void *pvZeroBuf;
90 /** Write mode only: Size of the zero memory buffer. */
91 size_t cbZeroBuf;
92 /** Read mode only: Indicate if we reached end of file. */
93 volatile bool fEOF;
94// uint64_t calls;
95// uint64_t waits;
96} SHASTORAGEINTERNAL, *PSHASTORAGEINTERNAL;
97
98/******************************************************************************
99 * Defined Constants And Macros *
100 ******************************************************************************/
101
102#define STATUS_WAIT UINT32_C(0)
103#define STATUS_WRITE UINT32_C(1)
104#define STATUS_WRITING UINT32_C(2)
105#define STATUS_READ UINT32_C(3)
106#define STATUS_READING UINT32_C(4)
107#define STATUS_END UINT32_C(5)
108
109/* Enable for getting some flow history. */
110#if 0
111# define DEBUG_PRINT_FLOW() RTPrintf("%s\n", __FUNCTION__)
112#else
113# define DEBUG_PRINT_FLOW() do {} while (0)
114#endif
115
116/******************************************************************************
117 * Internal Functions *
118 ******************************************************************************/
119
120
121/******************************************************************************
122 * Internal: RTFile interface
123 ******************************************************************************/
124
125static int fileOpenCallback(void * /* pvUser */, const char *pszLocation, uint32_t fOpen,
126 PFNVDCOMPLETED pfnCompleted, void **ppInt)
127{
128 /* Validate input. */
129 AssertPtrReturn(ppInt, VERR_INVALID_POINTER);
130 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
131
132 DEBUG_PRINT_FLOW();
133
134 PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)RTMemAllocZ(sizeof(FILESTORAGEINTERNAL));
135 if (!pInt)
136 return VERR_NO_MEMORY;
137
138 pInt->pfnCompleted = pfnCompleted;
139
140 int rc = RTFileOpen(&pInt->file, pszLocation, fOpen);
141
142 if (RT_FAILURE(rc))
143 RTMemFree(pInt);
144 else
145 *ppInt = pInt;
146
147 return rc;
148}
149
150static int fileCloseCallback(void * /* pvUser */, void *pvStorage)
151{
152 /* Validate input. */
153 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
154
155 PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage;
156
157 DEBUG_PRINT_FLOW();
158
159 int rc = RTFileClose(pInt->file);
160
161 /* Cleanup */
162 RTMemFree(pInt);
163
164 return rc;
165}
166
167static int fileDeleteCallback(void * /* pvUser */, const char *pcszFilename)
168{
169 DEBUG_PRINT_FLOW();
170
171 return RTFileDelete(pcszFilename);
172}
173
174static int fileMoveCallback(void * /* pvUser */, const char *pcszSrc, const char *pcszDst, unsigned fMove)
175{
176 DEBUG_PRINT_FLOW();
177
178 return RTFileMove(pcszSrc, pcszDst, fMove);
179}
180
181static int fileGetFreeSpaceCallback(void * /* pvUser */, const char *pcszFilename, int64_t *pcbFreeSpace)
182{
183 /* Validate input. */
184 AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
185 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
186
187 DEBUG_PRINT_FLOW();
188
189 return VERR_NOT_IMPLEMENTED;
190}
191
192static int fileGetModificationTimeCallback(void * /* pvUser */, const char *pcszFilename, PRTTIMESPEC pModificationTime)
193{
194 /* Validate input. */
195 AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
196 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
197
198 DEBUG_PRINT_FLOW();
199
200 return VERR_NOT_IMPLEMENTED;
201}
202
203static int fileGetSizeCallback(void * /* pvUser */, void *pvStorage, uint64_t *pcbSize)
204{
205 /* Validate input. */
206 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
207
208 PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage;
209
210 DEBUG_PRINT_FLOW();
211
212 return RTFileGetSize(pInt->file, pcbSize);
213}
214
215static int fileSetSizeCallback(void * /* pvUser */, void *pvStorage, uint64_t cbSize)
216{
217 /* Validate input. */
218 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
219
220 PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage;
221
222 DEBUG_PRINT_FLOW();
223
224 return RTFileSetSize(pInt->file, cbSize);
225}
226
227static int fileWriteSyncCallback(void * /* pvUser */, void *pvStorage, uint64_t uOffset,
228 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
229{
230 /* Validate input. */
231 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
232
233 PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage;
234
235 return RTFileWriteAt(pInt->file, uOffset, pvBuf, cbWrite, pcbWritten);
236}
237
238static int fileReadSyncCallback(void * /* pvUser */, void *pvStorage, uint64_t uOffset,
239 void *pvBuf, size_t cbRead, size_t *pcbRead)
240{
241 /* Validate input. */
242 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
243
244// DEBUG_PRINT_FLOW();
245
246 PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage;
247
248 return RTFileReadAt(pInt->file, uOffset, pvBuf, cbRead, pcbRead);
249}
250
251static int fileFlushSyncCallback(void * /* pvUser */, void *pvStorage)
252{
253 /* Validate input. */
254 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
255
256 DEBUG_PRINT_FLOW();
257
258 PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage;
259
260 return RTFileFlush(pInt->file);
261}
262
263/******************************************************************************
264 * Internal: RTTar interface
265 ******************************************************************************/
266
267static int tarOpenCallback(void *pvUser, const char *pszLocation, uint32_t fOpen,
268 PFNVDCOMPLETED pfnCompleted, void **ppInt)
269{
270 /* Validate input. */
271 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
272 AssertPtrReturn(ppInt, VERR_INVALID_POINTER);
273 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
274// AssertReturn(!(fOpen & RTFILE_O_READWRITE), VERR_INVALID_PARAMETER);
275
276 RTTAR tar = (RTTAR)pvUser;
277
278 DEBUG_PRINT_FLOW();
279
280 PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)RTMemAllocZ(sizeof(TARSTORAGEINTERNAL));
281 if (!pInt)
282 return VERR_NO_MEMORY;
283
284 pInt->pfnCompleted = pfnCompleted;
285
286 int rc = VINF_SUCCESS;
287
288 if (fOpen & RTFILE_O_READ
289 && !(fOpen & RTFILE_O_WRITE))
290 {
291 /* Read only is a little bit more complicated than writing, cause we
292 * need streaming functionality. First try to open the file on the
293 * current file position. If this is the file the caller requested, we
294 * are fine. If not seek to the next file in the stream and check
295 * again. This is repeated until EOF of the OVA. */
296 /*
297 *
298 *
299 * TODO: recheck this with more VDMKs (or what else) in an test OVA.
300 *
301 *
302 */
303 bool fFound = false;
304
305 for (;;)
306 {
307 char *pszFilename = 0;
308 rc = RTTarCurrentFile(tar, &pszFilename);
309 if (RT_SUCCESS(rc))
310 {
311 if (rc == VINF_TAR_DIR_PATH)
312 {
313 break;
314 }
315
316 fFound = !RTStrICmp(pszFilename, pszLocation);
317
318 RTStrFree(pszFilename);
319 if (fFound)
320 break;
321 else
322 {
323 rc = RTTarSeekNextFile(tar);
324 if (RT_FAILURE(rc))
325 {
326 break;
327 }
328 }
329 }
330 else
331 break;
332 }
333 if (fFound)
334 rc = RTTarFileOpenCurrentFile(tar, &pInt->file, 0, fOpen);
335 }
336 else
337 rc = RTTarFileOpen(tar, &pInt->file, RTPathFilename(pszLocation), fOpen);
338
339 if (RT_FAILURE(rc))
340 RTMemFree(pInt);
341 else
342 *ppInt = pInt;
343
344 return rc;
345}
346
347static int tarCloseCallback(void *pvUser, void *pvStorage)
348{
349 /* Validate input. */
350 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
351 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
352
353 PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage;
354
355 DEBUG_PRINT_FLOW();
356
357 int rc = RTTarFileClose(pInt->file);
358
359 /* Cleanup */
360 RTMemFree(pInt);
361
362 return rc;
363}
364
365static int tarDeleteCallback(void *pvUser, const char *pcszFilename)
366{
367 /* Validate input. */
368 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
369 AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
370
371 DEBUG_PRINT_FLOW();
372
373 return VERR_NOT_IMPLEMENTED;
374}
375
376static int tarMoveCallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned /* fMove */)
377{
378 /* Validate input. */
379 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
380 AssertPtrReturn(pcszSrc, VERR_INVALID_POINTER);
381 AssertPtrReturn(pcszDst, VERR_INVALID_POINTER);
382
383 DEBUG_PRINT_FLOW();
384
385 return VERR_NOT_IMPLEMENTED;
386}
387
388static int tarGetFreeSpaceCallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
389{
390 /* Validate input. */
391 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
392 AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
393 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
394
395 DEBUG_PRINT_FLOW();
396
397 return VERR_NOT_IMPLEMENTED;
398}
399
400static int tarGetModificationTimeCallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
401{
402 /* Validate input. */
403 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
404 AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
405 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
406
407 DEBUG_PRINT_FLOW();
408
409 return VERR_NOT_IMPLEMENTED;
410}
411
412static int tarGetSizeCallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
413{
414 /* Validate input. */
415 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
416 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
417
418 PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage;
419
420 DEBUG_PRINT_FLOW();
421
422 return RTTarFileGetSize(pInt->file, pcbSize);
423}
424
425static int tarSetSizeCallback(void *pvUser, void *pvStorage, uint64_t cbSize)
426{
427 /* Validate input. */
428 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
429 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
430
431 PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage;
432
433 DEBUG_PRINT_FLOW();
434
435 return RTTarFileSetSize(pInt->file, cbSize);
436}
437
438static int tarWriteSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset,
439 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
440{
441 /* Validate input. */
442 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
443 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
444
445 PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage;
446
447 DEBUG_PRINT_FLOW();
448
449 return RTTarFileWriteAt(pInt->file, uOffset, pvBuf, cbWrite, pcbWritten);
450}
451
452static int tarReadSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset,
453 void *pvBuf, size_t cbRead, size_t *pcbRead)
454{
455 /* Validate input. */
456 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
457 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
458
459 PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage;
460
461// DEBUG_PRINT_FLOW();
462
463 return RTTarFileReadAt(pInt->file, uOffset, pvBuf, cbRead, pcbRead);
464}
465
466static int tarFlushSyncCallback(void *pvUser, void *pvStorage)
467{
468 /* Validate input. */
469 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
470 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
471
472 DEBUG_PRINT_FLOW();
473
474 return VERR_NOT_IMPLEMENTED;
475}
476
477/******************************************************************************
478 * Internal: RTSha interface
479 ******************************************************************************/
480
481DECLCALLBACK(int) shaCalcWorkerThread(RTTHREAD /* aThread */, void *pvUser)
482{
483 /* Validate input. */
484 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
485
486 PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvUser;
487
488 PVDINTERFACEIO pIfIo = VDIfIoGet(pInt->pShaStorage->pVDImageIfaces);
489 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
490
491 int rc = VINF_SUCCESS;
492 bool fLoop = true;
493 while (fLoop)
494 {
495 /* What should we do next? */
496 uint32_t u32Status = ASMAtomicReadU32(&pInt->u32Status);
497// RTPrintf("status: %d\n", u32Status);
498 switch (u32Status)
499 {
500 case STATUS_WAIT:
501 {
502 /* Wait for new work. */
503 rc = RTSemEventWait(pInt->newStatusEvent, 100);
504 if ( RT_FAILURE(rc)
505 && rc != VERR_TIMEOUT)
506 fLoop = false;
507 break;
508 }
509 case STATUS_WRITE:
510 {
511 ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WRITING, STATUS_WRITE);
512 size_t cbAvail = RTCircBufUsed(pInt->pCircBuf);
513 size_t cbMemAllRead = 0;
514 /* First loop over all the free memory in the circular
515 * memory buffer (could be turn around at the end). */
516 for (;;)
517 {
518 if ( cbMemAllRead == cbAvail
519 || fLoop == false)
520 break;
521 char *pcBuf;
522 size_t cbMemToRead = cbAvail - cbMemAllRead;
523 size_t cbMemRead = 0;
524 /* Try to acquire all the used space of the circular buffer. */
525 RTCircBufAcquireReadBlock(pInt->pCircBuf, cbMemToRead, (void**)&pcBuf, &cbMemRead);
526 size_t cbAllWritten = 0;
527 /* Second, write as long as used memory is there. The write
528 * method could also split the writes up into to smaller
529 * parts. */
530 for (;;)
531 {
532 if (cbAllWritten == cbMemRead)
533 break;
534 size_t cbToWrite = cbMemRead - cbAllWritten;
535 size_t cbWritten = 0;
536 rc = vdIfIoFileWriteSync(pIfIo, pInt->pvStorage, pInt->cbCurFile, &pcBuf[cbAllWritten], cbToWrite, &cbWritten);
537// RTPrintf ("%lu %lu %lu %Rrc\n", pInt->cbCurFile, cbToRead, cbRead, rc);
538 if (RT_FAILURE(rc))
539 {
540 fLoop = false;
541 break;
542 }
543 cbAllWritten += cbWritten;
544 pInt->cbCurFile += cbWritten;
545 }
546 /* Update the SHA1/SHA256 context with the next data block. */
547 if ( RT_SUCCESS(rc)
548 && pInt->pShaStorage->fCreateDigest)
549 {
550 if (pInt->pShaStorage->fSha256)
551 RTSha256Update(&pInt->ctx.Sha256, pcBuf, cbAllWritten);
552 else
553 RTSha1Update(&pInt->ctx.Sha1, pcBuf, cbAllWritten);
554 }
555 /* Mark the block as empty. */
556 RTCircBufReleaseReadBlock(pInt->pCircBuf, cbAllWritten);
557 cbMemAllRead += cbAllWritten;
558 }
559 /* Reset the thread status and signal the main thread that we
560 * are finished. Use CmpXchg, so we not overwrite other states
561 * which could be signaled in the meantime. */
562 if (ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WAIT, STATUS_WRITING))
563 rc = RTSemEventSignal(pInt->workFinishedEvent);
564 break;
565 }
566 case STATUS_READ:
567 {
568 ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_READING, STATUS_READ);
569 size_t cbAvail = RTCircBufFree(pInt->pCircBuf);
570 size_t cbMemAllWrite = 0;
571 /* First loop over all the available memory in the circular
572 * memory buffer (could be turn around at the end). */
573 for (;;)
574 {
575 if ( cbMemAllWrite == cbAvail
576 || fLoop == false)
577 break;
578 char *pcBuf;
579 size_t cbMemToWrite = cbAvail - cbMemAllWrite;
580 size_t cbMemWrite = 0;
581 /* Try to acquire all the free space of the circular buffer. */
582 RTCircBufAcquireWriteBlock(pInt->pCircBuf, cbMemToWrite, (void**)&pcBuf, &cbMemWrite);
583 /* Second, read as long as we filled all the memory. The
584 * read method could also split the reads up into to
585 * smaller parts. */
586 size_t cbAllRead = 0;
587 for (;;)
588 {
589 if (cbAllRead == cbMemWrite)
590 break;
591 size_t cbToRead = cbMemWrite - cbAllRead;
592 size_t cbRead = 0;
593 rc = vdIfIoFileReadSync(pIfIo, pInt->pvStorage, pInt->cbCurFile, &pcBuf[cbAllRead], cbToRead, &cbRead);
594// RTPrintf ("%lu %lu %lu %Rrc\n", pInt->cbCurFile, cbToRead, cbRead, rc);
595 if (RT_FAILURE(rc))
596 {
597 fLoop = false;
598 break;
599 }
600 /* This indicates end of file. Stop reading. */
601 if (cbRead == 0)
602 {
603 fLoop = false;
604 ASMAtomicWriteBool(&pInt->fEOF, true);
605 break;
606 }
607 cbAllRead += cbRead;
608 pInt->cbCurFile += cbRead;
609 }
610 /* Update the SHA1/SHA256 context with the next data block. */
611 if ( RT_SUCCESS(rc)
612 && pInt->pShaStorage->fCreateDigest)
613 {
614 if (pInt->pShaStorage->fSha256)
615 RTSha256Update(&pInt->ctx.Sha256, pcBuf, cbAllRead);
616 else
617 RTSha1Update(&pInt->ctx.Sha1, pcBuf, cbAllRead);
618 }
619 /* Mark the block as full. */
620 RTCircBufReleaseWriteBlock(pInt->pCircBuf, cbAllRead);
621 cbMemAllWrite += cbAllRead;
622 }
623 /* Reset the thread status and signal the main thread that we
624 * are finished. Use CmpXchg, so we not overwrite other states
625 * which could be signaled in the meantime. */
626 if (ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WAIT, STATUS_READING))
627 rc = RTSemEventSignal(pInt->workFinishedEvent);
628 break;
629 }
630 case STATUS_END:
631 {
632 /* End signaled */
633 fLoop = false;
634 break;
635 }
636 }
637 }
638 /* Cleanup any status changes to indicate we are finished. */
639 ASMAtomicWriteU32(&pInt->u32Status, STATUS_END);
640 rc = RTSemEventSignal(pInt->workFinishedEvent);
641 return rc;
642}
643
644DECLINLINE(int) shaSignalManifestThread(PSHASTORAGEINTERNAL pInt, uint32_t uStatus)
645{
646 ASMAtomicWriteU32(&pInt->u32Status, uStatus);
647 return RTSemEventSignal(pInt->newStatusEvent);
648}
649
650DECLINLINE(int) shaWaitForManifestThreadFinished(PSHASTORAGEINTERNAL pInt)
651{
652// RTPrintf("start\n");
653 int rc = VINF_SUCCESS;
654 for (;;)
655 {
656// RTPrintf(" wait\n");
657 uint32_t u32Status = ASMAtomicReadU32(&pInt->u32Status);
658 if (!( u32Status == STATUS_WRITE
659 || u32Status == STATUS_WRITING
660 || u32Status == STATUS_READ
661 || u32Status == STATUS_READING))
662 break;
663 rc = RTSemEventWait(pInt->workFinishedEvent, 100);
664 }
665 if (rc == VERR_TIMEOUT)
666 rc = VINF_SUCCESS;
667 return rc;
668}
669
670DECLINLINE(int) shaFlushCurBuf(PSHASTORAGEINTERNAL pInt)
671{
672 int rc = VINF_SUCCESS;
673 if (pInt->fOpenMode & RTFILE_O_WRITE)
674 {
675 /* Let the write worker thread start immediately. */
676 rc = shaSignalManifestThread(pInt, STATUS_WRITE);
677 if (RT_FAILURE(rc))
678 return rc;
679
680 /* Wait until the write worker thread has finished. */
681 rc = shaWaitForManifestThreadFinished(pInt);
682 }
683
684 return rc;
685}
686
687static int shaOpenCallback(void *pvUser, const char *pszLocation, uint32_t fOpen,
688 PFNVDCOMPLETED pfnCompleted, void **ppInt)
689{
690 /* Validate input. */
691 AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
692 AssertPtrReturn(pszLocation, VERR_INVALID_POINTER);
693 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
694 AssertPtrReturn(ppInt, VERR_INVALID_POINTER);
695 AssertReturn((fOpen & RTFILE_O_READWRITE) != RTFILE_O_READWRITE, VERR_INVALID_PARAMETER); /* No read/write allowed */
696
697 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
698 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
699 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
700
701 DEBUG_PRINT_FLOW();
702
703 PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)RTMemAllocZ(sizeof(SHASTORAGEINTERNAL));
704 if (!pInt)
705 return VERR_NO_MEMORY;
706
707 int rc = VINF_SUCCESS;
708 do
709 {
710 pInt->pfnCompleted = pfnCompleted;
711 pInt->pShaStorage = pShaStorage;
712 pInt->fEOF = false;
713 pInt->fOpenMode = fOpen;
714 pInt->u32Status = STATUS_WAIT;
715
716 /* Circular buffer in the read case. */
717 rc = RTCircBufCreate(&pInt->pCircBuf, _1M * 2);
718 if (RT_FAILURE(rc))
719 break;
720
721 if (fOpen & RTFILE_O_WRITE)
722 {
723 /* The zero buffer is used for appending empty parts at the end of the
724 * file (or our buffer) in setSize or when uOffset in writeSync is
725 * increased in steps bigger than a byte. */
726 pInt->cbZeroBuf = _1K;
727 pInt->pvZeroBuf = RTMemAllocZ(pInt->cbZeroBuf);
728 if (!pInt->pvZeroBuf)
729 {
730 rc = VERR_NO_MEMORY;
731 break;
732 }
733 }
734
735 /* Create an event semaphore to indicate a state change for the worker
736 * thread. */
737 rc = RTSemEventCreate(&pInt->newStatusEvent);
738 if (RT_FAILURE(rc))
739 break;
740 /* Create an event semaphore to indicate a finished calculation of the
741 worker thread. */
742 rc = RTSemEventCreate(&pInt->workFinishedEvent);
743 if (RT_FAILURE(rc))
744 break;
745 /* Create the worker thread. */
746 rc = RTThreadCreate(&pInt->pWorkerThread, shaCalcWorkerThread, pInt, 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, RTTHREADFLAGS_WAITABLE, "SHA-Worker");
747 if (RT_FAILURE(rc))
748 break;
749
750 if (pShaStorage->fCreateDigest)
751 {
752 /* Create a SHA1/SHA256 context the worker thread will work with. */
753 if (pShaStorage->fSha256)
754 RTSha256Init(&pInt->ctx.Sha256);
755 else
756 RTSha1Init(&pInt->ctx.Sha1);
757 }
758
759 /* Open the file. */
760 rc = vdIfIoFileOpen(pIfIo, pszLocation, fOpen, pInt->pfnCompleted,
761 &pInt->pvStorage);
762 if (RT_FAILURE(rc))
763 break;
764
765 if (fOpen & RTFILE_O_READ)
766 {
767 /* Immediately let the worker thread start the reading. */
768 rc = shaSignalManifestThread(pInt, STATUS_READ);
769 }
770 }
771 while (0);
772
773 if (RT_FAILURE(rc))
774 {
775 if (pInt->pWorkerThread)
776 {
777 shaSignalManifestThread(pInt, STATUS_END);
778 RTThreadWait(pInt->pWorkerThread, RT_INDEFINITE_WAIT, 0);
779 }
780 if (pInt->workFinishedEvent)
781 RTSemEventDestroy(pInt->workFinishedEvent);
782 if (pInt->newStatusEvent)
783 RTSemEventDestroy(pInt->newStatusEvent);
784 if (pInt->pCircBuf)
785 RTCircBufDestroy(pInt->pCircBuf);
786 if (pInt->pvZeroBuf)
787 RTMemFree(pInt->pvZeroBuf);
788 RTMemFree(pInt);
789 }
790 else
791 *ppInt = pInt;
792
793 return rc;
794}
795
796static int shaCloseCallback(void *pvUser, void *pvStorage)
797{
798 /* Validate input. */
799 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
800 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
801
802 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
803 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
804 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
805
806 PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage;
807
808 DEBUG_PRINT_FLOW();
809
810 int rc = VINF_SUCCESS;
811
812 /* Make sure all pending writes are flushed */
813 rc = shaFlushCurBuf(pInt);
814
815 if (pInt->pWorkerThread)
816 {
817 /* Signal the worker thread to end himself */
818 rc = shaSignalManifestThread(pInt, STATUS_END);
819 /* Worker thread stopped? */
820 rc = RTThreadWait(pInt->pWorkerThread, RT_INDEFINITE_WAIT, 0);
821 }
822
823 if ( RT_SUCCESS(rc)
824 && pShaStorage->fCreateDigest)
825 {
826 /* Finally calculate & format the SHA1/SHA256 sum */
827 unsigned char auchDig[RTSHA256_HASH_SIZE];
828 char *pszDigest;
829 size_t cbDigest;
830 if (pShaStorage->fSha256)
831 {
832 RTSha256Final(&pInt->ctx.Sha256, auchDig);
833 cbDigest = RTSHA256_DIGEST_LEN;
834 }
835 else
836 {
837 RTSha1Final(&pInt->ctx.Sha1, auchDig);
838 cbDigest = RTSHA1_DIGEST_LEN;
839 }
840 rc = RTStrAllocEx(&pszDigest, cbDigest + 1);
841 if (RT_SUCCESS(rc))
842 {
843 if (pShaStorage->fSha256)
844 rc = RTSha256ToString(auchDig, pszDigest, cbDigest + 1);
845 else
846 rc = RTSha1ToString(auchDig, pszDigest, cbDigest + 1);
847 if (RT_SUCCESS(rc))
848 pShaStorage->strDigest = pszDigest;
849 RTStrFree(pszDigest);
850 }
851 }
852
853 /* Close the file */
854 rc = vdIfIoFileClose(pIfIo, pInt->pvStorage);
855
856// RTPrintf("%lu %lu\n", pInt->calls, pInt->waits);
857
858 /* Cleanup */
859 if (pInt->workFinishedEvent)
860 RTSemEventDestroy(pInt->workFinishedEvent);
861 if (pInt->newStatusEvent)
862 RTSemEventDestroy(pInt->newStatusEvent);
863 if (pInt->pCircBuf)
864 RTCircBufDestroy(pInt->pCircBuf);
865 if (pInt->pvZeroBuf)
866 RTMemFree(pInt->pvZeroBuf);
867 RTMemFree(pInt);
868
869 return rc;
870}
871
872static int shaDeleteCallback(void *pvUser, const char *pcszFilename)
873{
874 /* Validate input. */
875 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
876
877 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
878 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
879 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
880
881 DEBUG_PRINT_FLOW();
882
883 return vdIfIoFileDelete(pIfIo, pcszFilename);
884}
885
886static int shaMoveCallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
887{
888 /* Validate input. */
889 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
890
891 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
892 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
893 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
894
895
896 DEBUG_PRINT_FLOW();
897
898 return vdIfIoFileMove(pIfIo, pcszSrc, pcszDst, fMove);
899}
900
901static int shaGetFreeSpaceCallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
902{
903 /* Validate input. */
904 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
905
906 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
907 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
908 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
909
910 DEBUG_PRINT_FLOW();
911
912 return vdIfIoFileGetFreeSpace(pIfIo, pcszFilename, pcbFreeSpace);
913}
914
915static int shaGetModificationTimeCallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
916{
917 /* Validate input. */
918 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
919
920 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
921 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
922 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
923
924 DEBUG_PRINT_FLOW();
925
926 return vdIfIoFileGetModificationTime(pIfIo, pcszFilename, pModificationTime);
927}
928
929
930static int shaGetSizeCallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
931{
932 /* Validate input. */
933 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
934 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
935
936 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
937 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
938 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
939
940 PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage;
941
942 DEBUG_PRINT_FLOW();
943
944 uint64_t cbSize;
945 int rc = vdIfIoFileGetSize(pIfIo, pInt->pvStorage, &cbSize);
946 if (RT_FAILURE(rc))
947 return rc;
948
949 *pcbSize = RT_MAX(pInt->cbCurAll, cbSize);
950
951 return VINF_SUCCESS;
952}
953
954static int shaSetSizeCallback(void *pvUser, void *pvStorage, uint64_t cbSize)
955{
956 /* Validate input. */
957 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
958 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
959
960 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
961 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
962 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
963
964 PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage;
965
966 DEBUG_PRINT_FLOW();
967
968 return vdIfIoFileSetSize(pIfIo, pInt->pvStorage, cbSize);
969}
970
971static int shaWriteSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset,
972 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
973{
974 /* Validate input. */
975 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
976 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
977
978 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
979 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
980 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
981
982 PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage;
983
984 DEBUG_PRINT_FLOW();
985
986 /* Check that the write is linear */
987 AssertMsgReturn(pInt->cbCurAll <= uOffset, ("Backward seeking is not allowed (uOffset: %7lu cbCurAll: %7lu)!", uOffset, pInt->cbCurAll), VERR_INVALID_PARAMETER);
988
989 int rc = VINF_SUCCESS;
990
991 /* Check if we have to add some free space at the end, before we start the
992 * real write. */
993 if (pInt->cbCurAll < uOffset)
994 {
995 size_t cbSize = (size_t)(uOffset - pInt->cbCurAll);
996 size_t cbAllWritten = 0;
997 for (;;)
998 {
999 /* Finished? */
1000 if (cbAllWritten == cbSize)
1001 break;
1002 size_t cbToWrite = RT_MIN(pInt->cbZeroBuf, cbSize - cbAllWritten);
1003 size_t cbWritten = 0;
1004 rc = shaWriteSyncCallback(pvUser, pvStorage, pInt->cbCurAll,
1005 pInt->pvZeroBuf, cbToWrite, &cbWritten);
1006 if (RT_FAILURE(rc))
1007 break;
1008 cbAllWritten += cbWritten;
1009 }
1010 if (RT_FAILURE(rc))
1011 return rc;
1012 }
1013// RTPrintf("Write uOffset: %7lu cbWrite: %7lu = %7lu\n", uOffset, cbWrite, uOffset + cbWrite);
1014
1015 size_t cbAllWritten = 0;
1016 for (;;)
1017 {
1018 /* Finished? */
1019 if (cbAllWritten == cbWrite)
1020 break;
1021 size_t cbAvail = RTCircBufFree(pInt->pCircBuf);
1022 if ( cbAvail == 0
1023 && pInt->fEOF)
1024 return VERR_EOF;
1025 /* If there isn't enough free space make sure the worker thread is
1026 * writing some data. */
1027 if ((cbWrite - cbAllWritten) > cbAvail)
1028 {
1029 rc = shaSignalManifestThread(pInt, STATUS_WRITE);
1030 if (RT_FAILURE(rc))
1031 break;
1032 /* If there is _no_ free space available, we have to wait until it is. */
1033 if (cbAvail == 0)
1034 {
1035 rc = shaWaitForManifestThreadFinished(pInt);
1036 if (RT_FAILURE(rc))
1037 break;
1038 cbAvail = RTCircBufFree(pInt->pCircBuf);
1039// RTPrintf("############## wait %lu %lu %lu \n", cbRead, cbAllRead, cbAvail);
1040// pInt->waits++;
1041 }
1042 }
1043 size_t cbToWrite = RT_MIN(cbWrite - cbAllWritten, cbAvail);
1044 char *pcBuf;
1045 size_t cbMemWritten = 0;
1046 /* Acquire a block for writing from our circular buffer. */
1047 RTCircBufAcquireWriteBlock(pInt->pCircBuf, cbToWrite, (void**)&pcBuf, &cbMemWritten);
1048 memcpy(pcBuf, &((char*)pvBuf)[cbAllWritten], cbMemWritten);
1049 /* Mark the block full. */
1050 RTCircBufReleaseWriteBlock(pInt->pCircBuf, cbMemWritten);
1051 cbAllWritten += cbMemWritten;
1052 pInt->cbCurAll += cbMemWritten;
1053 }
1054
1055 if (pcbWritten)
1056 *pcbWritten = cbAllWritten;
1057
1058 /* Signal the thread to write more data in the mean time. */
1059 if ( RT_SUCCESS(rc)
1060 && RTCircBufUsed(pInt->pCircBuf) >= (RTCircBufSize(pInt->pCircBuf) / 2))
1061 rc = shaSignalManifestThread(pInt, STATUS_WRITE);
1062
1063 return rc;
1064}
1065
1066static int shaReadSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset,
1067 void *pvBuf, size_t cbRead, size_t *pcbRead)
1068{
1069 /* Validate input. */
1070 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1071 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
1072
1073 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
1074 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
1075 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
1076
1077// DEBUG_PRINT_FLOW();
1078
1079 PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage;
1080
1081 int rc = VINF_SUCCESS;
1082
1083// pInt->calls++;
1084// RTPrintf("Read uOffset: %7lu cbRead: %7lu = %7lu\n", uOffset, cbRead, uOffset + cbRead);
1085
1086 /* Check if we jump forward in the file. If so we have to read the
1087 * remaining stuff in the gap anyway (SHA1/SHA256; streaming). */
1088 if (pInt->cbCurAll < uOffset)
1089 {
1090 rc = shaReadSyncCallback(pvUser, pvStorage, pInt->cbCurAll, 0,
1091 (size_t)(uOffset - pInt->cbCurAll), 0);
1092 if (RT_FAILURE(rc))
1093 return rc;
1094// RTPrintf("Gap Read uOffset: %7lu cbRead: %7lu = %7lu\n", uOffset, cbRead, uOffset + cbRead);
1095 }
1096 else if (uOffset < pInt->cbCurAll)
1097 AssertMsgFailed(("Jumping backwards is not possible, sequential access is supported only\n"));
1098
1099 size_t cbAllRead = 0;
1100 for (;;)
1101 {
1102 /* Finished? */
1103 if (cbAllRead == cbRead)
1104 break;
1105 size_t cbAvail = RTCircBufUsed(pInt->pCircBuf);
1106 if ( cbAvail == 0
1107 && pInt->fEOF
1108 && !RTCircBufIsWriting(pInt->pCircBuf))
1109 {
1110 break;
1111 }
1112 /* If there isn't enough data make sure the worker thread is fetching
1113 * more. */
1114 if ((cbRead - cbAllRead) > cbAvail)
1115 {
1116 rc = shaSignalManifestThread(pInt, STATUS_READ);
1117 if (RT_FAILURE(rc))
1118 break;
1119 /* If there is _no_ data available, we have to wait until it is. */
1120 if (cbAvail == 0)
1121 {
1122 rc = shaWaitForManifestThreadFinished(pInt);
1123 if (RT_FAILURE(rc))
1124 break;
1125 cbAvail = RTCircBufUsed(pInt->pCircBuf);
1126// RTPrintf("############## wait %lu %lu %lu \n", cbRead, cbAllRead, cbAvail);
1127// pInt->waits++;
1128 }
1129 }
1130 size_t cbToRead = RT_MIN(cbRead - cbAllRead, cbAvail);
1131 char *pcBuf;
1132 size_t cbMemRead = 0;
1133 /* Acquire a block for reading from our circular buffer. */
1134 RTCircBufAcquireReadBlock(pInt->pCircBuf, cbToRead, (void**)&pcBuf, &cbMemRead);
1135 if (pvBuf) /* Make it possible to blind read data (for skipping) */
1136 memcpy(&((char*)pvBuf)[cbAllRead], pcBuf, cbMemRead);
1137 /* Mark the block as empty again. */
1138 RTCircBufReleaseReadBlock(pInt->pCircBuf, cbMemRead);
1139 cbAllRead += cbMemRead;
1140
1141 pInt->cbCurAll += cbMemRead;
1142 }
1143
1144 if (pcbRead)
1145 *pcbRead = cbAllRead;
1146
1147 if (rc == VERR_EOF)
1148 rc = VINF_SUCCESS;
1149
1150 /* Signal the thread to read more data in the mean time. */
1151 if ( RT_SUCCESS(rc)
1152 && RTCircBufFree(pInt->pCircBuf) >= (RTCircBufSize(pInt->pCircBuf) / 2))
1153 rc = shaSignalManifestThread(pInt, STATUS_READ);
1154
1155 return rc;
1156}
1157
1158static int shaFlushSyncCallback(void *pvUser, void *pvStorage)
1159{
1160 /* Validate input. */
1161 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1162 AssertPtrReturn(pvStorage, VERR_INVALID_POINTER);
1163
1164 PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser;
1165 PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces);
1166 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
1167
1168 DEBUG_PRINT_FLOW();
1169
1170 PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage;
1171
1172 /* Check if there is still something in the buffer. If yes, flush it. */
1173 int rc = shaFlushCurBuf(pInt);
1174 if (RT_FAILURE(rc))
1175 return rc;
1176
1177 return vdIfIoFileFlushSync(pIfIo, pInt->pvStorage);
1178}
1179
1180PVDINTERFACEIO ShaCreateInterface()
1181{
1182 PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO));
1183 if (!pCallbacks)
1184 return NULL;
1185
1186 pCallbacks->pfnOpen = shaOpenCallback;
1187 pCallbacks->pfnClose = shaCloseCallback;
1188 pCallbacks->pfnDelete = shaDeleteCallback;
1189 pCallbacks->pfnMove = shaMoveCallback;
1190 pCallbacks->pfnGetFreeSpace = shaGetFreeSpaceCallback;
1191 pCallbacks->pfnGetModificationTime = shaGetModificationTimeCallback;
1192 pCallbacks->pfnGetSize = shaGetSizeCallback;
1193 pCallbacks->pfnSetSize = shaSetSizeCallback;
1194 pCallbacks->pfnReadSync = shaReadSyncCallback;
1195 pCallbacks->pfnWriteSync = shaWriteSyncCallback;
1196 pCallbacks->pfnFlushSync = shaFlushSyncCallback;
1197
1198 return pCallbacks;
1199}
1200
1201PVDINTERFACEIO FileCreateInterface()
1202{
1203 PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO));
1204 if (!pCallbacks)
1205 return NULL;
1206
1207 pCallbacks->pfnOpen = fileOpenCallback;
1208 pCallbacks->pfnClose = fileCloseCallback;
1209 pCallbacks->pfnDelete = fileDeleteCallback;
1210 pCallbacks->pfnMove = fileMoveCallback;
1211 pCallbacks->pfnGetFreeSpace = fileGetFreeSpaceCallback;
1212 pCallbacks->pfnGetModificationTime = fileGetModificationTimeCallback;
1213 pCallbacks->pfnGetSize = fileGetSizeCallback;
1214 pCallbacks->pfnSetSize = fileSetSizeCallback;
1215 pCallbacks->pfnReadSync = fileReadSyncCallback;
1216 pCallbacks->pfnWriteSync = fileWriteSyncCallback;
1217 pCallbacks->pfnFlushSync = fileFlushSyncCallback;
1218
1219 return pCallbacks;
1220}
1221
1222PVDINTERFACEIO TarCreateInterface()
1223{
1224 PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO));
1225 if (!pCallbacks)
1226 return NULL;
1227
1228 pCallbacks->pfnOpen = tarOpenCallback;
1229 pCallbacks->pfnClose = tarCloseCallback;
1230 pCallbacks->pfnDelete = tarDeleteCallback;
1231 pCallbacks->pfnMove = tarMoveCallback;
1232 pCallbacks->pfnGetFreeSpace = tarGetFreeSpaceCallback;
1233 pCallbacks->pfnGetModificationTime = tarGetModificationTimeCallback;
1234 pCallbacks->pfnGetSize = tarGetSizeCallback;
1235 pCallbacks->pfnSetSize = tarSetSizeCallback;
1236 pCallbacks->pfnReadSync = tarReadSyncCallback;
1237 pCallbacks->pfnWriteSync = tarWriteSyncCallback;
1238 pCallbacks->pfnFlushSync = tarFlushSyncCallback;
1239
1240 return pCallbacks;
1241}
1242
1243int ShaReadBuf(const char *pcszFilename, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pIfIo, void *pvUser)
1244{
1245 /* Validate input. */
1246 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER);
1247 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
1248 AssertPtrReturn(pIfIo, VERR_INVALID_POINTER);
1249
1250 void *pvStorage;
1251 int rc = pIfIo->pfnOpen(pvUser, pcszFilename,
1252 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, 0,
1253 &pvStorage);
1254 if (RT_FAILURE(rc))
1255 return rc;
1256
1257 void *pvTmpBuf = 0;
1258 void *pvBuf = 0;
1259 uint64_t cbTmpSize = _1M;
1260 size_t cbAllRead = 0;
1261 do
1262 {
1263 pvTmpBuf = RTMemAlloc(cbTmpSize);
1264 if (!pvTmpBuf)
1265 {
1266 rc = VERR_NO_MEMORY;
1267 break;
1268 }
1269
1270 for (;;)
1271 {
1272 size_t cbRead = 0;
1273 rc = pIfIo->pfnReadSync(pvUser, pvStorage, cbAllRead, pvTmpBuf, cbTmpSize, &cbRead);
1274 if ( RT_FAILURE(rc)
1275 || cbRead == 0)
1276 break;
1277 pvBuf = RTMemRealloc(pvBuf, cbAllRead + cbRead);
1278 if (!pvBuf)
1279 {
1280 rc = VERR_NO_MEMORY;
1281 break;
1282 }
1283 memcpy(&((char*)pvBuf)[cbAllRead], pvTmpBuf, cbRead);
1284 cbAllRead += cbRead;
1285 }
1286 } while (0);
1287
1288 pIfIo->pfnClose(pvUser, pvStorage);
1289
1290 if (rc == VERR_EOF)
1291 rc = VINF_SUCCESS;
1292
1293 if (pvTmpBuf)
1294 RTMemFree(pvTmpBuf);
1295
1296 if (RT_SUCCESS(rc))
1297 {
1298 *ppvBuf = pvBuf;
1299 *pcbSize = cbAllRead;
1300 }
1301 else
1302 {
1303 if (pvBuf)
1304 RTMemFree(pvBuf);
1305 }
1306
1307 return rc;
1308}
1309
1310int ShaWriteBuf(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFACEIO pIfIo, void *pvUser)
1311{
1312 /* Validate input. */
1313 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1314 AssertReturn(cbSize, VERR_INVALID_PARAMETER);
1315 AssertPtrReturn(pIfIo, VERR_INVALID_POINTER);
1316
1317 void *pvStorage;
1318 int rc = pIfIo->pfnOpen(pvUser, pcszFilename,
1319 RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL, 0,
1320 &pvStorage);
1321 if (RT_FAILURE(rc))
1322 return rc;
1323
1324 size_t cbAllWritten = 0;
1325 for (;;)
1326 {
1327 if (cbAllWritten >= cbSize)
1328 break;
1329 size_t cbToWrite = cbSize - cbAllWritten;
1330 size_t cbWritten = 0;
1331 rc = pIfIo->pfnWriteSync(pvUser, pvStorage, cbAllWritten, &((char*)pvBuf)[cbAllWritten], cbToWrite, &cbWritten);
1332 if (RT_FAILURE(rc))
1333 break;
1334 cbAllWritten += cbWritten;
1335 }
1336
1337 rc = pIfIo->pfnClose(pvUser, pvStorage);
1338
1339 return rc;
1340}
1341
1342static int zipDecompressBuffer(z_stream streamIn,
1343 uint32_t *cbDecompressed,
1344 bool *finished)
1345{
1346 int rc;
1347 int sh = streamIn.avail_out;
1348
1349 *cbDecompressed = 0;
1350
1351 do
1352 {
1353 rc = inflate(&streamIn, Z_NO_FLUSH);
1354
1355 if (rc < 0)
1356 {
1357 switch (rc)
1358 {
1359 case Z_STREAM_ERROR:
1360 rc = VERR_ZIP_CORRUPTED;
1361 break;
1362 case Z_DATA_ERROR:
1363 rc = VERR_ZIP_CORRUPTED;
1364 break;
1365 case Z_MEM_ERROR:
1366 rc = VERR_ZIP_NO_MEMORY;
1367 break;
1368 case Z_BUF_ERROR:
1369 rc = VERR_ZIP_ERROR;
1370 break;
1371 case Z_VERSION_ERROR:
1372 rc = VERR_ZIP_UNSUPPORTED_VERSION;
1373 break;
1374 case Z_ERRNO: /* We shouldn't see this status! */
1375 default:
1376 AssertMsgFailed(("%d\n", rc));
1377 if (rc >= 0)
1378 rc = VINF_SUCCESS;
1379 rc = VERR_ZIP_ERROR;
1380 }
1381
1382 break;
1383 }
1384
1385 *cbDecompressed += (sh - streamIn.avail_out);
1386 sh = streamIn.avail_out;
1387 }
1388 while (streamIn.avail_out > 0 && streamIn.avail_in > 0 );
1389
1390 if (RT_SUCCESS(rc))
1391 {
1392 if (streamIn.avail_in == 0)
1393 *finished = true;
1394 else
1395 {
1396 if (streamIn.avail_out == 0)
1397 *finished = false;
1398 }
1399 }
1400
1401 return rc;
1402}
1403
1404int decompressImageAndSave(const char *pcszFullFilenameIn, const char *pcszFullFilenameOut, PVDINTERFACEIO pIfIo, void *pvUser)
1405{
1406 /* Validate input. */
1407 AssertPtrReturn(pIfIo, VERR_INVALID_POINTER);
1408
1409 /*
1410 * Open the source file.
1411 */
1412 void *pvStorage;
1413 int rc = pIfIo->pfnOpen(pvUser, pcszFullFilenameIn,
1414 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, 0,
1415 &pvStorage);
1416 if (RT_FAILURE(rc))
1417 return rc;
1418#ifdef VBOX_MAIN_USE_VFS
1419
1420 /* Turn the source file handle/whatever into a VFS stream. */
1421 RTVFSIOSTREAM hVfsIosCompressedSrc;
1422 rc = VDIfCreateVfsStream(pIfIo, pvStorage, RTFILE_O_READ, &hVfsIosCompressedSrc);
1423 if (RT_SUCCESS(rc))
1424 {
1425 /* Pass the source thru gunzip. */
1426 RTVFSIOSTREAM hVfsIosSrc;
1427 rc = RTZipGzipDecompressIoStream(hVfsIosCompressedSrc, 0, &hVfsIosSrc);
1428 if (RT_SUCCESS(rc))
1429 {
1430 /*
1431 * Create the output file, including necessary paths.
1432 * Any existing file will be overwritten.
1433 */
1434 rc = VirtualBox::ensureFilePathExists(Utf8Str(pcszFullFilenameOut), true /*fCreate*/);
1435 if (RT_SUCCESS(rc))
1436 {
1437 RTVFSIOSTREAM hVfsIosDst;
1438 rc = RTVfsIoStrmOpenNormal(pcszFullFilenameOut,
1439 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL,
1440 &hVfsIosDst);
1441 if (RT_SUCCESS(rc))
1442 {
1443 /*
1444 * Pump the bytes thru. If we fail, delete the output file.
1445 */
1446 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0);
1447
1448 RTVfsIoStrmRelease(hVfsIosDst);
1449 RTFileDelete(pcszFullFilenameOut);
1450 }
1451 }
1452
1453 RTVfsIoStrmRelease(hVfsIosSrc);
1454 }
1455 RTVfsIoStrmRelease(hVfsIosCompressedSrc);
1456 }
1457 pIfIo->pfnClose(pvUser, pvStorage);
1458
1459#else
1460
1461 Bytef *decompressedBuffer = 0;
1462 Bytef *compressedBuffer = 0;
1463 uint64_t cbTmpSize = _1M;
1464 size_t cbAllRead = 0;
1465 size_t cbAllWritten = 0;
1466 RTFILE pFile = NULL;
1467 z_stream gzipStream;
1468
1469 Utf8Str pathOut(pcszFullFilenameOut);
1470 pathOut = pathOut.stripFilename();
1471
1472 Utf8Str fileIn(pcszFullFilenameIn);
1473 fileIn = fileIn.stripPath();
1474
1475 do
1476 {
1477 if (RTFileExists(pcszFullFilenameOut) == false)
1478 {
1479 /* ensure the directory exists */
1480 rc = VirtualBox::ensureFilePathExists(Utf8Str(pcszFullFilenameOut), true);
1481 if (FAILED(rc))
1482 {
1483 rc = VBOX_E_FILE_ERROR; /** @todo r=bird: You're mixing COM and VBox status codes... */
1484 break;
1485 }
1486
1487 rc = RTFileOpen(&pFile, pcszFullFilenameOut, RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1488 }
1489 else
1490 {
1491 rc = RTFileOpen(&pFile, pcszFullFilenameOut, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1492 }
1493
1494 if (FAILED(rc) || pFile == NULL)
1495 {
1496 rc = VBOX_E_FILE_ERROR;
1497 break;
1498 }
1499
1500 compressedBuffer = (Bytef *)RTMemAlloc(cbTmpSize);
1501
1502 if (!compressedBuffer)
1503 {
1504 rc = VERR_NO_MEMORY;
1505 break;
1506 }
1507
1508
1509 decompressedBuffer = (Bytef *)RTMemAlloc(cbTmpSize*10);
1510
1511 if (!decompressedBuffer)
1512 {
1513 rc = VERR_NO_MEMORY;
1514 break;
1515 }
1516
1517 gzipStream.zalloc = Z_NULL;
1518 gzipStream.zfree = Z_NULL;
1519 gzipStream.opaque = Z_NULL;
1520 gzipStream.next_in = compressedBuffer;
1521 gzipStream.avail_in = 0;
1522 gzipStream.next_out = decompressedBuffer;
1523 gzipStream.avail_out = cbTmpSize*10;
1524
1525 rc = inflateInit2(&gzipStream, MAX_WBITS + 16 /* autodetect gzip header */);
1526
1527 if (rc < 0)
1528 {
1529 break;
1530 }
1531
1532 size_t cbRead = 0;
1533 size_t cbDecompressed = 0;
1534 bool fFinished = true;
1535
1536 for (;;)
1537 {
1538 if (fFinished == true)
1539 {
1540 rc = pIfIo->pfnReadSync(pvUser, pvStorage, cbAllRead, compressedBuffer, cbTmpSize, &cbRead);
1541 if ( RT_FAILURE(rc)
1542 || cbRead == 0)
1543 break;
1544
1545 gzipStream.avail_in = cbRead;
1546 gzipStream.avail_out = cbTmpSize*10;
1547 }
1548
1549 /* decompress the buffer */
1550 rc = zipDecompressBuffer(gzipStream,
1551 (uint32_t *)(&cbDecompressed),
1552 &fFinished);
1553
1554 if (RT_FAILURE(rc))
1555 break;
1556
1557 rc = RTFileWrite(pFile, decompressedBuffer, cbDecompressed, &cbDecompressed);
1558
1559 if (RT_FAILURE(rc))
1560 break;
1561
1562 cbAllWritten += cbDecompressed;
1563 cbAllRead += cbRead;
1564 }
1565 } while (0);
1566
1567 pIfIo->pfnClose(pvUser, pvStorage);
1568
1569 if (rc == VERR_EOF)
1570 rc = VINF_SUCCESS;
1571
1572 rc = inflateEnd(&gzipStream);
1573
1574 rc = RTFileClose(pFile);
1575
1576 if (decompressedBuffer)
1577 RTMemFree(decompressedBuffer);
1578 if (compressedBuffer)
1579 RTMemFree(compressedBuffer);
1580#endif /* !VBOX_MAIN_USE_VFS */
1581
1582 return rc;
1583}
1584
1585
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use