VirtualBox

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

Last change on this file since 106901 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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