VirtualBox

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

Last change on this file since 80519 was 80519, checked in by vboxsync, 5 years ago

vboximg-mount: Fixes, use RTVfsDirRewind to reset the directory handle to the beginning before reading the entries

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