VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 16 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: 44.7 KB
Line 
1/* $Id: dvmvfs.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT Disk Volume Management API (DVM) - VFS glue.
4 */
5
6/*
7 * Copyright (C) 2012-2023 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 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_FS /** @todo fix log group */
42#include <iprt/types.h>
43#include <iprt/assert.h>
44#include <iprt/mem.h>
45#include <iprt/dvm.h>
46#include <iprt/err.h>
47#include <iprt/asm.h>
48#include <iprt/string.h>
49#include <iprt/file.h>
50#include <iprt/sg.h>
51#include <iprt/vfslowlevel.h>
52#include <iprt/poll.h>
53#include <iprt/log.h>
54#include "internal/dvm.h"
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/** Pointer to a volume manager VFS. */
61typedef struct RTDVMVFSVOL *PRTDVMVFSVOL;
62
63/**
64 * The internal data of a DVM volume I/O stream.
65 */
66typedef struct RTVFSDVMFILE
67{
68 /** The volume the VFS file belongs to. */
69 RTDVMVOLUME hVol;
70 /** Pointer to the VFS volume. Can be NULL. */
71 PRTDVMVFSVOL pVfsVol;
72 /** Current position. */
73 uint64_t offCurPos;
74 /** Set if readable. */
75 bool fCanRead;
76 /** Set if writable. */
77 bool fCanWrite;
78} RTVFSDVMFILE;
79/** Pointer to a the internal data of a DVM volume file. */
80typedef RTVFSDVMFILE *PRTVFSDVMFILE;
81
82/**
83 * The internal data of a DVM volume symlink.
84 */
85typedef struct RTVFSDVMSYMLINK
86{
87 /** The DVM volume the symlink represent. */
88 RTDVMVOLUME hVol;
89 /** The DVM volume manager @a hVol belongs to. */
90 RTDVM hVolMgr;
91 /** The symlink name. */
92 char *pszSymlink;
93 /** The symlink target (volXX). */
94 char szTarget[16];
95} RTVFSDVMSYMLINK;
96/** Pointer to a the internal data of a DVM volume file. */
97typedef RTVFSDVMSYMLINK *PRTVFSDVMSYMLINK;
98
99/**
100 * The volume manager VFS (root) dir data.
101 */
102typedef struct RTDVMVFSDIR
103{
104 /** Pointer to the VFS volume. */
105 PRTDVMVFSVOL pVfsVol;
106 /** The current directory offset. */
107 uint32_t offDir;
108 /** Set if we need to try return hCurVolume again because of buffer overflow. */
109 bool fReturnCurrent;
110 /** Pointer to name alias string (returned by RTDvmVolumeQueryName, free it). */
111 char *pszNameAlias;
112 /** The current DVM volume. */
113 RTDVMVOLUME hCurVolume;
114} RTDVMVFSDIR;
115/** Pointer to a volume manager VFS (root) dir. */
116typedef RTDVMVFSDIR *PRTDVMVFSDIR;
117
118/**
119 * A volume manager VFS for use in chains (thing pseudo/devfs).
120 */
121typedef struct RTDVMVFSVOL
122{
123 /** The volume manager. */
124 RTDVM hVolMgr;
125 /** Whether to close it on success. */
126 bool fCloseDvm;
127 /** Whether the access is read-only. */
128 bool fReadOnly;
129 /** Number of volumes. */
130 uint32_t cVolumes;
131 /** Self reference. */
132 RTVFS hVfsSelf;
133} RTDVMVFSVOL;
134
135
136/*********************************************************************************************************************************
137* Internal Functions *
138*********************************************************************************************************************************/
139static DECLCALLBACK(int) rtDvmVfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir);
140
141
142/**
143 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
144 */
145static DECLCALLBACK(int) rtDvmVfsFile_Close(void *pvThis)
146{
147 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
148
149 RTDvmVolumeRelease(pThis->hVol);
150 return VINF_SUCCESS;
151}
152
153
154/**
155 * Worker for rtDvmVfsFile_QueryInfoWorker and rtDvmVfsSym_QueryInfoWorker.
156 */
157static int rtDvmVfsFileSym_QueryAddAttrWorker(RTDVMVOLUME hVolume, RTDVM hVolMgr,
158 PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
159{
160 switch (enmAddAttr)
161 {
162 case RTFSOBJATTRADD_NOTHING:
163 case RTFSOBJATTRADD_UNIX:
164 pObjInfo->Attr.u.Unix.uid = (RTUID)RTDvmVolumeGetType(hVolume);
165 pObjInfo->Attr.u.Unix.gid = hVolMgr != NIL_RTDVM ? (RTGID)RTDvmMapGetFormatType(hVolMgr) : NIL_RTGID;
166 pObjInfo->Attr.u.Unix.cHardlinks = 1;
167 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
168 pObjInfo->Attr.u.Unix.INodeId = 0;
169 pObjInfo->Attr.u.Unix.fFlags = 0;
170 pObjInfo->Attr.u.Unix.GenerationId = 0;
171 pObjInfo->Attr.u.Unix.Device = 0;
172 break;
173
174 case RTFSOBJATTRADD_UNIX_OWNER:
175 {
176 RTDVMVOLTYPE enmType = RTDvmVolumeGetType(hVolume);
177 pObjInfo->Attr.u.UnixOwner.uid = (RTUID)enmType;
178 RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName),
179 RTDvmVolumeTypeGetDescr(enmType));
180 break;
181 }
182
183 case RTFSOBJATTRADD_UNIX_GROUP:
184 if (hVolMgr != NIL_RTDVM)
185 {
186 pObjInfo->Attr.u.UnixGroup.gid = (RTGID)RTDvmMapGetFormatType(hVolMgr);
187 RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
188 RTDvmMapGetFormatName(hVolMgr));
189 }
190 else
191 {
192 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
193 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
194 }
195 break;
196
197 case RTFSOBJATTRADD_EASIZE:
198 pObjInfo->Attr.u.EASize.cb = 0;
199 break;
200
201 default:
202 return VERR_INVALID_PARAMETER;
203 }
204 return VINF_SUCCESS;
205}
206
207
208/**
209 * Worker for rtDvmVfsFile_QueryInfo, rtDvmVfsDir_QueryEntryInfo, and
210 * rtDvmVfsDir_ReadDir.
211 */
212static int rtDvmVfsFile_QueryInfoWorker(RTDVMVOLUME hVolume, RTDVM hVolMgr, bool fReadOnly,
213 PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
214{
215
216 pObjInfo->cbObject = RTDvmVolumeGetSize(hVolume);
217 pObjInfo->cbAllocated = pObjInfo->cbObject;
218 RTTimeSpecSetNano(&pObjInfo->AccessTime, 0);
219 RTTimeSpecSetNano(&pObjInfo->ModificationTime, 0);
220 RTTimeSpecSetNano(&pObjInfo->ChangeTime, 0);
221 RTTimeSpecSetNano(&pObjInfo->BirthTime, 0);
222 pObjInfo->Attr.fMode = RTFS_TYPE_FILE | RTFS_DOS_NT_NORMAL;
223 if (fReadOnly)
224 pObjInfo->Attr.fMode |= RTFS_DOS_READONLY | 0444;
225 else
226 pObjInfo->Attr.fMode |= 0666;
227
228 return rtDvmVfsFileSym_QueryAddAttrWorker(hVolume, hVolMgr, pObjInfo, enmAddAttr);
229}
230
231
232/**
233 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
234 */
235static DECLCALLBACK(int) rtDvmVfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
236{
237 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
238 return rtDvmVfsFile_QueryInfoWorker(pThis->hVol,
239 pThis->pVfsVol ? pThis->pVfsVol->hVolMgr : NIL_RTDVM,
240 pThis->pVfsVol ? pThis->pVfsVol->fReadOnly : !pThis->fCanWrite,
241 pObjInfo, enmAddAttr);
242}
243
244
245/**
246 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
247 */
248static DECLCALLBACK(int) rtDvmVfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
249{
250 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
251 int rc = VINF_SUCCESS;
252
253 Assert(pSgBuf->cSegs == 1);
254 NOREF(fBlocking);
255
256 /*
257 * Find the current position and check if it's within the volume.
258 */
259 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
260 if (offUnsigned >= RTDvmVolumeGetSize(pThis->hVol))
261 {
262 if (pcbRead)
263 {
264 *pcbRead = 0;
265 pThis->offCurPos = offUnsigned;
266 return VINF_EOF;
267 }
268 return VERR_EOF;
269 }
270
271 size_t cbLeftToRead;
272 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > RTDvmVolumeGetSize(pThis->hVol))
273 {
274 if (!pcbRead)
275 return VERR_EOF;
276 *pcbRead = cbLeftToRead = (size_t)(RTDvmVolumeGetSize(pThis->hVol) - offUnsigned);
277 }
278 else
279 {
280 cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
281 if (pcbRead)
282 *pcbRead = cbLeftToRead;
283 }
284
285 /*
286 * Ok, we've got a valid stretch within the file. Do the reading.
287 */
288 if (cbLeftToRead > 0)
289 {
290 rc = RTDvmVolumeRead(pThis->hVol, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToRead);
291 if (RT_SUCCESS(rc))
292 offUnsigned += cbLeftToRead;
293 }
294
295 pThis->offCurPos = offUnsigned;
296 return rc;
297}
298
299
300/**
301 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
302 */
303static DECLCALLBACK(int) rtDvmVfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
304{
305 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
306 int rc = VINF_SUCCESS;
307
308 Assert(pSgBuf->cSegs == 1);
309 NOREF(fBlocking);
310
311 /*
312 * Find the current position and check if it's within the volume.
313 * Writing beyond the end of a volume is not supported.
314 */
315 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
316 if (offUnsigned >= RTDvmVolumeGetSize(pThis->hVol))
317 {
318 if (pcbWritten)
319 {
320 *pcbWritten = 0;
321 pThis->offCurPos = offUnsigned;
322 }
323 return VERR_NOT_SUPPORTED;
324 }
325
326 size_t cbLeftToWrite;
327 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > RTDvmVolumeGetSize(pThis->hVol))
328 {
329 if (!pcbWritten)
330 return VERR_EOF;
331 *pcbWritten = cbLeftToWrite = (size_t)(RTDvmVolumeGetSize(pThis->hVol) - offUnsigned);
332 }
333 else
334 {
335 cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
336 if (pcbWritten)
337 *pcbWritten = cbLeftToWrite;
338 }
339
340 /*
341 * Ok, we've got a valid stretch within the file. Do the reading.
342 */
343 if (cbLeftToWrite > 0)
344 {
345 rc = RTDvmVolumeWrite(pThis->hVol, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToWrite);
346 if (RT_SUCCESS(rc))
347 offUnsigned += cbLeftToWrite;
348 }
349
350 pThis->offCurPos = offUnsigned;
351 return rc;
352}
353
354
355/**
356 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
357 */
358static DECLCALLBACK(int) rtDvmVfsFile_Flush(void *pvThis)
359{
360 NOREF(pvThis);
361 return VINF_SUCCESS; /** @todo Implement missing DVM API. */
362}
363
364
365/**
366 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
367 */
368static DECLCALLBACK(int) rtDvmVfsFile_Tell(void *pvThis, PRTFOFF poffActual)
369{
370 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
371 *poffActual = pThis->offCurPos;
372 return VINF_SUCCESS;
373}
374
375
376/**
377 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
378 */
379static DECLCALLBACK(int) rtDvmVfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
380{
381 NOREF(pvThis);
382 NOREF(fMode);
383 NOREF(fMask);
384 return VERR_NOT_SUPPORTED;
385}
386
387
388/**
389 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
390 */
391static DECLCALLBACK(int) rtDvmVfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
392 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
393{
394 NOREF(pvThis);
395 NOREF(pAccessTime);
396 NOREF(pModificationTime);
397 NOREF(pChangeTime);
398 NOREF(pBirthTime);
399 return VERR_NOT_SUPPORTED;
400}
401
402
403/**
404 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
405 */
406static DECLCALLBACK(int) rtDvmVfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
407{
408 NOREF(pvThis);
409 NOREF(uid);
410 NOREF(gid);
411 return VERR_NOT_SUPPORTED;
412}
413
414
415/**
416 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
417 */
418static DECLCALLBACK(int) rtDvmVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
419{
420 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
421
422 /*
423 * Seek relative to which position.
424 */
425 uint64_t offWrt;
426 switch (uMethod)
427 {
428 case RTFILE_SEEK_BEGIN:
429 offWrt = 0;
430 break;
431
432 case RTFILE_SEEK_CURRENT:
433 offWrt = pThis->offCurPos;
434 break;
435
436 case RTFILE_SEEK_END:
437 offWrt = RTDvmVolumeGetSize(pThis->hVol);
438 break;
439
440 default:
441 return VERR_INTERNAL_ERROR_5;
442 }
443
444 /*
445 * Calc new position, take care to stay within bounds.
446 *
447 * @todo: Setting position beyond the end of the volume does not make sense.
448 */
449 uint64_t offNew;
450 if (offSeek == 0)
451 offNew = offWrt;
452 else if (offSeek > 0)
453 {
454 offNew = offWrt + offSeek;
455 if ( offNew < offWrt
456 || offNew > RTFOFF_MAX)
457 offNew = RTFOFF_MAX;
458 }
459 else if ((uint64_t)-offSeek < offWrt)
460 offNew = offWrt + offSeek;
461 else
462 offNew = 0;
463
464 /*
465 * Update the state and set return value.
466 */
467 pThis->offCurPos = offNew;
468
469 *poffActual = offNew;
470 return VINF_SUCCESS;
471}
472
473
474/**
475 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
476 */
477static DECLCALLBACK(int) rtDvmVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
478{
479 PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
480 *pcbFile = RTDvmVolumeGetSize(pThis->hVol);
481 return VINF_SUCCESS;
482}
483
484
485/**
486 * Standard file operations.
487 */
488DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtDvmVfsStdFileOps =
489{
490 { /* Stream */
491 { /* Obj */
492 RTVFSOBJOPS_VERSION,
493 RTVFSOBJTYPE_FILE,
494 "DvmFile",
495 rtDvmVfsFile_Close,
496 rtDvmVfsFile_QueryInfo,
497 NULL,
498 RTVFSOBJOPS_VERSION
499 },
500 RTVFSIOSTREAMOPS_VERSION,
501 RTVFSIOSTREAMOPS_FEAT_NO_SG,
502 rtDvmVfsFile_Read,
503 rtDvmVfsFile_Write,
504 rtDvmVfsFile_Flush,
505 NULL /*pfnPollOne*/,
506 rtDvmVfsFile_Tell,
507 NULL /*Skip*/,
508 NULL /*ZeroFill*/,
509 RTVFSIOSTREAMOPS_VERSION,
510 },
511 RTVFSFILEOPS_VERSION,
512 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
513 { /* ObjSet */
514 RTVFSOBJSETOPS_VERSION,
515 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
516 rtDvmVfsFile_SetMode,
517 rtDvmVfsFile_SetTimes,
518 rtDvmVfsFile_SetOwner,
519 RTVFSOBJSETOPS_VERSION
520 },
521 rtDvmVfsFile_Seek,
522 rtDvmVfsFile_QuerySize,
523 NULL /*SetSize*/,
524 NULL /*QueryMaxSize*/,
525 RTVFSFILEOPS_VERSION
526};
527
528
529/**
530 * Internal worker for RTDvmVolumeCreateVfsFile and rtDvmVfsDir_OpenFile.
531 *
532 * @returns IPRT status code.
533 * @param pVfsVol The VFS volume, optional.
534 * @param hVol The volume handle. (Reference not consumed.)
535 * @param fOpen RTFILE_O_XXX (valid).
536 * @param phVfsFileOut Where to return the handle to the file.
537 */
538static int rtDvmVfsCreateFileForVolume(PRTDVMVFSVOL pVfsVol, RTDVMVOLUME hVol, uint64_t fOpen, PRTVFSFILE phVfsFileOut)
539{
540 uint32_t cRefs = RTDvmVolumeRetain(hVol);
541 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
542
543 /*
544 * Create the volume file.
545 */
546 RTVFSFILE hVfsFile;
547 PRTVFSDVMFILE pThis;
548 int rc = RTVfsNewFile(&g_rtDvmVfsStdFileOps, sizeof(*pThis), fOpen, NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
549 if (RT_SUCCESS(rc))
550 {
551 pThis->offCurPos = 0;
552 pThis->hVol = hVol;
553 pThis->fCanRead = RT_BOOL(fOpen & RTFILE_O_READ);
554 pThis->fCanWrite = RT_BOOL(fOpen & RTFILE_O_WRITE);
555 pThis->pVfsVol = pVfsVol;
556
557 *phVfsFileOut = hVfsFile;
558 return VINF_SUCCESS;
559 }
560
561 RTDvmVolumeRelease(hVol);
562 return rc;
563}
564
565
566RTDECL(int) RTDvmVolumeCreateVfsFile(RTDVMVOLUME hVol, uint64_t fOpen, PRTVFSFILE phVfsFileOut)
567{
568 AssertPtrReturn(hVol, VERR_INVALID_HANDLE);
569 AssertPtrReturn(phVfsFileOut, VERR_INVALID_POINTER);
570 AssertReturn(fOpen & RTFILE_O_ACCESS_MASK, VERR_INVALID_FLAGS);
571 AssertReturn(!(fOpen & ~RTFILE_O_VALID_MASK), VERR_INVALID_FLAGS);
572 return rtDvmVfsCreateFileForVolume(NULL, hVol, fOpen, phVfsFileOut);
573}
574
575
576/*********************************************************************************************************************************
577* DVM Symbolic Link Objects *
578*********************************************************************************************************************************/
579/**
580 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
581 */
582static DECLCALLBACK(int) rtDvmVfsSym_Close(void *pvThis)
583{
584 PRTVFSDVMSYMLINK pThis = (PRTVFSDVMSYMLINK)pvThis;
585 if (pThis->pszSymlink)
586 {
587 RTStrFree(pThis->pszSymlink);
588 pThis->pszSymlink = NULL;
589 }
590 if (pThis->hVol != NIL_RTDVMVOLUME)
591 {
592 RTDvmVolumeRelease(pThis->hVol);
593 pThis->hVol = NIL_RTDVMVOLUME;
594 }
595 if (pThis->hVolMgr != NIL_RTDVM)
596 {
597 RTDvmRelease(pThis->hVolMgr);
598 pThis->hVolMgr = NIL_RTDVM;
599 }
600 return VINF_SUCCESS;
601}
602
603
604/**
605 * Worker for rtDvmVfsSym_QueryInfo and rtDvmVfsDir_Read.
606 *
607 * @returns IPRT status code.
608 * @param hVolume The volume handle.
609 * @param hVolMgr The volume manager handle. Optional.
610 * @param pszTarget The link target.
611 * @param pObjInfo The object info structure to populate.
612 * @param enmAddAttr The additional attributes to supply.
613 */
614static int rtDvmVfsSym_QueryInfoWorker(RTDVMVOLUME hVolume, RTDVM hVolMgr, const char *pszTarget,
615 PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
616{
617 RT_ZERO(*pObjInfo);
618 pObjInfo->cbObject = pObjInfo->cbAllocated = pszTarget ? strlen(pszTarget) : 0;
619 pObjInfo->Attr.fMode = 0777 | RTFS_TYPE_SYMLINK | RTFS_DOS_NT_REPARSE_POINT;
620
621 return rtDvmVfsFileSym_QueryAddAttrWorker(hVolume, hVolMgr, pObjInfo, enmAddAttr);
622}
623
624
625/**
626 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
627 */
628static DECLCALLBACK(int) rtDvmVfsSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
629{
630 PRTVFSDVMSYMLINK pThis = (PRTVFSDVMSYMLINK)pvThis;
631 return rtDvmVfsSym_QueryInfoWorker(pThis->hVol, pThis->hVolMgr, pThis->szTarget, pObjInfo, enmAddAttr);
632}
633
634
635/**
636 * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
637 */
638static DECLCALLBACK(int) rtDvmVfsSym_Read(void *pvThis, char *pszTarget, size_t cbTarget)
639{
640 PRTVFSDVMSYMLINK pThis = (PRTVFSDVMSYMLINK)pvThis;
641 return RTStrCopy(pszTarget, cbTarget, pThis->szTarget);
642}
643
644
645/**
646 * DVM symbolic link operations.
647 */
648static const RTVFSSYMLINKOPS g_rtDvmVfsSymOps =
649{
650 { /* Obj */
651 RTVFSOBJOPS_VERSION,
652 RTVFSOBJTYPE_SYMLINK,
653 "DvmSymlink",
654 rtDvmVfsSym_Close,
655 rtDvmVfsSym_QueryInfo,
656 NULL,
657 RTVFSOBJOPS_VERSION
658 },
659 RTVFSSYMLINKOPS_VERSION,
660 0,
661 { /* ObjSet */
662 RTVFSOBJSETOPS_VERSION,
663 RT_UOFFSETOF(RTVFSSYMLINKOPS, ObjSet) - RT_UOFFSETOF(RTVFSSYMLINKOPS, Obj),
664 NULL /*rtDvmVfsSym_SetMode*/,
665 NULL /*rtDvmVfsSym_SetTimes*/,
666 NULL /*rtDvmVfsSym_SetOwner*/,
667 RTVFSOBJSETOPS_VERSION
668 },
669 rtDvmVfsSym_Read,
670 RTVFSSYMLINKOPS_VERSION
671};
672
673
674/**
675 * Internal worker for rtDvmVfsDir_OpenFile.
676 *
677 * @returns IPRT status code.
678 * @param hVol The volume handle (not consumed).
679 * @param hVolMgr The volume manager handle (not consumed).
680 * @param iVol The volume number.
681 * @param pszSymlink The volume name. Consumed on success.
682 * @param phVfsSymlinkOut Where to return the handle to the file.
683 */
684static int rtDvmVfsCreateSymlinkForVolume(RTDVMVOLUME hVol, RTDVM hVolMgr, uint32_t iVol, char *pszSymlink,
685 PRTVFSSYMLINK phVfsSymlinkOut)
686{
687 uint32_t cRefs = RTDvmVolumeRetain(hVol);
688 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
689
690 cRefs = RTDvmRetain(hVolMgr);
691 AssertReturnStmt(cRefs != UINT32_MAX, RTDvmVolumeRelease(hVol), VERR_INVALID_HANDLE);
692
693 /*
694 * Create the symlink.
695 */
696 RTVFSSYMLINK hVfsSym;
697 PRTVFSDVMSYMLINK pThis;
698 int rc = RTVfsNewSymlink(&g_rtDvmVfsSymOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsSym, (void **)&pThis);
699 if (RT_SUCCESS(rc))
700 {
701 pThis->hVol = hVol;
702 pThis->hVolMgr = hVolMgr;
703 pThis->pszSymlink = pszSymlink;
704 RTStrPrintf(pThis->szTarget, sizeof(pThis->szTarget), "vol%u", iVol);
705
706 *phVfsSymlinkOut = hVfsSym;
707 return VINF_SUCCESS;
708 }
709 RTDvmRelease(hVolMgr);
710 RTDvmVolumeRelease(hVol);
711 return rc;
712}
713
714
715
716/*********************************************************************************************************************************
717* DVM Directory Objects *
718*********************************************************************************************************************************/
719
720/**
721 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
722 */
723static DECLCALLBACK(int) rtDvmVfsDir_Close(void *pvThis)
724{
725 PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
726
727 if (pThis->hCurVolume != NIL_RTDVMVOLUME)
728 {
729 RTDvmVolumeRelease(pThis->hCurVolume);
730 pThis->hCurVolume = NIL_RTDVMVOLUME;
731 }
732
733 if (pThis->pszNameAlias)
734 {
735 RTStrFree(pThis->pszNameAlias);
736 pThis->pszNameAlias = NULL;
737 }
738
739 pThis->pVfsVol = NULL;
740
741 return VINF_SUCCESS;
742}
743
744
745/**
746 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
747 */
748static DECLCALLBACK(int) rtDvmVfsDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
749{
750 PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
751 pObjInfo->cbObject = pThis->pVfsVol->cVolumes;
752 pObjInfo->cbAllocated = pThis->pVfsVol->cVolumes;
753 RTTimeSpecSetNano(&pObjInfo->AccessTime, 0);
754 RTTimeSpecSetNano(&pObjInfo->ModificationTime, 0);
755 RTTimeSpecSetNano(&pObjInfo->ChangeTime, 0);
756 RTTimeSpecSetNano(&pObjInfo->BirthTime, 0);
757 pObjInfo->Attr.fMode = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY;
758 if (pThis->pVfsVol->fReadOnly)
759 pObjInfo->Attr.fMode |= RTFS_DOS_READONLY | 0555;
760 else
761 pObjInfo->Attr.fMode |= 0777;
762
763 switch (enmAddAttr)
764 {
765 case RTFSOBJATTRADD_NOTHING:
766 case RTFSOBJATTRADD_UNIX:
767 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
768 pObjInfo->Attr.u.Unix.gid = (RTGID)RTDvmMapGetFormatType(pThis->pVfsVol->hVolMgr);
769 pObjInfo->Attr.u.Unix.cHardlinks = pThis->pVfsVol->cVolumes;
770 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
771 pObjInfo->Attr.u.Unix.INodeId = 0;
772 pObjInfo->Attr.u.Unix.fFlags = 0;
773 pObjInfo->Attr.u.Unix.GenerationId = 0;
774 pObjInfo->Attr.u.Unix.Device = 0;
775 break;
776
777 case RTFSOBJATTRADD_UNIX_OWNER:
778 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
779 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
780 break;
781
782 case RTFSOBJATTRADD_UNIX_GROUP:
783 pObjInfo->Attr.u.UnixGroup.gid = (RTGID)RTDvmMapGetFormatType(pThis->pVfsVol->hVolMgr);
784 RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
785 RTDvmMapGetFormatName(pThis->pVfsVol->hVolMgr));
786 break;
787
788 case RTFSOBJATTRADD_EASIZE:
789 pObjInfo->Attr.u.EASize.cb = 0;
790 break;
791
792 default:
793 return VERR_INVALID_PARAMETER;
794 }
795 return VINF_SUCCESS;
796}
797
798
799/**
800 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
801 */
802static DECLCALLBACK(int) rtDvmVfsDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
803{
804 NOREF(pvThis); NOREF(fMode); NOREF(fMask);
805 return VERR_NOT_SUPPORTED;
806}
807
808
809/**
810 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
811 */
812static DECLCALLBACK(int) rtDvmVfsDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
813 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
814{
815 NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
816 return VERR_NOT_SUPPORTED;
817}
818
819
820/**
821 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
822 */
823static DECLCALLBACK(int) rtDvmVfsDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
824{
825 RT_NOREF(pvThis, uid, gid);
826 return VERR_NOT_SUPPORTED;
827}
828
829
830static int rtDvmVfsDir_FindEntry(PRTDVMVFSDIR pThis, const char *pszEntry,
831 PRTDVMVOLUME phVolume, uint32_t *piVol, char **ppszSymlink)
832{
833 *phVolume = NIL_RTDVMVOLUME;
834 *ppszSymlink = NULL;
835 *piVol = UINT32_MAX;
836
837 /*
838 * Enumerate the volumes and try match the volume name.
839 */
840 int rc;
841 PRTDVMVFSVOL pVfsVol = pThis->pVfsVol;
842 if (pVfsVol->cVolumes > 0)
843 {
844 /* The first volume. */
845 uint32_t iVol = 0;
846 RTDVMVOLUME hVol;
847 rc = RTDvmMapQueryFirstVolume(pThis->pVfsVol->hVolMgr, &hVol);
848 while (RT_SUCCESS(rc))
849 {
850 /* Match the name. */
851 bool fMatch;
852 char *pszVolName;
853 rc = RTDvmVolumeQueryName(hVol, &pszVolName);
854 if (RT_SUCCESS(rc))
855 {
856 fMatch = RTStrCmp(pszEntry, pszVolName) == 0 && *pszVolName != '\0';
857 if (fMatch)
858 {
859 *phVolume = hVol;
860 *ppszSymlink = pszVolName;
861 *piVol = iVol;
862 return VINF_SUCCESS;
863 }
864 RTStrFree(pszVolName);
865 }
866 else if (rc == VERR_NOT_SUPPORTED)
867 fMatch = false;
868 else
869 {
870 RTDvmVolumeRelease(hVol);
871 break;
872 }
873
874 /* Match the sequential volume number. */
875 if (!fMatch)
876 {
877 char szTmp[16];
878 RTStrPrintf(szTmp, sizeof(szTmp), "vol%u", iVol);
879 fMatch = RTStrCmp(pszEntry, szTmp) == 0;
880 }
881
882 if (fMatch)
883 {
884 *phVolume = hVol;
885 *piVol = iVol;
886 return VINF_SUCCESS;
887 }
888
889 /* More volumes? */
890 iVol++;
891 if (iVol >= pVfsVol->cVolumes)
892 {
893 RTDvmVolumeRelease(hVol);
894 rc = VERR_FILE_NOT_FOUND;
895 break;
896 }
897
898 /* Get the next volume. */
899 RTDVMVOLUME hVolNext;
900 rc = RTDvmMapQueryNextVolume(pThis->pVfsVol->hVolMgr, hVol, &hVolNext);
901 RTDvmVolumeRelease(hVol);
902 hVol = hVolNext;
903 }
904 }
905 else
906 rc = VERR_FILE_NOT_FOUND;
907 return rc;
908}
909
910
911/**
912 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
913 */
914static DECLCALLBACK(int) rtDvmVfsDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen, uint32_t fFlags, PRTVFSOBJ phVfsObj)
915{
916 PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
917
918 /*
919 * Special case: '.' and '..'
920 */
921 if ( pszEntry[0] == '.'
922 && ( pszEntry[1] == '\0'
923 || ( pszEntry[1] == '.'
924 && pszEntry[2] == '\0')))
925 {
926 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
927 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
928 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
929 {
930 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
931 {
932 RTVFSDIR hVfsDir;
933 int rc = rtDvmVfsVol_OpenRoot(pThis->pVfsVol, &hVfsDir);
934 if (RT_SUCCESS(rc))
935 {
936 *phVfsObj = RTVfsObjFromDir(hVfsDir);
937 RTVfsDirRelease(hVfsDir);
938 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
939 }
940 return rc;
941 }
942 return VERR_IS_A_DIRECTORY;
943 }
944 return VERR_ACCESS_DENIED;
945 }
946
947 /*
948 * Open volume file.
949 */
950 RTDVMVOLUME hVolume = NIL_RTDVMVOLUME;
951 uint32_t iVol = 0;
952 char *pszSymlink = NULL;
953 int rc = rtDvmVfsDir_FindEntry(pThis, pszEntry, &hVolume, &iVol, &pszSymlink);
954 if (RT_SUCCESS(rc))
955 {
956 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
957 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
958 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
959 {
960 if (fFlags & (RTVFSOBJ_F_OPEN_FILE | RTVFSOBJ_F_OPEN_DEV_BLOCK))
961 {
962 if (!pszSymlink)
963 {
964 if ( !(fOpen & RTFILE_O_WRITE)
965 || !pThis->pVfsVol->fReadOnly)
966 {
967 /* Create file object. */
968 RTVFSFILE hVfsFile;
969 rc = rtDvmVfsCreateFileForVolume(pThis->pVfsVol, hVolume, fOpen, &hVfsFile);
970 if (RT_SUCCESS(rc))
971 {
972 *phVfsObj = RTVfsObjFromFile(hVfsFile);
973 RTVfsFileRelease(hVfsFile);
974 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
975 }
976 }
977 else
978 rc = VERR_WRITE_PROTECT;
979 }
980 else
981 rc = VERR_IS_A_SYMLINK;
982 }
983 else if (fFlags & RTVFSOBJ_F_OPEN_SYMLINK)
984 {
985 /* Create symlink object */
986 RTVFSSYMLINK hVfsSym = NIL_RTVFSSYMLINK; /* (older gcc maybe used uninitialized) */
987 rc = rtDvmVfsCreateSymlinkForVolume(hVolume, pThis->pVfsVol ? pThis->pVfsVol->hVolMgr : NIL_RTDVM, iVol,
988 pszSymlink, &hVfsSym);
989 if (RT_SUCCESS(rc))
990 {
991 *phVfsObj = RTVfsObjFromSymlink(hVfsSym);
992 RTVfsSymlinkRelease(hVfsSym);
993 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
994 pszSymlink = NULL;
995 }
996 }
997 else
998 rc = VERR_IS_A_FILE;
999 }
1000 else
1001 rc = VERR_ALREADY_EXISTS;
1002 RTDvmVolumeRelease(hVolume);
1003 if (pszSymlink)
1004 RTStrFree(pszSymlink);
1005 }
1006 return rc;
1007}
1008
1009
1010/**
1011 * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
1012 */
1013static DECLCALLBACK(int) rtDvmVfsDir_OpenFile(void *pvThis, const char *pszFilename, uint64_t fOpen, PRTVFSFILE phVfsFile)
1014{
1015 RTVFSOBJ hVfsObj;
1016 int rc = rtDvmVfsDir_Open(pvThis, pszFilename, fOpen, RTVFSOBJ_F_OPEN_FILE, &hVfsObj);
1017 if (RT_SUCCESS(rc))
1018 {
1019 *phVfsFile = RTVfsObjToFile(hVfsObj);
1020 RTVfsObjRelease(hVfsObj);
1021 }
1022 return rc;
1023}
1024
1025
1026/**
1027 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
1028 */
1029static DECLCALLBACK(int) rtDvmVfsDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
1030{
1031 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
1032 return VERR_NOT_SUPPORTED;
1033}
1034
1035
1036/**
1037 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
1038 */
1039static DECLCALLBACK(int) rtDvmVfsDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
1040{
1041 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
1042 return VERR_NOT_SUPPORTED;
1043}
1044
1045
1046/**
1047 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
1048 */
1049static DECLCALLBACK(int) rtDvmVfsDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
1050 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
1051{
1052 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
1053 return VERR_NOT_SUPPORTED;
1054}
1055
1056
1057/**
1058 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
1059 */
1060static DECLCALLBACK(int) rtDvmVfsDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
1061{
1062 RT_NOREF(pvThis, pszEntry, fType);
1063 return VERR_NOT_IMPLEMENTED;
1064}
1065
1066
1067/**
1068 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
1069 */
1070static DECLCALLBACK(int) rtDvmVfsDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
1071{
1072 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
1073 return VERR_NOT_IMPLEMENTED;
1074}
1075
1076
1077/**
1078 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
1079 */
1080static DECLCALLBACK(int) rtDvmVfsDir_RewindDir(void *pvThis)
1081{
1082 PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
1083
1084 if (pThis->hCurVolume != NIL_RTDVMVOLUME)
1085 {
1086 RTDvmVolumeRelease(pThis->hCurVolume);
1087 pThis->hCurVolume = NIL_RTDVMVOLUME;
1088 }
1089 pThis->fReturnCurrent = false;
1090 pThis->offDir = 0;
1091 if (pThis->pszNameAlias)
1092 {
1093 RTStrFree(pThis->pszNameAlias);
1094 pThis->pszNameAlias = NULL;
1095 }
1096
1097 return VINF_SUCCESS;
1098}
1099
1100
1101/**
1102 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
1103 */
1104static DECLCALLBACK(int) rtDvmVfsDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
1105 RTFSOBJATTRADD enmAddAttr)
1106{
1107 PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
1108 PRTDVMVFSVOL pVfsVol = pThis->pVfsVol;
1109 int rc;
1110
1111 /*
1112 * Format the volume name since we'll be needing it all but the final call.
1113 */
1114 char szVolNo[16];
1115 size_t const cchVolNo = RTStrPrintf(szVolNo, sizeof(szVolNo), "vol%u", pThis->offDir);
1116
1117 if (!pThis->fReturnCurrent)
1118 {
1119 /*
1120 * Do we have a pending name alias to return?
1121 */
1122 if (pThis->pszNameAlias)
1123 {
1124 size_t cchNameAlias = strlen(pThis->pszNameAlias);
1125 size_t cbNeeded = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cchNameAlias + 1]);
1126 if (cbNeeded <= *pcbDirEntry)
1127 {
1128 *pcbDirEntry = cbNeeded;
1129
1130 /* Do the names. */
1131 pDirEntry->cbName = (uint16_t)cchNameAlias;
1132 memcpy(pDirEntry->szName, pThis->pszNameAlias, cchNameAlias + 1);
1133 pDirEntry->cwcShortName = 0;
1134 pDirEntry->wszShortName[0] = '\0';
1135
1136
1137 /* Do the rest. */
1138 rc = rtDvmVfsSym_QueryInfoWorker(pThis->hCurVolume, pVfsVol->hVolMgr, szVolNo, &pDirEntry->Info, enmAddAttr);
1139 if (RT_SUCCESS(rc))
1140 {
1141 RTStrFree(pThis->pszNameAlias);
1142 pThis->pszNameAlias = NULL;
1143 pThis->offDir += 1;
1144 }
1145 return rc;
1146 }
1147
1148 *pcbDirEntry = cbNeeded;
1149 return VERR_BUFFER_OVERFLOW;
1150 }
1151
1152 /*
1153 * Get the next volume to return info about.
1154 */
1155 if (pThis->offDir < pVfsVol->cVolumes)
1156 {
1157 RTDVMVOLUME hNextVolume;
1158 if (pThis->offDir == 0)
1159 rc = RTDvmMapQueryFirstVolume(pVfsVol->hVolMgr, &hNextVolume);
1160 else
1161 rc = RTDvmMapQueryNextVolume(pVfsVol->hVolMgr, pThis->hCurVolume, &hNextVolume);
1162 if (RT_FAILURE(rc))
1163 return rc;
1164 RTDvmVolumeRelease(pThis->hCurVolume);
1165 pThis->hCurVolume = hNextVolume;
1166
1167 /* Check if we need to return a name alias later. */
1168 rc = RTDvmVolumeQueryName(pThis->hCurVolume, &pThis->pszNameAlias);
1169 if (RT_FAILURE(rc))
1170 pThis->pszNameAlias = NULL;
1171 else if (*pThis->pszNameAlias == '\0')
1172 {
1173 RTStrFree(pThis->pszNameAlias);
1174 pThis->pszNameAlias = NULL;
1175 }
1176 }
1177 else
1178 {
1179 RTDvmVolumeRelease(pThis->hCurVolume);
1180 pThis->hCurVolume = NIL_RTDVMVOLUME;
1181 return VERR_NO_MORE_FILES;
1182 }
1183 }
1184
1185 /*
1186 * Figure out the name length.
1187 */
1188 size_t cbNeeded = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cchVolNo + 1]);
1189 if (cbNeeded <= *pcbDirEntry)
1190 {
1191 *pcbDirEntry = cbNeeded;
1192
1193 /* Do the names. */
1194 pDirEntry->cbName = (uint16_t)cchVolNo;
1195 memcpy(pDirEntry->szName, szVolNo, cchVolNo + 1);
1196 pDirEntry->cwcShortName = 0;
1197 pDirEntry->wszShortName[0] = '\0';
1198
1199 /* Do the rest. */
1200 rc = rtDvmVfsFile_QueryInfoWorker(pThis->hCurVolume, pVfsVol->hVolMgr, pVfsVol->fReadOnly, &pDirEntry->Info, enmAddAttr);
1201 if (RT_SUCCESS(rc))
1202 {
1203 pThis->fReturnCurrent = false;
1204 if (!pThis->pszNameAlias)
1205 pThis->offDir += 1;
1206 return rc;
1207 }
1208 }
1209 else
1210 {
1211 *pcbDirEntry = cbNeeded;
1212 rc = VERR_BUFFER_OVERFLOW;
1213 }
1214 pThis->fReturnCurrent = true;
1215 return rc;
1216}
1217
1218
1219/**
1220 * DVM (root) directory operations.
1221 */
1222static const RTVFSDIROPS g_rtDvmVfsDirOps =
1223{
1224 { /* Obj */
1225 RTVFSOBJOPS_VERSION,
1226 RTVFSOBJTYPE_DIR,
1227 "DvmDir",
1228 rtDvmVfsDir_Close,
1229 rtDvmVfsDir_QueryInfo,
1230 NULL,
1231 RTVFSOBJOPS_VERSION
1232 },
1233 RTVFSDIROPS_VERSION,
1234 0,
1235 { /* ObjSet */
1236 RTVFSOBJSETOPS_VERSION,
1237 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
1238 rtDvmVfsDir_SetMode,
1239 rtDvmVfsDir_SetTimes,
1240 rtDvmVfsDir_SetOwner,
1241 RTVFSOBJSETOPS_VERSION
1242 },
1243 rtDvmVfsDir_Open,
1244 NULL /* pfnFollowAbsoluteSymlink */,
1245 rtDvmVfsDir_OpenFile,
1246 NULL /* pfnOpenDir */,
1247 rtDvmVfsDir_CreateDir,
1248 rtDvmVfsDir_OpenSymlink,
1249 rtDvmVfsDir_CreateSymlink,
1250 NULL /* pfnQueryEntryInfo */,
1251 rtDvmVfsDir_UnlinkEntry,
1252 rtDvmVfsDir_RenameEntry,
1253 rtDvmVfsDir_RewindDir,
1254 rtDvmVfsDir_ReadDir,
1255 RTVFSDIROPS_VERSION,
1256};
1257
1258
1259
1260/**
1261 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
1262 */
1263static DECLCALLBACK(int) rtDvmVfsVol_Close(void *pvThis)
1264{
1265 PRTDVMVFSVOL pThis = (PRTDVMVFSVOL)pvThis;
1266 LogFlow(("rtDvmVfsVol_Close(%p)\n", pThis));
1267
1268 if ( pThis->fCloseDvm
1269 && pThis->hVolMgr != NIL_RTDVM )
1270 RTDvmRelease(pThis->hVolMgr);
1271 pThis->hVolMgr = NIL_RTDVM;
1272
1273 return VINF_SUCCESS;
1274}
1275
1276
1277/**
1278 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
1279 */
1280static DECLCALLBACK(int) rtDvmVfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1281{
1282 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
1283 return VERR_WRONG_TYPE;
1284}
1285
1286
1287/**
1288 * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
1289 */
1290static DECLCALLBACK(int) rtDvmVfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
1291{
1292 PRTDVMVFSVOL pThis = (PRTDVMVFSVOL)pvThis;
1293
1294 PRTDVMVFSDIR pNewDir;
1295 int rc = RTVfsNewDir(&g_rtDvmVfsDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
1296 NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
1297 if (RT_SUCCESS(rc))
1298 {
1299 pNewDir->offDir = 0;
1300 pNewDir->pVfsVol = pThis;
1301 pNewDir->fReturnCurrent = false;
1302 pNewDir->pszNameAlias = NULL;
1303 pNewDir->hCurVolume = NIL_RTDVMVOLUME;
1304 }
1305 return rc;
1306}
1307
1308
1309/**
1310 * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
1311 */
1312static DECLCALLBACK(int) rtDvmVfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
1313{
1314 RT_NOREF(pvThis, off, cb, pfUsed);
1315 return VERR_NOT_IMPLEMENTED;
1316}
1317
1318
1319DECL_HIDDEN_CONST(const RTVFSOPS) g_rtDvmVfsVolOps =
1320{
1321 { /* Obj */
1322 RTVFSOBJOPS_VERSION,
1323 RTVFSOBJTYPE_VFS,
1324 "DvmVol",
1325 rtDvmVfsVol_Close,
1326 rtDvmVfsVol_QueryInfo,
1327 NULL,
1328 RTVFSOBJOPS_VERSION
1329 },
1330 RTVFSOPS_VERSION,
1331 0 /* fFeatures */,
1332 rtDvmVfsVol_OpenRoot,
1333 rtDvmVfsVol_QueryRangeState,
1334 RTVFSOPS_VERSION
1335};
1336
1337
1338
1339/**
1340 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
1341 */
1342static DECLCALLBACK(int) rtDvmVfsChain_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
1343 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
1344{
1345 RT_NOREF(pProviderReg, pSpec);
1346
1347 /*
1348 * Basic checks.
1349 */
1350 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
1351 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
1352 if (pElement->enmType != RTVFSOBJTYPE_VFS)
1353 return VERR_VFS_CHAIN_ONLY_VFS;
1354
1355 if (pElement->cArgs > 1)
1356 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
1357
1358 /*
1359 * Parse the flag if present, save in pElement->uProvider.
1360 */
1361 /** @todo allow specifying sector size */
1362 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
1363 if (pElement->cArgs > 0)
1364 {
1365 const char *psz = pElement->paArgs[0].psz;
1366 if (*psz)
1367 {
1368 if ( !strcmp(psz, "ro")
1369 || !strcmp(psz, "r"))
1370 fReadOnly = true;
1371 else if (!strcmp(psz, "rw"))
1372 fReadOnly = false;
1373 else
1374 {
1375 *poffError = pElement->paArgs[0].offSpec;
1376 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
1377 }
1378 }
1379 }
1380
1381 pElement->uProvider = fReadOnly;
1382 return VINF_SUCCESS;
1383}
1384
1385
1386/**
1387 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
1388 */
1389static DECLCALLBACK(int) rtDvmVfsChain_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
1390 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
1391 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
1392{
1393 RT_NOREF(pProviderReg, pSpec, poffError, pErrInfo);
1394 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
1395
1396 /*
1397 * Instantiate the volume manager and open the map stuff.
1398 */
1399 RTVFSFILE hPrevVfsFile = RTVfsObjToFile(hPrevVfsObj);
1400 AssertReturn(hPrevVfsFile != NIL_RTVFSFILE, VERR_VFS_CHAIN_CAST_FAILED);
1401
1402 RTDVM hVolMgr;
1403 int rc = RTDvmCreate(&hVolMgr, hPrevVfsFile, 512, 0 /*fFlags*/);
1404 RTVfsFileRelease(hPrevVfsFile);
1405 if (RT_SUCCESS(rc))
1406 {
1407 rc = RTDvmMapOpen(hVolMgr);
1408 if (RT_SUCCESS(rc))
1409 {
1410 /*
1411 * Create a VFS instance for the volume manager.
1412 */
1413 RTVFS hVfs = NIL_RTVFS;
1414 PRTDVMVFSVOL pThis = NULL;
1415 rc = RTVfsNew(&g_rtDvmVfsVolOps, sizeof(RTDVMVFSVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
1416 if (RT_SUCCESS(rc))
1417 {
1418 pThis->hVolMgr = hVolMgr;
1419 pThis->fCloseDvm = true;
1420 pThis->fReadOnly = pElement->uProvider == (uint64_t)true;
1421 pThis->cVolumes = RTDvmMapGetValidVolumes(hVolMgr);
1422 pThis->hVfsSelf = hVfs;
1423
1424 *phVfsObj = RTVfsObjFromVfs(hVfs);
1425 RTVfsRelease(hVfs);
1426 return *phVfsObj != NIL_RTVFSOBJ ? VINF_SUCCESS : VERR_VFS_CHAIN_CAST_FAILED;
1427 }
1428 }
1429 else
1430 rc = RTErrInfoSetF(pErrInfo, rc, "RTDvmMapOpen failed: %Rrc", rc);
1431 RTDvmRelease(hVolMgr);
1432 }
1433 else
1434 rc = RTErrInfoSetF(pErrInfo, rc, "RTDvmCreate failed: %Rrc", rc);
1435 return rc;
1436}
1437
1438
1439/**
1440 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
1441 */
1442static DECLCALLBACK(bool) rtDvmVfsChain_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
1443 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
1444 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
1445{
1446 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
1447 return false;
1448}
1449
1450
1451/** VFS chain element 'file'. */
1452static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
1453{
1454 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
1455 /* fReserved = */ 0,
1456 /* pszName = */ "dvm",
1457 /* ListEntry = */ { NULL, NULL },
1458 /* pszHelp = */ "Opens a container image using the VD API.\n"
1459 "Optionally takes one parameter 'ro' (read only) or 'rw' (read write).\n",
1460 /* pfnValidate = */ rtDvmVfsChain_Validate,
1461 /* pfnInstantiate = */ rtDvmVfsChain_Instantiate,
1462 /* pfnCanReuseElement = */ rtDvmVfsChain_CanReuseElement,
1463 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
1464};
1465
1466RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
1467
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use