diff options
Diffstat (limited to 'src/VBox/ImageMounter')
-rw-r--r-- | src/VBox/ImageMounter/Makefile.kmk | 36 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/.scm-settings | 30 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/Makefile.kmk | 55 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h | 323 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/fuse-calls.h | 67 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/fuse.cpp | 35 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/fuse.h | 185 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp | 1414 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/vboximgCrypto.cpp | 422 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/vboximgCrypto.h | 265 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/vboximgMedia.cpp | 409 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/vboximgMedia.h | 50 | ||||
-rw-r--r-- | src/VBox/ImageMounter/vboximg-mount/vboximgOpts.h | 54 |
13 files changed, 3345 insertions, 0 deletions
diff --git a/src/VBox/ImageMounter/Makefile.kmk b/src/VBox/ImageMounter/Makefile.kmk new file mode 100644 index 00000000..b1a15b20 --- /dev/null +++ b/src/VBox/ImageMounter/Makefile.kmk @@ -0,0 +1,36 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the image mounting utilities. +# + +# +# Copyright (C) 2018-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# SPDX-License-Identifier: GPL-3.0-only +# + +SUB_DEPTH = ../../.. +include $(KBUILD_PATH)/subheader.kmk + +if defined(VBOX_WITH_VBOXIMGMOUNT) && "$(intersects $(KBUILD_TARGET_ARCH), $(VBOX_SUPPORTED_HOST_ARCHS))" != "" + include $(PATH_SUB_CURRENT)/vboximg-mount/Makefile.kmk +endif + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/ImageMounter/vboximg-mount/.scm-settings b/src/VBox/ImageMounter/vboximg-mount/.scm-settings new file mode 100644 index 00000000..5f622189 --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/.scm-settings @@ -0,0 +1,30 @@ +# $Id: .scm-settings $ +## @file +# Source code massager settings for the includes. +# + +# +# Copyright (C) 2010-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# SPDX-License-Identifier: GPL-3.0-only +# + + +# Some headers actually shouldn't have include guards: +/fuse-calls.h: --no-fix-header-guards diff --git a/src/VBox/ImageMounter/vboximg-mount/Makefile.kmk b/src/VBox/ImageMounter/vboximg-mount/Makefile.kmk new file mode 100644 index 00000000..e3f2f648 --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/Makefile.kmk @@ -0,0 +1,55 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the vboximg-mount Program. + +# +# Copyright (C) 2006-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# SPDX-License-Identifier: GPL-3.0-only +# + + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# +# vboximg-mount - Disk Image Flatting FUSE Program. +# +PROGRAMS += vboximg-mount + +vboximg-mount_TEMPLATE = VBoxMainClientExe +vboximg-mount_DEFS.darwin = __FreeBSD_==10 +vboximg-mount_DEFS = _FILE_OFFSET_BITS=64 + +vboximg-mount_SOURCES = \ + vboximg-mount.cpp \ + vboximg-mount.h \ + vboximgCrypto.cpp \ + vboximgCrypto.h \ + vboximgMedia.cpp \ + vboximgMedia.h \ + vboximgOpts.h \ + SelfSizingTable.h \ + fuse.cpp + +vboximg-mount_LIBS = \ + $(LIB_DDU) \ + $(LIB_RUNTIME) + +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h b/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h new file mode 100644 index 00000000..58f25769 --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h @@ -0,0 +1,323 @@ +/* $Id: SelfSizingTable.h $ */ +/** @file + * vboxraw header file + */ + +/* + * Copyright (C) 2018-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +/* SELFSIZINGTABLE + * + * An ANSI text-display oriented table, whose column widths conform to width of + * their contents. The goal is to optimize whitespace usage, so there's neither too + * much nor too little whitespace (e.g. min. necessary for optimal readability). + * + * Contents can only be added to and redisplayed, not manipulated after adding. + * + * Simple API (see example below): + * + * 1. Create table instance. + * 2. Add column definitions. + * 3. Add each row and set data for each column in a row. + * 4. Invoke the displayTable() method. + * + * Each time the table is [re]displayed its contents are [re]evaluated to determine + * the column sizes and header and data padding. + * + * Example: + * + * SELFSIZINGTABLE tbl(2); + * void *colPlanet = tbl.addCol("Planet" "%s", 1); + * void *colInhabit = tbl.addCol("Inhabitability", "%-12s = %s"); + * + * // This is an 'unrolled loop' example. More typical would be to iterate, + * // providing data content from arrays, indicies, in-place calculations, + * // databases, etc... rather than just hardcoded literals. + * + * void *row = tbl.addRow(); + * tbl.setCell(row, colPlanet, "Earth"); + * tbl.setCell(row, colInhabit, "Viability", "Decreasing"); + * row = tbl.addRow(); + * tbl.setCell(row, colPlanet, "Mars"); + * tbl.setCell(row, colInhabit, "Tolerability", "Miserable"); + * row = tbl.addRow(); + * tbl.setCell(row, colPlanet, "Neptune"); + * tbl.setCell(row, colInhabit, "Plausibility", "Forget it"); + * + * tbl.displayTable(); + * + * Planet Inhabitability + * Earth Viability = Decreasing + * Mars Tolerability = Miserable + * Neptune Plausibility = Forget it + * + * (note: + * Column headers displayed in bold red to distinguish from data) + * + */ + +#ifndef VBOX_INCLUDED_SRC_vboximg_mount_SelfSizingTable_h +#define VBOX_INCLUDED_SRC_vboximg_mount_SelfSizingTable_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/types.h> +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/message.h> +#include <iprt/stream.h> + +#define ANSI_BOLD "\x1b[1m" /** ANSI terminal esc. seq [CSI] to switch font to bold */ +#define ANSI_BLACK "\x1b[30m" /** ANSI terminal esc. seq [CSI] to switch font to black */ +#define ANSI_RED "\x1b[31m" /** ANSI terminal esc. seq [CSI] to switch font to red */ +#define ANSI_RESET "\x1b[m" /** ANSI terminal esc. seq to reset terminal attributes mode */ + +#define HDRLABEL_MAX 30 /** Maximum column header label length (for RTStrNLen()) */ +#define COLUMN_WIDTH_MAX 256 /** Maximum width of a display column */ + +typedef class SelfSizingTable +{ + public: + SelfSizingTable(int cbDefaultPadding = 1); + ~SelfSizingTable(); + void *addCol(const char *pszHdr, const char *pszFmt, int8_t align = LEFT, int8_t padRight = 0); + void *addRow(); + void setCell(void *row, void *col, ...); + void displayTable(); + + private: + typedef struct ColDesc { + struct ColDesc *next; + char *pszHdr; + uint8_t hdrLen; + char *pszFmt; + int8_t alignment; + uint8_t cbPadRightOpt; + uint8_t cbWidestDataInCol; + } COLDESC; + + typedef struct ColData + { + struct ColData *next; + COLDESC *pColDesc; + char *pszData; + uint8_t cbData; + } COLDATA; + + typedef struct Row + { + struct Row *next; + uint32_t id; + COLDATA colDataListhead; + } ROW; + + int cbDefaultColPadding; + COLDESC colDescListhead; + ROW rowListhead; + + public: + enum Alignment /* column/cell alignment */ + { + CENTER = 0, RIGHT = 1, LEFT = -1, + }; + +} SELFSIZINGTABLE; + +SELFSIZINGTABLE::SelfSizingTable(int cbDefaultPadding) +{ + this->cbDefaultColPadding = cbDefaultPadding; + colDescListhead.next = NULL; + rowListhead.next = NULL; +} +SELFSIZINGTABLE::~SelfSizingTable() +{ + COLDESC *pColDesc = colDescListhead.next; + while (pColDesc) + { + COLDESC *pColDescNext = pColDesc->next; + RTMemFree(pColDesc->pszHdr); + RTMemFree(pColDesc->pszFmt); + delete pColDesc; + pColDesc = pColDescNext; + } + ROW *pRow = rowListhead.next; + while(pRow) + { + ROW *pRowNext = pRow->next; + COLDATA *pColData = pRow->colDataListhead.next; + while (pColData) + { + COLDATA *pColDataNext = pColData->next; + delete[] pColData->pszData; + delete pColData; + pColData = pColDataNext; + } + delete pRow; + pRow = pRowNext; + } +} + +void *SELFSIZINGTABLE::addCol(const char *pszHdr, const char *pszFmt, int8_t align, int8_t padRight) +{ + COLDESC *pColDescNew = new COLDESC(); + if (!pColDescNew) + { + RTMsgErrorExitFailure("out of memory"); + return NULL; + } + pColDescNew->pszHdr = RTStrDup(pszHdr); + pColDescNew->hdrLen = RTStrNLen(pszHdr, HDRLABEL_MAX); + pColDescNew->pszFmt = RTStrDup(pszFmt); + pColDescNew->alignment = align; + pColDescNew->cbPadRightOpt = padRight; + COLDESC *pColDesc = &colDescListhead; + + while (pColDesc->next) + pColDesc = pColDesc->next; + + pColDesc->next = pColDescNew; + return (void *)pColDescNew; +} + +void *SELFSIZINGTABLE::addRow() +{ + ROW *pNewRow = new Row(); + COLDESC *pColDesc = colDescListhead.next; + COLDATA *pCurColData = &pNewRow->colDataListhead; + while (pColDesc) + { + COLDATA *pNewColData = new COLDATA(); + pNewColData->pColDesc = pColDesc; + pCurColData = pCurColData->next = pNewColData; + pColDesc = pColDesc->next; + } + ROW *pRow = &rowListhead; + while (pRow->next) + pRow = pRow->next; + pRow->next = pNewRow; + return (void *)pNewRow; +} + +void SELFSIZINGTABLE::setCell(void *row, void *col, ...) +{ + ROW *pRow = (ROW *)row; + COLDESC *pColDesc = (COLDESC *)col; + va_list ap; + va_start(ap, col); + + char *pszData = new char[COLUMN_WIDTH_MAX]; + int cbData = RTStrPrintfV(pszData, COLUMN_WIDTH_MAX, pColDesc->pszFmt, ap); + COLDATA *pColData = pRow->colDataListhead.next; + while (pColData) + { + if (pColData->pColDesc == pColDesc) + { + pColData->pszData = pszData; + pColData->cbData = cbData; + break; + } + pColData = pColData->next; + } +} + +void SELFSIZINGTABLE::displayTable() +{ + /* Determine max cell (and column header) length for each column */ + + COLDESC *pColDesc = colDescListhead.next; + while (pColDesc) + { + pColDesc->cbWidestDataInCol = pColDesc->hdrLen; + pColDesc = pColDesc->next; + } + ROW *pRow = rowListhead.next; + while(pRow) + { + COLDATA *pColData = pRow->colDataListhead.next; + while (pColData) + { + pColDesc = pColData->pColDesc; + if (pColData->cbData > pColDesc->cbWidestDataInCol) + pColDesc->cbWidestDataInCol = pColData->cbData;; + pColData = pColData->next; + } + pRow = pRow->next; + } + + /* Display col headers based on actual column size w/alignment & padding */ + pColDesc = colDescListhead.next; + while (pColDesc) + { + uint8_t colWidth = pColDesc->cbWidestDataInCol; + char colHdr[colWidth + 1], *pszColHdr = (char *)colHdr; + switch (pColDesc->alignment) + { + case RIGHT: + RTStrPrintf(pszColHdr, colWidth + 1, "%*s", colWidth, pColDesc->pszHdr); + break; + case LEFT: + RTStrPrintf(pszColHdr, colWidth + 1, "%-*s", colWidth, pColDesc->pszHdr); + break; + case CENTER: + int cbPad = (colWidth - pColDesc->hdrLen) / 2; + RTStrPrintf(pszColHdr, colWidth + 1, "%*s%s%*s", cbPad, "", pColDesc->pszHdr, cbPad, ""); + } + RTPrintf(ANSI_BOLD ANSI_RED); + uint8_t cbPad = pColDesc->cbPadRightOpt ? pColDesc->cbPadRightOpt : cbDefaultColPadding; + RTPrintf("%s%*s", pszColHdr, cbPad, " "); + RTPrintf(ANSI_RESET); + pColDesc = pColDesc->next; + } + RTPrintf("\n"); + /* + * Display each of the column data items for the row + */ + pRow = rowListhead.next; + while(pRow) + { + COLDATA *pColData = pRow->colDataListhead.next; + while (pColData) + { pColDesc = pColData->pColDesc; + uint8_t colWidth = pColDesc->cbWidestDataInCol; + char aCell[colWidth + 1]; + switch (pColDesc->alignment) + { + case RIGHT: + RTStrPrintf(aCell, colWidth + 1, "%*s", colWidth, pColData->pszData); + break; + case LEFT: + RTStrPrintf(aCell, colWidth + 1, "%-*s", colWidth, pColData->pszData); + break; + case CENTER: + int cbPad = (colWidth - pColData->cbData) / 2; + RTStrPrintf(aCell, colWidth + 1, "%*s%s%*s", cbPad, "", pColData->pszData, cbPad, ""); + } + uint8_t cbPad = pColDesc->cbPadRightOpt ? pColDesc->cbPadRightOpt : this->cbDefaultColPadding; + RTPrintf("%s%*s", aCell, cbPad, " "); + pColData = pColData->next; + } + RTPrintf("\n"); + pRow = pRow->next; + } +} +#endif /* !VBOX_INCLUDED_SRC_vboximg_mount_SelfSizingTable_h */ diff --git a/src/VBox/ImageMounter/vboximg-mount/fuse-calls.h b/src/VBox/ImageMounter/vboximg-mount/fuse-calls.h new file mode 100644 index 00000000..e7a09823 --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/fuse-calls.h @@ -0,0 +1,67 @@ +/** @file + * Stubs for dynamically loading libfuse/libosxfuse and the symbols which are needed by + * VirtualBox. + */ + +/* + * Copyright (C) 2019-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ +/** The file name of the fuse library */ +#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS) +# define RT_RUNTIME_LOADER_LIB_NAME "libfuse.so.2" +#elif defined(RT_OS_DARWIN) +# define RT_RUNTIME_LOADER_LIB_NAME "libosxfuse.dylib" +#else +# error "Unsupported host OS, manual work required" +#endif + +/** The name of the loader function */ +#define RT_RUNTIME_LOADER_FUNCTION RTFuseLoadLib + +/** The following are the symbols which we need from the fuse library. */ +#define RT_RUNTIME_LOADER_INSERT_SYMBOLS \ + RT_PROXY_STUB(fuse_main_real, int, (int argc, char **argv, struct fuse_operations *fuse_ops, size_t op_size, void *pv), \ + (argc, argv, fuse_ops, op_size, pv)) \ + RT_PROXY_STUB(fuse_opt_parse, int, (struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc), \ + (args, data, opts, proc)) \ + RT_PROXY_STUB(fuse_opt_add_arg, int, (struct fuse_args *args, const char *arg), \ + (args, arg)) \ + RT_PROXY_STUB(fuse_opt_free_args, void, (struct fuse_args *args), \ + (args)) + +#ifdef VBOX_FUSE_GENERATE_HEADER +# define RT_RUNTIME_LOADER_GENERATE_HEADER +# define RT_RUNTIME_LOADER_GENERATE_DECLS +# include <iprt/runtime-loader.h> +# undef RT_RUNTIME_LOADER_GENERATE_HEADER +# undef RT_RUNTIME_LOADER_GENERATE_DECLS + +#elif defined(VBOX_FUSE_GENERATE_BODY) +# define RT_RUNTIME_LOADER_GENERATE_BODY_STUBS +# include <iprt/runtime-loader.h> +# undef RT_RUNTIME_LOADER_GENERATE_BODY_STUBS + +#else +# error This file should only be included to generate stubs for loading the Fuse library at runtime +#endif + +#undef RT_RUNTIME_LOADER_LIB_NAME +#undef RT_RUNTIME_LOADER_INSERT_SYMBOLS diff --git a/src/VBox/ImageMounter/vboximg-mount/fuse.cpp b/src/VBox/ImageMounter/vboximg-mount/fuse.cpp new file mode 100644 index 00000000..06e76efc --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/fuse.cpp @@ -0,0 +1,35 @@ +/* $Id: fuse.cpp $ */ +/** @file + * + * Module to dynamically load libfuse and load all symbols + * which are needed by vboximg-mount. + */ + +/* + * Copyright (C) 2019-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define LOG_GROUP RTLOGGROUP_DEFAULT +#include "fuse.h" + +/* Declarations of the functions that we need from libfuse */ +#define VBOX_FUSE_GENERATE_BODY +#include "fuse-calls.h" diff --git a/src/VBox/ImageMounter/vboximg-mount/fuse.h b/src/VBox/ImageMounter/vboximg-mount/fuse.h new file mode 100644 index 00000000..a9ffd37b --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/fuse.h @@ -0,0 +1,185 @@ +/** @file + * Module to dynamically load libfuse/libosxfuse and load all symbols which are needed by + * vboximg-mount. + */ + +/* + * Copyright (C) 2019-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + * -------------------------------------------------------------------- + * + * This code is based on and contains parts of: + * + * libfuse + * + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB. + */ + +#ifndef VBOX_INCLUDED_SRC_vboximg_mount_fuse_h +#define VBOX_INCLUDED_SRC_vboximg_mount_fuse_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/types.h> +#include <iprt/stdarg.h> + +RT_C_DECLS_BEGIN + +/* Forward declaration of stat buffer. */ +struct stat; + +/** + * Fuse option structure. + */ +typedef struct fuse_opt +{ + /** Argument template with optional parameter formatting. */ + const char *pszTempl; + /** Offset where the parameter is stored inside the data struct passed to fuse_opt_parse(). */ + unsigned long uOffset; + /** The value to set the variable to if the template has no argument format. */ + int uVal; +} fuse_opt; + +#define FUSE_OPT_KEY(templ, key) { templ, -1U, key } +#define FUSE_OPT_END { NULL, 0, 0 } +#define FUSE_OPT_KEY_NONOPT -2 + +/** + * Fuse argument vector. + */ +typedef struct fuse_args +{ + int argc; + char **argv; + int allocated; +} fuse_args; + +#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } + + +/** + * Fuse file info structure - for us only the fh member is of interest for now. + */ +typedef struct fuse_file_info +{ + int flags; + unsigned long fh_old; + int writepage; + unsigned int oth_flags; + uint64_t fh; + uint64_t lock_owner; +} fuse_file_info; + +/** Option processing function. */ +typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs); + +/** Directory entry filler function. */ +typedef int (*fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off); + + +/** + * Fuse FS callback table implementing the filesystem functionality. + * + * Only required methods are filled out, others are stubbed (change as required). + */ +typedef struct fuse_operations +{ + int (*getattr) (const char *pszPath, struct stat *pStatBuf); + int (*readlink) (const char *pszPath, char *pbBuf, size_t cbBuf); + PFNRT getdir; + PFNRT mknod; + PFNRT mkdir; + PFNRT unlink; + PFNRT rmdir; + PFNRT symlink; + PFNRT rename; + PFNRT link; + PFNRT chmod; + PFNRT chown; + PFNRT truncate; + PFNRT utime; + int (*open) (const char *pszPath, struct fuse_file_info *pInfo); + int (*read) (const char *pszPath, char *pbBuf, size_t cbBuf, off_t off, struct fuse_file_info *pInfo); + int (*write) (const char *pszPath, const char *pbBuf, size_t cbBuf, off_t off, struct fuse_file_info *pInfo); + PFNRT statfs; + PFNRT flush; + int (*release) (const char *pszPath, struct fuse_file_info *pInfo); + PFNRT fsync; + PFNRT setxattr; /* OSXFuse has a different parameter layout. */ + PFNRT getxattr; /* OSXFuse has a different parameter layout. */ + PFNRT listxattr; + PFNRT removexattr; + int (*opendir) (const char *pszPath, struct fuse_file_info *pInfo); + int (*readdir) (const char *pszPath, void *pvBuf, fuse_fill_dir_t pfnFiller, off_t offset, struct fuse_file_info *pInfo); + PFNRT releasedir; + PFNRT fsyncdir; + PFNRT init; + PFNRT destroy; + PFNRT access; + PFNRT create; + PFNRT ftruncate; + PFNRT fgettatr; + PFNRT lock; + PFNRT utimens; + PFNRT bmap; + unsigned int flag_null_path_ok: 1; + unsigned int flag_nopath: 1; + unsigned int flag_utime_omit_ok: 1; + unsigned int flag_reserved: 20; + PFNRT ioctl; + PFNRT poll; + PFNRT write_buf; + PFNRT read_buf; + PFNRT flock; + PFNRT fallocate; +#ifdef RT_OS_DARWIN + PFNRT rsvd00; + PFNRT rsvd01; + PFNRT rsvd02; + PFNRT statfs_x; + PFNRT setvolname; + PFNRT exchange; + PFNRT getxtimes; + PFNRT setbkuptime; + PFNRT setchgtime; + PFNRT setcrtime; + PFNRT chflags; + PFNRT setattr_x; + PFNRT fsetattr_x; +#endif +} fuse_operations; + +/* Declarations of the functions that we need from libfuse */ +#define VBOX_FUSE_GENERATE_HEADER + +#include "fuse-calls.h" + +#undef VBOX_FUSE_GENERATE_HEADER + +RT_C_DECLS_END + +#endif /* !VBOX_INCLUDED_SRC_vboximg_mount_fuse_h */ + diff --git a/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp b/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp new file mode 100644 index 00000000..8e36358d --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp @@ -0,0 +1,1414 @@ +/* $Id: vboximg-mount.cpp $ */ +/** @file + * vboximg-mount - Disk Image Flattening FUSE Program. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ + +#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo log group */ + +#define RTTIME_INCL_TIMESPEC +#define FUSE_USE_VERSION 27 +#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) +# define UNIX_DERIVATIVE +#endif +#define MAX_READERS (INT32_MAX / 32) +#ifdef UNIX_DERIVATIVE +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <libgen.h> +#include <unistd.h> +#include <math.h> +#include <cstdarg> +#include <sys/stat.h> +#include <sys/time.h> +#endif +#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX) +# include <sys/param.h> +# undef PVM /* Blasted old BSD mess still hanging around darwin. */ +#endif +#ifdef RT_OS_LINUX +# include <linux/fs.h> +# include <linux/hdreg.h> +#endif +#include <VirtualBox_XPCOM.h> +#include <VBox/com/VirtualBox.h> +#include <VBox/vd.h> +#include <VBox/vd-ifs.h> +#include <VBox/log.h> +#include <VBox/err.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/NativeEventQueue.h> +#include <VBox/com/com.h> +#include <VBox/com/string.h> +#include <VBox/com/Guid.h> +#include <VBox/com/array.h> +#include <VBox/com/errorprint.h> +#include <VBox/vd-plugin.h> +#include <iprt/initterm.h> +#include <iprt/assert.h> +#include <iprt/message.h> +#include <iprt/critsect.h> +#include <iprt/asm.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#include <iprt/types.h> +#include <iprt/path.h> +#include <iprt/utf16.h> +#include <iprt/base64.h> +#include <iprt/vfs.h> +#include <iprt/dvm.h> +#include <iprt/time.h> + +#include "fuse.h" +#include "vboximgCrypto.h" +#include "vboximgMedia.h" +#include "SelfSizingTable.h" +#include "vboximgOpts.h" + +using namespace com; + +enum { + USAGE_FLAG, +}; + +#if !defined(S_ISTXT) && defined(S_ISVTX) +# define S_ISTXT (S_ISVTX) +#endif + +#define VBOX_EXTPACK "Oracle VM VirtualBox Extension Pack" +#define VERBOSE g_vboximgOpts.fVerbose + +#define SAFENULL(strPtr) (strPtr ? strPtr : "") +#define CSTR(arg) Utf8Str(arg).c_str() /* Converts XPCOM string type to C string type */ + +static struct fuse_operations g_vboximgOps; /** FUSE structure that defines allowed ops for this FS */ + +/** + * Volume data. + */ +typedef struct VBOXIMGMOUNTVOL +{ + /** The volume handle. */ + RTDVMVOLUME hVol; + /** The VFS file associated with the volume. */ + RTVFSFILE hVfsFileVol; + /** Handle to the VFS root if supported and specified. */ + RTVFS hVfsRoot; + /** Handle to the root directory. */ + RTVFSDIR hVfsDirRoot; +} VBOXIMGMOUNTVOL; +/** Pointer to a volume data structure. */ +typedef VBOXIMGMOUNTVOL *PVBOXIMGMOUNTVOL; + +/* Global variables */ +static RTVFSFILE g_hVfsFileDisk = NIL_RTVFSFILE; /** Disk as VFS file handle. */ +static uint32_t g_cbSector; /** Disk sector size. */ +static RTDVM g_hDvmMgr; /** Handle to the volume manager. */ +static char *g_pszDiskUuid; /** UUID of image (if known, otherwise NULL) */ +static PVDINTERFACE g_pVdIfs; /** @todo Remove when VD I/O becomes threadsafe */ +static VDINTERFACETHREADSYNC g_VDIfThreadSync; /** @todo Remove when VD I/O becomes threadsafe */ +static RTCRITSECT g_vdioLock; /** @todo Remove when VD I/O becomes threadsafe */ +static char *g_pszImageName = NULL; /** Base filename for current VD image */ +static char *g_pszImagePath; /** Full path to current VD image */ +static char *g_pszBaseImagePath; /** Base image known after parsing */ +static char *g_pszBaseImageName; /** Base image known after parsing */ +static uint32_t g_cImages; /** Number of images in diff chain */ + +/** Pointer to the detected volumes. */ +static PVBOXIMGMOUNTVOL g_paVolumes; +/** Number of detected volumes. */ +static uint32_t g_cVolumes; + +VBOXIMGOPTS g_vboximgOpts; + +#define OPTION(fmt, pos, val) { fmt, offsetof(struct vboximgOpts, pos), val } + +static struct fuse_opt vboximgOptDefs[] = { + OPTION("--image %s", pszImageUuidOrPath, 0), + OPTION("-i %s", pszImageUuidOrPath, 0), + OPTION("--rw", fRW, 1), + OPTION("--root", fAllowRoot, 1), + OPTION("--vm %s", pszVm, 0), + OPTION("-l", fList, 1), + OPTION("--list", fList, 1), + OPTION("-g", fGstFs, 1), + OPTION("--guest-filesystem", fGstFs, 1), + OPTION("--verbose", fVerbose, 1), + OPTION("-v", fVerbose, 1), + OPTION("--wide", fWide, 1), + OPTION("-w", fWide, 1), + OPTION("-lv", fVerboseList, 1), + OPTION("-vl", fVerboseList, 1), + OPTION("-lw", fWideList, 1), + OPTION("-wl", fWideList, 1), + OPTION("-h", fBriefUsage, 1), + FUSE_OPT_KEY("--help", USAGE_FLAG), + FUSE_OPT_KEY("-vm", FUSE_OPT_KEY_NONOPT), + FUSE_OPT_END +}; + +typedef struct IMAGELIST +{ + struct IMAGELIST *next; + struct IMAGELIST *prev; + ComPtr<IToken> pLockToken; + bool fWriteable; + ComPtr<IMedium> pImage; + Bstr pImageName; + Bstr pImagePath; +} IMAGELIST; + +IMAGELIST listHeadLockList; /* flink & blink intentionally left NULL */ + + + +/** @todo Remove when VD I/O becomes threadsafe */ +static DECLCALLBACK(int) vboximgThreadStartRead(void *pvUser) +{ + PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser; + return RTCritSectEnter(vdioLock); +} + +static DECLCALLBACK(int) vboximgThreadFinishRead(void *pvUser) +{ + PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser; + return RTCritSectLeave(vdioLock); +} + +static DECLCALLBACK(int) vboximgThreadStartWrite(void *pvUser) +{ + PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser; + return RTCritSectEnter(vdioLock); +} + +static DECLCALLBACK(int) vboximgThreadFinishWrite(void *pvUser) +{ + PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser; + return RTCritSectLeave(vdioLock); +} +/** @todo (end of to do section) */ + + +static void +briefUsage() +{ + RTPrintf("usage: vboximg-mount [options] <mount point directory path>\n\n" + "vboximg-mount options:\n\n" + " [ { -i | --image } <specifier> ] VirtualBox disk base image or snapshot,\n" + " specified by UUID or path\n" + "\n" + " [ { -l | --list } ] If --image specified, list its partitions,\n" + " otherwise, list registered VMs and their\n" + " attached virtual HDD disk media. In verbose\n" + " mode, VM/media list will be long format,\n" + " i.e. including snapshot images and paths.\n" + "\n" + " [ { -w | --wide } ] List media in wide / tabular format\n" + " (reduces vertical scrolling but requires\n" + " wider than standard 80 column window)\n" + "\n" + " [ { -g | --guest-filesystem } ] Exposes supported guest filesystems directly\n" + " in the mounted directory without the need\n" + " for a filesystem driver on the host\n" + "\n" + " [ --vm UUID ] Restrict media list to specified vm.\n" + "\n" + " [ --rw ] Make image writeable (default = readonly)\n" + "\n" + " [ --root ] Same as -o allow_root.\n" + "\n" + " [ { -v | --verbose } ] Log extra information.\n" + "\n" + " [ -o opt[,opt...]] FUSE mount options.\n" + "\n" + " [ { --help | -h | -? } ] Display this usage information.\n" + ); + RTPrintf("\n" + "vboximg-mount is a utility to make VirtualBox disk images available to the host\n" + "operating system for privileged or non-priviliged access. Any version of the\n" + "disk can be mounted from its available history of snapshots.\n" + "\n" + "If the user specifies a base image identifier using the --image option, only\n" + "the base image will be mounted, disregarding any snapshots. Alternatively,\n" + "if a snapshot is specified, the state of the FUSE-mounted virtual disk\n" + "is synthesized from the implied chain of snapshots, including the base image.\n" + "\n" + "The virtual disk is exposed as a device node within a FUSE-based filesystem\n" + "that overlays the user-provided mount point. The FUSE filesystem consists of a\n" + "directory containing a number of files and possibly other directories:" + " * vhdd: Provides access to the raw disk image data as a flat image\n" + " * vol<id>: Provides access to individual volumes on the accessed disk image\n" + " * fs<id>: Provides access to a supported filesystem without the need for a" + " host filesystem driver\n" + "\n" + "The directory will also contain a symbolic link which has the same basename(1)\n" + "as the virtual disk base image and points to the location of the\n" + "virtual disk base image.\n" + "\n" + ); +} + +static int +vboximgOptHandler(void *data, const char *arg, int optKey, struct fuse_args *outargs) +{ + RT_NOREF(data); + RT_NOREF(arg); + RT_NOREF(optKey); + RT_NOREF(outargs); + + /* + * Apparently this handler is only called for arguments FUSE can't parse, + * and arguments that don't result in variable assignment such as "USAGE" + * In this impl. that's always deemed a parsing error. + */ + if (*arg != '-') /* could be user's mount point */ + return 1; + + return -1; +} + + +/** + * Queries the VFS object handle from the given path. + * + * @returns IPRT status code. + * @retval VERR_NOT_FOUND if the object denoted by the path couldn't be found. + * @param pszPath The path. + * @param phVfsObj Where to store the handle to the VFS object on success. + */ +static int vboxImgMntVfsObjQueryFromPath(const char *pszPath, PRTVFSOBJ phVfsObj) +{ + PRTPATHSPLIT pPathSplit = NULL; + int rc = RTPathSplitA(pszPath, &pPathSplit, RTPATH_STR_F_STYLE_HOST); + if (RT_SUCCESS(rc)) + { + if ( RTPATH_PROP_HAS_ROOT_SPEC(pPathSplit->fProps) + && pPathSplit->cComps >= 2) + { + /* Skip the root specifier and start with the component coming afterwards. */ + if ( !RTStrCmp(pPathSplit->apszComps[1], "vhdd") + && g_hVfsFileDisk != NIL_RTVFSFILE) + *phVfsObj = RTVfsObjFromFile(g_hVfsFileDisk); + else if (!RTStrNCmp(pPathSplit->apszComps[1], "vol", sizeof("vol") - 1)) + { + /* Retrieve the accessed volume and return the stat data. */ + uint32_t idxVol; + int vrc = RTStrToUInt32Full(&pPathSplit->apszComps[1][3], 10, &idxVol); + if ( vrc == VINF_SUCCESS + && idxVol < g_cVolumes + && g_paVolumes[idxVol].hVfsFileVol != NIL_RTVFSFILE) + *phVfsObj = RTVfsObjFromFile(g_paVolumes[idxVol].hVfsFileVol); + else + rc = VERR_NOT_FOUND; + } + else if (!RTStrNCmp(pPathSplit->apszComps[1], "fs", sizeof("fs") - 1)) + { + /* Retrieve the accessed volume and return the stat data. */ + uint32_t idxVol; + int vrc = RTStrToUInt32Full(&pPathSplit->apszComps[1][2], 10, &idxVol); + if ( vrc == VINF_SUCCESS + && idxVol < g_cVolumes + && g_paVolumes[idxVol].hVfsDirRoot != NIL_RTVFSDIR) + *phVfsObj = RTVfsObjFromDir(g_paVolumes[idxVol].hVfsDirRoot); + else + rc = VERR_NOT_FOUND; + + /* Is an object inside the guest filesystem requested? */ + if (pPathSplit->cComps > 2) + { + PRTPATHSPLIT pPathSplitVfs = (PRTPATHSPLIT)RTMemTmpAllocZ(RT_UOFFSETOF_DYN(RTPATHSPLIT, apszComps[pPathSplit->cComps - 1])); + if (RT_LIKELY(pPathSplitVfs)) + { + pPathSplitVfs->cComps = pPathSplit->cComps - 1; + pPathSplitVfs->fProps = pPathSplit->fProps; + pPathSplitVfs->cchPath = pPathSplit->cchPath - strlen(pPathSplit->apszComps[1]) - 1; + pPathSplitVfs->cbNeeded = pPathSplit->cbNeeded; + pPathSplitVfs->pszSuffix = pPathSplit->pszSuffix; + pPathSplitVfs->apszComps[0] = pPathSplit->apszComps[0]; + for (uint32_t i = 1; i < pPathSplitVfs->cComps; i++) + pPathSplitVfs->apszComps[i] = pPathSplit->apszComps[i + 1]; + + /* Reassemble the path. */ + char *pszPathVfs = (char *)RTMemTmpAllocZ(pPathSplitVfs->cbNeeded); + if (RT_LIKELY(pszPathVfs)) + { + rc = RTPathSplitReassemble(pPathSplitVfs, RTPATH_STR_F_STYLE_HOST, pszPathVfs, pPathSplitVfs->cbNeeded); + if (RT_SUCCESS(rc)) + { + rc = RTVfsObjOpen(g_paVolumes[idxVol].hVfsRoot, pszPathVfs, + RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, + RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK, + phVfsObj); + } + RTMemTmpFree(pszPathVfs); + } + + RTMemTmpFree(pPathSplitVfs); + } + else + rc = VERR_NO_MEMORY; + } + } + else + rc = VERR_NOT_FOUND; + + rc = VINF_SUCCESS; + } + else + rc = VERR_NOT_FOUND; + RTPathSplitFree(pPathSplit); + } + + return rc; +} + + +/** @copydoc fuse_operations::open */ +static int vboximgOp_open(const char *pszPath, struct fuse_file_info *pInfo) +{ + LogFlowFunc(("pszPath=%s\n", pszPath)); + int rc = 0; + + RTVFSOBJ hVfsObj; + int vrc = vboxImgMntVfsObjQueryFromPath(pszPath, &hVfsObj); + if (RT_SUCCESS(vrc)) + { + uint32_t fNotSup = 0; + +#ifdef UNIX_DERIVATIVE +# ifdef RT_OS_DARWIN + fNotSup = O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK | + O_ASYNC | O_CREAT | O_TRUNC | O_EXCL | O_EVTONLY; +# elif defined(RT_OS_LINUX) + fNotSup = O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK; + /* | O_LARGEFILE | O_SYNC | ? */ +# elif defined(RT_OS_FREEBSD) + fNotSup = O_APPEND | O_ASYNC | O_DIRECT | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK; + /* | O_LARGEFILE | O_SYNC | ? */ +# endif +#else +# error "Port me" +#endif + + if (!(pInfo->flags & fNotSup)) + { +#ifdef UNIX_DERIVATIVE + if ((pInfo->flags & O_ACCMODE) == O_ACCMODE) + rc = -EINVAL; +# ifdef O_DIRECTORY + if (pInfo->flags & O_DIRECTORY) + rc = -ENOTDIR; +# endif +#endif + if (!rc) + { + pInfo->fh = (uintptr_t)hVfsObj; + return 0; + } + } + else + rc = -EINVAL; + + RTVfsObjRelease(hVfsObj); + } + else + rc = -RTErrConvertToErrno(vrc); + + LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath)); + return rc; + +} + +/** @copydoc fuse_operations::release */ +static int vboximgOp_release(const char *pszPath, struct fuse_file_info *pInfo) +{ + RT_NOREF(pszPath); + + LogFlowFunc(("pszPath=%s\n", pszPath)); + + RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh; + RTVfsObjRelease(hVfsObj); + + LogFlowFunc(("\"%s\"\n", pszPath)); + return 0; +} + + +/** @copydoc fuse_operations::read */ +static int vboximgOp_read(const char *pszPath, char *pbBuf, size_t cbBuf, + off_t offset, struct fuse_file_info *pInfo) +{ + RT_NOREF(pszPath); + + LogFlowFunc(("offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath)); + + AssertReturn(offset >= 0, -EINVAL); + AssertReturn((int)cbBuf >= 0, -EINVAL); + AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL); + + int rc = 0; + RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh; + switch (RTVfsObjGetType(hVfsObj)) + { + case RTVFSOBJTYPE_FILE: + { + size_t cbRead = 0; + RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj); + int vrc = RTVfsFileReadAt(hVfsFile, offset, pbBuf, cbBuf, &cbRead); + if (cbRead) + rc = cbRead; + else if (vrc == VINF_EOF) + rc = -RTErrConvertToErrno(VERR_EOF); + RTVfsFileRelease(hVfsFile); + break; + } + default: + rc = -EINVAL; + } + + if (rc < 0) + LogFlowFunc(("%s\n", strerror(rc))); + return rc; +} + +/** @copydoc fuse_operations::write */ +static int vboximgOp_write(const char *pszPath, const char *pbBuf, size_t cbBuf, + off_t offset, struct fuse_file_info *pInfo) +{ + RT_NOREF(pszPath); + RT_NOREF(pInfo); + + LogFlowFunc(("offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath)); + + AssertReturn(offset >= 0, -EINVAL); + AssertReturn((int)cbBuf >= 0, -EINVAL); + AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL); + + if (!g_vboximgOpts.fRW) + { + LogFlowFunc(("WARNING: vboximg-mount (FUSE FS) --rw option not specified\n" + " (write operation ignored w/o error!)\n")); + return cbBuf; + } + + int rc = 0; + RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh; + switch (RTVfsObjGetType(hVfsObj)) + { + case RTVFSOBJTYPE_FILE: + { + size_t cbWritten = 0; + RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj); + int vrc = RTVfsFileWriteAt(hVfsFile, offset, pbBuf, cbBuf, &cbWritten); + if (cbWritten) + rc = cbWritten; + else if (vrc == VINF_EOF) + rc = -RTErrConvertToErrno(VERR_EOF); + RTVfsFileRelease(hVfsFile); + break; + } + default: + rc = -EINVAL; + } + + if (rc < 0) + LogFlowFunc(("%s\n", strerror(rc))); + + return rc; +} + +/** @copydoc fuse_operations::getattr */ +static int vboximgOp_getattr(const char *pszPath, struct stat *stbuf) +{ + int rc = 0; + + LogFlowFunc(("pszPath=%s, stat(\"%s\")\n", pszPath, g_pszImagePath)); + + memset(stbuf, 0, sizeof(struct stat)); + + if (RTStrCmp(pszPath, "/") == 0) + { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + } + else if ( g_pszImageName + && RTStrNCmp(pszPath + 1, g_pszImageName, strlen(g_pszImageName)) == 0) + { + /* When the disk is partitioned, the symbolic link named from `basename` of + * resolved path to VBox disk image, has appended to it formatted text + * representing the offset range of the partition. + * + * $ vboximg-mount -i /stroll/along/the/path/simple_fixed_disk.vdi -p 1 /mnt/tmpdir + * $ ls /mnt/tmpdir + * simple_fixed_disk.vdi[20480:2013244928] vhdd + */ + rc = stat(g_pszImagePath, stbuf); + if (rc < 0) + return rc; + stbuf->st_size = 0; + stbuf->st_mode = S_IFLNK | 0444; + stbuf->st_nlink = 1; + stbuf->st_uid = 0; + stbuf->st_gid = 0; + } + else + { + /* Query the VFS object and fill in the data. */ + RTVFSOBJ hVfsObj = NIL_RTVFSOBJ; + int vrc = vboxImgMntVfsObjQueryFromPath(pszPath, &hVfsObj); + if (RT_SUCCESS(vrc)) + { + RTFSOBJINFO ObjInfo; + + vrc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_UNIX); + if (RT_SUCCESS(vrc)) + { + stbuf->st_size = ObjInfo.cbObject; + stbuf->st_nlink = 1; + stbuf->st_uid = 0; + stbuf->st_gid = 0; + +#ifdef RT_OS_DARWIN + RTTimeSpecGetTimespec(&ObjInfo.AccessTime, &stbuf->st_atimespec); + RTTimeSpecGetTimespec(&ObjInfo.ModificationTime, &stbuf->st_mtimespec); + RTTimeSpecGetTimespec(&ObjInfo.ChangeTime, &stbuf->st_ctimespec); + RTTimeSpecGetTimespec(&ObjInfo.BirthTime, &stbuf->st_birthtimespec); +#else + RTTimeSpecGetTimespec(&ObjInfo.AccessTime, &stbuf->st_atim); + RTTimeSpecGetTimespec(&ObjInfo.ModificationTime, &stbuf->st_mtim); + RTTimeSpecGetTimespec(&ObjInfo.ChangeTime, &stbuf->st_ctim); +#endif + + switch (ObjInfo.Attr.fMode & RTFS_TYPE_MASK) + { + case RTFS_TYPE_FIFO: + { + stbuf->st_mode = S_IFIFO; + break; + } + case RTFS_TYPE_DEV_CHAR: + { + stbuf->st_mode = S_IFCHR; + break; + } + case RTFS_TYPE_DIRECTORY: + { + stbuf->st_mode = S_IFDIR; + stbuf->st_nlink = 2; + break; + } + case RTFS_TYPE_DEV_BLOCK: + { + stbuf->st_mode = S_IFBLK; + break; + } + case RTFS_TYPE_FILE: + { + stbuf->st_mode = S_IFREG; + break; + } + case RTFS_TYPE_SYMLINK: + { + stbuf->st_mode = S_IFLNK; + break; + } + case RTFS_TYPE_SOCKET: + { + stbuf->st_mode = S_IFSOCK; + break; + } +#if 0 /* Not existing on Linux. */ + case RTFS_TYPE_WHITEOUT: + { + stbuf->st_mode = S_IFWHT; + break; + } +#endif + default: + stbuf->st_mode = 0; + } + + if (ObjInfo.Attr.fMode & RTFS_UNIX_ISUID) + stbuf->st_mode |= S_ISUID; + if (ObjInfo.Attr.fMode & RTFS_UNIX_ISGID) + stbuf->st_mode |= S_ISGID; + if (ObjInfo.Attr.fMode & RTFS_UNIX_ISTXT) + stbuf->st_mode |= S_ISTXT; + + /* Owner permissions. */ + if (ObjInfo.Attr.fMode & RTFS_UNIX_IRUSR) + stbuf->st_mode |= S_IRUSR; + if (ObjInfo.Attr.fMode & RTFS_UNIX_IWUSR) + stbuf->st_mode |= S_IWUSR; + if (ObjInfo.Attr.fMode & RTFS_UNIX_IXUSR) + stbuf->st_mode |= S_IXUSR; + + /* Group permissions. */ + if (ObjInfo.Attr.fMode & RTFS_UNIX_IRGRP) + stbuf->st_mode |= S_IRGRP; + if (ObjInfo.Attr.fMode & RTFS_UNIX_IWGRP) + stbuf->st_mode |= S_IWGRP; + if (ObjInfo.Attr.fMode & RTFS_UNIX_IXGRP) + stbuf->st_mode |= S_IXGRP; + + /* Other permissions. */ + if (ObjInfo.Attr.fMode & RTFS_UNIX_IROTH) + stbuf->st_mode |= S_IROTH; + if (ObjInfo.Attr.fMode & RTFS_UNIX_IWOTH) + stbuf->st_mode |= S_IWOTH; + if (ObjInfo.Attr.fMode & RTFS_UNIX_IXOTH) + stbuf->st_mode |= S_IXOTH; + + if (ObjInfo.Attr.enmAdditional == RTFSOBJATTRADD_UNIX) + { + stbuf->st_uid = ObjInfo.Attr.u.Unix.uid; + stbuf->st_gid = ObjInfo.Attr.u.Unix.gid; + stbuf->st_nlink = ObjInfo.Attr.u.Unix.cHardlinks; + stbuf->st_ino = ObjInfo.Attr.u.Unix.INodeId; + stbuf->st_dev = ObjInfo.Attr.u.Unix.INodeIdDevice; + /*stbuf->st_flags = ObjInfo.Attr.u.Unix.fFlags;*/ /* Not existing on Linux. */ + /*stbuf->st_gen = ObjInfo.Attr.u.Unix.GenerationId;*/ /* Not existing on Linux. */ + stbuf->st_rdev = ObjInfo.Attr.u.Unix.Device; + } + } + + RTVfsObjRelease(hVfsObj); + } + else if (vrc == VERR_NOT_FOUND) + rc = -ENOENT; + else + rc = -RTErrConvertToErrno(vrc); + } + + return rc; +} + +/** @copydoc fuse_operations::readdir */ +static int vboximgOp_readdir(const char *pszPath, void *pvBuf, fuse_fill_dir_t pfnFiller, + off_t offset, struct fuse_file_info *pInfo) +{ + RT_NOREF(offset); + RT_NOREF(pInfo); + + int rc = 0; + + /* Special root directory handling?. */ + if (!RTStrCmp(pszPath, "/")) + { + /* + * mandatory '.', '..', ... + */ + pfnFiller(pvBuf, ".", NULL, 0); + pfnFiller(pvBuf, "..", NULL, 0); + + if (g_pszImageName) + { + /* + * Create FUSE FS dir entry that is depicted here (and exposed via stat()) as + * a symbolic link back to the resolved path to the VBox virtual disk image, + * whose symlink name is basename that path. This is a convenience so anyone + * listing the dir can figure out easily what the vhdd FUSE node entry + * represents. + */ + pfnFiller(pvBuf, g_pszImageName, NULL, 0); + } + + if (g_hVfsFileDisk != NIL_RTVFSFILE) + { + /* + * Create entry named "vhdd" denoting the whole disk, which getattr() will describe as a + * regular file, and thus will go through the open/release/read/write vectors + * to access the VirtualBox image as processed by the IRPT VD API. + */ + pfnFiller(pvBuf, "vhdd", NULL, 0); + } + + /* Create entries for the individual volumes. */ + for (uint32_t i = 0; i < g_cVolumes; i++) + { + char tmp[64]; + if (g_paVolumes[i].hVfsFileVol != NIL_RTVFSFILE) + { + RTStrPrintf(tmp, sizeof (tmp), "vol%u", i); + pfnFiller(pvBuf, tmp, NULL, 0); + } + + if (g_paVolumes[i].hVfsRoot != NIL_RTVFS) + { + RTStrPrintf(tmp, sizeof (tmp), "fs%u", i); + pfnFiller(pvBuf, tmp, NULL, 0); + } + } + } + else + { + /* Query the VFS object and fill in the data. */ + RTVFSOBJ hVfsObj = NIL_RTVFSOBJ; + int vrc = vboxImgMntVfsObjQueryFromPath(pszPath, &hVfsObj); + if (RT_SUCCESS(vrc)) + { + switch (RTVfsObjGetType(hVfsObj)) + { + case RTVFSOBJTYPE_DIR: + { + RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj); + RTDIRENTRYEX DirEntry; + + vrc = RTVfsDirRewind(hVfsDir); AssertRC(vrc); + vrc = RTVfsDirReadEx(hVfsDir, &DirEntry, NULL, RTFSOBJATTRADD_NOTHING); + while (RT_SUCCESS(vrc)) + { + pfnFiller(pvBuf, DirEntry.szName, NULL, 0); + vrc = RTVfsDirReadEx(hVfsDir, &DirEntry, NULL, RTFSOBJATTRADD_NOTHING); + } + + RTVfsDirRelease(hVfsDir); + break; + } + default: + rc = -EINVAL; + } + + RTVfsObjRelease(hVfsObj); + } + else + rc = -RTErrConvertToErrno(vrc); + } + + return rc; +} + +/** @copydoc fuse_operations::readlink */ +static int vboximgOp_readlink(const char *pszPath, char *buf, size_t size) +{ + RT_NOREF(pszPath); + RTStrCopy(buf, size, g_pszImagePath); + return 0; +} + + +/** + * Displays the list of volumes on the opened image. + */ +static void vboxImgMntVolumesDisplay(void) +{ + /* + * Partition table is most readable and concise when headers and columns + * are adapted to the actual data, to avoid insufficient or excessive whitespace. + */ + + RTPrintf( "Virtual disk image:\n\n"); + RTPrintf(" Base: %s\n", g_pszBaseImagePath); + if (g_cImages > 1) + RTPrintf(" Diff: %s\n", g_pszImagePath); + if (g_pszDiskUuid) + RTPrintf(" UUID: %s\n\n", g_pszDiskUuid); + + SELFSIZINGTABLE tbl(2); + + void *colPartition = tbl.addCol("Partition", "%s(%d)", -1); + void *colBoot = tbl.addCol("Boot", "%c ", 1); + void *colStart = tbl.addCol("Start", "%lld", 1); + void *colSectors = tbl.addCol("Sectors", "%lld", -1, 2); + void *colSize = tbl.addCol("Size", "%s", 1); + void *colOffset = tbl.addCol("Offset", "%lld", 1); + void *colType = tbl.addCol("Type", "%s", -1, 2); + + for (uint32_t i = 0; i < g_cVolumes; i++) + { + PVBOXIMGMOUNTVOL pVol = &g_paVolumes[i]; + uint64_t fVolFlags = RTDvmVolumeGetFlags(pVol->hVol); + uint64_t cbVol = RTDvmVolumeGetSize(pVol->hVol); + RTDVMVOLTYPE enmType = RTDvmVolumeGetType(pVol->hVol); + uint64_t offStart = 0; + uint64_t offEnd = 0; + + if (fVolFlags & DVMVOLUME_F_CONTIGUOUS) + { + int rc = RTDvmVolumeQueryRange(pVol->hVol, &offStart, &offEnd); + AssertRC(rc); + } + + void *row = tbl.addRow(); + tbl.setCell(row, colPartition, g_pszBaseImageName, i); + tbl.setCell(row, colBoot, (fVolFlags & DVMVOLUME_FLAGS_BOOTABLE) ? '*' : ' '); + tbl.setCell(row, colStart, offStart / g_cbSector); + tbl.setCell(row, colSectors, cbVol / g_cbSector); + tbl.setCell(row, colSize, vboximgScaledSize(cbVol)); + tbl.setCell(row, colOffset, offStart); + tbl.setCell(row, colType, RTDvmVolumeTypeGetDescr(enmType)); + } + tbl.displayTable(); + RTPrintf ("\n"); +} + + +/** + * Sets up the volumes for the disk. + * + * @returns IPRT status code. + */ +static int vboxImgMntVolumesSetup(void) +{ + g_cVolumes = 0; + g_paVolumes = NULL; + + int rc = RTDvmCreate(&g_hDvmMgr, g_hVfsFileDisk, g_cbSector, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + rc = RTDvmMapOpen(g_hDvmMgr); + if (RT_SUCCESS(rc)) + { + g_cVolumes = RTDvmMapGetValidVolumes(g_hDvmMgr); + if ( g_cVolumes != UINT32_MAX + && g_cVolumes > 0) + { + g_paVolumes = (PVBOXIMGMOUNTVOL)RTMemAllocZ(g_cVolumes * sizeof(VBOXIMGMOUNTVOL)); + if (RT_LIKELY(g_paVolumes)) + { + g_paVolumes[0].hVfsRoot = NIL_RTVFS; + + rc = RTDvmMapQueryFirstVolume(g_hDvmMgr, &g_paVolumes[0].hVol); + if (RT_SUCCESS(rc)) + rc = RTDvmVolumeCreateVfsFile(g_paVolumes[0].hVol, + RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE, + &g_paVolumes[0].hVfsFileVol); + + for (uint32_t i = 1; i < g_cVolumes && RT_SUCCESS(rc); i++) + { + g_paVolumes[i].hVfsRoot = NIL_RTVFS; + rc = RTDvmMapQueryNextVolume(g_hDvmMgr, g_paVolumes[i-1].hVol, &g_paVolumes[i].hVol); + if (RT_SUCCESS(rc)) + rc = RTDvmVolumeCreateVfsFile(g_paVolumes[i].hVol, + RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE, + &g_paVolumes[i].hVfsFileVol); + } + + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + RTMemFree(g_paVolumes); + g_paVolumes = NULL; + g_cVolumes = 0; + } + else + rc = VERR_NO_MEMORY; + } + else if (g_cVolumes == UINT32_MAX) + { + g_cVolumes = 0; + rc = VERR_INTERNAL_ERROR; + } + + RTDvmRelease(g_hDvmMgr); + } + else if (rc == VERR_NOT_FOUND) + rc = VINF_SUCCESS; + } + + return rc; +} + + +static int vboxImgMntImageSetup(struct fuse_args *args) +{ + /* + * Initialize COM. + */ + using namespace com; + HRESULT hrc = com::Initialize(); + if (FAILED(hrc)) + { +# ifdef VBOX_WITH_XPCOM + if (hrc == NS_ERROR_FILE_ACCESS_DENIED) + { + char szHome[RTPATH_MAX] = ""; + com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome)); + return RTMsgErrorExit(RTEXITCODE_FAILURE, + "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome); + } +# endif + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM! (hrc=%Rhrc)", hrc); + } + + /* + * Get the remote VirtualBox object and create a local session object. + */ + ComPtr<IVirtualBoxClient> pVirtualBoxClient; + ComPtr<IVirtualBox> pVirtualBox; + + hrc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient); + if (SUCCEEDED(hrc)) + hrc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam()); + + if (FAILED(hrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get IVirtualBox object! (hrc=%Rhrc)", hrc); + + if (g_vboximgOpts.fList && g_vboximgOpts.pszImageUuidOrPath == NULL) + { + vboximgListVMs(pVirtualBox); + return VINF_SUCCESS; + } + + if (!g_vboximgOpts.pszImageUuidOrPath) + return RTMsgErrorExitFailure("A image UUID or path needs to be provided using the --image/-i option\n"); + + Bstr pMediumUuid; + ComPtr<IMedium> pVDiskMedium = NULL; + char *pszFormat; + VDTYPE enmType; + + /* + * Open chain of images from what is provided on command line, to base image + */ + if (g_vboximgOpts.pszImageUuidOrPath) + { + /* compiler was too fussy about access mode's data type in conditional expr, so... */ + if (g_vboximgOpts.fRW) + CHECK_ERROR(pVirtualBox, OpenMedium(Bstr(g_vboximgOpts.pszImageUuidOrPath).raw(), DeviceType_HardDisk, + AccessMode_ReadWrite, false /* forceNewUuid */, pVDiskMedium.asOutParam())); + + else + CHECK_ERROR(pVirtualBox, OpenMedium(Bstr(g_vboximgOpts.pszImageUuidOrPath).raw(), DeviceType_HardDisk, + AccessMode_ReadOnly, false /* forceNewUuid */, pVDiskMedium.asOutParam())); + + if (FAILED(hrc)) + return RTMsgErrorExitFailure("\nCould't find specified VirtualBox base or snapshot disk image:\n%s", + g_vboximgOpts.pszImageUuidOrPath); + + + CHECK_ERROR(pVDiskMedium, COMGETTER(Id)(pMediumUuid.asOutParam())); + g_pszDiskUuid = RTStrDup((char *)CSTR(pMediumUuid)); + + /* + * Lock & cache the disk image media chain (from leaf to base). + * Only leaf can be rw (and only if media is being mounted in non-default writable (rw) mode) + * + * Note: Failure to acquire lock is intentionally fatal (e.g. program termination) + */ + + if (VERBOSE) + RTPrintf("\nAttempting to lock medium chain from leaf image to base image\n"); + + bool fLeaf = true; + g_cImages = 0; + + do + { + ++g_cImages; + IMAGELIST *pNewEntry= new IMAGELIST(); + pNewEntry->pImage = pVDiskMedium; + CHECK_ERROR(pVDiskMedium, COMGETTER(Name)((pNewEntry->pImageName).asOutParam())); + CHECK_ERROR(pVDiskMedium, COMGETTER(Location)((pNewEntry->pImagePath).asOutParam())); + + if (VERBOSE) + RTPrintf(" %s", CSTR(pNewEntry->pImageName)); + + if (fLeaf && g_vboximgOpts.fRW) + { + if (VERBOSE) + RTPrintf(" ... Locking for write\n"); + CHECK_ERROR_RET(pVDiskMedium, LockWrite((pNewEntry->pLockToken).asOutParam()), hrc); + pNewEntry->fWriteable = true; + } + else + { + if (VERBOSE) + RTPrintf(" ... Locking for read\n"); + CHECK_ERROR_RET(pVDiskMedium, LockRead((pNewEntry->pLockToken).asOutParam()), hrc); + } + + IMAGELIST *pCurImageEntry = &listHeadLockList; + while (pCurImageEntry->next) + pCurImageEntry = pCurImageEntry->next; + pCurImageEntry->next = pNewEntry; + pNewEntry->prev = pCurImageEntry; + listHeadLockList.prev = pNewEntry; + + CHECK_ERROR(pVDiskMedium, COMGETTER(Parent)(pVDiskMedium.asOutParam())); + fLeaf = false; + } + while(pVDiskMedium); + } + + ComPtr<IMedium> pVDiskBaseMedium = listHeadLockList.prev->pImage; + Bstr pVDiskBaseImagePath = listHeadLockList.prev->pImagePath; + Bstr pVDiskBaseImageName = listHeadLockList.prev->pImageName; + + g_pszBaseImagePath = RTStrDup((char *)CSTR(pVDiskBaseImagePath)); + g_pszBaseImageName = RTStrDup((char *)CSTR(pVDiskBaseImageName)); + + g_pszImagePath = RTStrDup((char *)CSTR(listHeadLockList.next->pImagePath)); + g_pszImageName = RTStrDup((char *)CSTR(listHeadLockList.next->pImageName)); + + /* + * Attempt to VDOpen media (base and any snapshots), handling encryption, + * if that property is set for base media + */ + Bstr pBase64EncodedKeyStore; + + hrc = pVDiskBaseMedium->GetProperty(Bstr("CRYPT/KeyStore").raw(), pBase64EncodedKeyStore.asOutParam()); + if (SUCCEEDED(hrc) && strlen(CSTR(pBase64EncodedKeyStore)) != 0) + { + RTPrintf("\nvboximgMount: Encrypted disks not supported in this version\n\n"); + return -1; + } + + +/* ***************** BEGIN IFDEF'D (STUBBED-OUT) CODE ************** */ +/* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ + +#if 0 /* The following encrypted disk related code is stubbed out until it can be finished. + * What is here is an attempt to port the VBoxSVC specific code in i_openForIO to + * a client's proximity. It is supplemented by code in vboximgCrypto.cpp and + * vboximageCrypt.h that was lifed from SecretKeyStore.cpp along with the setup + * task function. + * + * The ultimate solution may be to use a simpler but less efficient COM interface, + * or to use VD encryption interfaces and key containers entirely. The keystore + * handling/filter approach that is here may be a bumbling hybrid approach + * that is broken (trying to bridge incompatible disk encryption mechanisms) or otherwise + * doesn't make sense. */ + + Bstr pKeyId; + ComPtr<IExtPackManager> pExtPackManager; + ComPtr<IExtPack> pExtPack; + com::SafeIfaceArray<IExtPackPlugIn> pExtPackPlugIns; + + if (SUCCEEDED(rc)) + { + RTPrintf("Got GetProperty(\"CRYPT/KeyStore\") = %s\n", CSTR(pBase64EncodedKeyStore)); + if (strlen(CSTR(pBase64EncodedKeyStore)) == 0) + return RTMsgErrorExitFailure("Image '%s' is configured for encryption but " + "there is no key store to retrieve the password from", CSTR(pVDiskBaseImageName)); + + SecretKeyStore keyStore(false); + RTBase64Decode(CSTR(pBase64EncodedKeyStore), &keyStore, sizeof (SecretKeyStore), NULL, NULL); + + rc = pVDiskBaseMedium->GetProperty(Bstr("CRYPT/KeyId").raw(), pKeyId.asOutParam()); + if (SUCCEEDED(rc) && strlen(CSTR(pKeyId)) == 0) + return RTMsgErrorExitFailure("Image '%s' is configured for encryption but " + "doesn't have a key identifier set", CSTR(pVDiskBaseImageName)); + + RTPrintf(" key id: %s\n", CSTR(pKeyId)); + +#ifndef VBOX_WITH_EXTPACK + return RTMsgErrorExitFailure( + "Encryption is not supported because extension pack support is not built in"); +#endif + + CHECK_ERROR(pVirtualBox, COMGETTER(ExtensionPackManager)(pExtPackManager.asOutParam())); + BOOL fExtPackUsable; + CHECK_ERROR(pExtPackManager, IsExtPackUsable((PRUnichar *)VBOX_EXTPACK, &fExtPackUsable)); + if (fExtPackUsable) + { + /* Load the PlugIn */ + + CHECK_ERROR(pExtPackManager, Find((PRUnichar *)VBOX_EXTPACK, pExtPack.asOutParam())); + if (RT_FAILURE(rc)) + return RTMsgErrorExitFailure( + "Encryption is not supported because the extension pack '%s' is missing", + VBOX_EXTPACK); + + CHECK_ERROR(pExtPack, COMGETTER(PlugIns)(ComSafeArrayAsOutParam(pExtPackPlugIns))); + + Bstr pPlugInPath; + size_t iPlugIn; + for (iPlugIn = 0; iPlugIn < pExtPackPlugIns.size(); iPlugIn++) + { + Bstr pPlugInName; + CHECK_ERROR(pExtPackPlugIns[iPlugIn], COMGETTER(Name)(pPlugInName.asOutParam())); + if (RTStrCmp(CSTR(pPlugInName), "VDPlugInCrypt") == 0) + { + CHECK_ERROR(pExtPackPlugIns[iPlugIn], COMGETTER(ModulePath)(pPlugInPath.asOutParam())); + break; + } + } + if (iPlugIn == pExtPackPlugIns.size()) + return RTMsgErrorExitFailure("Encryption is not supported because the extension pack '%s' " + "is missing the encryption PlugIn (old extension pack installed?)", VBOX_EXTPACK); + + rc = VDPluginLoadFromFilename(CSTR(pPlugInPath)); + if (RT_FAILURE(rc)) + return RTMsgErrorExitFailure("Retrieving encryption settings of the image failed " + "because the encryption PlugIn could not be loaded\n"); + } + + SecretKey *pKey = NULL; + rc = keyStore.retainSecretKey(Utf8Str(pKeyId), &pKey); + if (RT_FAILURE(rc)) + return RTMsgErrorExitFailure( + "Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)", + CSTR(pKeyId), rc); + + VDISKCRYPTOSETTINGS vdiskCryptoSettings, *pVDiskCryptoSettings = &vdiskCryptoSettings; + + vboxImageCryptoSetup(pVDiskCryptoSettings, NULL, + (const char *)CSTR(pBase64EncodedKeyStore), (const char *)pKey->getKeyBuffer(), false); + + rc = VDFilterAdd(g_pVDisk, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pVDiskCryptoSettings->vdFilterIfaces); + keyStore.releaseSecretKey(Utf8Str(pKeyId)); + + if (rc == VERR_VD_PASSWORD_INCORRECT) + return RTMsgErrorExitFailure("The password to decrypt the image is incorrect"); + + if (RT_FAILURE(rc)) + return RTMsgErrorExitFailure("Failed to load the decryption filter: %Rrc", rc); + } +#endif + +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ +/* **************** END IFDEF'D (STUBBED-OUT) CODE ***************** */ + + int vrc = RTCritSectInit(&g_vdioLock); + if (RT_SUCCESS(vrc)) + { + g_VDIfThreadSync.pfnStartRead = vboximgThreadStartRead; + g_VDIfThreadSync.pfnFinishRead = vboximgThreadFinishRead; + g_VDIfThreadSync.pfnStartWrite = vboximgThreadStartWrite; + g_VDIfThreadSync.pfnFinishWrite = vboximgThreadFinishWrite; + vrc = VDInterfaceAdd(&g_VDIfThreadSync.Core, "vboximg_ThreadSync", VDINTERFACETYPE_THREADSYNC, + &g_vdioLock, sizeof(VDINTERFACETHREADSYNC), &g_pVdIfs); + } + else + return RTMsgErrorExitFailure("ERROR: Failed to create critsects " + "for virtual disk I/O, rc=%Rrc\n", vrc); + + /* + * Create HDD container to open base image and differencing images into + */ + vrc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/, + CSTR(pVDiskBaseImagePath), VDTYPE_INVALID, &pszFormat, &enmType); + + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure("VDGetFormat(,,%s,,) " + "failed (during HDD container creation), rc=%Rrc\n", g_pszImagePath, vrc); + + if (VERBOSE) + RTPrintf("\nCreating container for base image of format %s\n", pszFormat); + + PVDISK pVDisk = NULL; + vrc = VDCreate(g_pVdIfs, enmType, &pVDisk); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure("ERROR: Couldn't create virtual disk container\n"); + + /* Open all virtual disk media from leaf snapshot (if any) to base image*/ + + if (VERBOSE) + RTPrintf("\nOpening medium chain\n"); + + IMAGELIST *pCurMedium = listHeadLockList.prev; /* point to base image */ + while (pCurMedium != &listHeadLockList) + { + if (VERBOSE) + RTPrintf(" Open: %s\n", CSTR(pCurMedium->pImagePath)); + + vrc = VDOpen(pVDisk, + pszFormat, + CSTR(pCurMedium->pImagePath), + pCurMedium->fWriteable ? 0 : VD_OPEN_FLAGS_READONLY, + g_pVdIfs); + + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure("Could not open the medium storage unit '%s' %Rrc", + CSTR(pCurMedium->pImagePath), vrc); + + pCurMedium = pCurMedium->prev; + } + + RTStrFree(pszFormat); + + /* Create the VFS file to use for the disk image access. */ + vrc = VDCreateVfsFileFromDisk(pVDisk, VD_VFSFILE_DESTROY_ON_RELEASE, &g_hVfsFileDisk); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure("Error creating VFS file wrapper for disk image\n"); + + g_cbSector = VDGetSectorSize(pVDisk, VD_LAST_IMAGE); + + vrc = vboxImgMntVolumesSetup(); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure("Error parsing volumes on disk\n"); + + if (g_vboximgOpts.fList) + { + if (g_hVfsFileDisk == NIL_RTVFSFILE) + return RTMsgErrorExitFailure("No valid --image to list partitions from\n"); + + RTPrintf("\n"); + vboxImgMntVolumesDisplay(); + return VINF_SUCCESS; /** @todo r=andy Re-visit this. */ + } + + /* Try to "mount" supported filesystems inside the disk image if specified. */ + if (g_vboximgOpts.fGstFs) + { + for (uint32_t i = 0; i < g_cVolumes; i++) + { + vrc = RTVfsMountVol(g_paVolumes[i].hVfsFileVol, + g_vboximgOpts.fRW ? 0 : RTVFSMNT_F_READ_ONLY, + &g_paVolumes[i].hVfsRoot, + NULL); + if (RT_SUCCESS(vrc)) + { + vrc = RTVfsOpenRoot(g_paVolumes[i].hVfsRoot, &g_paVolumes[i].hVfsDirRoot); + if (RT_FAILURE(vrc)) + { + RTPrintf("\nvboximg-mount: Failed to access filesystem on volume %u, ignoring\n", i); + RTVfsRelease(g_paVolumes[i].hVfsRoot); + g_paVolumes[i].hVfsRoot = NIL_RTVFS; + } + } + else + RTPrintf("\nvboximg-mount: Failed to access filesystem on volume %u, ignoring\n", i); + } + } + + /* + * Hand control over to libfuse. + */ + if (VERBOSE) + RTPrintf("\nvboximg-mount: Going into background...\n"); + + int rc = fuse_main_real(args->argc, args->argv, &g_vboximgOps, sizeof(g_vboximgOps), NULL); + RTPrintf("vboximg-mount: fuse_main -> %d\n", rc); + + int rc2 = RTVfsFileRelease(g_hVfsFileDisk); + AssertRC(rc2); + + return vrc; +} + + +int main(int argc, char **argv) +{ + + int rc = RTR3InitExe(argc, &argv, 0); + if (RT_FAILURE(rc)) + return RTMsgErrorExitFailure("RTR3InitExe failed, rc=%Rrc\n", rc); + + rc = VDInit(); + if (RT_FAILURE(rc)) + return RTMsgErrorExitFailure("VDInit failed, rc=%Rrc\n", rc); + + rc = RTFuseLoadLib(); + if (RT_FAILURE(rc)) + return RTMsgErrorExitFailure("Failed to load the fuse library, rc=%Rrc\n", rc); + + memset(&g_vboximgOps, 0, sizeof(g_vboximgOps)); + g_vboximgOps.open = vboximgOp_open; + g_vboximgOps.read = vboximgOp_read; + g_vboximgOps.write = vboximgOp_write; + g_vboximgOps.getattr = vboximgOp_getattr; + g_vboximgOps.release = vboximgOp_release; + g_vboximgOps.readdir = vboximgOp_readdir; + g_vboximgOps.readlink = vboximgOp_readlink; + + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + memset(&g_vboximgOpts, 0, sizeof(g_vboximgOpts)); + + rc = fuse_opt_parse(&args, &g_vboximgOpts, vboximgOptDefs, vboximgOptHandler); + if (rc < 0 || argc < 2 || RTStrCmp(argv[1], "-?" ) == 0 || g_vboximgOpts.fBriefUsage) + { + briefUsage(); + return 0; + } + + if (g_vboximgOpts.fAllowRoot) + fuse_opt_add_arg(&args, "-oallow_root"); + + /* + * FUSE doesn't seem to like combining options with one hyphen, as traditional UNIX + * command line utilities allow. The following flags, fWideList and fVerboseList, + * and their respective option definitions give the appearance of combined opts, + * so that -vl, -lv, -wl, -lw options are allowed, since those in particular would + * tend to conveniently facilitate some of the most common use cases. + */ + if (g_vboximgOpts.fWideList) + { + g_vboximgOpts.fWide = true; + g_vboximgOpts.fList = true; + } + if (g_vboximgOpts.fVerboseList) + { + g_vboximgOpts.fVerbose = true; + g_vboximgOpts.fList = true; + } + if (g_vboximgOpts.fAllowRoot) + fuse_opt_add_arg(&args, "-oallow_root"); + + if ( !g_vboximgOpts.pszImageUuidOrPath + || !RTVfsChainIsSpec(g_vboximgOpts.pszImageUuidOrPath)) + return vboxImgMntImageSetup(&args); + + /* Mount the VFS chain. */ + RTVFSOBJ hVfsObj; + rc = RTVfsChainOpenObj(g_vboximgOpts.pszImageUuidOrPath, RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, + RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK, + &hVfsObj, NULL, NULL); + if ( RT_SUCCESS(rc) + && RTVfsObjGetType(hVfsObj) == RTVFSOBJTYPE_VFS) + { + g_paVolumes = (PVBOXIMGMOUNTVOL)RTMemAllocZ(sizeof(*g_paVolumes)); + if (RT_LIKELY(g_paVolumes)) + { + g_cVolumes = 1; + g_paVolumes[0].hVfsRoot = RTVfsObjToVfs(hVfsObj); + g_paVolumes[0].hVfsFileVol = NIL_RTVFSFILE; + RTVfsObjRelease(hVfsObj); + + rc = RTVfsOpenRoot(g_paVolumes[0].hVfsRoot, &g_paVolumes[0].hVfsDirRoot); + if (RT_SUCCESS(rc)) + { + /* + * Hand control over to libfuse. + */ + if (VERBOSE) + RTPrintf("\nvboximg-mount: Going into background...\n"); + + rc = fuse_main_real(args.argc, args.argv, &g_vboximgOps, sizeof(g_vboximgOps), NULL); + RTVfsDirRelease(g_paVolumes[0].hVfsDirRoot); + RTVfsRelease(g_paVolumes[0].hVfsRoot); + } + + RTMemFree(g_paVolumes); + g_paVolumes = NULL; + g_cVolumes = 0; + } + else + rc = VERR_NO_MEMORY; + + RTVfsObjRelease(hVfsObj); + } + + return rc; +} + diff --git a/src/VBox/ImageMounter/vboximg-mount/vboximgCrypto.cpp b/src/VBox/ImageMounter/vboximg-mount/vboximgCrypto.cpp new file mode 100644 index 00000000..0ac487b5 --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/vboximgCrypto.cpp @@ -0,0 +1,422 @@ +/* $Id: vboximgCrypto.cpp $ */ + +/** @file + * vboximgCypto.cpp - Disk Image Flattening FUSE Program. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include <iprt/cdefs.h> +#include <VBox/err.h> +#include <VBox/settings.h> +#include <VBox/vd.h> +#include "vboximgCrypto.h" +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/memsafer.h> + +/* + * Apparently there is a more COM:: oriented (but less efficient?) approach to dealing + * with the keystore and disk encryption, which will need to be investigated. Keeping + * all this duplicated code in a separate file until the ideal approach is determined. + */ +SecretKey::SecretKey(const uint8_t *pbKey, size_t cbKey, bool fKeyBufNonPageable) +{ + m_cRefs = 0; + m_fRemoveOnSuspend = false; + m_cUsers = 0; + m_cbKey = cbKey; + + int rc = RTMemSaferAllocZEx((void **)&this->m_pbKey, cbKey, + fKeyBufNonPageable ? RTMEMSAFER_F_REQUIRE_NOT_PAGABLE : 0); + if (RT_SUCCESS(rc)) + { + memcpy(this->m_pbKey, pbKey, cbKey); + + /* Scramble content to make retrieving the key more difficult. */ + rc = RTMemSaferScramble(this->m_pbKey, cbKey); + } + else + throw rc; +} + +SecretKey::~SecretKey() +{ + Assert(!m_cRefs); + + RTMemSaferFree(m_pbKey, m_cbKey); + m_cRefs = 0; + m_pbKey = NULL; + m_cbKey = 0; + m_fRemoveOnSuspend = false; + m_cUsers = 0; +} + +uint32_t SecretKey::retain() +{ + uint32_t cRefs = ASMAtomicIncU32(&m_cRefs); + if (cRefs == 1) + { + int rc = RTMemSaferUnscramble(m_pbKey, m_cbKey); + AssertRC(rc); + } + + return cRefs; +} + +uint32_t SecretKey::release() +{ + uint32_t cRefs = ASMAtomicDecU32(&m_cRefs); + if (!cRefs) + { + int rc = RTMemSaferScramble(m_pbKey, m_cbKey); + AssertRC(rc); + } + + return cRefs; +} + +uint32_t SecretKey::refCount() +{ + return m_cRefs; +} + +int SecretKey::setUsers(uint32_t cUsers) +{ + m_cUsers = cUsers; + return VINF_SUCCESS; +} + +uint32_t SecretKey::getUsers() +{ + return m_cUsers; +} + +int SecretKey::setRemoveOnSuspend(bool fRemoveOnSuspend) +{ + m_fRemoveOnSuspend = fRemoveOnSuspend; + return VINF_SUCCESS; +} + +bool SecretKey::getRemoveOnSuspend() +{ + return m_fRemoveOnSuspend; +} + +const void *SecretKey::getKeyBuffer() +{ + AssertReturn(m_cRefs > 0, NULL); + return m_pbKey; +} + +size_t SecretKey::getKeySize() +{ + return m_cbKey; +} + +SecretKeyStore::SecretKeyStore(bool fKeyBufNonPageable) +{ + m_fKeyBufNonPageable = fKeyBufNonPageable; +} + +SecretKeyStore::~SecretKeyStore() +{ + int rc = deleteAllSecretKeys(false /* fSuspend */, true /* fForce */); + AssertRC(rc); +} + +int SecretKeyStore::addSecretKey(const com::Utf8Str &strKeyId, const uint8_t *pbKey, size_t cbKey) +{ + /* Check that the ID is not existing already. */ + SecretKeyMap::const_iterator it = m_mapSecretKeys.find(strKeyId); + if (it != m_mapSecretKeys.end()) + return VERR_ALREADY_EXISTS; + + SecretKey *pKey = NULL; + try + { + pKey = new SecretKey(pbKey, cbKey, m_fKeyBufNonPageable); + + m_mapSecretKeys.insert(std::make_pair(strKeyId, pKey)); + } + catch (int rc) + { + return rc; + } + catch (std::bad_alloc &) + { + if (pKey) + delete pKey; + return VERR_NO_MEMORY; + } + + return VINF_SUCCESS; +} + +int SecretKeyStore::deleteSecretKey(const com::Utf8Str &strKeyId) +{ + SecretKeyMap::iterator it = m_mapSecretKeys.find(strKeyId); + if (it == m_mapSecretKeys.end()) + return VERR_NOT_FOUND; + + SecretKey *pKey = it->second; + if (pKey->refCount() != 0) + return VERR_RESOURCE_IN_USE; + + m_mapSecretKeys.erase(it); + delete pKey; + + return VINF_SUCCESS; +} + +int SecretKeyStore::retainSecretKey(const com::Utf8Str &strKeyId, SecretKey **ppKey) +{ + SecretKeyMap::const_iterator it = m_mapSecretKeys.find(strKeyId); + if (it == m_mapSecretKeys.end()) + return VERR_NOT_FOUND; + + SecretKey *pKey = it->second; + pKey->retain(); + + *ppKey = pKey; + + return VINF_SUCCESS; +} + +int SecretKeyStore::releaseSecretKey(const com::Utf8Str &strKeyId) +{ + SecretKeyMap::const_iterator it = m_mapSecretKeys.find(strKeyId); + if (it == m_mapSecretKeys.end()) + return VERR_NOT_FOUND; + + SecretKey *pKey = it->second; + pKey->release(); + return VINF_SUCCESS; +} + +int SecretKeyStore::deleteAllSecretKeys(bool fSuspend, bool fForce) +{ + /* First check whether a key is still in use. */ + if (!fForce) + { + for (SecretKeyMap::iterator it = m_mapSecretKeys.begin(); + it != m_mapSecretKeys.end(); + ++it) + { + SecretKey *pKey = it->second; + if ( pKey->refCount() + && ( ( pKey->getRemoveOnSuspend() + && fSuspend) + || !fSuspend)) + return VERR_RESOURCE_IN_USE; + } + } + + SecretKeyMap::iterator it = m_mapSecretKeys.begin(); + while (it != m_mapSecretKeys.end()) + { + SecretKey *pKey = it->second; + if ( pKey->getRemoveOnSuspend() + || !fSuspend) + { + AssertMsg(!pKey->refCount(), ("No one should access the stored key at this point anymore!\n")); + delete pKey; + SecretKeyMap::iterator itNext = it; + ++itNext; + m_mapSecretKeys.erase(it); + it = itNext; + } + else + ++it; + } + + return VINF_SUCCESS; +} + +void vboxImageCryptoSetup(VDISKCRYPTOSETTINGS *pSettings, const char *pszCipher, + const char *pszKeyStore, const char *pszPassword, + bool fCreateKeyStore) +{ + pSettings->pszCipher = pszCipher; + pSettings->pszPassword = pszPassword; + pSettings->pszKeyStoreLoad = pszKeyStore; + pSettings->fCreateKeyStore = fCreateKeyStore; + pSettings->pbDek = NULL; + pSettings->cbDek = 0; + pSettings->vdFilterIfaces = NULL; + + pSettings->vdIfCfg.pfnAreKeysValid = vboximgVdCryptoConfigAreKeysValid; + pSettings->vdIfCfg.pfnQuerySize = vboximgVdCryptoConfigQuerySize; + pSettings->vdIfCfg.pfnQuery = vboximgVdCryptoConfigQuery; + pSettings->vdIfCfg.pfnQueryBytes = NULL; + + pSettings->vdIfCrypto.pfnKeyRetain = vboximgVdCryptoKeyRetain; + pSettings->vdIfCrypto.pfnKeyRelease = vboximgVdCryptoKeyRelease; + pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = vboximgVdCryptoKeyStorePasswordRetain; + pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = vboximgVdCryptoKeyStorePasswordRelease; + pSettings->vdIfCrypto.pfnKeyStoreSave = vboximgVdCryptoKeyStoreSave; + pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = vboximgVdCryptoKeyStoreReturnParameters; + + int rc = VDInterfaceAdd(&pSettings->vdIfCfg.Core, + "vboximgVdInterfaceCfgCrypto", + VDINTERFACETYPE_CONFIG, pSettings, + sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces); + AssertRC(rc); + + rc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core, + "vboximgVdInterfaceCrypto", + VDINTERFACETYPE_CRYPTO, pSettings, + sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces); + AssertRC(rc); +} + +DECLCALLBACK(bool) vboximgVdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid) +{ + /* Just return always true here. */ + NOREF(pvUser); + NOREF(pszzValid); + return true; +} + +DECLCALLBACK(int) vboximgVdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue) +{ + VDISKCRYPTOSETTINGS *pSettings = (VDISKCRYPTOSETTINGS *)pvUser; + AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE); + AssertPtrReturn(pcbValue, VERR_INVALID_POINTER); + + size_t cbValue = 0; + if (!strcmp(pszName, "Algorithm")) + cbValue = strlen(pSettings->pszCipher) + 1; + else if (!strcmp(pszName, "KeyId")) + cbValue = sizeof("irrelevant"); + else if (!strcmp(pszName, "KeyStore")) + { + if (!pSettings->pszKeyStoreLoad) + return VERR_CFGM_VALUE_NOT_FOUND; + cbValue = strlen(pSettings->pszKeyStoreLoad) + 1; + } + else if (!strcmp(pszName, "CreateKeyStore")) + cbValue = 2; /* Single digit + terminator. */ + else + return VERR_CFGM_VALUE_NOT_FOUND; + + *pcbValue = cbValue + 1 /* include terminator */; + + return VINF_SUCCESS; +} + +DECLCALLBACK(int) vboximgVdCryptoConfigQuery(void *pvUser, const char *pszName, + char *pszValue, size_t cchValue) +{ + VDISKCRYPTOSETTINGS *pSettings = (VDISKCRYPTOSETTINGS *)pvUser; + AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE); + AssertPtrReturn(pszValue, VERR_INVALID_POINTER); + + const char *psz = NULL; + if (!strcmp(pszName, "Algorithm")) + psz = pSettings->pszCipher; + else if (!strcmp(pszName, "KeyId")) + psz = "irrelevant"; + else if (!strcmp(pszName, "KeyStore")) + psz = pSettings->pszKeyStoreLoad; + else if (!strcmp(pszName, "CreateKeyStore")) + { + if (pSettings->fCreateKeyStore) + psz = "1"; + else + psz = "0"; + } + else + return VERR_CFGM_VALUE_NOT_FOUND; + + size_t cch = strlen(psz); + if (cch >= cchValue) + return VERR_CFGM_NOT_ENOUGH_SPACE; + + memcpy(pszValue, psz, cch + 1); + return VINF_SUCCESS; +} + +DECLCALLBACK(int) vboximgVdCryptoKeyRetain(void *pvUser, const char *pszId, + const uint8_t **ppbKey, size_t *pcbKey) +{ + VDISKCRYPTOSETTINGS *pSettings = (VDISKCRYPTOSETTINGS *)pvUser; + NOREF(pszId); + NOREF(ppbKey); + NOREF(pcbKey); + AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE); + AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE); +} + +DECLCALLBACK(int) vboximgVdCryptoKeyRelease(void *pvUser, const char *pszId) +{ + VDISKCRYPTOSETTINGS *pSettings = (VDISKCRYPTOSETTINGS *)pvUser; + NOREF(pszId); + AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE); + AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE); +} + +DECLCALLBACK(int) vboximgVdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword) +{ + VDISKCRYPTOSETTINGS *pSettings = (VDISKCRYPTOSETTINGS *)pvUser; + AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE); + + NOREF(pszId); + *ppszPassword = pSettings->pszPassword; + return VINF_SUCCESS; +} + +DECLCALLBACK(int) vboximgVdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId) +{ + VDISKCRYPTOSETTINGS *pSettings = (VDISKCRYPTOSETTINGS *)pvUser; + AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE); + NOREF(pszId); + return VINF_SUCCESS; +} + +DECLCALLBACK(int) vboximgVdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore) +{ + VDISKCRYPTOSETTINGS *pSettings = (VDISKCRYPTOSETTINGS *)pvUser; + AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE); + + pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore); + if (!pSettings->pszKeyStore) + return VERR_NO_MEMORY; + + memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore); + return VINF_SUCCESS; +} + +DECLCALLBACK(int) vboximgVdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher, + const uint8_t *pbDek, size_t cbDek) +{ + VDISKCRYPTOSETTINGS *pSettings = (VDISKCRYPTOSETTINGS *)pvUser; + AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE); + + pSettings->pszCipherReturned = RTStrDup(pszCipher); + pSettings->pbDek = pbDek; + pSettings->cbDek = cbDek; + + return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY; +} diff --git a/src/VBox/ImageMounter/vboximg-mount/vboximgCrypto.h b/src/VBox/ImageMounter/vboximg-mount/vboximgCrypto.h new file mode 100644 index 00000000..49f44262 --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/vboximgCrypto.h @@ -0,0 +1,265 @@ +/* $Id: vboximgCrypto.h $ */ + +/** @file + * vboximgCrypto.h + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef VBOX_INCLUDED_SRC_vboximg_mount_vboximgCrypto_h +#define VBOX_INCLUDED_SRC_vboximg_mount_vboximgCrypto_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <map> + +/* + * Settings for a crypto filter instance. + */ +typedef struct VDiskCryptoSettings +{ + VDiskCryptoSettings() + : fCreateKeyStore(false), + pszPassword(NULL), + pszKeyStore(NULL), + pszKeyStoreLoad(NULL), + pbDek(NULL), + cbDek(0), + pszCipher(NULL), + pszCipherReturned(NULL) + { } + + bool fCreateKeyStore; + const char *pszPassword; + char *pszKeyStore; + const char *pszKeyStoreLoad; + + const uint8_t *pbDek; + size_t cbDek; + const char *pszCipher; + + /** The cipher returned by the crypto filter. */ + char *pszCipherReturned; + + PVDINTERFACE vdFilterIfaces; + + VDINTERFACECONFIG vdIfCfg; + VDINTERFACECRYPTO vdIfCrypto; +} VDISKCRYPTOSETTINGS; + + +class SecretKey +{ + public: + + /** + * Constructor for a secret key. + * + * @param pbKey The key buffer. + * @param cbKey Size of the key. + * @param fKeyBufNonPageable Flag whether the key buffer should be non pageable. + */ + SecretKey(const uint8_t *pbKey, size_t cbKey, bool fKeyBufNonPageable); + + /** + * Secret key destructor. + */ + ~SecretKey(); + + /** + * Increments the reference counter of the key. + * + * @returns The new reference count. + */ + uint32_t retain(); + + /** + * Releases a reference of the key. + * If the reference counter reaches 0 the key buffer might be protected + * against further access or the data will become scrambled. + * + * @returns The new reference count. + */ + uint32_t release(); + + /** + * Returns the reference count of the secret key. + */ + uint32_t refCount(); + + /** + * Sets the possible number of users for this key. + * + * @returns VBox status code. + * @param cUsers The possible number of user for this key. + */ + int setUsers(uint32_t cUsers); + + /** + * Returns the possible amount of users. + * + * @returns Possible amount of users. + */ + uint32_t getUsers(); + + /** + * Sets the remove on suspend flag. + * + * @returns VBox status code. + * @param fRemoveOnSuspend Flag whether to remove the key on host suspend. + */ + int setRemoveOnSuspend(bool fRemoveOnSuspend); + + /** + * Returns whether the key should be destroyed on suspend. + */ + bool getRemoveOnSuspend(); + + /** + * Returns the buffer to the key. + */ + const void *getKeyBuffer(); + + /** + * Returns the size of the key. + */ + size_t getKeySize(); + + private: + /** Reference counter of the key. */ + volatile uint32_t m_cRefs; + /** Key material. */ + uint8_t *m_pbKey; + /** Size of the key in bytes. */ + size_t m_cbKey; + /** Flag whether to remove the key on suspend. */ + bool m_fRemoveOnSuspend; + /** Number of entities which will use this key. */ + uint32_t m_cUsers; +}; + +class SecretKeyStore +{ + public: + + /** + * Constructor for a secret key store. + * + * @param fKeyBufNonPageable Flag whether the key buffer is required to + * be non pageable. + */ + SecretKeyStore(bool fKeyBufNonPageable); + + /** + * Destructor of a secret key store. This will free all stored secret keys + * inluding the key buffers. Make sure there no one accesses one of the keys + * stored. + */ + ~SecretKeyStore(); + + /** + * Add a secret key to the store. + * + * @returns VBox status code. + * @param strKeyId The key identifier. + * @param pbKey The key to store. + * @param cbKey Size of the key. + */ + int addSecretKey(const com::Utf8Str &strKeyId, const uint8_t *pbKey, size_t cbKey); + + /** + * Deletes a key from the key store associated with the given identifier. + * + * @returns VBox status code. + * @param strKeyId The key identifier. + */ + int deleteSecretKey(const com::Utf8Str &strKeyId); + + /** + * Returns the secret key object associated with the given identifier. + * This increments the reference counter of the secret key object. + * + * @returns VBox status code. + * @param strKeyId The key identifier. + * @param ppKey Where to store the secret key object on success. + */ + int retainSecretKey(const com::Utf8Str &strKeyId, SecretKey **ppKey); + + /** + * Releases a reference to the secret key object. + * + * @returns VBox status code. + * @param strKeyId The key identifier. + */ + int releaseSecretKey(const com::Utf8Str &strKeyId); + + /** + * Deletes all secret keys from the key store. + * + * @returns VBox status code. + * @param fSuspend Flag whether to delete only keys which are + * marked for deletion during a suspend. + * @param fForce Flag whether to force deletion if some keys + * are still in use. Otherwise an error is returned. + */ + int deleteAllSecretKeys(bool fSuspend, bool fForce); + + private: + + typedef std::map<com::Utf8Str, SecretKey *> SecretKeyMap; + + /** The map to map key identifers to secret keys. */ + SecretKeyMap m_mapSecretKeys; + /** Flag whether key buffers should be non pagable. */ + bool m_fKeyBufNonPageable; +}; + +void vboxImageCryptoSetup(VDISKCRYPTOSETTINGS *pSettings, const char *pszCipher, + const char *pszKeyStore, const char *pszPassword, + bool fCreateKeyStore); + +DECLCALLBACK(bool) vboximgVdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid); + +DECLCALLBACK(int) vboximgVdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue); + +DECLCALLBACK(int) vboximgVdCryptoConfigQuery(void *pvUser, const char *pszName, + char *pszValue, size_t cchValue); + +DECLCALLBACK(int) vboximgVdCryptoKeyRetain(void *pvUser, const char *pszId, + const uint8_t **ppbKey, size_t *pcbKey); + +DECLCALLBACK(int) vboximgVdCryptoKeyRelease(void *pvUser, const char *pszId); + +DECLCALLBACK(int) vboximgVdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword); + +DECLCALLBACK(int) vboximgVdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId); + +DECLCALLBACK(int) vboximgVdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore); + +DECLCALLBACK(int) vboximgVdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher, + const uint8_t *pbDek, size_t cbDek); + + +#endif /* !VBOX_INCLUDED_SRC_vboximg_mount_vboximgCrypto_h */ + diff --git a/src/VBox/ImageMounter/vboximg-mount/vboximgMedia.cpp b/src/VBox/ImageMounter/vboximg-mount/vboximgMedia.cpp new file mode 100644 index 00000000..bb49867c --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/vboximgMedia.cpp @@ -0,0 +1,409 @@ +/* $Id: vboximgMedia.cpp $ */ +/** @file + * vboximgMedia.cpp - Disk Image Flattening FUSE Program. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include <VirtualBox_XPCOM.h> +#include <VBox/com/VirtualBox.h> +#include <VBox/vd.h> +#include <VBox/vd-ifs.h> +#include <VBox/log.h> +#include <iprt/errcore.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/NativeEventQueue.h> +#include <VBox/com/com.h> +#include <VBox/com/string.h> +#include <VBox/com/Guid.h> +#include <VBox/com/array.h> +#include <VBox/com/errorprint.h> +#include <VBox/vd-plugin.h> +#include <iprt/initterm.h> +#include <iprt/assert.h> +#include <iprt/message.h> +#include <iprt/critsect.h> +#include <iprt/asm.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#include <iprt/types.h> +#include <iprt/path.h> +#include <iprt/utf16.h> +#include <math.h> +#include "vboximgOpts.h" + +using namespace com; + +extern VBOXIMGOPTS g_vboximgOpts; + +#define SAFENULL(strPtr) (strPtr ? strPtr : "") /** Makes null harmless to print */ +#define CSTR(arg) Utf8Str(arg).c_str() /** Converts XPCOM string type to C string type */ +#define MAX_UUID_LEN 256 /** Max length of a UUID */ +#define VM_MAX_NAME 32 /** Max length of VM name we handle */ + +typedef struct MEDIUMINFO +{ + char *pszName; + char *pszUuid; + char *pszBaseUuid; + char *pszPath; + char *pszDescription; + char *pszState; + char *pszType; + char *pszFormat; + bool fSnapshot; + PRInt64 cbSize; + MediumType_T type; + MediumState_T state; + ~MEDIUMINFO() + { + RTMemFree(pszName); + RTMemFree(pszUuid); + RTMemFree(pszPath); + RTMemFree(pszDescription); + RTMemFree(pszFormat); + } +} MEDIUMINFO; + + +char *vboximgScaledSize(size_t size) +{ + uint64_t exp = 0; + if (size > 0) + exp = log2((double)size); + char scaledMagnitude = ((char []){ ' ', 'K', 'M', 'G', 'T', 'P' })[exp / 10]; + /* This workaround is because IPRT RT*Printf* funcs don't handle floating point format specifiers */ + double cbScaled = (double)size / pow(2, (double)(((uint64_t)(exp / 10)) * 10)); + uint64_t intPart = cbScaled; + uint64_t fracPart = (cbScaled - (double)intPart) * 10; + char tmp[256]; + RTStrPrintf(tmp, sizeof (tmp), "%d.%d%c", intPart, fracPart, scaledMagnitude); + return RTStrDup(tmp); +} + +static int getMediumInfo(IMachine *pMachine, IMedium *pMedium, MEDIUMINFO **ppMediumInfo) +{ + RT_NOREF(pMachine); + + MEDIUMINFO *info = new MEDIUMINFO(); + *ppMediumInfo = info; + + Bstr name; + Bstr uuid; + Bstr baseUuid; + Bstr path; + Bstr description; + Bstr format; + PRInt64 *pSize = &info->cbSize; + ComPtr<IMedium> pBase; + MediumType_T *pType = &info->type; + MediumState_T *pState = &info->state; + + *pState = MediumState_NotCreated; + + HRESULT hrc; + + CHECK_ERROR(pMedium, RefreshState(pState)); + CHECK_ERROR(pMedium, COMGETTER(Id)(uuid.asOutParam())); + CHECK_ERROR(pMedium, COMGETTER(Base)(pBase.asOutParam())); + CHECK_ERROR(pBase, COMGETTER(Id)(baseUuid.asOutParam())); + + CHECK_ERROR(pMedium, COMGETTER(State)(pState)); + + CHECK_ERROR(pMedium, COMGETTER(Location)(path.asOutParam())); + CHECK_ERROR(pMedium, COMGETTER(Format)(format.asOutParam())); + CHECK_ERROR(pMedium, COMGETTER(Type)(pType)); + CHECK_ERROR(pMedium, COMGETTER(Size)(pSize)); + + info->pszUuid = RTStrDup((char *)CSTR(uuid)); + info->pszBaseUuid = RTStrDup((char *)CSTR(baseUuid)); + info->pszPath = RTStrDup((char *)CSTR(path)); + info->pszFormat = RTStrDup((char *)CSTR(format)); + info->fSnapshot = RTStrCmp(CSTR(uuid), CSTR(baseUuid)) != 0; + + if (info->fSnapshot) + { + /** @todo Determine the VM snapshot this and set name and description + * to the snapshot name/description + */ + CHECK_ERROR(pMedium, COMGETTER(Name)(name.asOutParam())); + CHECK_ERROR(pMedium, COMGETTER(Description)(description.asOutParam())); + } + else + { + CHECK_ERROR(pMedium, COMGETTER(Name)(name.asOutParam())); + CHECK_ERROR(pMedium, COMGETTER(Description)(description.asOutParam())); + } + + info->pszName = RTStrDup((char *)CSTR(name)); + info->pszDescription = RTStrDup((char *)CSTR(description)); + + switch(*pType) + { + case MediumType_Normal: + info->pszType = (char *)"normal"; + break; + case MediumType_Immutable: + info->pszType = (char *)"immutable"; + break; + case MediumType_Writethrough: + info->pszType = (char *)"writethrough"; + break; + case MediumType_Shareable: + info->pszType = (char *)"shareable"; + break; + case MediumType_Readonly: + info->pszType = (char *)"readonly"; + break; + case MediumType_MultiAttach: + info->pszType = (char *)"multiattach"; + break; + default: + info->pszType = (char *)"?"; + } + + switch(*pState) + { + case MediumState_NotCreated: + info->pszState = (char *)"uncreated"; + break; + case MediumState_Created: + info->pszState = (char *)"created"; + break; + case MediumState_LockedRead: + info->pszState = (char *)"rlock"; + break; + case MediumState_LockedWrite: + info->pszState = (char *)"wlock"; + break; + case MediumState_Inaccessible: + info->pszState = (char *)"no access"; + break; + case MediumState_Creating: + info->pszState = (char *)"creating"; + break; + case MediumState_Deleting: + info->pszState = (char *)"deleting"; + break; + default: + info->pszState = (char *)"?"; + } + return VINF_SUCCESS; +} + +static void displayMediumInfo(MEDIUMINFO *pInfo, int nestLevel, bool fLast) +{ + char *pszSzScaled = vboximgScaledSize(pInfo->cbSize); + int cPad = nestLevel * 2; + if (g_vboximgOpts.fWide && !g_vboximgOpts.fVerbose) + { + RTPrintf("%3s %-*s %7s %-9s %9s %-*s %s\n", + !fLast ? (pInfo->fSnapshot ? " | " : " +-") : (pInfo->fSnapshot ? " " : " +-"), + VM_MAX_NAME, pInfo->fSnapshot ? "+- <snapshot>" : pInfo->pszName, + pszSzScaled, + pInfo->pszFormat, + pInfo->pszState, + cPad, "", pInfo->pszUuid); + } + else + { + if (!pInfo->fSnapshot) + { + RTPrintf(" Image: %s\n", pInfo->pszName); + if (pInfo->pszDescription && RTStrNLen(pInfo->pszDescription, 256) > 0) + RTPrintf("Desc: %s\n", pInfo->pszDescription); + RTPrintf(" UUID: %s\n", pInfo->pszUuid); + if (g_vboximgOpts.fVerbose) + { + RTPrintf(" Path: %s\n", pInfo->pszPath); + RTPrintf(" Format: %s\n", pInfo->pszFormat); + RTPrintf(" Size: %s\n", pszSzScaled); + RTPrintf(" State: %s\n", pInfo->pszState); + RTPrintf(" Type: %s\n", pInfo->pszType); + } + RTPrintf("\n"); + } + else + { + RTPrintf(" Snapshot: %s\n", pInfo->pszUuid); + if (g_vboximgOpts.fVerbose) + { + RTPrintf(" Name: %s\n", pInfo->pszName); + RTPrintf(" Desc: %s\n", pInfo->pszDescription); + } + RTPrintf(" Size: %s\n", pszSzScaled); + if (g_vboximgOpts.fVerbose) + RTPrintf(" Path: %s\n", pInfo->pszPath); + RTPrintf("\n"); + } + } + RTMemFree(pszSzScaled); +} + +static int vboximgListBranch(IMachine *pMachine, IMedium *pMedium, uint8_t nestLevel, bool fLast) +{ + MEDIUMINFO *pMediumInfo; + int vrc = getMediumInfo(pMachine, pMedium, &pMediumInfo); + if (RT_FAILURE(vrc)) + return vrc; + + displayMediumInfo(pMediumInfo, nestLevel, fLast); + + HRESULT hrc; + com::SafeIfaceArray<IMedium> pChildren; + CHECK_ERROR_RET(pMedium, COMGETTER(Children)(ComSafeArrayAsOutParam(pChildren)), VERR_NOT_FOUND); /** @todo r=andy Find a better rc. */ + + for (size_t i = 0; i < pChildren.size(); i++) + vboximgListBranch(pMachine, pChildren[i], nestLevel + 1, fLast); + + delete pMediumInfo; + + return VINF_SUCCESS; +} + +static int listMedia(IVirtualBox *pVirtualBox, IMachine *pMachine, char *vmName, char *vmUuid) +{ + RT_NOREF(pVirtualBox); + RT_NOREF(vmName); + RT_NOREF(vmUuid); + + int vrc = VINF_SUCCESS; + + com::SafeIfaceArray<IMediumAttachment> pMediumAttachments; + + HRESULT hrc; + CHECK_ERROR(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(pMediumAttachments))); + + for (size_t i = 0; i < pMediumAttachments.size(); i++) + { + bool fLast = (i == pMediumAttachments.size() - 1); + DeviceType_T deviceType; + + CHECK_ERROR(pMediumAttachments[i], COMGETTER(Type)(&deviceType)); + if (deviceType != DeviceType_HardDisk) + continue; + + ComPtr<IMedium> pMedium; + CHECK_ERROR(pMediumAttachments[i], COMGETTER(Medium)(pMedium.asOutParam())); + + ComPtr<IMedium> pBase; + CHECK_ERROR(pMedium, COMGETTER(Base)(pBase.asOutParam())); + if (g_vboximgOpts.fWide && !g_vboximgOpts.fVerbose) + RTPrintf(" |\n"); + else + RTPrintf("\n"); + + vrc = vboximgListBranch(pMachine, pBase, 0, fLast); + if (RT_FAILURE(vrc)) + { + RTPrintf("vboximgListBranch failed with %Rrc\n", vrc); + break; + } + } + + return vrc; +} +/** + * Display all registered VMs on the screen with some information about each + * + * @param virtualBox VirtualBox instance object. + */ +int vboximgListVMs(IVirtualBox *pVirtualBox) +{ + HRESULT hrc; + com::SafeIfaceArray<IMachine> pMachines; + CHECK_ERROR(pVirtualBox, COMGETTER(Machines)(ComSafeArrayAsOutParam(pMachines))); + + if (g_vboximgOpts.fWide) + { + RTPrintf("\n"); + RTPrintf("VM Image Size Type State UUID (hierarchy)\n"); + } + + int vrc = VINF_SUCCESS; + + for (size_t i = 0; i < pMachines.size(); ++i) + { + ComPtr<IMachine> pMachine = pMachines[i]; + if (pMachine) + { + BOOL fAccessible; + CHECK_ERROR(pMachines[i], COMGETTER(Accessible)(&fAccessible)); + if (fAccessible) + { + Bstr machineName; + Bstr machineUuid; + Bstr description; + Bstr machineLocation; + + CHECK_ERROR(pMachine, COMGETTER(Name)(machineName.asOutParam())); + CHECK_ERROR(pMachine, COMGETTER(Id)(machineUuid.asOutParam())); + CHECK_ERROR(pMachine, COMGETTER(Description)(description.asOutParam())); + CHECK_ERROR(pMachine, COMGETTER(SettingsFilePath)(machineLocation.asOutParam())); + + + if ( g_vboximgOpts.pszVm == NULL + || RTStrNCmp(CSTR(machineUuid), g_vboximgOpts.pszVm, MAX_UUID_LEN) == 0 + || RTStrNCmp((const char *)machineName.raw(), g_vboximgOpts.pszVm, MAX_UUID_LEN) == 0) + { + if (g_vboximgOpts.fVerbose) + { + RTPrintf("-----------------------------------------------------------------\n"); + RTPrintf("VM Name: \"%s\"\n", CSTR(machineName)); + RTPrintf("UUID: %s\n", CSTR(machineUuid)); + if (*description.raw() != '\0') + RTPrintf("Desc: %s\n", CSTR(description)); + RTPrintf("Path: %s\n", CSTR(machineLocation)); + } + else + { + if (g_vboximgOpts.fWide & !g_vboximgOpts.fVerbose) + { + RTPrintf("----------------------------------------------------------------- " + "------------------------------------\n"); + RTPrintf("%-*s %*s %s\n", VM_MAX_NAME, CSTR(machineName), 33, "", CSTR(machineUuid)); + } + else + { + RTPrintf("-----------------------------------------------------------------\n"); + RTPrintf("VM: %s\n", CSTR(machineName)); + RTPrintf("UUID: %s\n", CSTR(machineUuid)); + } + } + + int vrc2 = listMedia(pVirtualBox, pMachine, + RTStrDup(CSTR(machineName)), RTStrDup(CSTR(machineUuid))); + if (RT_SUCCESS(vrc)) + vrc = vrc2; + + RTPrintf("\n"); + } + } + } + } + + return vrc; +} + diff --git a/src/VBox/ImageMounter/vboximg-mount/vboximgMedia.h b/src/VBox/ImageMounter/vboximg-mount/vboximgMedia.h new file mode 100644 index 00000000..67ee4036 --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/vboximgMedia.h @@ -0,0 +1,50 @@ +/* $Id: vboximgMedia.h $ */ + +/** @file + * vboximgMedia.h + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef VBOX_INCLUDED_SRC_vboximg_mount_vboximgMedia_h +#define VBOX_INCLUDED_SRC_vboximg_mount_vboximgMedia_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +typedef struct MEDIUMINFO +{ + char *name; + char *uuid; + char *location; + char *description; + char *state; + char *size; + char *format; + int ro; +} MEDIUMINFO; + +int vboximgListVMs(IVirtualBox *pVirtualBox); +char *vboximgScaledSize(size_t size); + +#endif /* !VBOX_INCLUDED_SRC_vboximg_mount_vboximgMedia_h */ diff --git a/src/VBox/ImageMounter/vboximg-mount/vboximgOpts.h b/src/VBox/ImageMounter/vboximg-mount/vboximgOpts.h new file mode 100644 index 00000000..348ac337 --- /dev/null +++ b/src/VBox/ImageMounter/vboximg-mount/vboximgOpts.h @@ -0,0 +1,54 @@ + +/* $Id: vboximgOpts.h $ */ + +/** @file + * vboximgOpts.h + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef VBOX_INCLUDED_SRC_vboximg_mount_vboximgOpts_h +#define VBOX_INCLUDED_SRC_vboximg_mount_vboximgOpts_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + + +typedef struct vboximgOpts { + char *pszVm; /** optional VM UUID */ + char *pszImageUuidOrPath; /** Virtual Disk image UUID or path */ + uint32_t fListMediaLong; /** Flag to list virtual disks of all known VMs */ + uint32_t fVerboseList; /** FUSE parsing doesn't understand combined flags (-lv, -vl), so we kludge it */ + uint32_t fWideList; /** FUSE parsing doesn't understand combined flags,(-lw, -wl) so we kludge it */ + uint32_t fList; /** Flag to list virtual disks of all known VMs */ + uint32_t fListParts; /** Flag to summarily list partitions associated with pszImage */ + uint32_t fGstFs; /** Flag to try to exposes supported filesystems directly in the mountpoint inside a subdirectory */ + uint32_t fAllowRoot; /** Flag to allow root to access this FUSE FS */ + uint32_t fRW; /** Flag to allow changes to FUSE-mounted Virtual Disk image */ + uint32_t fWide; /** Flag to use wide-format list mode */ + uint32_t fBriefUsage; /** Flag to display only FS-specific program usage options */ + uint32_t fVerbose; /** Add more info to lists and operations */ +} VBOXIMGOPTS; + + +#endif /* !VBOX_INCLUDED_SRC_vboximg_mount_vboximgOpts_h */ |