VirtualBox

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

Last change on this file since 74886 was 74444, checked in by vboxsync, 6 years ago

ImageMounter/vboxraw: Build fix.

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