VirtualBox

source: vbox/trunk/src/VBox/Storage/VDPlugin.cpp@ 107044

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.7 KB
Line 
1/* $Id: VDPlugin.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VD - Virtual disk container implementation, plugin related bits.
4 */
5
6/*
7 * Copyright (C) 2017-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_VD
33#include <VBox/err.h>
34#include <VBox/sup.h>
35#include <VBox/log.h>
36#include <VBox/vd-plugin.h>
37
38#include <iprt/dir.h>
39#include <iprt/ldr.h>
40#include <iprt/mem.h>
41#include <iprt/path.h>
42
43#include "VDInternal.h"
44#include "VDBackends.h"
45
46
47/*********************************************************************************************************************************
48* Structures and Typedefs *
49*********************************************************************************************************************************/
50/**
51 * Plugin structure.
52 */
53typedef struct VDPLUGIN
54{
55 /** Pointer to the next plugin structure. */
56 RTLISTNODE NodePlugin;
57 /** Handle of loaded plugin library. */
58 RTLDRMOD hPlugin;
59 /** Filename of the loaded plugin. */
60 char *pszFilename;
61} VDPLUGIN;
62/** Pointer to a plugin structure. */
63typedef VDPLUGIN *PVDPLUGIN;
64
65
66/*********************************************************************************************************************************
67* Global Variables *
68*********************************************************************************************************************************/
69#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
70/** Head of loaded plugin list. */
71static RTLISTANCHOR g_ListPluginsLoaded;
72#endif
73
74/** Number of image backends supported. */
75static unsigned g_cBackends = 0;
76/** Array of pointers to the image backends. */
77static PCVDIMAGEBACKEND *g_apBackends = NULL;
78#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
79/** Array of handles to the corresponding plugin. */
80static RTLDRMOD *g_ahBackendPlugins = NULL;
81#endif
82/**
83 * Builtin image backends.
84 *
85 * @note As long as the pfnProb() calls aren't scored, the ordering influences
86 * which backend take precedence. In particular, the RAW backend should
87 * be thowards the end of the list.
88 */
89static PCVDIMAGEBACKEND aStaticBackends[] =
90{
91 &g_VmdkBackend,
92 &g_VDIBackend,
93 &g_VhdBackend,
94 &g_ParallelsBackend,
95 &g_DmgBackend,
96 &g_QedBackend,
97 &g_QCowBackend,
98 &g_VhdxBackend,
99 &g_CueBackend,
100 &g_VBoxIsoMakerBackend,
101 &g_RawBackend,
102 &g_ISCSIBackend
103};
104
105/** Number of supported cache backends. */
106static unsigned g_cCacheBackends = 0;
107/** Array of pointers to the cache backends. */
108static PCVDCACHEBACKEND *g_apCacheBackends = NULL;
109#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
110/** Array of handles to the corresponding plugin.
111 *
112 * @todo r=bird: This looks rather pointless.
113 */
114static RTLDRMOD *g_ahCacheBackendPlugins = NULL;
115#endif
116/** Builtin cache backends. */
117static PCVDCACHEBACKEND aStaticCacheBackends[] =
118{
119 &g_VciCacheBackend
120};
121
122/** Number of supported filter backends. */
123static unsigned g_cFilterBackends = 0;
124/** Array of pointers to the filters backends. */
125static PCVDFILTERBACKEND *g_apFilterBackends = NULL;
126#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
127/** Array of handles to the corresponding plugin. */
128static PRTLDRMOD g_pahFilterBackendPlugins = NULL;
129#endif
130
131
132/*********************************************************************************************************************************
133* Internal Functions *
134*********************************************************************************************************************************/
135
136/**
137 * Add an array of image format backends from the given plugin to the list of known
138 * image formats.
139 *
140 * @returns VBox status code.
141 * @param hPlugin The plugin handle the backends belong to, can be NIL_RTLDRMOD
142 * for compiled in backends.
143 * @param ppBackends The array of image backend descriptors to add.
144 * @param cBackends Number of descriptors in the array.
145 */
146static int vdAddBackends(RTLDRMOD hPlugin, PCVDIMAGEBACKEND *ppBackends, unsigned cBackends)
147{
148 PCVDIMAGEBACKEND *pTmp = (PCVDIMAGEBACKEND *)RTMemRealloc(g_apBackends,
149 (g_cBackends + cBackends) * sizeof(PCVDIMAGEBACKEND));
150 if (RT_UNLIKELY(!pTmp))
151 return VERR_NO_MEMORY;
152 g_apBackends = pTmp;
153 memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PCVDIMAGEBACKEND));
154
155#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
156 RTLDRMOD *pTmpPlugins = (RTLDRMOD*)RTMemRealloc(g_ahBackendPlugins,
157 (g_cBackends + cBackends) * sizeof(RTLDRMOD));
158 if (RT_UNLIKELY(!pTmpPlugins))
159 return VERR_NO_MEMORY;
160 g_ahBackendPlugins = pTmpPlugins;
161 for (unsigned i = g_cBackends; i < g_cBackends + cBackends; i++)
162 g_ahBackendPlugins[i] = hPlugin;
163#else
164 RT_NOREF(hPlugin);
165#endif
166
167 g_cBackends += cBackends;
168 return VINF_SUCCESS;
169}
170
171
172/**
173 * Add an array of cache format backends from the given plugin to the list of known
174 * cache formats.
175 *
176 * @returns VBox status code.
177 * @param hPlugin The plugin handle the backends belong to, can be NIL_RTLDRMOD
178 * for compiled in backends.
179 * @param ppBackends The array of cache backend descriptors to add.
180 * @param cBackends Number of descriptors in the array.
181 */
182static int vdAddCacheBackends(RTLDRMOD hPlugin, PCVDCACHEBACKEND *ppBackends, unsigned cBackends)
183{
184 PCVDCACHEBACKEND *pTmp = (PCVDCACHEBACKEND*)RTMemReallocTag(g_apCacheBackends,
185 (g_cCacheBackends + cBackends) * sizeof(PCVDCACHEBACKEND),
186 "may-leak:vdAddCacheBackend");
187 if (RT_UNLIKELY(!pTmp))
188 return VERR_NO_MEMORY;
189 g_apCacheBackends = pTmp;
190 memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PCVDCACHEBACKEND));
191
192#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
193 RTLDRMOD *pTmpPlugins = (RTLDRMOD*)RTMemReallocTag(g_ahCacheBackendPlugins,
194 (g_cCacheBackends + cBackends) * sizeof(RTLDRMOD),
195 "may-leak:vdAddCacheBackend");
196 if (RT_UNLIKELY(!pTmpPlugins))
197 return VERR_NO_MEMORY;
198 g_ahCacheBackendPlugins = pTmpPlugins;
199 for (unsigned i = g_cCacheBackends; i < g_cCacheBackends + cBackends; i++)
200 g_ahCacheBackendPlugins[i] = hPlugin;
201#else
202 RT_NOREF(hPlugin);
203#endif
204
205 g_cCacheBackends += cBackends;
206 return VINF_SUCCESS;
207}
208
209#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
210/**
211 * Add a single image format backend to the list of known image formats.
212 *
213 * @returns VBox status code.
214 * @param hPlugin The plugin handle the backend belongs to, can be NIL_RTLDRMOD
215 * for compiled in backends.
216 * @param pBackend The image backend descriptors to add.
217 */
218DECLINLINE(int) vdAddBackend(RTLDRMOD hPlugin, PCVDIMAGEBACKEND pBackend)
219{
220 return vdAddBackends(hPlugin, &pBackend, 1);
221}
222
223
224/**
225 * Add a single cache format backend to the list of known cache formats.
226 *
227 * @returns VBox status code.
228 * @param hPlugin The plugin handle the backend belongs to, can be NIL_RTLDRMOD
229 * for compiled in backends.
230 * @param pBackend The cache backend descriptors to add.
231 */
232DECLINLINE(int) vdAddCacheBackend(RTLDRMOD hPlugin, PCVDCACHEBACKEND pBackend)
233{
234 return vdAddCacheBackends(hPlugin, &pBackend, 1);
235}
236
237
238/**
239 * Add several filter backends.
240 *
241 * @returns VBox status code.
242 * @param hPlugin Plugin handle to add.
243 * @param ppBackends Array of filter backends to add.
244 * @param cBackends Number of backends to add.
245 */
246static int vdAddFilterBackends(RTLDRMOD hPlugin, PCVDFILTERBACKEND *ppBackends, unsigned cBackends)
247{
248 PCVDFILTERBACKEND *pTmp = (PCVDFILTERBACKEND *)RTMemRealloc(g_apFilterBackends,
249 (g_cFilterBackends + cBackends) * sizeof(PCVDFILTERBACKEND));
250 if (RT_UNLIKELY(!pTmp))
251 return VERR_NO_MEMORY;
252 g_apFilterBackends = pTmp;
253
254#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
255 PRTLDRMOD pTmpPlugins = (PRTLDRMOD)RTMemRealloc(g_pahFilterBackendPlugins,
256 (g_cFilterBackends + cBackends) * sizeof(RTLDRMOD));
257 if (RT_UNLIKELY(!pTmpPlugins))
258 return VERR_NO_MEMORY;
259
260 g_pahFilterBackendPlugins = pTmpPlugins;
261 memcpy(&g_apFilterBackends[g_cFilterBackends], ppBackends, cBackends * sizeof(PCVDFILTERBACKEND));
262 for (unsigned i = g_cFilterBackends; i < g_cFilterBackends + cBackends; i++)
263 g_pahFilterBackendPlugins[i] = hPlugin;
264#else
265 RT_NOREF(hPlugin);
266#endif
267
268 g_cFilterBackends += cBackends;
269 return VINF_SUCCESS;
270}
271
272
273/**
274 * Add a single filter backend to the list of supported filters.
275 *
276 * @returns VBox status code.
277 * @param hPlugin Plugin handle to add.
278 * @param pBackend The backend to add.
279 */
280DECLINLINE(int) vdAddFilterBackend(RTLDRMOD hPlugin, PCVDFILTERBACKEND pBackend)
281{
282 return vdAddFilterBackends(hPlugin, &pBackend, 1);
283}
284
285/**
286 * @interface_method_impl{VDBACKENDREGISTER,pfnRegisterImage}
287 */
288static DECLCALLBACK(int) vdPluginRegisterImage(void *pvUser, PCVDIMAGEBACKEND pBackend)
289{
290 int rc = VINF_SUCCESS;
291
292 if (VD_VERSION_ARE_COMPATIBLE(VD_IMGBACKEND_VERSION, pBackend->u32Version))
293 vdAddBackend((RTLDRMOD)pvUser, pBackend);
294 else
295 {
296 LogFunc(("ignored plugin: pBackend->u32Version=%u rc=%Rrc\n", pBackend->u32Version, rc));
297 rc = VERR_IGNORED;
298 }
299
300 return rc;
301}
302
303/**
304 * @interface_method_impl{VDBACKENDREGISTER,pfnRegisterCache}
305 */
306static DECLCALLBACK(int) vdPluginRegisterCache(void *pvUser, PCVDCACHEBACKEND pBackend)
307{
308 int rc = VINF_SUCCESS;
309
310 if (VD_VERSION_ARE_COMPATIBLE(VD_CACHEBACKEND_VERSION, pBackend->u32Version))
311 vdAddCacheBackend((RTLDRMOD)pvUser, pBackend);
312 else
313 {
314 LogFunc(("ignored plugin: pBackend->u32Version=%u rc=%Rrc\n", pBackend->u32Version, rc));
315 rc = VERR_IGNORED;
316 }
317
318 return rc;
319}
320
321/**
322 * @interface_method_impl{VDBACKENDREGISTER,pfnRegisterFilter}
323 */
324static DECLCALLBACK(int) vdPluginRegisterFilter(void *pvUser, PCVDFILTERBACKEND pBackend)
325{
326 int rc = VINF_SUCCESS;
327
328 if (VD_VERSION_ARE_COMPATIBLE(VD_FLTBACKEND_VERSION, pBackend->u32Version))
329 vdAddFilterBackend((RTLDRMOD)pvUser, pBackend);
330 else
331 {
332 LogFunc(("ignored plugin: pBackend->u32Version=%u rc=%Rrc\n", pBackend->u32Version, rc));
333 rc = VERR_IGNORED;
334 }
335
336 return rc;
337}
338
339/**
340 * Checks whether the given plugin filename was already loaded.
341 *
342 * @returns Pointer to already loaded plugin, NULL if not found.
343 * @param pszFilename The filename to check.
344 */
345static PVDPLUGIN vdPluginFind(const char *pszFilename)
346{
347 PVDPLUGIN pIt;
348 RTListForEach(&g_ListPluginsLoaded, pIt, VDPLUGIN, NodePlugin)
349 {
350 if (!RTStrCmp(pIt->pszFilename, pszFilename))
351 return pIt;
352 }
353
354 return NULL;
355}
356
357/**
358 * Adds a plugin to the list of loaded plugins.
359 *
360 * @returns VBox status code.
361 * @param hPlugin Plugin handle to add.
362 * @param pszFilename The associated filename, used for finding duplicates.
363 */
364static int vdAddPlugin(RTLDRMOD hPlugin, const char *pszFilename)
365{
366 int rc = VINF_SUCCESS;
367 PVDPLUGIN pPlugin = (PVDPLUGIN)RTMemAllocZ(sizeof(VDPLUGIN));
368
369 if (pPlugin)
370 {
371 pPlugin->hPlugin = hPlugin;
372 pPlugin->pszFilename = RTStrDup(pszFilename);
373 if (pPlugin->pszFilename)
374 RTListAppend(&g_ListPluginsLoaded, &pPlugin->NodePlugin);
375 else
376 {
377 RTMemFree(pPlugin);
378 rc = VERR_NO_MEMORY;
379 }
380 }
381 else
382 rc = VERR_NO_MEMORY;
383
384 return rc;
385}
386
387/**
388 * Removes a single plugin given by the filename.
389 *
390 * @returns VBox status code.
391 * @param pszFilename The plugin filename to remove.
392 */
393static int vdRemovePlugin(const char *pszFilename)
394{
395 /* Find plugin to be removed from the list. */
396 PVDPLUGIN pIt = vdPluginFind(pszFilename);
397 if (!pIt)
398 return VINF_SUCCESS;
399
400 /** @todo r=klaus: need to add a plugin entry point for unregistering the
401 * backends. Only if this doesn't exist (or fails to work) we should fall
402 * back to the following uncoordinated backend cleanup. */
403 for (unsigned i = 0; i < g_cBackends; i++)
404 {
405 while (i < g_cBackends && g_ahBackendPlugins[i] == pIt->hPlugin)
406 {
407 memmove(&g_apBackends[i], &g_apBackends[i + 1], (g_cBackends - i - 1) * sizeof(PCVDIMAGEBACKEND));
408 memmove(&g_ahBackendPlugins[i], &g_ahBackendPlugins[i + 1], (g_cBackends - i - 1) * sizeof(RTLDRMOD));
409 /** @todo for now skip reallocating, doesn't save much */
410 g_cBackends--;
411 }
412 }
413 for (unsigned i = 0; i < g_cCacheBackends; i++)
414 {
415 while (i < g_cCacheBackends && g_ahCacheBackendPlugins[i] == pIt->hPlugin)
416 {
417 memmove(&g_apCacheBackends[i], &g_apCacheBackends[i + 1], (g_cCacheBackends - i - 1) * sizeof(PCVDCACHEBACKEND));
418 memmove(&g_ahCacheBackendPlugins[i], &g_ahCacheBackendPlugins[i + 1], (g_cCacheBackends - i - 1) * sizeof(RTLDRMOD));
419 /** @todo for now skip reallocating, doesn't save much */
420 g_cCacheBackends--;
421 }
422 }
423 for (unsigned i = 0; i < g_cFilterBackends; i++)
424 {
425 while (i < g_cFilterBackends && g_pahFilterBackendPlugins[i] == pIt->hPlugin)
426 {
427 memmove(&g_apFilterBackends[i], &g_apFilterBackends[i + 1], (g_cFilterBackends - i - 1) * sizeof(PCVDFILTERBACKEND));
428 memmove(&g_pahFilterBackendPlugins[i], &g_pahFilterBackendPlugins[i + 1], (g_cFilterBackends - i - 1) * sizeof(RTLDRMOD));
429 /** @todo for now skip reallocating, doesn't save much */
430 g_cFilterBackends--;
431 }
432 }
433
434 /* Remove the plugin node now, all traces of it are gone. */
435 RTListNodeRemove(&pIt->NodePlugin);
436 RTLdrClose(pIt->hPlugin);
437 RTStrFree(pIt->pszFilename);
438 RTMemFree(pIt);
439
440 return VINF_SUCCESS;
441}
442
443#endif /* VBOX_HDD_NO_DYNAMIC_BACKENDS*/
444
445/**
446 * Returns the number of known image format backends.
447 *
448 * @returns Number of image formats known.
449 */
450DECLHIDDEN(uint32_t) vdGetImageBackendCount(void)
451{
452 return g_cBackends;
453}
454
455
456/**
457 * Queries a image backend descriptor by the index.
458 *
459 * @returns VBox status code.
460 * @param idx The index of the backend to query.
461 * @param ppBackend Where to store the pointer to the backend descriptor on success.
462 */
463DECLHIDDEN(int) vdQueryImageBackend(uint32_t idx, PCVDIMAGEBACKEND *ppBackend)
464{
465 if (idx >= g_cBackends)
466 return VERR_OUT_OF_RANGE;
467
468 *ppBackend = g_apBackends[idx];
469 return VINF_SUCCESS;
470}
471
472
473/**
474 * Returns the image backend descriptor matching the given identifier if known.
475 *
476 * @returns VBox status code.
477 * @param pszBackend The backend identifier to look for.
478 * @param ppBackend Where to store the pointer to the backend descriptor on success.
479 */
480DECLHIDDEN(int) vdFindImageBackend(const char *pszBackend, PCVDIMAGEBACKEND *ppBackend)
481{
482 int rc = VERR_NOT_FOUND;
483 PCVDIMAGEBACKEND pBackend = NULL;
484
485 if (!g_apBackends)
486 VDInit();
487
488 for (unsigned i = 0; i < g_cBackends; i++)
489 {
490 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
491 {
492 pBackend = g_apBackends[i];
493 rc = VINF_SUCCESS;
494 break;
495 }
496 }
497 *ppBackend = pBackend;
498 return rc;
499}
500
501/**
502 * Returns the number of known cache format backends.
503 *
504 * @returns Number of image formats known.
505 */
506DECLHIDDEN(uint32_t) vdGetCacheBackendCount(void)
507{
508 return g_cCacheBackends;
509}
510
511
512/**
513 * Queries a cache backend descriptor by the index.
514 *
515 * @returns VBox status code.
516 * @param idx The index of the backend to query.
517 * @param ppBackend Where to store the pointer to the backend descriptor on success.
518 */
519DECLHIDDEN(int) vdQueryCacheBackend(uint32_t idx, PCVDCACHEBACKEND *ppBackend)
520{
521 if (idx >= g_cCacheBackends)
522 return VERR_OUT_OF_RANGE;
523
524 *ppBackend = g_apCacheBackends[idx];
525 return VINF_SUCCESS;
526}
527
528
529/**
530 * Returns the cache backend descriptor matching the given identifier if known.
531 *
532 * @returns VBox status code.
533 * @param pszBackend The backend identifier to look for.
534 * @param ppBackend Where to store the pointer to the backend descriptor on success.
535 */
536DECLHIDDEN(int) vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend)
537{
538 int rc = VERR_NOT_FOUND;
539 PCVDCACHEBACKEND pBackend = NULL;
540
541 if (!g_apCacheBackends)
542 VDInit();
543
544 for (unsigned i = 0; i < g_cCacheBackends; i++)
545 {
546 if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName))
547 {
548 pBackend = g_apCacheBackends[i];
549 rc = VINF_SUCCESS;
550 break;
551 }
552 }
553 *ppBackend = pBackend;
554 return rc;
555}
556
557
558/**
559 * Returns the number of known filter backends.
560 *
561 * @returns Number of image formats known.
562 */
563DECLHIDDEN(uint32_t) vdGetFilterBackendCount(void)
564{
565 return g_cFilterBackends;
566}
567
568
569/**
570 * Queries a filter backend descriptor by the index.
571 *
572 * @returns VBox status code.
573 * @param idx The index of the backend to query.
574 * @param ppBackend Where to store the pointer to the backend descriptor on success.
575 */
576DECLHIDDEN(int) vdQueryFilterBackend(uint32_t idx, PCVDFILTERBACKEND *ppBackend)
577{
578 if (idx >= g_cFilterBackends)
579 return VERR_OUT_OF_RANGE;
580
581 *ppBackend = g_apFilterBackends[idx];
582 return VINF_SUCCESS;
583}
584
585
586/**
587 * Returns the filter backend descriptor matching the given identifier if known.
588 *
589 * @returns VBox status code.
590 * @param pszFilter The filter identifier to look for.
591 * @param ppBackend Where to store the pointer to the backend descriptor on success.
592 */
593DECLHIDDEN(int) vdFindFilterBackend(const char *pszFilter, PCVDFILTERBACKEND *ppBackend)
594{
595 int rc = VERR_NOT_FOUND;
596 PCVDFILTERBACKEND pBackend = NULL;
597
598 for (unsigned i = 0; i < g_cFilterBackends; i++)
599 {
600 if (!RTStrICmp(pszFilter, g_apFilterBackends[i]->pszBackendName))
601 {
602 pBackend = g_apFilterBackends[i];
603 rc = VINF_SUCCESS;
604 break;
605 }
606 }
607 *ppBackend = pBackend;
608 return rc;
609}
610
611
612/**
613 * Worker for VDPluginLoadFromFilename() and vdPluginLoadFromPath().
614 *
615 * @returns VBox status code.
616 * @param pszFilename The plugin filename to load.
617 */
618DECLHIDDEN(int) vdPluginLoadFromFilename(const char *pszFilename)
619{
620#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
621 /* Plugin loaded? Nothing to do. */
622 if (vdPluginFind(pszFilename))
623 {
624 LogFlowFunc(("Plugin '%s' already loaded\n", pszFilename));
625 return VINF_SUCCESS;
626 }
627
628 RTLDRMOD hPlugin = NIL_RTLDRMOD;
629 int rc = SUPR3HardenedLdrLoadPlugIn(pszFilename, &hPlugin, NULL);
630 LogFlowFunc(("SUPR3HardenedLdrLoadPlugIn('%s') -> %Rrc\n", pszFilename, rc));
631 if (RT_SUCCESS(rc))
632 {
633 VDBACKENDREGISTER BackendRegister;
634 PFNVDPLUGINLOAD pfnVDPluginLoad = NULL;
635
636 BackendRegister.u32Version = VD_BACKENDREG_CB_VERSION;
637 BackendRegister.pfnRegisterImage = vdPluginRegisterImage;
638 BackendRegister.pfnRegisterCache = vdPluginRegisterCache;
639 BackendRegister.pfnRegisterFilter = vdPluginRegisterFilter;
640
641 rc = RTLdrGetSymbol(hPlugin, VD_PLUGIN_LOAD_NAME, (void**)&pfnVDPluginLoad);
642 if (RT_FAILURE(rc) || !pfnVDPluginLoad)
643 {
644 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDPluginLoad=%#p\n",
645 VD_PLUGIN_LOAD_NAME, pszFilename, rc, pfnVDPluginLoad));
646 if (RT_SUCCESS(rc))
647 rc = VERR_SYMBOL_NOT_FOUND;
648 }
649
650 if (RT_SUCCESS(rc))
651 {
652 /* Get the function table. */
653 rc = pfnVDPluginLoad(hPlugin, &BackendRegister);
654 }
655 else
656 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszFilename, rc));
657
658 /* Create a plugin entry on success. */
659 if (RT_SUCCESS(rc))
660 vdAddPlugin(hPlugin, pszFilename);
661 else
662 RTLdrClose(hPlugin);
663 }
664
665 return rc;
666#else
667 RT_NOREF1(pszFilename);
668 return VERR_NOT_IMPLEMENTED;
669#endif
670}
671
672/**
673 * Worker for VDPluginLoadFromPath() and vdLoadDynamicBackends().
674 *
675 * @returns VBox status code.
676 * @param pszPath The path to load plugins from.
677 */
678DECLHIDDEN(int) vdPluginLoadFromPath(const char *pszPath)
679{
680#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
681 /* To get all entries with VBoxHDD as prefix. */
682 char *pszPluginFilter = RTPathJoinA(pszPath, VD_PLUGIN_PREFIX "*");
683 if (!pszPluginFilter)
684 return VERR_NO_STR_MEMORY;
685
686 PRTDIRENTRYEX pPluginDirEntry = NULL;
687 RTDIR hPluginDir;
688 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
689 int rc = RTDirOpenFiltered(&hPluginDir, pszPluginFilter, RTDIRFILTER_WINNT, 0 /*fFlags*/);
690 if (RT_SUCCESS(rc))
691 {
692 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
693 if (pPluginDirEntry)
694 {
695 while ( (rc = RTDirReadEx(hPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
696 != VERR_NO_MORE_FILES)
697 {
698 char *pszPluginPath = NULL;
699
700 if (rc == VERR_BUFFER_OVERFLOW)
701 {
702 /* allocate new buffer. */
703 RTMemFree(pPluginDirEntry);
704 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
705 if (!pPluginDirEntry)
706 {
707 rc = VERR_NO_MEMORY;
708 break;
709 }
710 /* Retry. */
711 rc = RTDirReadEx(hPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
712 if (RT_FAILURE(rc))
713 break;
714 }
715 else if (RT_FAILURE(rc))
716 break;
717
718 /* We got the new entry. */
719 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
720 continue;
721
722 /* Prepend the path to the libraries. */
723 pszPluginPath = RTPathJoinA(pszPath, pPluginDirEntry->szName);
724 if (!pszPluginPath)
725 {
726 rc = VERR_NO_STR_MEMORY;
727 break;
728 }
729
730 vdPluginLoadFromFilename(pszPluginPath);
731 RTStrFree(pszPluginPath);
732 }
733
734 RTMemFree(pPluginDirEntry);
735 }
736 else
737 rc = VERR_NO_MEMORY;
738
739 RTDirClose(hPluginDir);
740 }
741 else
742 {
743 /* On Windows the above immediately signals that there are no
744 * files matching, while on other platforms enumerating the
745 * files below fails. Either way: no plugins. */
746 }
747
748 if (rc == VERR_NO_MORE_FILES)
749 rc = VINF_SUCCESS;
750 RTStrFree(pszPluginFilter);
751 return rc;
752#else
753 RT_NOREF1(pszPath);
754 return VERR_NOT_IMPLEMENTED;
755#endif
756}
757
758#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
759/**
760 * internal: scans plugin directory and loads found plugins.
761 */
762static int vdLoadDynamicBackends(void)
763{
764 /*
765 * Enumerate plugin backends from the application directory where the other
766 * shared libraries are.
767 */
768 char szPath[RTPATH_MAX];
769 int rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
770 if (RT_FAILURE(rc))
771 return rc;
772
773 return vdPluginLoadFromPath(szPath);
774}
775#endif
776
777/**
778 * Worker for VDPluginUnloadFromFilename() and vdPluginUnloadFromPath().
779 *
780 * @returns VBox status code.
781 * @param pszFilename The plugin filename to unload.
782 */
783DECLHIDDEN(int) vdPluginUnloadFromFilename(const char *pszFilename)
784{
785#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
786 return vdRemovePlugin(pszFilename);
787#else
788 RT_NOREF1(pszFilename);
789 return VERR_NOT_IMPLEMENTED;
790#endif
791}
792
793/**
794 * Worker for VDPluginUnloadFromPath().
795 *
796 * @returns VBox status code.
797 * @param pszPath The path to unload plugins from.
798 */
799DECLHIDDEN(int) vdPluginUnloadFromPath(const char *pszPath)
800{
801#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
802 /* To get all entries with VBoxHDD as prefix. */
803 char *pszPluginFilter = RTPathJoinA(pszPath, VD_PLUGIN_PREFIX "*");
804 if (!pszPluginFilter)
805 return VERR_NO_STR_MEMORY;
806
807 PRTDIRENTRYEX pPluginDirEntry = NULL;
808 RTDIR hPluginDir;
809 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
810 int rc = RTDirOpenFiltered(&hPluginDir, pszPluginFilter, RTDIRFILTER_WINNT, 0 /*fFlags*/);
811 if (RT_SUCCESS(rc))
812 {
813 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
814 if (pPluginDirEntry)
815 {
816 while ((rc = RTDirReadEx(hPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
817 {
818 char *pszPluginPath = NULL;
819
820 if (rc == VERR_BUFFER_OVERFLOW)
821 {
822 /* allocate new buffer. */
823 RTMemFree(pPluginDirEntry);
824 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
825 if (!pPluginDirEntry)
826 {
827 rc = VERR_NO_MEMORY;
828 break;
829 }
830 /* Retry. */
831 rc = RTDirReadEx(hPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
832 if (RT_FAILURE(rc))
833 break;
834 }
835 else if (RT_FAILURE(rc))
836 break;
837
838 /* We got the new entry. */
839 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
840 continue;
841
842 /* Prepend the path to the libraries. */
843 pszPluginPath = RTPathJoinA(pszPath, pPluginDirEntry->szName);
844 if (!pszPluginPath)
845 {
846 rc = VERR_NO_STR_MEMORY;
847 break;
848 }
849
850 vdPluginUnloadFromFilename(pszPluginPath);
851 RTStrFree(pszPluginPath);
852 }
853
854 RTMemFree(pPluginDirEntry);
855 }
856 else
857 rc = VERR_NO_MEMORY;
858
859 RTDirClose(hPluginDir);
860 }
861 else
862 {
863 /* On Windows the above immediately signals that there are no
864 * files matching, while on other platforms enumerating the
865 * files below fails. Either way: no plugins. */
866 }
867
868 if (rc == VERR_NO_MORE_FILES)
869 rc = VINF_SUCCESS;
870 RTStrFree(pszPluginFilter);
871 return rc;
872#else
873 RT_NOREF1(pszPath);
874 return VERR_NOT_IMPLEMENTED;
875#endif
876}
877
878
879/**
880 * Initializes the plugin state to be able to load further plugins and populates
881 * the backend lists with the compiled in backends.
882 *
883 * @returns VBox status code.
884 */
885DECLHIDDEN(int) vdPluginInit(void)
886{
887 int rc = vdAddBackends(NIL_RTLDRMOD, aStaticBackends, RT_ELEMENTS(aStaticBackends));
888 if (RT_SUCCESS(rc))
889 {
890 rc = vdAddCacheBackends(NIL_RTLDRMOD, aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
891#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
892 if (RT_SUCCESS(rc))
893 {
894 RTListInit(&g_ListPluginsLoaded);
895 rc = vdLoadDynamicBackends();
896 }
897#endif
898 }
899
900 return rc;
901}
902
903
904/**
905 * Tears down the plugin related state.
906 *
907 * @returns VBox status code.
908 */
909DECLHIDDEN(int) vdPluginTerm(void)
910{
911 if (!g_apBackends)
912 return VERR_INTERNAL_ERROR;
913
914#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
915 if (g_pahFilterBackendPlugins)
916 RTMemFree(g_pahFilterBackendPlugins);
917#endif
918 if (g_apFilterBackends)
919 RTMemFree(g_apFilterBackends);
920#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
921 if (g_ahCacheBackendPlugins)
922 RTMemFree(g_ahCacheBackendPlugins);
923#endif
924 if (g_apCacheBackends)
925 RTMemFree(g_apCacheBackends);
926 RTMemFree(g_apBackends);
927
928 g_cBackends = 0;
929 g_apBackends = NULL;
930
931 /* Clear the supported cache backends. */
932 g_cCacheBackends = 0;
933 g_apCacheBackends = NULL;
934#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
935 g_ahCacheBackendPlugins = NULL;
936#endif
937
938 /* Clear the supported filter backends. */
939 g_cFilterBackends = 0;
940 g_apFilterBackends = NULL;
941#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
942 g_pahFilterBackendPlugins = NULL;
943#endif
944
945#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
946 PVDPLUGIN pPlugin, pPluginNext;
947 RTListForEachSafe(&g_ListPluginsLoaded, pPlugin, pPluginNext, VDPLUGIN, NodePlugin)
948 {
949 RTLdrClose(pPlugin->hPlugin);
950 RTStrFree(pPlugin->pszFilename);
951 RTListNodeRemove(&pPlugin->NodePlugin);
952 RTMemFree(pPlugin);
953 }
954#endif
955
956 return VINF_SUCCESS;
957}
958
959
960/**
961 * Returns whether the plugin related state is initialized.
962 *
963 * @returns true if the plugin state is initialized and plugins can be loaded,
964 * false otherwise.
965 */
966DECLHIDDEN(bool) vdPluginIsInitialized(void)
967{
968 return g_apBackends != NULL;
969}
970
Note: See TracBrowser for help on using the repository browser.

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