openvmm_helpers/
disk.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Guest disk helpers.
5
6use anyhow::Context;
7use std::path::Path;
8use vm_resource::Resource;
9use vm_resource::kind::DiskHandleKind;
10
11fn disk_open_error(path: &Path, verb: &str) -> String {
12    let mut msg = format!("{verb} '{}'", path.display());
13
14    // On windows, attempt to detect we ran under wsl by reading the WSLENV and
15    // bail out with a helpful hint that it needs to be a windows path.
16    if cfg!(windows) && std::env::var_os("WSLENV").is_some() {
17        msg += ". Linux paths are not supported when running Windows executables \
18                under WSL, make sure the path is a valid Windows path \
19                (use `wslpath -w` to convert)";
20    }
21
22    msg
23}
24
25/// Opens the resources needed for using a disk from a file at `path`.
26///
27/// If the file ends with .vhd and is a fixed VHD1, it will be opened using
28/// the user-mode VHD parser. Otherwise, if the file ends with .vhd or
29/// .vhdx, the file will be opened using the kernel-mode VHD parser.
30pub fn open_disk_type(path: &Path, read_only: bool) -> anyhow::Result<Resource<DiskHandleKind>> {
31    Ok(match path.extension().and_then(|s| s.to_str()) {
32        Some("vhd") => {
33            let file = std::fs::OpenOptions::new()
34                .read(true)
35                .write(!read_only)
36                .open(path)
37                .with_context(|| disk_open_error(path, "failed to open"))?;
38
39            match disk_vhd1::Vhd1Disk::open_fixed(file, read_only) {
40                Ok(vhd) => Resource::new(disk_backend_resources::FixedVhd1DiskHandle(
41                    vhd.into_inner(),
42                )),
43                Err(disk_vhd1::OpenError::NotFixed) => {
44                    #[cfg(windows)]
45                    {
46                        Resource::new(disk_vhdmp::OpenVhdmpDiskConfig(
47                            disk_vhdmp::VhdmpDisk::open_vhd(path, read_only)
48                                .with_context(|| disk_open_error(path, "failed to open"))?,
49                        ))
50                    }
51                    #[cfg(not(windows))]
52                    anyhow::bail!("non-fixed VHD not supported on Linux");
53                }
54                Err(err) => return Err(err.into()),
55            }
56        }
57        Some("vhdx") => {
58            #[cfg(windows)]
59            {
60                Resource::new(disk_vhdmp::OpenVhdmpDiskConfig(
61                    disk_vhdmp::VhdmpDisk::open_vhd(path, read_only)
62                        .with_context(|| disk_open_error(path, "failed to open"))?,
63                ))
64            }
65            #[cfg(not(windows))]
66            anyhow::bail!("VHDX not supported on Linux");
67        }
68        Some("iso") if !read_only => {
69            anyhow::bail!("iso file cannot be opened as read/write")
70        }
71        Some("vmgs") => {
72            // VMGS files are fixed VHD1s. Don't bother to validate the footer
73            // here; let the resource resolver do that later.
74            let file = std::fs::OpenOptions::new()
75                .read(true)
76                .write(!read_only)
77                .open(path)
78                .with_context(|| disk_open_error(path, "failed to open"))?;
79
80            Resource::new(disk_backend_resources::FixedVhd1DiskHandle(file))
81        }
82        _ => {
83            let file = std::fs::OpenOptions::new()
84                .read(true)
85                .write(!read_only)
86                .open(path)
87                .with_context(|| disk_open_error(path, "failed to open"))?;
88
89            Resource::new(disk_backend_resources::FileDiskHandle(file))
90        }
91    })
92}
93
94/// Create and open the resources needed for using a disk from a file at `path`.
95pub fn create_disk_type(path: &Path, size: u64) -> anyhow::Result<Resource<DiskHandleKind>> {
96    Ok(match path.extension().and_then(|s| s.to_str()) {
97        Some("vhd") | Some("vmgs") => {
98            let file = std::fs::OpenOptions::new()
99                .create(true)
100                .truncate(true)
101                .read(true)
102                .write(true)
103                .open(path)
104                .with_context(|| disk_open_error(path, "failed to create"))?;
105
106            file.set_len(size)?;
107            disk_vhd1::Vhd1Disk::make_fixed(&file)?;
108            Resource::new(disk_backend_resources::FixedVhd1DiskHandle(file))
109        }
110        Some("vhdx") => {
111            anyhow::bail!("creating vhdx not supported")
112        }
113        Some("iso") => {
114            anyhow::bail!("creating iso not supported")
115        }
116        _ => {
117            let file = std::fs::OpenOptions::new()
118                .create(true)
119                .truncate(true)
120                .read(true)
121                .write(true)
122                .open(path)
123                .with_context(|| disk_open_error(path, "failed to create"))?;
124
125            file.set_len(size)?;
126            Resource::new(disk_backend_resources::FileDiskHandle(file))
127        }
128    })
129}