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, Ordering};
28use std::sync::{Arc, 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 get_info(&self) -> block_server::DeviceInfo {
138 let mut info = self.info.lock().clone();
139 info.device_flags = self.block_client.block_flags();
140 info.max_transfer_blocks = self.block_client.max_transfer_blocks();
141 block_server::DeviceInfo::Partition(info)
142 }
143
144 pub async fn read(
145 &self,
146 device_block_offset: u64,
147 block_count: u32,
148 vmo_id: &VmoId,
149 vmo_offset: u64, opts: ReadOptions,
151 trace_flow_id: Option<NonZero<u64>>,
152 ) -> Result<(), zx::Status> {
153 let dev_offset = self
154 .absolute_offset(device_block_offset, block_count)
155 .map(|offset| offset * self.block_size() as u64)?;
156 let buffer = MutableBufferSlice::new_with_vmo_id(
157 vmo_id,
158 vmo_offset,
159 (block_count * self.block_size()) as u64,
160 );
161 self.block_client
162 .read_at_with_opts_traced(buffer, dev_offset, opts, trace_id(trace_flow_id))
163 .await
164 }
165
166 pub async fn write(
167 &self,
168 device_block_offset: u64,
169 block_count: u32,
170 vmo_id: &VmoId,
171 vmo_offset: u64, opts: WriteOptions,
173 trace_flow_id: Option<NonZero<u64>>,
174 ) -> Result<(), zx::Status> {
175 let dev_offset = self
176 .absolute_offset(device_block_offset, block_count)
177 .map(|offset| offset * self.block_size() as u64)?;
178 let buffer = BufferSlice::new_with_vmo_id(
179 vmo_id,
180 vmo_offset,
181 (block_count * self.block_size()) as u64,
182 );
183 self.block_client
184 .write_at_with_opts_traced(buffer, dev_offset, opts, trace_id(trace_flow_id))
185 .await
186 }
187
188 pub async fn flush(&self, trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
189 self.block_client.flush_traced(trace_id(trace_flow_id)).await
190 }
191
192 pub async fn trim(
193 &self,
194 device_block_offset: u64,
195 block_count: u32,
196 trace_flow_id: Option<NonZero<u64>>,
197 ) -> Result<(), zx::Status> {
198 let dev_offset = self
199 .absolute_offset(device_block_offset, block_count)
200 .map(|offset| offset * self.block_size() as u64)?;
201 let len = block_count as u64 * self.block_size() as u64;
202 let end = dev_offset.checked_add(len).ok_or(zx::Status::OUT_OF_RANGE)?;
203
204 self.block_client.trim_traced(dev_offset..end, trace_id(trace_flow_id)).await
205 }
206
207 fn absolute_offset(&self, mut offset: u64, len: u32) -> Result<u64, zx::Status> {
211 let info = self.info.lock();
212 let Some(start_block) = info.start_block_offset else {
213 return Err(zx::Status::BAD_STATE);
216 };
217 offset = offset.checked_add(start_block).ok_or(zx::Status::OUT_OF_RANGE)?;
218 let end = offset.checked_add(len as u64).ok_or(zx::Status::OUT_OF_RANGE)?;
219 if end > start_block + info.block_count {
220 Err(zx::Status::OUT_OF_RANGE)
221 } else {
222 Ok(offset)
223 }
224 }
225}
226
227struct PendingTransaction {
228 transaction: gpt::Transaction,
229 client_koid: zx::Koid,
230 added_partitions: Vec<u32>,
233 _signal_task: fasync::Task<()>,
235}
236
237struct Inner {
238 gpt: gpt::Gpt,
239 partitions: BTreeMap<u32, Arc<BlockServer<SessionManager<PartitionBackend>>>>,
240 composite_partitions: BTreeMap<u32, Arc<BlockServer<SessionManager<PartitionBackend>>>>,
242 partitions_dir: PartitionsDirectory,
245 pending_transaction: Option<PendingTransaction>,
246}
247
248impl Inner {
249 fn ensure_transaction_matches(&self, transaction: &zx::EventPair) -> Result<(), zx::Status> {
251 if let Some(pending) = self.pending_transaction.as_ref() {
252 if transaction.koid()? == pending.client_koid {
253 Ok(())
254 } else {
255 Err(zx::Status::BAD_HANDLE)
256 }
257 } else {
258 Err(zx::Status::BAD_STATE)
259 }
260 }
261
262 fn bind_partition(
263 &mut self,
264 parent: &Arc<GptManager>,
265 index: u32,
266 info: block_server::PartitionInfo,
267 composite_mappings: OffsetMap,
268 composite_indexes: Vec<usize>,
269 ) -> Result<(), Error> {
270 ensure!(
271 composite_indexes.is_empty() == composite_mappings.is_empty(),
272 "Composite partitions must provide mappings"
273 );
274 let passthrough = should_passthrough_partition(&info);
275 let mappings = if passthrough && composite_mappings.is_empty() {
276 single_partition_mapping(&info)?
278 } else {
279 composite_mappings
283 };
284 log::debug!(
285 "GPT part {index}{}{}: {info:?}",
286 if !composite_indexes.is_empty() { " (composite)" } else { "" },
287 if passthrough { " (passthrough)" } else { "" },
288 );
289 let partition = PartitionBackend::new(
290 GptPartition::new(parent, self.gpt.client().clone(), info),
291 mappings,
292 );
293 let block_server = Arc::new(BlockServer::new(parent.block_size, partition));
294 if !composite_indexes.is_empty() {
295 self.partitions_dir.add_composite(
296 &partition_directory_entry_name(index),
297 Arc::downgrade(&block_server),
298 Arc::downgrade(parent),
299 composite_indexes,
300 );
301 self.composite_partitions.insert(index, block_server);
302 } else {
303 self.partitions_dir.add_partition(
304 &partition_directory_entry_name(index),
305 Arc::downgrade(&block_server),
306 Arc::downgrade(parent),
307 index as usize,
308 );
309 self.partitions.insert(index, block_server);
310 }
311 Ok(())
312 }
313
314 fn bind_super_and_userdata_partition(
315 &mut self,
316 parent: &Arc<GptManager>,
317 super_partition: (u32, gpt::PartitionInfo),
318 userdata_partition: (u32, gpt::PartitionInfo),
319 ) -> Result<(), Error> {
320 let extent1 = block_server::BlockOffsetMapping {
321 target_block_offset: super_partition.1.start_block,
322 length: super_partition.1.num_blocks,
323 };
324 let extent2 = block_server::BlockOffsetMapping {
325 target_block_offset: userdata_partition.1.start_block,
326 length: userdata_partition.1.num_blocks,
327 };
328 let mappings =
329 block_server::OffsetMap::new(block_server::coalesce_mappings(vec![extent1, extent2]))?;
330 let info = block_server::PartitionInfo {
331 name: "super_and_userdata".to_string(),
333 type_guid: super_partition.1.type_guid.to_bytes(),
334 instance_guid: super_partition.1.instance_guid.to_bytes(),
335 block_count: mappings.total_blocks(),
336 ..Default::default()
337 };
338 log::trace!(
339 "GPT merged parts {:?} + {:?} -> {info:?}",
340 super_partition.1,
341 userdata_partition.1
342 );
343 self.bind_partition(
344 parent,
345 super_partition.0,
346 info,
347 mappings,
348 vec![super_partition.0 as usize, userdata_partition.0 as usize],
349 )
350 }
351
352 fn bind_all_partitions(&mut self, parent: &Arc<GptManager>) -> Result<(), Error> {
353 self.partitions.clear();
354 self.composite_partitions.clear();
355 self.partitions_dir.clear();
356
357 let mut partitions = self.gpt.partitions().clone();
358 if parent.config.merge_super_and_userdata {
359 let super_part = match partitions
362 .iter()
363 .find(|(_, info)| info.label == "super")
364 .map(|(index, _)| *index)
365 {
366 Some(index) => partitions.remove_entry(&index),
367 None => None,
368 };
369 let userdata_part = match partitions
370 .iter()
371 .find(|(_, info)| info.label == "userdata")
372 .map(|(index, _)| *index)
373 {
374 Some(index) => partitions.remove_entry(&index),
375 None => None,
376 };
377 if super_part.is_some() && userdata_part.is_some() {
378 let super_part = super_part.unwrap();
379 let userdata_part = userdata_part.unwrap();
380 self.bind_super_and_userdata_partition(parent, super_part, userdata_part)?;
381 } else if super_part.is_some() || userdata_part.is_some() {
382 log::warn!("Only one of super/userdata found; not merging");
383 let (index, info) = super_part.or(userdata_part).unwrap();
384 self.bind_partition(
385 parent,
386 index,
387 block_server::PartitionInfo::from(&info),
388 OffsetMap::empty(),
389 vec![],
390 )?;
391 }
392 }
393 for (index, info) in partitions {
394 self.bind_partition(
395 parent,
396 index,
397 block_server::PartitionInfo::from(&info),
398 OffsetMap::empty(),
399 vec![],
400 )?;
401 }
402 Ok(())
403 }
404
405 fn add_partition(&mut self, info: gpt::PartitionInfo) -> Result<usize, gpt::AddPartitionError> {
406 let pending = self.pending_transaction.as_mut().unwrap();
407 let idx = self.gpt.add_partition(&mut pending.transaction, info)?;
408 pending.added_partitions.push(idx as u32);
409 Ok(idx)
410 }
411}
412
413pub struct GptManager {
415 config: Config,
416 block_proxy: fblock::BlockProxy,
417 block_size: u32,
418 block_count: u64,
419 inner: futures::lock::Mutex<Inner>,
420 shutdown: AtomicBool,
421}
422
423impl std::fmt::Debug for GptManager {
424 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
425 f.debug_struct("GptManager")
426 .field("block_size", &self.block_size)
427 .field("block_count", &self.block_count)
428 .finish()
429 }
430}
431
432impl GptManager {
433 pub async fn new(
434 block_proxy: fblock::BlockProxy,
435 partitions_dir: Arc<vfs::directory::immutable::Simple>,
436 ) -> Result<Arc<Self>, Error> {
437 Self::new_with_config(block_proxy, partitions_dir, Config::default()).await
438 }
439
440 pub async fn new_with_config(
441 block_proxy: fblock::BlockProxy,
442 partitions_dir: Arc<vfs::directory::immutable::Simple>,
443 config: Config,
444 ) -> Result<Arc<Self>, Error> {
445 log::info!("Binding to GPT");
446 let client = Arc::new(RemoteBlockClient::new(block_proxy.clone()).await?);
447 let block_size = client.block_size();
448 let block_count = client.block_count();
449 let gpt = gpt::Gpt::open(client).await.context("Failed to load GPT")?;
450
451 let this = Arc::new(Self {
452 config,
453 block_proxy,
454 block_size,
455 block_count,
456 inner: futures::lock::Mutex::new(Inner {
457 gpt,
458 partitions: BTreeMap::new(),
459 composite_partitions: BTreeMap::new(),
460 partitions_dir: PartitionsDirectory::new(partitions_dir),
461 pending_transaction: None,
462 }),
463 shutdown: AtomicBool::new(false),
464 });
465 this.inner.lock().await.bind_all_partitions(&this)?;
466 log::info!("Starting all partitions OK!");
467 Ok(this)
468 }
469
470 pub fn block_size(&self) -> u32 {
471 self.block_size
472 }
473
474 pub fn block_count(&self) -> u64 {
475 self.block_count
476 }
477
478 pub async fn create_transaction(self: &Arc<Self>) -> Result<zx::EventPair, zx::Status> {
479 let mut inner = self.inner.lock().await;
480 if inner.pending_transaction.is_some() {
481 return Err(zx::Status::ALREADY_EXISTS);
482 }
483 let transaction = inner.gpt.create_transaction().unwrap();
484 let (client_end, server_end) = zx::EventPair::create();
485 let client_koid = client_end.koid()?;
486 let signal_waiter = fasync::OnSignals::new(server_end, zx::Signals::EVENTPAIR_PEER_CLOSED);
487 let this = self.clone();
488 let task = fasync::Task::spawn(async move {
489 let _ = signal_waiter.await;
490 let mut inner = this.inner.lock().await;
491 if inner.pending_transaction.as_ref().map_or(false, |t| t.client_koid == client_koid) {
492 inner.pending_transaction = None;
493 }
494 });
495 inner.pending_transaction = Some(PendingTransaction {
496 transaction,
497 client_koid,
498 added_partitions: vec![],
499 _signal_task: task,
500 });
501 Ok(client_end)
502 }
503
504 pub async fn commit_transaction(
505 self: &Arc<Self>,
506 transaction: zx::EventPair,
507 ) -> Result<(), zx::Status> {
508 let mut inner = self.inner.lock().await;
509 inner.ensure_transaction_matches(&transaction)?;
510 let pending = std::mem::take(&mut inner.pending_transaction).unwrap();
511 let partitions = pending.transaction.partitions.clone();
512 if let Err(error) = inner.gpt.commit_transaction(pending.transaction).await {
513 log::warn!(error:?; "Failed to commit transaction");
514 return Err(zx::Status::IO);
515 }
516 for (info, idx) in partitions
518 .iter()
519 .zip(0u32..)
520 .filter(|(info, idx)| !info.is_nil() && !pending.added_partitions.contains(idx))
521 {
522 if let Some(part) = inner.partitions.get(&idx) {
529 part.session_manager().interface().update_info(info.clone());
530 }
531 }
532 for idx in pending.added_partitions {
533 if let Some(gpt_info) = inner.gpt.partitions().get(&idx).cloned() {
534 let partition_info = block_server::PartitionInfo::from(&gpt_info);
535 if let Err(error) =
536 inner.bind_partition(self, idx, partition_info, OffsetMap::empty(), vec![])
537 {
538 log::error!(error:?; "Failed to bind partition");
539 }
540 }
541 }
542 Ok(())
543 }
544
545 pub async fn add_partition(
546 &self,
547 request: fpartitions::PartitionsManagerAddPartitionRequest,
548 ) -> Result<(), zx::Status> {
549 let mut inner = self.inner.lock().await;
550 inner.ensure_transaction_matches(
551 request.transaction.as_ref().ok_or(zx::Status::BAD_HANDLE)?,
552 )?;
553 let info = gpt::PartitionInfo {
554 label: request.name.ok_or(zx::Status::INVALID_ARGS)?,
555 type_guid: request
556 .type_guid
557 .map(|value| gpt::Guid::from_bytes(value.value))
558 .ok_or(zx::Status::INVALID_ARGS)?,
559 instance_guid: request
560 .instance_guid
561 .map(|value| gpt::Guid::from_bytes(value.value))
562 .unwrap_or_else(|| gpt::Guid::generate()),
563 start_block: 0,
564 num_blocks: request.num_blocks.ok_or(zx::Status::INVALID_ARGS)?,
565 flags: request.flags.unwrap_or_default(),
566 };
567 let idx = inner.add_partition(info)?;
568 let partition =
569 inner.pending_transaction.as_ref().unwrap().transaction.partitions.get(idx).unwrap();
570 log::info!(
571 "Allocated partition {:?} at {:?}",
572 partition.label,
573 partition.start_block..partition.start_block + partition.num_blocks
574 );
575 Ok(())
576 }
577
578 pub async fn handle_partitions_requests(
579 &self,
580 gpt_index: usize,
581 mut requests: fpartitions::PartitionRequestStream,
582 ) -> Result<(), zx::Status> {
583 while let Some(request) = requests.try_next().await.unwrap() {
584 match request {
585 fpartitions::PartitionRequest::UpdateMetadata { payload, responder } => {
586 responder
587 .send(
588 self.update_partition_metadata(gpt_index, payload)
589 .await
590 .map_err(|status| status.into_raw()),
591 )
592 .unwrap_or_else(
593 |error| log::error!(error:?; "Failed to send UpdateMetadata response"),
594 );
595 }
596 }
597 }
598 Ok(())
599 }
600
601 async fn update_partition_metadata(
602 &self,
603 gpt_index: usize,
604 request: fpartitions::PartitionUpdateMetadataRequest,
605 ) -> Result<(), zx::Status> {
606 let mut inner = self.inner.lock().await;
607 inner.ensure_transaction_matches(
608 request.transaction.as_ref().ok_or(zx::Status::BAD_HANDLE)?,
609 )?;
610
611 let transaction = &mut inner.pending_transaction.as_mut().unwrap().transaction;
612 let entry = transaction.partitions.get_mut(gpt_index).ok_or(zx::Status::BAD_STATE)?;
613 if let Some(type_guid) = request.type_guid.as_ref().cloned() {
614 entry.type_guid = gpt::Guid::from_bytes(type_guid.value);
615 }
616 if let Some(flags) = request.flags.as_ref() {
617 entry.flags = *flags;
618 }
619 Ok(())
620 }
621
622 pub async fn handle_composite_partitions_requests(
623 &self,
624 gpt_indexes: Vec<usize>,
625 mut requests: fpartitions::OverlayPartitionRequestStream,
626 ) -> Result<(), zx::Status> {
627 while let Some(request) = requests.try_next().await.unwrap() {
628 match request {
629 fpartitions::OverlayPartitionRequest::GetPartitions { responder } => {
630 match self.get_composite_partition_info(&gpt_indexes[..]).await {
631 Ok(partitions) => responder.send(Ok(&partitions[..])),
632 Err(status) => responder.send(Err(status.into_raw())),
633 }
634 .unwrap_or_else(
635 |error| log::error!(error:?; "Failed to send GetPartitions response"),
636 );
637 }
638 }
639 }
640 Ok(())
641 }
642
643 async fn get_composite_partition_info(
644 &self,
645 gpt_indexes: &[usize],
646 ) -> Result<Vec<fpartitions::PartitionInfo>, zx::Status> {
647 fn convert_partition_info(info: &gpt::PartitionInfo) -> fpartitions::PartitionInfo {
648 fpartitions::PartitionInfo {
649 name: Some(info.label.to_string()),
650 type_guid: Some(fblock::Guid { value: info.type_guid.to_bytes() }),
651 instance_guid: Some(fblock::Guid { value: info.instance_guid.to_bytes() }),
652 start_block_offset: Some(info.start_block),
653 num_blocks: Some(info.num_blocks),
654 flags: Some(info.flags),
655 ..Default::default()
656 }
657 }
658
659 let inner = self.inner.lock().await;
660 let mut partitions = vec![];
661 for index in gpt_indexes {
662 let index: u32 = *index as u32;
663 partitions.push(
664 inner
665 .gpt
666 .partitions()
667 .get(&index)
668 .map(convert_partition_info)
669 .ok_or(zx::Status::BAD_STATE)?,
670 );
671 }
672 Ok(partitions)
673 }
674
675 pub async fn reset_partition_table(
676 self: &Arc<Self>,
677 partitions: Vec<gpt::PartitionInfo>,
678 ) -> Result<(), zx::Status> {
679 let mut inner = self.inner.lock().await;
680 if inner.pending_transaction.is_some() {
681 return Err(zx::Status::BAD_STATE);
682 }
683
684 log::info!("Resetting gpt. Expect data loss!!!");
685 let mut transaction = inner.gpt.create_transaction().unwrap();
686 transaction.partitions = partitions;
687 inner.gpt.commit_transaction(transaction).await?;
688
689 if let Err(error) = inner.bind_all_partitions(&self) {
690 log::error!(error:?; "Failed to rebind partitions");
691 return Err(zx::Status::BAD_STATE);
692 }
693 log::info!("Rebinding partitions OK!");
694 Ok(())
695 }
696
697 pub async fn shutdown(self: Arc<Self>) {
698 log::info!("Shutting down gpt");
699 let mut inner = self.inner.lock().await;
700 inner.partitions_dir.clear();
701 inner.partitions.clear();
702 inner.composite_partitions.clear();
703 self.shutdown.store(true, Ordering::Relaxed);
704 log::info!("Shutting down gpt OK");
705 }
706}
707
708impl Drop for GptManager {
709 fn drop(&mut self) {
710 assert!(self.shutdown.load(Ordering::Relaxed), "Did you forget to shutdown?");
711 }
712}
713
714#[cfg(test)]
715mod tests {
716 use super::GptManager;
717 use block_client::{
718 BlockClient as _, BlockDeviceFlag, BufferSlice, MutableBufferSlice, RemoteBlockClient,
719 WriteFlags,
720 };
721 use block_server::{BlockInfo, DeviceInfo, WriteOptions};
722 use fidl_fuchsia_io as fio;
723 use fidl_fuchsia_storage_block as fblock;
724 use fidl_fuchsia_storage_partitions as fpartitions;
725 use fuchsia_async as fasync;
726 use fuchsia_component::client::connect_to_named_protocol_at_dir_root;
727 use gpt::{Gpt, Guid, PartitionInfo};
728 use std::num::NonZero;
729 use std::sync::Arc;
730 use std::sync::atomic::{AtomicBool, Ordering};
731 use test_vmo_backed_block_server::{
732 InitialContents, Observer, VmoBackedServer, VmoBackedServerOptions, WriteAction,
733 };
734
735 async fn setup(
736 block_size: u32,
737 block_count: u64,
738 partitions: Vec<PartitionInfo>,
739 ) -> (Arc<VmoBackedServer>, Arc<vfs::directory::immutable::Simple>) {
740 setup_with_options(
741 VmoBackedServerOptions {
742 initial_contents: InitialContents::FromCapacity(block_count),
743 block_size,
744 ..Default::default()
745 },
746 partitions,
747 )
748 .await
749 }
750
751 async fn setup_with_options(
752 opts: VmoBackedServerOptions<'_>,
753 partitions: Vec<PartitionInfo>,
754 ) -> (Arc<VmoBackedServer>, Arc<vfs::directory::immutable::Simple>) {
755 let server = Arc::new(opts.build().unwrap());
756 {
757 let (block_client, block_server) =
758 fidl::endpoints::create_proxy::<fblock::BlockMarker>();
759 let volume_stream = fidl::endpoints::ServerEnd::<fblock::BlockMarker>::from(
760 block_server.into_channel(),
761 )
762 .into_stream();
763 let server_clone = server.clone();
764 let _task = fasync::Task::spawn(async move { server_clone.serve(volume_stream).await });
765 let client = Arc::new(RemoteBlockClient::new(block_client).await.unwrap());
766 Gpt::format(client, partitions).await.unwrap();
767 }
768 (server, vfs::directory::immutable::simple())
769 }
770
771 #[fuchsia::test]
772 async fn load_unformatted_gpt() {
773 let server =
774 Arc::new(VmoBackedServer::new(8, 512, &[]).expect("Failed to create VmoBackedServer"));
775
776 GptManager::new(server.connect(), vfs::directory::immutable::simple())
777 .await
778 .expect_err("load should fail");
779 }
780
781 #[fuchsia::test]
782 async fn load_formatted_empty_gpt() {
783 let (block_device, partitions_dir) = setup(512, 8, vec![]).await;
784
785 let runner = GptManager::new(block_device.connect(), partitions_dir)
786 .await
787 .expect("load should succeed");
788 runner.shutdown().await;
789 }
790
791 #[fuchsia::test]
792 async fn load_formatted_gpt_with_one_partition() {
793 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
794 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
795 const PART_NAME: &str = "part";
796
797 let (block_device, partitions_dir) = setup(
798 512,
799 8,
800 vec![PartitionInfo {
801 label: PART_NAME.to_string(),
802 type_guid: Guid::from_bytes(PART_TYPE_GUID),
803 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
804 start_block: 4,
805 num_blocks: 1,
806 flags: 0,
807 }],
808 )
809 .await;
810
811 let partitions_dir_clone = partitions_dir.clone();
812 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
813 .await
814 .expect("load should succeed");
815 partitions_dir.get_entry("part-000").expect("No entry found");
816 partitions_dir.get_entry("part-001").map(|_| ()).expect_err("Extra entry found");
817 runner.shutdown().await;
818 }
819
820 #[fuchsia::test]
821 async fn load_formatted_gpt_with_two_partitions() {
822 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
823 const PART_INSTANCE_1_GUID: [u8; 16] = [2u8; 16];
824 const PART_INSTANCE_2_GUID: [u8; 16] = [3u8; 16];
825 const PART_1_NAME: &str = "part1";
826 const PART_2_NAME: &str = "part2";
827
828 let (block_device, partitions_dir) = setup(
829 512,
830 8,
831 vec![
832 PartitionInfo {
833 label: PART_1_NAME.to_string(),
834 type_guid: Guid::from_bytes(PART_TYPE_GUID),
835 instance_guid: Guid::from_bytes(PART_INSTANCE_1_GUID),
836 start_block: 4,
837 num_blocks: 1,
838 flags: 0,
839 },
840 PartitionInfo {
841 label: PART_2_NAME.to_string(),
842 type_guid: Guid::from_bytes(PART_TYPE_GUID),
843 instance_guid: Guid::from_bytes(PART_INSTANCE_2_GUID),
844 start_block: 5,
845 num_blocks: 1,
846 flags: 0,
847 },
848 ],
849 )
850 .await;
851
852 let partitions_dir_clone = partitions_dir.clone();
853 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
854 .await
855 .expect("load should succeed");
856 partitions_dir.get_entry("part-000").expect("No entry found");
857 partitions_dir.get_entry("part-001").expect("No entry found");
858 partitions_dir.get_entry("part-002").map(|_| ()).expect_err("Extra entry found");
859 runner.shutdown().await;
860 }
861
862 #[fuchsia::test]
863 async fn partition_io() {
864 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
865 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
866 const PART_NAME: &str = "part";
867
868 let (block_device, partitions_dir) = setup(
869 512,
870 8,
871 vec![PartitionInfo {
872 label: PART_NAME.to_string(),
873 type_guid: Guid::from_bytes(PART_TYPE_GUID),
874 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
875 start_block: 4,
876 num_blocks: 2,
877 flags: 0,
878 }],
879 )
880 .await;
881
882 let partitions_dir_clone = partitions_dir.clone();
883 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
884 .await
885 .expect("load should succeed");
886
887 let proxy = vfs::serve_directory(
888 partitions_dir.clone(),
889 vfs::path::Path::validate_and_split("part-000").unwrap(),
890 vfs::execution_scope::ExecutionScope::new(),
891 fio::PERM_READABLE,
892 );
893 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
894 .expect("Failed to open block service");
895 let client = RemoteBlockClient::new(block).await.expect("Failed to create block client");
896
897 assert_eq!(client.block_count(), 2);
898 assert_eq!(client.block_size(), 512);
899
900 let buf = vec![0xabu8; 512];
901 client.write_at(BufferSlice::Memory(&buf[..]), 0).await.expect("write_at failed");
902 client
903 .write_at(BufferSlice::Memory(&buf[..]), 1024)
904 .await
905 .expect_err("write_at should fail when writing past partition end");
906 let mut buf2 = vec![0u8; 512];
907 client.read_at(MutableBufferSlice::Memory(&mut buf2[..]), 0).await.expect("read_at failed");
908 assert_eq!(buf, buf2);
909 client
910 .read_at(MutableBufferSlice::Memory(&mut buf2[..]), 1024)
911 .await
912 .expect_err("read_at should fail when reading past partition end");
913 client.trim(512..1024).await.expect("trim failed");
914 client.trim(1..512).await.expect_err("trim with invalid range should fail");
915 client.trim(1024..1536).await.expect_err("trim past end of partition should fail");
916 runner.shutdown().await;
917
918 let mut buf = vec![0u8; 512];
920 let client =
921 RemoteBlockClient::new(block_device.connect::<fblock::BlockProxy>()).await.unwrap();
922 client.read_at(MutableBufferSlice::Memory(&mut buf[..]), 2048).await.unwrap();
923 assert_eq!(&buf[..], &[0xabu8; 512]);
924 }
925
926 #[fuchsia::test]
927 async fn load_formatted_gpt_with_invalid_primary_header() {
928 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
929 const PART_INSTANCE_1_GUID: [u8; 16] = [2u8; 16];
930 const PART_INSTANCE_2_GUID: [u8; 16] = [3u8; 16];
931 const PART_1_NAME: &str = "part1";
932 const PART_2_NAME: &str = "part2";
933
934 let (block_device, partitions_dir) = setup(
935 512,
936 8,
937 vec![
938 PartitionInfo {
939 label: PART_1_NAME.to_string(),
940 type_guid: Guid::from_bytes(PART_TYPE_GUID),
941 instance_guid: Guid::from_bytes(PART_INSTANCE_1_GUID),
942 start_block: 4,
943 num_blocks: 1,
944 flags: 0,
945 },
946 PartitionInfo {
947 label: PART_2_NAME.to_string(),
948 type_guid: Guid::from_bytes(PART_TYPE_GUID),
949 instance_guid: Guid::from_bytes(PART_INSTANCE_2_GUID),
950 start_block: 5,
951 num_blocks: 1,
952 flags: 0,
953 },
954 ],
955 )
956 .await;
957 {
958 let (client, stream) =
959 fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
960 let server = block_device.clone();
961 let _task = fasync::Task::spawn(async move { server.serve(stream).await });
962 let client = RemoteBlockClient::new(client).await.unwrap();
963 client.write_at(BufferSlice::Memory(&[0xffu8; 512]), 512).await.unwrap();
964 }
965
966 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
967 .await
968 .expect("load should succeed");
969 partitions_dir.get_entry("part-000").expect("No entry found");
970 partitions_dir.get_entry("part-001").expect("No entry found");
971 runner.shutdown().await;
972 }
973
974 #[fuchsia::test]
975 async fn load_formatted_gpt_with_invalid_primary_partition_table() {
976 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
977 const PART_INSTANCE_1_GUID: [u8; 16] = [2u8; 16];
978 const PART_INSTANCE_2_GUID: [u8; 16] = [3u8; 16];
979 const PART_1_NAME: &str = "part1";
980 const PART_2_NAME: &str = "part2";
981
982 let (block_device, partitions_dir) = setup(
983 512,
984 8,
985 vec![
986 PartitionInfo {
987 label: PART_1_NAME.to_string(),
988 type_guid: Guid::from_bytes(PART_TYPE_GUID),
989 instance_guid: Guid::from_bytes(PART_INSTANCE_1_GUID),
990 start_block: 4,
991 num_blocks: 1,
992 flags: 0,
993 },
994 PartitionInfo {
995 label: PART_2_NAME.to_string(),
996 type_guid: Guid::from_bytes(PART_TYPE_GUID),
997 instance_guid: Guid::from_bytes(PART_INSTANCE_2_GUID),
998 start_block: 5,
999 num_blocks: 1,
1000 flags: 0,
1001 },
1002 ],
1003 )
1004 .await;
1005 {
1006 let (client, stream) =
1007 fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1008 let server = block_device.clone();
1009 let _task = fasync::Task::spawn(async move { server.serve(stream).await });
1010 let client = RemoteBlockClient::new(client).await.unwrap();
1011 client.write_at(BufferSlice::Memory(&[0xffu8; 512]), 1024).await.unwrap();
1012 }
1013
1014 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1015 .await
1016 .expect("load should succeed");
1017 partitions_dir.get_entry("part-000").expect("No entry found");
1018 partitions_dir.get_entry("part-001").expect("No entry found");
1019 runner.shutdown().await;
1020 }
1021
1022 #[fuchsia::test]
1023 async fn force_access_passed_through() {
1024 const BLOCK_SIZE: u32 = 512;
1025 const BLOCK_COUNT: u64 = 1024;
1026
1027 struct ForceAccessObserver(Arc<AtomicBool>);
1028
1029 impl Observer for ForceAccessObserver {
1030 fn write(
1031 &self,
1032 _device_block_offset: u64,
1033 _block_count: u32,
1034 _vmo: &Arc<zx::Vmo>,
1035 _vmo_offset: u64,
1036 opts: WriteOptions,
1037 ) -> WriteAction {
1038 assert_eq!(
1039 opts.flags.contains(WriteFlags::FORCE_ACCESS),
1040 self.0.load(Ordering::Relaxed)
1041 );
1042 WriteAction::Write
1043 }
1044 }
1045
1046 let expect_force_access = Arc::new(AtomicBool::new(false));
1047 let (server, partitions_dir) = setup_with_options(
1048 VmoBackedServerOptions {
1049 initial_contents: InitialContents::FromCapacity(BLOCK_COUNT),
1050 block_size: BLOCK_SIZE,
1051 observer: Some(Box::new(ForceAccessObserver(expect_force_access.clone()))),
1052 info: DeviceInfo::Block(BlockInfo {
1053 device_flags: fblock::DeviceFlag::FUA_SUPPORT,
1054 ..Default::default()
1055 }),
1056 ..Default::default()
1057 },
1058 vec![PartitionInfo {
1059 label: "foo".to_string(),
1060 type_guid: Guid::from_bytes([1; 16]),
1061 instance_guid: Guid::from_bytes([2; 16]),
1062 start_block: 4,
1063 num_blocks: 1,
1064 flags: 0,
1065 }],
1066 )
1067 .await;
1068
1069 let manager = GptManager::new(server.connect(), partitions_dir.clone()).await.unwrap();
1070
1071 let proxy = vfs::serve_directory(
1072 partitions_dir.clone(),
1073 vfs::path::Path::validate_and_split("part-000").unwrap(),
1074 vfs::execution_scope::ExecutionScope::new(),
1075 fio::PERM_READABLE,
1076 );
1077 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1078 .expect("Failed to open block service");
1079 let client = RemoteBlockClient::new(block).await.expect("Failed to create block client");
1080
1081 let buffer = vec![0; BLOCK_SIZE as usize];
1082 client.write_at(BufferSlice::Memory(&buffer), 0).await.unwrap();
1083
1084 expect_force_access.store(true, Ordering::Relaxed);
1085
1086 client
1087 .write_at_with_opts(
1088 BufferSlice::Memory(&buffer),
1089 0,
1090 WriteOptions { flags: WriteFlags::FORCE_ACCESS, ..Default::default() },
1091 )
1092 .await
1093 .unwrap();
1094
1095 manager.shutdown().await;
1096 }
1097
1098 #[fuchsia::test]
1099 async fn barrier_passed_through() {
1100 const BLOCK_SIZE: u32 = 512;
1101 const BLOCK_COUNT: u64 = 1024;
1102
1103 struct BarrierObserver(Arc<AtomicBool>);
1104
1105 impl Observer for BarrierObserver {
1106 fn write(
1107 &self,
1108 _device_block_offset: u64,
1109 _block_count: u32,
1110 _vmo: &Arc<zx::Vmo>,
1111 _vmo_offset: u64,
1112 opts: WriteOptions,
1113 ) -> WriteAction {
1114 assert_eq!(
1115 opts.flags.contains(WriteFlags::PRE_BARRIER),
1116 self.0.load(Ordering::Relaxed)
1117 );
1118 WriteAction::Write
1119 }
1120 }
1121
1122 let expect_barrier = Arc::new(AtomicBool::new(false));
1123 let (server, partitions_dir) = setup_with_options(
1124 VmoBackedServerOptions {
1125 initial_contents: InitialContents::FromCapacity(BLOCK_COUNT),
1126 block_size: BLOCK_SIZE,
1127 observer: Some(Box::new(BarrierObserver(expect_barrier.clone()))),
1128 info: DeviceInfo::Block(BlockInfo {
1129 device_flags: fblock::DeviceFlag::BARRIER_SUPPORT,
1130 ..Default::default()
1131 }),
1132 ..Default::default()
1133 },
1134 vec![PartitionInfo {
1135 label: "foo".to_string(),
1136 type_guid: Guid::from_bytes([1; 16]),
1137 instance_guid: Guid::from_bytes([2; 16]),
1138 start_block: 4,
1139 num_blocks: 1,
1140 flags: 0,
1141 }],
1142 )
1143 .await;
1144
1145 let manager = GptManager::new(server.connect(), partitions_dir.clone()).await.unwrap();
1146
1147 let proxy = vfs::serve_directory(
1148 partitions_dir.clone(),
1149 vfs::path::Path::validate_and_split("part-000").unwrap(),
1150 vfs::execution_scope::ExecutionScope::new(),
1151 fio::PERM_READABLE,
1152 );
1153 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1154 .expect("Failed to open block service");
1155 let client = RemoteBlockClient::new(block).await.expect("Failed to create block client");
1156
1157 let buffer = vec![0; BLOCK_SIZE as usize];
1158 client.write_at(BufferSlice::Memory(&buffer), 0).await.unwrap();
1159
1160 expect_barrier.store(true, Ordering::Relaxed);
1161 client.barrier();
1162 client.write_at(BufferSlice::Memory(&buffer), 0).await.unwrap();
1163
1164 manager.shutdown().await;
1165 }
1166
1167 #[fuchsia::test]
1168 async fn commit_transaction() {
1169 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1170 const PART_1_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1171 const PART_1_NAME: &str = "part";
1172 const PART_2_INSTANCE_GUID: [u8; 16] = [3u8; 16];
1173 const PART_2_NAME: &str = "part2";
1174
1175 let (block_device, partitions_dir) = setup(
1176 512,
1177 16,
1178 vec![
1179 PartitionInfo {
1180 label: PART_1_NAME.to_string(),
1181 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1182 instance_guid: Guid::from_bytes(PART_1_INSTANCE_GUID),
1183 start_block: 4,
1184 num_blocks: 1,
1185 flags: 0,
1186 },
1187 PartitionInfo {
1188 label: PART_2_NAME.to_string(),
1189 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1190 instance_guid: Guid::from_bytes(PART_2_INSTANCE_GUID),
1191 start_block: 5,
1192 num_blocks: 1,
1193 flags: 0,
1194 },
1195 ],
1196 )
1197 .await;
1198 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1199 .await
1200 .expect("load should succeed");
1201
1202 let part_0_dir = vfs::serve_directory(
1203 partitions_dir.clone(),
1204 vfs::Path::validate_and_split("part-000").unwrap(),
1205 vfs::execution_scope::ExecutionScope::new(),
1206 fio::PERM_READABLE,
1207 );
1208 let part_1_dir = vfs::serve_directory(
1209 partitions_dir.clone(),
1210 vfs::Path::validate_and_split("part-001").unwrap(),
1211 vfs::execution_scope::ExecutionScope::new(),
1212 fio::PERM_READABLE,
1213 );
1214 let part_0_proxy = connect_to_named_protocol_at_dir_root::<fpartitions::PartitionMarker>(
1215 &part_0_dir,
1216 "partition",
1217 )
1218 .expect("Failed to open Partition service");
1219 let part_1_proxy = connect_to_named_protocol_at_dir_root::<fpartitions::PartitionMarker>(
1220 &part_1_dir,
1221 "partition",
1222 )
1223 .expect("Failed to open Partition service");
1224
1225 let transaction = runner.create_transaction().await.expect("Failed to create transaction");
1226 part_0_proxy
1227 .update_metadata(fpartitions::PartitionUpdateMetadataRequest {
1228 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1229 type_guid: Some(fblock::Guid { value: [0xffu8; 16] }),
1230 ..Default::default()
1231 })
1232 .await
1233 .expect("FIDL error")
1234 .expect("Failed to update_metadata");
1235 part_1_proxy
1236 .update_metadata(fpartitions::PartitionUpdateMetadataRequest {
1237 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1238 flags: Some(1234),
1239 ..Default::default()
1240 })
1241 .await
1242 .expect("FIDL error")
1243 .expect("Failed to update_metadata");
1244 runner.commit_transaction(transaction).await.expect("Failed to commit transaction");
1245
1246 let part_0_block =
1248 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_0_dir, "volume")
1249 .expect("Failed to open Volume service");
1250 let (status, guid) = part_0_block.get_type_guid().await.expect("FIDL error");
1251 assert_eq!(zx::Status::from_raw(status), zx::Status::OK);
1252 assert_eq!(guid.unwrap().value, [0xffu8; 16]);
1253 let part_1_block =
1254 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_1_dir, "volume")
1255 .expect("Failed to open Volume service");
1256 let metadata =
1257 part_1_block.get_metadata().await.expect("FIDL error").expect("get_metadata failed");
1258 assert_eq!(metadata.type_guid.unwrap().value, PART_TYPE_GUID);
1259 assert_eq!(metadata.flags, Some(1234));
1260
1261 runner.shutdown().await;
1262 }
1263
1264 #[fuchsia::test]
1265 async fn commit_transaction_with_io_error() {
1266 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1267 const PART_1_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1268 const PART_1_NAME: &str = "part";
1269 const PART_2_INSTANCE_GUID: [u8; 16] = [3u8; 16];
1270 const PART_2_NAME: &str = "part2";
1271
1272 #[derive(Clone)]
1273 struct TransactionObserver(Arc<AtomicBool>);
1274 impl Observer for TransactionObserver {
1275 fn write(
1276 &self,
1277 _device_block_offset: u64,
1278 _block_count: u32,
1279 _vmo: &Arc<zx::Vmo>,
1280 _vmo_offset: u64,
1281 _opts: WriteOptions,
1282 ) -> WriteAction {
1283 if self.0.load(Ordering::Relaxed) { WriteAction::Fail } else { WriteAction::Write }
1284 }
1285 }
1286 let observer = TransactionObserver(Arc::new(AtomicBool::new(false)));
1287 let (block_device, partitions_dir) = setup_with_options(
1288 VmoBackedServerOptions {
1289 initial_contents: InitialContents::FromCapacity(16),
1290 block_size: 512,
1291 observer: Some(Box::new(observer.clone())),
1292 ..Default::default()
1293 },
1294 vec![
1295 PartitionInfo {
1296 label: PART_1_NAME.to_string(),
1297 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1298 instance_guid: Guid::from_bytes(PART_1_INSTANCE_GUID),
1299 start_block: 4,
1300 num_blocks: 1,
1301 flags: 0,
1302 },
1303 PartitionInfo {
1304 label: PART_2_NAME.to_string(),
1305 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1306 instance_guid: Guid::from_bytes(PART_2_INSTANCE_GUID),
1307 start_block: 5,
1308 num_blocks: 1,
1309 flags: 0,
1310 },
1311 ],
1312 )
1313 .await;
1314 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1315 .await
1316 .expect("load should succeed");
1317
1318 let part_0_dir = vfs::serve_directory(
1319 partitions_dir.clone(),
1320 vfs::Path::validate_and_split("part-000").unwrap(),
1321 vfs::execution_scope::ExecutionScope::new(),
1322 fio::PERM_READABLE,
1323 );
1324 let part_1_dir = vfs::serve_directory(
1325 partitions_dir.clone(),
1326 vfs::Path::validate_and_split("part-001").unwrap(),
1327 vfs::execution_scope::ExecutionScope::new(),
1328 fio::PERM_READABLE,
1329 );
1330 let part_0_proxy = connect_to_named_protocol_at_dir_root::<fpartitions::PartitionMarker>(
1331 &part_0_dir,
1332 "partition",
1333 )
1334 .expect("Failed to open Partition service");
1335 let part_1_proxy = connect_to_named_protocol_at_dir_root::<fpartitions::PartitionMarker>(
1336 &part_1_dir,
1337 "partition",
1338 )
1339 .expect("Failed to open Partition service");
1340
1341 let transaction = runner.create_transaction().await.expect("Failed to create transaction");
1342 part_0_proxy
1343 .update_metadata(fpartitions::PartitionUpdateMetadataRequest {
1344 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1345 type_guid: Some(fblock::Guid { value: [0xffu8; 16] }),
1346 ..Default::default()
1347 })
1348 .await
1349 .expect("FIDL error")
1350 .expect("Failed to update_metadata");
1351 part_1_proxy
1352 .update_metadata(fpartitions::PartitionUpdateMetadataRequest {
1353 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1354 flags: Some(1234),
1355 ..Default::default()
1356 })
1357 .await
1358 .expect("FIDL error")
1359 .expect("Failed to update_metadata");
1360
1361 observer.0.store(true, Ordering::Relaxed); runner.commit_transaction(transaction).await.expect_err("Commit transaction should fail");
1363
1364 let part_0_block =
1366 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_0_dir, "volume")
1367 .expect("Failed to open Volume service");
1368 let (status, guid) = part_0_block.get_type_guid().await.expect("FIDL error");
1369 assert_eq!(zx::Status::from_raw(status), zx::Status::OK);
1370 assert_eq!(guid.unwrap().value, [2u8; 16]);
1371 let part_1_block =
1372 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_1_dir, "volume")
1373 .expect("Failed to open Volume service");
1374 let metadata =
1375 part_1_block.get_metadata().await.expect("FIDL error").expect("get_metadata failed");
1376 assert_eq!(metadata.type_guid.unwrap().value, PART_TYPE_GUID);
1377 assert_eq!(metadata.flags, Some(0));
1378
1379 runner.shutdown().await;
1380 }
1381
1382 #[fuchsia::test]
1383 async fn reset_partition_tables() {
1384 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1387 const PART_1_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1388 const PART_1_NAME: &str = "part";
1389 const PART_2_INSTANCE_GUID: [u8; 16] = [3u8; 16];
1390 const PART_2_NAME: &str = "part2";
1391 const PART_3_NAME: &str = "part3";
1392 const PART_4_NAME: &str = "part4";
1393
1394 let (block_device, partitions_dir) = setup(
1395 512,
1396 1048576 / 512,
1397 vec![
1398 PartitionInfo {
1399 label: PART_1_NAME.to_string(),
1400 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1401 instance_guid: Guid::from_bytes(PART_1_INSTANCE_GUID),
1402 start_block: 4,
1403 num_blocks: 1,
1404 flags: 0,
1405 },
1406 PartitionInfo {
1407 label: PART_2_NAME.to_string(),
1408 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1409 instance_guid: Guid::from_bytes(PART_2_INSTANCE_GUID),
1410 start_block: 5,
1411 num_blocks: 1,
1412 flags: 0,
1413 },
1414 ],
1415 )
1416 .await;
1417 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1418 .await
1419 .expect("load should succeed");
1420 let nil_entry = PartitionInfo {
1421 label: "".to_string(),
1422 type_guid: Guid::from_bytes([0u8; 16]),
1423 instance_guid: Guid::from_bytes([0u8; 16]),
1424 start_block: 0,
1425 num_blocks: 0,
1426 flags: 0,
1427 };
1428 let mut new_partitions = vec![nil_entry; 128];
1429 new_partitions[0] = PartitionInfo {
1430 label: PART_3_NAME.to_string(),
1431 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1432 instance_guid: Guid::from_bytes([1u8; 16]),
1433 start_block: 64,
1434 num_blocks: 2,
1435 flags: 0,
1436 };
1437 new_partitions[2] = PartitionInfo {
1438 label: PART_4_NAME.to_string(),
1439 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1440 instance_guid: Guid::from_bytes([2u8; 16]),
1441 start_block: 66,
1442 num_blocks: 4,
1443 flags: 0,
1444 };
1445 runner.reset_partition_table(new_partitions).await.expect("reset_partition_table failed");
1446 partitions_dir.get_entry("part-000").expect("No entry found");
1447 partitions_dir.get_entry("part-001").map(|_| ()).expect_err("Extra entry found");
1448 partitions_dir.get_entry("part-002").expect("No entry found");
1449
1450 let proxy = vfs::serve_directory(
1451 partitions_dir.clone(),
1452 vfs::path::Path::validate_and_split("part-000").unwrap(),
1453 vfs::execution_scope::ExecutionScope::new(),
1454 fio::PERM_READABLE,
1455 );
1456 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1457 .expect("Failed to open block service");
1458 let (status, name) = block.get_name().await.expect("FIDL error");
1459 assert_eq!(zx::Status::from_raw(status), zx::Status::OK);
1460 assert_eq!(name.unwrap(), PART_3_NAME);
1461
1462 runner.shutdown().await;
1463 }
1464
1465 #[fuchsia::test]
1466 async fn reset_partition_tables_fails_if_too_many_partitions() {
1467 let (block_device, partitions_dir) = setup(512, 8, vec![]).await;
1468 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1469 .await
1470 .expect("load should succeed");
1471 let nil_entry = PartitionInfo {
1472 label: "".to_string(),
1473 type_guid: Guid::from_bytes([0u8; 16]),
1474 instance_guid: Guid::from_bytes([0u8; 16]),
1475 start_block: 0,
1476 num_blocks: 0,
1477 flags: 0,
1478 };
1479 let new_partitions = vec![nil_entry; 128];
1480 runner
1481 .reset_partition_table(new_partitions)
1482 .await
1483 .expect_err("reset_partition_table should fail");
1484
1485 runner.shutdown().await;
1486 }
1487
1488 #[fuchsia::test]
1489 async fn reset_partition_tables_fails_if_too_large_partitions() {
1490 let (block_device, partitions_dir) = setup(512, 64, vec![]).await;
1491 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1492 .await
1493 .expect("load should succeed");
1494 let new_partitions = vec![
1495 PartitionInfo {
1496 label: "a".to_string(),
1497 type_guid: Guid::from_bytes([1u8; 16]),
1498 instance_guid: Guid::from_bytes([1u8; 16]),
1499 start_block: 4,
1500 num_blocks: 2,
1501 flags: 0,
1502 },
1503 PartitionInfo {
1504 label: "b".to_string(),
1505 type_guid: Guid::from_bytes([2u8; 16]),
1506 instance_guid: Guid::from_bytes([2u8; 16]),
1507 start_block: 6,
1508 num_blocks: 200,
1509 flags: 0,
1510 },
1511 ];
1512 runner
1513 .reset_partition_table(new_partitions)
1514 .await
1515 .expect_err("reset_partition_table should fail");
1516
1517 runner.shutdown().await;
1518 }
1519
1520 #[fuchsia::test]
1521 async fn reset_partition_tables_fails_if_partition_overlaps_metadata() {
1522 let (block_device, partitions_dir) = setup(512, 64, vec![]).await;
1523 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1524 .await
1525 .expect("load should succeed");
1526 let new_partitions = vec![PartitionInfo {
1527 label: "a".to_string(),
1528 type_guid: Guid::from_bytes([1u8; 16]),
1529 instance_guid: Guid::from_bytes([1u8; 16]),
1530 start_block: 1,
1531 num_blocks: 2,
1532 flags: 0,
1533 }];
1534 runner
1535 .reset_partition_table(new_partitions)
1536 .await
1537 .expect_err("reset_partition_table should fail");
1538
1539 runner.shutdown().await;
1540 }
1541
1542 #[fuchsia::test]
1543 async fn reset_partition_tables_fails_if_partitions_overlap() {
1544 let (block_device, partitions_dir) = setup(512, 64, vec![]).await;
1545 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1546 .await
1547 .expect("load should succeed");
1548 let new_partitions = vec![
1549 PartitionInfo {
1550 label: "a".to_string(),
1551 type_guid: Guid::from_bytes([1u8; 16]),
1552 instance_guid: Guid::from_bytes([1u8; 16]),
1553 start_block: 32,
1554 num_blocks: 2,
1555 flags: 0,
1556 },
1557 PartitionInfo {
1558 label: "b".to_string(),
1559 type_guid: Guid::from_bytes([2u8; 16]),
1560 instance_guid: Guid::from_bytes([2u8; 16]),
1561 start_block: 33,
1562 num_blocks: 1,
1563 flags: 0,
1564 },
1565 ];
1566 runner
1567 .reset_partition_table(new_partitions)
1568 .await
1569 .expect_err("reset_partition_table should fail");
1570
1571 runner.shutdown().await;
1572 }
1573
1574 #[fuchsia::test]
1575 async fn add_partition() {
1576 let (block_device, partitions_dir) = setup(512, 64, vec![PartitionInfo::nil(); 64]).await;
1577 let runner = GptManager::new(block_device.connect(), partitions_dir.clone())
1578 .await
1579 .expect("load should succeed");
1580
1581 let transaction = runner.create_transaction().await.expect("Create transaction failed");
1582 let request = fpartitions::PartitionsManagerAddPartitionRequest {
1583 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1584 name: Some("a".to_string()),
1585 type_guid: Some(fblock::Guid { value: [1u8; 16] }),
1586 num_blocks: Some(2),
1587 ..Default::default()
1588 };
1589 runner.add_partition(request).await.expect("add_partition failed");
1590 runner.commit_transaction(transaction).await.expect("add_partition failed");
1591
1592 let proxy = vfs::serve_directory(
1593 partitions_dir.clone(),
1594 vfs::path::Path::validate_and_split("part-000").unwrap(),
1595 vfs::execution_scope::ExecutionScope::new(),
1596 fio::PERM_READABLE,
1597 );
1598 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1599 .expect("Failed to open block service");
1600 let client: RemoteBlockClient =
1601 RemoteBlockClient::new(block).await.expect("Failed to create block client");
1602
1603 assert_eq!(client.block_count(), 2);
1604 assert_eq!(client.block_size(), 512);
1605
1606 runner.shutdown().await;
1607 }
1608
1609 #[fuchsia::test]
1610 async fn partition_info() {
1611 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1612 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1613 const PART_NAME: &str = "part";
1614
1615 let (block_device, partitions_dir) = setup_with_options(
1616 VmoBackedServerOptions {
1617 initial_contents: InitialContents::FromCapacity(16),
1618 block_size: 512,
1619 info: DeviceInfo::Block(BlockInfo {
1620 max_transfer_blocks: NonZero::new(2),
1621 device_flags: BlockDeviceFlag::READONLY
1622 | BlockDeviceFlag::REMOVABLE
1623 | BlockDeviceFlag::ZSTD_DECOMPRESSION_SUPPORT,
1624 ..Default::default()
1625 }),
1626 ..Default::default()
1627 },
1628 vec![PartitionInfo {
1629 label: PART_NAME.to_string(),
1630 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1631 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
1632 start_block: 4,
1633 num_blocks: 1,
1634 flags: 0xabcd,
1635 }],
1636 )
1637 .await;
1638
1639 let partitions_dir_clone = partitions_dir.clone();
1640 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
1641 .await
1642 .expect("load should succeed");
1643
1644 let part_dir = vfs::serve_directory(
1645 partitions_dir.clone(),
1646 vfs::path::Path::validate_and_split("part-000").unwrap(),
1647 vfs::execution_scope::ExecutionScope::new(),
1648 fio::PERM_READABLE,
1649 );
1650 let part_block =
1651 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_dir, "volume")
1652 .expect("Failed to open Volume service");
1653 let info: fblock::BlockInfo =
1654 part_block.get_info().await.expect("FIDL error").expect("get_info failed");
1655 assert_eq!(info.block_count, 1);
1656 assert_eq!(info.block_size, 512);
1657 assert_eq!(
1658 info.flags,
1659 BlockDeviceFlag::READONLY
1660 | BlockDeviceFlag::REMOVABLE
1661 | BlockDeviceFlag::ZSTD_DECOMPRESSION_SUPPORT
1662 );
1663 assert_eq!(info.max_transfer_size, 1024);
1664
1665 let metadata: fblock::PartitionInfo =
1666 part_block.get_metadata().await.expect("FIDL error").expect("get_metadata failed");
1667 assert_eq!(metadata.name, Some(PART_NAME.to_string()));
1668 assert_eq!(metadata.type_guid.unwrap().value, PART_TYPE_GUID);
1669 assert_eq!(metadata.instance_guid.unwrap().value, PART_INSTANCE_GUID);
1670 assert_eq!(metadata.start_block_offset, Some(4));
1671 assert_eq!(metadata.num_blocks, Some(1));
1672 assert_eq!(metadata.flags, Some(0xabcd));
1673
1674 runner.shutdown().await;
1675 }
1676
1677 #[fuchsia::test]
1678 async fn nested_gpt() {
1679 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1680 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1681 const PART_NAME: &str = "part";
1682
1683 let vmo = zx::Vmo::create(64 * 512).unwrap();
1684 let vmo_clone = vmo.create_child(zx::VmoChildOptions::REFERENCE, 0, 0).unwrap();
1685 let (outer_block_device, outer_partitions_dir) = setup_with_options(
1686 VmoBackedServerOptions {
1687 initial_contents: InitialContents::FromVmo(vmo_clone),
1688 block_size: 512,
1689 info: DeviceInfo::Block(BlockInfo {
1690 device_flags: BlockDeviceFlag::READONLY | BlockDeviceFlag::REMOVABLE,
1691 ..Default::default()
1692 }),
1693 ..Default::default()
1694 },
1695 vec![PartitionInfo {
1696 label: PART_NAME.to_string(),
1697 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1698 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
1699 start_block: 4,
1700 num_blocks: 16,
1701 flags: 0xabcd,
1702 }],
1703 )
1704 .await;
1705
1706 let outer_partitions_dir_clone = outer_partitions_dir.clone();
1707 let outer_runner =
1708 GptManager::new(outer_block_device.connect(), outer_partitions_dir_clone)
1709 .await
1710 .expect("load should succeed");
1711
1712 let outer_part_dir = vfs::serve_directory(
1713 outer_partitions_dir.clone(),
1714 vfs::path::Path::validate_and_split("part-000").unwrap(),
1715 vfs::execution_scope::ExecutionScope::new(),
1716 fio::PERM_READABLE,
1717 );
1718 let part_block =
1719 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&outer_part_dir, "volume")
1720 .expect("Failed to open Block service");
1721
1722 let client = Arc::new(RemoteBlockClient::new(part_block.clone()).await.unwrap());
1723 let _ = gpt::Gpt::format(
1724 client,
1725 vec![PartitionInfo {
1726 label: PART_NAME.to_string(),
1727 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1728 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
1729 start_block: 5,
1730 num_blocks: 1,
1731 flags: 0xabcd,
1732 }],
1733 )
1734 .await
1735 .unwrap();
1736
1737 let partitions_dir = vfs::directory::immutable::simple();
1738 let partitions_dir_clone = partitions_dir.clone();
1739 let runner =
1740 GptManager::new(part_block, partitions_dir_clone).await.expect("load should succeed");
1741 let part_dir = vfs::serve_directory(
1742 partitions_dir.clone(),
1743 vfs::path::Path::validate_and_split("part-000").unwrap(),
1744 vfs::execution_scope::ExecutionScope::new(),
1745 fio::PERM_READABLE,
1746 );
1747 let inner_part_block =
1748 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_dir, "volume")
1749 .expect("Failed to open Block service");
1750
1751 let client =
1752 RemoteBlockClient::new(inner_part_block).await.expect("Failed to create block client");
1753 assert_eq!(client.block_count(), 1);
1754 assert_eq!(client.block_size(), 512);
1755
1756 let buffer = vec![0xaa; 512];
1757 client.write_at(BufferSlice::Memory(&buffer), 0).await.unwrap();
1758 client
1759 .write_at(BufferSlice::Memory(&buffer), 512)
1760 .await
1761 .expect_err("Write past end should fail");
1762 client.flush().await.unwrap();
1763
1764 runner.shutdown().await;
1765 outer_runner.shutdown().await;
1766
1767 let data = vmo.read_to_vec::<u8>(9 * 512, 512).unwrap();
1769 assert_eq!(&data[..], &buffer[..]);
1770 }
1771
1772 #[fuchsia::test]
1773 async fn open_session_with_options_is_rejected() {
1774 const PART_TYPE_GUID: [u8; 16] = [2u8; 16];
1775 const PART_INSTANCE_GUID: [u8; 16] = [2u8; 16];
1776 const PART_NAME: &str = "foo";
1777
1778 let (block_device, partitions_dir) = setup_with_options(
1779 VmoBackedServerOptions {
1780 initial_contents: InitialContents::FromCapacity(16),
1781 block_size: 512,
1782 ..Default::default()
1783 },
1784 vec![PartitionInfo {
1785 label: PART_NAME.to_string(),
1786 type_guid: Guid::from_bytes(PART_TYPE_GUID),
1787 instance_guid: Guid::from_bytes(PART_INSTANCE_GUID),
1788 start_block: 4,
1789 num_blocks: 2,
1790 flags: 0xabcd,
1791 }],
1792 )
1793 .await;
1794
1795 let partitions_dir_clone = partitions_dir.clone();
1796 let runner = GptManager::new(block_device.connect(), partitions_dir_clone)
1797 .await
1798 .expect("load should succeed");
1799
1800 let part_dir = vfs::serve_directory(
1801 partitions_dir.clone(),
1802 vfs::path::Path::validate_and_split("part-000").unwrap(),
1803 vfs::execution_scope::ExecutionScope::new(),
1804 fio::PERM_READABLE,
1805 );
1806
1807 let part_block =
1808 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_dir, "volume")
1809 .expect("Failed to open Block service");
1810
1811 let (session, server_end) = fidl::endpoints::create_proxy::<fblock::SessionMarker>();
1813 part_block
1814 .open_session_with_options(
1815 server_end,
1816 &[fblock::BlockOffsetMapping { target_block_offset: 1, length: 2 }],
1817 )
1818 .expect("FIDL error");
1819 session
1820 .get_fifo()
1821 .await
1822 .expect_err("Session should be closed because nested mappings are not supported");
1823
1824 runner.shutdown().await;
1825 }
1826
1827 #[fuchsia::test]
1828 async fn test_open_session_with_options_rejects_nested_mappings() {
1829 let (block_device, partitions_dir) = setup(
1830 512,
1831 2048,
1832 vec![
1833 PartitionInfo {
1834 label: "super".to_string(),
1835 type_guid: Guid::from_bytes([1; 16]),
1836 instance_guid: Guid::from_bytes([2; 16]),
1837 start_block: 34,
1838 num_blocks: 10,
1839 flags: 0,
1840 },
1841 PartitionInfo {
1842 label: "userdata".to_string(),
1843 type_guid: Guid::from_bytes([1; 16]),
1844 instance_guid: Guid::from_bytes([3; 16]),
1845 start_block: 50,
1846 num_blocks: 10,
1847 flags: 0,
1848 },
1849 ],
1850 )
1851 .await;
1852
1853 let partitions_dir_clone = partitions_dir.clone();
1854 let runner = GptManager::new_with_config(
1855 block_device.connect(),
1856 partitions_dir_clone,
1857 crate::config::Config { merge_super_and_userdata: true, ..Default::default() },
1858 )
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
1869 let part_block =
1870 connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&part_dir, "volume")
1871 .expect("Failed to open Block service");
1872
1873 let metadata: fblock::PartitionInfo =
1874 part_block.get_metadata().await.expect("FIDL error").expect("get_metadata failed");
1875 assert_eq!(metadata.name, Some("super_and_userdata".to_string()));
1876 assert!(metadata.start_block_offset.is_none());
1877 assert!(metadata.flags.is_none());
1878
1879 let (session, server_end) = fidl::endpoints::create_proxy::<fblock::SessionMarker>();
1882 part_block
1883 .open_session_with_options(
1884 server_end,
1885 &[fblock::BlockOffsetMapping { target_block_offset: 0, length: 3 }],
1886 )
1887 .expect("FIDL error");
1888 session.get_fifo().await.expect_err("Session should be closed due to nested mapping");
1889
1890 {
1891 let inner = runner.inner.lock().await;
1892 let backend = inner.composite_partitions.get(&0).unwrap().session_manager().interface();
1893 assert!(backend.passthrough());
1894 }
1895
1896 runner.shutdown().await;
1897 }
1898
1899 #[fuchsia::test]
1900 async fn test_vmos_detached_on_session_close() {
1901 let (block_device, partitions_dir) = setup(
1902 512,
1903 100,
1904 vec![PartitionInfo {
1905 type_guid: Guid::from_bytes([2u8; 16]),
1906 instance_guid: Guid::from_bytes([2u8; 16]),
1907 start_block: 34,
1908 num_blocks: 10,
1909 flags: 0,
1910 label: "test".to_string(),
1911 }],
1912 )
1913 .await;
1914
1915 let runner = GptManager::new(block_device.connect(), partitions_dir.clone()).await.unwrap();
1916 let proxy = vfs::serve_directory(
1917 partitions_dir.clone(),
1918 vfs::path::Path::validate_and_split("part-000").unwrap(),
1919 vfs::execution_scope::ExecutionScope::new(),
1920 fio::PERM_READABLE,
1921 );
1922 let block = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(&proxy, "volume")
1923 .expect("Failed to open block service");
1924 let client = RemoteBlockClient::new(block).await.expect("Failed to create block client");
1925
1926 {
1927 let inner = runner.inner.lock().await;
1928 let backend = inner.partitions.get(&0).unwrap().session_manager().interface();
1929 assert_eq!(backend.vmo_count(), 1);
1930 }
1931
1932 client.close().await.expect("Failed to close client");
1933
1934 {
1935 let inner = runner.inner.lock().await;
1936 let backend = inner.partitions.get(&0).unwrap().session_manager().interface();
1937 assert_eq!(backend.vmo_count(), 0);
1938 }
1939
1940 runner.shutdown().await;
1941 }
1942
1943 #[test]
1944 fn test_should_passthrough_partition() {
1945 use super::{ALL_SYSTEM_PARTITION_LABELS, should_passthrough_partition};
1946
1947 let system_label = ALL_SYSTEM_PARTITION_LABELS[0].to_string();
1948
1949 let single_config = block_server::PartitionInfo {
1951 name: system_label.clone(),
1952 type_guid: [1; 16],
1953 instance_guid: [2; 16],
1954 flags: Some(0),
1955 start_block_offset: Some(0),
1956 block_count: 100,
1957 ..Default::default()
1958 };
1959 assert!(should_passthrough_partition(&single_config));
1960
1961 let multi_config = block_server::PartitionInfo {
1963 name: system_label,
1964 type_guid: [1; 16],
1965 instance_guid: [2; 16],
1966 flags: Some(0),
1967 start_block_offset: Some(0),
1968 block_count: 200,
1969 ..Default::default()
1970 };
1971 assert!(should_passthrough_partition(&multi_config));
1972 }
1973
1974 #[fuchsia::test]
1975 async fn test_merged_partition_passthrough_behavior() {
1976 {
1978 let (block_device, partitions_dir) = setup(
1979 512,
1980 2048,
1981 vec![
1982 PartitionInfo {
1983 label: "super".to_string(),
1984 type_guid: Guid::from_bytes([1; 16]),
1985 instance_guid: Guid::from_bytes([2; 16]),
1986 start_block: 34,
1987 num_blocks: 10,
1988 flags: 0,
1989 },
1990 PartitionInfo {
1991 label: "userdata".to_string(),
1992 type_guid: Guid::from_bytes([1; 16]),
1993 instance_guid: Guid::from_bytes([3; 16]),
1994 start_block: 50, num_blocks: 10,
1996 flags: 0,
1997 },
1998 ],
1999 )
2000 .await;
2001
2002 let runner = GptManager::new_with_config(
2003 block_device.connect(),
2004 partitions_dir,
2005 crate::config::Config { merge_super_and_userdata: true, ..Default::default() },
2006 )
2007 .await
2008 .expect("load should succeed");
2009
2010 {
2011 let inner = runner.inner.lock().await;
2012 let backend =
2013 inner.composite_partitions.get(&0).unwrap().session_manager().interface();
2014 assert!(backend.passthrough());
2015 }
2016 runner.shutdown().await;
2017 }
2018
2019 {
2021 let (block_device, partitions_dir) = setup(
2022 512,
2023 2048,
2024 vec![
2025 PartitionInfo {
2026 label: "super".to_string(),
2027 type_guid: Guid::from_bytes([1; 16]),
2028 instance_guid: Guid::from_bytes([2; 16]),
2029 start_block: 34,
2030 num_blocks: 10,
2031 flags: 0,
2032 },
2033 PartitionInfo {
2034 label: "userdata".to_string(),
2035 type_guid: Guid::from_bytes([1; 16]),
2036 instance_guid: Guid::from_bytes([3; 16]),
2037 start_block: 44, num_blocks: 10,
2039 flags: 0,
2040 },
2041 ],
2042 )
2043 .await;
2044
2045 let runner = GptManager::new_with_config(
2046 block_device.connect(),
2047 partitions_dir,
2048 crate::config::Config { merge_super_and_userdata: true, ..Default::default() },
2049 )
2050 .await
2051 .expect("load should succeed");
2052
2053 {
2054 let inner = runner.inner.lock().await;
2055 let backend =
2056 inner.composite_partitions.get(&0).unwrap().session_manager().interface();
2057 assert!(backend.passthrough());
2058 }
2059 runner.shutdown().await;
2060 }
2061 }
2062}