Changeset 80513 in vbox
- Timestamp:
- Aug 30, 2019 2:43:53 PM (6 years ago)
- svn:sync-xref-src-repo-rev:
- 133014
- Location:
- trunk/src/VBox/ImageMounter/vboximg-mount
- Files:
-
- 1 deleted
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp
r80512 r80513 75 75 #include <iprt/base64.h> 76 76 #include <iprt/vfs.h> 77 78 #include "vboximg-mount.h" 77 #include <iprt/dvm.h> 78 79 79 #include "vboximgCrypto.h" 80 80 #include "vboximgMedia.h" … … 88 88 }; 89 89 90 enum { PARTITION_TABLE_MBR = 1, PARTITION_TABLE_GPT = 2 };91 92 90 #define VBOX_EXTPACK "Oracle VM VirtualBox Extension Pack" 93 #define GPT_PTABLE_SIZE 32 * 512 /** Max size we to read for GPT partition table */94 #define MBR_PARTITIONS_MAX 4 /** Fixed number of partitions in Master Boot Record */95 #define BASENAME_MAX 256 /** Maximum name for the basename of a path (for RTStrNLen()*/96 #define VBOXIMG_PARTITION_MAX 256 /** How much storage to allocate to store partition info */97 #define PARTITION_NAME_MAX 72 /** Maximum partition name size (accomodates GPT partition name) */98 #define DOS_BOOT_RECORD_SIGNATURE 0xaa55 /** MBR and EBR (partition table) signature [EOT boundary] */99 #define NULL_BOOT_RECORD_SIGNATURE 0x0000 /** MBR or EBR null signature value */100 #define MAX_UUID_LEN 256 /** Max length of a UUID */101 #define LBA(n) (n * g_cbSector)102 91 #define VERBOSE g_vboximgOpts.fVerbose 103 104 #define PARTTYPE_IS_NULL(parType) ((uint8_t)parType == 0x00)105 #define PARTTYPE_IS_GPT(parType) ((uint8_t)parType == 0xee)106 #define PARTTYPE_IS_EXT(parType) (( (uint8_t)parType) == 0x05 /* Extended */ \107 || ((uint8_t)parType) == 0x0f /* W95 Extended (LBA) */ \108 || ((uint8_t)parType) == 0x85) /* Linux Extended */109 92 110 93 #define SAFENULL(strPtr) (strPtr ? strPtr : "") … … 113 96 static struct fuse_operations g_vboximgOps; /** FUSE structure that defines allowed ops for this FS */ 114 97 98 /** 99 * Volume data. 100 */ 101 typedef struct VBOXIMGMOUNTVOL 102 { 103 /** The volume handle. */ 104 RTDVMVOLUME hVol; 105 /** The VFS file associated with the volume. */ 106 RTVFSFILE hVfsFileVol; 107 } VBOXIMGMOUNTVOL; 108 /** Pointer to a volume data structure. */ 109 typedef VBOXIMGMOUNTVOL *PVBOXIMGMOUNTVOL; 110 115 111 /* Global variables */ 116 117 112 static RTVFSFILE g_hVfsFileDisk; /** Disk as VFS file handle. */ 118 113 static uint32_t g_cbSector; /** Disk sector size. */ 114 static RTDVM g_hDvmMgr; /** Handle to the volume manager. */ 119 115 static char *g_pszDiskUuid; /** UUID of image (if known, otherwise NULL) */ 120 static off_t g_vDiskOffset; /** Biases r/w from start of VD */121 static off_t g_vDiskSize; /** Limits r/w length for VD */122 static int32_t g_cReaders; /** Number of readers for VD */123 static int32_t g_cWriters; /** Number of writers for VD */124 static RTFOFF g_cbEntireVDisk; /** Size of VD */125 116 static PVDINTERFACE g_pVdIfs; /** @todo Remove when VD I/O becomes threadsafe */ 126 117 static VDINTERFACETHREADSYNC g_VDIfThreadSync; /** @todo Remove when VD I/O becomes threadsafe */ 127 118 static RTCRITSECT g_vdioLock; /** @todo Remove when VD I/O becomes threadsafe */ 128 static uint16_t g_lastPartNbr; /** Last partition number found in MBR + EBR chain */129 static bool g_fGPT; /** True if GPT type partition table was found */130 119 static char *g_pszImageName; /** Base filename for current VD image */ 131 120 static char *g_pszImagePath; /** Full path to current VD image */ … … 134 123 static uint32_t g_cImages; /** Number of images in diff chain */ 135 124 136 /* Table entry containing partition info parsed out of GPT or MBR and EBR chain of specified VD */ 137 138 typedef struct 139 { 140 int idxPartition; /** partition number */ 141 char *pszName; 142 off_t offPartition; /** partition offset from start of disk, in bytes */ 143 uint64_t cbPartition; /** partition size in bytes */ 144 uint8_t fBootable; /** partition bootable */ 145 union 146 { 147 uint8_t legacy; /** partition type MBR/EBR */ 148 uint128_t gptGuidTypeSpecifier; /** partition type GPT */ 149 } partitionType; /** uint8_t for MBR/EBR (legacy) and GUID for GPT */ 150 union 151 { 152 MBRPARTITIONENTRY mbrEntry; /** MBR (also EBR partition entry) */ 153 GPTPARTITIONENTRY gptEntry; /** GPT partition entry */ 154 } partitionEntry; 155 } PARTITIONINFO; 156 157 PARTITIONINFO g_aParsedPartitionInfo[VBOXIMG_PARTITION_MAX + 1]; /* Note: Element 0 reserved for EntireDisk partitionEntry */ 125 /** Pointer to the detected volumes. */ 126 static PVBOXIMGMOUNTVOL g_paVolumes; 127 /** Number of detected volumes. */ 128 static uint32_t g_cVolumes; 158 129 159 130 VBOXIMGOPTS g_vboximgOpts; … … 201 172 202 173 IMAGELIST listHeadLockList; /* flink & blink intentionally left NULL */ 174 175 176 177 /** @todo Remove when VD I/O becomes threadsafe */ 178 static DECLCALLBACK(int) vboximgThreadStartRead(void *pvUser) 179 { 180 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser; 181 return RTCritSectEnter(vdioLock); 182 } 183 184 static DECLCALLBACK(int) vboximgThreadFinishRead(void *pvUser) 185 { 186 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser; 187 return RTCritSectLeave(vdioLock); 188 } 189 190 static DECLCALLBACK(int) vboximgThreadStartWrite(void *pvUser) 191 { 192 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser; 193 return RTCritSectEnter(vdioLock); 194 } 195 196 static DECLCALLBACK(int) vboximgThreadFinishWrite(void *pvUser) 197 { 198 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser; 199 return RTCritSectLeave(vdioLock); 200 } 201 /** @todo (end of to do section) */ 202 203 203 204 204 static void … … 283 283 } 284 284 285 286 /** 287 * Queries the VFS object handle from the given path. 288 * 289 * @returns IPRT status code. 290 * @retval VERR_NOT_FOUND if the object denoted by the path couldn't be found. 291 * @param pszPath The path. 292 * @param phVfsObj Where to store the handle to the VFS object on success. 293 */ 294 static int vboxImgMntVfsObjQueryFromPath(const char *pszPath, PRTVFSOBJ phVfsObj) 295 { 296 PRTPATHSPLIT pPathSplit = NULL; 297 int rc = RTPathSplitA(pszPath, &pPathSplit, RTPATH_STR_F_STYLE_HOST); 298 if (RT_SUCCESS(rc)) 299 { 300 if ( RTPATH_PROP_HAS_ROOT_SPEC(pPathSplit->fProps) 301 && pPathSplit->cComps == 2) 302 { 303 /* Skip the root specifier and start with the component coming afterwards. */ 304 if (!RTStrCmp(pPathSplit->apszComps[1], "vhdd")) 305 *phVfsObj = RTVfsObjFromFile(g_hVfsFileDisk); 306 else if (!RTStrNCmp(&pszPath[1], "vol", sizeof("vol") - 1)) 307 { 308 /* Retrieve the accessed volume and return the stat data. */ 309 uint32_t idxVol; 310 int rcIprt = RTStrToUInt32Full(&pszPath[4], 10, &idxVol); 311 if ( rcIprt == VINF_SUCCESS 312 && idxVol < g_cVolumes) 313 *phVfsObj = RTVfsObjFromFile(g_paVolumes[idxVol].hVfsFileVol); 314 else 315 rc = VERR_NOT_FOUND; 316 } 317 else 318 rc = VERR_NOT_FOUND; 319 320 rc = VINF_SUCCESS; 321 } 322 else 323 rc = VERR_NOT_FOUND; 324 RTPathSplitFree(pPathSplit); 325 } 326 327 return rc; 328 } 329 330 285 331 /** @copydoc fuse_operations::open */ 286 332 static int vboximgOp_open(const char *pszPath, struct fuse_file_info *pInfo) 287 333 { 288 RT_NOREF(pszPath, pInfo);;289 334 LogFlowFunc(("pszPath=%s\n", pszPath)); 290 uint32_t notsup = 0;291 335 int rc = 0; 292 336 337 RTVFSOBJ hVfsObj; 338 int rcIprt = vboxImgMntVfsObjQueryFromPath(pszPath, &hVfsObj); 339 if (RT_SUCCESS(rc)) 340 { 341 uint32_t fNotSup = 0; 342 293 343 #ifdef UNIX_DERIVATIVE 294 # 295 notsup = O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK |296 O_ASYNC | O_CREAT | O_TRUNC | O_EXCL | O_EVTONLY;297 # 298 notsup = O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;299 /* | O_LARGEFILE | O_SYNC | ? */300 # 301 notsup = O_APPEND | O_ASYNC | O_DIRECT | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;302 /* | O_LARGEFILE | O_SYNC | ? */303 # 344 # ifdef RT_OS_DARWIN 345 fNotSup = O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK | 346 O_ASYNC | O_CREAT | O_TRUNC | O_EXCL | O_EVTONLY; 347 # elif defined(RT_OS_LINUX) 348 fNotSup = O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK; 349 /* | O_LARGEFILE | O_SYNC | ? */ 350 # elif defined(RT_OS_FREEBSD) 351 fNotSup = O_APPEND | O_ASYNC | O_DIRECT | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK; 352 /* | O_LARGEFILE | O_SYNC | ? */ 353 # endif 304 354 #else 305 355 # error "Port me" 306 356 #endif 307 357 308 if (pInfo->flags & notsup) 309 rc -EINVAL; 310 358 if (!(pInfo->flags & fNotSup)) 359 { 311 360 #ifdef UNIX_DERIVATIVE 312 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)313 rc = -EINVAL;314 # 315 if (pInfo->flags & O_DIRECTORY)316 rc = -ENOTDIR;317 # 361 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE) 362 rc = -EINVAL; 363 # ifdef O_DIRECTORY 364 if (pInfo->flags & O_DIRECTORY) 365 rc = -ENOTDIR; 366 # endif 318 367 #endif 319 368 320 if (RT_FAILURE(rc)) 321 { 322 LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath)); 323 return rc; 324 } 325 326 int fWriteable = (pInfo->flags & O_ACCMODE) == O_WRONLY 327 || (pInfo->flags & O_ACCMODE) == O_RDWR; 328 if (g_cWriters) 329 rc = -ETXTBSY; 369 if (!rc) 370 { 371 pInfo->fh = (uintptr_t)hVfsObj; 372 return 0; 373 } 374 } 375 else 376 rc = -EINVAL; 377 378 RTVfsObjRelease(hVfsObj); 379 } 330 380 else 331 { 332 if (fWriteable) 333 g_cWriters++; 334 else 335 { 336 if (g_cReaders + 1 > MAX_READERS) 337 rc = -EMLINK; 338 else 339 g_cReaders++; 340 } 341 } 381 rc = -RTErrConvertToErrno(rcIprt); 382 342 383 LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath)); 343 384 return rc; … … 345 386 } 346 387 347 348 /** @todo Remove when VD I/O becomes threadsafe */349 static DECLCALLBACK(int) vboximgThreadStartRead(void *pvUser)350 {351 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;352 return RTCritSectEnter(vdioLock);353 }354 355 static DECLCALLBACK(int) vboximgThreadFinishRead(void *pvUser)356 {357 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;358 return RTCritSectLeave(vdioLock);359 }360 361 static DECLCALLBACK(int) vboximgThreadStartWrite(void *pvUser)362 {363 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;364 return RTCritSectEnter(vdioLock);365 }366 367 static DECLCALLBACK(int) vboximgThreadFinishWrite(void *pvUser)368 {369 PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;370 return RTCritSectLeave(vdioLock);371 }372 /** @todo (end of to do section) */373 374 388 /** @copydoc fuse_operations::release */ 375 389 static int vboximgOp_release(const char *pszPath, struct fuse_file_info *pInfo) … … 379 393 LogFlowFunc(("pszPath=%s\n", pszPath)); 380 394 381 if ( (pInfo->flags & O_ACCMODE) == O_WRONLY 382 || (pInfo->flags & O_ACCMODE) == O_RDWR) 383 { 384 g_cWriters--; 385 Assert(g_cWriters >= 0); 386 } 387 else if ((pInfo->flags & O_ACCMODE) == O_RDONLY) 388 { 389 g_cReaders--; 390 Assert(g_cReaders >= 0); 391 } 392 else 393 AssertFailed(); 395 RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh; 396 RTVfsObjRelease(hVfsObj); 394 397 395 398 LogFlowFunc(("\"%s\"\n", pszPath)); … … 403 406 { 404 407 NOREF(pszPath); 405 NOREF(pInfo); 406 407 LogFlowFunc(("my offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath)); 408 409 LogFlowFunc(("offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath)); 408 410 409 411 AssertReturn(offset >= 0, -EINVAL); … … 411 413 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL); 412 414 413 AssertReturn(offset + g_vDiskOffset >= 0, -EINVAL);414 int64_t adjOff = offset + g_vDiskOffset;415 416 415 int rc = 0; 417 if ((off_t)(adjOff + cbBuf) < adjOff) 418 rc = -EINVAL; 419 else if (adjOff >= g_vDiskSize) 420 return 0; 421 else if (!cbBuf) 422 return 0; 423 424 if (rc >= 0) 425 { 426 int rcIprt = RTVfsFileReadAt(g_hVfsFileDisk, adjOff, pbBuf, cbBuf, NULL); 427 if (RT_FAILURE(rc)) 428 rc = -RTErrConvertToErrno(rcIprt); 429 else 430 rc = cbBuf; 431 } 416 RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh; 417 switch (RTVfsObjGetType(hVfsObj)) 418 { 419 case RTVFSOBJTYPE_FILE: 420 { 421 RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj); 422 int rcIprt = RTVfsFileReadAt(hVfsFile, offset, pbBuf, cbBuf, NULL); 423 if (RT_FAILURE(rc)) 424 rc = -RTErrConvertToErrno(rcIprt); 425 else 426 rc = cbBuf; 427 RTVfsFileRelease(hVfsFile); 428 break; 429 } 430 default: 431 rc = -EINVAL; 432 } 433 432 434 if (rc < 0) 433 435 LogFlowFunc(("%s\n", strerror(rc))); … … 447 449 AssertReturn((int)cbBuf >= 0, -EINVAL); 448 450 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL); 449 AssertReturn(offset + g_vDiskOffset >= 0, -EINVAL); 450 int64_t adjOff = offset + g_vDiskOffset; 451 452 int rc = 0; 453 if (!g_vboximgOpts.fRW) { 451 452 if (!g_vboximgOpts.fRW) 453 { 454 454 LogFlowFunc(("WARNING: vboximg-mount (FUSE FS) --rw option not specified\n" 455 455 " (write operation ignored w/o error!)\n")); 456 456 return cbBuf; 457 } else if ((off_t)(adjOff + cbBuf) < adjOff) 458 rc = -EINVAL; 459 else if (offset >= g_vDiskSize) 460 return 0; 461 else if (!cbBuf) 462 return 0; 463 464 if (rc >= 0) 465 { 466 int rcIprt = RTVfsFileWriteAt(g_hVfsFileDisk, adjOff, pbBuf, cbBuf, NULL); 467 if (RT_FAILURE(rc)) 468 rc = -RTErrConvertToErrno(rcIprt); 469 else 470 rc = cbBuf; 471 } 457 } 458 459 int rc = 0; 460 RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh; 461 switch (RTVfsObjGetType(hVfsObj)) 462 { 463 case RTVFSOBJTYPE_FILE: 464 { 465 RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj); 466 int rcIprt = RTVfsFileWriteAt(hVfsFile, offset, pbBuf, cbBuf, NULL); 467 if (RT_FAILURE(rc)) 468 rc = -RTErrConvertToErrno(rcIprt); 469 else 470 rc = cbBuf; 471 RTVfsFileRelease(hVfsFile); 472 break; 473 } 474 default: 475 rc = -EINVAL; 476 } 477 472 478 if (rc < 0) 473 479 LogFlowFunc(("%s\n", strerror(rc))); … … 501 507 * (or overridden) directly by the { -s | --size } option on the command line. 502 508 */ 503 stbuf->st_size = g_vDiskSize; 509 uint64_t cbFile = 0; 510 int rcIprt = RTVfsFileGetSize(g_hVfsFileDisk, &cbFile); 511 if (RT_SUCCESS(rcIprt)) 512 stbuf->st_size = cbFile; 513 else 514 rc = -RTErrConvertToErrno(rcIprt); 504 515 stbuf->st_nlink = 1; 516 } 517 else if (!RTStrNCmp(&pszPath[1], "vol", sizeof("vol") - 1)) 518 { 519 /* Retrieve the accessed volume and return the stat data. */ 520 uint32_t idxVol; 521 int rcIprt = RTStrToUInt32Full(&pszPath[4], 10, &idxVol); 522 if ( rcIprt == VINF_SUCCESS 523 && idxVol < g_cVolumes) 524 { 525 rc = stat(g_pszImagePath, stbuf); 526 if (rc < 0) 527 return rc; 528 /* 529 * st_size represents the size of the FUSE FS-mounted portion of the disk. 530 * By default it is the whole disk, but can be a partition or specified 531 * (or overridden) directly by the { -s | --size } option on the command line. 532 */ 533 stbuf->st_size = RTDvmVolumeGetSize(g_paVolumes[idxVol].hVol); 534 stbuf->st_nlink = 1; 535 } 536 else 537 rc = -ENOENT; 505 538 } 506 539 else if (RTStrNCmp(pszPath + 1, g_pszImageName, strlen(g_pszImageName)) == 0) … … 553 586 * represents. 554 587 */ 555 556 if (g_vDiskOffset == 0 && (g_vDiskSize == 0 || g_vDiskSize == g_cbEntireVDisk)) 557 pfnFiller(pvBuf, g_pszImageName, NULL, 0); 558 else 559 { 560 char tmp[BASENAME_MAX]; 561 RTStrPrintf(tmp, sizeof (tmp), "%s[%jd:%jd]", g_pszImageName, g_vDiskOffset, g_vDiskSize); 562 pfnFiller(pvBuf, tmp, NULL, 0); 563 } 588 pfnFiller(pvBuf, g_pszImageName, NULL, 0); 589 564 590 /* 565 * Create entry named "vhdd" , which getattr() will describe as a591 * Create entry named "vhdd" denoting the whole disk, which getattr() will describe as a 566 592 * regular file, and thus will go through the open/release/read/write vectors 567 593 * to access the VirtualBox image as processed by the IRPT VD API. 568 594 */ 569 595 pfnFiller(pvBuf, "vhdd", NULL, 0); 596 597 /* Create entries for the individual volumes. */ 598 for (uint32_t i = 0; i < g_cVolumes; i++) 599 { 600 char tmp[64]; 601 RTStrPrintf(tmp, sizeof (tmp), "vol%u", i); 602 pfnFiller(pvBuf, tmp, NULL, 0); 603 } 570 604 return 0; 571 605 } … … 580 614 } 581 615 582 uint8_t 583 parsePartitionTable(void) 584 { 585 MBR_t mbr; 586 EBR_t ebr; 587 PTH_t parTblHdr; 588 589 ASSERT(sizeof (mbr) == 512); 590 ASSERT(sizeof (ebr) == 512); 616 617 /** 618 * Displays the list of volumes on the opened image. 619 * 620 * @returns nothing. 621 */ 622 static void vboxImgMntVolumesDisplay(void) 623 { 591 624 /* 592 * First entry describes entire disk as a single entity 625 * Partition table is most readable and concise when headers and columns 626 * are adapted to the actual data, to avoid insufficient or excessive whitespace. 593 627 */ 594 g_aParsedPartitionInfo[0].idxPartition = 0;595 g_aParsedPartitionInfo[0].offPartition = 0;596 g_aParsedPartitionInfo[0].pszName = RTStrDup("EntireDisk");597 598 int rc = RTVfsFileGetSize(g_hVfsFileDisk, &g_aParsedPartitionInfo[0].cbPartition);599 if (RT_FAILURE(rc))600 return RTMsgErrorExitFailure("Error querying disk image size\n");601 602 /*603 * Currently only DOS partitioned disks are supported. Ensure this one conforms604 */605 rc = RTVfsFileReadAt(g_hVfsFileDisk, 0, &mbr, sizeof (mbr), NULL);606 if (RT_FAILURE(rc))607 return RTMsgErrorExitFailure("Error reading MBR block from disk\n");608 609 if (mbr.signature == NULL_BOOT_RECORD_SIGNATURE)610 return RTMsgErrorExitFailure("Unprt disk (null MBR signature)\n");611 612 if (mbr.signature != DOS_BOOT_RECORD_SIGNATURE)613 return RTMsgErrorExitFailure("Invalid MBR found on image with signature 0x%04hX\n",614 mbr.signature);615 /*616 * Parse the four physical partition entires in the MBR (any one, and only one, can be an EBR)617 */618 int idxEbrPartitionInMbr = 0;619 for (int idxPartition = 1;620 idxPartition <= MBR_PARTITIONS_MAX;621 idxPartition++)622 {623 MBRPARTITIONENTRY *pMbrPartitionEntry =624 &g_aParsedPartitionInfo[idxPartition].partitionEntry.mbrEntry;;625 memcpy (pMbrPartitionEntry, &mbr.partitionEntry[idxPartition - 1], sizeof (MBRPARTITIONENTRY));626 627 if (PARTTYPE_IS_NULL(pMbrPartitionEntry->type))628 continue;629 630 if (PARTTYPE_IS_EXT(pMbrPartitionEntry->type))631 {632 if (idxEbrPartitionInMbr)633 return RTMsgErrorExitFailure("Multiple EBRs found found in MBR\n");634 idxEbrPartitionInMbr = idxPartition;635 }636 637 PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];638 639 ppi->idxPartition = idxPartition;640 ppi->offPartition = (off_t) pMbrPartitionEntry->partitionLba * g_cbSector;641 ppi->cbPartition = (off_t) pMbrPartitionEntry->partitionBlkCnt * g_cbSector;642 ppi->fBootable = pMbrPartitionEntry->bootIndicator == 0x80;643 ppi->partitionType.legacy = pMbrPartitionEntry->type;644 645 g_lastPartNbr = idxPartition;646 647 if (PARTTYPE_IS_GPT(pMbrPartitionEntry->type))648 {649 g_fGPT = true;650 break;651 }652 }653 654 if (g_fGPT)655 {656 g_lastPartNbr = 2; /* from the 'protective MBR' */657 658 rc = RTVfsFileReadAt(g_hVfsFileDisk, LBA(1), &parTblHdr, sizeof (parTblHdr), NULL);659 if (RT_FAILURE(rc))660 return RTMsgErrorExitFailure("Error reading Partition Table Header (LBA 1) from disk\n");661 662 uint8_t *pTblBuf = (uint8_t *)RTMemAlloc(GPT_PTABLE_SIZE);663 664 if (!pTblBuf)665 return RTMsgErrorExitFailure("Out of memory\n");666 667 rc = RTVfsFileReadAt(g_hVfsFileDisk, LBA(2), pTblBuf, GPT_PTABLE_SIZE, NULL);668 if (RT_FAILURE(rc))669 return RTMsgErrorExitFailure("Error reading Partition Table blocks from disk\n");670 671 uint32_t cEntries = parTblHdr.cPartitionEntries;672 uint32_t cbEntry = parTblHdr.cbPartitionEntry;673 if (cEntries * cbEntry > GPT_PTABLE_SIZE)674 {675 RTPrintf("Partition entries exceed GPT table read from disk (pruning!)\n");676 while (cEntries * cbEntry > GPT_PTABLE_SIZE && cEntries > 0)677 --cEntries;678 }679 uint8_t *pEntryRaw = pTblBuf;680 for (uint32_t i = 0; i < cEntries; i++)681 {682 GPTPARTITIONENTRY *pEntry = (GPTPARTITIONENTRY *)pEntryRaw;683 PARTITIONINFO *ppi = &g_aParsedPartitionInfo[g_lastPartNbr];684 memcpy(&(ppi->partitionEntry).gptEntry, pEntry, sizeof(GPTPARTITIONENTRY));685 if (!pEntry->firstLba)686 break;687 ppi->offPartition = pEntry->firstLba * g_cbSector;688 ppi->cbPartition = (pEntry->lastLba - pEntry->firstLba) * g_cbSector;689 ppi->fBootable = pEntry->attrFlags & (1 << GPT_LEGACY_BIOS_BOOTABLE);690 ppi->partitionType.gptGuidTypeSpecifier = pEntry->partitionTypeGuid;691 size_t cwName = sizeof (pEntry->partitionName) / 2;692 RTUtf16LittleToUtf8Ex((PRTUTF16)pEntry->partitionName, RTSTR_MAX, &ppi->pszName, cwName, NULL);693 ppi->idxPartition = g_lastPartNbr++;694 pEntryRaw += cbEntry;695 }696 return PARTITION_TABLE_GPT;697 }698 699 /*700 * Starting with EBR located in MBR, walk EBR chain to parse the logical partition entries701 */702 if (idxEbrPartitionInMbr)703 {704 uint32_t firstEbrLba705 = g_aParsedPartitionInfo[idxEbrPartitionInMbr].partitionEntry.mbrEntry.partitionLba;706 off_t firstEbrOffset = (off_t)firstEbrLba * g_cbSector;707 off_t chainedEbrOffset = 0;708 709 if (!firstEbrLba)710 return RTMsgErrorExitFailure("Inconsistency for logical partition start. Aborting\n");711 712 for (int idxPartition = 5;713 idxPartition <= VBOXIMG_PARTITION_MAX;714 idxPartition++)715 {716 717 off_t currentEbrOffset = firstEbrOffset + chainedEbrOffset;718 RTVfsFileReadAt(g_hVfsFileDisk, currentEbrOffset, &ebr, sizeof (ebr), NULL);719 720 if (ebr.signature != DOS_BOOT_RECORD_SIGNATURE)721 return RTMsgErrorExitFailure("Invalid EBR found on image with signature 0x%04hX\n",722 ebr.signature);723 724 MBRPARTITIONENTRY *pEbrPartitionEntry =725 &g_aParsedPartitionInfo[idxPartition].partitionEntry.mbrEntry; /* EBR entry struct same as MBR */726 memcpy(pEbrPartitionEntry, &ebr.partitionEntry, sizeof (MBRPARTITIONENTRY));727 728 if (pEbrPartitionEntry->type == NULL_BOOT_RECORD_SIGNATURE)729 return RTMsgErrorExitFailure("Logical partition with type 0 encountered");730 731 if (!pEbrPartitionEntry->partitionLba)732 return RTMsgErrorExitFailure("Logical partition invalid partition start offset (LBA) encountered");733 734 PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];735 ppi->idxPartition = idxPartition;736 ppi->offPartition = currentEbrOffset + (off_t)pEbrPartitionEntry->partitionLba * g_cbSector;737 ppi->cbPartition = (off_t)pEbrPartitionEntry->partitionBlkCnt * g_cbSector;738 ppi->fBootable = pEbrPartitionEntry->bootIndicator == 0x80;739 ppi->partitionType.legacy = pEbrPartitionEntry->type;740 741 g_lastPartNbr = idxPartition;742 743 if (ebr.chainingPartitionEntry.type == 0) /* end of chain */744 break;745 746 if (!PARTTYPE_IS_EXT(ebr.chainingPartitionEntry.type))747 return RTMsgErrorExitFailure("Logical partition chain broken");748 749 chainedEbrOffset = ebr.chainingPartitionEntry.partitionLba * g_cbSector;750 }751 }752 return PARTITION_TABLE_MBR;753 }754 755 const char *getClassicPartitionDesc(uint8_t type)756 {757 for (uint32_t i = 0; i < sizeof (g_partitionDescTable) / sizeof (struct PartitionDesc); i++)758 {759 if (g_partitionDescTable[i].type == type)760 return g_partitionDescTable[i].desc;761 }762 return "????";763 }764 765 void766 displayGptPartitionTable(void)767 {768 628 769 629 RTPrintf( "Virtual disk image:\n\n"); … … 774 634 RTPrintf(" UUID: %s\n\n", g_pszDiskUuid); 775 635 776 void *colBoot = NULL;777 778 SELFSIZINGTABLE tbl(2);779 780 /* Note: Omitting partition name column because type/UUID seems suffcient */781 void *colPartNbr = tbl.addCol("#", "%3d", 1);782 783 /* If none of the partitions supports legacy BIOS boot, don't show that column */784 for (int idxPartition = 2; idxPartition <= g_lastPartNbr; idxPartition++)785 if (g_aParsedPartitionInfo[idxPartition].fBootable) {786 colBoot = tbl.addCol("Boot", "%c ", 1);787 break;788 }789 790 void *colStart = tbl.addCol("Start", "%lld", 1);791 void *colSectors = tbl.addCol("Sectors", "%lld", -1, 2);792 void *colSize = tbl.addCol("Size", "%s", 1);793 void *colOffset = tbl.addCol("Offset", "%lld", 1);794 void *colType = tbl.addCol("Type", "%s", -1, 2);795 796 #if 0 /* need to see how other OSes w/GPT use 'Name' field, right now 'Type' seems to suffice */797 void *colName = tbl.addCol("Name", "%s", -1); */798 #endif799 800 for (int idxPartition = 2; idxPartition <= g_lastPartNbr; idxPartition++)801 {802 PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];803 if (ppi->idxPartition)804 {805 char abGuid[GUID_STRING_LENGTH * 2];806 RTStrPrintf(abGuid, sizeof(abGuid), "%RTuuid", &ppi->partitionType.gptGuidTypeSpecifier);807 808 char *pszPartitionTypeDesc = NULL;809 for (uint32_t i = 0; i < sizeof(g_gptPartitionTypes) / sizeof(GPTPARTITIONTYPE); i++)810 if (RTStrNICmp(abGuid, g_gptPartitionTypes[i].gptPartitionUuid, GUID_STRING_LENGTH) == 0)811 {812 pszPartitionTypeDesc = (char *)g_gptPartitionTypes[i].gptPartitionTypeDesc;813 break;814 }815 816 if (!pszPartitionTypeDesc)817 RTPrintf("Couldn't find GPT partitiontype for GUID: %s\n", abGuid);818 819 void *row = tbl.addRow();820 tbl.setCell(row, colPartNbr, idxPartition - 1);821 if (colBoot)822 tbl.setCell(row, colBoot, ppi->fBootable ? '*' : ' ');823 tbl.setCell(row, colStart, ppi->offPartition / g_cbSector);824 tbl.setCell(row, colSectors, ppi->cbPartition / g_cbSector);825 tbl.setCell(row, colSize, vboximgScaledSize(ppi->cbPartition));826 tbl.setCell(row, colOffset, ppi->offPartition);827 tbl.setCell(row, colType, SAFENULL(pszPartitionTypeDesc));828 829 #if 0 /* see comment for stubbed-out 'Name' column definition above */830 tbl.setCell(row, colName, ppi->pszName);831 #endif832 833 }834 }835 tbl.displayTable();836 RTPrintf ("\n");837 }838 839 void840 displayLegacyPartitionTable(void)841 {842 /*843 * Partition table is most readable and concise when headers and columns844 * are adapted to the actual data, to avoid insufficient or excessive whitespace.845 */846 847 RTPrintf( "Virtual disk image:\n\n");848 RTPrintf(" Base: %s\n", g_pszBaseImagePath);849 if (g_cImages > 1)850 RTPrintf(" Diff: %s\n", g_pszImagePath);851 if (g_pszDiskUuid)852 RTPrintf(" UUID: %s\n\n", g_pszDiskUuid);853 854 636 SELFSIZINGTABLE tbl(2); 855 637 … … 860 642 void *colSize = tbl.addCol("Size", "%s", 1); 861 643 void *colOffset = tbl.addCol("Offset", "%lld", 1); 862 void *colId = tbl.addCol("Id", "%2x", 1);863 644 void *colType = tbl.addCol("Type", "%s", -1, 2); 864 645 865 for (int idxPartition = 1; idxPartition <= g_lastPartNbr; idxPartition++) 866 { 867 PARTITIONINFO *p = &g_aParsedPartitionInfo[idxPartition]; 868 if (p->idxPartition) 646 for (uint32_t i = 0; i < g_cVolumes; i++) 647 { 648 PVBOXIMGMOUNTVOL pVol = &g_paVolumes[i]; 649 uint64_t fVolFlags = RTDvmVolumeGetFlags(pVol->hVol); 650 uint64_t cbVol = RTDvmVolumeGetSize(pVol->hVol); 651 RTDVMVOLTYPE enmType = RTDvmVolumeGetType(pVol->hVol); 652 uint64_t offStart = 0; 653 uint64_t offEnd = 0; 654 655 if (fVolFlags & DVMVOLUME_F_CONTIGUOUS) 869 656 { 870 void *row = tbl.addRow(); 871 tbl.setCell(row, colPartition, g_pszBaseImageName, idxPartition); 872 tbl.setCell(row, colBoot, p->fBootable ? '*' : ' '); 873 tbl.setCell(row, colStart, p->offPartition / g_cbSector); 874 tbl.setCell(row, colSectors, p->cbPartition / g_cbSector); 875 tbl.setCell(row, colSize, vboximgScaledSize(p->cbPartition)); 876 tbl.setCell(row, colOffset, p->offPartition); 877 tbl.setCell(row, colId, p->partitionType.legacy); 878 tbl.setCell(row, colType, getClassicPartitionDesc((p->partitionType).legacy)); 657 int rc = RTDvmVolumeQueryRange(pVol->hVol, &offStart, &offEnd); 658 AssertRC(rc); 879 659 } 660 661 void *row = tbl.addRow(); 662 tbl.setCell(row, colPartition, g_pszBaseImageName, i); 663 tbl.setCell(row, colBoot, (fVolFlags & DVMVOLUME_FLAGS_BOOTABLE) ? '*' : ' '); 664 tbl.setCell(row, colStart, offStart / g_cbSector); 665 tbl.setCell(row, colSectors, cbVol / g_cbSector); 666 tbl.setCell(row, colSize, vboximgScaledSize(cbVol)); 667 tbl.setCell(row, colOffset, offStart); 668 tbl.setCell(row, colType, RTDvmVolumeTypeGetDescr(enmType)); 880 669 } 881 670 tbl.displayTable(); 882 671 RTPrintf ("\n"); 672 } 673 674 675 /** 676 * Sets up the volumes for the disk. 677 * 678 * @returns IPRT status code. 679 */ 680 static int vboxImgMntVolumesSetup(void) 681 { 682 g_cVolumes = 0; 683 g_paVolumes = NULL; 684 685 int rc = RTDvmCreate(&g_hDvmMgr, g_hVfsFileDisk, g_cbSector, 0 /*fFlags*/); 686 if (RT_SUCCESS(rc)) 687 { 688 rc = RTDvmMapOpen(g_hDvmMgr); 689 if (RT_SUCCESS(rc)) 690 { 691 g_cVolumes = RTDvmMapGetValidVolumes(g_hDvmMgr); 692 if ( g_cVolumes != UINT32_MAX 693 && g_cVolumes > 0) 694 { 695 g_paVolumes = (PVBOXIMGMOUNTVOL)RTMemAllocZ(g_cVolumes * sizeof(VBOXIMGMOUNTVOL)); 696 if (RT_LIKELY(g_paVolumes)) 697 { 698 rc = RTDvmMapQueryFirstVolume(g_hDvmMgr, &g_paVolumes[0].hVol); 699 if (RT_SUCCESS(rc)) 700 rc = RTDvmVolumeCreateVfsFile(g_paVolumes[0].hVol, 701 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE, 702 &g_paVolumes[0].hVfsFileVol); 703 704 for (uint32_t i = 1; i < g_cVolumes && RT_SUCCESS(rc); i++) 705 { 706 rc = RTDvmMapQueryNextVolume(g_hDvmMgr, g_paVolumes[i-1].hVol, &g_paVolumes[i].hVol); 707 if (RT_SUCCESS(rc)) 708 rc = RTDvmVolumeCreateVfsFile(g_paVolumes[i].hVol, 709 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE, 710 &g_paVolumes[i].hVfsFileVol); 711 } 712 713 if (RT_SUCCESS(rc)) 714 return VINF_SUCCESS; 715 716 RTMemFree(g_paVolumes); 717 g_paVolumes = NULL; 718 g_cVolumes = 0; 719 } 720 else 721 rc = VERR_NO_MEMORY; 722 } 723 else if (g_cVolumes == UINT32_MAX) 724 { 725 g_cVolumes = 0; 726 rc = VERR_INTERNAL_ERROR; 727 } 728 729 RTDvmRelease(g_hDvmMgr); 730 } 731 else if (rc == VERR_NOT_FOUND) 732 rc = VINF_SUCCESS; 733 } 734 735 return rc; 883 736 } 884 737 … … 1247 1100 return RTMsgErrorExitFailure("Error creating VFS file wrapper for disk image\n"); 1248 1101 1249 g_c Readers = VDIsReadOnly(pVDisk) ? INT32_MAX / 2 : 0;1250 g_cWriters = 0; 1251 rc = RTVfsFileGetSize(g_hVfsFileDisk, (uint64_t *)&g_cbEntireVDisk);1102 g_cbSector = VDGetSectorSize(pVDisk, VD_LAST_IMAGE); 1103 1104 rc = vboxImgMntVolumesSetup(); 1252 1105 if (RT_FAILURE(rc)) 1253 return RTMsgErrorExitFailure("Error querying disk image size from VFS wrapper\n"); 1254 1255 g_cbSector = VDGetSectorSize(pVDisk, VD_LAST_IMAGE); 1106 return RTMsgErrorExitFailure("Error parsing volumes on disk\n"); 1256 1107 1257 1108 if (g_vboximgOpts.fList) … … 1261 1112 1262 1113 RTPrintf("\n"); 1263 rc = parsePartitionTable(); 1264 switch(rc) 1265 { 1266 case PARTITION_TABLE_MBR: 1267 displayLegacyPartitionTable(); 1268 break; 1269 case PARTITION_TABLE_GPT: 1270 displayGptPartitionTable(); 1271 break; 1272 default: 1273 return rc; 1274 } 1114 vboxImgMntVolumesDisplay(); 1275 1115 return 0; 1276 1116 } 1277 1278 if (g_vboximgOpts.idxPartition >= 0)1279 {1280 if (g_vboximgOpts.offset)1281 return RTMsgErrorExitFailure("--offset and --partition are mutually exclusive options\n");1282 1283 if (g_vboximgOpts.size)1284 return RTMsgErrorExitFailure("--size and --partition are mutually exclusive options\n");1285 1286 /*1287 * --partition option specified. That will set the global offset and limit1288 * honored by the disk read and write sanitizers to constrain operations1289 * to within the specified partion based on an initial parsing of the MBR1290 */1291 rc = parsePartitionTable();1292 if (rc < 0)1293 return RTMsgErrorExitFailure("Error parsing disk MBR/Partition table\n");1294 int partNbr = g_vboximgOpts.idxPartition;1295 1296 if (partNbr < 0 || partNbr > g_lastPartNbr)1297 return RTMsgErrorExitFailure("Non-valid partition number specified\n");1298 if (partNbr == 0)1299 {1300 g_vDiskOffset = 0;1301 g_vDiskSize = g_cbEntireVDisk;1302 if (VERBOSE)1303 RTPrintf("\nPartition 0 specified - Whole disk will be accessible\n");1304 } else {1305 int fFoundPartition = false;1306 for (int i = 1; i < g_lastPartNbr + 1; i++)1307 {1308 /* If GPT, display vboximg's representation of partition table starts at partition 21309 * but the table is displayed calling it partition 1, because the protective MBR1310 * record is relatively pointless to display or reference in this context */1311 if (g_aParsedPartitionInfo[i].idxPartition == partNbr + (g_fGPT ? 1 : 0))1312 {1313 fFoundPartition = true;1314 g_vDiskOffset = g_aParsedPartitionInfo[i].offPartition;1315 g_vDiskSize = g_vDiskOffset + g_aParsedPartitionInfo[i].cbPartition;1316 if (VERBOSE)1317 RTPrintf("\nPartition %d specified. Only sectors %llu to %llu of disk will be accessible\n",1318 g_vboximgOpts.idxPartition, g_vDiskOffset / g_cbSector, g_vDiskSize / g_cbSector);1319 }1320 }1321 if (!fFoundPartition)1322 return RTMsgErrorExitFailure("Couldn't find partition %d in partition table\n", partNbr);1323 }1324 } else {1325 if (g_vboximgOpts.offset) {1326 if (g_vboximgOpts.offset < 0 || g_vboximgOpts.offset + g_vboximgOpts.size > g_cbEntireVDisk)1327 return RTMsgErrorExitFailure("User specified offset out of range of virtual disk\n");1328 1329 if (VERBOSE)1330 RTPrintf("Setting r/w bias (offset) to user requested value for sector %llu\n", g_vDiskOffset / g_cbSector);1331 1332 g_vDiskOffset = g_vboximgOpts.offset;1333 }1334 if (g_vboximgOpts.size) {1335 if (g_vboximgOpts.size < 0 || g_vboximgOpts.offset + g_vboximgOpts.size > g_cbEntireVDisk)1336 return RTMsgErrorExitFailure("User specified size out of range of virtual disk\n");1337 1338 if (VERBOSE)1339 RTPrintf("Setting r/w size limit to user requested value %llu\n", g_vDiskSize / g_cbSector);1340 1341 g_vDiskSize = g_vboximgOpts.size;1342 }1343 }1344 if (g_vDiskSize == 0)1345 g_vDiskSize = g_cbEntireVDisk - g_vDiskOffset;1346 1117 1347 1118 /*
Note:
See TracChangeset
for help on using the changeset viewer.