disk_layered/
resolver.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Resolvers for layered disks.
5
6use super::InvalidLayeredDisk;
7use super::LayerConfiguration;
8use super::LayeredDisk;
9use super::resolve::ResolveDiskLayerParameters;
10use super::resolve::ResolvedDiskLayer;
11use crate::DiskLayer;
12use async_trait::async_trait;
13use disk_backend::InvalidDisk;
14use disk_backend::resolve::ResolveDiskParameters;
15use disk_backend::resolve::ResolvedDisk;
16use disk_backend_resources::LayeredDiskHandle;
17use disk_backend_resources::layer::DiskLayerHandle;
18use futures::future::TryJoinAll;
19use thiserror::Error;
20use vm_resource::AsyncResolveResource;
21use vm_resource::ResolveError;
22use vm_resource::ResourceResolver;
23use vm_resource::declare_static_async_resolver;
24use vm_resource::kind::DiskHandleKind;
25use vm_resource::kind::DiskLayerHandleKind;
26
27declare_static_async_resolver! {
28    LayeredDiskResolver,
29    (DiskHandleKind, LayeredDiskHandle),
30    (DiskLayerHandleKind, DiskLayerHandle)
31}
32
33/// Resolver for [`LayeredDiskHandle`] and [`DiskLayerHandle`].
34pub struct LayeredDiskResolver;
35
36/// Error type for [`LayeredDiskResolver`].
37#[derive(Debug, Error)]
38pub enum ResolveLayeredDiskError {
39    /// Failed to resolve a layer resource.
40    #[error("failed to resolve layer {0}")]
41    ResolveLayer(usize, #[source] ResolveError),
42    /// Failed to create the layered disk.
43    #[error("failed to create layered disk")]
44    CreateDisk(#[source] InvalidLayeredDisk),
45    /// Failed to instantiate the disk.
46    #[error("invalid disk")]
47    InvalidDisk(#[source] InvalidDisk),
48}
49
50#[async_trait]
51impl AsyncResolveResource<DiskHandleKind, LayeredDiskHandle> for LayeredDiskResolver {
52    type Output = ResolvedDisk;
53    type Error = ResolveLayeredDiskError;
54
55    async fn resolve(
56        &self,
57        resolver: &ResourceResolver,
58        resource: LayeredDiskHandle,
59        input: ResolveDiskParameters<'_>,
60    ) -> Result<Self::Output, Self::Error> {
61        let mut read_only = input.read_only;
62        let layers = resource
63            .layers
64            .into_iter()
65            .enumerate()
66            .map(|(i, desc)| {
67                let this_read_only = read_only && !desc.read_cache;
68                if !desc.write_through {
69                    read_only = true;
70                }
71                async move {
72                    let layer = resolver
73                        .resolve(
74                            desc.layer,
75                            ResolveDiskLayerParameters {
76                                read_only: this_read_only,
77                                _async_trait_workaround: &(),
78                            },
79                        )
80                        .await
81                        .map_err(|err| ResolveLayeredDiskError::ResolveLayer(i, err))?;
82
83                    Ok(LayerConfiguration {
84                        layer: layer.0,
85                        write_through: desc.write_through,
86                        read_cache: desc.read_cache,
87                    })
88                }
89            })
90            .collect::<TryJoinAll<_>>()
91            .await?;
92
93        let disk = LayeredDisk::new(input.read_only, layers)
94            .await
95            .map_err(ResolveLayeredDiskError::CreateDisk)?;
96
97        ResolvedDisk::new(disk).map_err(ResolveLayeredDiskError::InvalidDisk)
98    }
99}
100
101#[async_trait]
102impl AsyncResolveResource<DiskLayerHandleKind, DiskLayerHandle> for LayeredDiskResolver {
103    type Output = ResolvedDiskLayer;
104    type Error = ResolveError;
105
106    async fn resolve(
107        &self,
108        resolver: &ResourceResolver,
109        resource: DiskLayerHandle,
110        input: ResolveDiskLayerParameters<'_>,
111    ) -> Result<Self::Output, Self::Error> {
112        let disk = resolver
113            .resolve(
114                resource.0,
115                ResolveDiskParameters {
116                    read_only: input.read_only,
117                    _async_trait_workaround: &(),
118                },
119            )
120            .await?;
121
122        Ok(ResolvedDiskLayer(DiskLayer::from_disk(disk.0)))
123    }
124}