Skip to content

vhdxtool: add cross-platform VHDX utility - #4370

Draft
John Starks (jstarks) wants to merge 2 commits into
microsoft:mainfrom
jstarks:vhdxtool
Draft

vhdxtool: add cross-platform VHDX utility#4370
John Starks (jstarks) wants to merge 2 commits into
microsoft:mainfrom
jstarks:vhdxtool

Conversation

@jstarks

Copy link
Copy Markdown
Member

Add a standalone utility for creating, inspecting, mapping, validating, replaying, and converting VHDX images. This provides a portable workflow for working with VHDX files directly through the repository's native format implementation.

Extend the VHDX creation API with typed dynamic, fixed, and differencing disk configuration so invalid parent combinations cannot be represented. Add parent-locator serialization and typed interpretation, including canonical linkage identifiers and optional parent paths, to support creating and validating differencing chains.

Add a standalone utility for creating, inspecting, mapping, validating,
replaying, and converting VHDX images. This provides a portable workflow for
working with VHDX files directly through the repository's native format
implementation.

Extend the VHDX creation API with typed dynamic, fixed, and differencing disk
configuration so invalid parent combinations cannot be represented. Add
parent-locator serialization and typed interpretation, including canonical
linkage identifiers and optional parent paths, to support creating and
validating differencing chains.
Copilot AI lite review requested due to automatic review settings September 2, 2026 22:32
@github-actions github-actions Bot added the unsafe Related to unsafe code label Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

⚠️ Unsafe Code Detected

This PR modifies files containing unsafe Rust code. Extra scrutiny is required during review.

For more on why we check whole files, instead of just diffs, check out the Rustonomicon

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Differencing-disk creation currently treats relative-path computation as mandatory (breaking common cross-root/drive cases) and there’s a misleading new test name that should be corrected to reflect actual assertions.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a new cross-platform vhdxtool CLI to create/inspect/map/convert/check/replay VHDX images, and extends the vhdx crate creation/open surface to represent disk allocation + differencing parent metadata with typed APIs (including parent-locator serialization/parsing) so invalid configurations are unrepresentable.

Changes:

  • Introduce vhdxtool (new crate) with commands for creating, inspecting, mapping, converting, validating parent chains, and replaying logs.
  • Refactor vhdx::CreateParams to use typed DiskType + VhdxParent, and add parent-locator building/parsing and open-error classification.
  • Update vhdx tests to the new creation API and validate locator behavior.
File summaries
File Description
vm/devices/storage/vhdxtool/src/util.rs Adds CLI helpers for size parsing/formatting and relative parent-path computation.
vm/devices/storage/vhdxtool/src/main.rs Implements the vhdxtool CLI and operational logic for create/info/map/convert/check/replay.
vm/devices/storage/vhdxtool/src/file.rs Provides a cross-platform AsyncFile implementation for positional I/O backing vhdxtool.
vm/devices/storage/vhdxtool/README.md Documents vhdxtool usage and expected behaviors (exit codes, parent locator notes, sparsity).
vm/devices/storage/vhdxtool/Cargo.toml Defines the new vhdxtool crate and dependencies.
vm/devices/storage/vhdx/tests/native_cross_validation.rs Updates cross-validation harness to create differencing disks via typed DiskType.
vm/devices/storage/vhdx/src/tests/trim_tests.rs Updates fixed-disk creation in trim tests to use DiskType::Fixed.
vm/devices/storage/vhdx/src/tests/mod.rs Updates integration tests to use DiskType::Differencing(...).
vm/devices/storage/vhdx/src/tests/io_tests.rs Updates I/O tests for differencing disks to use typed DiskType.
vm/devices/storage/vhdx/src/sector_bitmap.rs Updates sector-bitmap tests to use DiskType::Differencing(...).
vm/devices/storage/vhdx/src/open.rs Adds/updates tests around differencing opens and fully allocated behavior.
vm/devices/storage/vhdx/src/locator.rs Adds typed vhdx_parent() interpretation + makes locator construction size-safe.
vm/devices/storage/vhdx/src/lib.rs Re-exports new public types (DiskType, VhdxParent, OpenErrorKind, etc.).
vm/devices/storage/vhdx/src/error.rs Adds OpenErrorKind classification and new invalid-format reasons related to parent locators.
vm/devices/storage/vhdx/src/create.rs Implements typed disk creation + parent-locator metadata emission with size validation.
Cargo.toml Registers vhdxtool as a workspace member.
Cargo.lock Adds the new vhdxtool package entry and its resolved dependency set.
Review details
  • Files reviewed: 16/17 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread vm/devices/storage/vhdxtool/src/main.rs Outdated
