1use crate::fuchsia::errors::map_to_status;
6use crate::fuchsia::fxblob::blob::FxBlob;
7use crate::fuchsia::fxblob::directory::BlobDirectory;
8use crate::fuchsia::node::OpenedNode;
9use anyhow::Error;
10use fidl_fuchsia_storage_mapping as fmapping;
11use fuchsia_merkle::Hash;
12use futures::TryStreamExt;
13use fxfs::errors::FxfsError;
14use log::{error, warn};
15use mapping::{
16 Extents, MAPPING_VMO_SIZE, MappingCommand, PENDING_COMMANDS_CAPACITY, RawMappingCommand,
17};
18use std::collections::HashMap;
19use std::sync::Arc;
20use vmo_fifo::AsyncSender;
21
22pub struct BlobMappingProvider {
24 blob_directory: Arc<BlobDirectory>,
25}
26
27impl BlobMappingProvider {
28 pub fn new(blob_directory: Arc<BlobDirectory>) -> Result<Self, Error> {
29 Ok(Self { blob_directory })
30 }
31
32 pub async fn handle_mapping_provider_requests(
33 self: Arc<Self>,
34 mut stream: fmapping::MappingProviderRequestStream,
35 ) {
36 while let Ok(Some(request)) = stream.try_next().await {
37 match request {
38 fmapping::MappingProviderRequest::OpenSession { session, responder } => {
39 let vmo = match zx::Vmo::create(MAPPING_VMO_SIZE) {
40 Ok(v) => v,
41 Err(e) => {
42 let _ = responder.send(Err(e.into_raw()));
43 continue;
44 }
45 };
46 let sender = match AsyncSender::<RawMappingCommand>::new(
47 vmo,
48 8,
49 PENDING_COMMANDS_CAPACITY,
50 ) {
51 Ok(s) => s,
52 Err(e) => {
53 let _ = responder.send(Err(e.into_raw()));
54 continue;
55 }
56 };
57 let vmo_clone = match sender.vmo().duplicate_handle(zx::Rights::SAME_RIGHTS) {
58 Ok(v) => v,
59 Err(e) => {
60 let _ = responder.send(Err(e.into_raw()));
61 continue;
62 }
63 };
64 if let Err(error) = responder.send(Ok(vmo_clone)) {
65 error!(error:?; "Failed to send open session response");
66 } else {
67 let mapping_session =
68 BlobMappingSession::new(self.blob_directory.clone(), sender);
69 self.blob_directory.volume().scope().spawn(async move {
70 mapping_session
71 .handle_mapping_session_requests(session.into_stream())
72 .await;
73 });
74 }
75 }
76 fmapping::MappingProviderRequest::_UnknownMethod { ordinal, .. } => {
77 warn!(ordinal; "Unknown MappingProvider method");
78 }
79 }
80 }
81 }
82}
83
84pub struct BlobMappingSession {
89 blob_directory: Arc<BlobDirectory>,
90 sender: AsyncSender<RawMappingCommand>,
91 opened_blobs: HashMap<u64, OpenedNode<FxBlob>>,
92}
93
94impl BlobMappingSession {
95 pub fn new(blob_directory: Arc<BlobDirectory>, sender: AsyncSender<RawMappingCommand>) -> Self {
96 Self { blob_directory, sender, opened_blobs: HashMap::new() }
97 }
98
99 async fn open_blob(&mut self, key: u64, hash: Hash) -> Result<u64, Error> {
102 let node = self.blob_directory.open_blob(&hash.into()).await?.ok_or(FxfsError::NotFound)?;
103
104 let extents = node.get_mapping_extents().await?;
105 let size = node.as_ref().byte_size();
106 let stored_size = node.as_ref().stored_size().await?;
107 let data_extents = Extents::try_new(&extents.data, 0)?;
108 let merkle_extents = Extents::try_new(&extents.merkle, 0)?;
109 let extent_count = data_extents.len() as u32;
110 let metadata_count = merkle_extents.len() as u32;
111
112 let allocation_size = (extent_count + metadata_count) as usize * std::mem::size_of::<u64>();
113
114 if allocation_size > 0 {
115 let mut payload = self.sender.reserve_payload(allocation_size).await?;
116 let offset_in_vmo = payload.offset();
117
118 for (mut chunk, val_res) in payload.data().chunks_mut(std::mem::size_of::<u64>()).zip(
119 Extents::encode_extents(&data_extents)
120 .chain(Extents::encode_extents(&merkle_extents)),
121 ) {
122 chunk.copy_from_slice(&val_res.to_le_bytes());
123 }
124
125 let command = MappingCommand::Mappings {
126 key,
127 offset: offset_in_vmo as u32,
128 stored_size,
129 device_offset: 0,
130 metadata_count,
131 extent_count,
132 encrypted: false,
133 };
134
135 payload.commit(command.into()).await?;
136 }
137
138 self.opened_blobs.insert(key, node);
139
140 Ok(size)
141 }
142
143 async fn close_blob(&mut self, key: u64) -> Result<(), Error> {
145 if self.opened_blobs.remove(&key).is_none() {
146 return Ok(());
147 }
148 self.sender.push(MappingCommand::CloseBlob { key }.into()).await?;
149 Ok(())
150 }
151
152 pub async fn handle_mapping_session_requests(
153 mut self,
154 mut stream: fmapping::MappingSessionRequestStream,
155 ) {
156 while let Ok(Some(request)) = stream.try_next().await {
157 match request {
158 fmapping::MappingSessionRequest::Open { key, identifier, responder } => {
159 if identifier.len() != fuchsia_hash::HASH_SIZE {
161 responder.send(Err(zx::Status::INVALID_ARGS.into_raw())).unwrap_or_else(
162 |error| warn!(error:?; "Failed to send mapping session response"),
163 );
164 continue;
165 }
166 let hash = Hash::from(<[u8; 32]>::try_from(identifier.as_slice()).unwrap());
167 match self.open_blob(key, hash).await {
168 Ok(size) => {
169 responder.send(Ok(size)).unwrap_or_else(
170 |error| warn!(error:?; "Failed to send mapping session response"),
171 );
172 }
173 Err(error) => {
174 error!(error:?; "Failed to open blob");
175 responder.send(Err(map_to_status(error).into_raw())).unwrap_or_else(
176 |error| warn!(error:?; "Failed to send mapping session response"),
177 );
178 }
179 }
180 }
181 fmapping::MappingSessionRequest::Close { key, responder } => {
182 let result = match self.close_blob(key).await {
183 Ok(()) => Ok(()),
184 Err(error) => {
185 error!(error:?; "Failed to close blob");
186 Err(map_to_status(error).into_raw())
187 }
188 };
189 responder.send(result).unwrap_or_else(
190 |error| warn!(error:?; "Failed to send mapping session response"),
191 );
192 }
193 fmapping::MappingSessionRequest::GetProfile { name, responder } => {
194 let result = match self.blob_directory.volume().get_profile_vmo(&name).await {
195 Ok(vmo) => Ok(vmo),
196 Err(error) => Err(map_to_status(error).into_raw()),
197 };
198 responder.send(result).unwrap_or_else(
199 |error| warn!(error:?; "Failed to send mapping session response"),
200 );
201 }
202 fmapping::MappingSessionRequest::WriteProfile { name, profile_vmo, responder } => {
203 let result = match self
204 .blob_directory
205 .volume()
206 .write_profile_vmo(&name, profile_vmo)
207 .await
208 {
209 Ok(()) => Ok(()),
210 Err(error) => {
211 error!(error:?, name = name.as_str(); "Failed to write profile VMO");
212 Err(map_to_status(error).into_raw())
213 }
214 };
215 responder.send(result).unwrap_or_else(
216 |error| warn!(error:?; "Failed to send mapping session response"),
217 );
218 }
219 fmapping::MappingSessionRequest::_UnknownMethod { ordinal, .. } => {
220 warn!(ordinal; "Unknown MappingSession method");
221 }
222 }
223 }
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use crate::fuchsia::fxblob::testing::{BlobFixture, new_blob_fixture, open_blob_fixture};
231 use crate::fuchsia::node::FxNode;
232 use crate::fuchsia::testing::TestFixture;
233 use blob_writer::BlobWriter;
234 use delivery_blob::{CompressionMode, Type1Blob};
235 use fidl_fuchsia_io::UnlinkOptions;
236 use fuchsia_async as fasync;
237 use futures::channel::oneshot;
238 use fxfs::lsm_tree::types::Item;
239 use fxfs::object_handle::ObjectHandle;
240 use fxfs::object_store::{
241 AttributeId, ExtentValue, HandleOptions, ObjectKey, ObjectStore, ObjectValue,
242 };
243 use storage_device::Device;
244 use storage_device::buffer::OwnedBuffer;
245 use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
246 use vmo_fifo::Receiver;
247
248 #[fuchsia::test]
249 async fn test_blob_mapping_provider() {
250 let fixture = new_blob_fixture().await;
251 let data = vec![42; 300_000];
253 let hash = fixture.write_blob(&data, CompressionMode::Never).await;
254
255 let blob_dir = fixture
256 .volume()
257 .root()
258 .clone()
259 .as_node()
260 .into_any()
261 .downcast::<BlobDirectory>()
262 .expect("Failed to downcast root directory to BlobDirectory");
263
264 let node = blob_dir
265 .open_blob(&hash.into())
266 .await
267 .expect("Failed to open blob in Fxfs")
268 .expect("open_blob returned None instead of node");
269 let extents = node.get_mapping_extents().await.expect("Failed to retrieve extents");
270 let data_extents = extents.data;
271 let merkle_extents = extents.merkle;
272
273 drop(node);
276
277 let vmo = zx::Vmo::create(MAPPING_VMO_SIZE).unwrap();
278 let client_mapping = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
279 let sender =
280 AsyncSender::<RawMappingCommand>::new(vmo, 8, PENDING_COMMANDS_CAPACITY).unwrap();
281
282 let mut session = BlobMappingSession::new(blob_dir, sender);
283
284 let receiver_task = fasync::unblock(move || {
285 let mut receiver = Receiver::<RawMappingCommand>::new(client_mapping, 256)
286 .expect("Failed to create the Receiver wrapper");
287
288 let cmd1_raw = receiver.peek().expect("peek failed");
290 let cmd1 = MappingCommand::try_from(*cmd1_raw).expect("try_from failed");
291
292 let (cmd1_offset, cmd1_extent_count, cmd1_metadata_count) = match cmd1 {
293 MappingCommand::Mappings {
294 key,
295 offset,
296 stored_size: _,
297 device_offset: _,
298 metadata_count,
299 extent_count,
300 encrypted,
301 } => {
302 assert_eq!(key, 1);
303 assert_eq!(extent_count, data_extents.len() as u32);
304 assert_eq!(metadata_count, merkle_extents.len() as u32);
305 assert!(!encrypted);
306 (offset, extent_count, metadata_count)
307 }
308 _ => panic!("Expected Mappings command"),
309 };
310
311 let total_extents = cmd1_extent_count + cmd1_metadata_count;
313 let buffer = cmd1_raw.payload_slice(cmd1_offset, total_extents * 8).to_vec();
314
315 let data_extents_container = Extents::try_new(&data_extents, 0).unwrap();
316 let merkle_extents_container = Extents::try_new(&merkle_extents, 0).unwrap();
317 let mut expected_payload = Vec::new();
318 for val in Extents::encode_extents(&data_extents_container)
319 .chain(Extents::encode_extents(&merkle_extents_container))
320 {
321 expected_payload.extend_from_slice(&val.to_le_bytes());
322 }
323 assert_eq!(buffer, expected_payload);
324 cmd1_raw.pop().expect("Failed pop_commit");
325
326 let cmd2_raw = receiver.peek().expect("peek failed");
327 let cmd2 = MappingCommand::try_from(*cmd2_raw).expect("try_from failed");
328 match cmd2 {
329 MappingCommand::CloseBlob { key } => assert_eq!(key, 1),
330 _ => panic!("Expected CloseBlob command"),
331 };
332 cmd2_raw.pop().expect("Failed pop_commit");
333 });
334
335 let server_task = async move {
336 let key = 1;
337 let size = session.open_blob(key, hash).await.expect("open_blob failed");
338 assert_eq!(size, data.len() as u64);
339
340 session.close_blob(key).await.expect("close_blob failed on existing key");
341
342 std::mem::drop(session);
343 };
344
345 futures::join!(receiver_task, server_task);
346
347 fixture.close().await;
348 }
349
350 #[fuchsia::test]
351 async fn test_missing_blob() {
352 let fixture = new_blob_fixture().await;
353 let blob_dir = fixture
354 .volume()
355 .root()
356 .clone()
357 .as_node()
358 .into_any()
359 .downcast::<BlobDirectory>()
360 .expect("Failed to downcast");
361
362 let vmo = zx::Vmo::create(MAPPING_VMO_SIZE).unwrap();
363 let sender =
364 AsyncSender::<RawMappingCommand>::new(vmo, 8, PENDING_COMMANDS_CAPACITY).unwrap();
365 let mut session = BlobMappingSession::new(blob_dir, sender);
366 let hash = Hash::from([1u8; 32]);
367 session
368 .open_blob(1, hash)
369 .await
370 .expect_err("open_blob should fail with blob that doesn't exist");
371
372 std::mem::drop(session);
373 fixture.close().await;
374 }
375
376 #[fuchsia::test]
377 async fn test_invalid_key() {
378 let fixture = new_blob_fixture().await;
379 let blob_dir = fixture
380 .volume()
381 .root()
382 .clone()
383 .as_node()
384 .into_any()
385 .downcast::<BlobDirectory>()
386 .expect("Failed to downcast");
387
388 let vmo = zx::Vmo::create(MAPPING_VMO_SIZE).unwrap();
389 let sender =
390 AsyncSender::<RawMappingCommand>::new(vmo, 8, PENDING_COMMANDS_CAPACITY).unwrap();
391 let mut session = BlobMappingSession::new(blob_dir, sender);
392 session.close_blob(42).await.expect("close_blob should return Ok with invalid key");
393 std::mem::drop(session);
394
395 fixture.close().await;
396 }
397
398 #[fuchsia::test]
401 async fn test_unlink_open_blob() {
402 let fixture = new_blob_fixture().await;
403 let data = vec![77u8; 8192];
404 let hash = fixture.write_blob(&data, CompressionMode::Never).await;
405 let hash_string = format!("{}", hash);
406
407 let object_id = fixture.get_blob_handle(&hash_string).await.object_id();
408
409 let blob_dir = fixture
410 .volume()
411 .root()
412 .clone()
413 .as_node()
414 .into_any()
415 .downcast::<BlobDirectory>()
416 .expect("Failed to downcast root directory to BlobDirectory");
417
418 let vmo = zx::Vmo::create(MAPPING_VMO_SIZE).unwrap();
419 let sender =
420 AsyncSender::<RawMappingCommand>::new(vmo, 8, PENDING_COMMANDS_CAPACITY).unwrap();
421 let mut session = BlobMappingSession::new(blob_dir, sender);
422
423 let key = 1;
424 session.open_blob(key, hash).await.expect("open_blob failed");
425
426 fixture
428 .root()
429 .unlink(&hash_string, &UnlinkOptions::default())
430 .await
431 .expect("FIDL failed")
432 .expect("unlink failed");
433
434 fixture.fs().graveyard().flush().await;
436
437 assert!(
439 ObjectStore::open_object(
440 fixture.volume().volume(),
441 object_id,
442 HandleOptions::default(),
443 None
444 )
445 .await
446 .is_ok(),
447 "Blob was purged from store while still open in BlobMappingSession"
448 );
449
450 session.close_blob(key).await.expect("close_blob failed");
452
453 fixture.fs().graveyard().flush().await;
455
456 assert!(
458 ObjectStore::open_object(
459 fixture.volume().volume(),
460 object_id,
461 HandleOptions::default(),
462 None
463 )
464 .await
465 .is_err(),
466 "Blob should have been purged from store after close_blob"
467 );
468
469 drop(session);
470 fixture.close().await;
471 }
472
473 #[fuchsia::test]
474 async fn test_mapping_provider_and_mapping_session() {
475 let fixture = new_blob_fixture().await;
476 let data = vec![42; 8192];
477 let hash = fixture.write_blob(&data, CompressionMode::Never).await;
478
479 let blob_dir = fixture
480 .volume()
481 .root()
482 .clone()
483 .as_node()
484 .into_any()
485 .downcast::<BlobDirectory>()
486 .expect("Failed to downcast root directory to BlobDirectory");
487
488 let scope = blob_dir.volume().scope().clone();
489 let server = Arc::new(
490 BlobMappingProvider::new(blob_dir).expect("Failed to create BlobMappingProvider"),
491 );
492
493 let (provider_proxy, provider_server_end) =
495 fidl::endpoints::create_proxy::<fmapping::MappingProviderMarker>();
496 scope.spawn(async move {
497 server.handle_mapping_provider_requests(provider_server_end.into_stream()).await;
498 });
499
500 let (session_proxy, session_server_end) =
502 fidl::endpoints::create_proxy::<fmapping::MappingSessionMarker>();
503 let _shared_vmo = provider_proxy
504 .open_session(session_server_end)
505 .await
506 .expect("open_session failed")
507 .expect("vmo returned an error");
508
509 let not_found_profile_err = session_proxy
510 .get_profile(".boot")
511 .await
512 .expect("get_profile wire call failed")
513 .expect_err("expected get_profile(.boot) to return NOT_FOUND");
514 assert_eq!(not_found_profile_err, zx::Status::NOT_FOUND.into_raw());
515
516 let profile_data = vec![0xABu8; 4096];
517 let write_vmo = zx::Vmo::create(profile_data.len() as u64).expect("create vmo failed");
518 write_vmo.write(&profile_data, 0).expect("vmo write failed");
519 session_proxy
520 .write_profile(".boot", write_vmo)
521 .await
522 .expect("write_profile wire call failed")
523 .expect("write_profile returned an error");
524
525 let read_vmo = session_proxy
526 .get_profile(".boot")
527 .await
528 .expect("get_profile wire call failed")
529 .expect("get_profile returned an error");
530 assert_eq!(
531 read_vmo.read_to_vec::<u8>(0, profile_data.len() as u64).expect("vmo read failed"),
532 profile_data
533 );
534
535 let id: [u8; 32] = hash.into();
536 let key = 1;
537 let size = session_proxy
538 .open(key, &id)
539 .await
540 .expect("open failed")
541 .expect("open explicitly returned an error");
542
543 assert_eq!(size, 8192);
544
545 session_proxy
546 .close(key)
547 .await
548 .expect("close failed")
549 .expect("close explicitly returned an error");
550
551 let bad_length_id = vec![1u8, 2, 3];
555 let invalid_args_err = session_proxy
556 .open(key, &bad_length_id)
557 .await
558 .expect("open wire call failed")
559 .unwrap_err();
560 assert_eq!(invalid_args_err, zx::Status::INVALID_ARGS.into_raw());
561
562 let not_found_id = [0u8; 32];
564 let not_found_err = session_proxy
565 .open(key, ¬_found_id)
566 .await
567 .expect("open wire call failed")
568 .unwrap_err();
569 assert_eq!(not_found_err, zx::Status::NOT_FOUND.into_raw());
570
571 session_proxy.close(123).await.expect("close wire call failed").expect("close failed");
575
576 fixture.close().await;
577 }
578
579 struct DeviceBlockService {
580 device: Arc<dyn Device>,
581 }
582
583 impl mapping::reader::BlockService for DeviceBlockService {
584 fn allocate_buffer(&self, max_len: usize) -> OwnedBuffer {
585 let block_size = self.device.block_size() as usize;
586 let nblocks = (std::cmp::max(max_len, block_size) + block_size - 1) / block_size;
587 let aligned_len = nblocks * block_size;
588 let pool_size = nblocks.next_power_of_two() * block_size;
589 let buffer_source = BufferSource::new(pool_size);
590 let allocator = BufferAllocator::new(block_size, buffer_source);
591 Arc::new(allocator).allocate_buffer_sync_owned(aligned_len)
592 }
593
594 fn read_blocks(
595 &self,
596 device_offset: u64,
597 mut dest_buffer: OwnedBuffer,
598 on_complete: Box<dyn FnOnce(Result<OwnedBuffer, anyhow::Error>) + Send>,
599 ) -> Result<(), anyhow::Error> {
600 let device = self.device.clone();
601 futures::executor::block_on(async move {
602 let res = device.read(device_offset, dest_buffer.as_mut()).await;
603 on_complete(res.map(|_| dest_buffer));
604 });
605 Ok(())
606 }
607 }
608
609 async fn run_blob_mapping_test(
610 fixture: &TestFixture,
611 test_data: &[u8],
612 mode: CompressionMode,
613 min_data_extents: usize,
614 min_merkle_extents: usize,
615 ) {
616 let hash = fixture.write_blob(test_data, mode).await;
617 run_blob_mapping_test_with_hash(
618 fixture,
619 hash,
620 test_data,
621 min_data_extents,
622 min_merkle_extents,
623 )
624 .await;
625 }
626
627 async fn write_blob_chunked(fx: &TestFixture, data: &[u8], mode: CompressionMode) -> Hash {
628 let hash = fuchsia_merkle::root_from_slice(data);
629 let compressed_data = Type1Blob::generate(data, mode);
630 let writer = fx.create_blob(&hash.into(), false).await.expect("create blob failed");
631 let vmo = writer
632 .get_vmo(compressed_data.len() as u64)
633 .await
634 .expect("transport error on get_vmo")
635 .expect("failed to get vmo");
636 let vmo_size = vmo.get_size().expect("failed to get vmo size");
637
638 let chunk_size = 512;
640 let mut write_offset = 0u64;
641 let mut bytes_left = compressed_data.len() as u64;
642 while bytes_left > 0 {
643 let chunk_len = std::cmp::min(bytes_left, chunk_size);
644 vmo.write(
645 &compressed_data[write_offset as usize..(write_offset + chunk_len) as usize],
646 write_offset % vmo_size,
647 )
648 .expect("failed to write to vmo");
649 let _ = writer
650 .bytes_ready(chunk_len)
651 .await
652 .expect("transport error on bytes_ready")
653 .expect("failed to write data to vmo");
654 write_offset += chunk_len;
655 bytes_left -= chunk_len;
656 }
657 hash
658 }
659
660 async fn run_blob_mapping_test_with_hash(
661 fixture: &TestFixture,
662 hash: Hash,
663 uncompressed_data: &[u8],
664 min_data_extents: usize,
665 min_merkle_extents: usize,
666 ) {
667 let blob_dir = fixture
668 .volume()
669 .root()
670 .clone()
671 .as_node()
672 .into_any()
673 .downcast::<BlobDirectory>()
674 .expect("Failed to downcast root directory to BlobDirectory");
675
676 let scope = blob_dir.volume().scope().clone();
677 let server = Arc::new(
678 BlobMappingProvider::new(blob_dir).expect("Failed to create BlobMappingProvider"),
679 );
680
681 let (provider_proxy, provider_server_end) =
682 fidl::endpoints::create_proxy::<fmapping::MappingProviderMarker>();
683 scope.spawn(async move {
684 server.handle_mapping_provider_requests(provider_server_end.into_stream()).await;
685 });
686
687 let (session_proxy, session_server_end) =
688 fidl::endpoints::create_proxy::<fmapping::MappingSessionMarker>();
689 let mapping_vmo = provider_proxy
690 .open_session(session_server_end)
691 .await
692 .expect("open_session failed")
693 .expect("vmo returned an error");
694
695 let mut receiver =
696 vmo_fifo::Receiver::<RawMappingCommand>::new(mapping_vmo, PENDING_COMMANDS_CAPACITY)
697 .expect("Failed to create receiver");
698
699 let device = fixture.fs().device().clone();
700 let service: Arc<dyn mapping::reader::BlockService> =
701 Arc::new(DeviceBlockService { device });
702
703 let port = zx::Port::create();
704 let delivery_queue = zx::Vmo::create(mapping::DELIVERY_VMO_SIZE)
705 .expect("Failed to create delivery_queue VMO");
706 let pager = Arc::new(zx::Pager::create(zx::PagerOptions::empty()).unwrap());
707 let vmo_provider = Arc::new(blob_pager_and_verifier::TestVmoProvider::new(
708 pager.clone(),
709 delivery_queue.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
710 ));
711 let delivery_receiver = vmo_fifo::Receiver::<mapping::RawDeliveryCommand>::new(
712 delivery_queue.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
713 mapping::PENDING_DELIVERY_COMMANDS_CAPACITY,
714 )
715 .unwrap();
716 let _delivery_processor = blob_pager_and_verifier::DeliveryQueueProcessor::spawn(
717 delivery_receiver,
718 vmo_provider.clone(),
719 delivery_queue.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
720 )
721 .unwrap();
722
723 let verifier = block_server::verifier::Verifier::new(delivery_queue);
724 let files = Arc::new(mapping::Files::new(
725 service,
726 verifier,
727 port.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
728 ));
729
730 let id: [u8; 32] = hash.into();
731 let key = 1;
732 let blob_size = session_proxy
733 .open(key, &id)
734 .await
735 .expect("open failed")
736 .expect("open explicitly returned an error");
737
738 assert_eq!(blob_size as usize, uncompressed_data.len());
739
740 let msg = receiver.peek().expect("Failed to peek message");
741 if let Ok(MappingCommand::Mappings { extent_count, metadata_count, .. }) =
742 MappingCommand::try_from(*msg)
743 {
744 if (extent_count as usize) < min_data_extents
745 || (metadata_count as usize) < min_merkle_extents
746 {
747 panic!(
748 "EXTENTS MISMATCH: extent_count = {}, metadata_count = {}, \
749 required min_data = {}, min_merkle = {}",
750 extent_count, metadata_count, min_data_extents, min_merkle_extents
751 );
752 }
753 }
754 mapping::process_mapping_command(&msg, &files).expect("process_mapping_command failed");
755 msg.pop().expect("pop failed");
756
757 let paged_vmo = pager.create_vmo(zx::VmoOptions::empty(), &port, key, blob_size).unwrap();
758 vmo_provider
759 .register_vmo(key, paged_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
760
761 let _pager_thread = files.spawn_pager_thread();
762
763 let (tx, rx) = oneshot::channel();
764 let len = uncompressed_data.len();
765 std::thread::spawn(move || {
766 let mut buf = vec![0u8; len];
767 paged_vmo.read(&mut buf, 0).expect("paged vmo read failed");
768 let _ = tx.send(buf);
769 });
770
771 let read_bytes = rx.await.unwrap();
772 assert_eq!(&read_bytes[..], &uncompressed_data[..]);
773
774 session_proxy.close(key).await.expect("close failed").expect("close error");
775 let msg = receiver.peek().expect("Failed to peek close message");
776 mapping::process_mapping_command(&msg, &files).expect("process_mapping_command failed");
777 msg.pop().expect("pop failed");
778 }
779
780 async fn fragment_free_space(
781 fixture: TestFixture,
782 block_size: usize,
783 anchor_size: Option<usize>,
784 ) -> TestFixture {
785 let mut data = vec![0u8; block_size];
786 let mut hashes = Vec::new();
787 let mut i = 0usize;
788 loop {
789 rand::fill(&mut data[..]);
790 data[..8].copy_from_slice(&(i as u64).to_le_bytes());
791 let hash = fuchsia_merkle::root_from_slice(&data);
792 let delivery_data = Type1Blob::generate(&data, CompressionMode::Never);
793 let writer = match fixture.create_blob(&hash.into(), false).await {
794 Ok(w) => w,
795 Err(_) => break,
796 };
797 if let Ok(mut blob_writer) =
798 BlobWriter::create(writer, delivery_data.len() as u64).await
799 {
800 if blob_writer.write(&delivery_data).await.is_err() {
801 break;
802 }
803 } else {
804 break;
805 }
806 hashes.push(hash);
807 i += 1;
808 }
809
810 let root = fixture.root();
812 for ix in (0..hashes.len()).step_by(2) {
813 let _ = root.unlink(&format!("{}", hashes[ix]), &UnlinkOptions::default()).await;
814 }
815
816 let device = fixture.close().await;
818 let fixture = open_blob_fixture(device).await;
819
820 if let Some(size) = anchor_size {
821 let anchor_data = vec![0u8; size];
822 let _ = fixture.write_blob(&anchor_data, CompressionMode::Never).await;
823 }
824
825 fixture
826 }
827
828 #[fuchsia::test]
829 async fn test_fxfs_blob_mapping_uncompressed() {
830 let fixture = new_blob_fixture().await;
831 let test_data = vec![123u8; 8192];
832 run_blob_mapping_test(&fixture, &test_data, CompressionMode::Never, 1, 0).await;
833 fixture.close().await;
834 }
835
836 #[fuchsia::test]
837 async fn test_fxfs_blob_mapping_compressed() {
838 let fixture = new_blob_fixture().await;
839 let mut test_data = vec![0u8; 16384];
841 for i in 0..test_data.len() {
842 test_data[i] = ((i / 64) % 256) as u8;
843 }
844 run_blob_mapping_test(&fixture, &test_data, CompressionMode::Always, 1, 0).await;
845 fixture.close().await;
846 }
847
848 #[fuchsia::test]
849 async fn test_fxfs_blob_mapping_fragmented_data() {
850 let fixture = new_blob_fixture().await;
851 let fixture = fragment_free_space(fixture, 32768, Some(1_000_000)).await;
852
853 let mut uncompressed_data = vec![0u8; 262_144];
854 rand::fill(&mut uncompressed_data[..]);
855 let hash1 = write_blob_chunked(&fixture, &uncompressed_data, CompressionMode::Never).await;
856 run_blob_mapping_test_with_hash(&fixture, hash1, &uncompressed_data, 2, 0).await;
857 fixture
858 .root()
859 .unlink(&format!("{}", hash1), &UnlinkOptions::default())
860 .await
861 .unwrap()
862 .unwrap();
863
864 let mut compressed_data = vec![0u8; 262_144];
865 rand::fill(&mut compressed_data[..]);
866 let hash2 = write_blob_chunked(&fixture, &compressed_data, CompressionMode::Always).await;
867 run_blob_mapping_test_with_hash(&fixture, hash2, &compressed_data, 2, 0).await;
868
869 fixture.close().await;
870 }
871
872 #[fuchsia::test]
873 async fn test_blob_mapping_provider_large_extent() {
874 let fixture = new_blob_fixture().await;
875 let data = vec![42; 8192];
876 let hash = fixture.write_blob(&data, CompressionMode::Never).await;
877
878 let blob_dir = fixture
879 .volume()
880 .root()
881 .clone()
882 .as_node()
883 .into_any()
884 .downcast::<BlobDirectory>()
885 .expect("Failed to downcast root directory to BlobDirectory");
886
887 let node = blob_dir
888 .open_blob(&hash.into())
889 .await
890 .expect("Failed to open blob in Fxfs")
891 .expect("open_blob returned None instead of node");
892 let object_id = node.object_id();
893 let original_extents =
894 node.get_mapping_extents().await.expect("Failed to retrieve extents");
895 assert_eq!(original_extents.data.len(), 1);
896 let start_dev = original_extents.data[0].device_offset().unwrap();
897 drop(node);
898
899 let large_len = (0x3fff_ffffu64 + 5) * mapping::BLOCK_SIZE;
903 let key = ObjectKey::extent(object_id, AttributeId::DATA, 0..large_len);
904 let lower_bound = key.key_for_merge_into();
905 fixture.volume().volume().store().tree().merge_into(
906 Item::new(key, ObjectValue::Extent(ExtentValue::new_raw(start_dev, 0))),
907 &lower_bound,
908 );
909
910 let vmo = zx::Vmo::create(MAPPING_VMO_SIZE).unwrap();
911 let client_mapping = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
912 let sender =
913 AsyncSender::<RawMappingCommand>::new(vmo, 8, PENDING_COMMANDS_CAPACITY).unwrap();
914 let mut session = BlobMappingSession::new(blob_dir, sender);
915
916 let merkle_extents = original_extents.merkle;
917 let receiver_task = fasync::unblock(move || {
918 let mut receiver = Receiver::<RawMappingCommand>::new(client_mapping, 256)
919 .expect("Failed to create the Receiver wrapper");
920
921 let cmd1_raw = receiver.peek().expect("peek failed");
922 let cmd1 = MappingCommand::try_from(*cmd1_raw).expect("try_from failed");
923
924 let (cmd1_offset, cmd1_extent_count, cmd1_metadata_count) = match cmd1 {
925 MappingCommand::Mappings {
926 key,
927 offset,
928 stored_size: _,
929 device_offset: _,
930 metadata_count,
931 extent_count,
932 encrypted,
933 } => {
934 assert_eq!(key, 1);
935 assert_eq!(extent_count, 2);
936 assert_eq!(metadata_count, merkle_extents.len() as u32);
937 assert!(!encrypted);
938 (offset, extent_count, metadata_count)
939 }
940 _ => panic!("Expected Mappings command"),
941 };
942
943 let total_extents = cmd1_extent_count + cmd1_metadata_count;
944 let buffer = cmd1_raw.payload_slice(cmd1_offset, total_extents * 8).to_vec();
945
946 let expected_data_extents =
947 Extents::try_new([mapping::Extent::new(0..large_len, Some(start_dev))], 0).unwrap();
948 let expected_merkle_extents = Extents::try_new(&merkle_extents, 0).unwrap();
949 let mut expected_payload = Vec::new();
950 for val in Extents::encode_extents(&expected_data_extents)
951 .chain(Extents::encode_extents(&expected_merkle_extents))
952 {
953 expected_payload.extend_from_slice(&val.to_le_bytes());
954 }
955 assert_eq!(buffer, expected_payload);
956 cmd1_raw.pop().expect("Failed pop_commit");
957
958 let cmd2_raw = receiver.peek().expect("peek failed");
959 let cmd2 = MappingCommand::try_from(*cmd2_raw).expect("try_from failed");
960 match cmd2 {
961 MappingCommand::CloseBlob { key } => assert_eq!(key, 1),
962 _ => panic!("Expected CloseBlob command"),
963 };
964 cmd2_raw.pop().expect("Failed pop_commit");
965 });
966
967 let server_task = async move {
968 let key = 1;
969 let size = session.open_blob(key, hash).await.expect("open_blob failed");
970 assert_eq!(size, data.len() as u64);
971 session.close_blob(key).await.expect("close_blob failed");
972 std::mem::drop(session);
973 };
974
975 futures::join!(receiver_task, server_task);
976
977 fixture.close().await;
978 }
979}