diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Storage/VDPlugin.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Storage/VDPlugin.cpp')
-rw-r--r-- | src/VBox/Storage/VDPlugin.cpp | 970 |
1 files changed, 970 insertions, 0 deletions
diff --git a/src/VBox/Storage/VDPlugin.cpp b/src/VBox/Storage/VDPlugin.cpp new file mode 100644 index 00000000..8943fe6b --- /dev/null +++ b/src/VBox/Storage/VDPlugin.cpp @@ -0,0 +1,970 @@ +/* $Id: VDPlugin.cpp $ */ +/** @file + * VD - Virtual disk container implementation, plugin related bits. + */ + +/* + * Copyright (C) 2017-2022 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_VD +#include <VBox/err.h> +#include <VBox/sup.h> +#include <VBox/log.h> +#include <VBox/vd-plugin.h> + +#include <iprt/dir.h> +#include <iprt/ldr.h> +#include <iprt/mem.h> +#include <iprt/path.h> + +#include "VDInternal.h" +#include "VDBackends.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Plugin structure. + */ +typedef struct VDPLUGIN +{ + /** Pointer to the next plugin structure. */ + RTLISTNODE NodePlugin; + /** Handle of loaded plugin library. */ + RTLDRMOD hPlugin; + /** Filename of the loaded plugin. */ + char *pszFilename; +} VDPLUGIN; +/** Pointer to a plugin structure. */ +typedef VDPLUGIN *PVDPLUGIN; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS +/** Head of loaded plugin list. */ +static RTLISTANCHOR g_ListPluginsLoaded; +#endif + +/** Number of image backends supported. */ +static unsigned g_cBackends = 0; +/** Array of pointers to the image backends. */ +static PCVDIMAGEBACKEND *g_apBackends = NULL; +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS +/** Array of handles to the corresponding plugin. */ +static RTLDRMOD *g_ahBackendPlugins = NULL; +#endif +/** + * Builtin image backends. + * + * @note As long as the pfnProb() calls aren't scored, the ordering influences + * which backend take precedence. In particular, the RAW backend should + * be thowards the end of the list. + */ +static PCVDIMAGEBACKEND aStaticBackends[] = +{ + &g_VmdkBackend, + &g_VDIBackend, + &g_VhdBackend, + &g_ParallelsBackend, + &g_DmgBackend, + &g_QedBackend, + &g_QCowBackend, + &g_VhdxBackend, + &g_CueBackend, + &g_VBoxIsoMakerBackend, + &g_RawBackend, + &g_ISCSIBackend +}; + +/** Number of supported cache backends. */ +static unsigned g_cCacheBackends = 0; +/** Array of pointers to the cache backends. */ +static PCVDCACHEBACKEND *g_apCacheBackends = NULL; +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS +/** Array of handles to the corresponding plugin. + * + * @todo r=bird: This looks rather pointless. + */ +static RTLDRMOD *g_ahCacheBackendPlugins = NULL; +#endif +/** Builtin cache backends. */ +static PCVDCACHEBACKEND aStaticCacheBackends[] = +{ + &g_VciCacheBackend +}; + +/** Number of supported filter backends. */ +static unsigned g_cFilterBackends = 0; +/** Array of pointers to the filters backends. */ +static PCVDFILTERBACKEND *g_apFilterBackends = NULL; +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS +/** Array of handles to the corresponding plugin. */ +static PRTLDRMOD g_pahFilterBackendPlugins = NULL; +#endif + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + +/** + * Add an array of image format backends from the given plugin to the list of known + * image formats. + * + * @returns VBox status code. + * @param hPlugin The plugin handle the backends belong to, can be NIL_RTLDRMOD + * for compiled in backends. + * @param ppBackends The array of image backend descriptors to add. + * @param cBackends Number of descriptors in the array. + */ +static int vdAddBackends(RTLDRMOD hPlugin, PCVDIMAGEBACKEND *ppBackends, unsigned cBackends) +{ + PCVDIMAGEBACKEND *pTmp = (PCVDIMAGEBACKEND *)RTMemRealloc(g_apBackends, + (g_cBackends + cBackends) * sizeof(PCVDIMAGEBACKEND)); + if (RT_UNLIKELY(!pTmp)) + return VERR_NO_MEMORY; + g_apBackends = pTmp; + memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PCVDIMAGEBACKEND)); + +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + RTLDRMOD *pTmpPlugins = (RTLDRMOD*)RTMemRealloc(g_ahBackendPlugins, + (g_cBackends + cBackends) * sizeof(RTLDRMOD)); + if (RT_UNLIKELY(!pTmpPlugins)) + return VERR_NO_MEMORY; + g_ahBackendPlugins = pTmpPlugins; + for (unsigned i = g_cBackends; i < g_cBackends + cBackends; i++) + g_ahBackendPlugins[i] = hPlugin; +#else + RT_NOREF(hPlugin); +#endif + + g_cBackends += cBackends; + return VINF_SUCCESS; +} + + +/** + * Add an array of cache format backends from the given plugin to the list of known + * cache formats. + * + * @returns VBox status code. + * @param hPlugin The plugin handle the backends belong to, can be NIL_RTLDRMOD + * for compiled in backends. + * @param ppBackends The array of cache backend descriptors to add. + * @param cBackends Number of descriptors in the array. + */ +static int vdAddCacheBackends(RTLDRMOD hPlugin, PCVDCACHEBACKEND *ppBackends, unsigned cBackends) +{ + PCVDCACHEBACKEND *pTmp = (PCVDCACHEBACKEND*)RTMemReallocTag(g_apCacheBackends, + (g_cCacheBackends + cBackends) * sizeof(PCVDCACHEBACKEND), + "may-leak:vdAddCacheBackend"); + if (RT_UNLIKELY(!pTmp)) + return VERR_NO_MEMORY; + g_apCacheBackends = pTmp; + memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PCVDCACHEBACKEND)); + +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + RTLDRMOD *pTmpPlugins = (RTLDRMOD*)RTMemReallocTag(g_ahCacheBackendPlugins, + (g_cCacheBackends + cBackends) * sizeof(RTLDRMOD), + "may-leak:vdAddCacheBackend"); + if (RT_UNLIKELY(!pTmpPlugins)) + return VERR_NO_MEMORY; + g_ahCacheBackendPlugins = pTmpPlugins; + for (unsigned i = g_cCacheBackends; i < g_cCacheBackends + cBackends; i++) + g_ahCacheBackendPlugins[i] = hPlugin; +#else + RT_NOREF(hPlugin); +#endif + + g_cCacheBackends += cBackends; + return VINF_SUCCESS; +} + +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS +/** + * Add a single image format backend to the list of known image formats. + * + * @returns VBox status code. + * @param hPlugin The plugin handle the backend belongs to, can be NIL_RTLDRMOD + * for compiled in backends. + * @param pBackend The image backend descriptors to add. + */ +DECLINLINE(int) vdAddBackend(RTLDRMOD hPlugin, PCVDIMAGEBACKEND pBackend) +{ + return vdAddBackends(hPlugin, &pBackend, 1); +} + + +/** + * Add a single cache format backend to the list of known cache formats. + * + * @returns VBox status code. + * @param hPlugin The plugin handle the backend belongs to, can be NIL_RTLDRMOD + * for compiled in backends. + * @param pBackend The cache backend descriptors to add. + */ +DECLINLINE(int) vdAddCacheBackend(RTLDRMOD hPlugin, PCVDCACHEBACKEND pBackend) +{ + return vdAddCacheBackends(hPlugin, &pBackend, 1); +} + + +/** + * Add several filter backends. + * + * @returns VBox status code. + * @param hPlugin Plugin handle to add. + * @param ppBackends Array of filter backends to add. + * @param cBackends Number of backends to add. + */ +static int vdAddFilterBackends(RTLDRMOD hPlugin, PCVDFILTERBACKEND *ppBackends, unsigned cBackends) +{ + PCVDFILTERBACKEND *pTmp = (PCVDFILTERBACKEND *)RTMemRealloc(g_apFilterBackends, + (g_cFilterBackends + cBackends) * sizeof(PCVDFILTERBACKEND)); + if (RT_UNLIKELY(!pTmp)) + return VERR_NO_MEMORY; + g_apFilterBackends = pTmp; + +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + PRTLDRMOD pTmpPlugins = (PRTLDRMOD)RTMemRealloc(g_pahFilterBackendPlugins, + (g_cFilterBackends + cBackends) * sizeof(RTLDRMOD)); + if (RT_UNLIKELY(!pTmpPlugins)) + return VERR_NO_MEMORY; + + g_pahFilterBackendPlugins = pTmpPlugins; + memcpy(&g_apFilterBackends[g_cFilterBackends], ppBackends, cBackends * sizeof(PCVDFILTERBACKEND)); + for (unsigned i = g_cFilterBackends; i < g_cFilterBackends + cBackends; i++) + g_pahFilterBackendPlugins[i] = hPlugin; +#else + RT_NOREF(hPlugin); +#endif + + g_cFilterBackends += cBackends; + return VINF_SUCCESS; +} + + +/** + * Add a single filter backend to the list of supported filters. + * + * @returns VBox status code. + * @param hPlugin Plugin handle to add. + * @param pBackend The backend to add. + */ +DECLINLINE(int) vdAddFilterBackend(RTLDRMOD hPlugin, PCVDFILTERBACKEND pBackend) +{ + return vdAddFilterBackends(hPlugin, &pBackend, 1); +} + +/** + * @interface_method_impl{VDBACKENDREGISTER,pfnRegisterImage} + */ +static DECLCALLBACK(int) vdPluginRegisterImage(void *pvUser, PCVDIMAGEBACKEND pBackend) +{ + int rc = VINF_SUCCESS; + + if (VD_VERSION_ARE_COMPATIBLE(VD_IMGBACKEND_VERSION, pBackend->u32Version)) + vdAddBackend((RTLDRMOD)pvUser, pBackend); + else + { + LogFunc(("ignored plugin: pBackend->u32Version=%u rc=%Rrc\n", pBackend->u32Version, rc)); + rc = VERR_IGNORED; + } + + return rc; +} + +/** + * @interface_method_impl{VDBACKENDREGISTER,pfnRegisterCache} + */ +static DECLCALLBACK(int) vdPluginRegisterCache(void *pvUser, PCVDCACHEBACKEND pBackend) +{ + int rc = VINF_SUCCESS; + + if (VD_VERSION_ARE_COMPATIBLE(VD_CACHEBACKEND_VERSION, pBackend->u32Version)) + vdAddCacheBackend((RTLDRMOD)pvUser, pBackend); + else + { + LogFunc(("ignored plugin: pBackend->u32Version=%u rc=%Rrc\n", pBackend->u32Version, rc)); + rc = VERR_IGNORED; + } + + return rc; +} + +/** + * @interface_method_impl{VDBACKENDREGISTER,pfnRegisterFilter} + */ +static DECLCALLBACK(int) vdPluginRegisterFilter(void *pvUser, PCVDFILTERBACKEND pBackend) +{ + int rc = VINF_SUCCESS; + + if (VD_VERSION_ARE_COMPATIBLE(VD_FLTBACKEND_VERSION, pBackend->u32Version)) + vdAddFilterBackend((RTLDRMOD)pvUser, pBackend); + else + { + LogFunc(("ignored plugin: pBackend->u32Version=%u rc=%Rrc\n", pBackend->u32Version, rc)); + rc = VERR_IGNORED; + } + + return rc; +} + +/** + * Checks whether the given plugin filename was already loaded. + * + * @returns Pointer to already loaded plugin, NULL if not found. + * @param pszFilename The filename to check. + */ +static PVDPLUGIN vdPluginFind(const char *pszFilename) +{ + PVDPLUGIN pIt; + RTListForEach(&g_ListPluginsLoaded, pIt, VDPLUGIN, NodePlugin) + { + if (!RTStrCmp(pIt->pszFilename, pszFilename)) + return pIt; + } + + return NULL; +} + +/** + * Adds a plugin to the list of loaded plugins. + * + * @returns VBox status code. + * @param hPlugin Plugin handle to add. + * @param pszFilename The associated filename, used for finding duplicates. + */ +static int vdAddPlugin(RTLDRMOD hPlugin, const char *pszFilename) +{ + int rc = VINF_SUCCESS; + PVDPLUGIN pPlugin = (PVDPLUGIN)RTMemAllocZ(sizeof(VDPLUGIN)); + + if (pPlugin) + { + pPlugin->hPlugin = hPlugin; + pPlugin->pszFilename = RTStrDup(pszFilename); + if (pPlugin->pszFilename) + RTListAppend(&g_ListPluginsLoaded, &pPlugin->NodePlugin); + else + { + RTMemFree(pPlugin); + rc = VERR_NO_MEMORY; + } + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + +/** + * Removes a single plugin given by the filename. + * + * @returns VBox status code. + * @param pszFilename The plugin filename to remove. + */ +static int vdRemovePlugin(const char *pszFilename) +{ + /* Find plugin to be removed from the list. */ + PVDPLUGIN pIt = vdPluginFind(pszFilename); + if (!pIt) + return VINF_SUCCESS; + + /** @todo r=klaus: need to add a plugin entry point for unregistering the + * backends. Only if this doesn't exist (or fails to work) we should fall + * back to the following uncoordinated backend cleanup. */ + for (unsigned i = 0; i < g_cBackends; i++) + { + while (i < g_cBackends && g_ahBackendPlugins[i] == pIt->hPlugin) + { + memmove(&g_apBackends[i], &g_apBackends[i + 1], (g_cBackends - i - 1) * sizeof(PCVDIMAGEBACKEND)); + memmove(&g_ahBackendPlugins[i], &g_ahBackendPlugins[i + 1], (g_cBackends - i - 1) * sizeof(RTLDRMOD)); + /** @todo for now skip reallocating, doesn't save much */ + g_cBackends--; + } + } + for (unsigned i = 0; i < g_cCacheBackends; i++) + { + while (i < g_cCacheBackends && g_ahCacheBackendPlugins[i] == pIt->hPlugin) + { + memmove(&g_apCacheBackends[i], &g_apCacheBackends[i + 1], (g_cCacheBackends - i - 1) * sizeof(PCVDCACHEBACKEND)); + memmove(&g_ahCacheBackendPlugins[i], &g_ahCacheBackendPlugins[i + 1], (g_cCacheBackends - i - 1) * sizeof(RTLDRMOD)); + /** @todo for now skip reallocating, doesn't save much */ + g_cCacheBackends--; + } + } + for (unsigned i = 0; i < g_cFilterBackends; i++) + { + while (i < g_cFilterBackends && g_pahFilterBackendPlugins[i] == pIt->hPlugin) + { + memmove(&g_apFilterBackends[i], &g_apFilterBackends[i + 1], (g_cFilterBackends - i - 1) * sizeof(PCVDFILTERBACKEND)); + memmove(&g_pahFilterBackendPlugins[i], &g_pahFilterBackendPlugins[i + 1], (g_cFilterBackends - i - 1) * sizeof(RTLDRMOD)); + /** @todo for now skip reallocating, doesn't save much */ + g_cFilterBackends--; + } + } + + /* Remove the plugin node now, all traces of it are gone. */ + RTListNodeRemove(&pIt->NodePlugin); + RTLdrClose(pIt->hPlugin); + RTStrFree(pIt->pszFilename); + RTMemFree(pIt); + + return VINF_SUCCESS; +} + +#endif /* VBOX_HDD_NO_DYNAMIC_BACKENDS*/ + +/** + * Returns the number of known image format backends. + * + * @returns Number of image formats known. + */ +DECLHIDDEN(uint32_t) vdGetImageBackendCount(void) +{ + return g_cBackends; +} + + +/** + * Queries a image backend descriptor by the index. + * + * @returns VBox status code. + * @param idx The index of the backend to query. + * @param ppBackend Where to store the pointer to the backend descriptor on success. + */ +DECLHIDDEN(int) vdQueryImageBackend(uint32_t idx, PCVDIMAGEBACKEND *ppBackend) +{ + if (idx >= g_cBackends) + return VERR_OUT_OF_RANGE; + + *ppBackend = g_apBackends[idx]; + return VINF_SUCCESS; +} + + +/** + * Returns the image backend descriptor matching the given identifier if known. + * + * @returns VBox status code. + * @param pszBackend The backend identifier to look for. + * @param ppBackend Where to store the pointer to the backend descriptor on success. + */ +DECLHIDDEN(int) vdFindImageBackend(const char *pszBackend, PCVDIMAGEBACKEND *ppBackend) +{ + int rc = VERR_NOT_FOUND; + PCVDIMAGEBACKEND pBackend = NULL; + + if (!g_apBackends) + VDInit(); + + for (unsigned i = 0; i < g_cBackends; i++) + { + if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName)) + { + pBackend = g_apBackends[i]; + rc = VINF_SUCCESS; + break; + } + } + *ppBackend = pBackend; + return rc; +} + +/** + * Returns the number of known cache format backends. + * + * @returns Number of image formats known. + */ +DECLHIDDEN(uint32_t) vdGetCacheBackendCount(void) +{ + return g_cCacheBackends; +} + + +/** + * Queries a cache backend descriptor by the index. + * + * @returns VBox status code. + * @param idx The index of the backend to query. + * @param ppBackend Where to store the pointer to the backend descriptor on success. + */ +DECLHIDDEN(int) vdQueryCacheBackend(uint32_t idx, PCVDCACHEBACKEND *ppBackend) +{ + if (idx >= g_cCacheBackends) + return VERR_OUT_OF_RANGE; + + *ppBackend = g_apCacheBackends[idx]; + return VINF_SUCCESS; +} + + +/** + * Returns the cache backend descriptor matching the given identifier if known. + * + * @returns VBox status code. + * @param pszBackend The backend identifier to look for. + * @param ppBackend Where to store the pointer to the backend descriptor on success. + */ +DECLHIDDEN(int) vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend) +{ + int rc = VERR_NOT_FOUND; + PCVDCACHEBACKEND pBackend = NULL; + + if (!g_apCacheBackends) + VDInit(); + + for (unsigned i = 0; i < g_cCacheBackends; i++) + { + if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName)) + { + pBackend = g_apCacheBackends[i]; + rc = VINF_SUCCESS; + break; + } + } + *ppBackend = pBackend; + return rc; +} + + +/** + * Returns the number of known filter backends. + * + * @returns Number of image formats known. + */ +DECLHIDDEN(uint32_t) vdGetFilterBackendCount(void) +{ + return g_cFilterBackends; +} + + +/** + * Queries a filter backend descriptor by the index. + * + * @returns VBox status code. + * @param idx The index of the backend to query. + * @param ppBackend Where to store the pointer to the backend descriptor on success. + */ +DECLHIDDEN(int) vdQueryFilterBackend(uint32_t idx, PCVDFILTERBACKEND *ppBackend) +{ + if (idx >= g_cFilterBackends) + return VERR_OUT_OF_RANGE; + + *ppBackend = g_apFilterBackends[idx]; + return VINF_SUCCESS; +} + + +/** + * Returns the filter backend descriptor matching the given identifier if known. + * + * @returns VBox status code. + * @param pszFilter The filter identifier to look for. + * @param ppBackend Where to store the pointer to the backend descriptor on success. + */ +DECLHIDDEN(int) vdFindFilterBackend(const char *pszFilter, PCVDFILTERBACKEND *ppBackend) +{ + int rc = VERR_NOT_FOUND; + PCVDFILTERBACKEND pBackend = NULL; + + for (unsigned i = 0; i < g_cFilterBackends; i++) + { + if (!RTStrICmp(pszFilter, g_apFilterBackends[i]->pszBackendName)) + { + pBackend = g_apFilterBackends[i]; + rc = VINF_SUCCESS; + break; + } + } + *ppBackend = pBackend; + return rc; +} + + +/** + * Worker for VDPluginLoadFromFilename() and vdPluginLoadFromPath(). + * + * @returns VBox status code. + * @param pszFilename The plugin filename to load. + */ +DECLHIDDEN(int) vdPluginLoadFromFilename(const char *pszFilename) +{ +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + /* Plugin loaded? Nothing to do. */ + if (vdPluginFind(pszFilename)) + { + LogFlowFunc(("Plugin '%s' already loaded\n", pszFilename)); + return VINF_SUCCESS; + } + + RTLDRMOD hPlugin = NIL_RTLDRMOD; + int rc = SUPR3HardenedLdrLoadPlugIn(pszFilename, &hPlugin, NULL); + LogFlowFunc(("SUPR3HardenedLdrLoadPlugIn('%s') -> %Rrc\n", pszFilename, rc)); + if (RT_SUCCESS(rc)) + { + VDBACKENDREGISTER BackendRegister; + PFNVDPLUGINLOAD pfnVDPluginLoad = NULL; + + BackendRegister.u32Version = VD_BACKENDREG_CB_VERSION; + BackendRegister.pfnRegisterImage = vdPluginRegisterImage; + BackendRegister.pfnRegisterCache = vdPluginRegisterCache; + BackendRegister.pfnRegisterFilter = vdPluginRegisterFilter; + + rc = RTLdrGetSymbol(hPlugin, VD_PLUGIN_LOAD_NAME, (void**)&pfnVDPluginLoad); + if (RT_FAILURE(rc) || !pfnVDPluginLoad) + { + LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDPluginLoad=%#p\n", + VD_PLUGIN_LOAD_NAME, pszFilename, rc, pfnVDPluginLoad)); + if (RT_SUCCESS(rc)) + rc = VERR_SYMBOL_NOT_FOUND; + } + + if (RT_SUCCESS(rc)) + { + /* Get the function table. */ + rc = pfnVDPluginLoad(hPlugin, &BackendRegister); + } + else + LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszFilename, rc)); + + /* Create a plugin entry on success. */ + if (RT_SUCCESS(rc)) + vdAddPlugin(hPlugin, pszFilename); + else + RTLdrClose(hPlugin); + } + + return rc; +#else + RT_NOREF1(pszFilename); + return VERR_NOT_IMPLEMENTED; +#endif +} + +/** + * Worker for VDPluginLoadFromPath() and vdLoadDynamicBackends(). + * + * @returns VBox status code. + * @param pszPath The path to load plugins from. + */ +DECLHIDDEN(int) vdPluginLoadFromPath(const char *pszPath) +{ +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + /* To get all entries with VBoxHDD as prefix. */ + char *pszPluginFilter = RTPathJoinA(pszPath, VD_PLUGIN_PREFIX "*"); + if (!pszPluginFilter) + return VERR_NO_STR_MEMORY; + + PRTDIRENTRYEX pPluginDirEntry = NULL; + RTDIR hPluginDir; + size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX); + int rc = RTDirOpenFiltered(&hPluginDir, pszPluginFilter, RTDIRFILTER_WINNT, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX)); + if (pPluginDirEntry) + { + while ( (rc = RTDirReadEx(hPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) + != VERR_NO_MORE_FILES) + { + char *pszPluginPath = NULL; + + if (rc == VERR_BUFFER_OVERFLOW) + { + /* allocate new buffer. */ + RTMemFree(pPluginDirEntry); + pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry); + if (!pPluginDirEntry) + { + rc = VERR_NO_MEMORY; + break; + } + /* Retry. */ + rc = RTDirReadEx(hPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); + if (RT_FAILURE(rc)) + break; + } + else if (RT_FAILURE(rc)) + break; + + /* We got the new entry. */ + if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode)) + continue; + + /* Prepend the path to the libraries. */ + pszPluginPath = RTPathJoinA(pszPath, pPluginDirEntry->szName); + if (!pszPluginPath) + { + rc = VERR_NO_STR_MEMORY; + break; + } + + rc = vdPluginLoadFromFilename(pszPluginPath); + RTStrFree(pszPluginPath); + } + + RTMemFree(pPluginDirEntry); + } + else + rc = VERR_NO_MEMORY; + + RTDirClose(hPluginDir); + } + else + { + /* On Windows the above immediately signals that there are no + * files matching, while on other platforms enumerating the + * files below fails. Either way: no plugins. */ + } + + if (rc == VERR_NO_MORE_FILES) + rc = VINF_SUCCESS; + RTStrFree(pszPluginFilter); + return rc; +#else + RT_NOREF1(pszPath); + return VERR_NOT_IMPLEMENTED; +#endif +} + +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS +/** + * internal: scans plugin directory and loads found plugins. + */ +static int vdLoadDynamicBackends(void) +{ + /* + * Enumerate plugin backends from the application directory where the other + * shared libraries are. + */ + char szPath[RTPATH_MAX]; + int rc = RTPathAppPrivateArch(szPath, sizeof(szPath)); + if (RT_FAILURE(rc)) + return rc; + + return vdPluginLoadFromPath(szPath); +} +#endif + +/** + * Worker for VDPluginUnloadFromFilename() and vdPluginUnloadFromPath(). + * + * @returns VBox status code. + * @param pszFilename The plugin filename to unload. + */ +DECLHIDDEN(int) vdPluginUnloadFromFilename(const char *pszFilename) +{ +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + return vdRemovePlugin(pszFilename); +#else + RT_NOREF1(pszFilename); + return VERR_NOT_IMPLEMENTED; +#endif +} + +/** + * Worker for VDPluginUnloadFromPath(). + * + * @returns VBox status code. + * @param pszPath The path to unload plugins from. + */ +DECLHIDDEN(int) vdPluginUnloadFromPath(const char *pszPath) +{ +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + /* To get all entries with VBoxHDD as prefix. */ + char *pszPluginFilter = RTPathJoinA(pszPath, VD_PLUGIN_PREFIX "*"); + if (!pszPluginFilter) + return VERR_NO_STR_MEMORY; + + PRTDIRENTRYEX pPluginDirEntry = NULL; + RTDIR hPluginDir; + size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX); + int rc = RTDirOpenFiltered(&hPluginDir, pszPluginFilter, RTDIRFILTER_WINNT, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX)); + if (pPluginDirEntry) + { + while ((rc = RTDirReadEx(hPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES) + { + char *pszPluginPath = NULL; + + if (rc == VERR_BUFFER_OVERFLOW) + { + /* allocate new buffer. */ + RTMemFree(pPluginDirEntry); + pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry); + if (!pPluginDirEntry) + { + rc = VERR_NO_MEMORY; + break; + } + /* Retry. */ + rc = RTDirReadEx(hPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); + if (RT_FAILURE(rc)) + break; + } + else if (RT_FAILURE(rc)) + break; + + /* We got the new entry. */ + if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode)) + continue; + + /* Prepend the path to the libraries. */ + pszPluginPath = RTPathJoinA(pszPath, pPluginDirEntry->szName); + if (!pszPluginPath) + { + rc = VERR_NO_STR_MEMORY; + break; + } + + rc = vdPluginUnloadFromFilename(pszPluginPath); + RTStrFree(pszPluginPath); + } + + RTMemFree(pPluginDirEntry); + } + else + rc = VERR_NO_MEMORY; + + RTDirClose(hPluginDir); + } + else + { + /* On Windows the above immediately signals that there are no + * files matching, while on other platforms enumerating the + * files below fails. Either way: no plugins. */ + } + + if (rc == VERR_NO_MORE_FILES) + rc = VINF_SUCCESS; + RTStrFree(pszPluginFilter); + return rc; +#else + RT_NOREF1(pszPath); + return VERR_NOT_IMPLEMENTED; +#endif +} + + +/** + * Initializes the plugin state to be able to load further plugins and populates + * the backend lists with the compiled in backends. + * + * @returns VBox status code. + */ +DECLHIDDEN(int) vdPluginInit(void) +{ + int rc = vdAddBackends(NIL_RTLDRMOD, aStaticBackends, RT_ELEMENTS(aStaticBackends)); + if (RT_SUCCESS(rc)) + { + rc = vdAddCacheBackends(NIL_RTLDRMOD, aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends)); +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + if (RT_SUCCESS(rc)) + { + RTListInit(&g_ListPluginsLoaded); + rc = vdLoadDynamicBackends(); + } +#endif + } + + return rc; +} + + +/** + * Tears down the plugin related state. + * + * @returns VBox status code. + */ +DECLHIDDEN(int) vdPluginTerm(void) +{ + if (!g_apBackends) + return VERR_INTERNAL_ERROR; + +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + if (g_pahFilterBackendPlugins) + RTMemFree(g_pahFilterBackendPlugins); +#endif + if (g_apFilterBackends) + RTMemFree(g_apFilterBackends); +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + if (g_ahCacheBackendPlugins) + RTMemFree(g_ahCacheBackendPlugins); +#endif + if (g_apCacheBackends) + RTMemFree(g_apCacheBackends); + RTMemFree(g_apBackends); + + g_cBackends = 0; + g_apBackends = NULL; + + /* Clear the supported cache backends. */ + g_cCacheBackends = 0; + g_apCacheBackends = NULL; +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + g_ahCacheBackendPlugins = NULL; +#endif + + /* Clear the supported filter backends. */ + g_cFilterBackends = 0; + g_apFilterBackends = NULL; +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + g_pahFilterBackendPlugins = NULL; +#endif + +#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS + PVDPLUGIN pPlugin, pPluginNext; + RTListForEachSafe(&g_ListPluginsLoaded, pPlugin, pPluginNext, VDPLUGIN, NodePlugin) + { + RTLdrClose(pPlugin->hPlugin); + RTStrFree(pPlugin->pszFilename); + RTListNodeRemove(&pPlugin->NodePlugin); + RTMemFree(pPlugin); + } +#endif + + return VINF_SUCCESS; +} + + +/** + * Returns whether the plugin related state is initialized. + * + * @returns true if the plugin state is initialized and plugins can be loaded, + * false otherwise. + */ +DECLHIDDEN(bool) vdPluginIsInitialized(void) +{ + return g_apBackends != NULL; +} + |