VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/dvm/dvmvfs.cpp@ 69609

Last change on this file since 69609 was 69609, checked in by vboxsync, 7 years ago

iprt/dvm: Some API adjusting to VFS - work in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/* $Id: dvmvfs.cpp 69609 2017-11-07 18:59:38Z vboxsync $ */
2/** @file
3 * IPRT Disk Volume Management API (DVM) - VFS glue.
4 */
5
6/*
7 * Copyright (C) 2012-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS /** @todo fix log group */
32#include <iprt/types.h>
33#include <iprt/assert.h>
34#include <iprt/mem.h>
35#include <iprt/dvm.h>
36#include <iprt/err.h>
37#include <iprt/asm.h>
38#include <iprt/string.h>
39#include <iprt/file.h>
40#include <iprt/sg.h>
41#include <iprt/vfslowlevel.h>
42#include <iprt/poll.h>
43#include <iprt/log.h>
44#include "internal/dvm.h"
45
46
47/*********************************************************************************************************************************
48* Structures and Typedefs *
49*********************************************************************************************************************************/
50
51/**
52 * The internal data of a DVM volume I/O stream.
53 */
54typedef struct RTVFSDVMFILE
55{
56 /** The volume the VFS file belongs to. */
57 RTDVMVOLUME hVol;
58 /** Current position. */
59 uint64_t offCurPos;
60} RTVFSDVMFILE;
61/** Pointer to a the internal data of a DVM volume file. */
62typedef RTVFSDVMFILE *PRTVFSDVMFILE;
63
64/**
65 * A volume manager VFS for use in chains (thing pseudo/devfs).
66 */
67typedef struct RTDVMVFSVOL
68{
69 /** The volume manager. */
70 RTDVM hVolMgr;
71 /** Whether to close it on success. */
72 bool fCloseDvm;
73 /** Whether the access is read-only. */
74 bool fReadOnly;
75 /** Number of volumes. */
76 uint32_t cVolumes;
77} RTDVMVFSVOL;
78/** Poitner to a volume manager VFS. */
79typedef RTDVMVFSVOL *PRTDVMVFSVOL;
80
81/**
82 * The volume manager VFS root dir data.
83 */
84typedef struct RTDVMVFSROOTDIR
85{
86 /** Pointer to the VFS volume. */
87 PRTDVMVFSVOL pVfsVol;
88 /** The current directory offset. */
89 uint32_t offDir;
90} RTDVMVFSROOTDIR;
91/** Poitner to a volume manager VFS root dir. */
92typedef RTDVMVFSROOTDIR *PRTDVMVFSROOTDIR;
93
94
95/**
96 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
97 */
98static DECLCALLBACK(int) rtDvmVfsFile_Close(void *pvThis)
99{
100 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
101
102 RTDvmVolumeRelease(pThis->hVol);
103 return VINF_SUCCESS;
104}
105
106
107/**
108 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
109 */
110static DECLCALLBACK(int) rtDvmVfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
111{
112 NOREF(pvThis);
113 NOREF(pObjInfo);
114 NOREF(enmAddAttr);
115 return VERR_NOT_SUPPORTED;
116}
117
118
119/**
120 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
121 */
122static DECLCALLBACK(int) rtDvmVfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
123{
124 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
125 int rc = VINF_SUCCESS;
126
127 Assert(pSgBuf->cSegs == 1);
128 NOREF(fBlocking);
129
130 /*
131 * Find the current position and check if it's within the volume.
132 */
133 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
134 if (offUnsigned >= RTDvmVolumeGetSize(pThis->hVol))
135 {
136 if (pcbRead)
137 {
138 *pcbRead = 0;
139 pThis->offCurPos = offUnsigned;
140 return VINF_EOF;
141 }
142 return VERR_EOF;
143 }
144
145 size_t cbLeftToRead;
146 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > RTDvmVolumeGetSize(pThis->hVol))
147 {
148 if (!pcbRead)
149 return VERR_EOF;
150 *pcbRead = cbLeftToRead = (size_t)(RTDvmVolumeGetSize(pThis->hVol) - offUnsigned);
151 }
152 else
153 {
154 cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
155 if (pcbRead)
156 *pcbRead = cbLeftToRead;
157 }
158
159 /*
160 * Ok, we've got a valid stretch within the file. Do the reading.
161 */
162 if (cbLeftToRead > 0)
163 {
164 rc = RTDvmVolumeRead(pThis->hVol, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbLeftToRead);
165 if (RT_SUCCESS(rc))
166 offUnsigned += cbLeftToRead;
167 }
168
169 pThis->offCurPos = offUnsigned;
170 return rc;
171}
172
173
174/**
175 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
176 */
177static DECLCALLBACK(int) rtDvmVfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
178{
179 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
180 int rc = VINF_SUCCESS;
181
182 Assert(pSgBuf->cSegs == 1);
183 NOREF(fBlocking);
184
185 /*
186 * Find the current position and check if it's within the volume.
187 * Writing beyond the end of a volume is not supported.
188 */
189 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
190 if (offUnsigned >= RTDvmVolumeGetSize(pThis->hVol))
191 {
192 if (pcbWritten)
193 {
194 *pcbWritten = 0;
195 pThis->offCurPos = offUnsigned;
196 }
197 return VERR_NOT_SUPPORTED;
198 }
199
200 size_t cbLeftToWrite;
201 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > RTDvmVolumeGetSize(pThis->hVol))
202 {
203 if (!pcbWritten)
204 return VERR_EOF;
205 *pcbWritten = cbLeftToWrite = (size_t)(RTDvmVolumeGetSize(pThis->hVol) - offUnsigned);
206 }
207 else
208 {
209 cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
210 if (pcbWritten)
211 *pcbWritten = cbLeftToWrite;
212 }
213
214 /*
215 * Ok, we've got a valid stretch within the file. Do the reading.
216 */
217 if (cbLeftToWrite > 0)
218 {
219 rc = RTDvmVolumeWrite(pThis->hVol, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbLeftToWrite);
220 if (RT_SUCCESS(rc))
221 offUnsigned += cbLeftToWrite;
222 }
223
224 pThis->offCurPos = offUnsigned;
225 return rc;
226}
227
228
229/**
230 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
231 */
232static DECLCALLBACK(int) rtDvmVfsFile_Flush(void *pvThis)
233{
234 NOREF(pvThis);
235 return VINF_SUCCESS; /** @todo Implement missing DVM API. */
236}
237
238
239/**
240 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
241 */
242static DECLCALLBACK(int) rtDvmVfsFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
243 uint32_t *pfRetEvents)
244{
245 NOREF(pvThis);
246 int rc;
247 if (fEvents != RTPOLL_EVT_ERROR)
248 {
249 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
250 rc = VINF_SUCCESS;
251 }
252 else
253 rc = RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
254 return rc;
255}
256
257
258/**
259 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
260 */
261static DECLCALLBACK(int) rtDvmVfsFile_Tell(void *pvThis, PRTFOFF poffActual)
262{
263 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
264 *poffActual = pThis->offCurPos;
265 return VINF_SUCCESS;
266}
267
268
269/**
270 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
271 */
272static DECLCALLBACK(int) rtDvmVfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
273{
274 NOREF(pvThis);
275 NOREF(fMode);
276 NOREF(fMask);
277 return VERR_NOT_SUPPORTED;
278}
279
280
281/**
282 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
283 */
284static DECLCALLBACK(int) rtDvmVfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
285 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
286{
287 NOREF(pvThis);
288 NOREF(pAccessTime);
289 NOREF(pModificationTime);
290 NOREF(pChangeTime);
291 NOREF(pBirthTime);
292 return VERR_NOT_SUPPORTED;
293}
294
295
296/**
297 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
298 */
299static DECLCALLBACK(int) rtDvmVfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
300{
301 NOREF(pvThis);
302 NOREF(uid);
303 NOREF(gid);
304 return VERR_NOT_SUPPORTED;
305}
306
307
308/**
309 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
310 */
311static DECLCALLBACK(int) rtDvmVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
312{
313 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
314
315 /*
316 * Seek relative to which position.
317 */
318 uint64_t offWrt;
319 switch (uMethod)
320 {
321 case RTFILE_SEEK_BEGIN:
322 offWrt = 0;
323 break;
324
325 case RTFILE_SEEK_CURRENT:
326 offWrt = pThis->offCurPos;
327 break;
328
329 case RTFILE_SEEK_END:
330 offWrt = RTDvmVolumeGetSize(pThis->hVol);
331 break;
332
333 default:
334 return VERR_INTERNAL_ERROR_5;
335 }
336
337 /*
338 * Calc new position, take care to stay within bounds.
339 *
340 * @todo: Setting position beyond the end of the volume does not make sense.
341 */
342 uint64_t offNew;
343 if (offSeek == 0)
344 offNew = offWrt;
345 else if (offSeek > 0)
346 {
347 offNew = offWrt + offSeek;
348 if ( offNew < offWrt
349 || offNew > RTFOFF_MAX)
350 offNew = RTFOFF_MAX;
351 }
352 else if ((uint64_t)-offSeek < offWrt)
353 offNew = offWrt + offSeek;
354 else
355 offNew = 0;
356
357 /*
358 * Update the state and set return value.
359 */
360 pThis->offCurPos = offNew;
361
362 *poffActual = offNew;
363 return VINF_SUCCESS;
364}
365
366
367/**
368 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
369 */
370static DECLCALLBACK(int) rtDvmVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
371{
372 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
373 *pcbFile = RTDvmVolumeGetSize(pThis->hVol);
374 return VINF_SUCCESS;
375}
376
377
378/**
379 * Standard file operations.
380 */
381DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtDvmVfsStdFileOps =
382{
383 { /* Stream */
384 { /* Obj */
385 RTVFSOBJOPS_VERSION,
386 RTVFSOBJTYPE_FILE,
387 "DvmFile",
388 rtDvmVfsFile_Close,
389 rtDvmVfsFile_QueryInfo,
390 RTVFSOBJOPS_VERSION
391 },
392 RTVFSIOSTREAMOPS_VERSION,
393 RTVFSIOSTREAMOPS_FEAT_NO_SG,
394 rtDvmVfsFile_Read,
395 rtDvmVfsFile_Write,
396 rtDvmVfsFile_Flush,
397 rtDvmVfsFile_PollOne,
398 rtDvmVfsFile_Tell,
399 NULL /*Skip*/,
400 NULL /*ZeroFill*/,
401 RTVFSIOSTREAMOPS_VERSION,
402 },
403 RTVFSFILEOPS_VERSION,
404 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
405 { /* ObjSet */
406 RTVFSOBJSETOPS_VERSION,
407 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
408 rtDvmVfsFile_SetMode,
409 rtDvmVfsFile_SetTimes,
410 rtDvmVfsFile_SetOwner,
411 RTVFSOBJSETOPS_VERSION
412 },
413 rtDvmVfsFile_Seek,
414 rtDvmVfsFile_QuerySize,
415 RTVFSFILEOPS_VERSION
416};
417
418
419RTDECL(int) RTDvmVolumeCreateVfsFile(RTDVMVOLUME hVol, PRTVFSFILE phVfsFileOut)
420{
421 AssertPtrReturn(hVol, VERR_INVALID_HANDLE);
422 AssertPtrReturn(phVfsFileOut, VERR_INVALID_POINTER);
423
424 uint32_t cRefs = RTDvmVolumeRetain(hVol);
425 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
426
427 /*
428 * Create the volume file.
429 */
430 RTVFSFILE hVfsFile;
431 PRTVFSDVMFILE pThis;
432 int rc = RTVfsNewFile(&g_rtDvmVfsStdFileOps, sizeof(*pThis), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_WRITE,
433 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
434 if (RT_SUCCESS(rc))
435 {
436 pThis->offCurPos = 0;
437 pThis->hVol = hVol;
438
439 *phVfsFileOut = hVfsFile;
440 return VINF_SUCCESS;
441 }
442 else
443 RTDvmVolumeRelease(hVol);
444 return rc;
445}
446
447
448
449
450/**
451 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
452 */
453static DECLCALLBACK(int) rtDvmVfsVol_Close(void *pvThis)
454{
455 PRTDVMVFSVOL pThis = (PRTDVMVFSVOL)pvThis;
456 LogFlow(("rtDvmVfsVol_Close(%p)\n", pThis));
457
458 if ( pThis->fCloseDvm
459 && pThis->hVolMgr != NIL_RTDVM )
460 RTDvmRelease(pThis->hVolMgr);
461 pThis->hVolMgr = NIL_RTDVM;
462
463 return VINF_SUCCESS;
464}
465
466
467/**
468 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
469 */
470static DECLCALLBACK(int) rtDvmVfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
471{
472 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
473 return VERR_WRONG_TYPE;
474}
475
476
477/**
478 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
479 */
480static DECLCALLBACK(int) rtDvmVfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
481{
482 //PRTDVMVFSVOL pThis = (PRTDVMVFSVOL)pvThis;
483
484 //rtDvmDirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
485 //return rtDvmDir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
486 RT_NOREF(pvThis, phVfsDir);
487 return VERR_NOT_IMPLEMENTED;
488}
489
490
491/**
492 * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
493 */
494static DECLCALLBACK(int) rtDvmVfsVol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
495{
496 RT_NOREF(pvThis, off, cb, pfUsed);
497 return VERR_NOT_IMPLEMENTED;
498}
499
500
501DECL_HIDDEN_CONST(const RTVFSOPS) g_rtDvmVfsVolOps =
502{
503 { /* Obj */
504 RTVFSOBJOPS_VERSION,
505 RTVFSOBJTYPE_VFS,
506 "DvmVol",
507 rtDvmVfsVol_Close,
508 rtDvmVfsVol_QueryInfo,
509 RTVFSOBJOPS_VERSION
510 },
511 RTVFSOPS_VERSION,
512 0 /* fFeatures */,
513 rtDvmVfsVol_OpenRoot,
514 rtDvmVfsVol_IsRangeInUse,
515 RTVFSOPS_VERSION
516};
517
518
519
520/**
521 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
522 */
523static DECLCALLBACK(int) rtDvmVfsChain_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
524 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
525{
526 RT_NOREF(pProviderReg, pSpec);
527
528 /*
529 * Basic checks.
530 */
531 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
532 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
533 if (pElement->enmType != RTVFSOBJTYPE_VFS)
534 return VERR_VFS_CHAIN_ONLY_VFS;
535
536 if (pElement->cArgs > 1)
537 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
538
539 /*
540 * Parse the flag if present, save in pElement->uProvider.
541 */
542 /** @todo allow specifying sector size */
543 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
544 if (pElement->cArgs > 0)
545 {
546 const char *psz = pElement->paArgs[0].psz;
547 if (*psz)
548 {
549 if ( !strcmp(psz, "ro")
550 || !strcmp(psz, "r"))
551 fReadOnly = true;
552 else if (!strcmp(psz, "rw"))
553 fReadOnly = false;
554 else
555 {
556 *poffError = pElement->paArgs[0].offSpec;
557 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
558 }
559 }
560 }
561
562 pElement->uProvider = fReadOnly;
563 return VINF_SUCCESS;
564}
565
566
567/**
568 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
569 */
570static DECLCALLBACK(int) rtDvmVfsChain_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
571 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
572 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
573{
574 RT_NOREF(pProviderReg, pSpec, poffError, pErrInfo);
575 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
576
577 /*
578 * Instantiate the volume manager and open the map stuff.
579 */
580 RTVFSFILE hPrevVfsFile = RTVfsObjToFile(hPrevVfsObj);
581 AssertReturn(hPrevVfsFile != NIL_RTVFSFILE, VERR_VFS_CHAIN_CAST_FAILED);
582
583 RTDVM hVolMgr;
584 int rc = RTDvmCreate(&hVolMgr, hPrevVfsFile, 512, 0 /*fFlags*/);
585 RTVfsFileRelease(hPrevVfsFile);
586 if (RT_SUCCESS(rc))
587 {
588 rc = RTDvmMapOpen(hVolMgr);
589 if (RT_SUCCESS(rc))
590 {
591 /*
592 * Create a VFS instance for the volume manager.
593 */
594 RTVFS hVfs = NIL_RTVFS;
595 PRTDVMVFSVOL pThis = NULL;
596 int rc = RTVfsNew(&g_rtDvmVfsVolOps, sizeof(RTDVMVFSVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
597 if (RT_SUCCESS(rc))
598 {
599 pThis->hVolMgr = hVolMgr;
600 pThis->fCloseDvm = true;
601 pThis->fReadOnly = pElement->uProvider == (uint64_t)true;
602 pThis->cVolumes = RTDvmMapGetValidVolumes(hVolMgr);
603
604 *phVfsObj = RTVfsObjFromVfs(hVfs);
605 RTVfsRelease(hVfs);
606 return *phVfsObj != NIL_RTVFSOBJ ? VINF_SUCCESS : VERR_VFS_CHAIN_CAST_FAILED;
607 }
608 }
609 else
610 rc = RTErrInfoSetF(pErrInfo, rc, "RTDvmMapOpen failed: %Rrc", rc);
611 RTDvmRelease(hVolMgr);
612 }
613 else
614 rc = RTErrInfoSetF(pErrInfo, rc, "RTDvmCreate failed: %Rrc", rc);
615 return rc;
616}
617
618
619/**
620 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
621 */
622static DECLCALLBACK(bool) rtDvmVfsChain_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
623 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
624 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
625{
626 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
627 return false;
628}
629
630
631/** VFS chain element 'file'. */
632static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
633{
634 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
635 /* fReserved = */ 0,
636 /* pszName = */ "dvm",
637 /* ListEntry = */ { NULL, NULL },
638 /* pszHelp = */ "Opens a container image using the VD API.\n",
639 /* pfnValidate = */ rtDvmVfsChain_Validate,
640 /* pfnInstantiate = */ rtDvmVfsChain_Instantiate,
641 /* pfnCanReuseElement = */ rtDvmVfsChain_CanReuseElement,
642 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
643};
644
645RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
646
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