[36868] | 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 | /*
|
---|
[98103] | 7 | * Copyright (C) 2011-2023 Oracle and/or its affiliates.
|
---|
[36868] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
[36868] | 11 | *
|
---|
[96407] | 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 | *
|
---|
[36868] | 25 | * The contents of this file may alternatively be used under the terms
|
---|
| 26 | * of the Common Development and Distribution License Version 1.0
|
---|
[96407] | 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
|
---|
[36868] | 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.
|
---|
[96407] | 33 | *
|
---|
| 34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
[36868] | 35 | */
|
---|
| 36 |
|
---|
[37023] | 37 |
|
---|
[57358] | 38 | /*********************************************************************************************************************************
|
---|
| 39 | * Header Files *
|
---|
| 40 | *********************************************************************************************************************************/
|
---|
[69651] | 41 | #define LOG_GROUP RTLOGGROUP_FS
|
---|
[36868] | 42 | #include <iprt/types.h>
|
---|
| 43 | #include <iprt/assert.h>
|
---|
| 44 | #include <iprt/mem.h>
|
---|
| 45 | #include <iprt/dvm.h>
|
---|
[69651] | 46 | #include <iprt/list.h>
|
---|
| 47 | #include <iprt/log.h>
|
---|
[36868] | 48 | #include <iprt/string.h>
|
---|
[85877] | 49 | #include <iprt/uuid.h>
|
---|
[36868] | 50 | #include "internal/dvm.h"
|
---|
| 51 |
|
---|
[37023] | 52 |
|
---|
[57358] | 53 | /*********************************************************************************************************************************
|
---|
[69651] | 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 | /*********************************************************************************************************************************
|
---|
[57358] | 61 | * Structures and Typedefs *
|
---|
| 62 | *********************************************************************************************************************************/
|
---|
[69651] | 63 | /** Pointer to a MBR sector. */
|
---|
| 64 | typedef struct RTDVMMBRSECTOR *PRTDVMMBRSECTOR;
|
---|
[36868] | 65 |
|
---|
[85877] | 66 |
|
---|
| 67 | /** The on-disk Cylinder/Head/Sector (CHS) info. */
|
---|
| 68 | typedef struct MBRCHSADDR
|
---|
| 69 | {
|
---|
| 70 | uint8_t uHead;
|
---|
| 71 | uint8_t uSector : 6;
|
---|
| 72 | uint8_t uCylinderH : 2;
|
---|
| 73 | uint8_t uCylinderL;
|
---|
| 74 | } MBRCHSADDR;
|
---|
| 75 | AssertCompileSize(MBRCHSADDR, 3);
|
---|
| 76 |
|
---|
| 77 |
|
---|
| 78 | /** A decoded cylinder/head/sector address. */
|
---|
| 79 | typedef struct RTDVMMBRCHSADDR
|
---|
| 80 | {
|
---|
| 81 | uint16_t uCylinder;
|
---|
| 82 | uint8_t uHead;
|
---|
| 83 | uint8_t uSector;
|
---|
| 84 | } RTDVMMBRCHSADDR;
|
---|
| 85 |
|
---|
| 86 |
|
---|
[36868] | 87 | /**
|
---|
[69651] | 88 | * MBR entry.
|
---|
| 89 | */
|
---|
| 90 | typedef 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;
|
---|
[85877] | 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;
|
---|
[69651] | 125 | } RTDVMMBRENTRY;
|
---|
| 126 | /** Pointer to an MBR entry. */
|
---|
| 127 | typedef RTDVMMBRENTRY *PRTDVMMBRENTRY;
|
---|
| 128 |
|
---|
| 129 | /**
|
---|
| 130 | * A MBR sector.
|
---|
| 131 | */
|
---|
| 132 | typedef 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;
|
---|
[85877] | 151 | #if ARCH_BITS == 64
|
---|
| 152 | uint32_t uAlignmentPadding;
|
---|
| 153 | #endif
|
---|
[69651] | 154 | /** The raw data. */
|
---|
[85877] | 155 | uint8_t abData[RT_FLEXIBLE_ARRAY_NESTED];
|
---|
[69651] | 156 | } RTDVMMBRSECTOR;
|
---|
| 157 |
|
---|
| 158 | /**
|
---|
[36868] | 159 | * MBR volume manager data.
|
---|
| 160 | */
|
---|
| 161 | typedef struct RTDVMFMTINTERNAL
|
---|
| 162 | {
|
---|
| 163 | /** Pointer to the underlying disk. */
|
---|
[69651] | 164 | PCRTDVMDISK pDisk;
|
---|
| 165 | /** Head of the list of in-use RTDVMMBRENTRY structures. This excludes
|
---|
| 166 | * extended partition table entries. */
|
---|
| 167 | RTLISTANCHOR PartitionHead;
|
---|
[85877] | 168 | /** The sector size to use when doing address calculation based on partition
|
---|
| 169 | * table sector addresses and counts. */
|
---|
| 170 | uint32_t cbSector;
|
---|
[69651] | 171 | /** The total number of partitions, not counting extended ones. */
|
---|
| 172 | uint32_t cPartitions;
|
---|
| 173 | /** The actual primary MBR sector. */
|
---|
| 174 | RTDVMMBRSECTOR Primary;
|
---|
[36868] | 175 | } RTDVMFMTINTERNAL;
|
---|
| 176 | /** Pointer to the MBR volume manager. */
|
---|
| 177 | typedef RTDVMFMTINTERNAL *PRTDVMFMTINTERNAL;
|
---|
| 178 |
|
---|
| 179 | /**
|
---|
| 180 | * MBR volume data.
|
---|
| 181 | */
|
---|
| 182 | typedef struct RTDVMVOLUMEFMTINTERNAL
|
---|
| 183 | {
|
---|
| 184 | /** Pointer to the volume manager. */
|
---|
[69651] | 185 | PRTDVMFMTINTERNAL pVolMgr;
|
---|
| 186 | /** The MBR entry. */
|
---|
| 187 | PRTDVMMBRENTRY pEntry;
|
---|
[36868] | 188 | } RTDVMVOLUMEFMTINTERNAL;
|
---|
| 189 | /** Pointer to an MBR volume. */
|
---|
| 190 | typedef RTDVMVOLUMEFMTINTERNAL *PRTDVMVOLUMEFMTINTERNAL;
|
---|
| 191 |
|
---|
| 192 |
|
---|
[57358] | 193 | /*********************************************************************************************************************************
|
---|
| 194 | * Global Variables *
|
---|
| 195 | *********************************************************************************************************************************/
|
---|
[36868] | 196 | /**
|
---|
| 197 | * Mapping of FS types to DVM volume types.
|
---|
| 198 | *
|
---|
[69651] | 199 | * @see https://en.wikipedia.org/wiki/Partition_type
|
---|
| 200 | * @see http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
|
---|
[36868] | 201 | */
|
---|
[69651] | 202 | static const struct RTDVMMBRFS2VOLTYPE
|
---|
[36868] | 203 | {
|
---|
[69651] | 204 | /** MBR FS Id. */
|
---|
| 205 | uint8_t bFsId;
|
---|
| 206 | /** DVM volume type. */
|
---|
| 207 | RTDVMVOLTYPE enmVolType;
|
---|
| 208 | } g_aFs2DvmVolTypes[] =
|
---|
| 209 | {
|
---|
[69618] | 210 | { 0x01, RTDVMVOLTYPE_FAT12 },
|
---|
| 211 | { 0x04, RTDVMVOLTYPE_FAT16 },
|
---|
| 212 | { 0x06, RTDVMVOLTYPE_FAT16 }, /* big FAT16 */
|
---|
[37023] | 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 },
|
---|
[69618] | 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 |
|
---|
[37023] | 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 },
|
---|
[73156] | 233 | { 0xaf, RTDVMVOLTYPE_DARWIN_HFS },
|
---|
[37023] | 234 | { 0xbf, RTDVMVOLTYPE_SOLARIS },
|
---|
| 235 | { 0xfd, RTDVMVOLTYPE_LINUX_SOFTRAID }
|
---|
[36868] | 236 | };
|
---|
| 237 |
|
---|
[85877] | 238 |
|
---|
[37024] | 239 | static DECLCALLBACK(int) rtDvmFmtMbrProbe(PCRTDVMDISK pDisk, uint32_t *puScore)
|
---|
[36868] | 240 | {
|
---|
| 241 | int rc = VINF_SUCCESS;
|
---|
| 242 | *puScore = RTDVM_MATCH_SCORE_UNSUPPORTED;
|
---|
[85877] | 243 | if (pDisk->cbDisk > RT_MAX(512, pDisk->cbSector))
|
---|
[36868] | 244 | {
|
---|
| 245 | /* Read from the disk and check for the 0x55aa signature at the end. */
|
---|
[85877] | 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. */
|
---|
[85878] | 255 | /** @todo this could easily confuser a DOS, OS/2 or NT boot sector with a MBR... */
|
---|
[85877] | 256 | RTMemTmpFree(pbMbr);
|
---|
| 257 | }
|
---|
| 258 | else
|
---|
| 259 | rc = VERR_NO_TMP_MEMORY;
|
---|
[36868] | 260 | }
|
---|
| 261 |
|
---|
| 262 | return rc;
|
---|
| 263 | }
|
---|
| 264 |
|
---|
[69651] | 265 |
|
---|
| 266 | static void rtDvmFmtMbrDestroy(PRTDVMFMTINTERNAL pThis)
|
---|
[36868] | 267 | {
|
---|
[69651] | 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;
|
---|
[36868] | 277 |
|
---|
[69651] | 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 |
|
---|
[85877] | 295 | /**
|
---|
| 296 | * Decodes the on-disk cylinder/head/sector info and stores it the
|
---|
| 297 | * destination structure.
|
---|
| 298 | */
|
---|
| 299 | DECLINLINE(void) rtDvmFmtMbrDecodeChs(RTDVMMBRCHSADDR *pDst, uint8_t *pbRaw)
|
---|
[69651] | 300 | {
|
---|
[85877] | 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 |
|
---|
| 308 | static int rtDvmFmtMbrReadExtended(PRTDVMFMTINTERNAL pThis, PRTDVMMBRENTRY pPrimaryEntry,
|
---|
| 309 | uint8_t *pidxAll, uint8_t *pidxVisible, uint8_t *pidxLinux)
|
---|
| 310 | {
|
---|
[69909] | 311 | uint64_t const cbExt = pPrimaryEntry->cbPart;
|
---|
[69651] | 312 | uint64_t const offExtBegin = pPrimaryEntry->offPart;
|
---|
| 313 |
|
---|
| 314 | uint64_t offCurBegin = offExtBegin;
|
---|
| 315 | PRTDVMMBRENTRY pCurEntry = pPrimaryEntry;
|
---|
| 316 | for (unsigned cTables = 1; ; cTables++)
|
---|
[36868] | 317 | {
|
---|
[69651] | 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 | }
|
---|
[36868] | 329 |
|
---|
[69651] | 330 | /* Limit the chain length. */
|
---|
| 331 | if (cTables > 64)
|
---|
[36868] | 332 | {
|
---|
[69651] | 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 | }
|
---|
[36868] | 337 |
|
---|
[69651] | 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 | }
|
---|
[36868] | 346 |
|
---|
[69651] | 347 | /*
|
---|
| 348 | * Allocate a new sector entry and read the sector with the table.
|
---|
| 349 | */
|
---|
[85877] | 350 | size_t const cbMbr = RT_MAX(512, pThis->pDisk->cbSector);
|
---|
| 351 | PRTDVMMBRSECTOR pNext = (PRTDVMMBRSECTOR)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTDVMMBRSECTOR, abData[cbMbr]));
|
---|
[69651] | 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 |
|
---|
[85877] | 361 | uint8_t *pabData = &pNext->abData[0];
|
---|
| 362 | int rc = rtDvmDiskReadUnaligned(pThis->pDisk, pNext->offOnDisk, pabData, cbMbr);
|
---|
[69651] | 363 | if ( RT_FAILURE(rc)
|
---|
[85877] | 364 | || pabData[510] != 0x55
|
---|
| 365 | || pabData[511] != 0xaa)
|
---|
[69651] | 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",
|
---|
[85877] | 371 | offCurBegin, pabData[510], pabData[511]));
|
---|
[69651] | 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];
|
---|
[85877] | 386 | uint8_t *pbMbrEntry = &pabData[446];
|
---|
[69651] | 387 | for (unsigned i = 0; i < 4; i++, pEntry++, pbMbrEntry += 16)
|
---|
| 388 | {
|
---|
[85877] | 389 | pEntry->pSector = pNext;
|
---|
| 390 | pEntry->idxTable = (uint8_t)i;
|
---|
| 391 | RTListInit(&pEntry->ListEntry);
|
---|
| 392 |
|
---|
[69651] | 393 | uint8_t const bType = pbMbrEntry[4];
|
---|
| 394 | if (bType != 0)
|
---|
[36868] | 395 | {
|
---|
[69651] | 396 | pEntry->bType = bType;
|
---|
| 397 | pEntry->fFlags = pbMbrEntry[0];
|
---|
[85877] | 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],
|
---|
[69651] | 405 | pbMbrEntry[0x08 + 1],
|
---|
| 406 | pbMbrEntry[0x08 + 2],
|
---|
| 407 | pbMbrEntry[0x08 + 3]);
|
---|
[85877] | 408 | pEntry->offPart *= pThis->cbSector;
|
---|
| 409 | pEntry->cbPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x0c + 0],
|
---|
[69651] | 410 | pbMbrEntry[0x0c + 1],
|
---|
| 411 | pbMbrEntry[0x0c + 2],
|
---|
| 412 | pbMbrEntry[0x0c + 3]);
|
---|
[85877] | 413 | pEntry->cbPart *= pThis->cbSector;
|
---|
[69651] | 414 | if (!RTDVMMBR_IS_EXTENDED(bType))
|
---|
| 415 | {
|
---|
[85877] | 416 | pEntry->offPart += offCurBegin;
|
---|
| 417 | pEntry->idxVisible = *pidxVisible;
|
---|
| 418 | *pidxVisible += 1;
|
---|
| 419 | pEntry->idxLinux = *pidxLinux;
|
---|
| 420 | *pidxLinux += 1;
|
---|
| 421 |
|
---|
[36868] | 422 | pThis->cPartitions++;
|
---|
[69651] | 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++;
|
---|
[36868] | 443 |
|
---|
| 444 | }
|
---|
[69651] | 445 | /* else: unused */
|
---|
| 446 | }
|
---|
[36868] | 447 |
|
---|
[69651] | 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 |
|
---|
| 460 | static DECLCALLBACK(int) rtDvmFmtMbrOpen(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
|
---|
| 461 | {
|
---|
| 462 | int rc;
|
---|
[85877] | 463 | size_t const cbMbr = RT_MAX(512, pDisk->cbSector);
|
---|
| 464 | PRTDVMFMTINTERNAL pThis = (PRTDVMFMTINTERNAL)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTDVMFMTINTERNAL, Primary.abData[cbMbr]));
|
---|
[69651] | 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 |
|
---|
[85877] | 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 |
|
---|
[69651] | 489 | /*
|
---|
| 490 | * Read the primary MBR.
|
---|
| 491 | */
|
---|
[85877] | 492 | uint8_t *pabData = &pThis->Primary.abData[0];
|
---|
| 493 | rc = rtDvmDiskRead(pDisk, 0, pabData, cbMbr);
|
---|
[69651] | 494 | if (RT_SUCCESS(rc))
|
---|
| 495 | {
|
---|
[85877] | 496 | Assert(pabData[510] == 0x55 && pabData[511] == 0xaa);
|
---|
[69651] | 497 |
|
---|
| 498 | /*
|
---|
| 499 | * Setup basic data for the 4 entries.
|
---|
| 500 | */
|
---|
| 501 | PRTDVMMBRENTRY pEntry = &pThis->Primary.aEntries[0];
|
---|
[85877] | 502 | uint8_t *pbMbrEntry = &pabData[446];
|
---|
| 503 | uint8_t idxVisible = 1;
|
---|
[69651] | 504 | for (unsigned i = 0; i < 4; i++, pEntry++, pbMbrEntry += 16)
|
---|
| 505 | {
|
---|
[85877] | 506 | pEntry->pSector = &pThis->Primary;
|
---|
| 507 | pEntry->idxTable = (uint8_t)i;
|
---|
[69651] | 508 | RTListInit(&pEntry->ListEntry);
|
---|
| 509 |
|
---|
[85877] | 510 | uint8_t const bType = pbMbrEntry[4];
|
---|
[69651] | 511 | if (bType != 0)
|
---|
| 512 | {
|
---|
[85877] | 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 |
|
---|
[69651] | 520 | pEntry->offPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x08 + 0],
|
---|
| 521 | pbMbrEntry[0x08 + 1],
|
---|
| 522 | pbMbrEntry[0x08 + 2],
|
---|
| 523 | pbMbrEntry[0x08 + 3]);
|
---|
[85877] | 524 | pEntry->offPart *= pThis->cbSector;
|
---|
[69651] | 525 | pEntry->cbPart = RT_MAKE_U32_FROM_U8(pbMbrEntry[0x0c + 0],
|
---|
| 526 | pbMbrEntry[0x0c + 1],
|
---|
| 527 | pbMbrEntry[0x0c + 2],
|
---|
| 528 | pbMbrEntry[0x0c + 3]);
|
---|
[85877] | 529 | pEntry->cbPart *= pThis->cbSector;
|
---|
[69651] | 530 | if (!RTDVMMBR_IS_EXTENDED(bType))
|
---|
| 531 | {
|
---|
[85877] | 532 | pEntry->idxVisible = idxVisible++;
|
---|
| 533 | pEntry->idxLinux = (uint8_t)(i + 1);
|
---|
[69651] | 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)
|
---|
[85877] | 557 | {
|
---|
| 558 | uint8_t idxAll = 5;
|
---|
| 559 | uint8_t idxLinux = 5;
|
---|
[69651] | 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;
|
---|
[85877] | 565 | rc = rtDvmFmtMbrReadExtended(pThis, &pThis->Primary.aEntries[i], &idxAll, &idxVisible, &idxLinux);
|
---|
[69651] | 566 | if (RT_FAILURE(rc))
|
---|
| 567 | break;
|
---|
| 568 | }
|
---|
[85877] | 569 | }
|
---|
[69651] | 570 | if (RT_SUCCESS(rc))
|
---|
| 571 | {
|
---|
| 572 | *phVolMgrFmt = pThis;
|
---|
| 573 | return rc;
|
---|
| 574 | }
|
---|
[36868] | 575 | }
|
---|
[85877] | 576 | rtDvmFmtMbrDestroy(pThis);
|
---|
[36868] | 577 | }
|
---|
| 578 | else
|
---|
| 579 | rc = VERR_NO_MEMORY;
|
---|
| 580 |
|
---|
| 581 | return rc;
|
---|
| 582 | }
|
---|
| 583 |
|
---|
[37024] | 584 | static DECLCALLBACK(int) rtDvmFmtMbrInitialize(PCRTDVMDISK pDisk, PRTDVMFMT phVolMgrFmt)
|
---|
[36868] | 585 | {
|
---|
[69651] | 586 | int rc;
|
---|
[85877] | 587 | size_t const cbMbr = RT_MAX(512, pDisk->cbSector);
|
---|
| 588 | PRTDVMFMTINTERNAL pThis = (PRTDVMFMTINTERNAL)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTDVMFMTINTERNAL, Primary.abData[cbMbr]));
|
---|
[40298] | 589 | if (pThis)
|
---|
[36868] | 590 | {
|
---|
[69651] | 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 |
|
---|
[36868] | 601 | /* Setup a new MBR and write it to the disk. */
|
---|
[85877] | 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);
|
---|
[36868] | 607 | if (RT_SUCCESS(rc))
|
---|
| 608 | {
|
---|
[69651] | 609 | pThis->pDisk = pDisk;
|
---|
[36868] | 610 | *phVolMgrFmt = pThis;
|
---|
| 611 | }
|
---|
| 612 | else
|
---|
| 613 | RTMemFree(pThis);
|
---|
| 614 | }
|
---|
| 615 | else
|
---|
| 616 | rc = VERR_NO_MEMORY;
|
---|
| 617 |
|
---|
| 618 | return rc;
|
---|
| 619 | }
|
---|
| 620 |
|
---|
[37024] | 621 | static DECLCALLBACK(void) rtDvmFmtMbrClose(RTDVMFMT hVolMgrFmt)
|
---|
[36868] | 622 | {
|
---|
[69651] | 623 | rtDvmFmtMbrDestroy(hVolMgrFmt);
|
---|
[36868] | 624 | }
|
---|
| 625 |
|
---|
[69887] | 626 | static DECLCALLBACK(int) rtDvmFmtMbrQueryRangeUse(RTDVMFMT hVolMgrFmt, uint64_t off, uint64_t cbRange, bool *pfUsed)
|
---|
[41549] | 627 | {
|
---|
[69887] | 628 | PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
|
---|
[42387] | 629 |
|
---|
[69887] | 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;
|
---|
[41549] | 666 | return VINF_SUCCESS;
|
---|
| 667 | }
|
---|
| 668 |
|
---|
[85877] | 669 | /** @copydoc RTDVMFMTOPS::pfnQueryDiskUuid */
|
---|
| 670 | static 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 |
|
---|
[37024] | 686 | static DECLCALLBACK(uint32_t) rtDvmFmtMbrGetValidVolumes(RTDVMFMT hVolMgrFmt)
|
---|
[36868] | 687 | {
|
---|
| 688 | PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
|
---|
| 689 |
|
---|
| 690 | return pThis->cPartitions;
|
---|
| 691 | }
|
---|
| 692 |
|
---|
[37024] | 693 | static DECLCALLBACK(uint32_t) rtDvmFmtMbrGetMaxVolumes(RTDVMFMT hVolMgrFmt)
|
---|
[36868] | 694 | {
|
---|
| 695 | NOREF(hVolMgrFmt);
|
---|
[63561] | 696 | return 4; /** @todo Add support for EBR? */
|
---|
[36868] | 697 | }
|
---|
| 698 |
|
---|
| 699 | /**
|
---|
| 700 | * Creates a new volume.
|
---|
| 701 | *
|
---|
| 702 | * @returns IPRT status code.
|
---|
[69651] | 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.
|
---|
[36868] | 706 | */
|
---|
[69651] | 707 | static int rtDvmFmtMbrVolumeCreate(PRTDVMFMTINTERNAL pThis, PRTDVMMBRENTRY pEntry, PRTDVMVOLUMEFMT phVolFmt)
|
---|
[36868] | 708 | {
|
---|
| 709 | PRTDVMVOLUMEFMTINTERNAL pVol = (PRTDVMVOLUMEFMTINTERNAL)RTMemAllocZ(sizeof(RTDVMVOLUMEFMTINTERNAL));
|
---|
[40298] | 710 | if (pVol)
|
---|
[36868] | 711 | {
|
---|
| 712 | pVol->pVolMgr = pThis;
|
---|
[69651] | 713 | pVol->pEntry = pEntry;
|
---|
[36868] | 714 | *phVolFmt = pVol;
|
---|
[69651] | 715 | return VINF_SUCCESS;
|
---|
[36868] | 716 | }
|
---|
[69651] | 717 | return VERR_NO_MEMORY;
|
---|
[36868] | 718 | }
|
---|
| 719 |
|
---|
[37024] | 720 | static DECLCALLBACK(int) rtDvmFmtMbrQueryFirstVolume(RTDVMFMT hVolMgrFmt, PRTDVMVOLUMEFMT phVolFmt)
|
---|
[36868] | 721 | {
|
---|
| 722 | PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
|
---|
| 723 | if (pThis->cPartitions != 0)
|
---|
[69651] | 724 | return rtDvmFmtMbrVolumeCreate(pThis, RTListGetFirst(&pThis->PartitionHead, RTDVMMBRENTRY, ListEntry), phVolFmt);
|
---|
| 725 | return VERR_DVM_MAP_EMPTY;
|
---|
[36868] | 726 | }
|
---|
| 727 |
|
---|
[37024] | 728 | static DECLCALLBACK(int) rtDvmFmtMbrQueryNextVolume(RTDVMFMT hVolMgrFmt, RTDVMVOLUMEFMT hVolFmt, PRTDVMVOLUMEFMT phVolFmtNext)
|
---|
[36868] | 729 | {
|
---|
[69651] | 730 | PRTDVMFMTINTERNAL pThis = hVolMgrFmt;
|
---|
| 731 | PRTDVMVOLUMEFMTINTERNAL pCurVol = hVolFmt;
|
---|
| 732 | if (pCurVol)
|
---|
[36868] | 733 | {
|
---|
[69651] | 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;
|
---|
[36868] | 738 | }
|
---|
[69651] | 739 | if (pThis->cPartitions != 0)
|
---|
| 740 | return rtDvmFmtMbrVolumeCreate(pThis, RTListGetFirst(&pThis->PartitionHead, RTDVMMBRENTRY, ListEntry), phVolFmtNext);
|
---|
| 741 | return VERR_DVM_MAP_EMPTY;
|
---|
[36868] | 742 | }
|
---|
| 743 |
|
---|
[85894] | 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 | */
|
---|
| 751 | static 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;
|
---|
[85897] | 764 | pCur = pCur->idxExtended != UINT8_MAX ? pCur->aEntries[pCur->idxExtended].pChain : NULL)
|
---|
[85894] | 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 */
|
---|
| 779 | static 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;
|
---|
[85897] | 810 | pCur = pCur->idxExtended != UINT8_MAX ? pCur->aEntries[pCur->idxExtended].pChain : NULL)
|
---|
[85894] | 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 |
|
---|
[37024] | 837 | static DECLCALLBACK(void) rtDvmFmtMbrVolumeClose(RTDVMVOLUMEFMT hVolFmt)
|
---|
[36868] | 838 | {
|
---|
| 839 | PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
|
---|
| 840 |
|
---|
| 841 | pVol->pVolMgr = NULL;
|
---|
[69651] | 842 | pVol->pEntry = NULL;
|
---|
[36868] | 843 |
|
---|
| 844 | RTMemFree(pVol);
|
---|
| 845 | }
|
---|
| 846 |
|
---|
[37024] | 847 | static DECLCALLBACK(uint64_t) rtDvmFmtMbrVolumeGetSize(RTDVMVOLUMEFMT hVolFmt)
|
---|
[36868] | 848 | {
|
---|
| 849 | PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
|
---|
| 850 |
|
---|
[69651] | 851 | return pVol->pEntry->cbPart;
|
---|
[36868] | 852 | }
|
---|
| 853 |
|
---|
[37024] | 854 | static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryName(RTDVMVOLUMEFMT hVolFmt, char **ppszVolName)
|
---|
[36868] | 855 | {
|
---|
[39083] | 856 | NOREF(hVolFmt); NOREF(ppszVolName);
|
---|
[36868] | 857 | return VERR_NOT_SUPPORTED;
|
---|
| 858 | }
|
---|
| 859 |
|
---|
[37024] | 860 | static DECLCALLBACK(RTDVMVOLTYPE) rtDvmFmtMbrVolumeGetType(RTDVMVOLUMEFMT hVolFmt)
|
---|
[36868] | 861 | {
|
---|
| 862 | PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
|
---|
| 863 |
|
---|
[69651] | 864 | uint8_t const bType = pVol->pEntry->bType;
|
---|
[36868] | 865 | for (unsigned i = 0; i < RT_ELEMENTS(g_aFs2DvmVolTypes); i++)
|
---|
[69651] | 866 | if (g_aFs2DvmVolTypes[i].bFsId == bType)
|
---|
| 867 | return g_aFs2DvmVolTypes[i].enmVolType;
|
---|
[36868] | 868 |
|
---|
[69651] | 869 | return RTDVMVOLTYPE_UNKNOWN;
|
---|
[36868] | 870 | }
|
---|
| 871 |
|
---|
[37024] | 872 | static DECLCALLBACK(uint64_t) rtDvmFmtMbrVolumeGetFlags(RTDVMVOLUMEFMT hVolFmt)
|
---|
[36868] | 873 | {
|
---|
| 874 | PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
|
---|
| 875 |
|
---|
[77256] | 876 | uint64_t fFlags = DVMVOLUME_F_CONTIGUOUS;
|
---|
[86169] | 877 | if (pVol->pEntry->fFlags & 0x80)
|
---|
[36868] | 878 | fFlags |= DVMVOLUME_FLAGS_BOOTABLE | DVMVOLUME_FLAGS_ACTIVE;
|
---|
| 879 |
|
---|
| 880 | return fFlags;
|
---|
| 881 | }
|
---|
| 882 |
|
---|
[77970] | 883 | static DECLCALLBACK(int) rtDvmFmtMbrVolumeQueryRange(RTDVMVOLUMEFMT hVolFmt, uint64_t *poffStart, uint64_t *poffLast)
|
---|
[77256] | 884 | {
|
---|
| 885 | PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
|
---|
| 886 | *poffStart = pVol->pEntry->offPart;
|
---|
[77970] | 887 | *poffLast = pVol->pEntry->offPart + pVol->pEntry->cbPart - 1;
|
---|
[77256] | 888 | return VINF_SUCCESS;
|
---|
| 889 | }
|
---|
| 890 |
|
---|
[69887] | 891 | static DECLCALLBACK(bool) rtDvmFmtMbrVolumeIsRangeIntersecting(RTDVMVOLUMEFMT hVolFmt, uint64_t offStart, size_t cbRange,
|
---|
| 892 | uint64_t *poffVol, uint64_t *pcbIntersect)
|
---|
[40027] | 893 | {
|
---|
| 894 | PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
|
---|
| 895 |
|
---|
[69651] | 896 | if (RTDVM_RANGE_IS_INTERSECTING(pVol->pEntry->offPart, pVol->pEntry->cbPart, offStart))
|
---|
[40027] | 897 | {
|
---|
[69651] | 898 | *poffVol = offStart - pVol->pEntry->offPart;
|
---|
| 899 | *pcbIntersect = RT_MIN(cbRange, pVol->pEntry->offPart + pVol->pEntry->cbPart - offStart);
|
---|
| 900 | return true;
|
---|
[40027] | 901 | }
|
---|
[69651] | 902 | return false;
|
---|
[40027] | 903 | }
|
---|
| 904 |
|
---|
[85877] | 905 | /** @copydoc RTDVMFMTOPS::pfnVolumeQueryTableLocation */
|
---|
| 906 | static 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 */
|
---|
| 915 | static 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:
|
---|
[85887] | 930 | case RTDVMVOLIDX_HOST:
|
---|
[85877] | 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 */
|
---|
| 940 | static 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 |
|
---|
[37024] | 1000 | static DECLCALLBACK(int) rtDvmFmtMbrVolumeRead(RTDVMVOLUMEFMT hVolFmt, uint64_t off, void *pvBuf, size_t cbRead)
|
---|
[36868] | 1001 | {
|
---|
| 1002 | PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
|
---|
[69651] | 1003 | AssertReturn(off + cbRead <= pVol->pEntry->cbPart, VERR_INVALID_PARAMETER);
|
---|
[36868] | 1004 |
|
---|
[99989] | 1005 | return rtDvmDiskReadUnaligned(pVol->pVolMgr->pDisk, pVol->pEntry->offPart + off, pvBuf, cbRead);
|
---|
[36868] | 1006 | }
|
---|
| 1007 |
|
---|
[37024] | 1008 | static DECLCALLBACK(int) rtDvmFmtMbrVolumeWrite(RTDVMVOLUMEFMT hVolFmt, uint64_t off, const void *pvBuf, size_t cbWrite)
|
---|
[36868] | 1009 | {
|
---|
| 1010 | PRTDVMVOLUMEFMTINTERNAL pVol = hVolFmt;
|
---|
[69651] | 1011 | AssertReturn(off + cbWrite <= pVol->pEntry->cbPart, VERR_INVALID_PARAMETER);
|
---|
[36868] | 1012 |
|
---|
[69651] | 1013 | return rtDvmDiskWrite(pVol->pVolMgr->pDisk, pVol->pEntry->offPart + off, pvBuf, cbWrite);
|
---|
[36868] | 1014 | }
|
---|
| 1015 |
|
---|
[85124] | 1016 | DECL_HIDDEN_CONST(const RTDVMFMTOPS) g_rtDvmFmtMbr =
|
---|
[36868] | 1017 | {
|
---|
[69616] | 1018 | /* pszFmt */
|
---|
[36868] | 1019 | "MBR",
|
---|
[69616] | 1020 | /* enmFormat */
|
---|
| 1021 | RTDVMFORMATTYPE_MBR,
|
---|
[36868] | 1022 | /* pfnProbe */
|
---|
[37024] | 1023 | rtDvmFmtMbrProbe,
|
---|
[36868] | 1024 | /* pfnOpen */
|
---|
[37024] | 1025 | rtDvmFmtMbrOpen,
|
---|
[36868] | 1026 | /* pfnInitialize */
|
---|
[37024] | 1027 | rtDvmFmtMbrInitialize,
|
---|
[36868] | 1028 | /* pfnClose */
|
---|
[37024] | 1029 | rtDvmFmtMbrClose,
|
---|
[41549] | 1030 | /* pfnQueryRangeUse */
|
---|
| 1031 | rtDvmFmtMbrQueryRangeUse,
|
---|
[85877] | 1032 | /* pfnQueryDiskUuid */
|
---|
| 1033 | rtDvmFmtMbrQueryDiskUuid,
|
---|
[36868] | 1034 | /* pfnGetValidVolumes */
|
---|
[37024] | 1035 | rtDvmFmtMbrGetValidVolumes,
|
---|
[36868] | 1036 | /* pfnGetMaxVolumes */
|
---|
[37024] | 1037 | rtDvmFmtMbrGetMaxVolumes,
|
---|
[36868] | 1038 | /* pfnQueryFirstVolume */
|
---|
[37024] | 1039 | rtDvmFmtMbrQueryFirstVolume,
|
---|
[36868] | 1040 | /* pfnQueryNextVolume */
|
---|
[37024] | 1041 | rtDvmFmtMbrQueryNextVolume,
|
---|
[85894] | 1042 | /* pfnQueryTableLocations */
|
---|
| 1043 | rtDvmFmtMbrQueryTableLocations,
|
---|
[36868] | 1044 | /* pfnVolumeClose */
|
---|
[37024] | 1045 | rtDvmFmtMbrVolumeClose,
|
---|
[36868] | 1046 | /* pfnVolumeGetSize */
|
---|
[37024] | 1047 | rtDvmFmtMbrVolumeGetSize,
|
---|
[36868] | 1048 | /* pfnVolumeQueryName */
|
---|
[37024] | 1049 | rtDvmFmtMbrVolumeQueryName,
|
---|
[36868] | 1050 | /* pfnVolumeGetType */
|
---|
[37024] | 1051 | rtDvmFmtMbrVolumeGetType,
|
---|
[36868] | 1052 | /* pfnVolumeGetFlags */
|
---|
[37024] | 1053 | rtDvmFmtMbrVolumeGetFlags,
|
---|
[77256] | 1054 | /* pfnVolumeQueryRange */
|
---|
| 1055 | rtDvmFmtMbrVolumeQueryRange,
|
---|
[40027] | 1056 | /* pfnVOlumeIsRangeIntersecting */
|
---|
| 1057 | rtDvmFmtMbrVolumeIsRangeIntersecting,
|
---|
[85877] | 1058 | /* pfnVolumeQueryTableLocation */
|
---|
| 1059 | rtDvmFmtMbrVolumeQueryTableLocation,
|
---|
| 1060 | /* pfnVolumeGetIndex */
|
---|
| 1061 | rtDvmFmtMbrVolumeGetIndex,
|
---|
| 1062 | /* pfnVolumeQueryProp */
|
---|
| 1063 | rtDvmFmtMbrVolumeQueryProp,
|
---|
[36868] | 1064 | /* pfnVolumeRead */
|
---|
[37024] | 1065 | rtDvmFmtMbrVolumeRead,
|
---|
[36868] | 1066 | /* pfnVolumeWrite */
|
---|
[37024] | 1067 | rtDvmFmtMbrVolumeWrite
|
---|
[36868] | 1068 | };
|
---|
| 1069 |
|
---|