1use crate::gpt::GptPartition;
5use anyhow::{Context as _, Error};
6use block_client::{ReadOptions, VmoId, WriteOptions};
7use block_server::async_interface::{Interface, PassthroughSession, SessionManager};
8use block_server::{DeviceInfo, OffsetMap};
9use fidl::endpoints::{RequestStream, ServerEnd};
10use fidl_fuchsia_storage_block as fblock;
11use fuchsia_async as fasync;
12
13use fuchsia_sync::Mutex;
14use std::borrow::Cow;
15use std::collections::BTreeMap;
16use std::future::Future;
17use std::num::NonZero;
18use std::sync::{Arc, OnceLock, Weak};
19
20pub struct VmoIdWrapper {
23 partition: Weak<GptPartition>,
24 vmo_id: VmoId,
25}
26
27impl std::ops::Deref for VmoIdWrapper {
28 type Target = VmoId;
29 fn deref(&self) -> &Self::Target {
30 &self.vmo_id
31 }
32}
33
34impl Drop for VmoIdWrapper {
35 fn drop(&mut self) {
36 let vmo_id = self.vmo_id.take().into_id();
40 if let Some(partition) = self.partition.upgrade() {
41 fasync::Task::spawn(async move {
42 if let Err(e) = partition.detach_vmo(VmoId::new(vmo_id)).await {
43 if e == zx::Status::CANCELED || e == zx::Status::PEER_CLOSED {
49 log::debug!("detach_vmo failed during shutdown: {:?}", e);
50 } else {
51 log::error!("detach_vmo failed: {:?}", e);
52 }
53 }
54 })
55 .detach();
56 }
57 }
58}
59
60pub struct PartitionBackend {
63 partition: Arc<GptPartition>,
64 vmo_keys_to_vmoids_map: Mutex<BTreeMap<usize, Arc<VmoIdWrapper>>>,
65 offset_map: OffsetMap,
66 mapper_key: OnceLock<u64>,
67}
68
69impl Interface for PartitionBackend {
70 async fn open_session(
71 &self,
72 session_manager: Arc<SessionManager<Self>>,
73 stream: fblock::SessionRequestStream,
74 offset_map: OffsetMap,
75 block_size: u32,
76 ) -> Result<(), Error> {
77 if !offset_map.is_empty() {
78 let _ = stream.control_handle().shutdown_with_epitaph(zx::Status::NOT_SUPPORTED);
80 anyhow::bail!("Client-provided offset maps are not supported");
81 }
82 if self.offset_map.is_empty() {
83 return session_manager
84 .serve_session(
85 stream,
86 OffsetMap::empty(),
87 self.get_info().max_transfer_blocks(),
88 block_size,
89 )
90 .await;
91 }
92 let (proxy, server_end) = fidl::endpoints::create_proxy::<fblock::SessionMarker>();
93 self.partition.open_passthrough_session(server_end, &self.offset_map);
94 let passthrough = PassthroughSession::new(proxy);
95 passthrough.serve(stream).await
96 }
97
98 fn open_mapper_session(
99 session_manager: Arc<SessionManager<Self>>,
100 session: ServerEnd<fblock::MapperSessionMarker>,
101 mapping_vmo: zx::Vmo,
102 _block_size: u32,
103 port: Option<zx::Port>,
104 delivery_queue: Option<zx::Vmo>,
105 ) -> Result<impl Future<Output = Result<(), Error>> + Send + 'static, zx::Status> {
106 let this = session_manager.interface();
107 let Some(gpt) = this.partition.gpt() else {
108 return Err(zx::Status::BAD_STATE);
109 };
110 if this.offset_map.is_empty() || !gpt.has_mapper() {
111 return Err(zx::Status::NOT_SUPPORTED);
112 }
113 let this = this.clone();
114 Ok(async move {
115 let mut init = false;
116 let key = *this.mapper_key.get_or_init(|| {
117 init = true;
118 gpt.next_partition_key()
119 });
120 if init {
123 gpt.register_mappings(key, &this.offset_map).await?;
124 }
125 let session_proxy = gpt.mapper_session_proxy().await;
126 session_proxy
127 .open_child_session(session, mapping_vmo, key, port, delivery_queue)
128 .await
129 .context("FIDL error calling OpenChildSession on mapper session")?
130 .map_err(|status| {
131 anyhow::anyhow!(
132 "OpenChildSession failed: {:?}",
133 zx::Status::err_from_raw(status)
134 )
135 })?;
136 Ok(())
137 })
138 }
139
140 async fn on_attach_vmo(&self, vmo: &zx::Vmo) -> Result<(), zx::Status> {
141 let key = std::ptr::from_ref(vmo) as usize;
142 let vmo_id = unsafe { self.partition.attach_vmo(vmo) }.await?;
145 self.vmo_keys_to_vmoids_map.lock().insert(
146 key,
147 Arc::new(VmoIdWrapper { partition: Arc::downgrade(&self.partition), vmo_id }),
148 );
149 Ok(())
150 }
151
152 fn on_detach_vmo(&self, vmo: &zx::Vmo) {
153 let key = std::ptr::from_ref(vmo) as usize;
156 self.vmo_keys_to_vmoids_map.lock().remove(&key);
157 }
158
159 fn get_info(&self) -> Cow<'_, DeviceInfo> {
160 Cow::Owned(self.partition.get_info())
161 }
162
163 async fn read(
164 &self,
165 device_block_offset: u64,
166 block_count: u32,
167 vmo: &Arc<zx::Vmo>,
168 vmo_offset: u64, opts: ReadOptions,
170 trace_flow_id: Option<NonZero<u64>>,
171 ) -> Result<(), zx::Status> {
172 let vmo_id = self.get_vmoid(vmo)?;
173 self.partition
174 .read(device_block_offset, block_count, &vmo_id, vmo_offset, opts, trace_flow_id)
175 .await
176 }
177
178 async fn write(
179 &self,
180 device_block_offset: u64,
181 length: u32,
182 vmo: &Arc<zx::Vmo>,
183 vmo_offset: u64, opts: WriteOptions,
185 trace_flow_id: Option<NonZero<u64>>,
186 ) -> Result<(), zx::Status> {
187 let vmo_id = self.get_vmoid(vmo)?;
188 self.partition
189 .write(device_block_offset, length, &vmo_id, vmo_offset, opts, trace_flow_id)
190 .await
191 }
192
193 async fn flush(&self, trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
194 self.partition.flush(trace_flow_id).await
195 }
196
197 async fn trim(
198 &self,
199 device_block_offset: u64,
200 block_count: u32,
201 trace_flow_id: Option<NonZero<u64>>,
202 ) -> Result<(), zx::Status> {
203 self.partition.trim(device_block_offset, block_count, trace_flow_id).await
204 }
205}
206
207impl PartitionBackend {
208 #[cfg(test)]
209 pub fn passthrough(&self) -> bool {
210 !self.offset_map.is_empty()
211 }
212
213 #[cfg(test)]
214 pub fn vmo_count(&self) -> usize {
215 self.vmo_keys_to_vmoids_map.lock().len()
216 }
217
218 pub fn new(partition: Arc<GptPartition>, offset_map: block_server::OffsetMap) -> Arc<Self> {
222 Arc::new(Self {
223 partition,
224 offset_map,
225 vmo_keys_to_vmoids_map: Mutex::new(BTreeMap::new()),
226 mapper_key: OnceLock::new(),
227 })
228 }
229
230 pub fn update_info(&self, info: gpt::PartitionInfo) {
232 self.partition.update_info(info)
233 }
234
235 fn get_vmoid(&self, vmo: &zx::Vmo) -> Result<Arc<VmoIdWrapper>, zx::Status> {
236 let key = std::ptr::from_ref(vmo) as usize;
237 self.vmo_keys_to_vmoids_map.lock().get(&key).map(Arc::clone).ok_or(zx::Status::NOT_FOUND)
238 }
239}