VirtualBox

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

Last change on this file since 80346 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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