VirtualBox

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

Last change on this file since 103068 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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