VirtualBox

source: vbox/trunk/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp

Last change on this file was 104692, checked in by vboxsync, 7 months ago

Main, FE/Qt, ImageMounter: bugref:10690: Replace locally hardcoded Extension Pack name with globally defined one, easier to rebrand.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.8 KB
Line 
1/* $Id: vboximg-mount.cpp 104692 2024-05-16 15:51:25Z vboxsync $ */
2/** @file
3 * vboximg-mount - Disk Image Flattening FUSE Program.
4 */
5
6/*
7 * Copyright (C) 2009-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
33#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo log group */
34
35#define RTTIME_INCL_TIMESPEC
36#define FUSE_USE_VERSION 27
37#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
38# define UNIX_DERIVATIVE
39#endif
40#define MAX_READERS (INT32_MAX / 32)
41#ifdef UNIX_DERIVATIVE
42#include <errno.h>
43#include <fcntl.h>
44#include <stdlib.h>
45#include <libgen.h>
46#include <unistd.h>
47#include <math.h>
48#include <cstdarg>
49#include <sys/stat.h>
50#include <sys/time.h>
51#endif
52#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX)
53# include <sys/param.h>
54# undef PVM /* Blasted old BSD mess still hanging around darwin. */
55#endif
56#ifdef RT_OS_LINUX
57# include <linux/fs.h>
58# include <linux/hdreg.h>
59#endif
60#include <VirtualBox_XPCOM.h>
61#include <VBox/com/VirtualBox.h>
62#include <VBox/vd.h>
63#include <VBox/vd-ifs.h>
64#include <VBox/log.h>
65#include <VBox/err.h>
66#include <VBox/com/ErrorInfo.h>
67#include <VBox/com/NativeEventQueue.h>
68#include <VBox/com/com.h>
69#include <VBox/com/string.h>
70#include <VBox/com/Guid.h>
71#include <VBox/com/array.h>
72#include <VBox/com/errorprint.h>
73#include <VBox/vd-plugin.h>
74#include <VBox/version.h> /* VBOX_PUEL_PRODUCT */
75#include <iprt/initterm.h>
76#include <iprt/assert.h>
77#include <iprt/message.h>
78#include <iprt/critsect.h>
79#include <iprt/asm.h>
80#include <iprt/mem.h>
81#include <iprt/string.h>
82#include <iprt/initterm.h>
83#include <iprt/stream.h>
84#include <iprt/types.h>
85#include <iprt/path.h>
86#include <iprt/utf16.h>
87#include <iprt/base64.h>
88#include <iprt/vfs.h>
89#include <iprt/dvm.h>
90#include <iprt/time.h>
91
92#include "fuse.h"
93#include "vboximgCrypto.h"
94#include "vboximgMedia.h"
95#include "SelfSizingTable.h"
96#include "vboximgOpts.h"
97
98using namespace com;
99
100enum {
101 USAGE_FLAG,
102};
103
104#if !defined(S_ISTXT) && defined(S_ISVTX)
105# define S_ISTXT (S_ISVTX)
106#endif
107
108#define VBOX_EXTPACK VBOX_PUEL_PRODUCT
109#define VERBOSE g_vboximgOpts.fVerbose
110
111#define SAFENULL(strPtr) (strPtr ? strPtr : "")
112#define CSTR(arg) Utf8Str(arg).c_str() /* Converts XPCOM string type to C string type */
113
114static struct fuse_operations g_vboximgOps; /** FUSE structure that defines allowed ops for this FS */
115
116/**
117 * Volume data.
118 */
119typedef struct VBOXIMGMOUNTVOL
120{
121 /** The volume handle. */
122 RTDVMVOLUME hVol;
123 /** The VFS file associated with the volume. */
124 RTVFSFILE hVfsFileVol;
125 /** Handle to the VFS root if supported and specified. */
126 RTVFS hVfsRoot;
127 /** Handle to the root directory. */
128 RTVFSDIR hVfsDirRoot;
129} VBOXIMGMOUNTVOL;
130/** Pointer to a volume data structure. */
131typedef VBOXIMGMOUNTVOL *PVBOXIMGMOUNTVOL;
132
133/* Global variables */
134static RTVFSFILE g_hVfsFileDisk = NIL_RTVFSFILE; /** Disk as VFS file handle. */
135static uint32_t g_cbSector; /** Disk sector size. */
136static RTDVM g_hDvmMgr; /** Handle to the volume manager. */
137static char *g_pszDiskUuid; /** UUID of image (if known, otherwise NULL) */
138static PVDINTERFACE g_pVdIfs; /** @todo Remove when VD I/O becomes threadsafe */
139static VDINTERFACETHREADSYNC g_VDIfThreadSync; /** @todo Remove when VD I/O becomes threadsafe */
140static RTCRITSECT g_vdioLock; /** @todo Remove when VD I/O becomes threadsafe */
141static char *g_pszImageName = NULL; /** Base filename for current VD image */
142static char *g_pszImagePath; /** Full path to current VD image */
143static char *g_pszBaseImagePath; /** Base image known after parsing */
144static char *g_pszBaseImageName; /** Base image known after parsing */
145static uint32_t g_cImages; /** Number of images in diff chain */
146
147/** Pointer to the detected volumes. */
148static PVBOXIMGMOUNTVOL g_paVolumes;
149/** Number of detected volumes. */
150static uint32_t g_cVolumes;
151
152VBOXIMGOPTS g_vboximgOpts;
153
154#define OPTION(fmt, pos, val) { fmt, offsetof(struct vboximgOpts, pos), val }
155
156static struct fuse_opt vboximgOptDefs[] = {
157 OPTION("--image %s", pszImageUuidOrPath, 0),
158 OPTION("-i %s", pszImageUuidOrPath, 0),
159 OPTION("--rw", fRW, 1),
160 OPTION("--root", fAllowRoot, 1),
161 OPTION("--vm %s", pszVm, 0),
162 OPTION("-l", fList, 1),
163 OPTION("--list", fList, 1),
164 OPTION("-g", fGstFs, 1),
165 OPTION("--guest-filesystem", fGstFs, 1),
166 OPTION("--verbose", fVerbose, 1),
167 OPTION("-v", fVerbose, 1),
168 OPTION("--wide", fWide, 1),
169 OPTION("-w", fWide, 1),
170 OPTION("-lv", fVerboseList, 1),
171 OPTION("-vl", fVerboseList, 1),
172 OPTION("-lw", fWideList, 1),
173 OPTION("-wl", fWideList, 1),
174 OPTION("-h", fBriefUsage, 1),
175 FUSE_OPT_KEY("--help", USAGE_FLAG),
176 FUSE_OPT_KEY("-vm", FUSE_OPT_KEY_NONOPT),
177 FUSE_OPT_END
178};
179
180typedef struct IMAGELIST
181{
182 struct IMAGELIST *next;
183 struct IMAGELIST *prev;
184 ComPtr<IToken> pLockToken;
185 bool fWriteable;
186 ComPtr<IMedium> pImage;
187 Bstr pImageName;
188 Bstr pImagePath;
189} IMAGELIST;
190
191IMAGELIST listHeadLockList; /* flink & blink intentionally left NULL */
192
193
194
195/** @todo Remove when VD I/O becomes threadsafe */
196static DECLCALLBACK(int) vboximgThreadStartRead(void *pvUser)
197{
198 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
199 return RTCritSectEnter(vdioLock);
200}
201
202static DECLCALLBACK(int) vboximgThreadFinishRead(void *pvUser)
203{
204 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
205 return RTCritSectLeave(vdioLock);
206}
207
208static DECLCALLBACK(int) vboximgThreadStartWrite(void *pvUser)
209{
210 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
211 return RTCritSectEnter(vdioLock);
212}
213
214static DECLCALLBACK(int) vboximgThreadFinishWrite(void *pvUser)
215{
216 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
217 return RTCritSectLeave(vdioLock);
218}
219/** @todo (end of to do section) */
220
221
222static void
223briefUsage()
224{
225 RTPrintf("usage: vboximg-mount [options] <mount point directory path>\n\n"
226 "vboximg-mount options:\n\n"
227 " [ { -i | --image } <specifier> ] VirtualBox disk base image or snapshot,\n"
228 " specified by UUID or path\n"
229 "\n"
230 " [ { -l | --list } ] If --image specified, list its partitions,\n"
231 " otherwise, list registered VMs and their\n"
232 " attached virtual HDD disk media. In verbose\n"
233 " mode, VM/media list will be long format,\n"
234 " i.e. including snapshot images and paths.\n"
235 "\n"
236 " [ { -w | --wide } ] List media in wide / tabular format\n"
237 " (reduces vertical scrolling but requires\n"
238 " wider than standard 80 column window)\n"
239 "\n"
240 " [ { -g | --guest-filesystem } ] Exposes supported guest filesystems directly\n"
241 " in the mounted directory without the need\n"
242 " for a filesystem driver on the host\n"
243 "\n"
244 " [ --vm UUID ] Restrict media list to specified vm.\n"
245 "\n"
246 " [ --rw ] Make image writeable (default = readonly)\n"
247 "\n"
248 " [ --root ] Same as -o allow_root.\n"
249 "\n"
250 " [ { -v | --verbose } ] Log extra information.\n"
251 "\n"
252 " [ -o opt[,opt...]] FUSE mount options.\n"
253 "\n"
254 " [ { --help | -h | -? } ] Display this usage information.\n"
255 );
256 RTPrintf("\n"
257 "vboximg-mount is a utility to make VirtualBox disk images available to the host\n"
258 "operating system for privileged or non-privileged access. Any version of the\n"
259 "disk can be mounted from its available history of snapshots.\n"
260 "\n"
261 "If the user specifies a base image identifier using the --image option, only\n"
262 "the base image will be mounted, disregarding any snapshots. Alternatively,\n"
263 "if a snapshot is specified, the state of the FUSE-mounted virtual disk\n"
264 "is synthesized from the implied chain of snapshots, including the base image.\n"
265 "\n"
266 "The virtual disk is exposed as a device node within a FUSE-based filesystem\n"
267 "that overlays the user-provided mount point. The FUSE filesystem consists of a\n"
268 "directory containing a number of files and possibly other directories:\n"
269 " * vhdd: Provides access to the raw disk image data as a flat image\n"
270 " * vol<id>: Provides access to individual volumes on the accessed disk image\n"
271 " * fs<id>: Provides access to a supported filesystem without the need for a\n"
272 " host filesystem driver\n"
273 "\n"
274 "The directory will also contain a symbolic link which has the same basename(1)\n"
275 "as the virtual disk base image and points to the location of the\n"
276 "virtual disk base image.\n"
277 "\n"
278 );
279}
280
281static int
282vboximgOptHandler(void *data, const char *arg, int optKey, struct fuse_args *outargs)
283{
284 RT_NOREF(data);
285 RT_NOREF(arg);
286 RT_NOREF(optKey);
287 RT_NOREF(outargs);
288
289 /*
290 * Apparently this handler is only called for arguments FUSE can't parse,
291 * and arguments that don't result in variable assignment such as "USAGE"
292 * In this impl. that's always deemed a parsing error.
293 */
294 if (*arg != '-') /* could be user's mount point */
295 return 1;
296
297 return -1;
298}
299
300
301/**
302 * Queries the VFS object handle from the given path.
303 *
304 * @returns IPRT status code.
305 * @retval VERR_NOT_FOUND if the object denoted by the path couldn't be found.
306 * @param pszPath The path.
307 * @param phVfsObj Where to store the handle to the VFS object on success.
308 */
309static int vboxImgMntVfsObjQueryFromPath(const char *pszPath, PRTVFSOBJ phVfsObj)
310{
311 PRTPATHSPLIT pPathSplit = NULL;
312 int rc = RTPathSplitA(pszPath, &pPathSplit, RTPATH_STR_F_STYLE_HOST);
313 if (RT_SUCCESS(rc))
314 {
315 if ( RTPATH_PROP_HAS_ROOT_SPEC(pPathSplit->fProps)
316 && pPathSplit->cComps >= 2)
317 {
318 /* Skip the root specifier and start with the component coming afterwards. */
319 if ( !RTStrCmp(pPathSplit->apszComps[1], "vhdd")
320 && g_hVfsFileDisk != NIL_RTVFSFILE)
321 *phVfsObj = RTVfsObjFromFile(g_hVfsFileDisk);
322 else if (!RTStrNCmp(pPathSplit->apszComps[1], "vol", sizeof("vol") - 1))
323 {
324 /* Retrieve the accessed volume and return the stat data. */
325 uint32_t idxVol;
326 int vrc = RTStrToUInt32Full(&pPathSplit->apszComps[1][3], 10, &idxVol);
327 if ( vrc == VINF_SUCCESS
328 && idxVol < g_cVolumes
329 && g_paVolumes[idxVol].hVfsFileVol != NIL_RTVFSFILE)
330 *phVfsObj = RTVfsObjFromFile(g_paVolumes[idxVol].hVfsFileVol);
331 else
332 rc = VERR_NOT_FOUND;
333 }
334 else if (!RTStrNCmp(pPathSplit->apszComps[1], "fs", sizeof("fs") - 1))
335 {
336 /* Retrieve the accessed volume and return the stat data. */
337 uint32_t idxVol;
338 int vrc = RTStrToUInt32Full(&pPathSplit->apszComps[1][2], 10, &idxVol);
339 if ( vrc == VINF_SUCCESS
340 && idxVol < g_cVolumes
341 && g_paVolumes[idxVol].hVfsDirRoot != NIL_RTVFSDIR)
342 *phVfsObj = RTVfsObjFromDir(g_paVolumes[idxVol].hVfsDirRoot);
343 else
344 rc = VERR_NOT_FOUND;
345
346 /* Is an object inside the guest filesystem requested? */
347 if (pPathSplit->cComps > 2)
348 {
349 PRTPATHSPLIT pPathSplitVfs = (PRTPATHSPLIT)RTMemTmpAllocZ(RT_UOFFSETOF_DYN(RTPATHSPLIT, apszComps[pPathSplit->cComps - 1]));
350 if (RT_LIKELY(pPathSplitVfs))
351 {
352 pPathSplitVfs->cComps = pPathSplit->cComps - 1;
353 pPathSplitVfs->fProps = pPathSplit->fProps;
354 pPathSplitVfs->cchPath = pPathSplit->cchPath - strlen(pPathSplit->apszComps[1]) - 1;
355 pPathSplitVfs->cbNeeded = pPathSplit->cbNeeded;
356 pPathSplitVfs->pszSuffix = pPathSplit->pszSuffix;
357 pPathSplitVfs->apszComps[0] = pPathSplit->apszComps[0];
358 for (uint32_t i = 1; i < pPathSplitVfs->cComps; i++)
359 pPathSplitVfs->apszComps[i] = pPathSplit->apszComps[i + 1];
360
361 /* Reassemble the path. */
362 char *pszPathVfs = (char *)RTMemTmpAllocZ(pPathSplitVfs->cbNeeded);
363 if (RT_LIKELY(pszPathVfs))
364 {
365 rc = RTPathSplitReassemble(pPathSplitVfs, RTPATH_STR_F_STYLE_HOST, pszPathVfs, pPathSplitVfs->cbNeeded);
366 if (RT_SUCCESS(rc))
367 {
368 rc = RTVfsObjOpen(g_paVolumes[idxVol].hVfsRoot, pszPathVfs,
369 RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
370 RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK,
371 phVfsObj);
372 }
373 RTMemTmpFree(pszPathVfs);
374 }
375
376 RTMemTmpFree(pPathSplitVfs);
377 }
378 else
379 rc = VERR_NO_MEMORY;
380 }
381 }
382 else
383 rc = VERR_NOT_FOUND;
384
385 rc = VINF_SUCCESS;
386 }
387 else
388 rc = VERR_NOT_FOUND;
389 RTPathSplitFree(pPathSplit);
390 }
391
392 return rc;
393}
394
395
396/** @copydoc fuse_operations::open */
397static int vboximgOp_open(const char *pszPath, struct fuse_file_info *pInfo)
398{
399 LogFlowFunc(("pszPath=%s\n", pszPath));
400 int rc = 0;
401
402 RTVFSOBJ hVfsObj;
403 int vrc = vboxImgMntVfsObjQueryFromPath(pszPath, &hVfsObj);
404 if (RT_SUCCESS(vrc))
405 {
406 uint32_t fNotSup = 0;
407
408#ifdef UNIX_DERIVATIVE
409# ifdef RT_OS_DARWIN
410 fNotSup = O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK |
411 O_ASYNC | O_CREAT | O_TRUNC | O_EXCL | O_EVTONLY;
412# elif defined(RT_OS_LINUX)
413 fNotSup = O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
414 /* | O_LARGEFILE | O_SYNC | ? */
415# elif defined(RT_OS_FREEBSD)
416 fNotSup = O_APPEND | O_ASYNC | O_DIRECT | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
417 /* | O_LARGEFILE | O_SYNC | ? */
418# endif
419#else
420# error "Port me"
421#endif
422
423 if (!(pInfo->flags & fNotSup))
424 {
425#ifdef UNIX_DERIVATIVE
426 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
427 rc = -EINVAL;
428# ifdef O_DIRECTORY
429 if (pInfo->flags & O_DIRECTORY)
430 rc = -ENOTDIR;
431# endif
432#endif
433 if (!rc)
434 {
435 pInfo->fh = (uintptr_t)hVfsObj;
436 return 0;
437 }
438 }
439 else
440 rc = -EINVAL;
441
442 RTVfsObjRelease(hVfsObj);
443 }
444 else
445 rc = -RTErrConvertToErrno(vrc);
446
447 LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath));
448 return rc;
449
450}
451
452/** @copydoc fuse_operations::release */
453static int vboximgOp_release(const char *pszPath, struct fuse_file_info *pInfo)
454{
455 RT_NOREF(pszPath);
456
457 LogFlowFunc(("pszPath=%s\n", pszPath));
458
459 RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh;
460 RTVfsObjRelease(hVfsObj);
461
462 LogFlowFunc(("\"%s\"\n", pszPath));
463 return 0;
464}
465
466
467/** @copydoc fuse_operations::read */
468static int vboximgOp_read(const char *pszPath, char *pbBuf, size_t cbBuf,
469 off_t offset, struct fuse_file_info *pInfo)
470{
471 RT_NOREF(pszPath);
472
473 LogFlowFunc(("offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
474
475 AssertReturn(offset >= 0, -EINVAL);
476 AssertReturn((int)cbBuf >= 0, -EINVAL);
477 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
478
479 int rc = 0;
480 RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh;
481 switch (RTVfsObjGetType(hVfsObj))
482 {
483 case RTVFSOBJTYPE_FILE:
484 {
485 size_t cbRead = 0;
486 RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
487 int vrc = RTVfsFileReadAt(hVfsFile, offset, pbBuf, cbBuf, &cbRead);
488 if (cbRead)
489 rc = cbRead;
490 else if (vrc == VINF_EOF)
491 rc = -RTErrConvertToErrno(VERR_EOF);
492 RTVfsFileRelease(hVfsFile);
493 break;
494 }
495 default:
496 rc = -EINVAL;
497 }
498
499 if (rc < 0)
500 LogFlowFunc(("%s\n", strerror(rc)));
501 return rc;
502}
503
504/** @copydoc fuse_operations::write */
505static int vboximgOp_write(const char *pszPath, const char *pbBuf, size_t cbBuf,
506 off_t offset, struct fuse_file_info *pInfo)
507{
508 RT_NOREF(pszPath);
509 RT_NOREF(pInfo);
510
511 LogFlowFunc(("offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
512
513 AssertReturn(offset >= 0, -EINVAL);
514 AssertReturn((int)cbBuf >= 0, -EINVAL);
515 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
516
517 if (!g_vboximgOpts.fRW)
518 {
519 LogFlowFunc(("WARNING: vboximg-mount (FUSE FS) --rw option not specified\n"
520 " (write operation ignored w/o error!)\n"));
521 return cbBuf;
522 }
523
524 int rc = 0;
525 RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh;
526 switch (RTVfsObjGetType(hVfsObj))
527 {
528 case RTVFSOBJTYPE_FILE:
529 {
530 size_t cbWritten = 0;
531 RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
532 int vrc = RTVfsFileWriteAt(hVfsFile, offset, pbBuf, cbBuf, &cbWritten);
533 if (cbWritten)
534 rc = cbWritten;
535 else if (vrc == VINF_EOF)
536 rc = -RTErrConvertToErrno(VERR_EOF);
537 RTVfsFileRelease(hVfsFile);
538 break;
539 }
540 default:
541 rc = -EINVAL;
542 }
543
544 if (rc < 0)
545 LogFlowFunc(("%s\n", strerror(rc)));
546
547 return rc;
548}
549
550/** @copydoc fuse_operations::getattr */
551static int vboximgOp_getattr(const char *pszPath, struct stat *stbuf)
552{
553 int rc = 0;
554
555 LogFlowFunc(("pszPath=%s, stat(\"%s\")\n", pszPath, g_pszImagePath));
556
557 memset(stbuf, 0, sizeof(struct stat));
558
559 if (RTStrCmp(pszPath, "/") == 0)
560 {
561 stbuf->st_mode = S_IFDIR | 0755;
562 stbuf->st_nlink = 2;
563 }
564 else if ( g_pszImageName
565 && RTStrNCmp(pszPath + 1, g_pszImageName, strlen(g_pszImageName)) == 0)
566 {
567 /* When the disk is partitioned, the symbolic link named from `basename` of
568 * resolved path to VBox disk image, has appended to it formatted text
569 * representing the offset range of the partition.
570 *
571 * $ vboximg-mount -i /stroll/along/the/path/simple_fixed_disk.vdi -p 1 /mnt/tmpdir
572 * $ ls /mnt/tmpdir
573 * simple_fixed_disk.vdi[20480:2013244928] vhdd
574 */
575 rc = stat(g_pszImagePath, stbuf);
576 if (rc < 0)
577 return rc;
578 stbuf->st_size = 0;
579 stbuf->st_mode = S_IFLNK | 0444;
580 stbuf->st_nlink = 1;
581 stbuf->st_uid = 0;
582 stbuf->st_gid = 0;
583 }
584 else
585 {
586 /* Query the VFS object and fill in the data. */
587 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
588 int vrc = vboxImgMntVfsObjQueryFromPath(pszPath, &hVfsObj);
589 if (RT_SUCCESS(vrc))
590 {
591 RTFSOBJINFO ObjInfo;
592
593 vrc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_UNIX);
594 if (RT_SUCCESS(vrc))
595 {
596 stbuf->st_size = ObjInfo.cbObject;
597 stbuf->st_nlink = 1;
598 stbuf->st_uid = 0;
599 stbuf->st_gid = 0;
600
601#ifdef RT_OS_DARWIN
602 RTTimeSpecGetTimespec(&ObjInfo.AccessTime, &stbuf->st_atimespec);
603 RTTimeSpecGetTimespec(&ObjInfo.ModificationTime, &stbuf->st_mtimespec);
604 RTTimeSpecGetTimespec(&ObjInfo.ChangeTime, &stbuf->st_ctimespec);
605 RTTimeSpecGetTimespec(&ObjInfo.BirthTime, &stbuf->st_birthtimespec);
606#else
607 RTTimeSpecGetTimespec(&ObjInfo.AccessTime, &stbuf->st_atim);
608 RTTimeSpecGetTimespec(&ObjInfo.ModificationTime, &stbuf->st_mtim);
609 RTTimeSpecGetTimespec(&ObjInfo.ChangeTime, &stbuf->st_ctim);
610#endif
611
612 switch (ObjInfo.Attr.fMode & RTFS_TYPE_MASK)
613 {
614 case RTFS_TYPE_FIFO:
615 {
616 stbuf->st_mode = S_IFIFO;
617 break;
618 }
619 case RTFS_TYPE_DEV_CHAR:
620 {
621 stbuf->st_mode = S_IFCHR;
622 break;
623 }
624 case RTFS_TYPE_DIRECTORY:
625 {
626 stbuf->st_mode = S_IFDIR;
627 stbuf->st_nlink = 2;
628 break;
629 }
630 case RTFS_TYPE_DEV_BLOCK:
631 {
632 stbuf->st_mode = S_IFBLK;
633 break;
634 }
635 case RTFS_TYPE_FILE:
636 {
637 stbuf->st_mode = S_IFREG;
638 break;
639 }
640 case RTFS_TYPE_SYMLINK:
641 {
642 stbuf->st_mode = S_IFLNK;
643 break;
644 }
645 case RTFS_TYPE_SOCKET:
646 {
647 stbuf->st_mode = S_IFSOCK;
648 break;
649 }
650#if 0 /* Not existing on Linux. */
651 case RTFS_TYPE_WHITEOUT:
652 {
653 stbuf->st_mode = S_IFWHT;
654 break;
655 }
656#endif
657 default:
658 stbuf->st_mode = 0;
659 }
660
661 if (ObjInfo.Attr.fMode & RTFS_UNIX_ISUID)
662 stbuf->st_mode |= S_ISUID;
663 if (ObjInfo.Attr.fMode & RTFS_UNIX_ISGID)
664 stbuf->st_mode |= S_ISGID;
665 if (ObjInfo.Attr.fMode & RTFS_UNIX_ISTXT)
666 stbuf->st_mode |= S_ISTXT;
667
668 /* Owner permissions. */
669 if (ObjInfo.Attr.fMode & RTFS_UNIX_IRUSR)
670 stbuf->st_mode |= S_IRUSR;
671 if (ObjInfo.Attr.fMode & RTFS_UNIX_IWUSR)
672 stbuf->st_mode |= S_IWUSR;
673 if (ObjInfo.Attr.fMode & RTFS_UNIX_IXUSR)
674 stbuf->st_mode |= S_IXUSR;
675
676 /* Group permissions. */
677 if (ObjInfo.Attr.fMode & RTFS_UNIX_IRGRP)
678 stbuf->st_mode |= S_IRGRP;
679 if (ObjInfo.Attr.fMode & RTFS_UNIX_IWGRP)
680 stbuf->st_mode |= S_IWGRP;
681 if (ObjInfo.Attr.fMode & RTFS_UNIX_IXGRP)
682 stbuf->st_mode |= S_IXGRP;
683
684 /* Other permissions. */
685 if (ObjInfo.Attr.fMode & RTFS_UNIX_IROTH)
686 stbuf->st_mode |= S_IROTH;
687 if (ObjInfo.Attr.fMode & RTFS_UNIX_IWOTH)
688 stbuf->st_mode |= S_IWOTH;
689 if (ObjInfo.Attr.fMode & RTFS_UNIX_IXOTH)
690 stbuf->st_mode |= S_IXOTH;
691
692 if (ObjInfo.Attr.enmAdditional == RTFSOBJATTRADD_UNIX)
693 {
694 stbuf->st_uid = ObjInfo.Attr.u.Unix.uid;
695 stbuf->st_gid = ObjInfo.Attr.u.Unix.gid;
696 stbuf->st_nlink = ObjInfo.Attr.u.Unix.cHardlinks;
697 stbuf->st_ino = ObjInfo.Attr.u.Unix.INodeId;
698 stbuf->st_dev = ObjInfo.Attr.u.Unix.INodeIdDevice;
699 /*stbuf->st_flags = ObjInfo.Attr.u.Unix.fFlags;*/ /* Not existing on Linux. */
700 /*stbuf->st_gen = ObjInfo.Attr.u.Unix.GenerationId;*/ /* Not existing on Linux. */
701 stbuf->st_rdev = ObjInfo.Attr.u.Unix.Device;
702 }
703 }
704
705 RTVfsObjRelease(hVfsObj);
706 }
707 else if (vrc == VERR_NOT_FOUND)
708 rc = -ENOENT;
709 else
710 rc = -RTErrConvertToErrno(vrc);
711 }
712
713 return rc;
714}
715
716/** @copydoc fuse_operations::readdir */
717static int vboximgOp_readdir(const char *pszPath, void *pvBuf, fuse_fill_dir_t pfnFiller,
718 off_t offset, struct fuse_file_info *pInfo)
719{
720 RT_NOREF(offset);
721 RT_NOREF(pInfo);
722
723 int rc = 0;
724
725 /* Special root directory handling?. */
726 if (!RTStrCmp(pszPath, "/"))
727 {
728 /*
729 * mandatory '.', '..', ...
730 */
731 pfnFiller(pvBuf, ".", NULL, 0);
732 pfnFiller(pvBuf, "..", NULL, 0);
733
734 if (g_pszImageName)
735 {
736 /*
737 * Create FUSE FS dir entry that is depicted here (and exposed via stat()) as
738 * a symbolic link back to the resolved path to the VBox virtual disk image,
739 * whose symlink name is basename that path. This is a convenience so anyone
740 * listing the dir can figure out easily what the vhdd FUSE node entry
741 * represents.
742 */
743 pfnFiller(pvBuf, g_pszImageName, NULL, 0);
744 }
745
746 if (g_hVfsFileDisk != NIL_RTVFSFILE)
747 {
748 /*
749 * Create entry named "vhdd" denoting the whole disk, which getattr() will describe as a
750 * regular file, and thus will go through the open/release/read/write vectors
751 * to access the VirtualBox image as processed by the IRPT VD API.
752 */
753 pfnFiller(pvBuf, "vhdd", NULL, 0);
754 }
755
756 /* Create entries for the individual volumes. */
757 for (uint32_t i = 0; i < g_cVolumes; i++)
758 {
759 char tmp[64];
760 if (g_paVolumes[i].hVfsFileVol != NIL_RTVFSFILE)
761 {
762 RTStrPrintf(tmp, sizeof (tmp), "vol%u", i);
763 pfnFiller(pvBuf, tmp, NULL, 0);
764 }
765
766 if (g_paVolumes[i].hVfsRoot != NIL_RTVFS)
767 {
768 RTStrPrintf(tmp, sizeof (tmp), "fs%u", i);
769 pfnFiller(pvBuf, tmp, NULL, 0);
770 }
771 }
772 }
773 else
774 {
775 /* Query the VFS object and fill in the data. */
776 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
777 int vrc = vboxImgMntVfsObjQueryFromPath(pszPath, &hVfsObj);
778 if (RT_SUCCESS(vrc))
779 {
780 switch (RTVfsObjGetType(hVfsObj))
781 {
782 case RTVFSOBJTYPE_DIR:
783 {
784 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
785 RTDIRENTRYEX DirEntry;
786
787 vrc = RTVfsDirRewind(hVfsDir); AssertRC(vrc);
788 vrc = RTVfsDirReadEx(hVfsDir, &DirEntry, NULL, RTFSOBJATTRADD_NOTHING);
789 while (RT_SUCCESS(vrc))
790 {
791 pfnFiller(pvBuf, DirEntry.szName, NULL, 0);
792 vrc = RTVfsDirReadEx(hVfsDir, &DirEntry, NULL, RTFSOBJATTRADD_NOTHING);
793 }
794
795 RTVfsDirRelease(hVfsDir);
796 break;
797 }
798 default:
799 rc = -EINVAL;
800 }
801
802 RTVfsObjRelease(hVfsObj);
803 }
804 else
805 rc = -RTErrConvertToErrno(vrc);
806 }
807
808 return rc;
809}
810
811/** @copydoc fuse_operations::readlink */
812static int vboximgOp_readlink(const char *pszPath, char *buf, size_t size)
813{
814 RT_NOREF(pszPath);
815 RTStrCopy(buf, size, g_pszImagePath);
816 return 0;
817}
818
819
820/**
821 * Displays the list of volumes on the opened image.
822 */
823static void vboxImgMntVolumesDisplay(void)
824{
825 /*
826 * Partition table is most readable and concise when headers and columns
827 * are adapted to the actual data, to avoid insufficient or excessive whitespace.
828 */
829
830 RTPrintf( "Virtual disk image:\n\n");
831 RTPrintf(" Base: %s\n", g_pszBaseImagePath);
832 if (g_cImages > 1)
833 RTPrintf(" Diff: %s\n", g_pszImagePath);
834 if (g_pszDiskUuid)
835 RTPrintf(" UUID: %s\n\n", g_pszDiskUuid);
836
837 SELFSIZINGTABLE tbl(2);
838
839 void *colPartition = tbl.addCol("Partition", "%s(%d)", -1);
840 void *colBoot = tbl.addCol("Boot", "%c ", 1);
841 void *colStart = tbl.addCol("Start", "%lld", 1);
842 void *colSectors = tbl.addCol("Sectors", "%lld", -1, 2);
843 void *colSize = tbl.addCol("Size", "%s", 1);
844 void *colOffset = tbl.addCol("Offset", "%lld", 1);
845 void *colType = tbl.addCol("Type", "%s", -1, 2);
846
847 for (uint32_t i = 0; i < g_cVolumes; i++)
848 {
849 PVBOXIMGMOUNTVOL pVol = &g_paVolumes[i];
850 uint64_t fVolFlags = RTDvmVolumeGetFlags(pVol->hVol);
851 uint64_t cbVol = RTDvmVolumeGetSize(pVol->hVol);
852 RTDVMVOLTYPE enmType = RTDvmVolumeGetType(pVol->hVol);
853 uint64_t offStart = 0;
854 uint64_t offEnd = 0;
855
856 if (fVolFlags & DVMVOLUME_F_CONTIGUOUS)
857 {
858 int rc = RTDvmVolumeQueryRange(pVol->hVol, &offStart, &offEnd);
859 AssertRC(rc);
860 }
861
862 void *row = tbl.addRow();
863 tbl.setCell(row, colPartition, g_pszBaseImageName, i);
864 tbl.setCell(row, colBoot, (fVolFlags & DVMVOLUME_FLAGS_BOOTABLE) ? '*' : ' ');
865 tbl.setCell(row, colStart, offStart / g_cbSector);
866 tbl.setCell(row, colSectors, cbVol / g_cbSector);
867 tbl.setCell(row, colSize, vboximgScaledSize(cbVol));
868 tbl.setCell(row, colOffset, offStart);
869 tbl.setCell(row, colType, RTDvmVolumeTypeGetDescr(enmType));
870 }
871 tbl.displayTable();
872 RTPrintf ("\n");
873}
874
875
876/**
877 * Sets up the volumes for the disk.
878 *
879 * @returns IPRT status code.
880 */
881static int vboxImgMntVolumesSetup(void)
882{
883 g_cVolumes = 0;
884 g_paVolumes = NULL;
885
886 int rc = RTDvmCreate(&g_hDvmMgr, g_hVfsFileDisk, g_cbSector, 0 /*fFlags*/);
887 if (RT_SUCCESS(rc))
888 {
889 rc = RTDvmMapOpen(g_hDvmMgr);
890 if (RT_SUCCESS(rc))
891 {
892 g_cVolumes = RTDvmMapGetValidVolumes(g_hDvmMgr);
893 if ( g_cVolumes != UINT32_MAX
894 && g_cVolumes > 0)
895 {
896 g_paVolumes = (PVBOXIMGMOUNTVOL)RTMemAllocZ(g_cVolumes * sizeof(VBOXIMGMOUNTVOL));
897 if (RT_LIKELY(g_paVolumes))
898 {
899 g_paVolumes[0].hVfsRoot = NIL_RTVFS;
900
901 rc = RTDvmMapQueryFirstVolume(g_hDvmMgr, &g_paVolumes[0].hVol);
902 if (RT_SUCCESS(rc))
903 rc = RTDvmVolumeCreateVfsFile(g_paVolumes[0].hVol,
904 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE,
905 &g_paVolumes[0].hVfsFileVol);
906
907 for (uint32_t i = 1; i < g_cVolumes && RT_SUCCESS(rc); i++)
908 {
909 g_paVolumes[i].hVfsRoot = NIL_RTVFS;
910 rc = RTDvmMapQueryNextVolume(g_hDvmMgr, g_paVolumes[i-1].hVol, &g_paVolumes[i].hVol);
911 if (RT_SUCCESS(rc))
912 rc = RTDvmVolumeCreateVfsFile(g_paVolumes[i].hVol,
913 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE,
914 &g_paVolumes[i].hVfsFileVol);
915 }
916
917 if (RT_SUCCESS(rc))
918 return VINF_SUCCESS;
919
920 RTMemFree(g_paVolumes);
921 g_paVolumes = NULL;
922 g_cVolumes = 0;
923 }
924 else
925 rc = VERR_NO_MEMORY;
926 }
927 else if (g_cVolumes == UINT32_MAX)
928 {
929 g_cVolumes = 0;
930 rc = VERR_INTERNAL_ERROR;
931 }
932
933 RTDvmRelease(g_hDvmMgr);
934 }
935 else if (rc == VERR_NOT_FOUND)
936 rc = VINF_SUCCESS;
937 }
938
939 return rc;
940}
941
942
943static int vboxImgMntImageSetup(struct fuse_args *args)
944{
945 /*
946 * Initialize COM.
947 */
948 using namespace com;
949 HRESULT hrc = com::Initialize();
950 if (FAILED(hrc))
951 {
952# ifdef VBOX_WITH_XPCOM
953 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
954 {
955 char szHome[RTPATH_MAX] = "";
956 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
957 return RTMsgErrorExit(RTEXITCODE_FAILURE,
958 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
959 }
960# endif
961 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM! (hrc=%Rhrc)", hrc);
962 }
963
964 /*
965 * Get the remote VirtualBox object and create a local session object.
966 */
967 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
968 ComPtr<IVirtualBox> pVirtualBox;
969
970 hrc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
971 if (SUCCEEDED(hrc))
972 hrc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
973
974 if (FAILED(hrc))
975 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get IVirtualBox object! (hrc=%Rhrc)", hrc);
976
977 if (g_vboximgOpts.fList && g_vboximgOpts.pszImageUuidOrPath == NULL)
978 {
979 vboximgListVMs(pVirtualBox);
980 return VINF_SUCCESS;
981 }
982
983 if (!g_vboximgOpts.pszImageUuidOrPath)
984 return RTMsgErrorExitFailure("A image UUID or path needs to be provided using the --image/-i option\n");
985
986 Bstr pMediumUuid;
987 ComPtr<IMedium> pVDiskMedium = NULL;
988 char *pszFormat;
989 VDTYPE enmType;
990
991 /*
992 * Open chain of images from what is provided on command line, to base image
993 */
994 if (g_vboximgOpts.pszImageUuidOrPath)
995 {
996 /* compiler was too fussy about access mode's data type in conditional expr, so... */
997 if (g_vboximgOpts.fRW)
998 CHECK_ERROR(pVirtualBox, OpenMedium(Bstr(g_vboximgOpts.pszImageUuidOrPath).raw(), DeviceType_HardDisk,
999 AccessMode_ReadWrite, false /* forceNewUuid */, pVDiskMedium.asOutParam()));
1000
1001 else
1002 CHECK_ERROR(pVirtualBox, OpenMedium(Bstr(g_vboximgOpts.pszImageUuidOrPath).raw(), DeviceType_HardDisk,
1003 AccessMode_ReadOnly, false /* forceNewUuid */, pVDiskMedium.asOutParam()));
1004
1005 if (FAILED(hrc))
1006 return RTMsgErrorExitFailure("\nCouldn't find specified VirtualBox base or snapshot disk image:\n%s",
1007 g_vboximgOpts.pszImageUuidOrPath);
1008
1009
1010 CHECK_ERROR(pVDiskMedium, COMGETTER(Id)(pMediumUuid.asOutParam()));
1011 g_pszDiskUuid = RTStrDup((char *)CSTR(pMediumUuid));
1012
1013 /*
1014 * Lock & cache the disk image media chain (from leaf to base).
1015 * Only leaf can be rw (and only if media is being mounted in non-default writable (rw) mode)
1016 *
1017 * Note: Failure to acquire lock is intentionally fatal (e.g. program termination)
1018 */
1019
1020 if (VERBOSE)
1021 RTPrintf("\nAttempting to lock medium chain from leaf image to base image\n");
1022
1023 bool fLeaf = true;
1024 g_cImages = 0;
1025
1026 do
1027 {
1028 ++g_cImages;
1029 IMAGELIST *pNewEntry= new IMAGELIST();
1030 pNewEntry->pImage = pVDiskMedium;
1031 CHECK_ERROR(pVDiskMedium, COMGETTER(Name)((pNewEntry->pImageName).asOutParam()));
1032 CHECK_ERROR(pVDiskMedium, COMGETTER(Location)((pNewEntry->pImagePath).asOutParam()));
1033
1034 if (VERBOSE)
1035 RTPrintf(" %s", CSTR(pNewEntry->pImageName));
1036
1037 if (fLeaf && g_vboximgOpts.fRW)
1038 {
1039 if (VERBOSE)
1040 RTPrintf(" ... Locking for write\n");
1041 CHECK_ERROR_RET(pVDiskMedium, LockWrite((pNewEntry->pLockToken).asOutParam()), hrc);
1042 pNewEntry->fWriteable = true;
1043 }
1044 else
1045 {
1046 if (VERBOSE)
1047 RTPrintf(" ... Locking for read\n");
1048 CHECK_ERROR_RET(pVDiskMedium, LockRead((pNewEntry->pLockToken).asOutParam()), hrc);
1049 }
1050
1051 IMAGELIST *pCurImageEntry = &listHeadLockList;
1052 while (pCurImageEntry->next)
1053 pCurImageEntry = pCurImageEntry->next;
1054 pCurImageEntry->next = pNewEntry;
1055 pNewEntry->prev = pCurImageEntry;
1056 listHeadLockList.prev = pNewEntry;
1057
1058 CHECK_ERROR(pVDiskMedium, COMGETTER(Parent)(pVDiskMedium.asOutParam()));
1059 fLeaf = false;
1060 }
1061 while(pVDiskMedium);
1062 }
1063
1064 ComPtr<IMedium> pVDiskBaseMedium = listHeadLockList.prev->pImage;
1065 Bstr pVDiskBaseImagePath = listHeadLockList.prev->pImagePath;
1066 Bstr pVDiskBaseImageName = listHeadLockList.prev->pImageName;
1067
1068 g_pszBaseImagePath = RTStrDup((char *)CSTR(pVDiskBaseImagePath));
1069 g_pszBaseImageName = RTStrDup((char *)CSTR(pVDiskBaseImageName));
1070
1071 g_pszImagePath = RTStrDup((char *)CSTR(listHeadLockList.next->pImagePath));
1072 g_pszImageName = RTStrDup((char *)CSTR(listHeadLockList.next->pImageName));
1073
1074 /*
1075 * Attempt to VDOpen media (base and any snapshots), handling encryption,
1076 * if that property is set for base media
1077 */
1078 Bstr pBase64EncodedKeyStore;
1079
1080 hrc = pVDiskBaseMedium->GetProperty(Bstr("CRYPT/KeyStore").raw(), pBase64EncodedKeyStore.asOutParam());
1081 if (SUCCEEDED(hrc) && strlen(CSTR(pBase64EncodedKeyStore)) != 0)
1082 {
1083 RTPrintf("\nvboximgMount: Encrypted disks not supported in this version\n\n");
1084 return -1;
1085 }
1086
1087
1088/* ***************** BEGIN IFDEF'D (STUBBED-OUT) CODE ************** */
1089/* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
1090
1091#if 0 /* The following encrypted disk related code is stubbed out until it can be finished.
1092 * What is here is an attempt to port the VBoxSVC specific code in i_openForIO to
1093 * a client's proximity. It is supplemented by code in vboximgCrypto.cpp and
1094 * vboximageCrypt.h that was lifed from SecretKeyStore.cpp along with the setup
1095 * task function.
1096 *
1097 * The ultimate solution may be to use a simpler but less efficient COM interface,
1098 * or to use VD encryption interfaces and key containers entirely. The keystore
1099 * handling/filter approach that is here may be a bumbling hybrid approach
1100 * that is broken (trying to bridge incompatible disk encryption mechanisms) or otherwise
1101 * doesn't make sense. */
1102
1103 Bstr pKeyId;
1104 ComPtr<IExtPackManager> pExtPackManager;
1105 ComPtr<IExtPack> pExtPack;
1106 com::SafeIfaceArray<IExtPackPlugIn> pExtPackPlugIns;
1107
1108 if (SUCCEEDED(rc))
1109 {
1110 RTPrintf("Got GetProperty(\"CRYPT/KeyStore\") = %s\n", CSTR(pBase64EncodedKeyStore));
1111 if (strlen(CSTR(pBase64EncodedKeyStore)) == 0)
1112 return RTMsgErrorExitFailure("Image '%s' is configured for encryption but "
1113 "there is no key store to retrieve the password from", CSTR(pVDiskBaseImageName));
1114
1115 SecretKeyStore keyStore(false);
1116 RTBase64Decode(CSTR(pBase64EncodedKeyStore), &keyStore, sizeof (SecretKeyStore), NULL, NULL);
1117
1118 rc = pVDiskBaseMedium->GetProperty(Bstr("CRYPT/KeyId").raw(), pKeyId.asOutParam());
1119 if (SUCCEEDED(rc) && strlen(CSTR(pKeyId)) == 0)
1120 return RTMsgErrorExitFailure("Image '%s' is configured for encryption but "
1121 "doesn't have a key identifier set", CSTR(pVDiskBaseImageName));
1122
1123 RTPrintf(" key id: %s\n", CSTR(pKeyId));
1124
1125#ifndef VBOX_WITH_EXTPACK
1126 return RTMsgErrorExitFailure(
1127 "Encryption is not supported because extension pack support is not built in");
1128#endif
1129
1130 CHECK_ERROR(pVirtualBox, COMGETTER(ExtensionPackManager)(pExtPackManager.asOutParam()));
1131 BOOL fExtPackUsable;
1132 CHECK_ERROR(pExtPackManager, IsExtPackUsable((PRUnichar *)VBOX_EXTPACK, &fExtPackUsable));
1133 if (fExtPackUsable)
1134 {
1135 /* Load the PlugIn */
1136
1137 CHECK_ERROR(pExtPackManager, Find((PRUnichar *)VBOX_EXTPACK, pExtPack.asOutParam()));
1138 if (RT_FAILURE(rc))
1139 return RTMsgErrorExitFailure(
1140 "Encryption is not supported because the extension pack '%s' is missing",
1141 VBOX_EXTPACK);
1142
1143 CHECK_ERROR(pExtPack, COMGETTER(PlugIns)(ComSafeArrayAsOutParam(pExtPackPlugIns)));
1144
1145 Bstr pPlugInPath;
1146 size_t iPlugIn;
1147 for (iPlugIn = 0; iPlugIn < pExtPackPlugIns.size(); iPlugIn++)
1148 {
1149 Bstr pPlugInName;
1150 CHECK_ERROR(pExtPackPlugIns[iPlugIn], COMGETTER(Name)(pPlugInName.asOutParam()));
1151 if (RTStrCmp(CSTR(pPlugInName), "VDPlugInCrypt") == 0)
1152 {
1153 CHECK_ERROR(pExtPackPlugIns[iPlugIn], COMGETTER(ModulePath)(pPlugInPath.asOutParam()));
1154 break;
1155 }
1156 }
1157 if (iPlugIn == pExtPackPlugIns.size())
1158 return RTMsgErrorExitFailure("Encryption is not supported because the extension pack '%s' "
1159 "is missing the encryption PlugIn (old extension pack installed?)", VBOX_EXTPACK);
1160
1161 rc = VDPluginLoadFromFilename(CSTR(pPlugInPath));
1162 if (RT_FAILURE(rc))
1163 return RTMsgErrorExitFailure("Retrieving encryption settings of the image failed "
1164 "because the encryption PlugIn could not be loaded\n");
1165 }
1166
1167 SecretKey *pKey = NULL;
1168 rc = keyStore.retainSecretKey(Utf8Str(pKeyId), &pKey);
1169 if (RT_FAILURE(rc))
1170 return RTMsgErrorExitFailure(
1171 "Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)",
1172 CSTR(pKeyId), rc);
1173
1174 VDISKCRYPTOSETTINGS vdiskCryptoSettings, *pVDiskCryptoSettings = &vdiskCryptoSettings;
1175
1176 vboxImageCryptoSetup(pVDiskCryptoSettings, NULL,
1177 (const char *)CSTR(pBase64EncodedKeyStore), (const char *)pKey->getKeyBuffer(), false);
1178
1179 rc = VDFilterAdd(g_pVDisk, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pVDiskCryptoSettings->vdFilterIfaces);
1180 keyStore.releaseSecretKey(Utf8Str(pKeyId));
1181
1182 if (rc == VERR_VD_PASSWORD_INCORRECT)
1183 return RTMsgErrorExitFailure("The password to decrypt the image is incorrect");
1184
1185 if (RT_FAILURE(rc))
1186 return RTMsgErrorExitFailure("Failed to load the decryption filter: %Rrc", rc);
1187 }
1188#endif
1189
1190/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
1191/* **************** END IFDEF'D (STUBBED-OUT) CODE ***************** */
1192
1193 int vrc = RTCritSectInit(&g_vdioLock);
1194 if (RT_SUCCESS(vrc))
1195 {
1196 g_VDIfThreadSync.pfnStartRead = vboximgThreadStartRead;
1197 g_VDIfThreadSync.pfnFinishRead = vboximgThreadFinishRead;
1198 g_VDIfThreadSync.pfnStartWrite = vboximgThreadStartWrite;
1199 g_VDIfThreadSync.pfnFinishWrite = vboximgThreadFinishWrite;
1200 vrc = VDInterfaceAdd(&g_VDIfThreadSync.Core, "vboximg_ThreadSync", VDINTERFACETYPE_THREADSYNC,
1201 &g_vdioLock, sizeof(VDINTERFACETHREADSYNC), &g_pVdIfs);
1202 }
1203 else
1204 return RTMsgErrorExitFailure("ERROR: Failed to create critsects "
1205 "for virtual disk I/O, rc=%Rrc\n", vrc);
1206
1207 /*
1208 * Create HDD container to open base image and differencing images into
1209 */
1210 vrc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/,
1211 CSTR(pVDiskBaseImagePath), VDTYPE_INVALID, &pszFormat, &enmType);
1212
1213 if (RT_FAILURE(vrc))
1214 return RTMsgErrorExitFailure("VDGetFormat(,,%s,,) "
1215 "failed (during HDD container creation), rc=%Rrc\n", g_pszImagePath, vrc);
1216
1217 if (VERBOSE)
1218 RTPrintf("\nCreating container for base image of format %s\n", pszFormat);
1219
1220 PVDISK pVDisk = NULL;
1221 vrc = VDCreate(g_pVdIfs, enmType, &pVDisk);
1222 if (RT_FAILURE(vrc))
1223 return RTMsgErrorExitFailure("ERROR: Couldn't create virtual disk container\n");
1224
1225 /* Open all virtual disk media from leaf snapshot (if any) to base image*/
1226
1227 if (VERBOSE)
1228 RTPrintf("\nOpening medium chain\n");
1229
1230 IMAGELIST *pCurMedium = listHeadLockList.prev; /* point to base image */
1231 while (pCurMedium != &listHeadLockList)
1232 {
1233 if (VERBOSE)
1234 RTPrintf(" Open: %s\n", CSTR(pCurMedium->pImagePath));
1235
1236 vrc = VDOpen(pVDisk,
1237 pszFormat,
1238 CSTR(pCurMedium->pImagePath),
1239 pCurMedium->fWriteable ? 0 : VD_OPEN_FLAGS_READONLY,
1240 g_pVdIfs);
1241
1242 if (RT_FAILURE(vrc))
1243 return RTMsgErrorExitFailure("Could not open the medium storage unit '%s' %Rrc",
1244 CSTR(pCurMedium->pImagePath), vrc);
1245
1246 pCurMedium = pCurMedium->prev;
1247 }
1248
1249 RTStrFree(pszFormat);
1250
1251 /* Create the VFS file to use for the disk image access. */
1252 vrc = VDCreateVfsFileFromDisk(pVDisk, VD_VFSFILE_DESTROY_ON_RELEASE, &g_hVfsFileDisk);
1253 if (RT_FAILURE(vrc))
1254 return RTMsgErrorExitFailure("Error creating VFS file wrapper for disk image\n");
1255
1256 g_cbSector = VDGetSectorSize(pVDisk, VD_LAST_IMAGE);
1257
1258 vrc = vboxImgMntVolumesSetup();
1259 if (RT_FAILURE(vrc))
1260 return RTMsgErrorExitFailure("Error parsing volumes on disk\n");
1261
1262 if (g_vboximgOpts.fList)
1263 {
1264 if (g_hVfsFileDisk == NIL_RTVFSFILE)
1265 return RTMsgErrorExitFailure("No valid --image to list partitions from\n");
1266
1267 RTPrintf("\n");
1268 vboxImgMntVolumesDisplay();
1269 return VINF_SUCCESS; /** @todo r=andy Re-visit this. */
1270 }
1271
1272 /* Try to "mount" supported filesystems inside the disk image if specified. */
1273 if (g_vboximgOpts.fGstFs)
1274 {
1275 for (uint32_t i = 0; i < g_cVolumes; i++)
1276 {
1277 vrc = RTVfsMountVol(g_paVolumes[i].hVfsFileVol,
1278 g_vboximgOpts.fRW ? 0 : RTVFSMNT_F_READ_ONLY,
1279 &g_paVolumes[i].hVfsRoot,
1280 NULL);
1281 if (RT_SUCCESS(vrc))
1282 {
1283 vrc = RTVfsOpenRoot(g_paVolumes[i].hVfsRoot, &g_paVolumes[i].hVfsDirRoot);
1284 if (RT_FAILURE(vrc))
1285 {
1286 RTPrintf("\nvboximg-mount: Failed to access filesystem on volume %u, ignoring\n", i);
1287 RTVfsRelease(g_paVolumes[i].hVfsRoot);
1288 g_paVolumes[i].hVfsRoot = NIL_RTVFS;
1289 }
1290 }
1291 else
1292 RTPrintf("\nvboximg-mount: Failed to access filesystem on volume %u, ignoring\n", i);
1293 }
1294 }
1295
1296 /*
1297 * Hand control over to libfuse.
1298 */
1299 if (VERBOSE)
1300 RTPrintf("\nvboximg-mount: Going into background...\n");
1301
1302 int rc = fuse_main_real(args->argc, args->argv, &g_vboximgOps, sizeof(g_vboximgOps), NULL);
1303 RTPrintf("vboximg-mount: fuse_main -> %d\n", rc);
1304
1305 int rc2 = RTVfsFileRelease(g_hVfsFileDisk);
1306 AssertRC(rc2);
1307
1308 return vrc;
1309}
1310
1311
1312int main(int argc, char **argv)
1313{
1314
1315 int rc = RTR3InitExe(argc, &argv, 0);
1316 if (RT_FAILURE(rc))
1317 return RTMsgErrorExitFailure("RTR3InitExe failed, rc=%Rrc\n", rc);
1318
1319 rc = VDInit();
1320 if (RT_FAILURE(rc))
1321 return RTMsgErrorExitFailure("VDInit failed, rc=%Rrc\n", rc);
1322
1323 rc = RTFuseLoadLib();
1324 if (RT_FAILURE(rc))
1325 return RTMsgErrorExitFailure("Failed to load the fuse library, rc=%Rrc\n", rc);
1326
1327 memset(&g_vboximgOps, 0, sizeof(g_vboximgOps));
1328 g_vboximgOps.open = vboximgOp_open;
1329 g_vboximgOps.read = vboximgOp_read;
1330 g_vboximgOps.write = vboximgOp_write;
1331 g_vboximgOps.getattr = vboximgOp_getattr;
1332 g_vboximgOps.release = vboximgOp_release;
1333 g_vboximgOps.readdir = vboximgOp_readdir;
1334 g_vboximgOps.readlink = vboximgOp_readlink;
1335
1336 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1337 memset(&g_vboximgOpts, 0, sizeof(g_vboximgOpts));
1338
1339 rc = fuse_opt_parse(&args, &g_vboximgOpts, vboximgOptDefs, vboximgOptHandler);
1340 if (rc < 0 || argc < 2 || RTStrCmp(argv[1], "-?" ) == 0 || g_vboximgOpts.fBriefUsage)
1341 {
1342 briefUsage();
1343 return 0;
1344 }
1345
1346 if (g_vboximgOpts.fAllowRoot)
1347 fuse_opt_add_arg(&args, "-oallow_root");
1348
1349 /*
1350 * FUSE doesn't seem to like combining options with one hyphen, as traditional UNIX
1351 * command line utilities allow. The following flags, fWideList and fVerboseList,
1352 * and their respective option definitions give the appearance of combined opts,
1353 * so that -vl, -lv, -wl, -lw options are allowed, since those in particular would
1354 * tend to conveniently facilitate some of the most common use cases.
1355 */
1356 if (g_vboximgOpts.fWideList)
1357 {
1358 g_vboximgOpts.fWide = true;
1359 g_vboximgOpts.fList = true;
1360 }
1361 if (g_vboximgOpts.fVerboseList)
1362 {
1363 g_vboximgOpts.fVerbose = true;
1364 g_vboximgOpts.fList = true;
1365 }
1366 if (g_vboximgOpts.fAllowRoot)
1367 fuse_opt_add_arg(&args, "-oallow_root");
1368
1369 if ( !g_vboximgOpts.pszImageUuidOrPath
1370 || !RTVfsChainIsSpec(g_vboximgOpts.pszImageUuidOrPath))
1371 return vboxImgMntImageSetup(&args);
1372
1373 /* Mount the VFS chain. */
1374 RTVFSOBJ hVfsObj;
1375 rc = RTVfsChainOpenObj(g_vboximgOpts.pszImageUuidOrPath, RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
1376 RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK,
1377 &hVfsObj, NULL, NULL);
1378 if ( RT_SUCCESS(rc)
1379 && RTVfsObjGetType(hVfsObj) == RTVFSOBJTYPE_VFS)
1380 {
1381 g_paVolumes = (PVBOXIMGMOUNTVOL)RTMemAllocZ(sizeof(*g_paVolumes));
1382 if (RT_LIKELY(g_paVolumes))
1383 {
1384 g_cVolumes = 1;
1385 g_paVolumes[0].hVfsRoot = RTVfsObjToVfs(hVfsObj);
1386 g_paVolumes[0].hVfsFileVol = NIL_RTVFSFILE;
1387 RTVfsObjRelease(hVfsObj);
1388
1389 rc = RTVfsOpenRoot(g_paVolumes[0].hVfsRoot, &g_paVolumes[0].hVfsDirRoot);
1390 if (RT_SUCCESS(rc))
1391 {
1392 /*
1393 * Hand control over to libfuse.
1394 */
1395 if (VERBOSE)
1396 RTPrintf("\nvboximg-mount: Going into background...\n");
1397
1398 rc = fuse_main_real(args.argc, args.argv, &g_vboximgOps, sizeof(g_vboximgOps), NULL);
1399 RTVfsDirRelease(g_paVolumes[0].hVfsDirRoot);
1400 RTVfsRelease(g_paVolumes[0].hVfsRoot);
1401 }
1402
1403 RTMemFree(g_paVolumes);
1404 g_paVolumes = NULL;
1405 g_cVolumes = 0;
1406 }
1407 else
1408 rc = VERR_NO_MEMORY;
1409
1410 RTVfsObjRelease(hVfsObj);
1411 }
1412
1413 return rc;
1414}
1415
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