VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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