Comment on lines +344 to +350
let absolute_parent = fs_err::canonicalize(&parent_path)
.with_context(|| format!("failed to resolve parent {}", parent_path.display()))?;
let relative_path = util::relative_path(child_directory, &absolute_parent)?
.to_string_lossy()
.replace(std::path::MAIN_SEPARATOR, "\\");
let vhdx_parent =
VhdxParent::new(parent.data_write_guid())?.with_relative_path(relative_path)?;
Comment on lines +873 to +875
#[async_test]
async fn open_differencing_disk_ignores_leave_blocks_allocated() {
let file = InMemoryFile::new(0);
Copilot AI review requested due to automatic review settings September 3, 2026 16:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

OpenError::kind() currently matches on self.0 by value (moving out of &self) and will not compile.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

vm/devices/storage/vhdx/src/locator.rs:235

  • The doc comment doesn’t explain why this now returns Option<Vec<u8>>. Since callers map None to ParentLocatorTooLarge, it would be helpful to document the failure conditions (oversize locator / overflow / too many entries).
    vm/devices/storage/vhdxtool/src/main.rs:764
  • Opening the newly created output file can fail, but this ? loses the path context in the error chain. Adding with_context here will make failures easier to diagnose (e.g., permission issues, file deleted between create and open).

This issue also appears on line 810 of the same file.

vm/devices/storage/vhdxtool/src/main.rs:810

  • Same as above: this open is part of the conversion flow, but failing here won’t include the output path unless you add context.
                    let output_file = BlockingFile::open(output_path, false)?;
  • Files reviewed: 16/17 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +82 to +94
pub fn kind(&self) -> OpenErrorKind {
match self.0 {
OpenErrorInner::Io(_) => OpenErrorKind::Io,
OpenErrorInner::Corrupt(CorruptionType::LogReplayRequired) => {
OpenErrorKind::LogReplayRequired
}
OpenErrorInner::Corrupt(_) => OpenErrorKind::Corruption,
OpenErrorInner::InvalidParameter(_) => OpenErrorKind::InvalidParameter,
OpenErrorInner::PipelineFailed(_) | OpenErrorInner::MetadataCache(_) => {
OpenErrorKind::Other
}
}
}
Comment on lines +346 to +349
let relative_path = util::relative_path(child_directory, &absolute_parent).map(|path| {
path.to_string_lossy()
.replace(std::path::MAIN_SEPARATOR, "\\")
});
//! can be repaired with `replay`.

#![forbid(unsafe_code)]
#![deny(missing_docs)]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just warn on missing_docs so that it doesn't block local builds.

block_alignment: Option<u64>,
/// Data write GUID. A random GUID is generated when omitted.
#[arg(long)]
id: Option<String>,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why we would want to expose this.

/// VHDX payload block size. Accepts binary size suffixes.
#[arg(long, value_parser = util::parse_size)]
block_size: Option<u64>,
/// Logical sector size in bytes: 512 or 4096.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the spec/vhdx implementation really limit us to these two values?

/// Parent VHDX path. Required for, and only valid with, differencing images.
#[arg(long)]
parent: Option<PathBuf>,
/// VHDX payload block size. Accepts binary size suffixes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth documenting here the minimum and default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

unsafe Related to unsafe code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants