VirtualBox

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

Last change on this file was 99989, checked in by vboxsync, 11 months ago

IPRT/dvm: Use rtDvmDiskReadUnaligned rather than rtDvmDiskRead in the RTDVMFMTOPS::pfnVolumeRead implementations to avoid triggering assertion when RTCp and others reads files via the FAT file system driver.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.1 KB
Line 
1/* $Id: dvmmbr.cpp 99989 2023-05-26 12:06:12Z vboxsync $ */
2/** @file
3 * IPRT Disk Volume Management API (DVM) - MBR format backend.
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#define LOG_GROUP RTLOGGROUP_FS
42#include <iprt/types.h>
43#include <iprt/assert.h>
44#include <iprt/mem.h>
45#include <iprt/dvm.h>
46#include <iprt/list.h>
47#include <iprt/log.h>
48#include <iprt/string.h>
49#include <iprt/uuid.h>
50#include "internal/dvm.h"
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56/** Checks if the partition type is an extended partition container. */
57#define RTDVMMBR_IS_EXTENDED(a_bType) ((a_bType) == 0x05 || (a_bType) == 0x0f)
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63/** Pointer to a MBR sector. */
64typedef struct RTDVMMBRSECTOR *PRTDVMMBRSECTOR;
65
66
67/** The on-disk Cylinder/Head/Sector (CHS) info. */
68typedef struct MBRCHSADDR
69{
70 uint8_t uHead;
71 uint8_t uSector : 6;
72 uint8_t uCylinderH : 2;
73 uint8_t uCylinderL;
74} MBRCHSADDR;
75AssertCompileSize(MBRCHSADDR, 3);
76
77
78/** A decoded cylinder/head/sector address. */
79typedef struct RTDVMMBRCHSADDR
80{
81 uint16_t uCylinder;
82 uint8_t uHead;
83 uint8_t uSector;
84} RTDVMMBRCHSADDR;
85
86
87/**
88 * MBR entry.
89 */
90typedef struct RTDVMMBRENTRY
91{
92 /** Our entry in the in-use partition entry list (RTDVMMBRENTRY). */
93 RTLISTNODE ListEntry;
94 /** Pointer to the MBR sector containing this entry. */
95 PRTDVMMBRSECTOR pSector;
96 /** Pointer to the next sector in the extended partition table chain. */
97 PRTDVMMBRSECTOR pChain;
98 /** The byte offset of the start of the partition (relative to disk). */
99 uint64_t offPart;
100 /** Number of bytes for this partition. */
101 uint64_t cbPart;
102 /** The partition/filesystem type. */
103 uint8_t bType;
104 /** The partition flags. */
105 uint8_t fFlags;
106 /** Bad entry. */
107 bool fBad;
108 /** RTDVMVOLIDX_IN_TABLE - Zero-based index within the table in pSector.
109 * (Also the index into RTDVMMBRSECTOR::aEntries.) */
110 uint8_t idxTable;
111 /** RTDVMVOLIDX_ALL - One-based index. All primary entries are included,
112 * whether they are used or not. In the extended table chain, only USED
113 * entries are counted (but we include RTDVMMBR_IS_EXTENDED entries). */
114 uint8_t idxAll;
115 /** RTDVMVOLIDX_USER_VISIBLE - One-base index. Skips all unused entries
116 * and RTDVMMBR_IS_EXTENDED. */
117 uint8_t idxVisible;
118 /** RTDVMVOLIDX_LINUX - One-based index following the /dev/sdaX scheme. */
119 uint8_t idxLinux;
120 uint8_t bUnused;
121 /** The first CHS address of this partition */
122 RTDVMMBRCHSADDR FirstChs;
123 /** The last CHS address of this partition */
124 RTDVMMBRCHSADDR LastChs;
125} RTDVMMBRENTRY;
126/** Pointer to an MBR entry. */
127typedef RTDVMMBRENTRY *PRTDVMMBRENTRY;
128
129/**
130 * A MBR sector.
131 */
132typedef struct RTDVMMBRSECTOR
133{
134 /** Internal representation of the entries. */
135 RTDVMMBRENTRY aEntries[4];
136 /** The byte offset of this MBR sector (relative to disk).
137 * We keep this for detecting cycles now, but it will be needed if we start
138 * updating the partition table at some point. */
139 uint64_t offOnDisk;
140 /** Pointer to the previous sector if this isn't a primary one. */
141 PRTDVMMBRENTRY pPrevSector;
142 /** Set if this is the primary MBR, cleared if an extended. */
143 bool fIsPrimary;
144 /** Number of used entries. */
145 uint8_t cUsed;
146 /** Number of extended entries. */
147 uint8_t cExtended;
148 /** The extended entry we're following (we only follow one, except when
149 * fIsPrimary is @c true). UINT8_MAX if none. */
150 uint8_t idxExtended;
151#if ARCH_BITS == 64
152 uint32_t uAlignmentPadding;
153#endif
154 /** The raw data. */
155 uint8_t abData[RT_FLEXIBLE_ARRAY_NESTED];
156} RTDVMMBRSECTOR;
157
158/**
159 * MBR volume manager data.
160 */
161typedef struct RTDVMFMTINTERNAL
162{
163 /** Pointer to the underlying disk. */
164 PCRTDVMDISK pDisk;
165 /** Head of the list of in-use RTDVMMBRENTRY structures. This excludes
166 * extended partition table entries. */
167 RTLISTANCHOR PartitionHead;
168 /** The sector size to use when doing address calculation based on partition
169 * table sector addresses and counts. */
170 uint32_t cbSector;
171 /** The total number of partitions, not counting extended ones. */
172 uint32_t cPartitions;
173 /** The actual primary MBR sector. */
174 RTDVMMBRSECTOR Primary;
175} RTDVMFMTINTERNAL;
176/** Pointer to the MBR volume manager. */
177typedef RTDVMFMTINTERNAL *PRTDVMFMTINTERNAL;
178
179/**
180 * MBR volume data.
181 */
182typedef struct RTDVMVOLUMEFMTINTERNAL
183{
184 /** Pointer to the volume manager. */
185 PRTDVMFMTINTERNAL pVolMgr;
186 /** The MBR entry. */
187 PRTDVMMBRENTRY pEntry;
188} RTDVMVOLUMEFMTINTERNAL;
189/** Pointer to an MBR volume. */
190typedef RTDVMVOLUMEFMTINTERNAL *PRTDVMVOLUMEFMTINTERNAL;
191
192
193/*********************************************************************************************************************************
194* Global Variables *
195*********************************************************************************************************************************/
196/**
197 * Mapping of FS types to DVM volume types.
198 *
199 * @see https://en.wikipedia.org/wiki/Partition_type
200 * @see http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
201 */
202static const struct RTDVMMBRFS2VOLTYPE
203{
204 /** MBR FS Id. */
205 uint8_t bFsId;
206 /** DVM volume type. */
207 RTDVMVOLTYPE enmVolType;
208} g_aFs2DvmVolTypes[] =
209{
210 { 0x01, RTDVMVOLTYPE_FAT12 },
211 { 0x04, RTDVMVOLTYPE_FAT16 },
212 { 0x06, RTDVMVOLTYPE_FAT16 }, /* big FAT16 */
213 { 0x07, RTDVMVOLTYPE_NTFS }, /* Simplification: Used for HPFS, exFAT, ++, too but NTFS is the more common one. */
214 { 0x0b, RTDVMVOLTYPE_FAT32 },
215 { 0x0c, RTDVMVOLTYPE_FAT32 },
216 { 0x0e, RTDVMVOLTYPE_FAT16 },
217
218 /* Hidden variants of the above: */
219 { 0x11, RTDVMVOLTYPE_FAT12 },
220 { 0x14, RTDVMVOLTYPE_FAT16 },
221 { 0x16, RTDVMVOLTYPE_FAT16 },
222 { 0x17, RTDVMVOLTYPE_NTFS },
223 { 0x1b, RTDVMVOLTYPE_FAT32 },
224 { 0x1c, RTDVMVOLTYPE_FAT32 },
225 { 0x1e, RTDVMVOLTYPE_FAT16 },
226
227 { 0x82, RTDVMVOLTYPE_LINUX_SWAP },
228 { 0x83, RTDVMVOLTYPE_LINUX_NATIVE },
229 { 0x8e, RTDVMVOLTYPE_LINUX_LVM },
230 { 0xa5, RTDVMVOLTYPE_FREEBSD },
231 { 0xa9, RTDVMVOLTYPE_NETBSD },
232 { 0xa6, RTDVMVOLTYPE_OPENBSD },
233 { 0xaf, RTDVMVOLTYPE_DARWIN_HFS },
234 { 0xbf, RTDVMVOLTYPE_SOLARIS },
235 { 0xfd, RTDVMVOLTYPE_LINUX_SOFTRAID }
236};
237
238
239static DECLCALLBACK(int) rtDvmFmtMbrProbe(PCRTDVMDISK pDisk, uint32_t *puScore)
240{
241 int rc = VINF_SUCCESS;
242 *puScore = RTDVM_MATCH_SCORE_UNSUPPORTED;
243 if (pDisk->cbDisk > RT_MAX(512, pDisk->cbSector))
244 {
245 /* Read from the disk and check for the 0x55aa signature at the end. */
246 size_t cbAlignedSize = RT_MAX(512, pDisk->cbSector);
247 uint8_t *pbMbr = (uint8_t *)RTMemTmpAllocZ(cbAlignedSize);
248 if (pbMbr)
249 {
250 rc = rtDvmDiskRead(pDisk, 0, pbMbr, cbAlignedSize);
251 if ( RT_SUCCESS(rc)
252 && pbMbr[510] == 0x55
253 && pbMbr[511] == 0xaa)
254 *puScore = RTDVM_MATCH_SCORE_SUPPORTED; /* Not perfect because GPTs have a protective MBR. */
255 /** @todo this could easily confuser a DOS, OS/2 or NT boot sector with a MBR... */
256 RTMemTmpFree(pbMbr);
257 }
258 else
259 rc = VERR_NO_TMP_MEMORY;
260 }
261
262 return rc;
263}
264
265
266static void rtDvmFmtMbrDestroy(PRTDVMFMTINTERNAL pThis)
267{
268 /*
269 * Delete chains of extended partitions.
270 */
271 for (unsigned i = 0; i < 4; i++)
272 {
273 PRTDVMMBRSECTOR pCur = pThis->Primary.aEntries[i].pChain;
274 while (pCur)
275 {
276 PRTDVMMBRSECTOR pNext = pCur->idxExtended != UINT8_MAX ? pCur->aEntries[pCur->idxExtended].pChain : NULL;
277
278 RT_ZERO(pCur->aEntries);
279 pCur->pPrevSector = NULL;
280 RTMemFree(pCur);
281
282 pCur = pNext;
283 }
284 }
285
286 /*
287 * Now kill this.
288 */
289 pThis->pDisk = NULL;
290 RT_ZERO(pThis->Primary.aEntries);
291 RTMemFree(pThis);
292}
293
294
295/**
296 * Decodes the on-disk cylinder/head/sector info and stores it the
297 * destination structure.
298 */
299DECLINLINE(void) rtDvmFmtMbrDecodeChs(RTDVMMBRCHSADDR *pDst, uint8_t *pbRaw)
300{
301 MBRCHSADDR *pRawChs = (MBRCHSADDR *)pbRaw;
302 pDst->uCylinder = RT_MAKE_U16(pRawChs->uCylinderL, pRawChs->uCylinderH);
303 pDst->uSector = pRawChs->uSector;
304 pDst->uHead = pRawChs->uHead;
305}
306
307
308static int rtDvmFmtMbrReadExtended(PRTDVMFMTINTERNAL pThis, PRTDVMMBRENTRY pPrimaryEntry,
309 uint8_t *pidxAll, uint8_t *pidxVisible, uint8_t *pidxLinux)
310{
311 uint64_t const cbExt = pPrimaryEntry->cbPart;
312 uint64_t const offExtBegin = pPrimaryEntry->offPart;
313
314 uint64_t offCurBegin = offExtBegin;
315 PRTDVMMBRENTRY pCurEntry = pPrimaryEntry;
316 for (unsigned cTables = 1; ; cTables++)
317 {
318 /*
319 * Do some sanity checking.
320 */
321 /* Check the address of the partition table. */
322 if (offCurBegin - offExtBegin >= cbExt)
323 {
324 LogRel(("rtDvmFmtMbrReadExtended: offCurBegin=%#RX64 is outside the extended partition: %#RX64..%#RX64 (LB %#RX64)\n",
325 offCurBegin, offExtBegin, offExtBegin + cbExt - 1, cbExt));
326 pCurEntry->fBad = true;
327 return -VERR_OUT_OF_RANGE;
328 }
329
330 /* Limit the chain length. */
331 if (cTables > 64)
332 {
333 LogRel(("rtDvmFmtMbrReadExtended: offCurBegin=%#RX64 is the %uth table, we stop here.\n", offCurBegin, cTables));
334 pCurEntry->fBad = true;
335 return -VERR_TOO_MANY_SYMLINKS;
336 }
337
338 /* Check for obvious cycles. */
339 for (PRTDVMMBRENTRY pPrev = pCurEntry->pSector->pPrevSector; pPrev != NULL; pPrev = pPrev->pSector->pPrevSector)
340 if (pPrev->offPart == offCurBegin)
341 {
342 LogRel(("rtDvmFmtMbrReadExtended: Cycle! We've seen offCurBegin=%#RX64 before\n", offCurBegin));
343 pCurEntry->fBad = true;
344 return -VERR_TOO_MANY_SYMLINKS;
345 }
346
347 /*
348 * Allocate a new sector entry and read the sector with the table.
349 */
350 size_t const cbMbr = RT_MAX(512, pThis->pDisk->cbSector);
351 PRTDVMMBRSECTOR pNext = (PRTDVMMBRSECTOR)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTDVMMBRSECTOR, abData[cbMbr]));
352 if (!pNext)
353 return VERR_NO_MEMORY;
354 pNext->offOnDisk = offCurBegin;
355 pNext->pPrevSector = pCurEntry;
356 //pNext->fIsPrimary = false;
357 //pNext->cUsed = 0;
358 //pNext->cExtended = 0;
359 pNext->idxExtended = UINT8_MAX;
360
361 uint8_t *pabData = &pNext->abData[0];
362 int rc = rtDvmDiskReadUnaligned(pThis->pDisk, pNext->offOnDisk, pabData, cbMbr);
363 if ( RT_FAILURE(rc)
364 || pabData[510] != 0x55
365 || pabData[511] != 0xaa)
366 {
367 if (RT_FAILURE(rc))
368 LogRel(("rtDvmFmtMbrReadExtended: Error reading extended partition table at sector %#RX64: %Rrc\n", offCurBegin, rc));
369 else
370 LogRel(("rtDvmFmtMbrReadExtended: Extended partition table at sector %#RX64 does not have a valid DOS signature: %#x %#x\n",
371 offCurBegin, pabData[510], pabData[511]));
372 RTMemFree(pNext);
373 pCurEntry->fBad = true;
374 return rc;
375 }
376 pCurEntry->pChain = pNext;
377
378 /*
379 * Process the table, taking down the first forward entry.
380 *
381 * As noted in the caller of this function, we only deal with one extended
382 * partition entry at this level since noone really ever put more than one
383 * here anyway.
384 */
385 PRTDVMMBRENTRY pEntry = &pNext->aEntries[0];
386 uint8_t *pbMbrEntry = &pabData[446];
387 for (unsigned i = 0; i < 4; i++, pEntry++, pbMbrEntry += 16)
388 {
389 pEntry->pSector = pNext;
390 pEntry->idxTable = (uint8_t)i;
391 RTListInit(&pEntry->ListEntry);
392
393 uint8_t const bType = pbMbrEntry[4];
394 if (bType != 0)
395 {
396 pEntry->bType = bType;
397 pEntry->fFlags = pbMbrEntry[0];
398 pEntry->idxAll = *pidxAll;
399 *pidxAll += 1;
400
401 rtDvmFmtMbrDecodeChs(&pEntry->FirstChs, &pbMbrEntry[1]);
402 rtDvmFmtMbrDecodeChs(&pEntry->LastChs, &pbMbrEntry[5]);
403
404 pEntry->offPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x08 + 0],
405 pbMbrEntry[0x08 + 1],
406 pbMbrEntry[0x08 + 2],
407 pbMbrEntry[0x08 + 3]);
408 pEntry->offPart *= pThis->cbSector;
409 pEntry->cbPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x0c + 0],
410 pbMbrEntry[0x0c + 1],
411 pbMbrEntry[0x0c + 2],
412 pbMbrEntry[0x0c + 3]);
413 pEntry->cbPart *= pThis->cbSector;
414 if (!RTDVMMBR_IS_EXTENDED(bType))
415 {
416 pEntry->offPart += offCurBegin;
417 pEntry->idxVisible = *pidxVisible;
418 *pidxVisible += 1;
419 pEntry->idxLinux = *pidxLinux;
420 *pidxLinux += 1;
421
422 pThis->cPartitions++;
423 RTListAppend(&pThis->PartitionHead, &pEntry->ListEntry);
424 Log2(("rtDvmFmtMbrReadExtended: %#012RX64::%u: vol%u bType=%#04x fFlags=%#04x offPart=%#012RX64 cbPart=%#012RX64\n",
425 offCurBegin, i, pThis->cPartitions - 1, pEntry->bType, pEntry->fFlags, pEntry->offPart, pEntry->cbPart));
426 }
427 else
428 {
429 pEntry->offPart += offExtBegin;
430 pNext->cExtended++;
431 if (pNext->idxExtended == UINT8_MAX)
432 pNext->idxExtended = (uint8_t)i;
433 else
434 {
435 pEntry->fBad = true;
436 LogRel(("rtDvmFmtMbrReadExtended: Warning! Both #%u and #%u are extended partition table entries! Only following the former\n",
437 i, pNext->idxExtended));
438 }
439 Log2(("rtDvmFmtMbrReadExtended: %#012RX64::%u: ext%u bType=%#04x fFlags=%#04x offPart=%#012RX64 cbPart=%#012RX64\n",
440 offCurBegin, i, pNext->cExtended - 1, pEntry->bType, pEntry->fFlags, pEntry->offPart, pEntry->cbPart));
441 }
442 pNext->cUsed++;
443
444 }
445 /* else: unused */
446 }
447
448 /*
449 * We're done if we didn't find any extended partition table entry.
450 * Otherwise, advance to the next one.
451 */
452 if (!pNext->cExtended)
453 return VINF_SUCCESS;
454 pCurEntry = &pNext->aEntries[pNext->idxExtended];
455 offCurBegin = pCurEntry->offPart;
456 }
457}
458
459
460static DECLCALLBACK(int) rtDvmFmtMbrOpen(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
461{
462 int rc;
463 size_t const cbMbr = RT_MAX(512, pDisk->cbSector);
464 PRTDVMFMTINTERNAL pThis = (PRTDVMFMTINTERNAL)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTDVMFMTINTERNAL, Primary.abData[cbMbr]));
465 if (pThis)
466 {
467 pThis->pDisk = pDisk;
468 //pThis->cPartitions = 0;
469 RTListInit(&pThis->PartitionHead);
470 //pThis->Primary.offOnDisk = 0;
471 //pThis->Primary.pPrevSector = NULL;
472 pThis->Primary.fIsPrimary = true;
473 //pThis->Primary.cUsed = 0;
474 //pThis->Primary.cExtended = 0;
475 pThis->Primary.idxExtended = UINT8_MAX;
476
477 /* We'll use the sector size reported by the disk.
478
479 Though, giiven that the MBR was hardwired to 512 byte sectors, we probably
480 should do some probing when the sector size differs from 512, but that can
481 wait till there is a real need for it and we've got some semi reliable
482 heuristics for doing that. */
483 pThis->cbSector = (uint32_t)pDisk->cbSector;
484 AssertLogRelMsgStmt( pThis->cbSector >= 512
485 && pThis->cbSector <= _64K,
486 ("cbSector=%#x\n", pThis->cbSector),
487 pThis->cbSector = 512);
488
489 /*
490 * Read the primary MBR.
491 */
492 uint8_t *pabData = &pThis->Primary.abData[0];
493 rc = rtDvmDiskRead(pDisk, 0, pabData, cbMbr);
494 if (RT_SUCCESS(rc))
495 {
496 Assert(pabData[510] == 0x55 && pabData[511] == 0xaa);
497
498 /*
499 * Setup basic data for the 4 entries.
500 */
501 PRTDVMMBRENTRY pEntry = &pThis->Primary.aEntries[0];
502 uint8_t *pbMbrEntry = &pabData[446];
503 uint8_t idxVisible = 1;
504 for (unsigned i = 0; i < 4; i++, pEntry++, pbMbrEntry += 16)
505 {
506 pEntry->pSector = &pThis->Primary;
507 pEntry->idxTable = (uint8_t)i;
508 RTListInit(&pEntry->ListEntry);
509
510 uint8_t const bType = pbMbrEntry[4];
511 if (bType != 0)
512 {
513 pEntry->bType = bType;
514 pEntry->fFlags = pbMbrEntry[0];
515 pEntry->idxAll = (uint8_t)(i + 1);
516
517 rtDvmFmtMbrDecodeChs(&pEntry->FirstChs, &pbMbrEntry[1]);
518 rtDvmFmtMbrDecodeChs(&pEntry->LastChs, &pbMbrEntry[5]);
519
520 pEntry->offPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x08 + 0],
521 pbMbrEntry[0x08 + 1],
522 pbMbrEntry[0x08 + 2],
523 pbMbrEntry[0x08 + 3]);
524 pEntry->offPart *= pThis->cbSector;
525 pEntry->cbPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x0c + 0],
526 pbMbrEntry[0x0c + 1],
527 pbMbrEntry[0x0c + 2],
528 pbMbrEntry[0x0c + 3]);
529 pEntry->cbPart *= pThis->cbSector;
530 if (!RTDVMMBR_IS_EXTENDED(bType))
531 {
532 pEntry->idxVisible = idxVisible++;
533 pEntry->idxLinux = (uint8_t)(i + 1);
534 pThis->cPartitions++;
535 RTListAppend(&pThis->PartitionHead, &pEntry->ListEntry);
536 Log2(("rtDvmFmtMbrOpen: %u: vol%u bType=%#04x fFlags=%#04x offPart=%#012RX64 cbPart=%#012RX64\n",
537 i, pThis->cPartitions - 1, pEntry->bType, pEntry->fFlags, pEntry->offPart, pEntry->cbPart));
538 }
539 else
540 {
541 pThis->Primary.cExtended++;
542 Log2(("rtDvmFmtMbrOpen: %u: ext%u bType=%#04x fFlags=%#04x offPart=%#012RX64 cbPart=%#012RX64\n",
543 i, pThis->Primary.cExtended - 1, pEntry->bType, pEntry->fFlags, pEntry->offPart, pEntry->cbPart));
544 }
545 pThis->Primary.cUsed++;
546 }
547 /* else: unused */
548 }
549
550 /*
551 * Now read any extended partitions. Since it's no big deal for us, we allow
552 * the primary partition table to have more than one extended partition. However
553 * in the extended tables we only allow a single forward link to avoid having to
554 * deal with recursion.
555 */
556 if (pThis->Primary.cExtended > 0)
557 {
558 uint8_t idxAll = 5;
559 uint8_t idxLinux = 5;
560 for (unsigned i = 0; i < 4; i++)
561 if (RTDVMMBR_IS_EXTENDED(pThis->Primary.aEntries[i].bType))
562 {
563 if (pThis->Primary.idxExtended == UINT8_MAX)
564 pThis->Primary.idxExtended = (uint8_t)i;
565 rc = rtDvmFmtMbrReadExtended(pThis, &pThis->Primary.aEntries[i], &idxAll, &idxVisible, &idxLinux);
566 if (RT_FAILURE(rc))
567 break;
568 }
569 }
570 if (RT_SUCCESS(rc))
571 {
572 *phVolMgrFmt = pThis;
573 return rc;
574 }
575 }
576 rtDvmFmtMbrDestroy(pThis);
577 }
578 else
579 rc = VERR_NO_MEMORY;
580
581 return rc;
582}
583
584static DECLCALLBACK(int) rtDvmFmtMbrInitialize(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
585{
586 int rc;
587 size_t const cbMbr = RT_MAX(512, pDisk->cbSector);
588 PRTDVMFMTINTERNAL pThis = (PRTDVMFMTINTERNAL)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTDVMFMTINTERNAL, Primary.abData[cbMbr]));
589 if (pThis)
590 {
591 pThis->pDisk = pDisk;
592 //pThis->cPartitions = 0;
593 RTListInit(&pThis->PartitionHead);
594 //pThis->Primary.offOnDisk = 0
595 //pThis->Primary.pPrevSector = NULL;
596 pThis->Primary.fIsPrimary = true;
597 //pThis->Primary.cUsed = 0;
598 //pThis->Primary.cExtended = 0;
599 pThis->Primary.idxExtended = UINT8_MAX;
600
601 /* Setup a new MBR and write it to the disk. */
602 uint8_t *pabData = &pThis->Primary.abData[0];
603 RT_BZERO(pabData, 512);
604 pabData[510] = 0x55;
605 pabData[511] = 0xaa;
606 rc = rtDvmDiskWrite(pDisk, 0, pabData, cbMbr);
607 if (RT_SUCCESS(rc))
608 {
609 pThis->pDisk = pDisk;
610 *phVolMgrFmt = pThis;
611 }
612 else
613 RTMemFree(pThis);
614 }
615 else
616 rc = VERR_NO_MEMORY;
617
618 return rc;
619}
620
621static DECLCALLBACK(void) rtDvmFmtMbrClose(RTDVMFMT hVolMgrFmt)
622{
623 rtDvmFmtMbrDestroy(hVolMgrFmt);
624}
625
626static DECLCALLBACK(int) rtDvmFmtMbrQueryRangeUse(RTDVMFMT hVolMgrFmt, uint64_t off, uint64_t cbRange, bool *pfUsed)
627{
628 PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
629
630 /*
631 * The MBR definitely uses the first 512 bytes, but we consider anything up
632 * to 1MB of alignment padding / cylinder gap to be considered in use too.
633 *
634 * The cylinder gap has been used by several boot managers and boot loaders
635 * to store code and data.
636 */
637 if (off < (uint64_t)_1M)
638 {
639 *pfUsed = true;
640 return VINF_SUCCESS;
641 }
642
643 /* Ditto for any extended partition tables. */
644 for (uint32_t iPrimary = 0; iPrimary < 4; iPrimary++)
645 {
646 PRTDVMMBRSECTOR pCur = pThis->Primary.aEntries[iPrimary].pChain;
647 while (pCur)
648 {
649 if ( off < pCur->offOnDisk + _1M
650 && off + cbRange > pCur->offOnDisk)
651 {
652 *pfUsed = true;
653 return VINF_SUCCESS;
654 }
655
656
657 if (pCur->idxExtended == UINT8_MAX)
658 break;
659 pCur = pCur->aEntries[pCur->idxExtended].pChain;
660 }
661
662 }
663
664 /* Not in use. */
665 *pfUsed = false;
666 return VINF_SUCCESS;
667}
668
669/** @copydoc RTDVMFMTOPS::pfnQueryDiskUuid */
670static DECLCALLBACK(int) rtDvmFmtMbrQueryDiskUuid(RTDVMFMT hVolMgrFmt, PRTUUID pUuid)
671{
672 PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
673 uint32_t idDisk = RT_MAKE_U32_FROM_U8(pThis->Primary.abData[440],
674 pThis->Primary.abData[441],
675 pThis->Primary.abData[442],
676 pThis->Primary.abData[443]);
677 if (idDisk != 0)
678 {
679 RTUuidClear(pUuid);
680 pUuid->Gen.u32TimeLow = idDisk;
681 return VINF_NOT_SUPPORTED;
682 }
683 return VERR_NOT_SUPPORTED;
684}
685
686static DECLCALLBACK(uint32_t) rtDvmFmtMbrGetValidVolumes(RTDVMFMT hVolMgrFmt)
687{
688 PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
689
690 return pThis->cPartitions;
691}
692
693static DECLCALLBACK(uint32_t) rtDvmFmtMbrGetMaxVolumes(RTDVMFMT hVolMgrFmt)
694{
695 NOREF(hVolMgrFmt);
696 return 4; /** @todo Add support for EBR? */
697}
698
699/**
700 * Creates a new volume.
701 *
702 * @returns IPRT status code.
703 * @param pThis The MBR volume manager data.
704 * @param pEntry The MBR entry to create a volume handle for.
705 * @param phVolFmt Where to store the volume data on success.
706 */
707static int rtDvmFmtMbrVolumeCreate(PRTDVMFMTINTERNAL pThis, PRTDVMMBRENTRY pEntry, PRTDVMVOLUMEFMT phVolFmt)
708{
709 PRTDVMVOLUMEFMTINTERNAL pVol = (PRTDVMVOLUMEFMTINTERNAL)RTMemAllocZ(sizeof(RTDVMVOLUMEFMTINTERNAL));
710 if (pVol)
711 {
712 pVol->pVolMgr = pThis;
713 pVol->pEntry = pEntry;
714 *phVolFmt = pVol;
715 return VINF_SUCCESS;
716 }
717 return VERR_NO_MEMORY;
718}
719
720static DECLCALLBACK(int) rtDvmFmtMbrQueryFirstVolume(RTDVMFMT hVolMgrFmt, PRTDVMVOLUMEFMT phVolFmt)
721{
722 PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
723 if (pThis->cPartitions != 0)
724 return rtDvmFmtMbrVolumeCreate(pThis, RTListGetFirst(&pThis->PartitionHead, RTDVMMBRENTRY, ListEntry), phVolFmt);
725 return VERR_DVM_MAP_EMPTY;
726}
727
728static DECLCALLBACK(int) rtDvmFmtMbrQueryNextVolume(RTDVMFMT hVolMgrFmt, RTDVMVOLUMEFMT hVolFmt, PRTDVMVOLUMEFMT phVolFmtNext)
729{
730 PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
731 PRTDVMVOLUMEFMTINTERNAL pCurVol = hVolFmt;
732 if (pCurVol)
733 {
734 PRTDVMMBRENTRY pNextEntry = RTListGetNext(&pThis->PartitionHead, pCurVol->pEntry, RTDVMMBRENTRY, ListEntry);
735 if (pNextEntry)
736 return rtDvmFmtMbrVolumeCreate(pThis, pNextEntry, phVolFmtNext);
737 return VERR_DVM_MAP_NO_VOLUME;
738 }
739 if (pThis->cPartitions != 0)
740 return rtDvmFmtMbrVolumeCreate(pThis, RTListGetFirst(&pThis->PartitionHead, RTDVMMBRENTRY, ListEntry), phVolFmtNext);
741 return VERR_DVM_MAP_EMPTY;
742}
743
744/**
745 * Helper for rtDvmFmtMbrQueryTableLocations that calculates the padding and/or
746 * free space at @a off.
747 *
748 * Because nothing need to be sorted by start offset, we have to traverse all
749 * partition tables to determine this.
750 */
751static uint64_t rtDvmFmtMbrCalcTablePadding(PRTDVMFMTINTERNAL pThis, uint64_t off)
752{
753 uint64_t offNext = pThis->pDisk->cbDisk;
754 for (unsigned i = 0; i < 4; i++)
755 {
756 /* Check this primary entry */
757 uint64_t offCur = pThis->Primary.aEntries[i].offPart;
758 if (offCur >= off && offCur < offNext && pThis->Primary.aEntries[i].bType != 0)
759 offNext = offCur;
760
761 /* If it's an extended partition, check the chained ones too. */
762 for (PRTDVMMBRSECTOR pCur = pThis->Primary.aEntries[i].pChain;
763 pCur != NULL;
764 pCur = pCur->idxExtended != UINT8_MAX ? pCur->aEntries[pCur->idxExtended].pChain : NULL)
765 {
766 for (unsigned j = 0; j < 4; j++)
767 {
768 offCur = pCur->aEntries[j].offPart;
769 if (offCur >= off && offCur < offNext && pCur->aEntries[j].bType != 0)
770 offNext = offCur;
771 }
772 }
773 }
774 Assert(offNext >= off);
775 return offNext - off;
776}
777
778/** @copydoc RTDVMFMTOPS::pfnQueryTableLocations */
779static DECLCALLBACK(int) rtDvmFmtMbrQueryTableLocations(RTDVMFMT hVolMgrFmt, uint32_t fFlags, PRTDVMTABLELOCATION paLocations,
780 size_t cLocations, size_t *pcActual)
781{
782 PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
783 RT_NOREF(fFlags);
784
785 /*
786 * The MBR.
787 */
788 int rc = VINF_SUCCESS;
789 size_t iLoc = 0;
790 if (cLocations > 0)
791 {
792 paLocations[iLoc].off = pThis->Primary.offOnDisk;
793 paLocations[iLoc].cb = pThis->cbSector;
794 paLocations[iLoc].cbPadding = rtDvmFmtMbrCalcTablePadding(pThis, 0 + pThis->cbSector);
795 }
796 else
797 rc = VERR_BUFFER_OVERFLOW;
798 iLoc++;
799
800 /*
801 * Now do the extended partitions.
802 *
803 * Remember, we only support multiple in the primary MBR, only the first
804 * one is honored in the chained ones.
805 */
806 for (unsigned i = 0; i < 4; i++)
807 {
808 for (PRTDVMMBRSECTOR pCur = pThis->Primary.aEntries[i].pChain;
809 pCur != NULL;
810 pCur = pCur->idxExtended != UINT8_MAX ? pCur->aEntries[pCur->idxExtended].pChain : NULL)
811 {
812 if (cLocations > iLoc)
813 {
814 paLocations[iLoc].off = pCur->offOnDisk;
815 paLocations[iLoc].cb = pThis->cbSector;
816 paLocations[iLoc].cbPadding = rtDvmFmtMbrCalcTablePadding(pThis, pCur->offOnDisk + pThis->cbSector);
817 }
818 else
819 rc = VERR_BUFFER_OVERFLOW;
820 iLoc++;
821 }
822 }
823
824 /*
825 * Return values.
826 */
827 if (pcActual)
828 *pcActual = iLoc;
829 else if (cLocations != iLoc && RT_SUCCESS(rc))
830 {
831 RT_BZERO(&paLocations[iLoc], (cLocations - iLoc) * sizeof(paLocations[0]));
832 rc = VERR_BUFFER_UNDERFLOW;
833 }
834 return rc;
835}
836
837static DECLCALLBACK(void) rtDvmFmtMbrVolumeClose(RTDVMVOLUMEFMT hVolFmt)
838{
839 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
840
841 pVol->pVolMgr = NULL;
842 pVol->pEntry = NULL;
843
844 RTMemFree(pVol);
845}
846
847static DECLCALLBACK(uint64_t) rtDvmFmtMbrVolumeGetSize(RTDVMVOLUMEFMT hVolFmt)
848{
849 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
850
851 return pVol->pEntry->cbPart;
852}
853
854static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryName(RTDVMVOLUMEFMT hVolFmt, char **ppszVolName)
855{
856 NOREF(hVolFmt); NOREF(ppszVolName);
857 return VERR_NOT_SUPPORTED;
858}
859
860static DECLCALLBACK(RTDVMVOLTYPE) rtDvmFmtMbrVolumeGetType(RTDVMVOLUMEFMT hVolFmt)
861{
862 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
863
864 uint8_t const bType = pVol->pEntry->bType;
865 for (unsigned i = 0; i < RT_ELEMENTS(g_aFs2DvmVolTypes); i++)
866 if (g_aFs2DvmVolTypes[i].bFsId == bType)
867 return g_aFs2DvmVolTypes[i].enmVolType;
868
869 return RTDVMVOLTYPE_UNKNOWN;
870}
871
872static DECLCALLBACK(uint64_t) rtDvmFmtMbrVolumeGetFlags(RTDVMVOLUMEFMT hVolFmt)
873{
874 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
875
876 uint64_t fFlags = DVMVOLUME_F_CONTIGUOUS;
877 if (pVol->pEntry->fFlags & 0x80)
878 fFlags |= DVMVOLUME_FLAGS_BOOTABLE | DVMVOLUME_FLAGS_ACTIVE;
879
880 return fFlags;
881}
882
883static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryRange(RTDVMVOLUMEFMT hVolFmt, uint64_t *poffStart, uint64_t *poffLast)
884{
885 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
886 *poffStart = pVol->pEntry->offPart;
887 *poffLast = pVol->pEntry->offPart + pVol->pEntry->cbPart - 1;
888 return VINF_SUCCESS;
889}
890
891static DECLCALLBACK(bool) rtDvmFmtMbrVolumeIsRangeIntersecting(RTDVMVOLUMEFMT hVolFmt, uint64_t offStart, size_t cbRange,
892 uint64_t *poffVol, uint64_t *pcbIntersect)
893{
894 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
895
896 if (RTDVM_RANGE_IS_INTERSECTING(pVol->pEntry->offPart, pVol->pEntry->cbPart, offStart))
897 {
898 *poffVol = offStart - pVol->pEntry->offPart;
899 *pcbIntersect = RT_MIN(cbRange, pVol->pEntry->offPart + pVol->pEntry->cbPart - offStart);
900 return true;
901 }
902 return false;
903}
904
905/** @copydoc RTDVMFMTOPS::pfnVolumeQueryTableLocation */
906static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryTableLocation(RTDVMVOLUMEFMT hVolFmt, uint64_t *poffTable, uint64_t *pcbTable)
907{
908 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
909 *poffTable = pVol->pEntry->pSector->offOnDisk;
910 *pcbTable = RT_MAX(512, pVol->pVolMgr->pDisk->cbSector);
911 return VINF_SUCCESS;
912}
913
914/** @copydoc RTDVMFMTOPS::pfnVolumeGetIndex */
915static DECLCALLBACK(uint32_t) rtDvmFmtMbrVolumeGetIndex(RTDVMVOLUMEFMT hVolFmt, RTDVMVOLIDX enmIndex)
916{
917 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
918 switch (enmIndex)
919 {
920 case RTDVMVOLIDX_USER_VISIBLE:
921 return pVol->pEntry->idxVisible;
922 case RTDVMVOLIDX_ALL:
923 return pVol->pEntry->idxAll;
924 case RTDVMVOLIDX_IN_TABLE:
925 return pVol->pEntry->idxTable;
926 case RTDVMVOLIDX_LINUX:
927 return pVol->pEntry->idxLinux;
928
929 case RTDVMVOLIDX_INVALID:
930 case RTDVMVOLIDX_HOST:
931 case RTDVMVOLIDX_END:
932 case RTDVMVOLIDX_32BIT_HACK:
933 break;
934 /* no default! */
935 }
936 return UINT32_MAX;
937}
938
939/** @copydoc RTDVMFMTOPS::pfnVolumeQueryProp */
940static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryProp(RTDVMVOLUMEFMT hVolFmt, RTDVMVOLPROP enmProperty,
941 void *pvBuf, size_t cbBuf, size_t *pcbBuf)
942{
943 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
944 switch (enmProperty)
945 {
946 case RTDVMVOLPROP_MBR_FIRST_CYLINDER:
947 *pcbBuf = sizeof(uint16_t);
948 Assert(cbBuf >= *pcbBuf);
949 *(uint16_t *)pvBuf = pVol->pEntry->FirstChs.uCylinder;
950 return VINF_SUCCESS;
951 case RTDVMVOLPROP_MBR_LAST_CYLINDER:
952 *pcbBuf = sizeof(uint16_t);
953 Assert(cbBuf >= *pcbBuf);
954 *(uint16_t *)pvBuf = pVol->pEntry->LastChs.uCylinder;
955 return VINF_SUCCESS;
956
957 case RTDVMVOLPROP_MBR_FIRST_HEAD:
958 *pcbBuf = sizeof(uint8_t);
959 Assert(cbBuf >= *pcbBuf);
960 *(uint8_t *)pvBuf = pVol->pEntry->FirstChs.uHead;
961 return VINF_SUCCESS;
962 case RTDVMVOLPROP_MBR_LAST_HEAD:
963 *pcbBuf = sizeof(uint8_t);
964 Assert(cbBuf >= *pcbBuf);
965 *(uint8_t *)pvBuf = pVol->pEntry->LastChs.uHead;
966 return VINF_SUCCESS;
967
968 case RTDVMVOLPROP_MBR_FIRST_SECTOR:
969 *pcbBuf = sizeof(uint8_t);
970 Assert(cbBuf >= *pcbBuf);
971 *(uint8_t *)pvBuf = pVol->pEntry->FirstChs.uSector;
972 return VINF_SUCCESS;
973 case RTDVMVOLPROP_MBR_LAST_SECTOR:
974 *pcbBuf = sizeof(uint8_t);
975 Assert(cbBuf >= *pcbBuf);
976 *(uint8_t *)pvBuf = pVol->pEntry->LastChs.uSector;
977 return VINF_SUCCESS;
978
979 case RTDVMVOLPROP_MBR_TYPE:
980 *pcbBuf = sizeof(uint8_t);
981 Assert(cbBuf >= *pcbBuf);
982 *(uint8_t *)pvBuf = pVol->pEntry->bType;
983 return VINF_SUCCESS;
984
985 case RTDVMVOLPROP_GPT_TYPE:
986 case RTDVMVOLPROP_GPT_UUID:
987 return VERR_NOT_SUPPORTED;
988
989 case RTDVMVOLPROP_INVALID:
990 case RTDVMVOLPROP_END:
991 case RTDVMVOLPROP_32BIT_HACK:
992 break;
993 /* not default! */
994 }
995 RT_NOREF(cbBuf);
996 AssertFailed();
997 return VERR_NOT_SUPPORTED;
998}
999
1000static DECLCALLBACK(int) rtDvmFmtMbrVolumeRead(RTDVMVOLUMEFMT hVolFmt, uint64_t off, void *pvBuf, size_t cbRead)
1001{
1002 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
1003 AssertReturn(off + cbRead <= pVol->pEntry->cbPart, VERR_INVALID_PARAMETER);
1004
1005 return rtDvmDiskReadUnaligned(pVol->pVolMgr->pDisk, pVol->pEntry->offPart + off, pvBuf, cbRead);
1006}
1007
1008static DECLCALLBACK(int) rtDvmFmtMbrVolumeWrite(RTDVMVOLUMEFMT hVolFmt, uint64_t off, const void *pvBuf, size_t cbWrite)
1009{
1010 PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
1011 AssertReturn(off + cbWrite <= pVol->pEntry->cbPart, VERR_INVALID_PARAMETER);
1012
1013 return rtDvmDiskWrite(pVol->pVolMgr->pDisk, pVol->pEntry->offPart + off, pvBuf, cbWrite);
1014}
1015
1016DECL_HIDDEN_CONST(const RTDVMFMTOPS) g_rtDvmFmtMbr =
1017{
1018 /* pszFmt */
1019 "MBR",
1020 /* enmFormat */
1021 RTDVMFORMATTYPE_MBR,
1022 /* pfnProbe */
1023 rtDvmFmtMbrProbe,
1024 /* pfnOpen */
1025 rtDvmFmtMbrOpen,
1026 /* pfnInitialize */
1027 rtDvmFmtMbrInitialize,
1028 /* pfnClose */
1029 rtDvmFmtMbrClose,
1030 /* pfnQueryRangeUse */
1031 rtDvmFmtMbrQueryRangeUse,
1032 /* pfnQueryDiskUuid */
1033 rtDvmFmtMbrQueryDiskUuid,
1034 /* pfnGetValidVolumes */
1035 rtDvmFmtMbrGetValidVolumes,
1036 /* pfnGetMaxVolumes */
1037 rtDvmFmtMbrGetMaxVolumes,
1038 /* pfnQueryFirstVolume */
1039 rtDvmFmtMbrQueryFirstVolume,
1040 /* pfnQueryNextVolume */
1041 rtDvmFmtMbrQueryNextVolume,
1042 /* pfnQueryTableLocations */
1043 rtDvmFmtMbrQueryTableLocations,
1044 /* pfnVolumeClose */
1045 rtDvmFmtMbrVolumeClose,
1046 /* pfnVolumeGetSize */
1047 rtDvmFmtMbrVolumeGetSize,
1048 /* pfnVolumeQueryName */
1049 rtDvmFmtMbrVolumeQueryName,
1050 /* pfnVolumeGetType */
1051 rtDvmFmtMbrVolumeGetType,
1052 /* pfnVolumeGetFlags */
1053 rtDvmFmtMbrVolumeGetFlags,
1054 /* pfnVolumeQueryRange */
1055 rtDvmFmtMbrVolumeQueryRange,
1056 /* pfnVOlumeIsRangeIntersecting */
1057 rtDvmFmtMbrVolumeIsRangeIntersecting,
1058 /* pfnVolumeQueryTableLocation */
1059 rtDvmFmtMbrVolumeQueryTableLocation,
1060 /* pfnVolumeGetIndex */
1061 rtDvmFmtMbrVolumeGetIndex,
1062 /* pfnVolumeQueryProp */
1063 rtDvmFmtMbrVolumeQueryProp,
1064 /* pfnVolumeRead */
1065 rtDvmFmtMbrVolumeRead,
1066 /* pfnVolumeWrite */
1067 rtDvmFmtMbrVolumeWrite
1068};
1069
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use