VirtualBox

source: vbox/trunk/src/VBox/Storage/VISO.cpp@ 97360

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

bugref:10180 - fixed issue where some files are not cleaned up after vm deletion or vm execution interruption, ensured enum value ordering stayed consistent

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.6 KB
Line 
1/* $Id: VISO.cpp 97360 2022-11-01 01:33:46Z vboxsync $ */
2/** @file
3 * VISO - Virtual ISO disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2017-2022 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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_VD
33#include <VBox/vd-plugin.h>
34#include <VBox/err.h>
35
36#include <VBox/log.h>
37#include <iprt/assert.h>
38#include <iprt/ctype.h>
39#include <iprt/fsisomaker.h>
40#include <iprt/getopt.h>
41#include <iprt/mem.h>
42#include <iprt/path.h>
43#include <iprt/string.h>
44#include <iprt/uuid.h>
45
46#include "VDBackends.h"
47#include "VDBackendsInline.h"
48
49
50/*********************************************************************************************************************************
51* Defined Constants And Macros *
52*********************************************************************************************************************************/
53/** The maximum file size. */
54#if ARCH_BITS >= 64
55# define VISO_MAX_FILE_SIZE _32M
56#else
57# define VISO_MAX_FILE_SIZE _8M
58#endif
59
60
61/*********************************************************************************************************************************
62* Structures and Typedefs *
63*********************************************************************************************************************************/
64
65/**
66 * VBox ISO maker image instance.
67 */
68typedef struct VISOIMAGE
69{
70 /** The ISO maker output file handle.
71 * This is NIL if in VD_OPEN_FLAGS_INFO mode. */
72 RTVFSFILE hIsoFile;
73 /** The image size. */
74 uint64_t cbImage;
75 /** The UUID ofr the image. */
76 RTUUID Uuid;
77
78 /** Open flags passed by VD layer. */
79 uint32_t fOpenFlags;
80 /** Image name. Allocation follows the region list, no need to free. */
81 const char *pszFilename;
82 /** The parent director of pszFilename.
83 * Allocation follows the region list, no need to free. */
84 const char *pszCwd;
85
86 /** I/O interface. */
87 PVDINTERFACEIOINT pIfIo;
88 /** Error interface. */
89 PVDINTERFACEERROR pIfError;
90
91 /** Internal region list (variable size). */
92 VDREGIONLIST RegionList;
93} VISOIMAGE;
94/** Pointer to an VBox ISO make image instance. */
95typedef VISOIMAGE *PVISOIMAGE;
96
97
98/*********************************************************************************************************************************
99* Global Variables *
100*********************************************************************************************************************************/
101/** NULL-terminated array of supported file extensions. */
102static const VDFILEEXTENSION g_aVBoXIsoMakerFileExtensions[] =
103{
104 //{ "vbox-iso-maker", VDTYPE_OPTICAL_DISC }, - clumsy.
105 { "viso", VDTYPE_OPTICAL_DISC },
106 { NULL, VDTYPE_INVALID }
107};
108
109/** NULL-terminated array of configuration option. */
110static const VDCONFIGINFO s_aVisoConfigInfo[] =
111{
112 /* Options for VMDK raw disks */
113 { "UnattendedInstall", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_EXPERT },
114 /* End of options list */
115 { NULL, NULL, VDCFGVALUETYPE_INTEGER, 0 }
116};
117
118
119/**
120 * Parses the UUID that follows the marker argument.
121 *
122 * @returns IPRT status code.
123 * @param pszMarker The marker.
124 * @param pUuid Where to return the UUID.
125 */
126static int visoParseUuid(char *pszMarker, PRTUUID pUuid)
127{
128 /* Skip the marker. */
129 char ch;
130 while ( (ch = *pszMarker) != '\0'
131 && !RT_C_IS_SPACE(ch)
132 && ch != ':'
133 && ch != '=')
134 pszMarker++;
135
136 /* Skip chars before the value. */
137 if ( ch == ':'
138 || ch == '=')
139 ch = *++pszMarker;
140 else
141 while (RT_C_IS_SPACE(ch))
142 ch = *++pszMarker;
143 const char * const pszUuid = pszMarker;
144
145 /* Find the end of the UUID value. */
146 while ( ch != '\0'
147 && !RT_C_IS_SPACE(ch))
148 ch = *++pszMarker;
149
150 /* Validate the value (temporarily terminate the value string) */
151 *pszMarker = '\0';
152 int rc = RTUuidFromStr(pUuid, pszUuid);
153 if (RT_SUCCESS(rc))
154 {
155 *pszMarker = ch;
156 return VINF_SUCCESS;
157 }
158
159 /* Complain and return VERR_VD_IMAGE_CORRUPTED to indicate we've identified
160 the right image format, but the producer got something wrong. */
161 if (pszUuid != pszMarker)
162 LogRel(("visoParseUuid: Malformed UUID '%s': %Rrc\n", pszUuid, rc));
163 else
164 LogRel(("visoParseUuid: Empty/missing UUID!\n"));
165 *pszMarker = ch;
166
167 return VERR_VD_IMAGE_CORRUPTED;
168}
169
170
171static int visoProbeWorker(const char *pszFilename, PVDINTERFACEIOINT pIfIo, PRTUUID pUuid)
172{
173 PVDIOSTORAGE pStorage = NULL;
174 int rc = vdIfIoIntFileOpen(pIfIo, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &pStorage);
175 if (RT_SUCCESS(rc))
176 {
177 /*
178 * Read the first part of the file.
179 */
180 uint64_t cbFile = 0;
181 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
182 if (RT_SUCCESS(rc))
183 {
184 char szChunk[_1K];
185 size_t cbToRead = (size_t)RT_MIN(sizeof(szChunk) - 1, cbFile);
186 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0 /*off*/, szChunk, cbToRead);
187 if (RT_SUCCESS(rc))
188 {
189 szChunk[cbToRead] = '\0';
190
191 /*
192 * Skip leading spaces and check for the eye-catcher.
193 */
194 char *psz = szChunk;
195 while (RT_C_IS_SPACE(*psz))
196 psz++;
197 if (strncmp(psz, RT_STR_TUPLE("--iprt-iso-maker-file-marker")) == 0)
198 {
199 rc = visoParseUuid(psz, pUuid);
200 if (RT_SUCCESS(rc))
201 {
202 /*
203 * Check the file size.
204 */
205 if (cbFile <= VISO_MAX_FILE_SIZE)
206 rc = VINF_SUCCESS;
207 else
208 {
209 LogRel(("visoProbeWorker: VERR_VD_INVALID_SIZE - cbFile=%#RX64 cbMaxFile=%#RX64\n",
210 cbFile, (uint64_t)VISO_MAX_FILE_SIZE));
211 rc = VERR_VD_INVALID_SIZE;
212 }
213 }
214 else
215 rc = VERR_VD_IMAGE_CORRUPTED;
216 }
217 else
218 rc = VERR_VD_GEN_INVALID_HEADER;
219 }
220 }
221 vdIfIoIntFileClose(pIfIo, pStorage);
222 }
223 LogFlowFunc(("returns %Rrc\n", rc));
224 return rc;
225}
226
227/**
228 * @interface_method_impl{VDIMAGEBACKEND,pfnProbe}
229 */
230static DECLCALLBACK(int) visoProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
231 VDTYPE enmDesiredType, VDTYPE *penmType)
232{
233 /*
234 * Validate input.
235 */
236 AssertPtrReturn(penmType, VERR_INVALID_POINTER);
237 *penmType = VDTYPE_INVALID;
238
239 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
240 AssertReturn(*pszFilename, VERR_INVALID_POINTER);
241
242 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
243 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
244
245 RT_NOREF(pVDIfsDisk);
246
247 /*
248 * We can only fake DVD stuff, so fail if the desired type doesn't match up
249 */
250 if (enmDesiredType != VDTYPE_OPTICAL_DISC && enmDesiredType != VDTYPE_INVALID)
251 return VERR_VD_GEN_INVALID_HEADER; /* Caller has strict, though undocument, status code expectations. */
252
253 /*
254 * Share worker with visoOpen and visoSetFlags.
255 */
256 RTUUID UuidIgn;
257 int rc = visoProbeWorker(pszFilename, pIfIo, &UuidIgn);
258 if (RT_SUCCESS(rc))
259 *penmType = VDTYPE_OPTICAL_DISC;
260 else if (rc == VERR_VD_IMAGE_CORRUPTED || rc == VERR_VD_INVALID_SIZE)
261 *penmType = VDTYPE_OPTICAL_DISC;
262 else
263 rc = VERR_VD_GEN_INVALID_HEADER; /* Caller has strict, though undocument, status code expectations. */
264
265 LogFlowFunc(("returns %Rrc - *penmType=%d\n", rc, *penmType));
266 return rc;
267}
268
269
270/**
271 * Worker for visoOpen and visoSetOpenFlags that creates a VFS file for the ISO.
272 *
273 * This also updates cbImage and the Uuid members.
274 *
275 * @returns VBox status code.
276 * @param pThis The VISO image instance.
277 */
278static int visoOpenWorker(PVISOIMAGE pThis)
279{
280 /*
281 * Open the file and read it into memory.
282 */
283 PVDIOSTORAGE pStorage = NULL;
284 int rc = vdIfIoIntFileOpen(pThis->pIfIo, pThis->pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &pStorage);
285 if (RT_FAILURE(rc))
286 {
287 LogRel(("VISO: Unable to open file '%s': %Rrc\n", pThis->pszFilename, rc));
288 return rc;
289 }
290
291 LogRel(("VISO: Handling file '%s'\n", pThis->pszFilename));
292
293 /*
294 * Read the file into memory, prefixing it with a dummy command name.
295 */
296 uint64_t cbFile = 0;
297 rc = vdIfIoIntFileGetSize(pThis->pIfIo, pStorage, &cbFile);
298 if (RT_SUCCESS(rc))
299 {
300 if (cbFile <= VISO_MAX_FILE_SIZE)
301 {
302 static char const s_szCmdPrefix[] = "VBox-Iso-Maker ";
303
304 char *pszContent = (char *)RTMemTmpAlloc(sizeof(s_szCmdPrefix) + cbFile);
305 if (pszContent)
306 {
307 char *pszReadDst = &pszContent[sizeof(s_szCmdPrefix) - 1];
308 rc = vdIfIoIntFileReadSync(pThis->pIfIo, pStorage, 0 /*off*/, pszReadDst, (size_t)cbFile);
309 if (RT_SUCCESS(rc))
310 {
311 /*
312 * Check the file marker and get the UUID that follows it.
313 * Ignore leading blanks.
314 */
315 pszReadDst[(size_t)cbFile] = '\0';
316 memcpy(pszContent, s_szCmdPrefix, sizeof(s_szCmdPrefix) - 1);
317
318 while (RT_C_IS_SPACE(*pszReadDst))
319 pszReadDst++;
320 if (strncmp(pszReadDst, RT_STR_TUPLE("--iprt-iso-maker-file-marker")) == 0)
321 {
322 rc = visoParseUuid(pszReadDst, &pThis->Uuid);
323 if (RT_SUCCESS(rc))
324 {
325 /*
326 * Make sure it's valid UTF-8 before letting
327 */
328 rc = RTStrValidateEncodingEx(pszContent, sizeof(s_szCmdPrefix) + cbFile,
329 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH
330 | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
331 if (RT_SUCCESS(rc))
332 {
333 /*
334 * Convert it into an argument vector.
335 * Free the content afterwards to reduce memory pressure.
336 */
337 uint32_t fGetOpt = strncmp(pszReadDst, RT_STR_TUPLE("--iprt-iso-maker-file-marker-ms")) != 0
338 ? RTGETOPTARGV_CNV_QUOTE_BOURNE_SH : RTGETOPTARGV_CNV_QUOTE_MS_CRT;
339 fGetOpt |= RTGETOPTARGV_CNV_MODIFY_INPUT;
340 char **papszArgs;
341 int cArgs;
342 rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszContent, fGetOpt, NULL);
343
344 if (RT_SUCCESS(rc))
345 {
346 /*
347 * Open the parent directory and use that as CWD for relative references.
348 */
349 RTVFSDIR hVfsCwd;
350 rc = RTVfsChainOpenDir(pThis->pszCwd, 0 /*fOpen*/, &hVfsCwd, NULL, NULL);
351 if (RT_SUCCESS(rc))
352 {
353 /*
354 * Try instantiate the ISO image maker.
355 * Free the argument vector afterward to reduce memory pressure.
356 */
357 RTVFSFILE hVfsFile;
358 RTERRINFOSTATIC ErrInfo;
359 rc = RTFsIsoMakerCmdEx(cArgs, papszArgs, hVfsCwd, pThis->pszCwd,
360 &hVfsFile, RTErrInfoInitStatic(&ErrInfo));
361
362 RTVfsDirRelease(hVfsCwd);
363
364 RTGetOptArgvFreeEx(papszArgs, fGetOpt);
365 papszArgs = NULL;
366
367 if (RT_SUCCESS(rc))
368 {
369 uint64_t cbImage;
370 rc = RTVfsFileQuerySize(hVfsFile, &cbImage);
371 if (RT_SUCCESS(rc))
372 {
373 /*
374 * Update the state.
375 */
376 pThis->cbImage = cbImage;
377 pThis->RegionList.aRegions[0].cRegionBlocksOrBytes = cbImage;
378
379 pThis->hIsoFile = hVfsFile;
380 hVfsFile = NIL_RTVFSFILE;
381
382 rc = VINF_SUCCESS;
383 LogRel(("VISO: %'RU64 bytes (%#RX64) - %s\n", cbImage, cbImage, pThis->pszFilename));
384 }
385
386 RTVfsFileRelease(hVfsFile);
387 }
388 else if (RTErrInfoIsSet(&ErrInfo.Core))
389 {
390 LogRel(("visoOpenWorker: RTFsIsoMakerCmdEx failed: %Rrc - %s\n", rc, ErrInfo.Core.pszMsg));
391 vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: %s", ErrInfo.Core.pszMsg);
392 }
393 else
394 {
395 LogRel(("visoOpenWorker: RTFsIsoMakerCmdEx failed: %Rrc\n", rc));
396 vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: RTFsIsoMakerCmdEx failed: %Rrc", rc);
397 }
398 }
399 else
400 vdIfError(pThis->pIfError, rc, RT_SRC_POS,
401 "VISO: Failed to open parent dir of: %s", pThis->pszFilename);
402 }
403 else
404 vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: RTGetOptArgvFromString failed: %Rrc", rc);
405 }
406 else
407 vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: Invalid file encoding");
408 }
409 else
410 vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: Parsing UUID failed: %Rrc", rc);
411 }
412 else
413 rc = VERR_VD_GEN_INVALID_HEADER;
414 }
415 else
416 vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: Reading file failed: %Rrc", rc);
417
418 RTMemTmpFree(pszContent);
419 }
420 else
421 rc = VERR_NO_TMP_MEMORY;
422 }
423 else
424 {
425 LogRel(("visoOpen: VERR_VD_INVALID_SIZE - cbFile=%#RX64 cbMaxFile=%#RX64\n",
426 cbFile, (uint64_t)VISO_MAX_FILE_SIZE));
427 rc = VERR_VD_INVALID_SIZE;
428 }
429 }
430
431 if (RT_FAILURE(rc))
432 LogRel(("VISO: Handling of file '%s' failed with %Rrc\n", pThis->pszFilename, rc));
433
434 vdIfIoIntFileClose(pThis->pIfIo, pStorage);
435 return rc;
436}
437
438
439/**
440 * @interface_method_impl{VDIMAGEBACKEND,pfnOpen}
441 */
442static DECLCALLBACK(int) visoOpen(const char *pszFilename, unsigned uOpenFlags, PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
443 VDTYPE enmType, void **ppBackendData)
444{
445 uint32_t const fOpenFlags = uOpenFlags;
446 LogFlowFunc(("pszFilename='%s' fOpenFlags=%#x pVDIfsDisk=%p pVDIfsImage=%p enmType=%u ppBackendData=%p\n",
447 pszFilename, fOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
448
449 /*
450 * Validate input.
451 */
452 AssertPtrReturn(ppBackendData, VERR_INVALID_POINTER);
453 *ppBackendData = NULL;
454
455 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
456 AssertReturn(*pszFilename, VERR_INVALID_POINTER);
457
458 AssertReturn(!(fOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_FLAGS);
459
460 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
461 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
462
463 PVDINTERFACEERROR pIfError = VDIfErrorGet(pVDIfsDisk);
464
465 AssertReturn(enmType == VDTYPE_OPTICAL_DISC, VERR_NOT_SUPPORTED);
466
467 /*
468 * Allocate and initialize the backend image instance data.
469 */
470 int rc;
471 size_t cbFilename = strlen(pszFilename) + 1;
472 PVISOIMAGE pThis = (PVISOIMAGE)RTMemAllocZ(RT_UOFFSETOF(VISOIMAGE, RegionList.aRegions[1]) + cbFilename * 2);
473 if (pThis)
474 {
475 pThis->hIsoFile = NIL_RTVFSFILE;
476 pThis->cbImage = 0;
477 pThis->fOpenFlags = fOpenFlags;
478 pThis->pIfIo = pIfIo;
479 pThis->pIfError = pIfError;
480
481 pThis->RegionList.fFlags = 0;
482 pThis->RegionList.cRegions = 1;
483 pThis->RegionList.aRegions[0].offRegion = 0;
484 pThis->RegionList.aRegions[0].cRegionBlocksOrBytes = 0;
485 pThis->RegionList.aRegions[0].cbBlock = 2048;
486 pThis->RegionList.aRegions[0].enmDataForm = VDREGIONDATAFORM_RAW;
487 pThis->RegionList.aRegions[0].enmMetadataForm = VDREGIONMETADATAFORM_NONE;
488 pThis->RegionList.aRegions[0].cbData = 2048;
489 pThis->RegionList.aRegions[0].cbMetadata = 0;
490
491 char *pszDst = (char *)&pThis->RegionList.aRegions[1];
492 memcpy(pszDst, pszFilename, cbFilename);
493 pThis->pszFilename = pszDst;
494 pszDst[cbFilename - 1] = '\0';
495 pszDst += cbFilename;
496
497 memcpy(pszDst, pszFilename, cbFilename);
498 pThis->pszCwd = pszDst;
499 pszDst[cbFilename - 1] = '\0';
500 RTPathStripFilename(pszDst);
501
502 /*
503 * Only go all the way if this isn't an info query. Re-mastering an ISO
504 * can potentially be a lot of work and we don't want to go thru with it
505 * just because the GUI wants to display the image size.
506 */
507 if (!(fOpenFlags & VD_OPEN_FLAGS_INFO))
508 rc = visoOpenWorker(pThis);
509 else
510 rc = visoProbeWorker(pThis->pszFilename, pThis->pIfIo, &pThis->Uuid);
511 if (RT_SUCCESS(rc))
512 {
513 *ppBackendData = pThis;
514 LogFlowFunc(("returns VINF_SUCCESS (UUID=%RTuuid, pszFilename=%s)\n", &pThis->Uuid, pThis->pszFilename));
515 return VINF_SUCCESS;
516 }
517
518 RTMemFree(pThis);
519 }
520 else
521 rc = VERR_NO_MEMORY;
522 LogFlowFunc(("returns %Rrc\n", rc));
523 return rc;
524}
525
526/**
527 * Scans the VISO file and removes all references to files
528 * which are in the same folder as the VISO and
529 * whose names begin with "Unattended-".
530 *
531 * @return VBox status code.
532 *
533 * @param pThis Pointer to VISO backend data.
534 */
535static int deleteReferences(PVISOIMAGE pThis)
536{
537 /*
538 * Open the file and read it into memory.
539 */
540 PVDIOSTORAGE pStorage = NULL;
541 int vrc = vdIfIoIntFileOpen(pThis->pIfIo, pThis->pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &pStorage);
542 if (RT_FAILURE(vrc))
543 {
544 LogRel(("VISO: Unable to open file '%s': %Rrc\n", pThis->pszFilename, vrc));
545 return vrc;
546 }
547
548 LogRel(("VISO: Handling file '%s' references\n", pThis->pszFilename));
549
550 /*
551 * Read the file into memory.
552 */
553 uint64_t cbFile = 0;
554 vrc = vdIfIoIntFileGetSize(pThis->pIfIo, pStorage, &cbFile);
555 if (RT_SUCCESS(vrc))
556 {
557 if (cbFile <= VISO_MAX_FILE_SIZE)
558 {
559 char *pszContent = (char *)RTMemTmpAlloc(cbFile + 1);
560 if (pszContent)
561 {
562 vrc = vdIfIoIntFileReadSync(pThis->pIfIo, pStorage, 0 /*off*/, pszContent, (size_t)cbFile);
563 if (RT_SUCCESS(vrc))
564 {
565 /*
566 * Check the file marker.
567 * Ignore leading blanks.
568 */
569 pszContent[(size_t)cbFile] = '\0';
570
571 char *pszReadDst = pszContent;
572 while (RT_C_IS_SPACE(*pszReadDst))
573 pszReadDst++;
574 if (strncmp(pszReadDst, RT_STR_TUPLE("--iprt-iso-maker-file-marker")) == 0)
575 {
576 vrc = visoParseUuid(pszReadDst, &pThis->Uuid);
577 if (RT_SUCCESS(vrc))
578 {
579 /*
580 * Make sure it's valid UTF-8 before letting
581 */
582 vrc = RTStrValidateEncodingEx(pszContent, cbFile + 1,
583 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH
584 | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
585 if (RT_SUCCESS(vrc))
586 {
587 /*
588 * Convert it into an argument vector.
589 * Free the content afterwards to reduce memory pressure.
590 */
591 uint32_t fGetOpt = strncmp(pszReadDst, RT_STR_TUPLE("--iprt-iso-maker-file-marker-ms")) != 0
592 ? RTGETOPTARGV_CNV_QUOTE_BOURNE_SH : RTGETOPTARGV_CNV_QUOTE_MS_CRT;
593 fGetOpt |= RTGETOPTARGV_CNV_MODIFY_INPUT;
594 char **papszArgs;
595 int cArgs;
596 vrc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszContent, fGetOpt, NULL);
597
598 if (RT_SUCCESS(vrc))
599 {
600 for (int i = 0; i < cArgs; ++i)
601 {
602 char *pszArg = papszArgs[i];
603 char *pszOffset = strrchr(pszArg, '=');
604 if (pszOffset != NULL)
605 pszArg = pszOffset + 1;
606
607 /* if it isn't option */
608 if (pszArg[0] != '-')
609 {
610 char *pszPath = RTPathAbsExDup(pThis->pszCwd, pszArg, 0);
611 if (RTStrStartsWith((const char *)pszPath, pThis->pszCwd))
612 {
613 char *pszFileName = RTPathFilename(pszPath);
614 if ( pszFileName != NULL
615 && RTStrStartsWith((const char *)pszFileName, "Unattended-"))
616 {
617 vrc = RTFileDelete(pszPath);
618 if (RT_SUCCESS(vrc))
619 LogRel(("VISO: file '%s' deleted\n", pszPath));
620 else
621 LogRel(("VISO: Failed to delete the file '%s' (%Rrc)\n", pszPath, vrc));
622 vrc = VINF_SUCCESS;
623 }
624 }
625 RTStrFree(pszPath);
626 }
627 }
628 RTGetOptArgvFreeEx(papszArgs, fGetOpt);
629 papszArgs = NULL;
630 }
631 else
632 vdIfError(pThis->pIfError, vrc, RT_SRC_POS, "VISO: RTGetOptArgvFromString failed: %Rrc", vrc);
633 }
634 else
635 vdIfError(pThis->pIfError, vrc, RT_SRC_POS, "VISO: Invalid file encoding");
636 }
637 else
638 vdIfError(pThis->pIfError, vrc, RT_SRC_POS, "VISO: Parsing UUID failed: %Rrc", vrc);
639 }
640 else
641 vrc = VERR_VD_GEN_INVALID_HEADER;
642 }
643 else
644 vdIfError(pThis->pIfError, vrc, RT_SRC_POS, "VISO: Reading file failed: %Rrc", vrc);
645
646 RTMemTmpFree(pszContent);
647 }
648 else
649 vrc = VERR_NO_TMP_MEMORY;
650 }
651 else
652 {
653 LogRel(("visoOpen: VERR_VD_INVALID_SIZE - cbFile=%#RX64 cbMaxFile=%#RX64\n",
654 cbFile, (uint64_t)VISO_MAX_FILE_SIZE));
655 vrc = VERR_VD_INVALID_SIZE;
656 }
657 }
658
659 if (RT_FAILURE(vrc))
660 LogRel(("VISO: Handling of file '%s' failed with %Rrc\n", pThis->pszFilename, vrc));
661
662 vdIfIoIntFileClose(pThis->pIfIo, pStorage);
663 return vrc;
664}
665
666/**
667* @interface_method_impl{VDIMAGEBACKEND,pfnClose}
668 */
669static DECLCALLBACK(int) visoClose(void *pBackendData, bool fDelete)
670{
671 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
672 LogFlowFunc(("pThis=%p fDelete=%RTbool\n", pThis, fDelete));
673
674 if (pThis)
675 {
676 if (fDelete)
677 {
678 PVDINTERFACECONFIG pImgCfg = VDIfConfigGet(&pThis->pIfIo->Core);
679
680 bool fUnattendedInstall = false;
681 int vrc = VDCFGQueryBool(pImgCfg, "UnattendedInstall", &fUnattendedInstall);
682
683 /*
684 * The VISO created by unattended installer, so delete all generated files
685 * included in the VISO. the file is considered generated if it is located
686 * in the same folder as VISO and its name begins with "Unattended-"
687 */
688 if (RT_SUCCESS(vrc) && fUnattendedInstall)
689 deleteReferences(pThis);
690 vdIfIoIntFileDelete(pThis->pIfIo, pThis->pszFilename);
691 }
692
693 if (pThis->hIsoFile != NIL_RTVFSFILE)
694 {
695 RTVfsFileRelease(pThis->hIsoFile);
696 pThis->hIsoFile = NIL_RTVFSFILE;
697 }
698
699 RTMemFree(pThis);
700 }
701
702 LogFlowFunc(("returns VINF_SUCCESS\n"));
703 return VINF_SUCCESS;
704}
705
706/**
707 * @interface_method_impl{VDIMAGEBACKEND,pfnRead}
708 */
709static DECLCALLBACK(int) visoRead(void *pBackendData, uint64_t uOffset, size_t cbToRead, PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
710{
711 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
712 uint64_t off = uOffset;
713 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
714 AssertReturn(pThis->hIsoFile != NIL_RTVFSFILE, VERR_VD_NOT_OPENED);
715 LogFlowFunc(("pThis=%p off=%#RX64 cbToRead=%#zx pIoCtx=%p pcbActuallyRead=%p\n", pThis, off, cbToRead, pIoCtx, pcbActuallyRead));
716
717 /*
718 * Check request.
719 */
720 AssertReturn( off < pThis->cbImage
721 || (off == pThis->cbImage && cbToRead == 0), VERR_EOF);
722
723 uint64_t cbLeftInImage = pThis->cbImage - off;
724 if (cbToRead >= cbLeftInImage)
725 cbToRead = cbLeftInImage; /* ASSUMES the caller can deal with this, given the pcbActuallyRead parameter... */
726
727 /*
728 * Work the I/O context using vdIfIoIntIoCtxSegArrayCreate.
729 */
730 int rc = VINF_SUCCESS;
731 size_t cbActuallyRead = 0;
732 while (cbToRead > 0)
733 {
734 RTSGSEG Seg;
735 unsigned cSegs = 1;
736 size_t cbThisRead = vdIfIoIntIoCtxSegArrayCreate(pThis->pIfIo, pIoCtx, &Seg, &cSegs, cbToRead);
737 AssertBreakStmt(cbThisRead != 0, rc = VERR_INTERNAL_ERROR_2);
738 Assert(cbThisRead == Seg.cbSeg);
739
740 rc = RTVfsFileReadAt(pThis->hIsoFile, off, Seg.pvSeg, cbThisRead, NULL);
741 AssertRCBreak(rc);
742
743 /* advance. */
744 cbActuallyRead += cbThisRead;
745 off += cbThisRead;
746 cbToRead -= cbThisRead;
747 }
748
749 *pcbActuallyRead = cbActuallyRead;
750 return rc;
751}
752
753/**
754 * @interface_method_impl{VDIMAGEBACKEND,pfnWrite}
755 */
756static DECLCALLBACK(int) visoWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
757 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
758 size_t *pcbPostRead, unsigned fWrite)
759{
760 RT_NOREF(uOffset, cbToWrite, pIoCtx, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite);
761 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
762 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
763 AssertReturn(pThis->hIsoFile != NIL_RTVFSFILE, VERR_VD_NOT_OPENED);
764 LogFlowFunc(("pThis=%p off=%#RX64 pIoCtx=%p cbToWrite=%#zx pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p -> VERR_VD_IMAGE_READ_ONLY\n",
765 pThis, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
766 return VERR_VD_IMAGE_READ_ONLY;
767}
768
769/**
770 * @interface_method_impl{VDIMAGEBACKEND,pfnFlush}
771 */
772static DECLCALLBACK(int) visoFlush(void *pBackendData, PVDIOCTX pIoCtx)
773{
774 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
775 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
776 AssertReturn(pThis->hIsoFile != NIL_RTVFSFILE, VERR_VD_NOT_OPENED);
777 RT_NOREF(pIoCtx);
778 return VINF_SUCCESS;
779}
780
781/**
782 * @interface_method_impl{VDIMAGEBACKEND,pfnGetVersion}
783 */
784static DECLCALLBACK(unsigned) visoGetVersion(void *pBackendData)
785{
786 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
787 AssertPtrReturn(pThis, 0);
788 LogFlowFunc(("pThis=%#p -> 1\n", pThis));
789 return 1;
790}
791
792/**
793 * @interface_method_impl{VDIMAGEBACKEND,pfnGetFileSize}
794 */
795static DECLCALLBACK(uint64_t) visoGetFileSize(void *pBackendData)
796{
797 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
798 AssertPtrReturn(pThis, 0);
799 LogFlowFunc(("pThis=%p -> %RX64 (%s)\n", pThis, pThis->cbImage, pThis->hIsoFile == NIL_RTVFSFILE ? "fake!" : "real"));
800 return pThis->cbImage;
801}
802
803/**
804 * @interface_method_impl{VDIMAGEBACKEND,pfnGetPCHSGeometry}
805 */
806static DECLCALLBACK(int) visoGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
807{
808 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
809 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
810 LogFlowFunc(("pThis=%p pPCHSGeometry=%p -> VERR_NOT_SUPPORTED\n", pThis, pPCHSGeometry));
811 RT_NOREF(pPCHSGeometry);
812 return VERR_NOT_SUPPORTED;
813}
814
815/**
816 * @interface_method_impl{VDIMAGEBACKEND,pfnSetPCHSGeometry}
817 */
818static DECLCALLBACK(int) visoSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
819{
820 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
821 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
822 LogFlowFunc(("pThis=%p pPCHSGeometry=%p:{%u/%u/%u} -> VERR_VD_IMAGE_READ_ONLY\n",
823 pThis, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
824 RT_NOREF(pPCHSGeometry);
825 return VERR_VD_IMAGE_READ_ONLY;
826}
827
828/**
829 * @interface_method_impl{VDIMAGEBACKEND,pfnGetLCHSGeometry}
830 */
831static DECLCALLBACK(int) visoGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
832{
833 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
834 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
835 LogFlowFunc(("pThis=%p pLCHSGeometry=%p -> VERR_NOT_SUPPORTED\n", pThis, pLCHSGeometry));
836 RT_NOREF(pLCHSGeometry);
837 return VERR_NOT_SUPPORTED;
838}
839
840/**
841 * @interface_method_impl{VDIMAGEBACKEND,pfnSetLCHSGeometry}
842 */
843static DECLCALLBACK(int) visoSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
844{
845 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
846 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
847 LogFlowFunc(("pThis=%p pLCHSGeometry=%p:{%u/%u/%u} -> VERR_VD_IMAGE_READ_ONLY\n",
848 pThis, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
849 RT_NOREF(pLCHSGeometry);
850 return VERR_VD_IMAGE_READ_ONLY;
851}
852
853/**
854 * @interface_method_impl{VDIMAGEBACKEND,pfnQueryRegions}
855 */
856static DECLCALLBACK(int) visoQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
857{
858 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
859 *ppRegionList = NULL;
860 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
861
862 *ppRegionList = &pThis->RegionList;
863 LogFlowFunc(("returns VINF_SUCCESS (one region: 0 LB %RX64; pThis=%p)\n", pThis->RegionList.aRegions[0].cbData, pThis));
864 return VINF_SUCCESS;
865}
866
867/**
868 * @interface_method_impl{VDIMAGEBACKEND,pfnRegionListRelease}
869 */
870static DECLCALLBACK(void) visoRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
871{
872 /* Nothing to do here. Just assert the input to avoid unused parameter warnings. */
873 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
874 LogFlowFunc(("pThis=%p pRegionList=%p\n", pThis, pRegionList));
875 AssertPtrReturnVoid(pThis);
876 AssertReturnVoid(pRegionList == &pThis->RegionList || pRegionList == 0);
877}
878
879/**
880 * @interface_method_impl{VDIMAGEBACKEND,pfnGetImageFlags}
881 */
882static DECLCALLBACK(unsigned) visoGetImageFlags(void *pBackendData)
883{
884 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
885 LogFlowFunc(("pThis=%p -> VD_IMAGE_FLAGS_NONE\n", pThis));
886 AssertPtrReturn(pThis, VD_IMAGE_FLAGS_NONE);
887 return VD_IMAGE_FLAGS_NONE;
888}
889
890/**
891 * @interface_method_impl{VDIMAGEBACKEND,pfnGetOpenFlags}
892 */
893static DECLCALLBACK(unsigned) visoGetOpenFlags(void *pBackendData)
894{
895 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
896 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
897 AssertPtrReturn(pThis, 0);
898
899 LogFlowFunc(("returns %#x\n", pThis->fOpenFlags));
900 return pThis->fOpenFlags;
901}
902
903/**
904 * @interface_method_impl{VDIMAGEBACKEND,pfnSetOpenFlags}
905 */
906static DECLCALLBACK(int) visoSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
907{
908 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
909 LogFlowFunc(("pThis=%p fOpenFlags=%#x\n", pThis, uOpenFlags));
910
911 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
912 uint32_t const fSupported = VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
913 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
914 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS;
915 AssertMsgReturn(!(uOpenFlags & ~fSupported), ("fOpenFlags=%#x\n", uOpenFlags), VERR_INVALID_FLAGS);
916
917 /*
918 * Only react if we switch from VD_OPEN_FLAGS_INFO to non-VD_OPEN_FLAGS_INFO mode,
919 * becuase that means we need to open the image.
920 */
921 if ( (pThis->fOpenFlags & VD_OPEN_FLAGS_INFO)
922 && !(uOpenFlags & VD_OPEN_FLAGS_INFO)
923 && pThis->hIsoFile == NIL_RTVFSFILE)
924 {
925 int rc = visoOpenWorker(pThis);
926 if (RT_FAILURE(rc))
927 {
928 LogFlowFunc(("returns %Rrc\n", rc));
929 return rc;
930 }
931 }
932
933 /*
934 * Update the flags.
935 */
936 pThis->fOpenFlags &= ~fSupported;
937 pThis->fOpenFlags |= fSupported & uOpenFlags;
938 pThis->fOpenFlags |= VD_OPEN_FLAGS_READONLY;
939 if (pThis->hIsoFile != NIL_RTVFSFILE)
940 pThis->fOpenFlags &= ~VD_OPEN_FLAGS_INFO;
941
942 return VINF_SUCCESS;
943}
944
945#define uOpenFlags fOpenFlags /* sigh */
946
947/**
948 * @interface_method_impl{VDIMAGEBACKEND,pfnGetComment}
949 */
950VD_BACKEND_CALLBACK_GET_COMMENT_DEF_NOT_SUPPORTED(visoGetComment);
951
952/**
953 * @interface_method_impl{VDIMAGEBACKEND,pfnSetComment}
954 */
955VD_BACKEND_CALLBACK_SET_COMMENT_DEF_NOT_SUPPORTED(visoSetComment, PVISOIMAGE);
956
957/**
958 * @interface_method_impl{VDIMAGEBACKEND,pfnGetUuid}
959 */
960static DECLCALLBACK(int) visoGetUuid(void *pBackendData, PRTUUID pUuid)
961{
962 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
963 *pUuid = pThis->Uuid;
964 LogFlowFunc(("returns VIF_SUCCESS (%RTuuid)\n", pUuid));
965 return VINF_SUCCESS;
966}
967
968/**
969 * @interface_method_impl{VDIMAGEBACKEND,pfnSetUuid}
970 */
971VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(visoSetUuid, PVISOIMAGE);
972
973/**
974 * @interface_method_impl{VDIMAGEBACKEND,pfnGetModificationUuid}
975 */
976VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(visoGetModificationUuid);
977
978/**
979 * @interface_method_impl{VDIMAGEBACKEND,pfnSetModificationUuid}
980 */
981VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(visoSetModificationUuid, PVISOIMAGE);
982
983/**
984 * @interface_method_impl{VDIMAGEBACKEND,pfnGetParentUuid}
985 */
986VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(visoGetParentUuid);
987
988/**
989 * @interface_method_impl{VDIMAGEBACKEND,pfnSetParentUuid}
990 */
991VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(visoSetParentUuid, PVISOIMAGE);
992
993/**
994 * @interface_method_impl{VDIMAGEBACKEND,pfnGetParentModificationUuid}
995 */
996VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(visoGetParentModificationUuid);
997
998/**
999 * @interface_method_impl{VDIMAGEBACKEND,pfnSetParentModificationUuid}
1000 */
1001VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(visoSetParentModificationUuid, PVISOIMAGE);
1002
1003#undef uOpenFlags
1004
1005/**
1006 * @interface_method_impl{VDIMAGEBACKEND,pfnDump}
1007 */
1008static DECLCALLBACK(void) visoDump(void *pBackendData)
1009{
1010 PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
1011 AssertPtrReturnVoid(pThis);
1012
1013 vdIfErrorMessage(pThis->pIfError, "Dumping CUE image '%s' fOpenFlags=%x cbImage=%#RX64\n",
1014 pThis->pszFilename, pThis->fOpenFlags, pThis->cbImage);
1015}
1016
1017
1018
1019/**
1020 * VBox ISO maker backend.
1021 */
1022const VDIMAGEBACKEND g_VBoxIsoMakerBackend =
1023{
1024 /* u32Version */
1025 VD_IMGBACKEND_VERSION,
1026 /* pszBackendName */
1027 "VBoxIsoMaker",
1028 /* uBackendCaps */
1029 VD_CAP_FILE,
1030 /* paFileExtensions */
1031 g_aVBoXIsoMakerFileExtensions,
1032 /* paConfigInfo */
1033 s_aVisoConfigInfo,
1034 /* pfnProbe */
1035 visoProbe,
1036 /* pfnOpen */
1037 visoOpen,
1038 /* pfnCreate */
1039 NULL,
1040 /* pfnRename */
1041 NULL,
1042 /* pfnClose */
1043 visoClose,
1044 /* pfnRead */
1045 visoRead,
1046 /* pfnWrite */
1047 visoWrite,
1048 /* pfnFlush */
1049 visoFlush,
1050 /* pfnDiscard */
1051 NULL,
1052 /* pfnGetVersion */
1053 visoGetVersion,
1054 /* pfnGetFileSize */
1055 visoGetFileSize,
1056 /* pfnGetPCHSGeometry */
1057 visoGetPCHSGeometry,
1058 /* pfnSetPCHSGeometry */
1059 visoSetPCHSGeometry,
1060 /* pfnGetLCHSGeometry */
1061 visoGetLCHSGeometry,
1062 /* pfnSetLCHSGeometry */
1063 visoSetLCHSGeometry,
1064 /* pfnQueryRegions */
1065 visoQueryRegions,
1066 /* pfnRegionListRelease */
1067 visoRegionListRelease,
1068 /* pfnGetImageFlags */
1069 visoGetImageFlags,
1070 /* pfnGetOpenFlags */
1071 visoGetOpenFlags,
1072 /* pfnSetOpenFlags */
1073 visoSetOpenFlags,
1074 /* pfnGetComment */
1075 visoGetComment,
1076 /* pfnSetComment */
1077 visoSetComment,
1078 /* pfnGetUuid */
1079 visoGetUuid,
1080 /* pfnSetUuid */
1081 visoSetUuid,
1082 /* pfnGetModificationUuid */
1083 visoGetModificationUuid,
1084 /* pfnSetModificationUuid */
1085 visoSetModificationUuid,
1086 /* pfnGetParentUuid */
1087 visoGetParentUuid,
1088 /* pfnSetParentUuid */
1089 visoSetParentUuid,
1090 /* pfnGetParentModificationUuid */
1091 visoGetParentModificationUuid,
1092 /* pfnSetParentModificationUuid */
1093 visoSetParentModificationUuid,
1094 /* pfnDump */
1095 visoDump,
1096 /* pfnGetTimestamp */
1097 NULL,
1098 /* pfnGetParentTimestamp */
1099 NULL,
1100 /* pfnSetParentTimestamp */
1101 NULL,
1102 /* pfnGetParentFilename */
1103 NULL,
1104 /* pfnSetParentFilename */
1105 NULL,
1106 /* pfnComposeLocation */
1107 genericFileComposeLocation,
1108 /* pfnComposeName */
1109 genericFileComposeName,
1110 /* pfnCompact */
1111 NULL,
1112 /* pfnResize */
1113 NULL,
1114 /* pfnRepair */
1115 NULL,
1116 /* pfnTraverseMetadata */
1117 NULL,
1118 /* u32VersionEnd */
1119 VD_IMGBACKEND_VERSION
1120};
1121
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