VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use