VirtualBox

source: vbox/trunk/src/VBox/ImageMounter/vboxraw/vboxraw.cpp@ 75713

Last change on this file since 75713 was 75476, checked in by vboxsync, 6 years ago

fixed a couple of typos

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.6 KB
Line 
1/* $Id: vboxraw.cpp 75476 2018-11-15 05:39:01Z vboxsync $ */
2/** @file
3 * vboxraw - Disk Image Flattening FUSE Program.
4 */
5
6/*
7 * Copyright (C) 2009-2017 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#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo log group */
23
24#define FUSE_USE_VERSION 27
25#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined(RT_OS_FEEBSD)
26# define UNIX_DERIVATIVE
27#endif
28#define MAX_READERS (INT32_MAX / 32)
29
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 <sys/stat.h>
38#endif
39#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX)
40# include <sys/param.h>
41# undef PVM /* Blasted old BSD mess still hanging around darwin. */
42#endif
43#ifdef RT_OS_LINUX
44# include <linux/fs.h>
45# include <linux/hdreg.h>
46#endif
47#include <VirtualBox_XPCOM.h>
48#include <VBox/com/VirtualBox.h>
49#include <VBox/vd.h>
50#include <VBox/vd-ifs.h>
51#include <VBox/log.h>
52#include <VBox/err.h>
53#include <VBox/com/ErrorInfo.h>
54#include <VBox/com/NativeEventQueue.h>
55#include <VBox/com/com.h>
56#include <VBox/com/string.h>
57#include <VBox/com/Guid.h>
58#include <VBox/com/array.h>
59#include <VBox/com/errorprint.h>
60
61#include <iprt/initterm.h>
62#include <iprt/assert.h>
63#include <iprt/message.h>
64#include <iprt/critsect.h>
65#include <iprt/asm.h>
66#include <iprt/mem.h>
67#include <iprt/string.h>
68#include <iprt/initterm.h>
69#include <iprt/stream.h>
70#include <iprt/types.h>
71#include <iprt/path.h>
72
73#pragma GCC diagnostic ignored "-Wunused-variable"
74#pragma GCC diagnostic ignored "-Wunused-parameter"
75#pragma GCC diagnostic ignored "-Wunused-function"
76
77using namespace com;
78
79enum {
80 USAGE_FLAG,
81};
82
83/* For getting the basename of the image path */
84union
85{
86 RTPATHSPLIT split;
87 uint8_t abPad[RTPATH_MAX + 1024];
88} g_u;
89
90
91#define VD_SECTOR_SIZE 0x200 /* 0t512 */
92#define VD_SECTOR_MASK (VD_SECTOR_SIZE - 1)
93#define VD_SECTOR_OUT_OF_BOUNDS_MASK (~UINT64_C(VD_SECTOR_MASK))
94
95#define PADMAX 50
96#define MAX_ID_LEN 256
97#define CSTR(arg) Utf8Str(arg).c_str()
98
99static struct fuse_operations g_vboxrawOps;
100PVDISK g_pVDisk;
101int32_t g_cReaders;
102int32_t g_cWriters;
103RTFOFF g_cbPrimary;
104char *g_pszBaseImageName;
105char *g_pszBaseImagePath;
106PVDINTERFACE g_pVdIfs; /** @todo Remove when VD I/O becomes threadsafe */
107VDINTERFACETHREADSYNC g_VDIfThreadSync; /** @todo Remove when VD I/O becomes threadsafe */
108RTCRITSECT g_vdioLock; /** @todo Remove when VD I/O becomes threadsafe */
109
110char *nsIDToString(nsID *guid);
111void printErrorInfo();
112/** XPCOM stuff */
113
114static struct vboxrawOpts {
115 char *pszVm;
116 char *pszImage;
117 char *pszImageUuid;
118 uint32_t cHddImageDiffMax;
119 uint32_t fList;
120 uint32_t fAllowRoot;
121 uint32_t fRW;
122 uint32_t fVerbose;
123} g_vboxrawOpts;
124
125#define OPTION(fmt, pos, val) { fmt, offsetof(struct vboxrawOpts, pos), val }
126
127static struct fuse_opt vboxrawOptDefs[] = {
128 OPTION("--list", fList, 1),
129 OPTION("--root", fAllowRoot, 1),
130 OPTION("--vm=%s", pszVm, 0),
131 OPTION("--maxdiff=%d", cHddImageDiffMax, 1),
132 OPTION("--diff=%d", cHddImageDiffMax, 1),
133 OPTION("--image=%s", pszImage, 0),
134 OPTION("--rw", fRW, 1),
135 OPTION("--verbose", fVerbose, 1),
136 FUSE_OPT_KEY("-h", USAGE_FLAG),
137 FUSE_OPT_KEY("--help", USAGE_FLAG),
138 FUSE_OPT_END
139};
140
141static int vboxrawOptHandler(void *data, const char *arg, int optKey, struct fuse_args *outargs)
142{
143 (void) data;
144 (void) arg;
145
146 char *progname = basename(outargs->argv[0]);
147 switch(optKey)
148 {
149 case USAGE_FLAG:
150 RTPrintf("usage: %s [options] <mountpoint>\n\n"
151 "%s options:\n"
152 " [--list] List media\n"
153 " [--root] Same as -o allow_root\n"
154 " [--rw] writeable (default = readonly)\n"
155 " [--vm <name | UUID >] vm UUID (limit media list to specific VM)\n\n"
156 " [--diff=<diff #> ] Apply diffs to base image up "
157 "to and including specified diff #\n"
158 " (0 = no diffs, default: all diffs)\n\n"
159 " [--image=<UUID, name or path>] Image UUID or path\n"
160 " [--verbose] Log extra information\n"
161 " -o opt[,opt...] mount options\n"
162 " -h --help print usage\n\n",
163 progname, progname);
164 RTPrintf("\n");
165 RTPrintf("When successful, the --image option creates a one-directory-deep filesystem \n");
166 RTPrintf("rooted at the specified mountpoint. The contents of the directory will be \n");
167 RTPrintf("a symbolic link with the base name of the image file pointing to the path of\n");
168 RTPrintf("the virtual disk image, and a regular file named 'vhdd', which represents\n");
169 RTPrintf("the byte stream of the disk image as interpreted by VirtualBox.\n");
170 RTPrintf("It is the vhdd file that the user or a utility will subsequently mount on\n");
171 RTPrintf("the host OS to gain access to the virtual disk contents.\n\n");
172 fuse_opt_add_arg(outargs, "-ho");
173 fuse_main(outargs->argc, outargs->argv, &g_vboxrawOps, NULL);
174 exit(1);
175 }
176 return 1;
177}
178
179/** @copydoc fuse_operations::open */
180static int vboxrawOp_open(const char *pszPath, struct fuse_file_info *pInfo)
181{
182 (void) pInfo;
183 LogFlowFunc(("pszPath=%s\n", pszPath));
184 uint32_t notsup = 0;
185 int rc = 0;
186
187#ifdef UNIX_DERIVATIVE
188# ifdef RT_OS_DARWIN
189 notsup = O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK |
190 O_ASYNC | O_CREAT | O_TRUNC | O_EXCL | O_EVTONLY;
191# elif defined(RT_OS_LINUX)
192 notsup = O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
193 /* | O_LARGEFILE | O_SYNC | ? */
194# elif defined(RT_OS_FREEBSD)
195 notsup = O_APPEND | O_ASYNC | O_DIRECT | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
196 /* | O_LARGEFILE | O_SYNC | ? */
197# endif
198#else
199# error "Port me"
200#endif
201
202if (pInfo->flags & notsup)
203 rc -EINVAL;
204
205#ifdef UNIX_DERIVATIVE
206 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
207 rc = -EINVAL;
208# ifdef O_DIRECTORY
209 if (pInfo->flags & O_DIRECTORY)
210 rc = -ENOTDIR;
211# endif
212#endif
213
214 if (RT_FAILURE(rc))
215 {
216 LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath));
217 return rc;
218 }
219
220 int fWriteable = (pInfo->flags & O_ACCMODE) == O_WRONLY
221 || (pInfo->flags & O_ACCMODE) == O_RDWR;
222 if (g_cWriters)
223 rc = -ETXTBSY;
224 else
225 {
226 if (fWriteable)
227 g_cWriters++;
228 else
229 {
230 if (g_cReaders + 1 > MAX_READERS)
231 rc = -EMLINK;
232 else
233 g_cReaders++;
234 }
235 }
236 LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath));
237 return rc;
238
239}
240
241/** @todo Remove when VD I/O becomes threadsafe */
242static DECLCALLBACK(int) vboxrawThreadStartRead(void *pvUser)
243{
244 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
245 return RTCritSectEnter(vdioLock);
246}
247
248static DECLCALLBACK(int) vboxrawThreadFinishRead(void *pvUser)
249{
250 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
251 return RTCritSectLeave(vdioLock);
252}
253
254static DECLCALLBACK(int) vboxrawThreadStartWrite(void *pvUser)
255{
256 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
257 return RTCritSectEnter(vdioLock);
258}
259
260static DECLCALLBACK(int) vboxrawThreadFinishWrite(void *pvUser)
261{
262 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
263 return RTCritSectLeave(vdioLock);
264}
265
266/** @todo (end of to do section) */
267
268/** @copydoc fuse_operations::release */
269static int vboxrawOp_release(const char *pszPath, struct fuse_file_info *pInfo)
270{
271 (void) pszPath;
272
273 LogFlowFunc(("pszPath=%s\n", pszPath));
274
275 if ( (pInfo->flags & O_ACCMODE) == O_WRONLY
276 || (pInfo->flags & O_ACCMODE) == O_RDWR)
277 {
278 g_cWriters--;
279 Assert(g_cWriters >= 0);
280 }
281 else if ((pInfo->flags & O_ACCMODE) == O_RDONLY)
282 {
283 g_cReaders--;
284 Assert(g_cReaders >= 0);
285 }
286 else
287 AssertFailed();
288
289 LogFlowFunc(("\"%s\"\n", pszPath));
290 return 0;
291}
292
293
294/**
295 * VD read Sanitizer taking care of unaligned accesses.
296 *
297 * @return VBox status code.
298 * @param pDisk VD disk container.
299 * @param off Offset to start reading from.
300 * @param pvBuf Pointer to the buffer to read into.
301 * @param cbRead Amount of bytes to read.
302 */
303static int vdReadSanitizer(PVDISK pDisk, uint64_t off, void *pvBuf, size_t cbRead)
304{
305 int rc;
306
307 uint64_t const cbMisalignHead = off & VD_SECTOR_MASK;
308 uint64_t const cbMisalignTail = (off + cbRead) & VD_SECTOR_MASK;
309
310 if (cbMisalignHead + cbMisalignTail == 0) /* perfectly aligned request; just read it and done */
311 rc = VDRead(pDisk, off, pvBuf, cbRead);
312 else
313 {
314 uint8_t *pbBuf = (uint8_t *)pvBuf;
315 uint8_t abBuf[VD_SECTOR_SIZE];
316
317 /* If offset not @ sector boundary, read whole sector, then copy unaligned
318 * bytes (requested by user), only up to sector boundary, into user's buffer
319 */
320 if (cbMisalignHead)
321 {
322 rc = VDRead(pDisk, off - cbMisalignHead, abBuf, VD_SECTOR_SIZE);
323 if (RT_SUCCESS(rc))
324 {
325 size_t const cbPart = RT_MIN(VD_SECTOR_SIZE - cbMisalignHead, cbRead);
326 memcpy(pbBuf, &abBuf[cbMisalignHead], cbPart);
327 pbBuf += cbPart;
328 off += cbPart; /* Beginning of next sector or EOD */
329 cbRead -= cbPart; /* # left to read */
330 }
331 }
332 else /* user's offset already aligned, did nothing */
333 rc = VINF_SUCCESS;
334
335 /* Read remaining aligned sectors, deferring any tail-skewed bytes */
336 if (RT_SUCCESS(rc) && cbRead >= VD_SECTOR_SIZE)
337 {
338 Assert(!(off % VD_SECTOR_SIZE));
339
340 size_t cbPart = cbRead - cbMisalignTail;
341 Assert(!(cbPart % VD_SECTOR_SIZE));
342 rc = VDRead(pDisk, off, pbBuf, cbPart);
343 if (RT_SUCCESS(rc))
344 {
345 pbBuf += cbPart;
346 off += cbPart;
347 cbRead -= cbPart;
348 }
349 }
350
351 /* Unaligned buffered read of tail. */
352 if (RT_SUCCESS(rc) && cbRead)
353 {
354 Assert(cbRead == cbMisalignTail);
355 Assert(cbRead < VD_SECTOR_SIZE);
356 Assert(!(off % VD_SECTOR_SIZE));
357
358 rc = VDRead(pDisk, off, abBuf, VD_SECTOR_SIZE);
359 if (RT_SUCCESS(rc))
360 memcpy(pbBuf, abBuf, cbRead);
361 }
362 }
363
364 if (RT_FAILURE(rc))
365 {
366 int sysrc = -RTErrConvertToErrno(rc);
367 LogFlowFunc(("error: %s (vbox err: %d)\n", strerror(sysrc), rc));
368 rc = sysrc;
369 }
370 return cbRead;
371}
372
373/**
374 * VD write Sanitizer taking care of unaligned accesses.
375 *
376 * @return VBox status code.
377 * @param pDisk VD disk container.
378 * @param off Offset to start writing to.
379 * @param pvSrc Pointer to the buffer to read from.
380 * @param cbWrite Amount of bytes to write.
381 */
382static int vdWriteSanitizer(PVDISK pDisk, uint64_t off, const void *pvSrc, size_t cbWrite)
383{
384 uint8_t const *pbSrc = (uint8_t const *)pvSrc;
385 uint8_t abBuf[4096];
386 int rc;
387 int cbRemaining = cbWrite;
388 /*
389 * Take direct route if the request is sector aligned.
390 */
391 uint64_t const cbMisalignHead = off & 511;
392 size_t const cbMisalignTail = (off + cbWrite) & 511;
393 if (!cbMisalignHead && !cbMisalignTail)
394 {
395 rc = VDWrite(pDisk, off, pbSrc, cbWrite);
396 do
397 {
398 size_t cbThisWrite = RT_MIN(cbWrite, sizeof(abBuf));
399 rc = VDWrite(pDisk, off, memcpy(abBuf, pbSrc, cbThisWrite), cbThisWrite);
400 if (RT_SUCCESS(rc))
401 {
402 pbSrc += cbThisWrite;
403 off += cbThisWrite;
404 cbRemaining -= cbThisWrite;
405 }
406 else
407 break;
408 } while (cbRemaining > 0);
409 }
410 else
411 {
412 /*
413 * Unaligned buffered read+write of head. Aligns the offset.
414 */
415 if (cbMisalignHead)
416 {
417 rc = VDRead(pDisk, off - cbMisalignHead, abBuf, VD_SECTOR_SIZE);
418 if (RT_SUCCESS(rc))
419 {
420 size_t const cbPart = RT_MIN(VD_SECTOR_SIZE - cbMisalignHead, cbWrite);
421 memcpy(&abBuf[cbMisalignHead], pbSrc, cbPart);
422 rc = VDWrite(pDisk, off - cbMisalignHead, abBuf, VD_SECTOR_SIZE);
423 if (RT_SUCCESS(rc))
424 {
425 pbSrc += cbPart;
426 off += cbPart;
427 cbRemaining -= cbPart;
428 }
429 }
430 }
431 else
432 rc = VINF_SUCCESS;
433
434 /*
435 * Aligned direct write.
436 */
437 if (RT_SUCCESS(rc) && cbWrite >= VD_SECTOR_SIZE)
438 {
439 Assert(!(off % VD_SECTOR_SIZE));
440 size_t cbPart = cbWrite - cbMisalignTail;
441 Assert(!(cbPart % VD_SECTOR_SIZE));
442 rc = VDWrite(pDisk, off, pbSrc, cbPart);
443 if (RT_SUCCESS(rc))
444 {
445 pbSrc += cbPart;
446 off += cbPart;
447 cbRemaining -= cbPart;
448 }
449 }
450
451 /*
452 * Unaligned buffered read + write of tail.
453 */
454 if ( RT_SUCCESS(rc) && cbWrite > 0)
455 {
456 Assert(cbWrite == cbMisalignTail);
457 Assert(cbWrite < VD_SECTOR_SIZE);
458 Assert(!(off % VD_SECTOR_SIZE));
459 rc = VDRead(pDisk, off, abBuf, VD_SECTOR_SIZE);
460 if (RT_SUCCESS(rc))
461 {
462 memcpy(abBuf, pbSrc, cbWrite);
463 rc = VDWrite(pDisk, off, abBuf, VD_SECTOR_SIZE);
464 }
465 }
466 }
467 if (RT_FAILURE(rc))
468 {
469 int sysrc = -RTErrConvertToErrno(rc);
470 LogFlowFunc(("error: %s (vbox err: %d)\n", strerror(sysrc), rc));
471 return sysrc;
472 }
473 return cbWrite - cbRemaining;
474}
475
476
477/** @copydoc fuse_operations::read */
478static int vboxrawOp_read(const char *pszPath, char *pbBuf, size_t cbBuf,
479 off_t offset, struct fuse_file_info *pInfo)
480{
481 (void) pszPath;
482 (void) pInfo;
483
484 LogFlowFunc(("my offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
485
486 AssertReturn(offset >= 0, -EINVAL);
487 AssertReturn((int)cbBuf >= 0, -EINVAL);
488 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
489
490 int rc = 0;
491 if ((off_t)(offset + cbBuf) < offset)
492 rc = -EINVAL;
493 else if (offset >= g_cbPrimary)
494 return 0;
495 else if (!cbBuf)
496 return 0;
497
498 if (rc >= 0)
499 rc = vdReadSanitizer(g_pVDisk, offset, pbBuf, cbBuf);
500 if (rc < 0)
501 LogFlowFunc(("%s\n", strerror(rc)));
502 return rc;
503}
504
505/** @copydoc fuse_operations::write */
506static int vboxrawOp_write(const char *pszPath, const char *pbBuf, size_t cbBuf,
507 off_t offset, struct fuse_file_info *pInfo)
508{
509 (void) pszPath;
510 (void) pInfo;
511
512 LogFlowFunc(("offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
513
514 AssertReturn(offset >= 0, -EINVAL);
515 AssertReturn((int)cbBuf >= 0, -EINVAL);
516 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
517
518 int rc = 0;
519 if (!g_vboxrawOpts.fRW) {
520 LogFlowFunc(("WARNING: vboxraw (FUSE FS) --rw option not specified\n"
521 " (write operation ignored w/o error!)\n"));
522 return cbBuf;
523 } else if ((off_t)(offset + cbBuf) < offset)
524 rc = -EINVAL;
525 else if (offset >= g_cbPrimary)
526 return 0;
527 else if (!cbBuf)
528 return 0;
529
530 if (rc >= 0)
531 rc = vdWriteSanitizer(g_pVDisk, offset, pbBuf, cbBuf);
532 if (rc < 0)
533 LogFlowFunc(("%s\n", strerror(rc)));
534
535 return rc;
536}
537
538/** @copydoc fuse_operations::getattr */
539static int
540vboxrawOp_getattr(const char *pszPath, struct stat *stbuf)
541{
542 int rc = 0;
543
544 LogFlowFunc(("pszPath=%s, stat(\"%s\")\n", pszPath, g_pszBaseImagePath));
545
546 memset(stbuf, 0, sizeof(struct stat));
547
548 if (RTStrCmp(pszPath, "/") == 0)
549 {
550 stbuf->st_mode = S_IFDIR | 0755;
551 stbuf->st_nlink = 2;
552 }
553 else if (RTStrCmp(pszPath + 1, "vhdd") == 0)
554 {
555 rc = stat(g_pszBaseImagePath, stbuf);
556 if (rc < 0)
557 return rc;
558
559 stbuf->st_size = VDGetSize(g_pVDisk, 0);
560 stbuf->st_nlink = 1;
561 }
562 else if (RTStrCmp(pszPath + 1, g_pszBaseImageName) == 0)
563 {
564 rc = stat(g_pszBaseImagePath, stbuf);
565 if (rc < 0)
566 return rc;
567 stbuf->st_size = 0;
568 stbuf->st_mode = S_IFLNK | 0444;
569 stbuf->st_nlink = 1;
570 stbuf->st_uid = 0;
571 stbuf->st_gid = 0;
572 } else
573 rc = -ENOENT;
574
575 return rc;
576}
577
578/** @copydoc fuse_operations::readdir */
579static int
580vboxrawOp_readdir(const char *pszPath, void *pvBuf, fuse_fill_dir_t pfnFiller,
581 off_t offset, struct fuse_file_info *pInfo)
582
583{
584
585 (void) offset;
586 (void) pInfo;
587
588 if (RTStrCmp(pszPath, "/") != 0)
589 return -ENOENT;
590
591 /*
592 * The usual suspects (mandatory)
593 */
594 pfnFiller(pvBuf, ".", NULL, 0);
595 pfnFiller(pvBuf, "..", NULL, 0);
596
597 /*
598 * Create one entry w/basename of original image that getattr() will
599 * depict as a symbolic link pointing back to the original file,
600 * as a convenience, so anyone who lists the FUSE file system can
601 * easily find the associated vdisk.
602 */
603 pfnFiller(pvBuf, g_pszBaseImageName, NULL, 0);
604
605 /*
606 * Create entry named "vhdd", which getattr() will describe as a
607 * regular file, and thus will go through the open/release/read/write vectors
608 * to access the VirtualBox image as processed by the IRPT VD API.
609 */
610 pfnFiller(pvBuf, "vhdd", NULL, 0);
611 return 0;
612}
613
614/** @copydoc fuse_operations::readlink */
615static int
616vboxrawOp_readlink(const char *pszPath, char *buf, size_t size)
617{
618 (void) pszPath;
619 RTStrCopy(buf, size, g_pszBaseImagePath);
620 return 0;
621}
622
623static void
624listMedia(IMachine *pMachine)
625{
626 int rc = 0;
627 com::SafeIfaceArray<IMediumAttachment> pMediumAttachments;
628
629 CHECK_ERROR(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(pMediumAttachments)));
630 for (size_t i = 0; i < pMediumAttachments.size(); i++)
631 {
632 ComPtr<IMedium> pMedium;
633 DeviceType_T deviceType;
634 Bstr pMediumUuid;
635 Bstr pMediumName;
636 Bstr pMediumPath;
637
638 CHECK_ERROR(pMediumAttachments[i], COMGETTER(Type)(&deviceType));
639
640 if (deviceType == DeviceType_HardDisk)
641 {
642 CHECK_ERROR(pMediumAttachments[i], COMGETTER(Medium)(pMedium.asOutParam()));
643 if (pMedium.isNull())
644 return;
645
646 MediumState_T state;
647 CHECK_ERROR(pMedium, COMGETTER(State)(&state));
648 if (FAILED(rc))
649 return;
650 if (state == MediumState_Inaccessible)
651 {
652 CHECK_ERROR(pMedium, RefreshState(&state));
653 if (FAILED(rc))
654 return;
655 }
656
657 ComPtr<IMedium> pEarliestAncestor;
658 CHECK_ERROR(pMedium, COMGETTER(Base)(pEarliestAncestor.asOutParam()));
659 ComPtr<IMedium> pChild = pEarliestAncestor;
660 uint32_t ancestorNumber = 0;
661 if (pEarliestAncestor.isNull())
662 return;
663 RTPrintf("\n");
664 do
665 {
666 com::SafeIfaceArray<IMedium> aMediumChildren;
667 CHECK_ERROR(pChild, COMGETTER(Name)(pMediumName.asOutParam()));
668 CHECK_ERROR(pChild, COMGETTER(Id)(pMediumUuid.asOutParam()));
669 CHECK_ERROR(pChild, COMGETTER(Location)(pMediumPath.asOutParam()));
670
671 if (ancestorNumber == 0)
672 {
673 RTPrintf(" -----------------------\n");
674 RTPrintf(" HDD base: \"%s\"\n", CSTR(pMediumName));
675 RTPrintf(" UUID: %s\n", CSTR(pMediumUuid));
676 RTPrintf(" Location: %s\n\n", CSTR(pMediumPath));
677 }
678 else
679 {
680 RTPrintf(" Diff %d:\n", ancestorNumber);
681 RTPrintf(" UUID: %s\n", CSTR(pMediumUuid));
682 RTPrintf(" Location: %s\n\n", CSTR(pMediumPath));
683 }
684 CHECK_ERROR_BREAK(pChild, COMGETTER(Children)(ComSafeArrayAsOutParam(aMediumChildren)));
685 pChild = (aMediumChildren.size()) ? aMediumChildren[0] : NULL;
686 ++ancestorNumber;
687 } while(pChild);
688 }
689 }
690}
691/**
692 * Display all registered VMs on the screen with some information about each
693 *
694 * @param virtualBox VirtualBox instance object.
695 */
696static void
697listVMs(IVirtualBox *pVirtualBox)
698{
699 HRESULT rc = 0;
700 com::SafeIfaceArray<IMachine> pMachines;
701 CHECK_ERROR(pVirtualBox, COMGETTER(Machines)(ComSafeArrayAsOutParam(pMachines)));
702 for (size_t i = 0; i < pMachines.size(); ++i)
703 {
704 ComPtr<IMachine> pMachine = pMachines[i];
705 if (pMachine)
706 {
707 BOOL fAccessible;
708 CHECK_ERROR(pMachines[i], COMGETTER(Accessible)(&fAccessible));
709 if (fAccessible)
710 {
711 Bstr pMachineName;
712 Bstr pMachineUuid;
713 Bstr pDescription;
714 Bstr pMachineLocation;
715
716 CHECK_ERROR(pMachine, COMGETTER(Name)(pMachineName.asOutParam()));
717 CHECK_ERROR(pMachine, COMGETTER(Id)(pMachineUuid.asOutParam()));
718 CHECK_ERROR(pMachine, COMGETTER(Description)(pDescription.asOutParam()));
719 CHECK_ERROR(pMachine, COMGETTER(SettingsFilePath)(pMachineLocation.asOutParam()));
720
721 if ( g_vboxrawOpts.pszVm == NULL
722 || RTStrNCmp(CSTR(pMachineUuid), g_vboxrawOpts.pszVm, MAX_ID_LEN) == 0
723 || RTStrNCmp((const char *)pMachineName.raw(), g_vboxrawOpts.pszVm, MAX_ID_LEN) == 0)
724 {
725 RTPrintf("------------------------------------------------------\n");
726 RTPrintf("VM Name: \"%s\"\n", CSTR(pMachineName));
727 RTPrintf("UUID: %s\n", CSTR(pMachineUuid));
728 if (*pDescription.raw() != '\0')
729 RTPrintf("Description: %s\n", CSTR(pDescription));
730 RTPrintf("Location: %s\n", CSTR(pMachineLocation));
731
732 listMedia(pMachine);
733
734 RTPrintf("\n");
735 }
736 }
737 }
738 }
739}
740
741static void
742searchForBaseImage(IVirtualBox *pVirtualBox, char *pszImageString, ComPtr<IMedium> *pBaseImage)
743{
744 int rc = 0;
745 com::SafeIfaceArray<IMedium> aDisks;
746
747 CHECK_ERROR(pVirtualBox, COMGETTER(HardDisks)(ComSafeArrayAsOutParam(aDisks)));
748 for (size_t i = 0; i < aDisks.size() && aDisks[i]; i++)
749 {
750 if (aDisks[i])
751 {
752 Bstr pMediumUuid;
753 Bstr pMediumName;
754
755 CHECK_ERROR(aDisks[i], COMGETTER(Name)(pMediumName.asOutParam()));
756 CHECK_ERROR(aDisks[i], COMGETTER(Id)(pMediumUuid.asOutParam()));
757
758 if ( RTStrCmp(pszImageString, CSTR(pMediumUuid)) == 0
759 || RTStrCmp(pszImageString, CSTR(pMediumName)) == 0)
760 {
761 *pBaseImage = aDisks[i];
762 return;
763 }
764 }
765 }
766}
767
768int
769main(int argc, char **argv)
770{
771
772 int rc = RTR3InitExe(argc, &argv, 0);
773 if (RT_FAILURE(rc))
774 return RTMsgErrorExitFailure("vboxraw: ERROR: RTR3InitExe failed, rc=%Rrc\n", rc);
775
776 rc = VDInit();
777 if (RT_FAILURE(rc))
778 return RTMsgErrorExitFailure("vboxraw: ERROR: VDInit failed, rc=%Rrc\n", rc);
779
780 memset(&g_vboxrawOps, 0, sizeof(g_vboxrawOps));
781 g_vboxrawOps.open = vboxrawOp_open;
782 g_vboxrawOps.read = vboxrawOp_read;
783 g_vboxrawOps.write = vboxrawOp_write;
784 g_vboxrawOps.getattr = vboxrawOp_getattr;
785 g_vboxrawOps.release = vboxrawOp_release;
786 g_vboxrawOps.readdir = vboxrawOp_readdir;
787 g_vboxrawOps.readlink = vboxrawOp_readlink;
788 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
789
790 memset(&g_vboxrawOpts, 0, sizeof(g_vboxrawOpts));
791
792 rc = fuse_opt_parse(&args, &g_vboxrawOpts, vboxrawOptDefs, vboxrawOptHandler);
793 if (g_vboxrawOpts.fAllowRoot)
794 fuse_opt_add_arg(&args, "-oallow_root");
795
796 if (rc == -1)
797 return RTMsgErrorExitFailure("vboxraw: ERROR: Couldn't parse fuse options, rc=%Rrc\n", rc);
798
799 /*
800 * Initialize COM.
801 */
802 using namespace com;
803 HRESULT hrc = com::Initialize();
804 if (FAILED(hrc))
805 {
806# ifdef VBOX_WITH_XPCOM
807 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
808 {
809 char szHome[RTPATH_MAX] = "";
810 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
811 return RTMsgErrorExit(RTEXITCODE_FAILURE,
812 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
813 }
814# endif
815 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM! (hrc=%Rhrc)", hrc);
816 }
817
818 /*
819 * Get the remote VirtualBox object and create a local session object.
820 */
821 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
822 ComPtr<IVirtualBox> pVirtualBox;
823
824 hrc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
825 if (SUCCEEDED(hrc))
826 hrc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
827 if (FAILED(hrc))
828 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get IVirtualBox object! (hrc=%Rhrc)", hrc);
829
830 if (g_vboxrawOpts.fVerbose)
831 RTPrintf("vboxraw: VirtualBox XPCOM object created\n");
832
833 if (g_vboxrawOpts.fList)
834 {
835 listVMs(pVirtualBox);
836 return 0;
837 }
838
839 if (g_vboxrawOpts.pszImage == NULL)
840 {
841 RTMsgErrorExitFailure("vboxraw: ERROR: "
842 "Must provide at at least --list or --image option\n");
843 return 0;
844 }
845 ComPtr<IMedium> pBaseImageMedium = NULL;
846 char *pszFormat;
847 VDTYPE enmType;
848
849 searchForBaseImage(pVirtualBox, g_vboxrawOpts.pszImage, &pBaseImageMedium);
850 if (pBaseImageMedium == NULL)
851 {
852 /*
853 * Try to locate base image pMedium via the VirtualBox API, given the user-provided path
854 * resolving symlinks back to hard path.
855 */
856 int cbNameMax = pathconf(g_vboxrawOpts.pszImage, _PC_PATH_MAX);
857 if (cbNameMax < 0)
858 return cbNameMax;
859
860 g_pszBaseImagePath = RTStrDup(g_vboxrawOpts.pszImage);
861 if (g_pszBaseImagePath == NULL)
862 return RTMsgErrorExitFailure("vboxraw: out of memory\n");
863
864 if (access(g_pszBaseImagePath, F_OK) < 0)
865 return RTMsgErrorExitFailure("vboxraw: ERROR: "
866 "Virtual disk image not found: \"%s\"\n", g_pszBaseImagePath);
867
868 if (access(g_pszBaseImagePath, R_OK) < 0)
869 return RTMsgErrorExitFailure("vboxraw: ERROR: "
870 "Virtual disk image not readable: \"%s\"\n", g_pszBaseImagePath);
871
872 if ( g_vboxrawOpts.fRW && access(g_vboxrawOpts.pszImage, W_OK) < 0)
873 return RTMsgErrorExitFailure("vboxraw: ERROR: "
874 "Virtual disk image not writeable: \"%s\"\n", g_pszBaseImagePath);
875 rc = RTPathSplit(g_pszBaseImagePath, &g_u.split, sizeof(g_u), 0);
876
877 if (RT_FAILURE(rc))
878 return RTMsgErrorExitFailure("vboxraw: "
879 "RTPathSplit failed on '%s': %Rrc", g_pszBaseImagePath);
880
881 if (!(g_u.split.fProps & RTPATH_PROP_FILENAME))
882 return RTMsgErrorExitFailure("vboxraw: "
883 "RTPATH_PROP_FILENAME not set for: '%s'", g_pszBaseImagePath);
884
885 g_pszBaseImageName = g_u.split.apszComps[g_u.split.cComps - 1];
886 searchForBaseImage(pVirtualBox, g_pszBaseImageName, &pBaseImageMedium);
887
888 if (pBaseImageMedium == NULL)
889 {
890 /*
891 * Can't find the user specified image Medium via the VirtualBox API
892 * Try to 'mount' the image via the user-provided path (without differencing images)
893 * Create VirtualBox disk container and open main image
894 */
895 rc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/,
896 g_pszBaseImagePath, &pszFormat, &enmType);
897 if (RT_FAILURE(rc))
898 return RTMsgErrorExitFailure("vboxraw: ERROR: VDGetFormat(%s,) "
899 "failed, rc=%Rrc\n", g_pszBaseImagePath, rc);
900
901 g_pVDisk = NULL;
902 rc = VDCreate(NULL /* pVDIIfsDisk */, enmType, &g_pVDisk);
903 if (RT_SUCCESS(rc))
904 {
905 rc = VDOpen(g_pVDisk, pszFormat, g_pszBaseImagePath, 0, NULL /* pVDIfsImage */);
906 if (RT_FAILURE(rc))
907 {
908 VDClose(g_pVDisk, false /* fDeletes */);
909 return RTMsgErrorExitFailure("vboxraw: ERROR: VDCreate(,%s,%s,,,) failed,"
910 " rc=%Rrc\n", pszFormat, g_pszBaseImagePath, rc);
911 }
912 }
913 else
914 return RTMsgErrorExitFailure("vboxraw: ERROR: VDCreate failed, rc=%Rrc\n", rc);
915 }
916 }
917
918 if (g_pVDisk == NULL)
919 {
920 com::SafeIfaceArray<IMedium> aMediumChildren;
921 ComPtr<IMedium> pChild = pBaseImageMedium;
922 uint32_t diffNumber = 0; /* diff # 0 = base image */
923 do
924 {
925 Bstr pMediumName;
926 Bstr pMediumPath;
927
928 CHECK_ERROR(pChild, COMGETTER(Name)(pMediumName.asOutParam()));
929 CHECK_ERROR(pChild, COMGETTER(Location)(pMediumPath.asOutParam()));
930
931 if (pChild == pBaseImageMedium)
932 {
933 free((void *)g_pszBaseImageName);
934 g_pszBaseImageName = RTStrDup(CSTR(pMediumName));
935
936 free((void *)g_pszBaseImagePath);
937 g_pszBaseImagePath = RTStrDup(CSTR(pMediumPath));
938 if (g_pszBaseImageName == NULL)
939 return RTMsgErrorExitFailure("vboxraw: out of memory\n");
940
941 if (g_pszBaseImagePath == NULL)
942 return RTMsgErrorExitFailure("vboxraw: out of memory\n");
943 /*
944 * Create HDD container to open base image and differencing images into
945 */
946 rc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/,
947 g_pszBaseImagePath, &pszFormat, &enmType);
948 if (RT_FAILURE(rc))
949 return RTMsgErrorExitFailure("vboxraw: VDGetFormat(,,%s,,) "
950 "failed (during HDD container creation), rc=%Rrc\n", g_pszBaseImagePath, rc);
951 if (g_vboxrawOpts.fVerbose)
952 RTPrintf("vboxraw: Creating container for base image of format %s\n", pszFormat);
953 /** @todo Remove I/O CB's and crit sect. when VDRead()/VDWrite() are made threadsafe */
954 rc = RTCritSectInit(&g_vdioLock);
955 if (RT_SUCCESS(rc))
956 {
957 g_VDIfThreadSync.pfnStartRead = vboxrawThreadStartRead;
958 g_VDIfThreadSync.pfnFinishRead = vboxrawThreadFinishRead;
959 g_VDIfThreadSync.pfnStartWrite = vboxrawThreadStartWrite;
960 g_VDIfThreadSync.pfnFinishWrite = vboxrawThreadFinishWrite;
961 rc = VDInterfaceAdd(&g_VDIfThreadSync.Core, "vboxraw_ThreadSync", VDINTERFACETYPE_THREADSYNC,
962 &g_vdioLock, sizeof(VDINTERFACETHREADSYNC), &g_pVdIfs);
963 }
964 else
965 return RTMsgErrorExitFailure("vboxraw: ERROR: Failed to create critsects "
966 "for virtual disk I/O, rc=%Rrc\n", rc);
967
968 g_pVDisk = NULL;
969 rc = VDCreate(g_pVdIfs, enmType, &g_pVDisk);
970 if (NS_FAILED(rc))
971 return RTMsgErrorExitFailure("vboxraw: ERROR: Couldn't create virtual disk container\n");
972 }
973 /** @todo (end of to do section) */
974
975 if ( g_vboxrawOpts.cHddImageDiffMax != 0 && diffNumber > g_vboxrawOpts.cHddImageDiffMax)
976 break;
977
978 if (g_vboxrawOpts.fVerbose)
979 {
980 if (diffNumber == 0)
981 RTPrintf("\nvboxraw: Opening base image into container:\n %s\n",
982 g_pszBaseImagePath);
983 else
984 RTPrintf("\nvboxraw: Opening difference image #%d into container:\n %s\n",
985 diffNumber, g_pszBaseImagePath);
986 }
987
988 rc = VDOpen(g_pVDisk, pszFormat, g_pszBaseImagePath, 0, NULL /* pVDIfsImage */);
989 if (RT_FAILURE(rc))
990 {
991 VDClose(g_pVDisk, false /* fDeletes */);
992 return RTMsgErrorExitFailure("vboxraw: VDOpen(,,%s,,) failed, rc=%Rrc\n",
993 g_pszBaseImagePath, rc);
994 }
995
996 CHECK_ERROR(pChild, COMGETTER(Children)(ComSafeArrayAsOutParam(aMediumChildren)));
997
998 if (aMediumChildren.size() != 0) {
999 pChild = aMediumChildren[0];
1000 }
1001
1002 aMediumChildren.setNull();
1003
1004 ++diffNumber;
1005
1006
1007 } while(NS_SUCCEEDED(rc) && aMediumChildren.size());
1008 }
1009
1010 g_cReaders = VDIsReadOnly(g_pVDisk) ? INT32_MAX / 2 : 0;
1011 g_cWriters = 0;
1012 g_cbPrimary = VDGetSize(g_pVDisk, 0 /* base */);
1013
1014 /*
1015 * Hand control over to libfuse.
1016 */
1017 if (g_vboxrawOpts.fVerbose)
1018 RTPrintf("\nvboxraw: Going into background...\n");
1019
1020 rc = fuse_main(args.argc, args.argv, &g_vboxrawOps, NULL);
1021
1022 int rc2 = VDClose(g_pVDisk, false /* fDelete */);
1023 AssertRC(rc2);
1024 RTPrintf("vboxraw: fuse_main -> %d\n", rc);
1025 return rc;
1026}
1027
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