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