VirtualBox

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

Last change on this file since 75309 was 75266, checked in by vboxsync, 6 years ago

Modified to use com:: interfaces. Works on Linux and MacOS now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.3 KB
Line 
1/* $Id: vboxraw.cpp 75266 2018-11-06 06:15:26Z 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#define UNUSED(x) (void)(x)
24
25#define FUSE_USE_VERSION 27
26#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined(RT_OS_FEEBSD)
27# define UNIX_DERIVATIVE
28#endif
29#define MAX_READERS (INT32_MAX / 32)
30
31#include <fuse.h>
32#ifdef UNIX_DERIVATIVE
33#include <errno.h>
34#include <fcntl.h>
35#include <stdlib.h>
36#include <libgen.h>
37#include <unistd.h>
38#include <sys/stat.h>
39#endif
40#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX)
41# include <sys/param.h>
42# undef PVM /* Blasted old BSD mess still hanging around darwin. */
43#endif
44#ifdef RT_OS_LINUX
45# include <linux/fs.h>
46# include <linux/hdreg.h>
47#endif
48#include <VirtualBox_XPCOM.h>
49#include <VBox/com/VirtualBox.h>
50#include <VBox/vd.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/critsect.h>
63#include <iprt/assert.h>
64#include <iprt/message.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
73using namespace com;
74
75enum {
76 USAGE_FLAG,
77};
78
79/* For getting the basename of the image path */
80union
81{
82 RTPATHSPLIT split;
83 uint8_t abPad[RTPATH_MAX + 1024];
84} g_u;
85
86
87/** The VDRead/VDWrite block granularity. */
88#define VBoxRAW_MIN_SIZE 512
89/** Offset mask corresponding to VBoxRAW_MIN_SIZE. */
90#define VBoxRAW_MIN_SIZE_MASK_OFF (0x1ff)
91/** Block mask corresponding to VBoxRAW_MIN_SIZE. */
92#define VBoxRAW_MIN_SIZE_MASK_BLK (~UINT64_C(0x1ff))
93
94#define PADMAX 50
95#define MAX_ID_LEN 256
96#define CSTR(arg) Utf8Str(arg).c_str()
97
98static struct fuse_operations g_vboxrawOps;
99PVDISK g_pVDisk;
100int32_t g_cReaders;
101int32_t g_cWriters;
102RTFOFF g_cbPrimary;
103char *g_pszBaseImageName;
104char *g_pszBaseImagePath;
105
106char *nsIDToString(nsID *guid);
107void printErrorInfo();
108/** XPCOM stuff */
109
110static struct vboxrawOpts {
111 char *pszVm;
112 char *pszImage;
113 char *pszImageUuid;
114 uint32_t cHddImageDiffMax;
115 uint32_t fList;
116 uint32_t fAllowRoot;
117 uint32_t fRW;
118 uint32_t fVerbose;
119} g_vboxrawOpts;
120
121#define OPTION(fmt, pos, val) { fmt, offsetof(struct vboxrawOpts, pos), val }
122
123static struct fuse_opt vboxrawOptDefs[] = {
124 OPTION("--list", fList, 1),
125 OPTION("--root", fAllowRoot, 1),
126 OPTION("--vm=%s", pszVm, 0),
127 OPTION("--maxdiff=%d", cHddImageDiffMax, 1),
128 OPTION("--diff=%d", cHddImageDiffMax, 1),
129 OPTION("--image=%s", pszImage, 0),
130 OPTION("--rw", fRW, 1),
131 OPTION("--verbose", fVerbose, 1),
132 FUSE_OPT_KEY("-h", USAGE_FLAG),
133 FUSE_OPT_KEY("--help", USAGE_FLAG),
134 FUSE_OPT_END
135};
136
137static int vboxrawOptHandler(void *data, const char *arg, int optKey, struct fuse_args *outargs)
138{
139 (void) data;
140 (void) arg;
141
142 char *progname = basename(outargs->argv[0]);
143 switch(optKey)
144 {
145 case USAGE_FLAG:
146 RTPrintf("usage: %s [options] <mountpoint>\n\n"
147 "%s options:\n"
148 " [--list] List media\n"
149 " [--root] Same as -o allow_root\n"
150 " [--rw] writeable (default = readonly)\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 UNIX_DERIVATIVE
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 UNIX_DERIVATIVE
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
262static int retryableVDRead(PVDISK pvDisk, uint64_t offset, void *pvBuf, size_t cbRead)
263{
264 int rc = -1;
265 int cRetry = 5;
266 do
267 {
268 if (cRetry < 5)
269 Log(("(rc=%d retrying read)\n", rc));
270 rc = VDRead(pvDisk, offset, pvBuf, cbRead);
271 } while (RT_FAILURE(rc) && --cRetry);
272 return rc;
273}
274
275/** @copydoc fuse_operations::read */
276static int vboxrawOp_read(const char *pszPath, char *pbBuf, size_t cbBuf,
277 off_t offset, struct fuse_file_info *pInfo)
278{
279
280 (void) pszPath;
281 (void) pInfo;
282
283 LogFlowFunc(("my offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
284
285 /* paranoia */
286 AssertReturn((int)cbBuf >= 0, -EINVAL);
287 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
288 AssertReturn(offset >= 0, -EINVAL);
289 AssertReturn((off_t)(offset + cbBuf) >= offset, -EINVAL);
290
291 int rc;
292 if ((off_t)(offset + cbBuf) < offset)
293 rc = -EINVAL;
294 else if (offset >= g_cbPrimary)
295 rc = 0;
296 else if (!cbBuf)
297 rc = 0;
298 else
299 {
300 /* Adjust for EOF. */
301 if ((off_t)(offset + cbBuf) >= g_cbPrimary)
302 cbBuf = g_cbPrimary - offset;
303
304 /*
305 * Aligned read?
306 */
307 int rc2;
308 if ( !(offset & VBoxRAW_MIN_SIZE_MASK_OFF)
309 && !(cbBuf & VBoxRAW_MIN_SIZE_MASK_OFF))
310 {
311 rc2 = retryableVDRead(g_pVDisk, offset, pbBuf, cbBuf);
312 if (RT_FAILURE(rc2))
313 {
314 rc = -RTErrConvertToErrno(rc2);
315 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
316 return rc;
317 }
318 }
319 else
320 {
321 /*
322 * Unaligned read - lots of extra work.
323 */
324 uint8_t abBlock[VBoxRAW_MIN_SIZE];
325 if (((offset + cbBuf) & VBoxRAW_MIN_SIZE_MASK_BLK) == (offset & VBoxRAW_MIN_SIZE_MASK_BLK))
326 {
327 /* a single partial block. */
328 rc2 = retryableVDRead(g_pVDisk, offset & VBoxRAW_MIN_SIZE_MASK_BLK, abBlock, VBoxRAW_MIN_SIZE);
329 if (RT_SUCCESS(rc2))
330 {
331 memcpy(pbBuf, &abBlock[offset & VBoxRAW_MIN_SIZE_MASK_OFF], cbBuf);
332 }
333 else
334 {
335 rc = -RTErrConvertToErrno(rc2);
336 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
337 return rc;
338 }
339 }
340 else
341 {
342 /* read unaligned head. */
343 rc2 = VINF_SUCCESS;
344 if (offset & VBoxRAW_MIN_SIZE_MASK_OFF)
345 {
346 rc2 = retryableVDRead(g_pVDisk, offset & VBoxRAW_MIN_SIZE_MASK_BLK, abBlock, VBoxRAW_MIN_SIZE);
347 if (RT_SUCCESS(rc2))
348 {
349 size_t cbCopy = VBoxRAW_MIN_SIZE - (offset & VBoxRAW_MIN_SIZE_MASK_OFF);
350 memcpy(pbBuf, &abBlock[offset & VBoxRAW_MIN_SIZE_MASK_OFF], cbCopy);
351 pbBuf += cbCopy;
352 offset += cbCopy;
353 cbBuf -= cbCopy;
354 }
355 else
356 {
357 rc = -RTErrConvertToErrno(rc2);
358 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
359 return rc;
360 }
361 }
362
363 /* read the middle. */
364 Assert(!(offset & VBoxRAW_MIN_SIZE_MASK_OFF));
365 if (cbBuf >= VBoxRAW_MIN_SIZE && RT_SUCCESS(rc2))
366 {
367 size_t cbRead = cbBuf & VBoxRAW_MIN_SIZE_MASK_BLK;
368 rc2 = retryableVDRead(g_pVDisk, offset, pbBuf, cbRead);
369 if (RT_SUCCESS(rc2))
370 {
371 pbBuf += cbRead;
372 offset += cbRead;
373 cbBuf -= cbRead;
374 }
375 else
376 {
377 rc = -RTErrConvertToErrno(rc2);
378 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
379 return rc;
380 }
381 }
382
383 /* unaligned tail read. */
384 Assert(cbBuf < VBoxRAW_MIN_SIZE);
385 Assert(!(offset & VBoxRAW_MIN_SIZE_MASK_OFF));
386 if (cbBuf && RT_SUCCESS(rc2))
387 {
388 rc2 = retryableVDRead(g_pVDisk, offset, abBlock, VBoxRAW_MIN_SIZE);
389 if (RT_SUCCESS(rc2)) {
390 memcpy(pbBuf, &abBlock[0], cbBuf);
391 }
392 else
393 {
394 rc = -RTErrConvertToErrno(rc2);
395 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
396 return rc;
397 }
398 }
399 }
400 }
401
402 /* convert the return code */
403 if (RT_SUCCESS(rc2))
404 rc = cbBuf;
405 else
406 {
407 rc = -RTErrConvertToErrno(rc2);
408 LogFlowFunc(("Error rc2=%d, rc=%d=%s\n", rc2, rc, strerror(rc)));
409 }
410 return rc;
411 }
412 return rc;
413}
414
415/** @copydoc fuse_operations::write */
416static int vboxrawOp_write(const char *pszPath, const char *pbBuf, size_t cbBuf,
417 off_t offset, struct fuse_file_info *pInfo)
418{
419
420 (void) pszPath;
421 (void) pInfo;
422
423
424 AssertReturn((int)cbBuf >= 0, -EINVAL);
425 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
426 AssertReturn(offset >= 0, -EINVAL);
427 AssertReturn((off_t)(offset + cbBuf) >= offset, -EINVAL);
428
429
430 LogFlowFunc(("offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
431
432 int rc;
433 if (!g_vboxrawOpts.fRW)
434 rc = -EPERM;
435 else if ((off_t)(offset + cbBuf) < offset)
436 rc = -EINVAL;
437 else if (offset >= g_cbPrimary)
438 rc = 0;
439 else if (!cbBuf)
440 rc = 0;
441 else
442 {
443 /* Adjust for EOF. */
444 if ((off_t)(offset + cbBuf) >= g_cbPrimary)
445 cbBuf = g_cbPrimary - offset;
446
447 /*
448 * Aligned write?
449 */
450 int rc2;
451 if ( !(offset & VBoxRAW_MIN_SIZE_MASK_OFF)
452 && !(cbBuf & VBoxRAW_MIN_SIZE_MASK_OFF))
453 {
454 rc2 = VDWrite(g_pVDisk, offset, pbBuf, cbBuf);
455 if (RT_FAILURE(rc2))
456 {
457 rc = -RTErrConvertToErrno(rc2);
458 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
459 return rc;
460 }
461 }
462 else
463 {
464 /*
465 * Unaligned write - lots of extra work.
466 */
467 uint8_t abBlock[VBoxRAW_MIN_SIZE];
468 if (((offset + cbBuf) & VBoxRAW_MIN_SIZE_MASK_BLK) == (offset & VBoxRAW_MIN_SIZE_MASK_BLK))
469 {
470 /* a single partial block. */
471 rc2 = VDRead(g_pVDisk, offset & VBoxRAW_MIN_SIZE_MASK_BLK, abBlock, VBoxRAW_MIN_SIZE);
472 if (RT_SUCCESS(rc2))
473 {
474 memcpy(&abBlock[offset & VBoxRAW_MIN_SIZE_MASK_OFF], pbBuf, cbBuf);
475 /* Update the block */
476 rc2 = VDWrite(g_pVDisk, offset & VBoxRAW_MIN_SIZE_MASK_BLK, abBlock, VBoxRAW_MIN_SIZE);
477 if (RT_FAILURE(rc2))
478 {
479 rc = -RTErrConvertToErrno(rc2);
480 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
481 return rc;
482 }
483 }
484 else
485 {
486 rc = -RTErrConvertToErrno(rc2);
487 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
488 return rc;
489 }
490 }
491 else
492 {
493 /* read unaligned head. */
494 rc2 = VINF_SUCCESS;
495 if (offset & VBoxRAW_MIN_SIZE_MASK_OFF)
496 {
497 rc2 = VDRead(g_pVDisk, offset & VBoxRAW_MIN_SIZE_MASK_BLK, abBlock, VBoxRAW_MIN_SIZE);
498 if (RT_SUCCESS(rc2))
499 {
500 size_t cbCopy = VBoxRAW_MIN_SIZE - (offset & VBoxRAW_MIN_SIZE_MASK_OFF);
501 memcpy(&abBlock[offset & VBoxRAW_MIN_SIZE_MASK_OFF], pbBuf, cbCopy);
502 pbBuf += cbCopy;
503 offset += cbCopy;
504 cbBuf -= cbCopy;
505 rc2 = VDWrite(g_pVDisk, offset & VBoxRAW_MIN_SIZE_MASK_BLK, abBlock, VBoxRAW_MIN_SIZE);
506 if (RT_FAILURE(rc2))
507 {
508 rc = -RTErrConvertToErrno(rc2);
509 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
510 return rc;
511 }
512 }
513 else
514 {
515 rc = -RTErrConvertToErrno(rc2);
516 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
517 return rc;
518 }
519 }
520
521 /* write the middle. */
522 Assert(!(offset & VBoxRAW_MIN_SIZE_MASK_OFF));
523 if (cbBuf >= VBoxRAW_MIN_SIZE && RT_SUCCESS(rc2))
524 {
525 size_t cbWrite = cbBuf & VBoxRAW_MIN_SIZE_MASK_BLK;
526 rc2 = VDWrite(g_pVDisk, offset, pbBuf, cbWrite);
527 if (RT_SUCCESS(rc2))
528 {
529 pbBuf += cbWrite;
530 offset += cbWrite;
531 cbBuf -= cbWrite;
532 }
533 if (RT_FAILURE(rc2))
534 {
535 rc = -RTErrConvertToErrno(rc2);
536 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
537 return rc;
538 }
539 }
540
541 /* unaligned tail write. */
542 Assert(cbBuf < VBoxRAW_MIN_SIZE);
543 Assert(!(offset & VBoxRAW_MIN_SIZE_MASK_OFF));
544 if (cbBuf && RT_SUCCESS(rc2))
545 {
546 rc2 = VDRead(g_pVDisk, offset, abBlock, VBoxRAW_MIN_SIZE);
547 if (RT_SUCCESS(rc2))
548 {
549 memcpy(&abBlock[0], pbBuf, cbBuf);
550 rc2 = VDWrite(g_pVDisk, offset, abBlock, VBoxRAW_MIN_SIZE);
551 if (RT_FAILURE(rc2))
552 {
553 rc = -RTErrConvertToErrno(rc2);
554 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
555 return rc;
556 }
557 }
558 else
559 {
560 rc = -RTErrConvertToErrno(rc2);
561 LogFlowFunc(("error: rc2=%d, rc=%d, %s\n", rc2, rc, strerror(rc)));
562 return rc;
563 }
564 }
565 }
566 }
567
568 /* convert the return code */
569 if (RT_SUCCESS(rc2))
570 rc = cbBuf;
571 else
572 {
573 rc = -RTErrConvertToErrno(rc2);
574 LogFlowFunc(("Error rc2=%d, rc=%d=%s\n", rc2, rc, strerror(rc)));
575 return rc;
576 }
577 }
578 return rc;
579}
580
581/** @copydoc fuse_operations::getattr */
582static int
583vboxrawOp_getattr(const char *pszPath, struct stat *stbuf)
584{
585 int rc = 0;
586
587 LogFlowFunc(("pszPath=%s, stat(\"%s\")\n", pszPath, g_pszBaseImagePath));
588
589 memset(stbuf, 0, sizeof(struct stat));
590
591 if (RTStrCmp(pszPath, "/") == 0)
592 {
593 stbuf->st_mode = S_IFDIR | 0755;
594 stbuf->st_nlink = 2;
595 }
596 else if (RTStrCmp(pszPath + 1, "vhdd") == 0)
597 {
598 rc = stat(g_pszBaseImagePath, stbuf);
599 if (rc < 0)
600 return rc;
601
602 stbuf->st_size = VDGetSize(g_pVDisk, 0);
603 stbuf->st_nlink = 1;
604 }
605 else if (RTStrCmp(pszPath + 1, g_pszBaseImageName) == 0)
606 {
607 rc = stat(g_pszBaseImagePath, stbuf);
608 if (rc < 0)
609 return rc;
610 stbuf->st_size = 0;
611 stbuf->st_mode = S_IFLNK | 0444;
612 stbuf->st_nlink = 1;
613 stbuf->st_uid = 0;
614 stbuf->st_gid = 0;
615 } else
616 rc = -ENOENT;
617
618 return rc;
619}
620
621/** @copydoc fuse_operations::readdir */
622static int
623vboxrawOp_readdir(const char *pszPath, void *pvBuf, fuse_fill_dir_t pfnFiller,
624 off_t offset, struct fuse_file_info *pInfo)
625
626{
627
628 (void) offset;
629 (void) pInfo;
630
631 if (RTStrCmp(pszPath, "/") != 0)
632 return -ENOENT;
633
634 /*
635 * The usual suspects (mandatory)
636 */
637 pfnFiller(pvBuf, ".", NULL, 0);
638 pfnFiller(pvBuf, "..", NULL, 0);
639
640 /*
641 * Create one entry w/basename of original image that getattr() will
642 * depict as a symbolic link pointing back to the original file,
643 * as a convenience, so anyone who lists the FUSE file system can
644 * easily find the associated vdisk.
645 */
646 pfnFiller(pvBuf, g_pszBaseImageName, NULL, 0);
647
648 /*
649 * Create entry named "vhdd", which getattr() will describe as a
650 * regular file, and thus will go through the open/release/read/write vectors
651 * to access the VirtualBox image as processed by the IRPT VD API.
652 */
653 pfnFiller(pvBuf, "vhdd", NULL, 0);
654 return 0;
655}
656
657/** @copydoc fuse_operations::readlink */
658static int
659vboxrawOp_readlink(const char *pszPath, char *buf, size_t size)
660{
661 (void) pszPath;
662 RTStrCopy(buf, size, g_pszBaseImagePath);
663 return 0;
664}
665
666static void
667listMedia(IMachine *pMachine)
668{
669 int rc = 0;
670 com::SafeIfaceArray<IMediumAttachment> pMediumAttachments;
671
672 CHECK_ERROR(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(pMediumAttachments)));
673 for (size_t i = 0; i < pMediumAttachments.size(); i++)
674 {
675 ComPtr<IMedium> pMedium;
676 DeviceType_T deviceType;
677 Bstr pMediumUuid;
678 Bstr pMediumName;
679 Bstr pMediumPath;
680
681 CHECK_ERROR(pMediumAttachments[i], COMGETTER(Type)(&deviceType));
682
683 if (deviceType == DeviceType_HardDisk)
684 {
685 CHECK_ERROR(pMediumAttachments[i], COMGETTER(Medium)(pMedium.asOutParam()));
686 if (pMedium.isNull())
687 return;
688
689 MediumState_T state;
690 CHECK_ERROR(pMedium, COMGETTER(State)(&state));
691 if (FAILED(rc))
692 return;
693 if (state == MediumState_Inaccessible)
694 {
695 CHECK_ERROR(pMedium, RefreshState(&state));
696 if (FAILED(rc))
697 return;
698 }
699
700 ComPtr<IMedium> pEarliestAncestor;
701 CHECK_ERROR(pMedium, COMGETTER(Base)(pEarliestAncestor.asOutParam()));
702 ComPtr<IMedium> pChild = pEarliestAncestor;
703 uint32_t ancestorNumber = 0;
704 if (pEarliestAncestor.isNull())
705 return;
706 RTPrintf("\n");
707 do
708 {
709 com::SafeIfaceArray<IMedium> aMediumChildren;
710 CHECK_ERROR(pChild, COMGETTER(Name)(pMediumName.asOutParam()));
711 CHECK_ERROR(pChild, COMGETTER(Id)(pMediumUuid.asOutParam()));
712 CHECK_ERROR(pChild, COMGETTER(Location)(pMediumPath.asOutParam()));
713
714 if (ancestorNumber == 0)
715 {
716 RTPrintf(" -----------------------\n");
717 RTPrintf(" HDD base: \"%s\"\n", CSTR(pMediumName));
718 RTPrintf(" UUID: %s\n", CSTR(pMediumUuid));
719 RTPrintf(" Location: %s\n\n", CSTR(pMediumPath));
720 }
721 else
722 {
723 RTPrintf(" Diff %d:\n", ancestorNumber);
724 RTPrintf(" UUID: %s\n", CSTR(pMediumUuid));
725 RTPrintf(" Location: %s\n\n", CSTR(pMediumPath));
726 }
727 CHECK_ERROR_BREAK(pChild, COMGETTER(Children)(ComSafeArrayAsOutParam(aMediumChildren)));
728 pChild = (aMediumChildren.size()) ? aMediumChildren[0] : NULL;
729 ++ancestorNumber;
730 } while(pChild);
731 }
732 }
733}
734/**
735 * Display all registered VMs on the screen with some information about each
736 *
737 * @param virtualBox VirtualBox instance object.
738 */
739static void
740listVMs(IVirtualBox *pVirtualBox)
741{
742 HRESULT rc = 0;
743 com::SafeIfaceArray<IMachine> pMachines;
744 CHECK_ERROR(pVirtualBox, COMGETTER(Machines)(ComSafeArrayAsOutParam(pMachines)));
745 for (size_t i = 0; i < pMachines.size(); ++i)
746 {
747 ComPtr<IMachine> pMachine = pMachines[i];
748 if (pMachine)
749 {
750 BOOL fAccessible;
751 CHECK_ERROR(pMachines[i], COMGETTER(Accessible)(&fAccessible));
752 if (fAccessible)
753 {
754 Bstr pMachineName;
755 Bstr pMachineUuid;
756 Bstr pDescription;
757 Bstr pMachineLocation;
758
759 CHECK_ERROR(pMachine, COMGETTER(Name)(pMachineName.asOutParam()));
760 CHECK_ERROR(pMachine, COMGETTER(Id)(pMachineUuid.asOutParam()));
761 CHECK_ERROR(pMachine, COMGETTER(Description)(pDescription.asOutParam()));
762 CHECK_ERROR(pMachine, COMGETTER(SettingsFilePath)(pMachineLocation.asOutParam()));
763
764 if ( g_vboxrawOpts.pszVm == NULL
765 || RTStrNCmp(CSTR(pMachineUuid), g_vboxrawOpts.pszVm, MAX_ID_LEN) == 0
766 || RTStrNCmp((const char *)pMachineName.raw(), g_vboxrawOpts.pszVm, MAX_ID_LEN) == 0)
767 {
768 RTPrintf("------------------------------------------------------\n");
769 RTPrintf("VM Name: \"%s\"\n", CSTR(pMachineName));
770 RTPrintf("UUID: %s\n", CSTR(pMachineUuid));
771 if (*pDescription.raw() != '\0')
772 RTPrintf("Description: %s\n", CSTR(pDescription));
773 RTPrintf("Location: %s\n", CSTR(pMachineLocation));
774
775 listMedia(pMachine);
776
777 RTPrintf("\n");
778 }
779 }
780 }
781 }
782}
783
784static void
785searchForBaseImage(IVirtualBox *pVirtualBox, char *pszImageString, ComPtr<IMedium> *pBaseImage)
786{
787 int rc = 0;
788 com::SafeIfaceArray<IMedium> aDisks;
789
790 CHECK_ERROR(pVirtualBox, COMGETTER(HardDisks)(ComSafeArrayAsOutParam(aDisks)));
791 for (size_t i = 0; i < aDisks.size() && aDisks[i]; i++)
792 {
793 if (aDisks[i])
794 {
795 Bstr pMediumUuid;
796 Bstr pMediumName;
797
798 CHECK_ERROR(aDisks[i], COMGETTER(Name)(pMediumName.asOutParam()));
799 CHECK_ERROR(aDisks[i], COMGETTER(Id)(pMediumUuid.asOutParam()));
800
801 if ( RTStrCmp(pszImageString, CSTR(pMediumUuid)) == 0
802 || RTStrCmp(pszImageString, CSTR(pMediumName)) == 0)
803 {
804 *pBaseImage = aDisks[i];
805 return;
806 }
807 }
808 }
809}
810
811int
812main(int argc, char **argv)
813{
814
815 int rc = RTR3InitExe(argc, &argv, 0);
816 if (RT_FAILURE(rc))
817 return RTMsgErrorExitFailure("vboxraw: ERROR: RTR3InitExe failed, rc=%Rrc\n", rc);
818
819 rc = VDInit();
820 if (RT_FAILURE(rc))
821 return RTMsgErrorExitFailure("vboxraw: ERROR: VDInit failed, rc=%Rrc\n", rc);
822
823 memset(&g_vboxrawOps, 0, sizeof(g_vboxrawOps));
824 g_vboxrawOps.open = vboxrawOp_open;
825 g_vboxrawOps.read = vboxrawOp_read;
826 g_vboxrawOps.write = vboxrawOp_write;
827 g_vboxrawOps.getattr = vboxrawOp_getattr;
828 g_vboxrawOps.release = vboxrawOp_release;
829 g_vboxrawOps.readdir = vboxrawOp_readdir;
830 g_vboxrawOps.readlink = vboxrawOp_readlink;
831 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
832
833 memset(&g_vboxrawOpts, 0, sizeof(g_vboxrawOpts));
834
835 rc = fuse_opt_parse(&args, &g_vboxrawOpts, vboxrawOptDefs, vboxrawOptHandler);
836 if (g_vboxrawOpts.fAllowRoot)
837 fuse_opt_add_arg(&args, "-oallow_root");
838
839 if (rc == -1)
840 return RTMsgErrorExitFailure("vboxraw: ERROR: Couldn't parse fuse options, rc=%Rrc\n", rc);
841
842 /*
843 * Initialize COM.
844 */
845 using namespace com;
846 HRESULT hrc = com::Initialize();
847 if (FAILED(hrc))
848 {
849# ifdef VBOX_WITH_XPCOM
850 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
851 {
852 char szHome[RTPATH_MAX] = "";
853 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
854 return RTMsgErrorExit(RTEXITCODE_FAILURE,
855 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
856 }
857# endif
858 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM! (hrc=%Rhrc)", hrc);
859 }
860
861 /*
862 * Get the remote VirtualBox object and create a local session object.
863 */
864 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
865 ComPtr<IVirtualBox> pVirtualBox;
866
867 hrc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
868 if (SUCCEEDED(hrc))
869 hrc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
870 if (FAILED(hrc))
871 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get IVirtualBox object! (hrc=%Rhrc)", hrc);
872
873 if (g_vboxrawOpts.fVerbose)
874 RTPrintf("vboxraw: VirtualBox XPCOM object created\n");
875
876 if (g_vboxrawOpts.fList)
877 {
878 listVMs(pVirtualBox);
879 return 0;
880 }
881
882 if (g_vboxrawOpts.pszImage == NULL)
883 {
884 RTMsgErrorExitFailure("vboxraw: ERROR: "
885 "Must provide at at least --list or --image option\n");
886 return 0;
887 }
888 ComPtr<IMedium> pBaseImageMedium = NULL;
889 char *pszFormat;
890 VDTYPE enmType;
891
892 searchForBaseImage(pVirtualBox, g_vboxrawOpts.pszImage, &pBaseImageMedium);
893 if (pBaseImageMedium == NULL)
894 {
895 /*
896 * Try to locate base image pMedium via the VirtualBox API, given the user-provided path
897 * resolving symlinks back to hard path.
898 */
899 int cbNameMax = pathconf(g_vboxrawOpts.pszImage, _PC_PATH_MAX);
900 if (cbNameMax < 0)
901 return cbNameMax;
902
903 g_pszBaseImagePath = RTStrDup(g_vboxrawOpts.pszImage);
904 if (g_pszBaseImagePath == NULL)
905 return RTMsgErrorExitFailure("vboxraw: out of memory\n");
906
907 if (access(g_pszBaseImagePath, F_OK) < 0)
908 return RTMsgErrorExitFailure("vboxraw: ERROR: "
909 "Virtual disk image not found: \"%s\"\n", g_pszBaseImagePath);
910
911 if (access(g_pszBaseImagePath, R_OK) < 0)
912 return RTMsgErrorExitFailure("vboxraw: ERROR: "
913 "Virtual disk image not readable: \"%s\"\n", g_pszBaseImagePath);
914
915 if ( g_vboxrawOpts.fRW && access(g_vboxrawOpts.pszImage, W_OK) < 0)
916 return RTMsgErrorExitFailure("vboxraw: ERROR: "
917 "Virtual disk image not writeable: \"%s\"\n", g_pszBaseImagePath);
918 rc = RTPathSplit(g_pszBaseImagePath, &g_u.split, sizeof(g_u), 0);
919
920 if (RT_FAILURE(rc))
921 return RTMsgErrorExitFailure("vboxraw: "
922 "RTPathSplit failed on '%s': %Rrc", g_pszBaseImagePath);
923
924 if (!(g_u.split.fProps & RTPATH_PROP_FILENAME))
925 return RTMsgErrorExitFailure("vboxraw: "
926 "RTPATH_PROP_FILENAME not set for: '%s'", g_pszBaseImagePath);
927
928 g_pszBaseImageName = g_u.split.apszComps[g_u.split.cComps - 1];
929 searchForBaseImage(pVirtualBox, g_pszBaseImageName, &pBaseImageMedium);
930
931 if (pBaseImageMedium == NULL)
932 {
933 /*
934 * Can't find the user specified image Medium via the VirtualBox API
935 * Try to 'mount' the image via the user-provided path (without differencing images)
936 * Create VirtualBox disk container and open main image
937 */
938 rc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/,
939 g_pszBaseImagePath, &pszFormat, &enmType);
940 if (RT_FAILURE(rc))
941 return RTMsgErrorExitFailure("vboxraw: ERROR: VDGetFormat(%s,) "
942 "failed, rc=%Rrc\n", g_pszBaseImagePath, rc);
943
944 g_pVDisk = NULL;
945 rc = VDCreate(NULL /* pVDIIfsDisk */, enmType, &g_pVDisk);
946 if (RT_SUCCESS(rc))
947 {
948 rc = VDOpen(g_pVDisk, pszFormat, g_pszBaseImagePath, 0, NULL /* pVDIfsImage */);
949 if (RT_FAILURE(rc))
950 {
951 VDClose(g_pVDisk, false /* fDeletes */);
952 return RTMsgErrorExitFailure("vboxraw: ERROR: VDCreate(,%s,%s,,,) failed,"
953 " rc=%Rrc\n", pszFormat, g_pszBaseImagePath, rc);
954 }
955 }
956 else
957 return RTMsgErrorExitFailure("vboxraw: ERROR: VDCreate failed, rc=%Rrc\n", rc);
958 }
959 }
960
961 if (g_pVDisk == NULL)
962 {
963 com::SafeIfaceArray<IMedium> aMediumChildren;
964 ComPtr<IMedium> pChild = pBaseImageMedium;
965 uint32_t diffNumber = 0; /* diff # 0 = base image */
966 do
967 {
968 Bstr pMediumName;
969 Bstr pMediumPath;
970
971 CHECK_ERROR(pChild, COMGETTER(Name)(pMediumName.asOutParam()));
972 CHECK_ERROR(pChild, COMGETTER(Location)(pMediumPath.asOutParam()));
973
974 if (pChild == pBaseImageMedium)
975 {
976 free((void *)g_pszBaseImageName);
977 g_pszBaseImageName = RTStrDup(CSTR(pMediumName));
978
979 free((void *)g_pszBaseImagePath);
980 g_pszBaseImagePath = RTStrDup(CSTR(pMediumPath));
981 if (g_pszBaseImageName == NULL)
982 return RTMsgErrorExitFailure("vboxraw: out of memory\n");
983
984 if (g_pszBaseImagePath == NULL)
985 return RTMsgErrorExitFailure("vboxraw: out of memory\n");
986 /*
987 * Create HDD container to open base image and differencing images into
988 */
989 rc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/,
990 g_pszBaseImagePath, &pszFormat, &enmType);
991 if (RT_FAILURE(rc))
992 return RTMsgErrorExitFailure("vboxraw: VDGetFormat(,,%s,,) "
993 "failed (during HDD container creation), rc=%Rrc\n", g_pszBaseImagePath, rc);
994 if (g_vboxrawOpts.fVerbose)
995 RTPrintf("vboxraw: Creating container for base image of format %s\n", pszFormat);
996
997 g_pVDisk = NULL;
998 rc = VDCreate(NULL /* pVDIIfsDisk */, enmType, &g_pVDisk);
999 if (NS_FAILED(rc))
1000 return RTMsgErrorExitFailure("vboxraw: ERROR: Couldn't create virtual disk container\n");
1001 }
1002 if ( g_vboxrawOpts.cHddImageDiffMax != 0 && diffNumber > g_vboxrawOpts.cHddImageDiffMax)
1003 break;
1004
1005 if (g_vboxrawOpts.fVerbose)
1006 {
1007 if (diffNumber == 0)
1008 RTPrintf("\nvboxraw: Opening base image into container:\n %s\n",
1009 g_pszBaseImagePath);
1010 else
1011 RTPrintf("\nvboxraw: Opening difference image #%d into container:\n %s\n",
1012 diffNumber, g_pszBaseImagePath);
1013 }
1014
1015 rc = VDOpen(g_pVDisk, pszFormat, g_pszBaseImagePath, 0, NULL /* pVDIfsImage */);
1016 if (RT_FAILURE(rc))
1017 {
1018 VDClose(g_pVDisk, false /* fDeletes */);
1019 return RTMsgErrorExitFailure("vboxraw: VDOpen(,,%s,,) failed, rc=%Rrc\n",
1020 g_pszBaseImagePath, rc);
1021 }
1022
1023 CHECK_ERROR(pChild, COMGETTER(Children)(ComSafeArrayAsOutParam(aMediumChildren)));
1024
1025 if (aMediumChildren.size() != 0) {
1026 pChild = aMediumChildren[0];
1027 }
1028
1029 aMediumChildren.setNull();
1030
1031 ++diffNumber;
1032
1033
1034 } while(NS_SUCCEEDED(rc) && aMediumChildren.size());
1035 }
1036
1037 g_cReaders = VDIsReadOnly(g_pVDisk) ? INT32_MAX / 2 : 0;
1038 g_cWriters = 0;
1039 g_cbPrimary = VDGetSize(g_pVDisk, 0 /* base */);
1040
1041 /*
1042 * Hand control over to libfuse.
1043 */
1044 if (g_vboxrawOpts.fVerbose)
1045 RTPrintf("\nvboxraw: Going into background...\n");
1046
1047 rc = fuse_main(args.argc, args.argv, &g_vboxrawOps, NULL);
1048
1049 int rc2 = VDClose(g_pVDisk, false /* fDelete */);
1050 AssertRC(rc2);
1051 RTPrintf("vboxraw: fuse_main -> %d\n", rc);
1052 return rc;
1053}
1054
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette