VirtualBox

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

Last change on this file was 100908, checked in by vboxsync, 8 months ago

IPRT,Storage,Puel: Changed the pfnRead and pfnWrite VFS methods and the RTVfsIoStrmSgRead, RTVfsIoStrmSgWrite, RTVfsFileSgRead and RTVfsFileSgWrite APIs to advance pSgBuf and respect the incoming position just like RTFileSgRead & RTFileSgWrite.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use