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