Skip to main content

win_durable_ioring/windows_backend/
file_target.rs

1// Copyright (c) 2026 Mike Grier
2//! [`Win32FileTarget`]: a target backed by a Win32 file, a raw volume or a whole disk (DI-D-48,
3//! DI-D-50, DI-D-60, DI-D-61, DI-D-62).
4
5use win_shared_os_owned_handle::SharedHandle;
6
7use super::opened_disk::OpenedDisk;
8use super::opened_file::{Buffering, OpenedFile};
9use super::opened_volume::OpenedVolume;
10use durable_ioring::FlushDomain;
11use durable_ioring::{Capabilities, Geometry, IoTarget, WarrantyClass};
12
13/// A target backed by a Win32 file, a raw volume or a whole disk this crate opened, held as a
14/// `SharedHandle` -- an owning form, never a raw handle (DI-D-4).
15///
16/// It is made from an [`OpenedFile`], an [`OpenedVolume`] or an [`OpenedDisk`] and from nothing else, and takes its
17/// warranty class, its alignment and its capabilities from how that was opened, not from a
18/// declaration (DI-D-60): one opened cached is [`WarrantyClass::BestEffort`], and one opened
19/// [`Unbuffered`](super::Unbuffered) is [`WarrantyClass::SharedDevice`], needs a sector's alignment,
20/// and offers per-write write-through. [`WarrantyClass::Complete`] is never one's: it needs
21/// knowledge of what else writes the device, which a volume's lock does not give (DI-D-61).
22///
23/// A volume's flush domains are found from its disks. A file's are what it cannot know, so they are
24/// declared with [`with_domains`](Self::with_domains); declaring them on a volume adds to the ones
25/// found, and never removes one.
26#[derive(Clone, Debug)]
27pub struct Win32FileTarget {
28    handle: SharedHandle,
29    /// What the backing found for itself: a volume's disks.
30    found: Vec<FlushDomain>,
31    /// What the consumer declared.
32    declared: Vec<FlushDomain>,
33    /// Both, each once: what the target reports.
34    domains: Vec<FlushDomain>,
35    warranty: WarrantyClass,
36    geometry: Geometry,
37    capabilities: Capabilities,
38}
39
40impl Win32FileTarget {
41    /// The target `file` makes, claiming what its open mode allows and no more.
42    #[must_use]
43    pub fn new<B: Buffering>(file: OpenedFile<B>) -> Self {
44        Self {
45            geometry: file.geometry(),
46            handle: file.handle().clone(),
47            found: Vec::new(),
48            declared: Vec::new(),
49            domains: Vec::new(),
50            warranty: B::WARRANTY,
51            capabilities: B::CAPABILITIES,
52        }
53    }
54
55    /// The target `volume` makes: its sector's alignment, the flush domains of the disks it sits
56    /// on, and what its open mode allows and no more.
57    #[must_use]
58    pub fn from_volume<B: Buffering>(volume: OpenedVolume<B>) -> Self {
59        let found = volume.flush_domains();
60        Self {
61            geometry: volume.geometry(),
62            handle: volume.handle().clone(),
63            domains: found.clone(),
64            found,
65            declared: Vec::new(),
66            warranty: B::WARRANTY,
67            capabilities: B::CAPABILITIES,
68        }
69    }
70
71    /// The target `disk` makes: its sector's alignment, the disk itself as its flush domain, and
72    /// what its open mode allows and no more.
73    #[must_use]
74    pub fn from_disk<B: Buffering>(disk: OpenedDisk<B>) -> Self {
75        let found = vec![disk.flush_domain()];
76        Self {
77            geometry: disk.geometry(),
78            handle: disk.handle().clone(),
79            domains: found.clone(),
80            found,
81            declared: Vec::new(),
82            warranty: B::WARRANTY,
83            capabilities: B::CAPABILITIES,
84        }
85    }
86
87    /// Declare every flush domain its writes depend on, beyond any the backing found for itself.
88    /// Declaring again replaces what was declared before, and never what the backing found.
89    #[must_use]
90    pub fn with_domains(mut self, domains: impl IntoIterator<Item = FlushDomain>) -> Self {
91        self.declared = domains.into_iter().collect();
92        self.domains = self.found.clone();
93        for domain in &self.declared {
94            if !self.domains.contains(domain) {
95                self.domains.push(domain.clone());
96            }
97        }
98        self
99    }
100
101    /// The file's handle.
102    #[must_use]
103    pub fn handle(&self) -> &SharedHandle {
104        &self.handle
105    }
106
107    /// State a class a file's open mode cannot give, to test what the backend does with each.
108    #[cfg(test)]
109    #[must_use]
110    pub(crate) fn declared(
111        mut self,
112        warranty: WarrantyClass,
113        geometry: Geometry,
114        capabilities: Capabilities,
115    ) -> Self {
116        self.warranty = warranty;
117        self.geometry = geometry;
118        self.capabilities = capabilities;
119        self
120    }
121}
122
123impl IoTarget for Win32FileTarget {
124    fn flush_domains(&self) -> &[FlushDomain] {
125        &self.domains
126    }
127
128    fn warranty(&self) -> WarrantyClass {
129        self.warranty
130    }
131
132    fn geometry(&self) -> Geometry {
133        self.geometry
134    }
135
136    fn capabilities(&self) -> Capabilities {
137        self.capabilities
138    }
139}