VirtualBox

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

Last change on this file since 90802 was 87350, checked in by vboxsync, 4 years ago

vboximg-mount: vboximg-mount no longer honors the '--root' option. ticketref:20073

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