1use crate::config::Config;
6use crate::partition::PartitionBackend;
7use crate::partitions_directory::PartitionsDirectory;
8use anyhow::{Context as _, Error, ensure};
9use block_client::{
10 BlockClient as _, BufferSlice, MutableBufferSlice, ReadOptions, RemoteBlockClient, VmoId,
11 WriteOptions,
12};
13use block_server::async_interface::SessionManager;
14use block_server::{BlockServer, OffsetMap};
15
16use fidl::endpoints::ServerEnd;
17use fidl_fuchsia_storage_block as fblock;
18use fidl_fuchsia_storage_partitions as fpartitions;
19use fs_management::format::constants::{
20 ALL_BENCHMARK_PARTITION_LABELS, ALL_SYSTEM_PARTITION_LABELS,
21};
22use fuchsia_async as fasync;
23use fuchsia_sync::Mutex;
24use futures::stream::TryStreamExt as _;
25use std::collections::BTreeMap;
26use std::num::NonZero;
27use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
28use std::sync::{Arc, OnceLock, Weak};
29
30fn partition_directory_entry_name(index: u32) -> String {
31 format!("part-{:03}", index)
32}
33
34fn should_passthrough_partition(info: &block_server::PartitionInfo) -> bool {
41 ALL_SYSTEM_PARTITION_LABELS.contains(&info.name.as_str())
43 || ALL_BENCHMARK_PARTITION_LABELS.contains(&info.name.as_str())
46 || info.start_block_offset.is_none()
50}
51
52fn single_partition_mapping(info: &block_server::PartitionInfo) -> Result<OffsetMap, Error> {
53 Ok(OffsetMap::new(vec![block_server::BlockOffsetMapping {
54 target_block_offset: info.start_block_offset.ok_or(zx::Status::INVALID_ARGS)?,
55 length: info.block_count,
56 }])?)
57}
58
59pub struct GptPartition {
61 gpt: Weak<GptManager>,
62 info: Mutex<block_server::PartitionInfo>,
63 block_client: Arc<RemoteBlockClient>,
64}
65
66fn trace_id(trace_flow_id: Option<NonZero<u64>>) -> u64 {
67 trace_flow_id.map(|v| v.get()).unwrap_or_default()
68}
69
70impl GptPartition {
71 pub fn new(
72 gpt: &Arc<GptManager>,
73 block_client: Arc<RemoteBlockClient>,
74 info: block_server::PartitionInfo,
75 ) -> Arc<Self> {
76 Arc::new(Self { gpt: Arc::downgrade(gpt), info: Mutex::new(info), block_client })
77 }
78
79 pub async fn terminate(&self) {
80 if let Err(error) = self.block_client.close().await {
81 log::warn!(error:?; "Failed to close block client");
82 }
83 }
84
85 pub fn update_info(&self, info: gpt::PartitionInfo) {
86 *self.info.lock() = info.into();
87 }
88
89 pub fn block_size(&self) -> u32 {
90 self.block_client.block_size()
91 }
92
93 pub fn block_count(&self) -> u64 {
94 self.info.lock().block_count
95 }
96
97 pub async unsafe fn attach_vmo(&self, vmo: &zx::Vmo) -> Result<VmoId, zx::Status> {
109 unsafe { self.block_client.attach_vmo(vmo) }.await
112 }
113
114 pub async fn detach_vmo(&self, vmoid: VmoId) -> Result<(), zx::Status> {
115 self.block_client.detach_vmo(vmoid).await
116 }
117
118 pub fn open_passthrough_session(
119 &self,
120 session: ServerEnd<fblock::SessionMarker>,
121 offset_map: &OffsetMap,
122 ) {
123 if let Some(gpt) = self.gpt.upgrade() {
124 let mappings: Vec<fblock::BlockOffsetMapping> = offset_map.into();
125 if let Err(error) = gpt.block_proxy.open_session_with_options(session, &mappings[..]) {
126 log::warn!(error:?; "Failed to open passthrough session");
129 }
130 } else {
131 if let Err(error) = session.close_with_epitaph(zx::Status::BAD_STATE) {
132 log::warn!(error:?; "Failed to send session epitaph");
133 }
134 }
135 }
136
137 pub fn gpt(&self) -> Option<Arc<GptManager>> {
139 self.gpt.upgrade()
140 }
141
142 pub fn get_info(&self) -> block_server::DeviceInfo {
143 let mut info = self.info.lock().clone();
144 info.device_flags = self.block_client.block_flags();
145 info.max_transfer_blocks = self.block_client.max_transfer_blocks();
146 block_server::DeviceInfo::Partition(info)
147 }
148
149 pub async fn read(
150 &self,
151 device_block_offset: u64,
152 block_count: u32,
153 vmo_id: &VmoId,
154 vmo_offset: u64, opts: ReadOptions,
156 trace_flow_id: Option<NonZero<u64>>,
157 ) -> Result<(), zx::Status> {
158 let dev_offset = self
159 .absolute_offset(device_block_offset, block_count)
160 .map(|offset| offset * self.block_size() as u64)?;
161 let buffer = MutableBufferSlice::new_with_vmo_id(
162 vmo_id,
163 vmo_offset,
164 (block_count * self.block_size()) as u64,
165 );
166 self.block_client
167 .read_at_with_opts_traced(buffer, dev_offset, opts, trace_id(trace_flow_id))
168 .await
169 }
170
171 pub async fn write(
172 &self,
173 device_block_offset: u64,
174 block_count: u32,
175 vmo_id: &VmoId,
176 vmo_offset: u64, opts: WriteOptions,
178 trace_flow_id: Option<NonZero<u64>>,
179 ) -> Result<(), zx::Status> {
180 let dev_offset = self
181 .absolute_offset(device_block_offset, block_count)
182 .map(|offset| offset * self.block_size() as u64)?;
183 let buffer = BufferSlice::new_with_vmo_id(
184 vmo_id,
185 vmo_offset,
186 (block_count * self.block_size()) as u64,
187 );
188 self.block_client
189 .write_at_with_opts_traced(buffer, dev_offset, opts, trace_id(trace_flow_id))
190 .await
191 }
192
193 pub async fn flush(&self, trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
194 self.block_client.flush_traced(trace_id(trace_flow_id)).await
195 }
196
197 pub 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 let dev_offset = self
204 .absolute_offset(device_block_offset, block_count)
205 .map(|offset| offset * self.block_size() as u64)?;
206 let len = block_count as u64 * self.block_size() as u64;
207 let end = dev_offset.checked_add(len).ok_or(zx::Status::OUT_OF_RANGE)?;
208
209 self.block_client.trim_traced(dev_offset..end, trace_id(trace_flow_id)).await
210 }
211
212 fn absolute_offset(&self, mut offset: u64, len: u32) -> Result<u64, zx::Status> {
216 let info = self.info.lock();
217 let Some(start_block) = info.start_block_offset else {
218 return Err(zx::Status::BAD_STATE);
221 };
222 offset = offset.checked_add(start_block).ok_or(zx::Status::OUT_OF_RANGE)?;
223 let end = offset.checked_add(len as u64).ok_or(zx::Status::OUT_OF_RANGE)?;
224 if end > start_block + info.block_count {
225 Err(zx::Status::OUT_OF_RANGE)
226 } else {
227 Ok(offset)
228 }
229 }
230}
231
232struct PendingTransaction {
233 transaction: gpt::Transaction,
234 client_koid: zx::Koid,
235 added_partitions: Vec<u32>,
238 _signal_task: fasync::Task<()>,
240}
241
242struct Inner {
243 gpt: gpt::Gpt,
244 partitions: BTreeMap<u32, Arc<BlockServer<SessionManager<PartitionBackend>>>>,
245 composite_partitions: BTreeMap<u32, Arc<BlockServer<SessionManager<PartitionBackend>>>>,
247 partitions_dir: PartitionsDirectory,
250 pending_transaction: Option<PendingTransaction>,
251}
252
253impl Inner {
254 fn ensure_transaction_matches(&self, transaction: &zx::EventPair) -> Result<(), zx::Status> {
256 if let Some(pending) = self.pending_transaction.as_ref() {
257 if transaction.koid()? == pending.client_koid {
258 Ok(())
259 } else {
260 Err(zx::Status::BAD_HANDLE)
261 }
262 } else {
263 Err(zx::Status::BAD_STATE)
264 }
265 }
266
267 fn bind_partition(
268 &mut self,
269 parent: &Arc<GptManager>,
270 index: u32,
271 info: block_server::PartitionInfo,
272 composite_mappings: OffsetMap,
273 composite_indexes: Vec<usize>,
274 ) -> Result<(), Error> {
275 ensure!(
276 composite_indexes.is_empty() == composite_mappings.is_empty(),
277 "Composite partitions must provide mappings"
278 );
279 let passthrough = should_passthrough_partition(&info);
280 let mappings = if passthrough && composite_mappings.is_empty() {
281 single_partition_mapping(&info)?
283 } else {
284 composite_mappings
288 };
289 log::debug!(
290 "GPT part {index}{}{}: {info:?}",
291 if !composite_indexes.is_empty() { " (composite)" } else { "" },
292 if passthrough { " (passthrough)" } else { "" },
293 );
294 let partition = PartitionBackend::new(
295 GptPartition::new(parent, self.gpt.client().clone(), info),
296 mappings,
297 );
298 let block_server = Arc::new(BlockServer::new(parent.block_size, partition));
299 if !composite_indexes.is_empty() {
300 self.partitions_dir.add_composite(
301 &partition_directory_entry_name(index),
302 Arc::downgrade(&block_server),
303 Arc::downgrade(parent),
304 composite_indexes,
305 );
306 self.composite_partitions.insert(index, block_server);
307 } else {
308 self.partitions_dir.add_partition(
309 &partition_directory_entry_name(index),
310 Arc::downgrade(&block_server),
311 Arc::downgrade(parent),
312 index as usize,
313 );
314 self.partitions.insert(index, block_server);
315 }
316 Ok(())
317 }
318
319 fn bind_super_and_userdata_partition(
320 &mut self,
321 parent: &Arc<GptManager>,
322 super_partition: (u32, gpt::PartitionInfo),
323 userdata_partition: (u32, gpt::PartitionInfo),
324 ) -> Result<(), Error> {
325 let extent1 = block_server::BlockOffsetMapping {
326 target_block_offset: super_partition.1.start_block,
327 length: super_partition.1.num_blocks,
328 };
329 let extent2 = block_server::BlockOffsetMapping {
330 target_block_offset: userdata_partition.1.start_block,
331 length: userdata_partition.1.num_blocks,
332 };
333 let mappings =
334 block_server::OffsetMap::new(block_server::coalesce_mappings(vec![extent1, extent2]))?;
335 let info = block_server::PartitionInfo {
336 name: "super_and_userdata".to_string(),
338 type_guid: super_partition.1.type_guid.to_bytes(),
339 instance_guid: super_partition.1.instance_guid.to_bytes(),
340 block_count: mappings.total_blocks(),
341 ..Default::default()
342 };
343 log::debug!(
344 "GPT merged parts {:?} + {:?} -> {info:?}",
345 super_partition.1,
346 userdata_partition.1
347 );
348 self.bind_partition(
349 parent,
350 super_partition.0,
351 info,
352 mappings,
353 vec![super_partition.0 as usize, userdata_partition.0 as usize],
354 )
355 }
356
357 async fn bind_all_partitions(&mut self, parent: &Arc<GptManager>) -> Result<(), Error> {
358 self.partitions.clear();
359 self.composite_partitions.clear();
360 self.partitions_dir.clear().await;
361
362 let mut partitions = self.gpt.partitions().clone();
363 if parent.config.merge_super_and_userdata {
364 let super_part = match partitions
367 .iter()
368 .find(|(_, info)| info.label == "super")
369 .map(|(index, _)| *index)
370 {
371 Some(index) => partitions.remove_entry(&index),
372 None => None,
373 };
374 let userdata_part = match partitions
375 .iter()
376 .find(|(_, info)| info.label == "userdata")
377 .map(|(index, _)| *index)
378 {
379 Some(index) => partitions.remove_entry(&index),
380 None => None,
381 };
382 if super_part.is_some() && userdata_part.is_some() {
383 let super_part = super_part.unwrap();
384 let userdata_part = userdata_part.unwrap();
385 self.bind_super_and_userdata_partition(parent, super_part, userdata_part)?;
386 } else if super_part.is_some() || userdata_part.is_some() {
387 log::warn!("Only one of super/userdata found; not merging");
388 let (index, info) = super_part.or(userdata_part).unwrap();
389 self.bind_partition(
390 parent,
391 index,
392 block_server::PartitionInfo::from(&info),
393 OffsetMap::empty(),
394 vec![],
395 )?;
396 }
397 }
398 for (index, info) in partitions {
399 self.bind_partition(
400 parent,
401 index,
402 block_server::PartitionInfo::from(&info),
403 OffsetMap::empty(),
404 vec![],
405 )?;
406 }
407 Ok(())
408 }
409
410 fn add_partition(&mut self, info: gpt::PartitionInfo) -> Result<usize, gpt::AddPartitionError> {
411 let pending = self.pending_transaction.as_mut().unwrap();
412 let idx = self.gpt.add_partition(&mut pending.transaction, info)?;
413 pending.added_partitions.push(idx as u32);
414 Ok(idx)
415 }
416}
417
418fn offset_map_to_extents(
423 offset_map: &OffsetMap,
424 block_size: u32,
425) -> Result<(Vec<u8>, u64, u32, u64), Error> {
426 let mappings = offset_map.mappings();
427 let block_size = block_size as u64;
428 let base_device_offset =
429 mappings.iter().map(|m| m.target_block_offset * block_size).min().unwrap_or(0);
430 let mut running_logical = 0u64;
431 let mut extents = Vec::with_capacity(mappings.len());
432 let num_mappings = mappings.len();
433 for (i, m) in mappings.iter().enumerate() {
434 let is_last = i == num_mappings - 1;
435 let total_bytes = m.length * block_size;
436 let len_bytes = if is_last {
437 total_bytes - (total_bytes % mapping::BLOCK_SIZE)
440 } else {
441 ensure!(total_bytes % mapping::BLOCK_SIZE == 0, zx::Status::NOT_SUPPORTED);
444 total_bytes
445 };
446 if len_bytes == 0 {
447 continue;
448 }
449 let dev_offset_bytes = m.target_block_offset * block_size;
450 let extent = mapping::Extent::new(
451 running_logical..running_logical + len_bytes,
452 Some(dev_offset_bytes),
453 );
454 running_logical += len_bytes;
455 extents.push(extent);
456 }
457 let extents = mapping::Extents::try_new(extents, base_device_offset)?;
458 let mut payload_bytes = Vec::with_capacity(mappings.len() * std::mem::size_of::<u64>());
459 let mut extent_count = 0u32;
460 for w in mapping::Extents::encode_extents_with_base_offset(&extents) {
461 payload_bytes.extend_from_slice(&w.to_le_bytes());
462 extent_count += 1;
463 }
464 Ok((payload_bytes, running_logical, extent_count, base_device_offset))
465}
466
467struct MapperSessionState {
468 session_proxy: fblock::MapperSessionProxy,
469 sender: futures::lock::Mutex<vmo_fifo::AsyncSender<mapping::RawMappingCommand>>,
470}
471
472pub struct GptManager {
474 config: Config,
475 block_proxy: fblock::BlockProxy,
476 mapper_proxy: Option<fblock::MapperProxy>,
477 mapper_session: OnceLock<MapperSessionState>,
478 next_partition_key: AtomicU64,
479 block_size: u32,
480 block_count: u64,
481 inner: futures::lock::Mutex<Inner>,
482 shutdown: AtomicBool,
483}
484
485impl std::fmt::Debug for GptManager {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
487 f.debug_struct("GptManager")
488 .field("block_size", &self.block_size)
489 .field("block_count", &self.block_count)
490 .finish()
491 }
492}
493
494impl GptManager {
495 pub async fn new(
496 block_proxy: fblock::BlockProxy,
497 partitions_dir: Arc<vfs::directory::immutable::Simple>,
498 ) -> Result<Arc<Self>, Error> {
499 Self::new_with_config(block_proxy, partitions_dir, Config::default()).await
500 }
501
502 pub async fn new_with_mapper(
504 block_proxy: fblock::BlockProxy,
505 mapper_proxy: Option<fblock::MapperProxy>,
506 partitions_dir: Arc<vfs::directory::immutable::Simple>,
507 ) -> Result<Arc<Self>, Error> {
508 Self::new_with_config_and_mapper(
509 block_proxy,
510 mapper_proxy,
511 partitions_dir,
512 Config::default(),
513 )
514 .await
515 }
516
517 pub async fn new_with_config(
518 block_proxy: fblock::BlockProxy,
519 partitions_dir: Arc<vfs::directory::immutable::Simple>,
520 config: Config,
521 ) -> Result<Arc<Self>, Error> {
522 Self::new_with_config_and_mapper(block_proxy, None, partitions_dir, config).await
523 }
524
525 pub async fn new_with_config_and_mapper(
527 block_proxy: fblock::BlockProxy,
528 mapper_proxy: Option<fblock::MapperProxy>,
529 partitions_dir: Arc<vfs::directory::immutable::Simple>,
530 config: Config,
531 ) -> Result<Arc<Self>, Error> {
532 log::info!("Binding to GPT");
533 let client = Arc::new(RemoteBlockClient::new(block_proxy.clone()).await?);
534 let block_size = client.block_size();
535 let block_count = client.block_count();
536 let gpt = gpt::Gpt::open(client).await.context("Failed to load GPT")?;
537
538 let mapper_proxy = match mapper_proxy {
539 Some(proxy) => Some(proxy),
540 None => {
541 let (proxy, server) = fidl::endpoints::create_proxy::<fblock::MapperMarker>();
542 if block_proxy.connect_mapper(server).await.is_ok_and(|res| res.is_ok()) {
543 Some(proxy)
544 } else {
545 None
546 }
547 }
548 };
549
550 let this = Arc::new(Self {
551 config,
552 block_proxy,
553 mapper_proxy,
554 mapper_session: OnceLock::new(),
555 next_partition_key: AtomicU64::new(1),
556 block_size,
557 block_count,
558 inner: futures::lock::Mutex::new(Inner {
559 gpt,
560 partitions: BTreeMap::new(),
561 composite_partitions: BTreeMap::new(),
562 partitions_dir: PartitionsDirectory::new(partitions_dir),
563 pending_transaction: None,
564 }),
565 shutdown: AtomicBool::new(false),
566 });
567 this.inner.lock().await.bind_all_partitions(&this).await?;
568 log::info!("Starting all partitions OK!");
569 Ok(this)
570 }
571
572 async fn mapper_state(&self) -> &MapperSessionState {
579 let mapper_proxy = self.mapper_proxy.as_ref().unwrap();
580
581 let mut init_server = None;
582 let state = self.mapper_session.get_or_init(|| {
583 let (session_proxy, session_server) =
584 fidl::endpoints::create_proxy::<fblock::MapperSessionMarker>();
585 let parent_mapping_vmo = zx::Vmo::create(mapping::MAPPING_VMO_SIZE).unwrap();
586 let sender = vmo_fifo::AsyncSender::<mapping::RawMappingCommand>::new(
587 parent_mapping_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
588 1024,
589 mapping::PENDING_COMMANDS_CAPACITY,
590 )
591 .unwrap();
592 init_server = Some((session_server, parent_mapping_vmo));
593 MapperSessionState { session_proxy, sender: futures::lock::Mutex::new(sender) }
594 });
595 if let Some((session_server, parent_mapping_vmo)) = init_server {
600 match mapper_proxy.open_session(session_server, parent_mapping_vmo, None, None).await {
601 Ok(Err(status)) => {
602 log::warn!(
603 status:? = zx::Status::err_from_raw(status);
604 "Failed to open mapper session on parent device"
605 );
606 }
607 Err(error) => {
608 log::warn!(error:?; "FIDL error calling Mapper.OpenSession on parent device");
609 }
610 Ok(Ok(())) => {}
611 }
612 }
613 state
614 }
615
616 pub async fn mapper_session_proxy(&self) -> &fblock::MapperSessionProxy {
622 &self.mapper_state().await.session_proxy
623 }
624
625 pub fn next_partition_key(&self) -> u64 {
627 self.next_partition_key.fetch_add(1, Ordering::Relaxed)
628 }
629
630 pub async fn register_mappings(&self, key: u64, offset_map: &OffsetMap) -> Result<(), Error> {
636 let state = self.mapper_state().await;
637 let mut sender = state.sender.lock().await;
638
639 let (payload_bytes, stored_size, extent_count, device_offset) =
640 offset_map_to_extents(offset_map, self.block_size)?;
641 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).await?;
642 let cmd = mapping::RawMappingCommand {
643 opcode: mapping::MAPPINGS_COMMAND,
644 offset: payload_buf.offset(),
645 key,
646 stored_size,
647 device_offset,
648 metadata_count: 0,
649 extent_count,
650 };
651 payload_buf.data().copy_from_slice(&payload_bytes);
652 payload_buf.commit(cmd).await?;
653 Ok(())
654 }
655
656 pub fn has_mapper(&self) -> bool {
658 self.mapper_proxy.is_some()
659 }
660
661 pub fn block_size(&self) -> u32 {
662 self.block_size
663 }
664
665 pub fn block_count(&self) -> u64 {
666 self.block_count
667 }
668
669 pub async fn create_transaction(self: &Arc<Self>) -> Result<zx::EventPair, zx::Status> {
670 let mut inner = self.inner.lock().await;
671 if inner.pending_transaction.is_some() {
672 return Err(zx::Status::ALREADY_EXISTS);
673 }
674 let transaction = inner.gpt.create_transaction().unwrap();
675 let (client_end, server_end) = zx::EventPair::create();
676 let client_koid = client_end.koid()?;
677 let signal_waiter = fasync::OnSignals::new(server_end, zx::Signals::EVENTPAIR_PEER_CLOSED);
678 let this = self.clone();
679 let task = fasync::Task::spawn(async move {
680 let _ = signal_waiter.await;
681 let mut inner = this.inner.lock().await;
682 if inner.pending_transaction.as_ref().map_or(false, |t| t.client_koid == client_koid) {
683 inner.pending_transaction = None;
684 }
685 });
686 inner.pending_transaction = Some(PendingTransaction {
687 transaction,
688 client_koid,
689 added_partitions: vec![],
690 _signal_task: task,
691 });
692 Ok(client_end)
693 }
694
695 pub async fn commit_transaction(
696 self: &Arc<Self>,
697 transaction: zx::EventPair,
698 ) -> Result<(), zx::Status> {
699 let mut inner = self.inner.lock().await;
700 inner.ensure_transaction_matches(&transaction)?;
701 let pending = std::mem::take(&mut inner.pending_transaction).unwrap();
702 let partitions = pending.transaction.partitions.clone();
703 if let Err(error) = inner.gpt.commit_transaction(pending.transaction).await {
704 log::warn!(error:?; "Failed to commit transaction");
705 return Err(zx::Status::IO);
706 }
707 for (info, idx) in partitions
709 .iter()
710 .zip(0u32..)
711 .filter(|(info, idx)| !info.is_nil() && !pending.added_partitions.contains(idx))
712 {
713 if let Some(part) = inner.partitions.get(&idx) {
720 part.session_manager().interface().update_info(info.clone());
721 }
722 }
723 for idx in pending.added_partitions {
724 if let Some(gpt_info) = inner.gpt.partitions().get(&idx).cloned() {
725 let partition_info = block_server::PartitionInfo::from(&gpt_info);
726 if let Err(error) =
727 inner.bind_partition(self, idx, partition_info, OffsetMap::empty(), vec![])
728 {
729 log::error!(error:?; "Failed to bind partition");
730 }
731 }
732 }
733 Ok(())
734 }
735
736 pub async fn add_partition(
737 &self,
738 request: fpartitions::PartitionsManagerAddPartitionRequest,
739 ) -> Result<(), zx::Status> {
740 let mut inner = self.inner.lock().await;
741 inner.ensure_transaction_matches(
742 request.transaction.as_ref().ok_or(zx::Status::BAD_HANDLE)?,
743 )?;
744 let info = gpt::PartitionInfo {
745 label: request.name.ok_or(zx::Status::INVALID_ARGS)?,
746 type_guid: request
747 .type_guid
748 .map(|value| gpt::Guid::from_bytes(value.value))
749 .ok_or(zx::Status::INVALID_ARGS)?,
750 instance_guid: request
751 .instance_guid
752 .map(|value| gpt::Guid::from_bytes(value.value))
753 .unwrap_or_else(|| gpt::Guid::generate()),
754 start_block: 0,
755 num_blocks: request.num_blocks.ok_or(zx::Status::INVALID_ARGS)?,
756 flags: request.flags.unwrap_or_default(),
757 };
758 let idx = inner.add_partition(info)?;
759 let partition =
760 inner.pending_transaction.as_ref().unwrap().transaction.partitions.get(idx).unwrap();
761 log::info!(
762 "Allocated partition {:?} at {:?}",
763 partition.label,
764 partition.start_block..partition.start_block + partition.num_blocks
765 );
766 Ok(())
767 }
768
769 pub async fn handle_partitions_requests(
770 &self,
771 gpt_index: usize,
772 mut requests: fpartitions::PartitionRequestStream,
773 ) -> Result<(), zx::Status> {
774 while let Some(request) = requests.try_next().await.unwrap() {
775 match request {
776 fpartitions::PartitionRequest::UpdateMetadata { payload, responder } => {
777 responder
778 .send(
779 self.update_partition_metadata(gpt_index, payload)
780 .await
781 .map_err(|status| status.into_raw()),
782 )
783 .unwrap_or_else(
784 |error| log::error!(error:?; "Failed to send UpdateMetadata response"),
785 );
786 }
787 }
788 }
789 Ok(())
790 }
791
792 async fn update_partition_metadata(
793 &self,
794 gpt_index: usize,
795 request: fpartitions::PartitionUpdateMetadataRequest,
796 ) -> Result<(), zx::Status> {
797 let mut inner = self.inner.lock().await;
798 inner.ensure_transaction_matches(
799 request.transaction.as_ref().ok_or(zx::Status::BAD_HANDLE)?,
800 )?;
801
802 let transaction = &mut inner.pending_transaction.as_mut().unwrap().transaction;
803 let entry = transaction.partitions.get_mut(gpt_index).ok_or(zx::Status::BAD_STATE)?;
804 if let Some(type_guid) = request.type_guid.as_ref().cloned() {
805 entry.type_guid = gpt::Guid::from_bytes(type_guid.value);
806 }
807 if let Some(flags) = request.flags.as_ref() {
808 entry.flags = *flags;
809 }
810 Ok(())
811 }
812
813 pub async fn handle_composite_partitions_requests(
814 &self,
815 gpt_indexes: Vec<usize>,
816 mut requests: fpartitions::OverlayPartitionRequestStream,
817 ) -> Result<(), zx::Status> {
818 while let Some(request) = requests.try_next().await.unwrap() {
819 match request {
820 fpartitions::OverlayPartitionRequest::GetPartitions { responder } => {
821 match self.get_composite_partition_info(&gpt_indexes[..]).await {
822 Ok(partitions) => responder.send(Ok(&partitions[..])),
823 Err(status) => responder.send(Err(status.into_raw())),
824 }
825 .unwrap_or_else(
826 |error| log::error!(error:?; "Failed to send GetPartitions response"),
827 );
828 }
829 }
830 }
831 Ok(())
832 }
833
834 async fn get_composite_partition_info(
835 &self,
836 gpt_indexes: &[usize],
837 ) -> Result<Vec<fpartitions::PartitionInfo>, zx::Status> {
838 fn convert_partition_info(info: &gpt::PartitionInfo) -> fpartitions::PartitionInfo {
839 fpartitions::PartitionInfo {
840 name: Some(info.label.to_string()),
841 type_guid: Some(fblock::Guid { value: info.type_guid.to_bytes() }),
842 instance_guid: Some(fblock::Guid { value: info.instance_guid.to_bytes() }),
843 start_block_offset: Some(info.start_block),
844 num_blocks: Some(info.num_blocks),
845 flags: Some(info.flags),
846 ..Default::default()
847 }
848 }
849
850 let inner = self.inner.lock().await;
851 let mut partitions = vec![];
852 for index in gpt_indexes {
853 let index: u32 = *index as u32;
854 partitions.push(
855 inner
856 .gpt
857 .partitions()
858 .get(&index)
859 .map(convert_partition_info)
860 .ok_or(zx::Status::BAD_STATE)?,
861 );
862 }
863 Ok(partitions)
864 }
865
866 pub async fn reset_partition_table(
867 self: &Arc<Self>,
868 partitions: Vec<gpt::PartitionInfo>,
869 ) -> Result<(), zx::Status> {
870 let mut inner = self.inner.lock().await;
871 if inner.pending_transaction.is_some() {
872 return Err(zx::Status::BAD_STATE);
873 }
874
875 inner.partitions.clear();
878 inner.composite_partitions.clear();
879 inner.partitions_dir.clear().await;
880
881 let client =
886 Arc::new(RemoteBlockClient::new(self.block_proxy.clone()).await.map_err(|e| {
887 log::error!(e:?; "Failed to re-establish block client");
888 zx::Status::IO
889 })?);
890 inner.gpt.set_client(client);
891
892 log::info!("Resetting gpt. Expect data loss!!!");
893 let mut transaction = inner.gpt.create_transaction().unwrap();
894 transaction.partitions = partitions;
895 inner.gpt.commit_transaction(transaction).await?;
896
897 if let Err(error) = inner.bind_all_partitions(&self).await {
898 log::error!(error:?; "Failed to rebind partitions");
899 return Err(zx::Status::BAD_STATE);
900 }
901 log::info!("Rebinding partitions OK!");
902 Ok(())
903 }
904
905 pub async fn shutdown(self: Arc<Self>) {
906 log::info!("Shutting down gpt");
907 let mut inner = self.inner.lock().await;
908 inner.partitions_dir.clear().await;
909 inner.partitions.clear();
910 inner.composite_partitions.clear();
911 if let Some(state) = self.mapper_session.get() {
912 let _ = state.session_proxy.close().await;
913 }
914 self.shutdown.store(true, Ordering::Relaxed);
915 log::info!("Shutting down gpt OK");
916 }
917}
918
919impl Drop for GptManager {
920 fn drop(&mut self) {
921 assert!(self.shutdown.load(Ordering::Relaxed), "Did you forget to shutdown?");
922 }
923}
924
925#[cfg(test)]
926mod tests {
927 use super::GptManager;
928 use block_client::{
929 BlockClient as _, BlockDeviceFlag, BufferSlice, MutableBufferSlice, RemoteBlockClient,
930 WriteFlags,
931 };
932 use block_server::{BlockInfo, DeviceInfo, OffsetMap, WriteOptions};
933 use fidl_fuchsia_io as fio;
934 use fidl_fuchsia_storage_block as fblock;
935 use fidl_fuchsia_storage_partitions as fpartitions;
936 use fuchsia_async as fasync;
937 use fuchsia_component::client::connect_to_named_protocol_at_dir_root;
938 use futures::StreamExt as _;
939 use gpt::{Gpt, Guid, PartitionInfo};
940 use std::num::NonZero;
941 use std::sync::Arc;
942 use std::sync::atomic::{AtomicBool, Ordering};
943 use test_vmo_backed_block_server::{
944 InitialContents, Observer, VmoBackedServer, VmoBackedServerOptions, WriteAction,
945 };
946
947 async fn setup(
948 block_size: u32,
949 block_count: u64,
950 partitions: Vec<PartitionInfo>,
951 ) -> (Arc<VmoBackedServer>, Arc<vfs::directory::immutable::Simple>) {
952 setup_with_options(
953 VmoBackedServerOptions {
954 initial_contents: InitialContents::FromCapacity(block_count),
955 block_size,
956 ..Default::default()
957 },
958 partitions,
959 )
960 .await
961 }
962
963 async fn setup_with_options(
964 opts: VmoBackedServerOptions<'_>,
965 partitions: Vec<PartitionInfo>,
966 ) -> (Arc<VmoBackedServer>, Arc<vfs::directory::immutable::Simple>) {
967 let server = Arc::new(opts.build().unwrap());
968 {
969 let (block_client, block_server) =
970 fidl::endpoints::create_proxy::<fblock::BlockMarker>();
971 let volume_stream = fidl::endpoints::ServerEnd::<fblock::BlockMarker>::from(
972 block_server.into_channel(),
973 )
974 .into_stream();
975 let server_clone = server.clone();
976 let _task = fasync::Task::spawn(async move { server_clone.serve(volume_stream).await });
977 let client = Arc::new(RemoteBlockClient::new(block_client).await.unwrap());
978 Gpt::format(client, partitions).await.unwrap();
979 }
980 (server, vfs::directory::immutable::simple())
981 }
982
983 #[fuchsia::test]
984 async fn load_unformatted_gpt() {
985 let server =
986 Arc::new(VmoBackedServer::new(8, 512, &[]).expect("Failed to create VmoBackedServer"));
987
988 GptManager::new(server.connect(), vfs::directory::immutable::simple())
989 .await
990 .expect_err("load should fail");
991 }
992
993 #[fuchsia::test]
994 async fn load_formatted_empty_gpt() {
995 let (block_device, partitions_dir) = setup(512, 8, vec![]).await;
996
997 let runner = GptManager::new(block_device.connect(), partitions_dir)
998 .await
999 .expect("load should succeed");
1000 runner.shutdown().await;
1001 }
1002
1003 #[fuchsia::test]
1004 async fn load_formatted_gpt_with_one_partition() {
1005 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1006 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1007 const PART_NAME: &str = "part";
1008
1009 let (block_device, partitions_dir) = setup(
1010 512,
1011 8,
1012 vec![PartitionInfo {
1013 label: PART_NAME.to_string(),
1014 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1015 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
1016 start_block: 4,
1017 num_blocks: 1,
1018 flags: 0,
1019 }],
1020 )
1021 .await;
1022
1023 let partitions_dir_clone = partitions_dir.clone();
1024 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
1025 .await
1026 .expect("load should succeed");
1027 partitions_dir.get_entry("part-000").expect("No entry found");
1028 partitions_dir.get_entry("part-001").map(|_| ()).expect_err("Extra entry found");
1029 runner.shutdown().await;
1030 }
1031
1032 #[fuchsia::test]
1033 async fn load_formatted_gpt_with_two_partitions() {
1034 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1035 const PART_INSTANCE_1_GUID: [u8; 16] = [2u8; 16];
1036 const PART_INSTANCE_2_GUID: [u8; 16] = [3u8; 16];
1037 const PART_1_NAME: &str = "part1";
1038 const PART_2_NAME: &str = "part2";
1039
1040 let (block_device, partitions_dir) = setup(
1041 512,
1042 8,
1043 vec![
1044 PartitionInfo {
1045 label: PART_1_NAME.to_string(),
1046 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1047 instance_guid: Guid::from_bytes(PART_INSTANCE_1_GUID),
1048 start_block: 4,
1049 num_blocks: 1,
1050 flags: 0,
1051 },
1052 PartitionInfo {
1053 label: PART_2_NAME.to_string(),
1054 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1055 instance_guid: Guid::from_bytes(PART_INSTANCE_2_GUID),
1056 start_block: 5,
1057 num_blocks: 1,
1058 flags: 0,
1059 },
1060 ],
1061 )
1062 .await;
1063
1064 let partitions_dir_clone = partitions_dir.clone();
1065 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
1066 .await
1067 .expect("load should succeed");
1068 partitions_dir.get_entry("part-000").expect("No entry found");
1069 partitions_dir.get_entry("part-001").expect("No entry found");
1070 partitions_dir.get_entry("part-002").map(|_| ()).expect_err("Extra entry found");
1071 runner.shutdown().await;
1072 }
1073
1074 #[fuchsia::test]
1075 async fn partition_io() {
1076 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1077 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1078 const PART_NAME: &str = "part";
1079
1080 let (block_device, partitions_dir) = setup(
1081 512,
1082 8,
1083 vec![PartitionInfo {
1084 label: PART_NAME.to_string(),
1085 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1086 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
1087 start_block: 4,
1088 num_blocks: 2,
1089 flags: 0,
1090 }],
1091 )
1092 .await;
1093
1094 let partitions_dir_clone = partitions_dir.clone();
1095 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
1096 .await
1097 .expect("load should succeed");
1098
1099 let proxy = vfs::serve_directory(
1100 partitions_dir.clone(),
1101 vfs::path::Path::validate_and_split("part-000").unwrap(),
1102 vfs::execution_scope::ExecutionScope::new(),
1103 fio::PERM_READABLE,
1104 );
1105 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1106 .expect("Failed to open block service");
1107 let client = RemoteBlockClient::new(block).await.expect("Failed to create block client");
1108
1109 assert_eq!(client.block_count(), 2);
1110 assert_eq!(client.block_size(), 512);
1111
1112 let buf = vec![0xabu8; 512];
1113 client.write_at(BufferSlice::Memory(&buf[..]), 0).await.expect("write_at failed");
1114 client
1115 .write_at(BufferSlice::Memory(&buf[..]), 1024)
1116 .await
1117 .expect_err("write_at should fail when writing past partition end");
1118 let mut buf2 = vec![0u8; 512];
1119 client.read_at(MutableBufferSlice::Memory(&mut buf2[..]), 0).await.expect("read_at failed");
1120 assert_eq!(buf, buf2);
1121 client
1122 .read_at(MutableBufferSlice::Memory(&mut buf2[..]), 1024)
1123 .await
1124 .expect_err("read_at should fail when reading past partition end");
1125 client.trim(512..1024).await.expect("trim failed");
1126 client.trim(1..512).await.expect_err("trim with invalid range should fail");
1127 client.trim(1024..1536).await.expect_err("trim past end of partition should fail");
1128 runner.shutdown().await;
1129
1130 let mut buf = vec![0u8; 512];
1132 let client =
1133 RemoteBlockClient::new(block_device.connect::<fblock::BlockProxy>()).await.unwrap();
1134 client.read_at(MutableBufferSlice::Memory(&mut buf[..]), 2048).await.unwrap();
1135 assert_eq!(&buf[..], &[0xabu8; 512]);
1136 }
1137
1138 #[fuchsia::test]
1139 async fn load_formatted_gpt_with_invalid_primary_header() {
1140 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1141 const PART_INSTANCE_1_GUID: [u8; 16] = [2u8; 16];
1142 const PART_INSTANCE_2_GUID: [u8; 16] = [3u8; 16];
1143 const PART_1_NAME: &str = "part1";
1144 const PART_2_NAME: &str = "part2";
1145
1146 let (block_device, partitions_dir) = setup(
1147 512,
1148 8,
1149 vec![
1150 PartitionInfo {
1151 label: PART_1_NAME.to_string(),
1152 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1153 instance_guid: Guid::from_bytes(PART_INSTANCE_1_GUID),
1154 start_block: 4,
1155 num_blocks: 1,
1156 flags: 0,
1157 },
1158 PartitionInfo {
1159 label: PART_2_NAME.to_string(),
1160 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1161 instance_guid: Guid::from_bytes(PART_INSTANCE_2_GUID),
1162 start_block: 5,
1163 num_blocks: 1,
1164 flags: 0,
1165 },
1166 ],
1167 )
1168 .await;
1169 {
1170 let (client, stream) =
1171 fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1172 let server = block_device.clone();
1173 let _task = fasync::Task::spawn(async move { server.serve(stream).await });
1174 let client = RemoteBlockClient::new(client).await.unwrap();
1175 client.write_at(BufferSlice::Memory(&[0xffu8; 512]), 512).await.unwrap();
1176 }
1177
1178 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1179 .await
1180 .expect("load should succeed");
1181 partitions_dir.get_entry("part-000").expect("No entry found");
1182 partitions_dir.get_entry("part-001").expect("No entry found");
1183 runner.shutdown().await;
1184 }
1185
1186 #[fuchsia::test]
1187 async fn load_formatted_gpt_with_invalid_primary_partition_table() {
1188 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1189 const PART_INSTANCE_1_GUID: [u8; 16] = [2u8; 16];
1190 const PART_INSTANCE_2_GUID: [u8; 16] = [3u8; 16];
1191 const PART_1_NAME: &str = "part1";
1192 const PART_2_NAME: &str = "part2";
1193
1194 let (block_device, partitions_dir) = setup(
1195 512,
1196 8,
1197 vec![
1198 PartitionInfo {
1199 label: PART_1_NAME.to_string(),
1200 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1201 instance_guid: Guid::from_bytes(PART_INSTANCE_1_GUID),
1202 start_block: 4,
1203 num_blocks: 1,
1204 flags: 0,
1205 },
1206 PartitionInfo {
1207 label: PART_2_NAME.to_string(),
1208 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1209 instance_guid: Guid::from_bytes(PART_INSTANCE_2_GUID),
1210 start_block: 5,
1211 num_blocks: 1,
1212 flags: 0,
1213 },
1214 ],
1215 )
1216 .await;
1217 {
1218 let (client, stream) =
1219 fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1220 let server = block_device.clone();
1221 let _task = fasync::Task::spawn(async move { server.serve(stream).await });
1222 let client = RemoteBlockClient::new(client).await.unwrap();
1223 client.write_at(BufferSlice::Memory(&[0xffu8; 512]), 1024).await.unwrap();
1224 }
1225
1226 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1227 .await
1228 .expect("load should succeed");
1229 partitions_dir.get_entry("part-000").expect("No entry found");
1230 partitions_dir.get_entry("part-001").expect("No entry found");
1231 runner.shutdown().await;
1232 }
1233
1234 #[fuchsia::test]
1235 async fn force_access_passed_through() {
1236 const BLOCK_SIZE: u32 = 512;
1237 const BLOCK_COUNT: u64 = 1024;
1238
1239 struct ForceAccessObserver(Arc<AtomicBool>);
1240
1241 impl Observer for ForceAccessObserver {
1242 fn write(
1243 &self,
1244 _device_block_offset: u64,
1245 _block_count: u32,
1246 _vmo: &Arc<zx::Vmo>,
1247 _vmo_offset: u64,
1248 opts: WriteOptions,
1249 ) -> WriteAction {
1250 assert_eq!(
1251 opts.flags.contains(WriteFlags::FORCE_ACCESS),
1252 self.0.load(Ordering::Relaxed)
1253 );
1254 WriteAction::Write
1255 }
1256 }
1257
1258 let expect_force_access = Arc::new(AtomicBool::new(false));
1259 let (server, partitions_dir) = setup_with_options(
1260 VmoBackedServerOptions {
1261 initial_contents: InitialContents::FromCapacity(BLOCK_COUNT),
1262 block_size: BLOCK_SIZE,
1263 observer: Some(Box::new(ForceAccessObserver(expect_force_access.clone()))),
1264 info: DeviceInfo::Block(BlockInfo {
1265 device_flags: fblock::DeviceFlag::FUA_SUPPORT,
1266 ..Default::default()
1267 }),
1268 ..Default::default()
1269 },
1270 vec![PartitionInfo {
1271 label: "foo".to_string(),
1272 type_guid: Guid::from_bytes([1; 16]),
1273 instance_guid: Guid::from_bytes([2; 16]),
1274 start_block: 4,
1275 num_blocks: 1,
1276 flags: 0,
1277 }],
1278 )
1279 .await;
1280
1281 let manager = GptManager::new(server.connect(), partitions_dir.clone()).await.unwrap();
1282
1283 let proxy = vfs::serve_directory(
1284 partitions_dir.clone(),
1285 vfs::path::Path::validate_and_split("part-000").unwrap(),
1286 vfs::execution_scope::ExecutionScope::new(),
1287 fio::PERM_READABLE,
1288 );
1289 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1290 .expect("Failed to open block service");
1291 let client = RemoteBlockClient::new(block).await.expect("Failed to create block client");
1292
1293 let buffer = vec![0; BLOCK_SIZE as usize];
1294 client.write_at(BufferSlice::Memory(&buffer), 0).await.unwrap();
1295
1296 expect_force_access.store(true, Ordering::Relaxed);
1297
1298 client
1299 .write_at_with_opts(
1300 BufferSlice::Memory(&buffer),
1301 0,
1302 WriteOptions { flags: WriteFlags::FORCE_ACCESS, ..Default::default() },
1303 )
1304 .await
1305 .unwrap();
1306
1307 manager.shutdown().await;
1308 }
1309
1310 #[fuchsia::test]
1311 async fn barrier_passed_through() {
1312 const BLOCK_SIZE: u32 = 512;
1313 const BLOCK_COUNT: u64 = 1024;
1314
1315 struct BarrierObserver(Arc<AtomicBool>);
1316
1317 impl Observer for BarrierObserver {
1318 fn write(
1319 &self,
1320 _device_block_offset: u64,
1321 _block_count: u32,
1322 _vmo: &Arc<zx::Vmo>,
1323 _vmo_offset: u64,
1324 opts: WriteOptions,
1325 ) -> WriteAction {
1326 assert_eq!(
1327 opts.flags.contains(WriteFlags::PRE_BARRIER),
1328 self.0.load(Ordering::Relaxed)
1329 );
1330 WriteAction::Write
1331 }
1332 }
1333
1334 let expect_barrier = Arc::new(AtomicBool::new(false));
1335 let (server, partitions_dir) = setup_with_options(
1336 VmoBackedServerOptions {
1337 initial_contents: InitialContents::FromCapacity(BLOCK_COUNT),
1338 block_size: BLOCK_SIZE,
1339 observer: Some(Box::new(BarrierObserver(expect_barrier.clone()))),
1340 info: DeviceInfo::Block(BlockInfo {
1341 device_flags: fblock::DeviceFlag::BARRIER_SUPPORT,
1342 ..Default::default()
1343 }),
1344 ..Default::default()
1345 },
1346 vec![PartitionInfo {
1347 label: "foo".to_string(),
1348 type_guid: Guid::from_bytes([1; 16]),
1349 instance_guid: Guid::from_bytes([2; 16]),
1350 start_block: 4,
1351 num_blocks: 1,
1352 flags: 0,
1353 }],
1354 )
1355 .await;
1356
1357 let manager = GptManager::new(server.connect(), partitions_dir.clone()).await.unwrap();
1358
1359 let proxy = vfs::serve_directory(
1360 partitions_dir.clone(),
1361 vfs::path::Path::validate_and_split("part-000").unwrap(),
1362 vfs::execution_scope::ExecutionScope::new(),
1363 fio::PERM_READABLE,
1364 );
1365 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1366 .expect("Failed to open block service");
1367 let client = RemoteBlockClient::new(block).await.expect("Failed to create block client");
1368
1369 let buffer = vec![0; BLOCK_SIZE as usize];
1370 client.write_at(BufferSlice::Memory(&buffer), 0).await.unwrap();
1371
1372 expect_barrier.store(true, Ordering::Relaxed);
1373 client
1374 .write_at_with_opts(
1375 BufferSlice::Memory(&buffer),
1376 0,
1377 WriteOptions { flags: WriteFlags::PRE_BARRIER, ..Default::default() },
1378 )
1379 .await
1380 .unwrap();
1381
1382 manager.shutdown().await;
1383 }
1384
1385 #[fuchsia::test]
1386 async fn commit_transaction() {
1387 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1388 const PART_1_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1389 const PART_1_NAME: &str = "part";
1390 const PART_2_INSTANCE_GUID: [u8; 16] = [3u8; 16];
1391 const PART_2_NAME: &str = "part2";
1392
1393 let (block_device, partitions_dir) = setup(
1394 512,
1395 16,
1396 vec![
1397 PartitionInfo {
1398 label: PART_1_NAME.to_string(),
1399 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1400 instance_guid: Guid::from_bytes(PART_1_INSTANCE_GUID),
1401 start_block: 4,
1402 num_blocks: 1,
1403 flags: 0,
1404 },
1405 PartitionInfo {
1406 label: PART_2_NAME.to_string(),
1407 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1408 instance_guid: Guid::from_bytes(PART_2_INSTANCE_GUID),
1409 start_block: 5,
1410 num_blocks: 1,
1411 flags: 0,
1412 },
1413 ],
1414 )
1415 .await;
1416 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1417 .await
1418 .expect("load should succeed");
1419
1420 let part_0_dir = vfs::serve_directory(
1421 partitions_dir.clone(),
1422 vfs::Path::validate_and_split("part-000").unwrap(),
1423 vfs::execution_scope::ExecutionScope::new(),
1424 fio::PERM_READABLE,
1425 );
1426 let part_1_dir = vfs::serve_directory(
1427 partitions_dir.clone(),
1428 vfs::Path::validate_and_split("part-001").unwrap(),
1429 vfs::execution_scope::ExecutionScope::new(),
1430 fio::PERM_READABLE,
1431 );
1432 let part_0_proxy = connect_to_named_protocol_at_dir_root::<fpartitions::PartitionMarker>(
1433 &part_0_dir,
1434 "partition",
1435 )
1436 .expect("Failed to open Partition service");
1437 let part_1_proxy = connect_to_named_protocol_at_dir_root::<fpartitions::PartitionMarker>(
1438 &part_1_dir,
1439 "partition",
1440 )
1441 .expect("Failed to open Partition service");
1442
1443 let transaction = runner.create_transaction().await.expect("Failed to create transaction");
1444 part_0_proxy
1445 .update_metadata(fpartitions::PartitionUpdateMetadataRequest {
1446 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1447 type_guid: Some(fblock::Guid { value: [0xffu8; 16] }),
1448 ..Default::default()
1449 })
1450 .await
1451 .expect("FIDL error")
1452 .expect("Failed to update_metadata");
1453 part_1_proxy
1454 .update_metadata(fpartitions::PartitionUpdateMetadataRequest {
1455 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1456 flags: Some(1234),
1457 ..Default::default()
1458 })
1459 .await
1460 .expect("FIDL error")
1461 .expect("Failed to update_metadata");
1462 runner.commit_transaction(transaction).await.expect("Failed to commit transaction");
1463
1464 let part_0_block =
1466 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_0_dir, "volume")
1467 .expect("Failed to open Volume service");
1468 let (status, guid) = part_0_block.get_type_guid().await.expect("FIDL error");
1469 assert_eq!(status, zx::sys::ZX_OK);
1470 assert_eq!(guid.unwrap().value, [0xffu8; 16]);
1471 let part_1_block =
1472 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_1_dir, "volume")
1473 .expect("Failed to open Volume service");
1474 let metadata =
1475 part_1_block.get_metadata().await.expect("FIDL error").expect("get_metadata failed");
1476 assert_eq!(metadata.type_guid.unwrap().value, PART_TYPE_GUID);
1477 assert_eq!(metadata.flags, Some(1234));
1478
1479 runner.shutdown().await;
1480 }
1481
1482 #[fuchsia::test]
1483 async fn commit_transaction_with_io_error() {
1484 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1485 const PART_1_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1486 const PART_1_NAME: &str = "part";
1487 const PART_2_INSTANCE_GUID: [u8; 16] = [3u8; 16];
1488 const PART_2_NAME: &str = "part2";
1489
1490 #[derive(Clone)]
1491 struct TransactionObserver(Arc<AtomicBool>);
1492 impl Observer for TransactionObserver {
1493 fn write(
1494 &self,
1495 _device_block_offset: u64,
1496 _block_count: u32,
1497 _vmo: &Arc<zx::Vmo>,
1498 _vmo_offset: u64,
1499 _opts: WriteOptions,
1500 ) -> WriteAction {
1501 if self.0.load(Ordering::Relaxed) { WriteAction::Fail } else { WriteAction::Write }
1502 }
1503 }
1504 let observer = TransactionObserver(Arc::new(AtomicBool::new(false)));
1505 let (block_device, partitions_dir) = setup_with_options(
1506 VmoBackedServerOptions {
1507 initial_contents: InitialContents::FromCapacity(16),
1508 block_size: 512,
1509 observer: Some(Box::new(observer.clone())),
1510 ..Default::default()
1511 },
1512 vec![
1513 PartitionInfo {
1514 label: PART_1_NAME.to_string(),
1515 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1516 instance_guid: Guid::from_bytes(PART_1_INSTANCE_GUID),
1517 start_block: 4,
1518 num_blocks: 1,
1519 flags: 0,
1520 },
1521 PartitionInfo {
1522 label: PART_2_NAME.to_string(),
1523 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1524 instance_guid: Guid::from_bytes(PART_2_INSTANCE_GUID),
1525 start_block: 5,
1526 num_blocks: 1,
1527 flags: 0,
1528 },
1529 ],
1530 )
1531 .await;
1532 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1533 .await
1534 .expect("load should succeed");
1535
1536 let part_0_dir = vfs::serve_directory(
1537 partitions_dir.clone(),
1538 vfs::Path::validate_and_split("part-000").unwrap(),
1539 vfs::execution_scope::ExecutionScope::new(),
1540 fio::PERM_READABLE,
1541 );
1542 let part_1_dir = vfs::serve_directory(
1543 partitions_dir.clone(),
1544 vfs::Path::validate_and_split("part-001").unwrap(),
1545 vfs::execution_scope::ExecutionScope::new(),
1546 fio::PERM_READABLE,
1547 );
1548 let part_0_proxy = connect_to_named_protocol_at_dir_root::<fpartitions::PartitionMarker>(
1549 &part_0_dir,
1550 "partition",
1551 )
1552 .expect("Failed to open Partition service");
1553 let part_1_proxy = connect_to_named_protocol_at_dir_root::<fpartitions::PartitionMarker>(
1554 &part_1_dir,
1555 "partition",
1556 )
1557 .expect("Failed to open Partition service");
1558
1559 let transaction = runner.create_transaction().await.expect("Failed to create transaction");
1560 part_0_proxy
1561 .update_metadata(fpartitions::PartitionUpdateMetadataRequest {
1562 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1563 type_guid: Some(fblock::Guid { value: [0xffu8; 16] }),
1564 ..Default::default()
1565 })
1566 .await
1567 .expect("FIDL error")
1568 .expect("Failed to update_metadata");
1569 part_1_proxy
1570 .update_metadata(fpartitions::PartitionUpdateMetadataRequest {
1571 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1572 flags: Some(1234),
1573 ..Default::default()
1574 })
1575 .await
1576 .expect("FIDL error")
1577 .expect("Failed to update_metadata");
1578
1579 observer.0.store(true, Ordering::Relaxed); runner.commit_transaction(transaction).await.expect_err("Commit transaction should fail");
1581
1582 let part_0_block =
1584 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_0_dir, "volume")
1585 .expect("Failed to open Volume service");
1586 let (status, guid) = part_0_block.get_type_guid().await.expect("FIDL error");
1587 assert_eq!(status, zx::sys::ZX_OK);
1588 assert_eq!(guid.unwrap().value, [2u8; 16]);
1589 let part_1_block =
1590 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_1_dir, "volume")
1591 .expect("Failed to open Volume service");
1592 let metadata =
1593 part_1_block.get_metadata().await.expect("FIDL error").expect("get_metadata failed");
1594 assert_eq!(metadata.type_guid.unwrap().value, PART_TYPE_GUID);
1595 assert_eq!(metadata.flags, Some(0));
1596
1597 runner.shutdown().await;
1598 }
1599
1600 #[fuchsia::test]
1601 async fn reset_partition_tables() {
1602 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1605 const PART_1_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1606 const PART_1_NAME: &str = "part";
1607 const PART_2_INSTANCE_GUID: [u8; 16] = [3u8; 16];
1608 const PART_2_NAME: &str = "part2";
1609 const PART_3_NAME: &str = "part3";
1610 const PART_4_NAME: &str = "part4";
1611
1612 let (block_device, partitions_dir) = setup(
1613 512,
1614 1048576 / 512,
1615 vec![
1616 PartitionInfo {
1617 label: PART_1_NAME.to_string(),
1618 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1619 instance_guid: Guid::from_bytes(PART_1_INSTANCE_GUID),
1620 start_block: 4,
1621 num_blocks: 1,
1622 flags: 0,
1623 },
1624 PartitionInfo {
1625 label: PART_2_NAME.to_string(),
1626 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1627 instance_guid: Guid::from_bytes(PART_2_INSTANCE_GUID),
1628 start_block: 5,
1629 num_blocks: 1,
1630 flags: 0,
1631 },
1632 ],
1633 )
1634 .await;
1635 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1636 .await
1637 .expect("load should succeed");
1638 let nil_entry = PartitionInfo {
1639 label: "".to_string(),
1640 type_guid: Guid::from_bytes([0u8; 16]),
1641 instance_guid: Guid::from_bytes([0u8; 16]),
1642 start_block: 0,
1643 num_blocks: 0,
1644 flags: 0,
1645 };
1646 let mut new_partitions = vec![nil_entry; 128];
1647 new_partitions[0] = PartitionInfo {
1648 label: PART_3_NAME.to_string(),
1649 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1650 instance_guid: Guid::from_bytes([1u8; 16]),
1651 start_block: 64,
1652 num_blocks: 2,
1653 flags: 0,
1654 };
1655 new_partitions[2] = PartitionInfo {
1656 label: PART_4_NAME.to_string(),
1657 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1658 instance_guid: Guid::from_bytes([2u8; 16]),
1659 start_block: 66,
1660 num_blocks: 4,
1661 flags: 0,
1662 };
1663 runner.reset_partition_table(new_partitions).await.expect("reset_partition_table failed");
1664 partitions_dir.get_entry("part-000").expect("No entry found");
1665 partitions_dir.get_entry("part-001").map(|_| ()).expect_err("Extra entry found");
1666 partitions_dir.get_entry("part-002").expect("No entry found");
1667
1668 let proxy = vfs::serve_directory(
1669 partitions_dir.clone(),
1670 vfs::path::Path::validate_and_split("part-000").unwrap(),
1671 vfs::execution_scope::ExecutionScope::new(),
1672 fio::PERM_READABLE,
1673 );
1674 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1675 .expect("Failed to open block service");
1676 let (status, name) = block.get_name().await.expect("FIDL error");
1677 assert_eq!(status, zx::sys::ZX_OK);
1678 assert_eq!(name.unwrap(), PART_3_NAME);
1679
1680 runner.shutdown().await;
1681 }
1682
1683 #[fuchsia::test]
1684 async fn reset_partition_tables_fails_if_too_many_partitions() {
1685 let (block_device, partitions_dir) = setup(512, 8, vec![]).await;
1686 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1687 .await
1688 .expect("load should succeed");
1689 let nil_entry = PartitionInfo {
1690 label: "".to_string(),
1691 type_guid: Guid::from_bytes([0u8; 16]),
1692 instance_guid: Guid::from_bytes([0u8; 16]),
1693 start_block: 0,
1694 num_blocks: 0,
1695 flags: 0,
1696 };
1697 let new_partitions = vec![nil_entry; 128];
1698 runner
1699 .reset_partition_table(new_partitions)
1700 .await
1701 .expect_err("reset_partition_table should fail");
1702
1703 runner.shutdown().await;
1704 }
1705
1706 #[fuchsia::test]
1707 async fn reset_partition_tables_fails_if_too_large_partitions() {
1708 let (block_device, partitions_dir) = setup(512, 64, vec![]).await;
1709 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1710 .await
1711 .expect("load should succeed");
1712 let new_partitions = vec![
1713 PartitionInfo {
1714 label: "a".to_string(),
1715 type_guid: Guid::from_bytes([1u8; 16]),
1716 instance_guid: Guid::from_bytes([1u8; 16]),
1717 start_block: 4,
1718 num_blocks: 2,
1719 flags: 0,
1720 },
1721 PartitionInfo {
1722 label: "b".to_string(),
1723 type_guid: Guid::from_bytes([2u8; 16]),
1724 instance_guid: Guid::from_bytes([2u8; 16]),
1725 start_block: 6,
1726 num_blocks: 200,
1727 flags: 0,
1728 },
1729 ];
1730 runner
1731 .reset_partition_table(new_partitions)
1732 .await
1733 .expect_err("reset_partition_table should fail");
1734
1735 runner.shutdown().await;
1736 }
1737
1738 #[fuchsia::test]
1739 async fn reset_partition_tables_fails_if_partition_overlaps_metadata() {
1740 let (block_device, partitions_dir) = setup(512, 64, vec![]).await;
1741 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1742 .await
1743 .expect("load should succeed");
1744 let new_partitions = vec![PartitionInfo {
1745 label: "a".to_string(),
1746 type_guid: Guid::from_bytes([1u8; 16]),
1747 instance_guid: Guid::from_bytes([1u8; 16]),
1748 start_block: 1,
1749 num_blocks: 2,
1750 flags: 0,
1751 }];
1752 runner
1753 .reset_partition_table(new_partitions)
1754 .await
1755 .expect_err("reset_partition_table should fail");
1756
1757 runner.shutdown().await;
1758 }
1759
1760 #[fuchsia::test]
1761 async fn reset_partition_tables_fails_if_partitions_overlap() {
1762 let (block_device, partitions_dir) = setup(512, 64, vec![]).await;
1763 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1764 .await
1765 .expect("load should succeed");
1766 let new_partitions = vec![
1767 PartitionInfo {
1768 label: "a".to_string(),
1769 type_guid: Guid::from_bytes([1u8; 16]),
1770 instance_guid: Guid::from_bytes([1u8; 16]),
1771 start_block: 32,
1772 num_blocks: 2,
1773 flags: 0,
1774 },
1775 PartitionInfo {
1776 label: "b".to_string(),
1777 type_guid: Guid::from_bytes([2u8; 16]),
1778 instance_guid: Guid::from_bytes([2u8; 16]),
1779 start_block: 33,
1780 num_blocks: 1,
1781 flags: 0,
1782 },
1783 ];
1784 runner
1785 .reset_partition_table(new_partitions)
1786 .await
1787 .expect_err("reset_partition_table should fail");
1788
1789 runner.shutdown().await;
1790 }
1791
1792 #[fuchsia::test]
1793 async fn add_partition() {
1794 let (block_device, partitions_dir) = setup(512, 64, vec![PartitionInfo::nil(); 64]).await;
1795 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1796 .await
1797 .expect("load should succeed");
1798
1799 let transaction = runner.create_transaction().await.expect("Create transaction failed");
1800 let request = fpartitions::PartitionsManagerAddPartitionRequest {
1801 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1802 name: Some("a".to_string()),
1803 type_guid: Some(fblock::Guid { value: [1u8; 16] }),
1804 num_blocks: Some(2),
1805 ..Default::default()
1806 };
1807 runner.add_partition(request).await.expect("add_partition failed");
1808 runner.commit_transaction(transaction).await.expect("add_partition failed");
1809
1810 let proxy = vfs::serve_directory(
1811 partitions_dir.clone(),
1812 vfs::path::Path::validate_and_split("part-000").unwrap(),
1813 vfs::execution_scope::ExecutionScope::new(),
1814 fio::PERM_READABLE,
1815 );
1816 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1817 .expect("Failed to open block service");
1818 let client: RemoteBlockClient =
1819 RemoteBlockClient::new(block).await.expect("Failed to create block client");
1820
1821 assert_eq!(client.block_count(), 2);
1822 assert_eq!(client.block_size(), 512);
1823
1824 runner.shutdown().await;
1825 }
1826
1827 #[fuchsia::test]
1828 async fn partition_info() {
1829 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1830 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1831 const PART_NAME: &str = "part";
1832
1833 let (block_device, partitions_dir) = setup_with_options(
1834 VmoBackedServerOptions {
1835 initial_contents: InitialContents::FromCapacity(16),
1836 block_size: 512,
1837 info: DeviceInfo::Block(BlockInfo {
1838 max_transfer_blocks: NonZero::new(2),
1839 device_flags: BlockDeviceFlag::READONLY
1840 | BlockDeviceFlag::REMOVABLE
1841 | BlockDeviceFlag::ZSTD_DECOMPRESSION_SUPPORT,
1842 ..Default::default()
1843 }),
1844 ..Default::default()
1845 },
1846 vec![PartitionInfo {
1847 label: PART_NAME.to_string(),
1848 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1849 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
1850 start_block: 4,
1851 num_blocks: 1,
1852 flags: 0xabcd,
1853 }],
1854 )
1855 .await;
1856
1857 let partitions_dir_clone = partitions_dir.clone();
1858 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
1859 .await
1860 .expect("load should succeed");
1861
1862 let part_dir = vfs::serve_directory(
1863 partitions_dir.clone(),
1864 vfs::path::Path::validate_and_split("part-000").unwrap(),
1865 vfs::execution_scope::ExecutionScope::new(),
1866 fio::PERM_READABLE,
1867 );
1868 let part_block =
1869 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_dir, "volume")
1870 .expect("Failed to open Volume service");
1871 let info: fblock::BlockInfo =
1872 part_block.get_info().await.expect("FIDL error").expect("get_info failed");
1873 assert_eq!(info.block_count, 1);
1874 assert_eq!(info.block_size, 512);
1875 assert_eq!(
1876 info.flags,
1877 BlockDeviceFlag::READONLY
1878 | BlockDeviceFlag::REMOVABLE
1879 | BlockDeviceFlag::ZSTD_DECOMPRESSION_SUPPORT
1880 );
1881 assert_eq!(info.max_transfer_size, 1024);
1882
1883 let metadata: fblock::PartitionInfo =
1884 part_block.get_metadata().await.expect("FIDL error").expect("get_metadata failed");
1885 assert_eq!(metadata.name, Some(PART_NAME.to_string()));
1886 assert_eq!(metadata.type_guid.unwrap().value, PART_TYPE_GUID);
1887 assert_eq!(metadata.instance_guid.unwrap().value, PART_INSTANCE_GUID);
1888 assert_eq!(metadata.start_block_offset, Some(4));
1889 assert_eq!(metadata.num_blocks, Some(1));
1890 assert_eq!(metadata.flags, Some(0xabcd));
1891
1892 runner.shutdown().await;
1893 }
1894
1895 #[fuchsia::test]
1896 async fn nested_gpt() {
1897 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1898 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1899 const PART_NAME: &str = "part";
1900
1901 let vmo = zx::Vmo::create(64 * 512).unwrap();
1902 let vmo_clone = vmo.create_child(zx::VmoChildOptions::REFERENCE, 0, 0).unwrap();
1903 let (outer_block_device, outer_partitions_dir) = setup_with_options(
1904 VmoBackedServerOptions {
1905 initial_contents: InitialContents::FromVmo(vmo_clone),
1906 block_size: 512,
1907 info: DeviceInfo::Block(BlockInfo {
1908 device_flags: BlockDeviceFlag::READONLY | BlockDeviceFlag::REMOVABLE,
1909 ..Default::default()
1910 }),
1911 ..Default::default()
1912 },
1913 vec![PartitionInfo {
1914 label: PART_NAME.to_string(),
1915 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1916 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
1917 start_block: 4,
1918 num_blocks: 16,
1919 flags: 0xabcd,
1920 }],
1921 )
1922 .await;
1923
1924 let outer_partitions_dir_clone = outer_partitions_dir.clone();
1925 let outer_runner =
1926 GptManager::new(outer_block_device.connect(), outer_partitions_dir_clone)
1927 .await
1928 .expect("load should succeed");
1929
1930 let outer_part_dir = vfs::serve_directory(
1931 outer_partitions_dir.clone(),
1932 vfs::path::Path::validate_and_split("part-000").unwrap(),
1933 vfs::execution_scope::ExecutionScope::new(),
1934 fio::PERM_READABLE,
1935 );
1936 let part_block =
1937 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&outer_part_dir, "volume")
1938 .expect("Failed to open Block service");
1939
1940 let client = Arc::new(RemoteBlockClient::new(part_block.clone()).await.unwrap());
1941 let _ = gpt::Gpt::format(
1942 client,
1943 vec![PartitionInfo {
1944 label: PART_NAME.to_string(),
1945 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1946 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
1947 start_block: 5,
1948 num_blocks: 1,
1949 flags: 0xabcd,
1950 }],
1951 )
1952 .await
1953 .unwrap();
1954
1955 let partitions_dir = vfs::directory::immutable::simple();
1956 let partitions_dir_clone = partitions_dir.clone();
1957 let runner =
1958 GptManager::new(part_block, partitions_dir_clone).await.expect("load should succeed");
1959 let part_dir = vfs::serve_directory(
1960 partitions_dir.clone(),
1961 vfs::path::Path::validate_and_split("part-000").unwrap(),
1962 vfs::execution_scope::ExecutionScope::new(),
1963 fio::PERM_READABLE,
1964 );
1965 let inner_part_block =
1966 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_dir, "volume")
1967 .expect("Failed to open Block service");
1968
1969 let client =
1970 RemoteBlockClient::new(inner_part_block).await.expect("Failed to create block client");
1971 assert_eq!(client.block_count(), 1);
1972 assert_eq!(client.block_size(), 512);
1973
1974 let buffer = vec![0xaa; 512];
1975 client.write_at(BufferSlice::Memory(&buffer), 0).await.unwrap();
1976 client
1977 .write_at(BufferSlice::Memory(&buffer), 512)
1978 .await
1979 .expect_err("Write past end should fail");
1980 client.flush().await.unwrap();
1981
1982 runner.shutdown().await;
1983 outer_runner.shutdown().await;
1984
1985 let data = vmo.read_to_vec::<u8>(9 * 512, 512).unwrap();
1987 assert_eq!(&data[..], &buffer[..]);
1988 }
1989
1990 #[fuchsia::test]
1991 async fn open_session_with_options_is_rejected() {
1992 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1993 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1994 const PART_NAME: &str = "foo";
1995
1996 let (block_device, partitions_dir) = setup_with_options(
1997 VmoBackedServerOptions {
1998 initial_contents: InitialContents::FromCapacity(16),
1999 block_size: 512,
2000 ..Default::default()
2001 },
2002 vec![PartitionInfo {
2003 label: PART_NAME.to_string(),
2004 type_guid: Guid::from_bytes(PART_TYPE_GUID),
2005 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
2006 start_block: 4,
2007 num_blocks: 2,
2008 flags: 0xabcd,
2009 }],
2010 )
2011 .await;
2012
2013 let partitions_dir_clone = partitions_dir.clone();
2014 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
2015 .await
2016 .expect("load should succeed");
2017
2018 let part_dir = vfs::serve_directory(
2019 partitions_dir.clone(),
2020 vfs::path::Path::validate_and_split("part-000").unwrap(),
2021 vfs::execution_scope::ExecutionScope::new(),
2022 fio::PERM_READABLE,
2023 );
2024
2025 let part_block =
2026 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_dir, "volume")
2027 .expect("Failed to open Block service");
2028
2029 let (session, server_end) = fidl::endpoints::create_proxy::<fblock::SessionMarker>();
2031 part_block
2032 .open_session_with_options(
2033 server_end,
2034 &[fblock::BlockOffsetMapping { target_block_offset: 1, length: 2 }],
2035 )
2036 .expect("FIDL error");
2037 session
2038 .get_fifo()
2039 .await
2040 .expect_err("Session should be closed because nested mappings are not supported");
2041
2042 runner.shutdown().await;
2043 }
2044
2045 #[fuchsia::test]
2046 async fn test_open_session_with_options_rejects_nested_mappings() {
2047 let (block_device, partitions_dir) = setup(
2048 512,
2049 2048,
2050 vec![
2051 PartitionInfo {
2052 label: "super".to_string(),
2053 type_guid: Guid::from_bytes([1; 16]),
2054 instance_guid: Guid::from_bytes([2; 16]),
2055 start_block: 34,
2056 num_blocks: 10,
2057 flags: 0,
2058 },
2059 PartitionInfo {
2060 label: "userdata".to_string(),
2061 type_guid: Guid::from_bytes([1; 16]),
2062 instance_guid: Guid::from_bytes([3; 16]),
2063 start_block: 50,
2064 num_blocks: 10,
2065 flags: 0,
2066 },
2067 ],
2068 )
2069 .await;
2070
2071 let partitions_dir_clone = partitions_dir.clone();
2072 let runner = GptManager::new_with_config(
2073 block_device.connect(),
2074 partitions_dir_clone,
2075 crate::config::Config { merge_super_and_userdata: true, ..Default::default() },
2076 )
2077 .await
2078 .expect("load should succeed");
2079
2080 let part_dir = vfs::serve_directory(
2081 partitions_dir.clone(),
2082 vfs::path::Path::validate_and_split("part-000").unwrap(),
2083 vfs::execution_scope::ExecutionScope::new(),
2084 fio::PERM_READABLE,
2085 );
2086
2087 let part_block =
2088 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_dir, "volume")
2089 .expect("Failed to open Block service");
2090
2091 let metadata: fblock::PartitionInfo =
2092 part_block.get_metadata().await.expect("FIDL error").expect("get_metadata failed");
2093 assert_eq!(metadata.name, Some("super_and_userdata".to_string()));
2094 assert!(metadata.start_block_offset.is_none());
2095 assert!(metadata.flags.is_none());
2096
2097 let (session, server_end) = fidl::endpoints::create_proxy::<fblock::SessionMarker>();
2100 part_block
2101 .open_session_with_options(
2102 server_end,
2103 &[fblock::BlockOffsetMapping { target_block_offset: 0, length: 3 }],
2104 )
2105 .expect("FIDL error");
2106 session.get_fifo().await.expect_err("Session should be closed due to nested mapping");
2107
2108 {
2109 let inner = runner.inner.lock().await;
2110 let backend = inner.composite_partitions.get(&0).unwrap().session_manager().interface();
2111 assert!(backend.passthrough());
2112 }
2113
2114 runner.shutdown().await;
2115 }
2116
2117 #[fuchsia::test]
2118 async fn test_vmos_detached_on_session_close() {
2119 let (block_device, partitions_dir) = setup(
2120 512,
2121 100,
2122 vec![PartitionInfo {
2123 type_guid: Guid::from_bytes([2u8; 16]),
2124 instance_guid: Guid::from_bytes([2u8; 16]),
2125 start_block: 34,
2126 num_blocks: 10,
2127 flags: 0,
2128 label: "test".to_string(),
2129 }],
2130 )
2131 .await;
2132
2133 let runner = GptManager::new(block_device.connect(), partitions_dir.clone()).await.unwrap();
2134 let proxy = vfs::serve_directory(
2135 partitions_dir.clone(),
2136 vfs::path::Path::validate_and_split("part-000").unwrap(),
2137 vfs::execution_scope::ExecutionScope::new(),
2138 fio::PERM_READABLE,
2139 );
2140 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
2141 .expect("Failed to open block service");
2142 let client = RemoteBlockClient::new(block).await.expect("Failed to create block client");
2143
2144 {
2145 let inner = runner.inner.lock().await;
2146 let backend = inner.partitions.get(&0).unwrap().session_manager().interface();
2147 assert_eq!(backend.vmo_count(), 1);
2148 }
2149
2150 client.close().await.expect("Failed to close client");
2151
2152 {
2153 let inner = runner.inner.lock().await;
2154 let backend = inner.partitions.get(&0).unwrap().session_manager().interface();
2155 assert_eq!(backend.vmo_count(), 0);
2156 }
2157
2158 runner.shutdown().await;
2159 }
2160
2161 #[test]
2162 fn test_should_passthrough_partition() {
2163 use super::{ALL_SYSTEM_PARTITION_LABELS, should_passthrough_partition};
2164
2165 let system_label = ALL_SYSTEM_PARTITION_LABELS[0].to_string();
2166
2167 let single_config = block_server::PartitionInfo {
2169 name: system_label.clone(),
2170 type_guid: [1; 16],
2171 instance_guid: [2; 16],
2172 flags: Some(0),
2173 start_block_offset: Some(0),
2174 block_count: 100,
2175 ..Default::default()
2176 };
2177 assert!(should_passthrough_partition(&single_config));
2178
2179 let multi_config = block_server::PartitionInfo {
2181 name: system_label,
2182 type_guid: [1; 16],
2183 instance_guid: [2; 16],
2184 flags: Some(0),
2185 start_block_offset: Some(0),
2186 block_count: 200,
2187 ..Default::default()
2188 };
2189 assert!(should_passthrough_partition(&multi_config));
2190 }
2191
2192 #[fuchsia::test]
2193 async fn test_merged_partition_passthrough_behavior() {
2194 {
2196 let (block_device, partitions_dir) = setup(
2197 512,
2198 2048,
2199 vec![
2200 PartitionInfo {
2201 label: "super".to_string(),
2202 type_guid: Guid::from_bytes([1; 16]),
2203 instance_guid: Guid::from_bytes([2; 16]),
2204 start_block: 34,
2205 num_blocks: 10,
2206 flags: 0,
2207 },
2208 PartitionInfo {
2209 label: "userdata".to_string(),
2210 type_guid: Guid::from_bytes([1; 16]),
2211 instance_guid: Guid::from_bytes([3; 16]),
2212 start_block: 50, num_blocks: 10,
2214 flags: 0,
2215 },
2216 ],
2217 )
2218 .await;
2219
2220 let runner = GptManager::new_with_config(
2221 block_device.connect(),
2222 partitions_dir,
2223 crate::config::Config { merge_super_and_userdata: true, ..Default::default() },
2224 )
2225 .await
2226 .expect("load should succeed");
2227
2228 {
2229 let inner = runner.inner.lock().await;
2230 let backend =
2231 inner.composite_partitions.get(&0).unwrap().session_manager().interface();
2232 assert!(backend.passthrough());
2233 }
2234 runner.shutdown().await;
2235 }
2236
2237 {
2239 let (block_device, partitions_dir) = setup(
2240 512,
2241 2048,
2242 vec![
2243 PartitionInfo {
2244 label: "super".to_string(),
2245 type_guid: Guid::from_bytes([1; 16]),
2246 instance_guid: Guid::from_bytes([2; 16]),
2247 start_block: 34,
2248 num_blocks: 10,
2249 flags: 0,
2250 },
2251 PartitionInfo {
2252 label: "userdata".to_string(),
2253 type_guid: Guid::from_bytes([1; 16]),
2254 instance_guid: Guid::from_bytes([3; 16]),
2255 start_block: 44, num_blocks: 10,
2257 flags: 0,
2258 },
2259 ],
2260 )
2261 .await;
2262
2263 let runner = GptManager::new_with_config(
2264 block_device.connect(),
2265 partitions_dir,
2266 crate::config::Config { merge_super_and_userdata: true, ..Default::default() },
2267 )
2268 .await
2269 .expect("load should succeed");
2270
2271 {
2272 let inner = runner.inner.lock().await;
2273 let backend =
2274 inner.composite_partitions.get(&0).unwrap().session_manager().interface();
2275 assert!(backend.passthrough());
2276 }
2277 runner.shutdown().await;
2278 }
2279 }
2280
2281 #[fuchsia::test]
2282 async fn reset_partition_table_severs_existing_connections() {
2283 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
2284 const PART_1_INSTANCE_GUID: [u8; 16] = [2u8; 16];
2285 const PART_1_NAME: &str = "part";
2286
2287 let (block_device, partitions_dir) = setup(
2288 512,
2289 1048576 / 512,
2290 vec![PartitionInfo {
2291 label: PART_1_NAME.to_string(),
2292 type_guid: Guid::from_bytes(PART_TYPE_GUID),
2293 instance_guid: Guid::from_bytes(PART_1_INSTANCE_GUID),
2294 start_block: 4,
2295 num_blocks: 10,
2296 flags: 0,
2297 }],
2298 )
2299 .await;
2300
2301 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
2302 .await
2303 .expect("load should succeed");
2304
2305 let part_0_dir = vfs::serve_directory(
2306 partitions_dir.clone(),
2307 vfs::path::Path::validate_and_split("part-000").unwrap(),
2308 vfs::execution_scope::ExecutionScope::new(),
2309 fio::PERM_READABLE,
2310 );
2311
2312 let part_0_block =
2313 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_0_dir, "volume")
2314 .expect("Failed to open Volume service");
2315 let part_0_partition =
2316 connect_to_named_protocol_at_dir_root::<fpartitions::PartitionMarker>(
2317 &part_0_dir,
2318 "partition",
2319 )
2320 .expect("Failed to open Partition service");
2321
2322 let (part_0_block_clone, server_end) =
2323 fidl::endpoints::create_proxy::<fblock::BlockMarker>();
2324 part_0_dir
2325 .open(
2326 "volume",
2327 fio::Flags::PROTOCOL_SERVICE,
2328 &fio::Options::default(),
2329 server_end.into_channel(),
2330 )
2331 .expect("Failed to open volume");
2332
2333 let client =
2334 RemoteBlockClient::new(part_0_block).await.expect("Failed to create block client");
2335
2336 let buf = vec![0xabu8; 512];
2337 client.write_at(BufferSlice::Memory(&buf[..]), 0).await.expect("write_at failed");
2338
2339 let nil_entry = PartitionInfo {
2340 label: "".to_string(),
2341 type_guid: Guid::from_bytes([0u8; 16]),
2342 instance_guid: Guid::from_bytes([0u8; 16]),
2343 start_block: 0,
2344 num_blocks: 0,
2345 flags: 0,
2346 };
2347 let mut new_partitions = vec![nil_entry; 128];
2348 new_partitions[0] = PartitionInfo {
2349 label: "part_new".to_string(),
2350 type_guid: Guid::from_bytes(PART_TYPE_GUID),
2351 instance_guid: Guid::from_bytes([1u8; 16]),
2352 start_block: 64,
2353 num_blocks: 2,
2354 flags: 0,
2355 };
2356
2357 runner.reset_partition_table(new_partitions).await.expect("reset_partition_table failed");
2358
2359 let transaction = runner.create_transaction().await.expect("Failed to create transaction");
2361 part_0_partition
2362 .update_metadata(fpartitions::PartitionUpdateMetadataRequest {
2363 transaction: Some(transaction),
2364 flags: Some(1234),
2365 ..Default::default()
2366 })
2367 .await
2368 .expect_err(
2369 "update_metadata on stale partition connection should fail with PEER_CLOSED",
2370 );
2371
2372 part_0_block_clone
2374 .get_name()
2375 .await
2376 .expect_err("get_name on stale block connection should fail");
2377
2378 client
2381 .write_at(BufferSlice::Memory(&buf[..]), 0)
2382 .await
2383 .expect_err("write_at on stale client should fail");
2384
2385 runner.shutdown().await;
2386 }
2387
2388 #[fuchsia::test(threads = 2)]
2389 async fn reset_partition_table_with_in_flight_io_succeeds() {
2390 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
2391 const PART_1_INSTANCE_GUID: [u8; 16] = [2u8; 16];
2392 const PART_1_NAME: &str = "part";
2393
2394 struct PauseObserver {
2395 started_tx: std::sync::mpsc::Sender<()>,
2396 resume_rx: std::sync::Mutex<std::sync::mpsc::Receiver<()>>,
2397 }
2398
2399 impl Observer for PauseObserver {
2400 fn read(
2401 &self,
2402 device_block_offset: u64,
2403 _block_count: u32,
2404 _vmo: &Arc<zx::Vmo>,
2405 _vmo_offset: u64,
2406 ) {
2407 if device_block_offset >= 4 {
2409 let _ = self.started_tx.send(());
2410 let _ = self.resume_rx.lock().unwrap().recv();
2411 }
2412 }
2413 }
2414
2415 let (started_tx, started_rx) = std::sync::mpsc::channel();
2416 let (resume_tx, resume_rx) = std::sync::mpsc::channel();
2417
2418 let (block_device, partitions_dir) = setup_with_options(
2419 VmoBackedServerOptions {
2420 initial_contents: InitialContents::FromCapacity(1048576 / 512),
2421 block_size: 512,
2422 observer: Some(Box::new(PauseObserver {
2423 started_tx,
2424 resume_rx: std::sync::Mutex::new(resume_rx),
2425 })),
2426 ..Default::default()
2427 },
2428 vec![PartitionInfo {
2429 label: PART_1_NAME.to_string(),
2430 type_guid: Guid::from_bytes(PART_TYPE_GUID),
2431 instance_guid: Guid::from_bytes(PART_1_INSTANCE_GUID),
2432 start_block: 4,
2433 num_blocks: 10,
2434 flags: 0,
2435 }],
2436 )
2437 .await;
2438
2439 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
2440 .await
2441 .expect("load should succeed");
2442
2443 let part_0_dir = vfs::serve_directory(
2444 partitions_dir.clone(),
2445 vfs::path::Path::validate_and_split("part-000").unwrap(),
2446 vfs::execution_scope::ExecutionScope::new(),
2447 fio::PERM_READABLE,
2448 );
2449
2450 let part_0_block =
2451 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_0_dir, "volume")
2452 .expect("Failed to open Volume service");
2453
2454 let client =
2455 RemoteBlockClient::new(part_0_block).await.expect("Failed to create block client");
2456
2457 let mut buf = vec![0u8; 512];
2459 let read_task = fasync::Task::spawn(async move {
2460 client.read_at(MutableBufferSlice::Memory(&mut buf[..]), 0).await
2461 });
2462
2463 started_rx.recv().expect("Failed to receive read start notification");
2465
2466 let nil_entry = PartitionInfo {
2467 label: "".to_string(),
2468 type_guid: Guid::from_bytes([0u8; 16]),
2469 instance_guid: Guid::from_bytes([0u8; 16]),
2470 start_block: 0,
2471 num_blocks: 0,
2472 flags: 0,
2473 };
2474 let mut new_partitions = vec![nil_entry; 128];
2475 new_partitions[0] = PartitionInfo {
2476 label: "part_new".to_string(),
2477 type_guid: Guid::from_bytes(PART_TYPE_GUID),
2478 instance_guid: Guid::from_bytes([1u8; 16]),
2479 start_block: 64,
2480 num_blocks: 2,
2481 flags: 0,
2482 };
2483
2484 let reset_fut = runner.reset_partition_table(new_partitions);
2488 let resume_task = fasync::Task::spawn(async move {
2489 fasync::Timer::new(std::time::Duration::from_millis(50)).await;
2490 let _ = resume_tx.send(());
2491 });
2492
2493 reset_fut.await.expect("reset_partition_table failed");
2494 resume_task.await;
2495
2496 read_task.await.expect_err("in-flight read should fail");
2498
2499 runner.shutdown().await;
2500 }
2501
2502 #[fuchsia::test]
2503 async fn test_mapper_passthrough_on_partition() {
2504 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
2505 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
2506 const PART_NAME: &str = "super";
2507
2508 let (block_device, partitions_dir) = setup(
2509 512,
2510 64,
2511 vec![PartitionInfo {
2512 label: PART_NAME.to_string(),
2513 type_guid: Guid::from_bytes(PART_TYPE_GUID),
2514 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
2515 start_block: 8,
2516 num_blocks: 16,
2517 flags: 0,
2518 }],
2519 )
2520 .await;
2521
2522 let mapper_proxy = block_device.connect_mapper();
2523 let partitions_dir_clone = partitions_dir.clone();
2524 let runner = GptManager::new_with_mapper(
2525 block_device.connect(),
2526 Some(mapper_proxy),
2527 partitions_dir_clone,
2528 )
2529 .await
2530 .expect("load should succeed");
2531
2532 let part_dir = vfs::serve_directory(
2533 partitions_dir.clone(),
2534 vfs::path::Path::validate_and_split("part-000").unwrap(),
2535 vfs::execution_scope::ExecutionScope::new(),
2536 fio::PERM_READABLE,
2537 );
2538 let part_mapper =
2539 connect_to_named_protocol_at_dir_root::<fblock::MapperMarker>(&part_dir, "mapper")
2540 .expect("Failed to open Mapper service");
2541
2542 let (_session_proxy, session_server_end) =
2543 fidl::endpoints::create_proxy::<fblock::MapperSessionMarker>();
2544 let mapping_vmo = zx::Vmo::create(mapping::MAPPING_VMO_SIZE).unwrap();
2545 let port = zx::Port::create();
2546 let delivery_queue = zx::Vmo::create(mapping::DELIVERY_VMO_SIZE).unwrap();
2547
2548 part_mapper
2549 .open_session(session_server_end, mapping_vmo, Some(port), Some(delivery_queue))
2550 .await
2551 .expect("FIDL open_session failed")
2552 .expect("open_session returned error");
2553
2554 runner.shutdown().await;
2555 }
2556
2557 #[fuchsia::test]
2558 async fn test_mapper_not_supported_without_parent_mapper() {
2559 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
2560 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
2561 const PART_NAME: &str = "super";
2562
2563 let (block_device, partitions_dir) = setup(
2564 512,
2565 64,
2566 vec![PartitionInfo {
2567 label: PART_NAME.to_string(),
2568 type_guid: Guid::from_bytes(PART_TYPE_GUID),
2569 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
2570 start_block: 8,
2571 num_blocks: 16,
2572 flags: 0,
2573 }],
2574 )
2575 .await;
2576
2577 let (block_proxy, mut stream) =
2578 fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2579 let underlying: fblock::BlockProxy = block_device.connect();
2580 let _block_task = fasync::Task::spawn(async move {
2581 while let Some(Ok(request)) = stream.next().await {
2582 match request {
2583 fblock::BlockRequest::ConnectMapper { responder, .. } => {
2584 let _ = responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw()));
2585 }
2586 fblock::BlockRequest::GetInfo { responder } => {
2587 let _ = responder
2588 .send(underlying.get_info().await.unwrap().as_ref().map_err(|s| *s));
2589 }
2590 fblock::BlockRequest::OpenSession { session, .. } => {
2591 let _ = underlying.open_session(session);
2592 }
2593 _ => unimplemented!(),
2594 }
2595 }
2596 });
2597
2598 let runner = GptManager::new(block_proxy, partitions_dir.clone())
2599 .await
2600 .expect("load should succeed");
2601
2602 let part_dir = vfs::serve_directory(
2603 partitions_dir.clone(),
2604 vfs::path::Path::validate_and_split("part-000").unwrap(),
2605 vfs::execution_scope::ExecutionScope::new(),
2606 fio::PERM_READABLE,
2607 );
2608 let part_block =
2609 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_dir, "volume")
2610 .expect("Failed to open Block service");
2611 let (part_mapper, mapper_server_end) =
2612 fidl::endpoints::create_proxy::<fblock::MapperMarker>();
2613 part_block
2614 .connect_mapper(mapper_server_end)
2615 .await
2616 .expect("FIDL connect_mapper failed")
2617 .expect("connect_mapper returned error");
2618
2619 let (_session_proxy, session_server_end) =
2620 fidl::endpoints::create_proxy::<fblock::MapperSessionMarker>();
2621 let mapping_vmo = zx::Vmo::create(mapping::MAPPING_VMO_SIZE).unwrap();
2622 let port = zx::Port::create();
2623 let delivery_queue = zx::Vmo::create(mapping::DELIVERY_VMO_SIZE).unwrap();
2624
2625 assert_eq!(
2626 part_mapper
2627 .open_session(session_server_end, mapping_vmo, Some(port), Some(delivery_queue))
2628 .await
2629 .expect("FIDL open_session failed"),
2630 Err(zx::Status::NOT_SUPPORTED.into_raw())
2631 );
2632
2633 runner.shutdown().await;
2634 }
2635
2636 #[fuchsia::test]
2637 async fn reset_partition_table_severs_passthrough_connections() {
2638 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
2639 const PART_1_INSTANCE_GUID: [u8; 16] = [2u8; 16];
2640 const PART_1_NAME: &str = "fvm";
2641
2642 let (block_device, partitions_dir) = setup(
2643 512,
2644 1048576 / 512,
2645 vec![PartitionInfo {
2646 label: PART_1_NAME.to_string(),
2647 type_guid: Guid::from_bytes(PART_TYPE_GUID),
2648 instance_guid: Guid::from_bytes(PART_1_INSTANCE_GUID),
2649 start_block: 4,
2650 num_blocks: 10,
2651 flags: 0,
2652 }],
2653 )
2654 .await;
2655
2656 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
2657 .await
2658 .expect("load should succeed");
2659
2660 let part_0_dir = vfs::serve_directory(
2661 partitions_dir.clone(),
2662 vfs::path::Path::validate_and_split("part-000").unwrap(),
2663 vfs::execution_scope::ExecutionScope::new(),
2664 fio::PERM_READABLE,
2665 );
2666
2667 let part_0_block =
2668 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_0_dir, "volume")
2669 .expect("Failed to open Volume service");
2670
2671 let (part_0_block_clone, server_end) =
2672 fidl::endpoints::create_proxy::<fblock::BlockMarker>();
2673 part_0_dir
2674 .open(
2675 "volume",
2676 fio::Flags::PROTOCOL_SERVICE,
2677 &fio::Options::default(),
2678 server_end.into_channel(),
2679 )
2680 .expect("Failed to open volume");
2681
2682 let client =
2683 RemoteBlockClient::new(part_0_block).await.expect("Failed to create block client");
2684
2685 let buf = vec![0xabu8; 512];
2686 client.write_at(BufferSlice::Memory(&buf[..]), 0).await.expect("write_at failed");
2687
2688 let nil_entry = PartitionInfo {
2689 label: "".to_string(),
2690 type_guid: Guid::from_bytes([0u8; 16]),
2691 instance_guid: Guid::from_bytes([0u8; 16]),
2692 start_block: 0,
2693 num_blocks: 0,
2694 flags: 0,
2695 };
2696 let mut new_partitions = vec![nil_entry; 128];
2697 new_partitions[0] = PartitionInfo {
2698 label: "part_new".to_string(),
2699 type_guid: Guid::from_bytes(PART_TYPE_GUID),
2700 instance_guid: Guid::from_bytes([1u8; 16]),
2701 start_block: 64,
2702 num_blocks: 2,
2703 flags: 0,
2704 };
2705
2706 runner.reset_partition_table(new_partitions).await.expect("reset_partition_table failed");
2707
2708 part_0_block_clone
2710 .get_name()
2711 .await
2712 .expect_err("get_name on stale block connection should fail");
2713
2714 client
2717 .write_at(BufferSlice::Memory(&buf[..]), 0)
2718 .await
2719 .expect_err("write_at on stale client should fail");
2720
2721 runner.shutdown().await;
2722 }
2723
2724 #[fuchsia::test]
2725 async fn test_mapper_passthrough_on_composite_partition() {
2726 let (block_device, partitions_dir) = setup(
2727 512,
2728 64,
2729 vec![
2730 PartitionInfo {
2731 label: "super".to_string(),
2732 type_guid: Guid::from_bytes([1u8; 16]),
2733 instance_guid: Guid::from_bytes([2u8; 16]),
2734 start_block: 8,
2735 num_blocks: 8,
2736 flags: 0,
2737 },
2738 PartitionInfo {
2739 label: "userdata".to_string(),
2740 type_guid: Guid::from_bytes([1u8; 16]),
2741 instance_guid: Guid::from_bytes([3u8; 16]),
2742 start_block: 16,
2743 num_blocks: 8,
2744 flags: 0,
2745 },
2746 ],
2747 )
2748 .await;
2749
2750 let mapper_proxy = block_device.connect_mapper();
2751 let partitions_dir_clone = partitions_dir.clone();
2752 let config = crate::config::Config { merge_super_and_userdata: true, ..Default::default() };
2753 let runner = GptManager::new_with_config_and_mapper(
2754 block_device.connect(),
2755 Some(mapper_proxy),
2756 partitions_dir_clone,
2757 config,
2758 )
2759 .await
2760 .expect("load should succeed");
2761
2762 let part_dir = vfs::serve_directory(
2763 partitions_dir.clone(),
2764 vfs::path::Path::validate_and_split("part-000").unwrap(),
2765 vfs::execution_scope::ExecutionScope::new(),
2766 fio::PERM_READABLE,
2767 );
2768 let part_mapper =
2769 connect_to_named_protocol_at_dir_root::<fblock::MapperMarker>(&part_dir, "mapper")
2770 .expect("Failed to open Mapper service");
2771
2772 let (_session_proxy, session_server_end) =
2773 fidl::endpoints::create_proxy::<fblock::MapperSessionMarker>();
2774 let mapping_vmo = zx::Vmo::create(mapping::MAPPING_VMO_SIZE).unwrap();
2775 let port = zx::Port::create();
2776 let delivery_queue = zx::Vmo::create(mapping::DELIVERY_VMO_SIZE).unwrap();
2777
2778 part_mapper
2779 .open_session(session_server_end, mapping_vmo, Some(port), Some(delivery_queue))
2780 .await
2781 .expect("FIDL open_session failed")
2782 .expect("open_session returned error");
2783
2784 runner.shutdown().await;
2785 }
2786
2787 #[fuchsia::test]
2788 async fn test_register_mappings_payload_preserved() {
2789 let (block_device, partitions_dir) = setup(512, 64, vec![]).await;
2790
2791 let (mapper_proxy, mut mapper_stream) =
2792 fidl::endpoints::create_proxy_and_stream::<fblock::MapperMarker>();
2793
2794 let (commands_tx, mut commands_rx) = futures::channel::mpsc::unbounded();
2795 let _mapper_task = fasync::Task::spawn(async move {
2796 if let Some(Ok(fblock::MapperRequest::OpenSession { mapping_vmo, responder, .. })) =
2797 mapper_stream.next().await
2798 {
2799 let vmo_dup = mapping_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
2800 let _receiver_thread = std::thread::spawn(move || {
2801 let mut receiver = vmo_fifo::Receiver::<mapping::RawMappingCommand>::new(
2802 vmo_dup,
2803 mapping::PENDING_COMMANDS_CAPACITY,
2804 )
2805 .unwrap();
2806 while let Ok(msg) = receiver.peek() {
2807 let cmd = *msg;
2808 let payload_len = cmd.extent_count as u32 * 8;
2809 let payload_slice = msg.payload_slice(cmd.offset, payload_len);
2810 let mut payload = vec![0u8; payload_len as usize];
2811 payload_slice.copy_to_slice(&mut payload);
2812 commands_tx.unbounded_send((cmd, payload)).unwrap();
2813 let _ = msg.pop();
2814 }
2815 });
2816 responder.send(Ok(())).unwrap();
2817 }
2818 });
2819
2820 let runner =
2821 GptManager::new_with_mapper(block_device.connect(), Some(mapper_proxy), partitions_dir)
2822 .await
2823 .expect("load should succeed");
2824
2825 let offset_map1 = block_server::OffsetMap::new(vec![block_server::BlockOffsetMapping {
2826 target_block_offset: 0,
2827 length: 8,
2828 }])
2829 .unwrap();
2830 let offset_map2 = block_server::OffsetMap::new(vec![block_server::BlockOffsetMapping {
2831 target_block_offset: 8,
2832 length: 8,
2833 }])
2834 .unwrap();
2835 runner.register_mappings(1, &offset_map1).await.expect("register 1 failed");
2836 runner.register_mappings(2, &offset_map2).await.expect("register 2 failed");
2837
2838 let (cmd1, payload1) = commands_rx.next().await.expect("expected first command");
2839 let (cmd2, payload2) = commands_rx.next().await.expect("expected second command");
2840
2841 assert_eq!(cmd1.key, 1);
2842 assert_eq!(cmd2.key, 2);
2843
2844 let (expected_payload1, _, _, expected_device_offset1) =
2845 super::offset_map_to_extents(&offset_map1, runner.block_size()).unwrap();
2846 let (expected_payload2, _, _, expected_device_offset2) =
2847 super::offset_map_to_extents(&offset_map2, runner.block_size()).unwrap();
2848 assert_eq!(cmd1.device_offset, expected_device_offset1);
2849 assert_eq!(cmd2.device_offset, expected_device_offset2);
2850 assert_eq!(payload1, expected_payload1);
2851 assert_eq!(payload2, expected_payload2);
2852
2853 runner.shutdown().await;
2854 }
2855
2856 #[fuchsia::test]
2857 async fn test_offset_map_to_extents_unaligned_length() {
2858 let offset_map = OffsetMap::new(vec![block_server::BlockOffsetMapping {
2859 target_block_offset: 34,
2860 length: 114654,
2861 }])
2862 .unwrap();
2863
2864 let (payload, logical_len, count, base_offset) =
2865 super::offset_map_to_extents(&offset_map, 512).unwrap();
2866
2867 assert_eq!(base_offset, 34 * 512);
2868 assert_eq!(logical_len, 58699776);
2870 assert_eq!(count, 1);
2871 assert_eq!(payload.len(), 8);
2872 }
2873
2874 #[fuchsia::test]
2875 async fn test_offset_map_to_extents_multiple_mappings() {
2876 let offset_map = OffsetMap::new(vec![
2880 block_server::BlockOffsetMapping { target_block_offset: 34, length: 8 },
2881 block_server::BlockOffsetMapping { target_block_offset: 50, length: 11 },
2882 ])
2883 .unwrap();
2884
2885 let (payload, logical_len, count, base_offset) =
2886 super::offset_map_to_extents(&offset_map, 512).unwrap();
2887
2888 assert_eq!(base_offset, 34 * 512);
2889 assert_eq!(logical_len, 8192);
2890 assert_eq!(count, 2);
2891 assert_eq!(payload.len(), 16);
2892 }
2893
2894 #[fuchsia::test]
2895 async fn test_offset_map_to_extents_earlier_mapping_unaligned_fails() {
2896 let offset_map = OffsetMap::new(vec![
2899 block_server::BlockOffsetMapping { target_block_offset: 34, length: 7 },
2900 block_server::BlockOffsetMapping { target_block_offset: 50, length: 8 },
2901 ])
2902 .unwrap();
2903
2904 let err = super::offset_map_to_extents(&offset_map, 512).unwrap_err();
2905 assert_eq!(err.root_cause().downcast_ref::<zx::Status>(), Some(&zx::Status::NOT_SUPPORTED));
2906 }
2907
2908 #[fuchsia::test]
2909 async fn test_offset_map_to_extents_lowest_physical_block_base_offset() {
2910 let offset_map = OffsetMap::new(vec![
2913 block_server::BlockOffsetMapping { target_block_offset: 50, length: 8 },
2914 block_server::BlockOffsetMapping { target_block_offset: 34, length: 8 },
2915 ])
2916 .unwrap();
2917
2918 let (payload, logical_len, count, base_offset) =
2919 super::offset_map_to_extents(&offset_map, 512).unwrap();
2920
2921 assert_eq!(base_offset, 34 * 512);
2922 assert_eq!(logical_len, 8192);
2923 assert_eq!(count, 2);
2924 assert_eq!(payload.len(), 16);
2925 }
2926}