VirtualBox

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

Last change on this file 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: 30.7 KB
Line 
1/* $Id: dvm.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT Disk Volume Management API (DVM) - generic code.
4 */
5
6/*
7 * Copyright (C) 2011-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#include <iprt/types.h>
42#include <iprt/assert.h>
43#include <iprt/mem.h>
44#include <iprt/dvm.h>
45#include <iprt/err.h>
46#include <iprt/asm.h>
47#include <iprt/string.h>
48#include <iprt/list.h>
49#include "internal/dvm.h"
50
51
52/*********************************************************************************************************************************
53* Structures and Typedefs *
54*********************************************************************************************************************************/
55
56/**
57 * The internal volume manager structure.
58 */
59typedef struct RTDVMINTERNAL
60{
61 /** The DVM magic (RTDVM_MAGIC). */
62 uint32_t u32Magic;
63 /** The disk descriptor. */
64 RTDVMDISK DvmDisk;
65 /** Pointer to the backend operations table after a successful probe. */
66 PCRTDVMFMTOPS pDvmFmtOps;
67 /** The format specific volume manager data. */
68 RTDVMFMT hVolMgrFmt;
69 /** Flags passed on manager creation. */
70 uint32_t fFlags;
71 /** Reference counter. */
72 uint32_t volatile cRefs;
73 /** List of recognised volumes (RTDVMVOLUMEINTERNAL). */
74 RTLISTANCHOR VolumeList;
75} RTDVMINTERNAL;
76/** Pointer to an internal volume manager. */
77typedef RTDVMINTERNAL *PRTDVMINTERNAL;
78
79/**
80 * The internal volume structure.
81 */
82typedef struct RTDVMVOLUMEINTERNAL
83{
84 /** The DVM volume magic (RTDVMVOLUME_MAGIC). */
85 uint32_t u32Magic;
86 /** Node for the volume list. */
87 RTLISTNODE VolumeNode;
88 /** Pointer to the owning volume manager. */
89 PRTDVMINTERNAL pVolMgr;
90 /** Format specific volume data. */
91 RTDVMVOLUMEFMT hVolFmt;
92 /** Set block status.callback */
93 PFNDVMVOLUMEQUERYBLOCKSTATUS pfnQueryBlockStatus;
94 /** Opaque user data. */
95 void *pvUser;
96 /** Reference counter. */
97 uint32_t volatile cRefs;
98} RTDVMVOLUMEINTERNAL;
99/** Pointer to an internal volume. */
100typedef RTDVMVOLUMEINTERNAL *PRTDVMVOLUMEINTERNAL;
101
102
103/*********************************************************************************************************************************
104* Global variables *
105*********************************************************************************************************************************/
106/**
107 * Supported volume formats.
108 */
109static PCRTDVMFMTOPS const g_aDvmFmts[] =
110{
111 &g_rtDvmFmtMbr,
112 &g_rtDvmFmtGpt,
113 &g_rtDvmFmtBsdLbl
114};
115
116/**
117 * Descriptions of the volume types.
118 *
119 * This is indexed by RTDVMVOLTYPE.
120 */
121static const char * const g_apszDvmVolTypes[] =
122{
123 "Invalid",
124 "Unknown",
125 "NTFS",
126 "FAT12",
127 "FAT16",
128 "FAT32",
129
130 "EFI system partition",
131
132 "Mac OS X HFS or HFS+",
133 "Mac OS X APFS",
134
135 "Linux swap",
136 "Linux native",
137 "Linux LVM",
138 "Linux SoftRaid",
139
140 "FreeBSD",
141 "NetBSD",
142 "OpenBSD",
143 "Solaris",
144
145 "Basic data partition",
146 "Microsoft reserved partition",
147 "Windows LDM metadata",
148 "Windows LDM data",
149 "Windows recovery partition",
150 "Windows storage spaces",
151
152 "IBM GPFS",
153
154 "OS/2",
155};
156AssertCompile(RT_ELEMENTS(g_apszDvmVolTypes) == RTDVMVOLTYPE_END);
157
158
159/**
160 * Read from the disk at the given offset, neither the offset nor the size is
161 * necessary sector aligned.
162 *
163 * @returns IPRT status code.
164 * @param pDisk The disk descriptor to read from.
165 * @param off Start offset.
166 * @param pvBuf Destination buffer.
167 * @param cbRead How much to read.
168 */
169DECLHIDDEN(int) rtDvmDiskReadUnaligned(PCRTDVMDISK pDisk, uint64_t off, void *pvBuf, size_t cbRead)
170{
171 size_t const cbSector = (size_t)pDisk->cbSector;
172 size_t const offDelta = off % cbSector;
173 size_t const cbDelta = cbRead % cbSector;
174 if (!cbDelta && !offDelta)
175 return rtDvmDiskRead(pDisk, off, pvBuf, cbRead);
176
177 int rc;
178 size_t cbExtra = offDelta + (cbDelta ? cbSector - cbDelta: 0);
179 uint8_t *pbTmpBuf = (uint8_t *)RTMemTmpAlloc(cbRead + cbExtra);
180 if (pbTmpBuf)
181 {
182 rc = rtDvmDiskRead(pDisk, off - offDelta, pbTmpBuf, cbRead + cbExtra);
183 if (RT_SUCCESS(rc))
184 memcpy(pvBuf, &pbTmpBuf[offDelta], cbRead);
185 else
186 RT_BZERO(pvBuf, cbRead);
187 RTMemTmpFree(pbTmpBuf);
188 }
189 else
190 rc = VERR_NO_TMP_MEMORY;
191 return rc;
192}
193
194
195/**
196 * Creates a new volume.
197 *
198 * @returns IPRT status code.
199 * @param pThis The DVM map instance.
200 * @param hVolFmt The format specific volume handle.
201 * @param phVol Where to store the generic volume handle on success.
202 */
203static int rtDvmVolumeCreate(PRTDVMINTERNAL pThis, RTDVMVOLUMEFMT hVolFmt, PRTDVMVOLUME phVol)
204{
205 PRTDVMVOLUMEINTERNAL pVol = (PRTDVMVOLUMEINTERNAL)RTMemAllocZ(sizeof(RTDVMVOLUMEINTERNAL));
206 if (pVol)
207 {
208 pVol->u32Magic = RTDVMVOLUME_MAGIC;
209 pVol->cRefs = 0;
210 pVol->pVolMgr = pThis;
211 pVol->hVolFmt = hVolFmt;
212
213 *phVol = pVol;
214 return VINF_SUCCESS;
215 }
216 return VERR_NO_MEMORY;
217}
218
219/**
220 * Destroys a volume handle.
221 *
222 * @param pThis The volume manager instance.
223 * @param pVol The volume to destroy.
224 */
225static void rtDvmVolumeDestroy(PRTDVMINTERNAL pThis, PRTDVMVOLUMEINTERNAL pVol)
226{
227 AssertPtr(pThis);
228 AssertPtr(pThis->pDvmFmtOps);
229 Assert(pVol->pVolMgr == pThis);
230
231 /* Close the volume. */
232 pThis->pDvmFmtOps->pfnVolumeClose(pVol->hVolFmt);
233
234 pVol->u32Magic = RTDVMVOLUME_MAGIC_DEAD;
235 pVol->pVolMgr = NULL;
236 pVol->hVolFmt = NIL_RTDVMVOLUMEFMT;
237 RTMemFree(pVol);
238}
239
240
241RTDECL(int) RTDvmCreate(PRTDVM phVolMgr, RTVFSFILE hVfsFile, uint32_t cbSector, uint32_t fFlags)
242{
243 AssertMsgReturn(!(fFlags & ~DVM_FLAGS_VALID_MASK), ("Invalid flags given %#x\n", fFlags), VERR_INVALID_FLAGS);
244 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
245 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
246
247 uint64_t cbDisk;
248 int rc = RTVfsFileQuerySize(hVfsFile, &cbDisk);
249 if (RT_SUCCESS(rc))
250 {
251 PRTDVMINTERNAL pThis = (PRTDVMINTERNAL)RTMemAllocZ(sizeof(RTDVMINTERNAL));
252 if (pThis)
253 {
254 pThis->u32Magic = RTDVM_MAGIC;
255 pThis->DvmDisk.cbDisk = cbDisk;
256 pThis->DvmDisk.cbSector = cbSector;
257 pThis->DvmDisk.hVfsFile = hVfsFile;
258
259 pThis->pDvmFmtOps = NULL;
260 pThis->hVolMgrFmt = NIL_RTDVMFMT;
261 pThis->fFlags = fFlags;
262 pThis->cRefs = 1;
263 RTListInit(&pThis->VolumeList);
264
265 *phVolMgr = pThis;
266 return VINF_SUCCESS;
267 }
268 rc = VERR_NO_MEMORY;
269 }
270 RTVfsFileRelease(hVfsFile);
271 return rc;
272}
273
274
275RTDECL(uint32_t) RTDvmRetain(RTDVM hVolMgr)
276{
277 PRTDVMINTERNAL pThis = hVolMgr;
278 AssertPtrReturn(pThis, UINT32_MAX);
279 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, UINT32_MAX);
280
281 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
282 AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
283 return cRefs;
284}
285
286/**
287 * Destroys a volume manager handle.
288 *
289 * @param pThis The volume manager to destroy.
290 */
291static void rtDvmDestroy(PRTDVMINTERNAL pThis)
292{
293 pThis->u32Magic = RTDVM_MAGIC_DEAD;
294
295 if (pThis->hVolMgrFmt != NIL_RTDVMFMT)
296 {
297 AssertPtr(pThis->pDvmFmtOps);
298
299 /* */
300 PRTDVMVOLUMEINTERNAL pItNext, pIt;
301 RTListForEachSafe(&pThis->VolumeList, pIt, pItNext, RTDVMVOLUMEINTERNAL, VolumeNode)
302 {
303 RTListNodeRemove(&pIt->VolumeNode);
304 rtDvmVolumeDestroy(pThis, pIt);
305 }
306
307 /* Let the backend do it's own cleanup first. */
308 pThis->pDvmFmtOps->pfnClose(pThis->hVolMgrFmt);
309 pThis->hVolMgrFmt = NIL_RTDVMFMT;
310 pThis->pDvmFmtOps = NULL;
311 }
312
313 pThis->DvmDisk.cbDisk = 0;
314 pThis->DvmDisk.cbSector = 0;
315 if (pThis->DvmDisk.hVfsFile != NIL_RTVFSFILE)
316 {
317 RTVfsFileRelease(pThis->DvmDisk.hVfsFile);
318 pThis->DvmDisk.hVfsFile = NIL_RTVFSFILE;
319 }
320
321 RTMemFree(pThis);
322}
323
324RTDECL(uint32_t) RTDvmRelease(RTDVM hVolMgr)
325{
326 PRTDVMINTERNAL pThis = hVolMgr;
327 if (pThis == NIL_RTDVM)
328 return 0;
329 AssertPtrReturn(pThis, UINT32_MAX);
330 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, UINT32_MAX);
331
332 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
333 AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
334 if (cRefs == 0)
335 rtDvmDestroy(pThis);
336 return cRefs;
337}
338
339RTDECL(int) RTDvmMapOpen(RTDVM hVolMgr)
340{
341 PRTDVMINTERNAL pThis = hVolMgr;
342 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
343 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE);
344 AssertReturn(pThis->hVolMgrFmt == NIL_RTDVMFMT, VERR_WRONG_ORDER);
345
346 Assert(!pThis->pDvmFmtOps);
347
348 /*
349 * Let each format backend have a go at the disk, pick the one which scores the highest.
350 */
351 int rc = VINF_SUCCESS;
352 uint32_t uScoreMax = RTDVM_MATCH_SCORE_UNSUPPORTED;
353 PCRTDVMFMTOPS pDvmFmtOpsMatch = NULL;
354 for (unsigned i = 0; i < RT_ELEMENTS(g_aDvmFmts); i++)
355 {
356 uint32_t uScore = 0;
357 PCRTDVMFMTOPS pDvmFmtOps = g_aDvmFmts[i];
358
359 rc = pDvmFmtOps->pfnProbe(&pThis->DvmDisk, &uScore);
360 if (RT_SUCCESS(rc))
361 {
362 if (uScore > uScoreMax)
363 {
364 pDvmFmtOpsMatch = pDvmFmtOps;
365 uScoreMax = uScore;
366 }
367 }
368 else
369 return rc;
370 }
371 if (uScoreMax > RTDVM_MATCH_SCORE_UNSUPPORTED)
372 {
373 AssertPtr(pDvmFmtOpsMatch);
374
375 /*
376 * Open the format.
377 */
378 rc = pDvmFmtOpsMatch->pfnOpen(&pThis->DvmDisk, &pThis->hVolMgrFmt);
379 if (RT_SUCCESS(rc))
380 {
381 pThis->pDvmFmtOps = pDvmFmtOpsMatch;
382
383 /*
384 * Construct volume list (we're done if none).
385 */
386 uint32_t cVols = pThis->pDvmFmtOps->pfnGetValidVolumes(pThis->hVolMgrFmt);
387 if (cVols == 0)
388 return VINF_SUCCESS;
389
390 /* First volume. */
391 RTDVMVOLUMEFMT hVolFmt = NIL_RTDVMVOLUMEFMT;
392 rc = pThis->pDvmFmtOps->pfnQueryFirstVolume(pThis->hVolMgrFmt, &hVolFmt);
393 if (RT_SUCCESS(rc))
394 {
395 for (;;)
396 {
397 PRTDVMVOLUMEINTERNAL pVol = NULL;
398 rc = rtDvmVolumeCreate(pThis, hVolFmt, &pVol);
399 if (RT_FAILURE(rc))
400 {
401 pThis->pDvmFmtOps->pfnVolumeClose(hVolFmt);
402 break;
403 }
404 RTListAppend(&pThis->VolumeList, &pVol->VolumeNode);
405
406 /* Done?*/
407 cVols--;
408 if (cVols < 1)
409 return VINF_SUCCESS;
410
411 /* Next volume. */
412 rc = pThis->pDvmFmtOps->pfnQueryNextVolume(pThis->hVolMgrFmt, pVol->hVolFmt, &hVolFmt);
413 if (RT_FAILURE(rc))
414 break;
415 }
416
417 /* Bail out. */
418 PRTDVMVOLUMEINTERNAL pItNext, pIt;
419 RTListForEachSafe(&pThis->VolumeList, pIt, pItNext, RTDVMVOLUMEINTERNAL, VolumeNode)
420 {
421 RTListNodeRemove(&pIt->VolumeNode);
422 rtDvmVolumeDestroy(pThis, pIt);
423 }
424 }
425
426 pDvmFmtOpsMatch->pfnClose(pThis->hVolMgrFmt);
427 }
428 }
429 else
430 rc = VERR_NOT_SUPPORTED;
431 return rc;
432}
433
434RTDECL(int) RTDvmMapInitialize(RTDVM hVolMgr, const char *pszFmt)
435{
436 PRTDVMINTERNAL pThis = hVolMgr;
437 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
438 AssertPtrReturn(pszFmt, VERR_INVALID_POINTER);
439 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE);
440 AssertReturn(pThis->hVolMgrFmt == NIL_RTDVMFMT, VERR_WRONG_ORDER);
441
442 for (unsigned i = 0; i < RT_ELEMENTS(g_aDvmFmts); i++)
443 {
444 PCRTDVMFMTOPS pDvmFmtOps = g_aDvmFmts[i];
445 if (!RTStrCmp(pDvmFmtOps->pszFmt, pszFmt))
446 {
447 int rc = pDvmFmtOps->pfnInitialize(&pThis->DvmDisk, &pThis->hVolMgrFmt);
448 if (RT_SUCCESS(rc))
449 pThis->pDvmFmtOps = pDvmFmtOps;
450 return rc;
451 }
452 }
453 return VERR_NOT_SUPPORTED;
454}
455
456RTDECL(const char *) RTDvmMapGetFormatName(RTDVM hVolMgr)
457{
458 PRTDVMINTERNAL pThis = hVolMgr;
459 AssertPtrReturn(pThis, NULL);
460 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, NULL);
461 AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, NULL);
462
463 return pThis->pDvmFmtOps->pszFmt;
464}
465
466RTDECL(RTDVMFORMATTYPE) RTDvmMapGetFormatType(RTDVM hVolMgr)
467{
468 PRTDVMINTERNAL pThis = hVolMgr;
469 AssertPtrReturn(pThis, RTDVMFORMATTYPE_INVALID);
470 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, RTDVMFORMATTYPE_INVALID);
471 AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, RTDVMFORMATTYPE_INVALID);
472
473 return pThis->pDvmFmtOps->enmFormat;
474}
475
476RTDECL(int) RTDvmMapQueryDiskUuid(RTDVM hVolMgr, PRTUUID pUuid)
477{
478 PRTDVMINTERNAL pThis = hVolMgr;
479 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
480 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE);
481 AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, VERR_INVALID_HANDLE);
482 AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
483
484 if (pThis->pDvmFmtOps->pfnQueryDiskUuid)
485 return pThis->pDvmFmtOps->pfnQueryDiskUuid(pThis->hVolMgrFmt, pUuid);
486 return VERR_NOT_SUPPORTED;
487}
488
489RTDECL(uint32_t) RTDvmMapGetValidVolumes(RTDVM hVolMgr)
490{
491 PRTDVMINTERNAL pThis = hVolMgr;
492 AssertPtrReturn(pThis, UINT32_MAX);
493 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, UINT32_MAX);
494 AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, UINT32_MAX);
495
496 return pThis->pDvmFmtOps->pfnGetValidVolumes(pThis->hVolMgrFmt);
497}
498
499RTDECL(uint32_t) RTDvmMapGetMaxVolumes(RTDVM hVolMgr)
500{
501 PRTDVMINTERNAL pThis = hVolMgr;
502 AssertPtrReturn(pThis, UINT32_MAX);
503 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, UINT32_MAX);
504 AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, UINT32_MAX);
505
506 return pThis->pDvmFmtOps->pfnGetMaxVolumes(pThis->hVolMgrFmt);
507}
508
509RTDECL(int) RTDvmMapQueryFirstVolume(RTDVM hVolMgr, PRTDVMVOLUME phVol)
510{
511 PRTDVMINTERNAL pThis = hVolMgr;
512 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
513 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE);
514 AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, VERR_INVALID_HANDLE);
515 AssertPtrReturn(phVol, VERR_INVALID_POINTER);
516
517 int rc = VERR_DVM_MAP_EMPTY;
518 PRTDVMVOLUMEINTERNAL pVol = RTListGetFirst(&pThis->VolumeList, RTDVMVOLUMEINTERNAL, VolumeNode);
519 if (pVol)
520 {
521 rc = VINF_SUCCESS;
522 RTDvmVolumeRetain(pVol);
523 *phVol = pVol;
524 }
525
526 return rc;
527}
528
529RTDECL(int) RTDvmMapQueryNextVolume(RTDVM hVolMgr, RTDVMVOLUME hVol, PRTDVMVOLUME phVolNext)
530{
531 PRTDVMINTERNAL pThis = hVolMgr;
532 PRTDVMVOLUMEINTERNAL pVol = hVol;
533 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
534 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE);
535 AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, VERR_INVALID_HANDLE);
536 AssertPtrReturn(pVol, VERR_INVALID_HANDLE);
537 AssertReturn(pVol->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE);
538 AssertPtrReturn(phVolNext, VERR_INVALID_POINTER);
539
540 int rc = VERR_DVM_MAP_NO_VOLUME;
541 PRTDVMVOLUMEINTERNAL pVolNext = RTListGetNext(&pThis->VolumeList, pVol, RTDVMVOLUMEINTERNAL, VolumeNode);
542 if (pVolNext)
543 {
544 rc = VINF_SUCCESS;
545 RTDvmVolumeRetain(pVolNext);
546 *phVolNext = pVolNext;
547 }
548
549 return rc;
550}
551
552RTDECL(int) RTDvmMapQueryBlockStatus(RTDVM hVolMgr, uint64_t off, uint64_t cb, bool *pfAllocated)
553{
554 PRTDVMINTERNAL pThis = hVolMgr;
555
556 /*
557 * Input validation.
558 */
559 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
560 AssertPtrReturn(pfAllocated, VERR_INVALID_POINTER);
561 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE);
562 AssertReturn(pThis->hVolMgrFmt != NIL_RTDVMFMT, VERR_WRONG_ORDER);
563 AssertMsgReturn( off <= pThis->DvmDisk.cbDisk
564 || cb <= pThis->DvmDisk.cbDisk
565 || off + cb <= pThis->DvmDisk.cbDisk,
566 ("off=%#RX64 cb=%#RX64 cbDisk=%#RX64\n", off, cb, pThis->DvmDisk.cbDisk),
567 VERR_OUT_OF_RANGE);
568
569 /*
570 * Check whether the range is inuse by the volume manager metadata first.
571 */
572 int rc = pThis->pDvmFmtOps->pfnQueryRangeUse(pThis->hVolMgrFmt, off, cb, pfAllocated);
573 if (RT_FAILURE(rc) || *pfAllocated)
574 return rc;
575
576 /*
577 * Not used by volume manager metadata, so work thru the specified range one
578 * volume / void (free space) at a time. All must be unallocated for us to
579 * reach the end, we return immediately if any portion is allocated.
580 */
581 while (cb > 0)
582 {
583 /*
584 * Search through all volumes.
585 *
586 * It is not possible to get all start sectors and sizes of all volumes
587 * here because volumes can be scattered around the disk for certain formats.
588 * Linux LVM is one example, it extents of logical volumes don't need to be
589 * contiguous on the medium.
590 */
591 bool fVolFound = false;
592 PRTDVMVOLUMEINTERNAL pVol;
593 RTListForEach(&pThis->VolumeList, pVol, RTDVMVOLUMEINTERNAL, VolumeNode)
594 {
595 uint64_t cbIntersect;
596 uint64_t offVol;
597 bool fIntersect = pThis->pDvmFmtOps->pfnVolumeIsRangeIntersecting(pVol->hVolFmt, off, cb, &offVol, &cbIntersect);
598 if (fIntersect)
599 {
600 fVolFound = true;
601 if (pVol->pfnQueryBlockStatus)
602 {
603 bool fVolAllocated = true;
604 rc = pVol->pfnQueryBlockStatus(pVol->pvUser, offVol, cbIntersect, &fVolAllocated);
605 if (RT_FAILURE(rc) || fVolAllocated)
606 {
607 *pfAllocated = true;
608 return rc;
609 }
610 }
611 else if (!(pThis->fFlags & DVM_FLAGS_NO_STATUS_CALLBACK_MARK_AS_UNUSED))
612 {
613 *pfAllocated = true;
614 return VINF_SUCCESS;
615 }
616 /* else, flag is set, continue. */
617
618 cb -= cbIntersect;
619 off += cbIntersect;
620 break;
621 }
622 }
623
624 if (!fVolFound)
625 {
626 if (pThis->fFlags & DVM_FLAGS_UNUSED_SPACE_MARK_AS_USED)
627 {
628 *pfAllocated = true;
629 return VINF_SUCCESS;
630 }
631
632 cb -= pThis->DvmDisk.cbSector;
633 off += pThis->DvmDisk.cbSector;
634 }
635 }
636
637 *pfAllocated = false;
638 return rc;
639}
640
641RTDECL(int) RTDvmMapQueryTableLocations(RTDVM hVolMgr, uint32_t fFlags,
642 PRTDVMTABLELOCATION paLocations, size_t cLocations, size_t *pcActual)
643{
644 PRTDVMINTERNAL pThis = hVolMgr;
645
646 /*
647 * Input validation.
648 */
649 if (cLocations)
650 {
651 AssertPtrReturn(paLocations, VERR_INVALID_POINTER);
652 if (pcActual)
653 {
654 AssertPtrReturn(pcActual, VERR_INVALID_POINTER);
655 *pcActual = 0;
656 }
657 }
658 else
659 {
660 AssertPtrReturn(pcActual, VERR_INVALID_POINTER);
661 *pcActual = 0;
662 }
663 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
664 AssertReturn(pThis->u32Magic == RTDVM_MAGIC, VERR_INVALID_HANDLE);
665 AssertReturn(!(fFlags & ~RTDVMMAPQTABLOC_F_VALID_MASK), VERR_INVALID_FLAGS);
666
667 /*
668 * Pass it down to the format backend.
669 */
670 return pThis->pDvmFmtOps->pfnQueryTableLocations(pThis->hVolMgrFmt, fFlags, paLocations, cLocations, pcActual);
671}
672
673RTDECL(uint32_t) RTDvmVolumeRetain(RTDVMVOLUME hVol)
674{
675 PRTDVMVOLUMEINTERNAL pThis = hVol;
676 AssertPtrReturn(pThis, UINT32_MAX);
677 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, UINT32_MAX);
678
679 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
680 AssertMsg(cRefs >= 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
681 if (cRefs == 1)
682 RTDvmRetain(pThis->pVolMgr);
683 return cRefs;
684}
685
686RTDECL(uint32_t) RTDvmVolumeRelease(RTDVMVOLUME hVol)
687{
688 PRTDVMVOLUMEINTERNAL pThis = hVol;
689 if (pThis == NIL_RTDVMVOLUME)
690 return 0;
691 AssertPtrReturn(pThis, UINT32_MAX);
692 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, UINT32_MAX);
693
694 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
695 AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
696 if (cRefs == 0)
697 {
698 /* Release the volume manager. */
699 pThis->pfnQueryBlockStatus = NULL;
700 RTDvmRelease(pThis->pVolMgr);
701 }
702 return cRefs;
703}
704
705RTDECL(void) RTDvmVolumeSetQueryBlockStatusCallback(RTDVMVOLUME hVol,
706 PFNDVMVOLUMEQUERYBLOCKSTATUS pfnQueryBlockStatus,
707 void *pvUser)
708{
709 PRTDVMVOLUMEINTERNAL pThis = hVol;
710 AssertPtrReturnVoid(pThis);
711 AssertReturnVoid(pThis->u32Magic == RTDVMVOLUME_MAGIC);
712
713 pThis->pfnQueryBlockStatus = pfnQueryBlockStatus;
714 pThis->pvUser = pvUser;
715}
716
717RTDECL(uint64_t) RTDvmVolumeGetSize(RTDVMVOLUME hVol)
718{
719 PRTDVMVOLUMEINTERNAL pThis = hVol;
720 AssertPtrReturn(pThis, 0);
721 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, 0);
722
723 return pThis->pVolMgr->pDvmFmtOps->pfnVolumeGetSize(pThis->hVolFmt);
724}
725
726RTDECL(int) RTDvmVolumeQueryName(RTDVMVOLUME hVol, char **ppszVolName)
727{
728 PRTDVMVOLUMEINTERNAL pThis = hVol;
729 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
730 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE);
731 AssertReturn(ppszVolName, VERR_INVALID_POINTER);
732
733 return pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryName(pThis->hVolFmt, ppszVolName);
734}
735
736RTDECL(RTDVMVOLTYPE) RTDvmVolumeGetType(RTDVMVOLUME hVol)
737{
738 PRTDVMVOLUMEINTERNAL pThis = hVol;
739 AssertPtrReturn(pThis, RTDVMVOLTYPE_INVALID);
740 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, RTDVMVOLTYPE_INVALID);
741
742 return pThis->pVolMgr->pDvmFmtOps->pfnVolumeGetType(pThis->hVolFmt);
743}
744
745RTDECL(uint64_t) RTDvmVolumeGetFlags(RTDVMVOLUME hVol)
746{
747 PRTDVMVOLUMEINTERNAL pThis = hVol;
748 AssertPtrReturn(pThis, UINT64_MAX);
749 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, UINT64_MAX);
750
751 return pThis->pVolMgr->pDvmFmtOps->pfnVolumeGetFlags(pThis->hVolFmt);
752}
753
754RTDECL(int) RTDvmVolumeQueryRange(RTDVMVOLUME hVol, uint64_t *poffStart, uint64_t *poffLast)
755{
756 PRTDVMVOLUMEINTERNAL pThis = hVol;
757 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
758 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE);
759 AssertPtrReturn(poffStart, VERR_INVALID_POINTER);
760 AssertPtrReturn(poffLast, VERR_INVALID_POINTER);
761
762 return pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryRange(pThis->hVolFmt, poffStart, poffLast);
763}
764
765RTDECL(int) RTDvmVolumeQueryTableLocation(RTDVMVOLUME hVol, uint64_t *poffTable, uint64_t *pcbTable)
766{
767 PRTDVMVOLUMEINTERNAL pThis = hVol;
768 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
769 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE);
770 AssertPtrReturn(poffTable, VERR_INVALID_POINTER);
771 AssertPtrReturn(pcbTable, VERR_INVALID_POINTER);
772
773 return pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryTableLocation(pThis->hVolFmt, poffTable, pcbTable);
774}
775
776RTDECL(uint32_t) RTDvmVolumeGetIndex(RTDVMVOLUME hVol, RTDVMVOLIDX enmIndex)
777{
778 PRTDVMVOLUMEINTERNAL pThis = hVol;
779 AssertPtrReturn(pThis, UINT32_MAX);
780 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, UINT32_MAX);
781 AssertReturn(enmIndex > RTDVMVOLIDX_INVALID && enmIndex < RTDVMVOLIDX_END, UINT32_MAX);
782
783 if (enmIndex == RTDVMVOLIDX_HOST)
784 {
785#ifdef RT_OS_WINDOWS
786 enmIndex = RTDVMVOLIDX_USER_VISIBLE;
787#elif defined(RT_OS_LINUX) \
788 || defined(RT_OS_FREEBSD) \
789 || defined(RT_OS_NETBSD) \
790 || defined(RT_OS_SOLARIS) \
791 || defined(RT_OS_DARWIN) \
792 || defined(RT_OS_OS2) /*whatever*/
793/* Darwing and freebsd matches the linux algo. Solaris matches linux algo partially, at least, in the part we use. */
794 enmIndex = RTDVMVOLIDX_LINUX;
795#else
796# error "PORTME"
797#endif
798 }
799
800 return pThis->pVolMgr->pDvmFmtOps->pfnVolumeGetIndex(pThis->hVolFmt, enmIndex);
801}
802
803/**
804 * Helper for RTDvmVolumeQueryProp.
805 */
806static void rtDvmReturnInteger(void *pvDst, size_t cbDst, PRTUINT64U pSrc, size_t cbSrc)
807{
808 /* Read the source: */
809 uint64_t uSrc;
810 switch (cbSrc)
811 {
812 case sizeof(uint8_t): uSrc = (uint8_t)pSrc->Words.w0; break;
813 case sizeof(uint16_t): uSrc = pSrc->Words.w0; break;
814 case sizeof(uint32_t): uSrc = pSrc->s.Lo; break;
815 default: AssertFailed(); RT_FALL_THROUGH();
816 case sizeof(uint64_t): uSrc = pSrc->u; break;
817 }
818
819 /* Write the destination: */
820 switch (cbDst)
821 {
822 default: AssertFailed(); RT_FALL_THROUGH();
823 case sizeof(uint8_t): *(uint8_t *)pvDst = (uint8_t)uSrc; break;
824 case sizeof(uint16_t): *(uint16_t *)pvDst = (uint16_t)uSrc; break;
825 case sizeof(uint32_t): *(uint32_t *)pvDst = (uint32_t)uSrc; break;
826 case sizeof(uint64_t): *(uint64_t *)pvDst = uSrc; break;
827 }
828}
829
830RTDECL(int) RTDvmVolumeQueryProp(RTDVMVOLUME hVol, RTDVMVOLPROP enmProperty, void *pvBuf, size_t cbBuf, size_t *pcbBuf)
831{
832 PRTDVMVOLUMEINTERNAL pThis = hVol;
833 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
834 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE);
835 size_t cbBufFallback = 0;
836 if (pcbBuf == NULL)
837 pcbBuf = &cbBufFallback;
838 AssertReturnStmt(enmProperty > RTDVMVOLPROP_INVALID && enmProperty < RTDVMVOLPROP_END, *pcbBuf = 0, VERR_INVALID_FUNCTION);
839
840 switch (enmProperty)
841 {
842 /* 8, 16, 32 or 64 bit sized integers: */
843 case RTDVMVOLPROP_MBR_FIRST_HEAD:
844 case RTDVMVOLPROP_MBR_FIRST_SECTOR:
845 case RTDVMVOLPROP_MBR_LAST_HEAD:
846 case RTDVMVOLPROP_MBR_LAST_SECTOR:
847 case RTDVMVOLPROP_MBR_TYPE:
848 {
849 *pcbBuf = sizeof(uint8_t);
850 AssertReturn( cbBuf == sizeof(uint8_t)
851 || cbBuf == sizeof(uint16_t)
852 || cbBuf == sizeof(uint32_t)
853 || cbBuf == sizeof(uint64_t), VERR_INVALID_PARAMETER);
854 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
855
856 RTUINT64U Union64 = {0};
857 int rc = pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryProp(pThis->hVolFmt, enmProperty, &Union64, cbBuf, pcbBuf);
858 rtDvmReturnInteger(pvBuf, cbBuf, &Union64, *pcbBuf);
859 return rc;
860 }
861
862 /* 16, 32 or 64 bit sized integers: */
863 case RTDVMVOLPROP_MBR_FIRST_CYLINDER:
864 case RTDVMVOLPROP_MBR_LAST_CYLINDER:
865 {
866 *pcbBuf = sizeof(uint16_t);
867 AssertReturn( cbBuf == sizeof(uint16_t)
868 || cbBuf == sizeof(uint32_t)
869 || cbBuf == sizeof(uint64_t), VERR_INVALID_PARAMETER);
870 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
871
872 RTUINT64U Union64 = {0};
873 int rc = pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryProp(pThis->hVolFmt, enmProperty, &Union64, cbBuf, pcbBuf);
874 rtDvmReturnInteger(pvBuf, cbBuf, &Union64, *pcbBuf);
875 return rc;
876 }
877
878 /* RTUUIDs: */
879 case RTDVMVOLPROP_GPT_TYPE:
880 case RTDVMVOLPROP_GPT_UUID:
881 {
882 *pcbBuf = sizeof(RTUUID);
883 AssertReturn(cbBuf == sizeof(RTUUID), VERR_INVALID_PARAMETER);
884 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
885
886 RTUUID Uuid = RTUUID_INITIALIZE_NULL;
887 int rc = pThis->pVolMgr->pDvmFmtOps->pfnVolumeQueryProp(pThis->hVolFmt, enmProperty, &Uuid, sizeof(RTUUID), pcbBuf);
888 memcpy(pvBuf, &Uuid, sizeof(Uuid));
889 return rc;
890 }
891
892 case RTDVMVOLPROP_INVALID:
893 case RTDVMVOLPROP_END:
894 case RTDVMVOLPROP_32BIT_HACK:
895 break;
896 /* No default case! */
897 }
898 AssertFailed();
899 return VERR_NOT_SUPPORTED;
900}
901
902RTDECL(uint64_t) RTDvmVolumeGetPropU64(RTDVMVOLUME hVol, RTDVMVOLPROP enmProperty, uint64_t uDefault)
903{
904 uint64_t uValue = uDefault;
905 int rc = RTDvmVolumeQueryProp(hVol, enmProperty, &uValue, sizeof(uValue), NULL);
906 if (RT_SUCCESS(rc))
907 return uValue;
908 AssertMsg(rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_FOUND, ("%Rrc enmProperty=%d\n", rc, enmProperty));
909 return uDefault;
910}
911
912RTDECL(int) RTDvmVolumeRead(RTDVMVOLUME hVol, uint64_t off, void *pvBuf, size_t cbRead)
913{
914 PRTDVMVOLUMEINTERNAL pThis = hVol;
915 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
916 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE);
917 AssertReturn(pvBuf, VERR_INVALID_POINTER);
918 AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER);
919
920 return pThis->pVolMgr->pDvmFmtOps->pfnVolumeRead(pThis->hVolFmt, off, pvBuf, cbRead);
921}
922
923RTDECL(int) RTDvmVolumeWrite(RTDVMVOLUME hVol, uint64_t off, const void *pvBuf, size_t cbWrite)
924{
925 PRTDVMVOLUMEINTERNAL pThis = hVol;
926 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
927 AssertReturn(pThis->u32Magic == RTDVMVOLUME_MAGIC, VERR_INVALID_HANDLE);
928 AssertReturn(pvBuf, VERR_INVALID_POINTER);
929 AssertReturn(cbWrite > 0, VERR_INVALID_PARAMETER);
930
931 return pThis->pVolMgr->pDvmFmtOps->pfnVolumeWrite(pThis->hVolFmt, off, pvBuf, cbWrite);
932}
933
934RTDECL(const char *) RTDvmVolumeTypeGetDescr(RTDVMVOLTYPE enmVolType)
935{
936 AssertReturn(enmVolType >= RTDVMVOLTYPE_INVALID && enmVolType < RTDVMVOLTYPE_END, NULL);
937
938 return g_apszDvmVolTypes[enmVolType];
939}
940
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use