1use crate::reader::{BlockService, read_aligned_range};
6use crate::{
7 BLOCK_SIZE, ENCRYPTION_KEY_SIZE, Extents, MappingCommand, NullPageRequest, PageRequest,
8 RawMappingCommand,
9};
10use anyhow::{Error, anyhow, bail};
11use blob_metadata::{BlobFormat, BlobMetadata, MerkleLeaves};
12use byteorder::{LittleEndian, ReadBytesExt};
13use delivery_blob::compression::{
14 CompressionAlgorithm, CompressionInfo, DataBuffer, StreamingDecompressor,
15};
16use fuchsia_sync::Mutex;
17use futures::channel::oneshot;
18use fxfs_crypto::{Cipher, FxfsCipher, UnwrappedKey};
19use std::borrow::Borrow;
20use std::cmp::min;
21use std::collections::hash_map::{Entry, HashMap};
22use std::ops::{ControlFlow, Range};
23use std::sync::Arc;
24use storage_profile::{FileMessage, Message as _};
25use vmo_fifo::Message;
26use zx::sys::zx_page_request_command_t::ZX_PAGER_VMO_READ;
27
28pub const TARGET_READ_AHEAD_SIZE: u64 = 128 * 1024;
33
34pub const DEFAULT_VERIFICATION_ALIGNMENT: u64 = 32 * 1024;
37
38fn read_ahead_size_for_chunk_size(compression_chunk_size: u64, target_read_ahead_size: u64) -> u64 {
42 if compression_chunk_size >= target_read_ahead_size {
43 compression_chunk_size
44 } else {
45 (target_read_ahead_size / compression_chunk_size) * compression_chunk_size
46 }
47}
48
49#[derive(Default)]
51pub enum Transform {
52 #[default]
54 None,
55 Compressed(CompressionInfo),
57 Encrypted(Arc<dyn Cipher>),
59}
60
61impl std::fmt::Debug for Transform {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 Self::None => write!(f, "Transform::None"),
65 Self::Compressed(_) => write!(f, "Transform::Compressed(..)"),
66 Self::Encrypted(cipher) => f.debug_tuple("Transform::Encrypted").field(cipher).finish(),
67 }
68 }
69}
70
71impl From<Option<CompressionInfo>> for Transform {
72 fn from(info: Option<CompressionInfo>) -> Self {
73 match info {
74 Some(info) => Self::Compressed(info),
75 None => Self::None,
76 }
77 }
78}
79
80impl From<CompressionInfo> for Transform {
81 fn from(info: CompressionInfo) -> Self {
82 Self::Compressed(info)
83 }
84}
85
86impl From<Arc<dyn Cipher>> for Transform {
87 fn from(cipher: Arc<dyn Cipher>) -> Self {
88 Self::Encrypted(cipher)
89 }
90}
91
92#[derive(Clone)]
93struct FileCompression(Arc<File>);
94
95impl Borrow<CompressionInfo> for FileCompression {
96 fn borrow(&self) -> &CompressionInfo {
97 self.0.compression_info().expect("compression_info missing")
98 }
99}
100
101pub struct File {
103 extents: Extents,
104 uncompressed_size: u64,
105 transform: Transform,
106}
107
108impl File {
109 pub fn new(extents: Extents, uncompressed_size: u64, transform: Transform) -> Self {
110 Self { extents, uncompressed_size, transform }
111 }
112
113 pub fn extents(&self) -> &Extents {
115 &self.extents
116 }
117
118 pub fn uncompressed_size(&self) -> u64 {
120 self.uncompressed_size
121 }
122
123 pub fn transform(&self) -> &Transform {
125 &self.transform
126 }
127
128 pub fn compression_info(&self) -> Option<&CompressionInfo> {
130 match &self.transform {
131 Transform::Compressed(info) => Some(info),
132 _ => None,
133 }
134 }
135
136 pub fn cipher(&self) -> Option<&Arc<dyn Cipher>> {
138 match &self.transform {
139 Transform::Encrypted(cipher) => Some(cipher),
140 _ => None,
141 }
142 }
143
144 pub fn read_ahead_size(&self) -> u64 {
146 match &self.transform {
147 Transform::Compressed(info) => {
148 read_ahead_size_for_chunk_size(info.chunk_size(), TARGET_READ_AHEAD_SIZE)
149 }
150 Transform::None | Transform::Encrypted(_) => TARGET_READ_AHEAD_SIZE,
151 }
152 }
153
154 pub fn verification_alignment(&self) -> u64 {
157 match &self.transform {
158 Transform::Compressed(info) => info.chunk_size(),
159 Transform::None | Transform::Encrypted(_) => DEFAULT_VERIFICATION_ALIGNMENT,
160 }
161 }
162
163 pub fn read_range(
165 self: &Arc<Self>,
166 service: &(impl BlockService + ?Sized),
167 page_request: impl PageRequest,
168 ) {
169 self.read_range_with_recording(self.read_ahead_size(), service, page_request, |_| {});
170 }
171
172 pub fn read_range_with_alignment(
179 self: &Arc<Self>,
180 window_alignment: u64,
181 service: &(impl BlockService + ?Sized),
182 page_request: impl PageRequest,
183 ) {
184 self.read_range_with_recording(window_alignment, service, page_request, |_| {});
185 }
186
187 fn read_range_with_recording(
188 self: &Arc<Self>,
189 window_alignment: u64,
190 service: &(impl BlockService + ?Sized),
191 mut page_request: impl PageRequest,
192 record_offset: impl Fn(u64),
193 ) {
194 let page_size = zx::system_get_page_size() as u64;
195 let original_range = page_request.range();
196 if original_range.is_empty() {
197 return;
198 }
199
200 let page_aligned_size = self.uncompressed_size.next_multiple_of(page_size);
201 if original_range.start >= page_aligned_size {
202 return;
203 }
204
205 let chunk_size = self.verification_alignment();
206 let read_range = (original_range.start / window_alignment) * window_alignment
207 ..std::cmp::min(
208 original_range.end.next_multiple_of(window_alignment),
209 page_aligned_size,
210 );
211 if page_request.prepare(read_range.clone(), chunk_size as usize).is_err() {
212 return;
213 }
214
215 for offset in (read_range.start..read_range.end).step_by(window_alignment as usize) {
216 record_offset(offset);
217 }
218
219 match &self.transform {
220 Transform::None => {
221 let mut current_offset = read_range.start;
222 let uncompressed_size = self.uncompressed_size;
223
224 read_aligned_range(&self.extents, read_range, service, move |res| {
225 let buffer = match res {
226 Ok(buffer) => buffer,
227 Err(error) => {
228 if !is_cancelled(&error) {
229 log::error!(error:?; "Failed to read blocks for mapped file");
230 }
231 return ControlFlow::Break(());
232 }
233 };
234 let valid_len =
235 min(buffer.len() as u64, uncompressed_size.saturating_sub(current_offset))
236 as usize;
237 let dest = page_request.mut_ptr_slice().subslice_mut(0..buffer.len());
238 let (mut head, mut tail) = dest.split_at_mut(valid_len);
239 head.copy_from_ptr_slice(buffer.as_ptr_slice().subslice(0..valid_len));
240 tail.fill(0);
241 if page_request.commit(buffer.len()).is_err() {
242 return ControlFlow::Break(());
243 }
244 current_offset += buffer.len() as u64;
245 ControlFlow::Continue(())
246 });
247 }
248 Transform::Compressed(_) => {
249 let Ok((mut decompressor, aligned_range)) = StreamingDecompressor::new(
250 FileCompression(self.clone()),
251 self.uncompressed_size,
252 page_request,
253 ) else {
254 return;
257 };
258
259 read_aligned_range(&self.extents, aligned_range, service, move |res| {
260 let buffer = match res {
261 Ok(buffer) => buffer,
262 Err(error) => {
263 if !is_cancelled(&error) {
264 log::error!(
265 error:?;
266 "Failed to read blocks for compressed mapped file"
267 );
268 }
269 return ControlFlow::Break(());
270 }
271 };
272 if decompressor.push(buffer.as_ptr_slice()).is_err() {
273 return ControlFlow::Break(());
274 }
275 ControlFlow::Continue(())
276 });
277 }
278 Transform::Encrypted(cipher) => {
279 let mut current_offset = read_range.start;
280 let uncompressed_size = self.uncompressed_size;
281 let cipher = cipher.clone();
282
283 read_aligned_range(&self.extents, read_range, service, move |res| {
284 let buffer = match res {
285 Ok(buffer) => buffer,
286 Err(error) => {
287 if !is_cancelled(&error) {
288 log::error!(
289 error:?;
290 "Failed to read blocks for encrypted mapped file"
291 );
292 }
293 return ControlFlow::Break(());
294 }
295 };
296 let mut dest = page_request.mut_ptr_slice().subslice_mut(0..buffer.len());
297 if let Err(error) = cipher.decrypt_to(
299 0,
300 0,
301 0,
302 current_offset,
303 buffer.as_ptr_slice(),
304 dest.reborrow(),
305 ) {
306 log::error!(error:?; "Failed to decrypt buffer");
307 return ControlFlow::Break(());
308 }
309 let valid_len =
310 min(buffer.len() as u64, uncompressed_size.saturating_sub(current_offset))
311 as usize;
312 dest.subslice_mut(valid_len..buffer.len()).fill(0);
313 if page_request.commit(buffer.len()).is_err() {
314 return ControlFlow::Break(());
315 }
316 current_offset += buffer.len() as u64;
317 ControlFlow::Continue(())
318 });
319 }
320 }
321 }
322
323 pub fn read_child_range<D: DeliveryHandler>(
339 self: &Arc<Self>,
340 child_key: u64,
341 original_range: Range<u64>,
342 parent_file: &Arc<File>,
343 service: &(impl BlockService + ?Sized),
344 delivery_handler: &Arc<D>,
345 ) -> bool {
346 let page_size = zx::system_get_page_size() as u64;
347 if original_range.is_empty() {
348 return false;
349 }
350 let page_aligned_size = self.uncompressed_size.next_multiple_of(page_size);
351 if original_range.start >= page_aligned_size {
352 return false;
353 }
354 let read_ahead_size = self.read_ahead_size();
355 let raw_start = (original_range.start / read_ahead_size) * read_ahead_size;
356 let raw_end = min(original_range.end.next_multiple_of(read_ahead_size), page_aligned_size);
357 let child_read_range = raw_start..raw_end;
358 let parent_align = parent_file.verification_alignment();
359
360 let mut started_async_io = false;
361 for extent in self
362 .extents
363 .iter_extents(child_read_range.start)
364 .take_while(|e| e.logical_range().start < child_read_range.end)
365 {
366 let slice_start = std::cmp::max(extent.logical_range().start, child_read_range.start);
367 let slice_end = min(extent.logical_range().end, child_read_range.end);
368 if slice_start >= slice_end {
369 continue;
370 }
371 let slice_len = slice_end - slice_start;
372 match extent.device_extent() {
373 crate::DeviceExtent::Sparse => {
374 let _ =
375 delivery_handler.deliver_child_sparse(child_key, slice_start, slice_len);
376 }
377 crate::DeviceExtent::Block(dev_offset) => {
378 let parent_start = dev_offset + (slice_start - extent.logical_range().start);
379 let parent_byte_range = parent_start..(parent_start + slice_len);
380 let child_req = delivery_handler.get_child_page_request(
381 child_key,
382 slice_start,
383 parent_byte_range,
384 );
385 parent_file.read_range_with_alignment(parent_align, service, child_req);
386 started_async_io = true;
387 }
388 crate::DeviceExtent::InlineTail(dev_offset) => {
389 let tail_end_in_file = min(extent.logical_range().end, self.uncompressed_size);
390 let tail_length = tail_end_in_file.saturating_sub(slice_start) as u32;
391 delivery_handler.deliver_child_inline_tail(
392 dev_offset,
393 child_key,
394 slice_start,
395 tail_length,
396 );
397 }
398 }
399 }
400 started_async_io
401 }
402}
403
404struct LoadingSlot<R> {
405 requests: Vec<R>,
406 waiters: Vec<oneshot::Sender<Arc<File>>>,
407}
408
409impl<R> Default for LoadingSlot<R> {
410 fn default() -> Self {
411 Self { requests: Vec::new(), waiters: Vec::new() }
412 }
413}
414
415enum FileEntry<R> {
416 Loading(LoadingSlot<R>),
417 Loaded(Arc<File>),
418}
419
420impl<R> Default for FileEntry<R> {
421 fn default() -> Self {
422 Self::Loading(LoadingSlot::default())
423 }
424}
425
426pub trait DeliveryHandler: Send + Sync + 'static {
428 type Request: PageRequest;
429
430 fn get_page_request(self: &Arc<Self>, key: u64, range: Range<u64>) -> Self::Request;
432
433 fn get_child_page_request(
440 self: &Arc<Self>,
441 _child_key: u64,
442 _child_target_offset: u64,
443 _parent_byte_range: Range<u64>,
444 ) -> Self::Request {
445 unreachable!()
446 }
447
448 fn deliver_child_inline_tail(
452 self: &Arc<Self>,
453 _parent_byte_offset: u64,
454 _child_key: u64,
455 _child_target_offset: u64,
456 _tail_length: u32,
457 ) {
458 unreachable!()
459 }
460
461 fn deliver_child_sparse(
464 &self,
465 _child_key: u64,
466 _child_target_offset: u64,
467 _child_length: u64,
468 ) -> Result<(), Error> {
469 unreachable!()
470 }
471
472 fn register_blob(
475 &self,
476 _key: u64,
477 _merkle_leaves: &[[u8; 32]],
478 _read_alignment: usize,
479 ) -> Result<(), Error> {
480 Ok(())
481 }
482
483 fn unregister_file(&self, _key: u64) {}
485}
486
487pub struct NoopDeliveryHandler;
490
491impl DeliveryHandler for NoopDeliveryHandler {
492 type Request = NullPageRequest;
493
494 fn get_page_request(self: &Arc<Self>, _key: u64, _range: Range<u64>) -> NullPageRequest {
495 NullPageRequest
496 }
497}
498
499const PAGER_SHUTDOWN_KEY: u64 = 0;
500const PAGER_WAKE_KEY: u64 = 1;
501
502#[must_use = "Dropping PagerThread immediately stops the background pager thread"]
507pub struct PagerThread<S: ?Sized, D: DeliveryHandler> {
508 files: Arc<Files<S, D>>,
509 thread: Option<std::thread::JoinHandle<()>>,
510}
511
512impl<S: ?Sized, D: DeliveryHandler> Drop for PagerThread<S, D> {
513 fn drop(&mut self) {
514 let packet = zx::Packet::from_user_packet(
515 PAGER_SHUTDOWN_KEY,
516 0,
517 zx::UserPacket::from_u8_array([0; 32]),
518 );
519 let _ = self.files.port.queue(&packet);
520 if let Some(thread) = self.thread.take() {
521 let _ = thread.join();
522 }
523 }
524}
525
526const MAX_PROFILE_VMO_SIZE: u64 = 64 * 1024 * 1024;
529
530struct ProfileRecording {
531 vmo: zx::Vmo,
532 vmo_offset: u64,
533 buffer: Vec<u8>,
534}
535
536impl ProfileRecording {
537 fn new() -> Result<Self, zx::Status> {
538 let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0)?;
539 Ok(Self { vmo, vmo_offset: 0, buffer: Vec::with_capacity(BLOCK_SIZE as usize) })
540 }
541
542 fn record(&mut self, key: u64, offset: u64) {
543 const MSG_SIZE: usize = std::mem::size_of::<FileMessage>();
544 const { assert!((BLOCK_SIZE as usize).is_multiple_of(MSG_SIZE)) };
547
548 if self.vmo_offset >= MAX_PROFILE_VMO_SIZE {
549 return;
550 }
551 let mut buf = [0u8; MSG_SIZE];
552 FileMessage { id: key, offset }.encode_to((&mut buf[..]).into());
553 self.buffer.extend_from_slice(&buf);
554 if self.buffer.len() == BLOCK_SIZE as usize
555 && let Err(error) = self.flush()
556 {
557 log::warn!(error:?; "Failed to flush profile buffer to VMO");
558 }
559 }
560
561 fn flush(&mut self) -> Result<(), zx::Status> {
562 let new_size = self.vmo_offset + BLOCK_SIZE;
563 let res = self.vmo.set_size(new_size).and_then(|()| {
564 self.vmo.write(&self.buffer, self.vmo_offset)?;
565 self.vmo_offset = new_size;
566 Ok(())
567 });
568 self.buffer.clear();
569 res
570 }
571
572 fn finish(mut self) -> Result<zx::Vmo, zx::Status> {
573 if !self.buffer.is_empty() {
574 self.flush()?;
575 }
576 Ok(self.vmo)
577 }
578}
579
580pub struct Files<S: ?Sized, D: DeliveryHandler> {
582 service: Arc<S>,
583 delivery_handler: Arc<D>,
584 port: zx::Port,
585 parent: Option<Arc<File>>,
588 map: Mutex<HashMap<u64, FileEntry<D::Request>>>,
589 deferred_page_requests: Mutex<Vec<(u64, Arc<File>, D::Request)>>,
592 recording: Mutex<Option<ProfileRecording>>,
594}
595
596impl<S: BlockService + ?Sized + 'static, D: DeliveryHandler> Files<S, D> {
597 pub fn new(service: Arc<S>, delivery_handler: D, port: zx::Port) -> Self {
599 Self::new_with_shared_handler(service, Arc::new(delivery_handler), port, None)
600 }
601
602 pub fn new_with_shared_handler(
605 service: Arc<S>,
606 delivery_handler: Arc<D>,
607 port: zx::Port,
608 parent: Option<Arc<File>>,
609 ) -> Self {
610 Self {
611 service,
612 delivery_handler,
613 port,
614 parent,
615 map: Mutex::new(HashMap::new()),
616 deferred_page_requests: Mutex::new(Vec::new()),
617 recording: Mutex::new(None),
618 }
619 }
620
621 pub fn start_recording(&self) -> Result<(), zx::Status> {
629 let mut recording = self.recording.lock();
630 if recording.is_some() {
631 return Err(zx::Status::BAD_STATE);
632 }
633 *recording = Some(ProfileRecording::new()?);
634 Ok(())
635 }
636
637 pub fn is_recording(&self) -> bool {
638 self.recording.lock().is_some()
639 }
640
641 pub fn stop_recording(&self) -> Result<zx::Vmo, zx::Status> {
646 let recording = self.recording.lock().take().ok_or(zx::Status::BAD_STATE)?;
647 recording.finish()
648 }
649
650 pub fn abort_recording(&self) {
652 *self.recording.lock() = None;
653 }
654
655 pub fn delivery_handler(&self) -> &Arc<D> {
657 &self.delivery_handler
658 }
659
660 pub fn has_parent(&self) -> bool {
663 self.parent.is_some()
664 }
665
666 pub fn spawn_pager_thread(self: &Arc<Self>) -> PagerThread<S, D> {
668 let files = self.clone();
669 let thread = std::thread::spawn(move || {
670 files.run_pager_loop();
671 });
672 PagerThread { files: self.clone(), thread: Some(thread) }
673 }
674
675 fn dispatch_page_range(&self, key: u64, file: &Arc<File>, original_range: Range<u64>) {
678 if let Some(parent_file) = &self.parent {
679 let _ = file.read_child_range(
680 key,
681 original_range,
682 parent_file,
683 self.service.as_ref(),
684 &self.delivery_handler,
685 );
686 } else {
687 let req = self.delivery_handler.get_page_request(key, original_range);
688 self.dispatch_page_request(key, file, req);
689 }
690 }
691
692 fn dispatch_page_request(&self, key: u64, file: &Arc<File>, req: D::Request) {
696 let original_range = req.range();
697 if let Some(parent_file) = &self.parent {
698 drop(req);
699 let _ = file.read_child_range(
700 key,
701 original_range,
702 parent_file,
703 self.service.as_ref(),
704 &self.delivery_handler,
705 );
706 } else {
707 file.read_range_with_recording(
708 file.read_ahead_size(),
709 self.service.as_ref(),
710 req,
711 |offset| {
712 if let Some(recording) = self.recording.lock().as_mut() {
713 recording.record(key, offset);
714 }
715 },
716 );
717 }
718 }
719
720 fn run_pager_loop(self: &Arc<Self>) {
723 while let Ok(packet) = self.port.wait(zx::MonotonicInstant::INFINITE) {
724 match packet.contents() {
725 zx::PacketContents::User(_) => match packet.key() {
726 PAGER_SHUTDOWN_KEY => break,
727 PAGER_WAKE_KEY => {
728 let requests = std::mem::take(&mut *self.deferred_page_requests.lock());
729 for (key, file, req) in requests {
730 self.dispatch_page_request(key, &file, req);
731 }
732 }
733 _ => {}
734 },
735 zx::PacketContents::Pager(pager_packet)
736 if pager_packet.command() == ZX_PAGER_VMO_READ =>
737 {
738 self.handle_page_request(packet.key(), pager_packet.range());
739 }
740 _ => {}
741 }
742 }
743 }
744
745 pub fn service(&self) -> &Arc<S> {
747 &self.service
748 }
749
750 pub fn register_blob(
753 &self,
754 key: u64,
755 merkle_leaves: &[[u8; 32]],
756 read_alignment: usize,
757 ) -> Result<(), Error> {
758 self.delivery_handler.register_blob(key, merkle_leaves, read_alignment)
759 }
760
761 fn handle_page_request(&self, key: u64, range: Range<u64>) {
767 {
768 let map = self.map.lock();
769 if let Some(FileEntry::Loaded(file)) = map.get(&key) {
770 let file = Arc::clone(file);
771 drop(map);
772 self.dispatch_page_range(key, &file, range);
773 return;
774 }
775 }
776
777 let req = self.delivery_handler.get_page_request(key, range);
778 let mut map = self.map.lock();
779 match map.entry(key) {
780 Entry::Occupied(mut entry) => match entry.get_mut() {
781 FileEntry::Loaded(file) => {
782 let file = Arc::clone(file);
783 drop(map);
784 self.dispatch_page_request(key, &file, req);
785 }
786 FileEntry::Loading(slot) => {
787 slot.requests.push(req);
788 }
789 },
790 Entry::Vacant(entry) => {
791 entry.insert(FileEntry::Loading(LoadingSlot {
799 requests: vec![req],
800 waiters: Vec::new(),
801 }));
802 }
803 }
804 }
805
806 pub fn begin_loading(&self, key: u64) {
809 self.map.lock().entry(key).or_default();
810 }
811
812 fn insert(&self, key: u64, file: Arc<File>) {
815 let (reqs, waiters) = {
816 let mut map = self.map.lock();
817 let prev = map.insert(key, FileEntry::Loaded(file.clone()));
818 match prev {
819 Some(FileEntry::Loading(slot)) => (slot.requests, slot.waiters),
820 _ => (Vec::new(), Vec::new()),
821 }
822 };
823
824 for waiter in waiters {
825 let _ = waiter.send(file.clone());
826 }
827
828 if !reqs.is_empty() {
829 self.deferred_page_requests
836 .lock()
837 .extend(reqs.into_iter().map(|req| (key, file.clone(), req)));
838 let packet = zx::Packet::from_user_packet(
839 PAGER_WAKE_KEY,
840 0,
841 zx::UserPacket::from_u8_array([0; 32]),
842 );
843 let _ = self.port.queue(&packet);
844 }
845 }
846
847 pub fn get_file(&self, key: u64) -> Option<Arc<File>> {
849 let map = self.map.lock();
850 match map.get(&key) {
851 Some(FileEntry::Loaded(file)) => Some(file.clone()),
852 _ => None,
853 }
854 }
855
856 pub async fn wait_for_file(&self, key: u64) -> Result<Arc<File>, Error> {
859 let receiver = {
860 let mut map = self.map.lock();
861 match map.entry(key) {
862 Entry::Occupied(mut entry) => match entry.get_mut() {
863 FileEntry::Loaded(file) => return Ok(file.clone()),
864 FileEntry::Loading(slot) => {
865 let (sender, receiver) = oneshot::channel();
866 slot.waiters.push(sender);
867 receiver
868 }
869 },
870 Entry::Vacant(entry) => {
871 let (sender, receiver) = oneshot::channel();
872 entry.insert(FileEntry::Loading(LoadingSlot {
873 requests: Vec::new(),
874 waiters: vec![sender],
875 }));
876 receiver
877 }
878 }
879 };
880
881 receiver.await.map_err(|_| anyhow!("File loading cancelled"))
882 }
883
884 pub fn remove(&self, key: u64) {
886 if self.parent.is_none() {
887 self.delivery_handler.unregister_file(key);
888 }
889 self.map.lock().remove(&key);
890 }
891
892 #[cfg(test)]
894 pub fn is_loading(&self, key: u64) -> bool {
895 matches!(self.map.lock().get(&key), Some(FileEntry::Loading(_)))
896 }
897
898 #[cfg(test)]
901 pub fn has_queued_page_requests(&self, key: u64) -> bool {
902 matches!(
903 self.map.lock().get(&key),
904 Some(FileEntry::Loading(slot)) if !slot.requests.is_empty()
905 )
906 }
907
908 #[cfg(test)]
910 pub fn is_loaded(&self, key: u64) -> bool {
911 matches!(self.map.lock().get(&key), Some(FileEntry::Loaded(_)))
912 }
913}
914
915impl<S: BlockService + ?Sized + 'static> Files<S, NoopDeliveryHandler> {
916 pub fn new_without_pager(service: Arc<S>) -> Self {
918 Self::new(service, NoopDeliveryHandler, zx::Port::create())
919 }
920}
921
922pub(crate) const BLOB_METADATA_VERSION: u32 = 53;
925
926fn deserialize_blob_metadata(mut bytes: &[u8]) -> Result<BlobMetadata, anyhow::Error> {
928 use bincode::Options;
929 let options = bincode::DefaultOptions::new().allow_trailing_bytes();
930
931 let version = bytes.read_u32::<LittleEndian>()?;
932 if version < BLOB_METADATA_VERSION {
933 bail!(
934 "Unsupported blob metadata version: {} (expected >= {})",
935 version,
936 BLOB_METADATA_VERSION
937 );
938 }
939
940 options
941 .deserialize::<BlobMetadata>(bytes)
942 .map_err(|e| anyhow!("Failed to deserialize BlobMetadata: {e:?}"))
943}
944
945pub struct DecodedBlobMetadata {
948 pub uncompressed_size: u64,
949 pub compression_info: Option<CompressionInfo>,
950 pub merkle_leaves: MerkleLeaves,
951}
952
953fn is_cancelled(error: &Error) -> bool {
956 error.root_cause().downcast_ref::<zx::Status>() == Some(&zx::Status::CANCELED)
957}
958
959pub fn read_blob_metadata(
965 service: &(impl BlockService + ?Sized),
966 metadata_extents: &Extents,
967 stored_data_size: u64,
968 callback: impl FnOnce(DecodedBlobMetadata) + Send + 'static,
969) {
970 let mut total_metadata_len = 0u64;
971 for extent in metadata_extents.iter_extents(0) {
972 total_metadata_len += extent.len();
973 }
974 if total_metadata_len == 0 {
975 callback(DecodedBlobMetadata {
976 uncompressed_size: stored_data_size,
977 compression_info: None,
978 merkle_leaves: MerkleLeaves::new(),
979 });
980 return;
981 }
982
983 let mut callback = Some(callback);
984 let mut metadata_bytes = Some(Vec::with_capacity(total_metadata_len as usize));
985 read_aligned_range(metadata_extents, 0..total_metadata_len, service, move |res| {
986 let buffer = match res {
987 Ok(buffer) => buffer,
988 Err(error) => {
989 if !is_cancelled(&error) {
990 log::error!(error:?; "Failed to read metadata blocks");
991 }
992 return ControlFlow::Break(());
993 }
994 };
995 let bytes = metadata_bytes.as_mut().unwrap();
996 let copy_len = min(buffer.len(), total_metadata_len as usize - bytes.len());
997 buffer.as_ptr_slice().subslice(0..copy_len).append_to(bytes);
998 if bytes.len() < total_metadata_len as usize {
999 return ControlFlow::Continue(());
1000 }
1001
1002 let metadata_bytes = metadata_bytes.take().unwrap();
1003 let cb = callback.take().unwrap();
1004 let metadata = match deserialize_blob_metadata(&metadata_bytes) {
1005 Ok(metadata) => metadata,
1006 Err(error) => {
1007 log::error!(error:?; "Failed to deserialize BlobMetadata");
1008 return ControlFlow::Break(());
1009 }
1010 };
1011
1012 let merkle_leaves = metadata.merkle_leaves;
1013 let res = match metadata.format {
1014 BlobFormat::Uncompressed => DecodedBlobMetadata {
1015 uncompressed_size: stored_data_size,
1016 compression_info: None,
1017 merkle_leaves,
1018 },
1019 BlobFormat::ChunkedZstd { uncompressed_size, chunk_size, compressed_offsets } => {
1020 match CompressionInfo::new(
1021 chunk_size,
1022 stored_data_size,
1023 &compressed_offsets,
1024 CompressionAlgorithm::Zstd,
1025 ) {
1026 Ok(compression_info) => DecodedBlobMetadata {
1027 uncompressed_size,
1028 compression_info: Some(compression_info),
1029 merkle_leaves,
1030 },
1031 Err(error) => {
1032 log::error!(error:?; "Failed to parse Zstd CompressionInfo");
1033 return ControlFlow::Break(());
1034 }
1035 }
1036 }
1037 BlobFormat::ChunkedLz4 { uncompressed_size, chunk_size, compressed_offsets } => {
1038 match CompressionInfo::new(
1039 chunk_size,
1040 stored_data_size,
1041 &compressed_offsets,
1042 CompressionAlgorithm::Lz4,
1043 ) {
1044 Ok(compression_info) => DecodedBlobMetadata {
1045 uncompressed_size,
1046 compression_info: Some(compression_info),
1047 merkle_leaves,
1048 },
1049 Err(error) => {
1050 log::error!(error:?; "Failed to parse Lz4 CompressionInfo");
1051 return ControlFlow::Break(());
1052 }
1053 }
1054 }
1055 };
1056 cb(res);
1057 ControlFlow::Break(())
1058 });
1059}
1060
1061struct LoadingFileGuard<S: BlockService + ?Sized + 'static, D: DeliveryHandler> {
1073 files: Option<Arc<Files<S, D>>>,
1074 key: u64,
1075}
1076
1077impl<S: BlockService + ?Sized + 'static, D: DeliveryHandler> LoadingFileGuard<S, D> {
1078 fn commit(mut self, file: Arc<File>) {
1081 self.files.take().unwrap().insert(self.key, file);
1082 }
1083}
1084
1085impl<S: BlockService + ?Sized + 'static, D: DeliveryHandler> Drop for LoadingFileGuard<S, D> {
1086 fn drop(&mut self) {
1087 if let Some(files) = self.files.take() {
1088 files.remove(self.key);
1089 }
1090 }
1091}
1092
1093pub fn process_mapping_command<S: BlockService + ?Sized + 'static, D: DeliveryHandler>(
1097 msg: &Message<'_, RawMappingCommand>,
1098 files: &Arc<Files<S, D>>,
1099) -> Result<(), Error> {
1100 let cmd = **msg;
1101 match MappingCommand::try_from(cmd)? {
1102 MappingCommand::Mappings {
1103 key,
1104 offset,
1105 stored_size,
1106 device_offset,
1107 metadata_count,
1108 extent_count,
1109 encrypted,
1110 } => {
1111 if encrypted && metadata_count > 0 {
1112 bail!("Encrypted files cannot have blob metadata");
1113 }
1114 let extent_bytes_len = (extent_count as usize)
1115 .checked_mul(8)
1116 .ok_or_else(|| anyhow!("Overflow calculating extent byte length"))?;
1117 let metadata_bytes_len = (metadata_count as usize)
1118 .checked_mul(8)
1119 .ok_or_else(|| anyhow!("Overflow calculating metadata extent byte length"))?;
1120 let key_bytes_len = if encrypted { ENCRYPTION_KEY_SIZE } else { 0 };
1121 let total_bytes_len = extent_bytes_len
1122 .checked_add(metadata_bytes_len)
1123 .and_then(|len| len.checked_add(key_bytes_len))
1124 .ok_or_else(|| anyhow!("Overflow calculating total extent byte length"))?;
1125 let payload_len: u32 = total_bytes_len
1126 .try_into()
1127 .map_err(|_| anyhow!("Extent byte length exceeds u32"))?;
1128
1129 let payload_bytes = msg.payload_slice(offset, payload_len);
1130 let data_bytes = payload_bytes.subslice(0..extent_bytes_len);
1131 let data_extents = Extents::from_encoded(data_bytes.iter_as::<u64>(), device_offset)
1132 .ok_or_else(|| anyhow!("Failed to decode data extents"))?;
1133
1134 if encrypted {
1135 let key_bytes = payload_bytes.subslice(extent_bytes_len..total_bytes_len);
1136 let cipher_key = UnwrappedKey::new(key_bytes.to_vec());
1137 let cipher = Arc::new(FxfsCipher::new(&cipher_key)) as Arc<dyn Cipher>;
1138 let file =
1139 Arc::new(File::new(data_extents, stored_size, Transform::Encrypted(cipher)));
1140 files.insert(key, file);
1141 return Ok(());
1142 }
1143
1144 if files.has_parent() && metadata_count == 0 {
1145 let file = Arc::new(File::new(data_extents, stored_size, Transform::None));
1146 files.insert(key, file);
1147 return Ok(());
1148 }
1149
1150 let metadata_bytes = payload_bytes.subslice(extent_bytes_len..total_bytes_len);
1151 let metadata_extents =
1152 Extents::from_encoded(metadata_bytes.iter_as::<u64>(), device_offset)
1153 .ok_or_else(|| anyhow!("Failed to decode metadata extents"))?;
1154
1155 files.begin_loading(key);
1156 let service = files.service().clone();
1157 let guard = LoadingFileGuard { files: Some(files.clone()), key };
1158 let files_clone = files.clone();
1159 read_blob_metadata(service.as_ref(), &metadata_extents, stored_size, move |metadata| {
1160 let file = Arc::new(File::new(
1161 data_extents,
1162 metadata.uncompressed_size,
1163 metadata.compression_info.into(),
1164 ));
1165 if !files_clone.has_parent() {
1168 if let Err(error) = files_clone.register_blob(
1169 key,
1170 &metadata.merkle_leaves,
1171 file.verification_alignment() as usize,
1172 ) {
1173 log::error!(error:?; "Failed to register blob {key}");
1174 return;
1175 }
1176 }
1177 guard.commit(file);
1178 });
1179 Ok(())
1180 }
1181 MappingCommand::CloseBlob { key } => {
1182 files.remove(key);
1183 Ok(())
1184 }
1185 }
1186}
1187
1188#[cfg(test)]
1189mod tests {
1190 use super::*;
1191 use crate::reader::tests::FakeBlockService;
1192 use crate::reader::{MAX_READ_BUFFER_SIZE, OwnedBuffer};
1193 use crate::testing::{TestDeliveryHandler, TestVecBuffer};
1194 use crate::{BLOCK_SIZE, Extent};
1195 use anyhow::Error;
1196 use bincode::Options;
1197 use byteorder::WriteBytesExt;
1198 use delivery_blob::compression::{ChunkedArchiveOptions, CompressionAlgorithm};
1199 use fuchsia_async as fasync;
1200 use fxfs_crypto::{Cipher, FxfsCipher, UnwrappedKey};
1201 use std::sync::Arc;
1202 use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
1203 use storage_ptr_slice::MutPtrByteSlice;
1204
1205 fn serialize_metadata(metadata: &BlobMetadata) -> Vec<u8> {
1206 let mut bytes = Vec::new();
1207 bytes.write_u32::<LittleEndian>(BLOB_METADATA_VERSION).unwrap();
1208 bincode::DefaultOptions::new()
1209 .allow_trailing_bytes()
1210 .serialize_into(&mut bytes, metadata)
1211 .unwrap();
1212 bytes
1213 }
1214
1215 #[test]
1216 fn test_read_range_uncompressed() {
1217 let block_count = 8;
1218 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
1219 for (i, byte) in expected_data.iter_mut().enumerate() {
1220 *byte = (i % 255) as u8;
1221 }
1222 let service = FakeBlockService::new(expected_data.clone());
1223
1224 let extents = Extents::try_new([Extent::new(0..(8 * BLOCK_SIZE), Some(0))], 0).unwrap();
1225 let file = Arc::new(File::new(extents, 8 * BLOCK_SIZE, Transform::None));
1226
1227 let (page_request, rx) = TestVecBuffer::new_with_range(0..(8 * BLOCK_SIZE));
1228 file.read_range(&service, page_request);
1229
1230 assert_eq!(rx.commits(), vec![(0, (8 * BLOCK_SIZE) as usize)]);
1231 assert_eq!(rx.output(), expected_data);
1232 }
1233
1234 #[test]
1235 fn test_read_range_compressed_zstd() {
1236 let uncompressed_size = 32768 * 2 + 1024;
1237 let mut uncompressed_data = vec![0u8; uncompressed_size];
1238 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
1239 *byte = ((i * 7) % 255) as u8;
1240 }
1241
1242 let options =
1243 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Zstd };
1244 let archive =
1245 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
1246
1247 let mut compressed_offsets = vec![0];
1248 let mut compressed_data = vec![];
1249 for chunk in archive.chunks() {
1250 compressed_data.extend_from_slice(&chunk.compressed_data);
1251 compressed_offsets.push(compressed_data.len() as u64);
1252 }
1253 compressed_offsets.pop();
1254
1255 let chunk_size = archive.chunk_size();
1256 let stored_size = compressed_data.len() as u64;
1257 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
1258 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
1259 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
1260 let service = FakeBlockService::new(device_data);
1261
1262 let extents =
1263 Extents::try_new([Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1264 let compression_info = CompressionInfo::new(
1265 chunk_size as u64,
1266 stored_size,
1267 &compressed_offsets,
1268 CompressionAlgorithm::Zstd,
1269 )
1270 .unwrap();
1271 let file = Arc::new(File::new(extents, uncompressed_size as u64, compression_info.into()));
1272
1273 let dest_alloc_size = uncompressed_size.next_multiple_of(chunk_size);
1274 let (mut page_request, rx) = TestVecBuffer::new_with_range(0..(uncompressed_size as u64));
1275 page_request.data.resize(dest_alloc_size, 0);
1276 file.read_range(&service, page_request);
1277
1278 assert_eq!(
1279 rx.commits(),
1280 vec![
1281 (0, chunk_size),
1282 (chunk_size as u64, chunk_size),
1283 (chunk_size as u64 * 2, chunk_size)
1284 ]
1285 );
1286 assert_eq!(&rx.output()[..uncompressed_size], &uncompressed_data[..]);
1287 }
1288
1289 #[test]
1290 fn test_read_range_compressed_lz4_split_across_buffers() {
1291 let uncompressed_size = 32768 * 2;
1292 let mut uncompressed_data = vec![0u8; uncompressed_size];
1293 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
1294 *byte = ((i * 13) % 255) as u8;
1295 }
1296
1297 let options =
1298 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Lz4 };
1299 let archive =
1300 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
1301
1302 let mut compressed_offsets = vec![0];
1303 let mut compressed_data = vec![];
1304 for chunk in archive.chunks() {
1305 compressed_data.extend_from_slice(&chunk.compressed_data);
1306 compressed_offsets.push(compressed_data.len() as u64);
1307 }
1308 compressed_offsets.pop();
1309
1310 let chunk_size = archive.chunk_size();
1311 let stored_size = compressed_data.len() as u64;
1312 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
1313 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
1314 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
1315
1316 let service = FakeBlockService::new_with_cap(device_data, Some(4096));
1319
1320 let extents =
1321 Extents::try_new([Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1322 let compression_info = CompressionInfo::new(
1323 chunk_size as u64,
1324 stored_size,
1325 &compressed_offsets,
1326 CompressionAlgorithm::Lz4,
1327 )
1328 .unwrap();
1329 let file = Arc::new(File::new(extents, uncompressed_size as u64, compression_info.into()));
1330
1331 let (page_request, rx) = TestVecBuffer::new_with_range(0..(uncompressed_size as u64));
1332 file.read_range(&service, page_request);
1333
1334 assert_eq!(rx.commits(), vec![(0, chunk_size), (chunk_size as u64, chunk_size)]);
1335 assert_eq!(rx.output(), uncompressed_data);
1336 }
1337
1338 #[test]
1339 fn test_read_range_invalid_range_noop() {
1340 let service = FakeBlockService::new(vec![0u8; 8192]);
1341 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
1342 let file = Arc::new(File::new(extents, 8192, Transform::None));
1343
1344 let (page_request, rx) = TestVecBuffer::new_with_range(4096..4096);
1345 file.read_range(&service, page_request);
1347 assert_eq!(rx.commits().len(), 0);
1348 }
1349
1350 #[test]
1351 fn test_file_getters() {
1352 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
1353 let uncompressed_size = 8192u64;
1354
1355 let file_uncompressed = File::new(extents, uncompressed_size, Transform::None);
1356 assert_eq!(file_uncompressed.uncompressed_size(), 8192);
1357 assert!(matches!(file_uncompressed.transform(), Transform::None));
1358 assert!(file_uncompressed.compression_info().is_none());
1359 assert!(file_uncompressed.cipher().is_none());
1360
1361 let compression_info =
1362 CompressionInfo::new(32768, 4096, &[0], CompressionAlgorithm::Zstd).unwrap();
1363 let file_compressed = File::new(
1364 Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap(),
1365 uncompressed_size,
1366 compression_info.into(),
1367 );
1368 assert!(matches!(file_compressed.transform(), Transform::Compressed(_)));
1369 assert!(file_compressed.compression_info().is_some());
1370 assert!(file_compressed.cipher().is_none());
1371
1372 let key = UnwrappedKey::new(vec![0x42u8; 32]);
1373 let cipher: Arc<dyn Cipher> = Arc::new(FxfsCipher::new(&key));
1374 let file_encrypted = File::new(
1375 Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap(),
1376 uncompressed_size,
1377 cipher.into(),
1378 );
1379 assert!(matches!(file_encrypted.transform(), Transform::Encrypted(_)));
1380 assert!(file_encrypted.compression_info().is_none());
1381 assert!(file_encrypted.cipher().is_some());
1382 }
1383
1384 #[test]
1385 fn test_read_range_block_service_error_returns_err() {
1386 struct FailingBlockService;
1387 impl BlockService for FailingBlockService {
1388 fn allocate_buffer(&self, max_len: usize) -> storage_device::buffer::OwnedBuffer {
1389 FakeBlockService::new(vec![0u8; max_len]).allocate_buffer(max_len)
1390 }
1391 fn read_blocks(
1392 &self,
1393 _device_offset: u64,
1394 _dest_buffer: storage_device::buffer::OwnedBuffer,
1395 _on_complete: Box<
1396 dyn FnOnce(Result<storage_device::buffer::OwnedBuffer, Error>) + Send,
1397 >,
1398 ) -> Result<(), Error> {
1399 Err(anyhow::anyhow!("block read failure"))
1400 }
1401 }
1402
1403 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
1404 let file = Arc::new(File::new(extents, 8192, Transform::None));
1405
1406 let (page_request, rx) = TestVecBuffer::new_with_range(0..8192);
1407
1408 file.read_range(&FailingBlockService, page_request);
1409 assert_eq!(rx.commits().len(), 0);
1410 }
1411
1412 #[test]
1413 fn test_read_range_uncompressed_multi_chunk() {
1414 let block_count = 4;
1415 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
1416 for (i, byte) in expected_data.iter_mut().enumerate() {
1417 *byte = ((i * 11) % 255) as u8;
1418 }
1419 let service = FakeBlockService::new_with_cap(expected_data.clone(), Some(4096));
1422
1423 let extents =
1424 Extents::try_new([Extent::new(0..(block_count * BLOCK_SIZE), Some(0))], 0).unwrap();
1425 let file = Arc::new(File::new(extents, block_count * BLOCK_SIZE, Transform::None));
1426
1427 let (page_request, rx) = TestVecBuffer::new_with_range(0..(block_count * BLOCK_SIZE));
1428 file.read_range(&service, page_request);
1429
1430 assert_eq!(rx.commits().len(), 4);
1431 assert_eq!(rx.output(), expected_data);
1432 }
1433
1434 #[test]
1435 fn test_read_range_uncompressed_unaligned_uncompressed_size() {
1436 let uncompressed_size = 5000u64;
1437 let mut expected_data = vec![0u8; 8192];
1438 for (i, byte) in expected_data.iter_mut().enumerate() {
1439 *byte = (i % 251) as u8;
1440 }
1441 let service = FakeBlockService::new(expected_data.clone());
1442
1443 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
1444 let file = Arc::new(File::new(extents, uncompressed_size, Transform::None));
1445
1446 let (page_request, rx) = TestVecBuffer::new_with_range(0..8192);
1447 file.read_range(&service, page_request);
1448
1449 assert_eq!(rx.commits(), vec![(0, 8192)]);
1450 assert_eq!(&rx.output()[..5000], &expected_data[..5000]);
1451 }
1452
1453 #[test]
1454 fn test_read_range_uncompressed_readahead() {
1455 let total_blocks = 64; let mut expected_data = vec![0u8; (total_blocks * BLOCK_SIZE) as usize];
1457 for (i, byte) in expected_data.iter_mut().enumerate() {
1458 *byte = ((i * 17) % 251) as u8;
1459 }
1460 let service = FakeBlockService::new(expected_data.clone());
1461
1462 let extents =
1463 Extents::try_new([Extent::new(0..(total_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1464 let file = Arc::new(File::new(extents, total_blocks * BLOCK_SIZE, Transform::None));
1465
1466 let (page_request, rx) = TestVecBuffer::new_with_range(4096..8192);
1468 file.read_range(&service, page_request);
1469
1470 assert_eq!(rx.commits(), vec![(0, TARGET_READ_AHEAD_SIZE as usize)]);
1471 assert_eq!(
1472 &rx.output()[..TARGET_READ_AHEAD_SIZE as usize],
1473 &expected_data[..TARGET_READ_AHEAD_SIZE as usize]
1474 );
1475 }
1476
1477 #[test]
1478 fn test_read_range_uncompressed_readahead_second_window() {
1479 let total_blocks = 64; let mut expected_data = vec![0u8; (total_blocks * BLOCK_SIZE) as usize];
1481 for (i, byte) in expected_data.iter_mut().enumerate() {
1482 *byte = ((i * 19) % 251) as u8;
1483 }
1484 let service = FakeBlockService::new(expected_data.clone());
1485
1486 let extents =
1487 Extents::try_new([Extent::new(0..(total_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1488 let file = Arc::new(File::new(extents, total_blocks * BLOCK_SIZE, Transform::None));
1489
1490 let (page_request, rx) = TestVecBuffer::new_with_range(135168..139264);
1493 file.read_range(&service, page_request);
1494
1495 assert_eq!(rx.commits(), vec![(TARGET_READ_AHEAD_SIZE, TARGET_READ_AHEAD_SIZE as usize)]);
1496 assert_eq!(
1497 &rx.output()[..TARGET_READ_AHEAD_SIZE as usize],
1498 &expected_data[TARGET_READ_AHEAD_SIZE as usize..2 * TARGET_READ_AHEAD_SIZE as usize]
1499 );
1500 }
1501
1502 #[test]
1503 fn test_read_range_uncompressed_readahead_tail_capped() {
1504 let uncompressed_size = 140_000u64;
1505 let total_blocks = (uncompressed_size.next_multiple_of(BLOCK_SIZE) / BLOCK_SIZE) as u64;
1506 let mut expected_data = vec![0u8; (total_blocks * BLOCK_SIZE) as usize];
1507 for i in 0..uncompressed_size as usize {
1508 expected_data[i] = ((i * 23) % 251) as u8;
1509 }
1510 let service = FakeBlockService::new(expected_data.clone());
1511
1512 let extents =
1513 Extents::try_new([Extent::new(0..(total_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1514 let file = Arc::new(File::new(extents, uncompressed_size, Transform::None));
1515
1516 let (page_request, rx) = TestVecBuffer::new_with_range(135168..139264);
1520 file.read_range(&service, page_request);
1521
1522 let expected_start = TARGET_READ_AHEAD_SIZE;
1523 let page_aligned_size = uncompressed_size.next_multiple_of(BLOCK_SIZE);
1524 let expected_len = (page_aligned_size - expected_start) as usize;
1525 assert_eq!(rx.commits(), vec![(expected_start, expected_len)]);
1526
1527 let valid_len = (uncompressed_size - expected_start) as usize;
1528 assert_eq!(
1529 &rx.output()[..valid_len],
1530 &expected_data[expected_start as usize..uncompressed_size as usize]
1531 );
1532 assert!(rx.output()[valid_len..expected_len].iter().all(|&b| b == 0));
1534 }
1535
1536 #[test]
1537 fn test_read_range_compressed_second_readahead_window() {
1538 let chunk_count = 8;
1539 let chunk_size = 32768usize;
1540 let uncompressed_size = chunk_count * chunk_size;
1541 let mut uncompressed_data = vec![0u8; uncompressed_size];
1542 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
1543 *byte = ((i * 7) % 251) as u8;
1544 }
1545
1546 let options =
1547 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Zstd };
1548 let archive =
1549 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
1550
1551 let mut compressed_offsets = vec![0];
1552 let mut compressed_data = vec![];
1553 for chunk in archive.chunks() {
1554 compressed_data.extend_from_slice(&chunk.compressed_data);
1555 compressed_offsets.push(compressed_data.len() as u64);
1556 }
1557 compressed_offsets.pop();
1558
1559 let stored_size = compressed_data.len() as u64;
1560 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
1561 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
1562 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
1563 let service = FakeBlockService::new(device_data);
1564
1565 let extents =
1566 Extents::try_new([Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1567 let compression_info = CompressionInfo::new(
1568 chunk_size as u64,
1569 stored_size,
1570 &compressed_offsets,
1571 CompressionAlgorithm::Zstd,
1572 )
1573 .unwrap();
1574 let file = Arc::new(File::new(extents, uncompressed_size as u64, compression_info.into()));
1575
1576 let (page_request, rx) = TestVecBuffer::new_with_range(135168..139264);
1579 file.read_range(&service, page_request);
1580
1581 assert_eq!(
1582 rx.commits(),
1583 vec![
1584 (131072, chunk_size),
1585 (163840, chunk_size),
1586 (196608, chunk_size),
1587 (229376, chunk_size),
1588 ]
1589 );
1590 assert_eq!(&rx.output()[..131072], &uncompressed_data[131072..262144]);
1591 }
1592
1593 #[test]
1594 fn test_read_range_compressed_partial_final_chunk_zero_tail() {
1595 let uncompressed_size = 32768 + 1024;
1596 let mut uncompressed_data = vec![0u8; uncompressed_size];
1597 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
1598 *byte = ((i * 13) % 251) as u8;
1599 }
1600
1601 let options =
1602 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Zstd };
1603 let archive =
1604 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
1605
1606 let mut compressed_offsets = vec![0];
1607 let mut compressed_data = vec![];
1608 for chunk in archive.chunks() {
1609 compressed_data.extend_from_slice(&chunk.compressed_data);
1610 compressed_offsets.push(compressed_data.len() as u64);
1611 }
1612 compressed_offsets.pop();
1613
1614 let chunk_size = archive.chunk_size();
1615 let stored_size = compressed_data.len() as u64;
1616 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
1617 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
1618 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
1619 let service = FakeBlockService::new(device_data);
1620
1621 let extents =
1622 Extents::try_new([Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1623 let compression_info = CompressionInfo::new(
1624 chunk_size as u64,
1625 stored_size,
1626 &compressed_offsets,
1627 CompressionAlgorithm::Zstd,
1628 )
1629 .unwrap();
1630 let file = Arc::new(File::new(extents, uncompressed_size as u64, compression_info.into()));
1631
1632 let (mut page_request, rx) = TestVecBuffer::new_with_range(0..(uncompressed_size as u64));
1634 page_request.data.resize(65536, 0);
1635 page_request.data.fill(0xFF);
1636 file.read_range(&service, page_request);
1637
1638 assert_eq!(rx.commits(), vec![(0, chunk_size), (chunk_size as u64, chunk_size)]);
1639 assert_eq!(&rx.output()[..uncompressed_size], &uncompressed_data[..]);
1640 assert_eq!(&rx.output()[uncompressed_size..65536], &[0u8; 31744]);
1641 }
1642
1643 #[test]
1644 fn test_files_registry() {
1645 let extents = Extents::try_new([Extent::new(0..4096, Some(0))], 0).unwrap();
1646 let file = Arc::new(File::new(extents, 4096, Transform::None));
1647 let service = Arc::new(FakeBlockService::new(vec![0u8; 4096]));
1648 let files = Files::new(
1649 service,
1650 TestDeliveryHandler(|_key, _range| TestVecBuffer::new(4096).0),
1651 zx::Port::create(),
1652 );
1653
1654 assert!(!files.is_loading(100));
1655 files.begin_loading(100);
1656 assert!(files.is_loading(100));
1657
1658 files.insert(100, file.clone());
1659 assert!(!files.is_loading(100));
1660 files.remove(100);
1661 assert!(!files.is_loading(100));
1662 }
1663
1664 #[test]
1665 fn test_files_new_without_pager() {
1666 let extents = Extents::try_new([Extent::new(0..4096, Some(0))], 0).unwrap();
1667 let file = Arc::new(File::new(extents, 4096, Transform::None));
1668 let service = Arc::new(FakeBlockService::new(vec![0u8; 4096]));
1669 let files = Files::new_without_pager(service);
1670
1671 files.insert(100, file.clone());
1672 assert_eq!(files.get_file(100).unwrap().uncompressed_size(), 4096);
1673 }
1674
1675 struct DelayedBlockService {
1676 device_data: Vec<u8>,
1677 pending: Mutex<Vec<Box<dyn FnOnce() + Send>>>,
1678 allocator: Option<Arc<BufferAllocator>>,
1679 }
1680
1681 impl DelayedBlockService {
1682 fn new(device_data: Vec<u8>) -> Arc<Self> {
1683 Arc::new(Self { device_data, pending: Mutex::new(Vec::new()), allocator: None })
1684 }
1685
1686 fn new_with_pool_capacity(device_data: Vec<u8>, pool_capacity: usize) -> Arc<Self> {
1687 Arc::new(Self {
1688 device_data,
1689 pending: Mutex::new(Vec::new()),
1690 allocator: Some(Arc::new(BufferAllocator::new(
1691 BLOCK_SIZE as usize,
1692 BufferSource::new(pool_capacity),
1693 ))),
1694 })
1695 }
1696
1697 fn wait_and_trigger_sync(&self) {
1698 loop {
1699 let callbacks = std::mem::take(&mut *self.pending.lock());
1700 if !callbacks.is_empty() {
1701 for cb in callbacks {
1702 cb();
1703 }
1704 break;
1705 }
1706 std::thread::sleep(std::time::Duration::from_millis(10));
1707 }
1708 }
1709 }
1710
1711 impl BlockService for DelayedBlockService {
1712 fn allocate_buffer(&self, max_len: usize) -> OwnedBuffer {
1713 if let Some(allocator) = &self.allocator {
1714 let max_len = std::cmp::min(
1715 std::cmp::min(max_len, MAX_READ_BUFFER_SIZE),
1716 allocator.buffer_source().size(),
1717 );
1718 allocator.allocate_buffer_sync_owned(max_len)
1719 } else {
1720 FakeBlockService::new(vec![0u8; max_len]).allocate_buffer(max_len)
1721 }
1722 }
1723
1724 fn read_blocks(
1725 &self,
1726 device_offset: u64,
1727 mut dest_buffer: OwnedBuffer,
1728 on_complete: Box<dyn FnOnce(Result<OwnedBuffer, Error>) + Send>,
1729 ) -> Result<(), Error> {
1730 let start = device_offset as usize;
1731 let end = (start + dest_buffer.len()).min(self.device_data.len());
1732 dest_buffer
1733 .as_mut_ptr_slice()
1734 .subslice_mut(0..end - start)
1735 .copy_from_slice(&self.device_data[start..end]);
1736 self.pending.lock().push(Box::new(move || {
1737 on_complete(Ok(dest_buffer));
1738 }));
1739 Ok(())
1740 }
1741 }
1742
1743 #[fuchsia::test]
1744 fn test_read_blob_metadata_callback() {
1745 let uncompressed_size = 65536u64;
1746 let chunk_size = 32768u64;
1747 let compressed_offsets = vec![0u64, 1200u64];
1748 let metadata = BlobMetadata {
1749 merkle_leaves: MerkleLeaves::new(),
1750 format: BlobFormat::ChunkedZstd {
1751 uncompressed_size,
1752 chunk_size,
1753 compressed_offsets: compressed_offsets.clone(),
1754 },
1755 };
1756 let encoded_metadata = serialize_metadata(&metadata);
1757 let mut device_data = vec![0u8; BLOCK_SIZE as usize];
1758 device_data[..encoded_metadata.len()].copy_from_slice(&encoded_metadata);
1759
1760 let service = DelayedBlockService::new(device_data);
1761 let metadata_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1762
1763 let (tx, rx) = std::sync::mpsc::channel();
1764 read_blob_metadata(service.as_ref(), &metadata_extents, 2400, move |res| {
1765 let _ = tx.send(res);
1766 });
1767
1768 service.wait_and_trigger_sync();
1770
1771 let metadata = rx.recv().unwrap();
1772 assert_eq!(metadata.uncompressed_size, uncompressed_size);
1773 assert!(metadata.compression_info.is_some());
1774 assert_eq!(metadata.merkle_leaves, MerkleLeaves::new());
1775 }
1776
1777 #[fuchsia::test]
1778 fn test_read_blob_metadata_error_drops_callback() {
1779 let metadata = BlobMetadata {
1781 merkle_leaves: MerkleLeaves::new(),
1782 format: BlobFormat::ChunkedZstd {
1783 uncompressed_size: 65536,
1784 chunk_size: 32768,
1785 compressed_offsets: vec![1000, 500],
1786 },
1787 };
1788 let encoded_metadata = serialize_metadata(&metadata);
1789 let mut device_data = vec![0u8; BLOCK_SIZE as usize];
1790 device_data[..encoded_metadata.len()].copy_from_slice(&encoded_metadata);
1791
1792 let service = DelayedBlockService::new(device_data);
1793 let metadata_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1794
1795 let (tx, rx) = std::sync::mpsc::channel();
1796 read_blob_metadata(service.as_ref(), &metadata_extents, 1200, move |res| {
1797 let _ = tx.send(res);
1798 });
1799
1800 service.wait_and_trigger_sync();
1801
1802 assert!(rx.recv().is_err());
1804 }
1805
1806 #[fuchsia::test]
1807 fn test_read_blob_metadata_corrupt_bincode_drops_callback() {
1808 let device_data = vec![0xFFu8; BLOCK_SIZE as usize];
1810 let service = DelayedBlockService::new(device_data);
1811 let metadata_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1812
1813 let (tx, rx) = std::sync::mpsc::channel();
1814 read_blob_metadata(service.as_ref(), &metadata_extents, 1200, move |res| {
1815 let _ = tx.send(res);
1816 });
1817
1818 service.wait_and_trigger_sync();
1819
1820 assert!(rx.recv().is_err());
1822 }
1823
1824 #[fuchsia::test]
1825 fn test_process_mapping_command_error_cleans_up_blob() {
1826 let metadata = BlobMetadata {
1827 merkle_leaves: MerkleLeaves::new(),
1828 format: BlobFormat::ChunkedZstd {
1829 uncompressed_size: 65536,
1830 chunk_size: 32768,
1831 compressed_offsets: vec![1000, 500],
1832 },
1833 };
1834 let encoded_metadata = serialize_metadata(&metadata);
1835 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
1836 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
1837 .copy_from_slice(&encoded_metadata);
1838
1839 let service = DelayedBlockService::new(device_data);
1840 let files = Arc::new(Files::new(
1841 service.clone(),
1842 TestDeliveryHandler(|_k, _r| TestVecBuffer::new(4096).0),
1843 zx::Port::create(),
1844 ));
1845
1846 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1847 let meta_extents =
1848 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
1849 let mut payload_bytes = Vec::new();
1850 for w in
1851 Extents::encode_extents(&data_extents).chain(Extents::encode_extents(&meta_extents))
1852 {
1853 payload_bytes.extend_from_slice(&w.to_le_bytes());
1854 }
1855
1856 let vmo = zx::Vmo::create(65536).unwrap();
1857 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
1858 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1859 1024,
1860 16,
1861 )
1862 .unwrap();
1863 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
1864 payload_buf.data().copy_from_slice(&payload_bytes);
1865 let cmd = crate::RawMappingCommand {
1866 opcode: crate::MAPPINGS_COMMAND,
1867 offset: payload_buf.offset(),
1868 key: 99,
1869 stored_size: 4096,
1870 device_offset: 0,
1871 metadata_count: 1,
1872 extent_count: 1,
1873 };
1874 payload_buf.commit(cmd).unwrap();
1875 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
1876
1877 let msg = receiver.peek().unwrap();
1878 process_mapping_command(&msg, &files).unwrap();
1879
1880 assert!(files.is_loading(99));
1882
1883 service.wait_and_trigger_sync();
1885
1886 assert!(!files.is_loading(99));
1888 }
1889
1890 #[fuchsia::test]
1891 fn test_process_mapping_command_registers_blob_merkle_leaves() {
1892 let leaf1 = [1u8; 32];
1893 let leaf2 = [2u8; 32];
1894
1895 let metadata =
1897 BlobMetadata { merkle_leaves: vec![leaf1, leaf2], format: BlobFormat::Uncompressed };
1898 let encoded_metadata = serialize_metadata(&metadata);
1899 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
1900 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
1901 .copy_from_slice(&encoded_metadata);
1902
1903 let service = DelayedBlockService::new(device_data);
1905 let registered = Arc::new(std::sync::Mutex::new(None));
1906 let registered_clone = registered.clone();
1907 struct TestRegisterHandler(Arc<std::sync::Mutex<Option<(u64, Vec<[u8; 32]>, usize)>>>);
1908 impl DeliveryHandler for TestRegisterHandler {
1909 type Request = TestVecBuffer;
1910 fn get_page_request(self: &Arc<Self>, _key: u64, _range: Range<u64>) -> Self::Request {
1911 TestVecBuffer::new(4096).0
1912 }
1913 fn register_blob(
1914 &self,
1915 key: u64,
1916 leaves: &[[u8; 32]],
1917 read_alignment: usize,
1918 ) -> Result<(), Error> {
1919 *self.0.lock().unwrap() = Some((key, leaves.to_vec(), read_alignment));
1920 Ok(())
1921 }
1922 }
1923 let files = Arc::new(Files::new(
1924 service.clone(),
1925 TestRegisterHandler(registered_clone),
1926 zx::Port::create(),
1927 ));
1928
1929 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1931 let meta_extents =
1932 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
1933 let data_extent_words = Extents::encode_extents(&data_extents);
1934 let meta_extent_words = Extents::encode_extents(&meta_extents);
1935 let mut payload_bytes = Vec::new();
1936 for w in data_extent_words.chain(meta_extent_words) {
1937 payload_bytes.extend_from_slice(&w.to_le_bytes());
1938 }
1939
1940 let vmo = zx::Vmo::create(65536).unwrap();
1942 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
1943 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1944 1024,
1945 16,
1946 )
1947 .unwrap();
1948 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
1949 payload_buf.data().copy_from_slice(&payload_bytes);
1950 let cmd = crate::RawMappingCommand {
1951 opcode: crate::MAPPINGS_COMMAND,
1952 offset: payload_buf.offset(),
1953 key: 42,
1954 stored_size: 4096,
1955 device_offset: 0,
1956 metadata_count: 1,
1957 extent_count: 1,
1958 };
1959 payload_buf.commit(cmd).unwrap();
1960 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
1961
1962 let msg = receiver.peek().unwrap();
1964 process_mapping_command(&msg, &files).unwrap();
1965
1966 assert!(files.is_loading(42));
1969 assert!(registered.lock().unwrap().is_none());
1970
1971 service.wait_and_trigger_sync();
1973
1974 assert!(!files.is_loading(42));
1979 assert!(files.get_file(42).is_some());
1980 let (reg_key, reg_leaves, reg_read_alignment) =
1981 registered.lock().unwrap().take().expect("registered callback called");
1982 assert_eq!(reg_key, 42);
1983 assert_eq!(reg_leaves, vec![leaf1, leaf2]);
1984 assert_eq!(reg_read_alignment, DEFAULT_VERIFICATION_ALIGNMENT as usize);
1985 }
1986
1987 #[fuchsia::test]
1988 fn test_loading_page_request_buffer_dropped_on_failure() {
1989 use delivery_blob::compression::{ChunkedArchiveError, DataBuffer};
1990 use std::sync::atomic::{AtomicBool, Ordering};
1991 use storage_ptr_slice::MutPtrByteSlice;
1992
1993 struct DroppingBuffer {
1994 data: Vec<u8>,
1995 range: Range<u64>,
1996 dropped: Arc<AtomicBool>,
1997 }
1998 impl Drop for DroppingBuffer {
1999 fn drop(&mut self) {
2000 self.dropped.store(true, Ordering::Relaxed);
2001 }
2002 }
2003 impl DataBuffer for DroppingBuffer {
2004 fn range(&self) -> Range<u64> {
2005 self.range.clone()
2006 }
2007 fn mut_ptr_slice(&mut self) -> MutPtrByteSlice<'_> {
2008 MutPtrByteSlice::from(&mut self.data[..])
2009 }
2010 fn commit(&mut self, _size: usize) -> Result<(), ChunkedArchiveError> {
2011 Ok(())
2012 }
2013 }
2014 impl PageRequest for DroppingBuffer {
2015 fn prepare(
2016 &mut self,
2017 _range: Range<u64>,
2018 _read_alignment: usize,
2019 ) -> Result<(), ChunkedArchiveError> {
2020 Ok(())
2021 }
2022 }
2023
2024 let metadata = BlobMetadata {
2025 merkle_leaves: MerkleLeaves::new(),
2026 format: BlobFormat::ChunkedZstd {
2027 uncompressed_size: 65536,
2028 chunk_size: 32768,
2029 compressed_offsets: vec![1000, 500],
2030 },
2031 };
2032 let encoded_metadata = serialize_metadata(&metadata);
2033 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
2034 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
2035 .copy_from_slice(&encoded_metadata);
2036
2037 let service = DelayedBlockService::new(device_data);
2038 let dropped = Arc::new(AtomicBool::new(false));
2039 let dropped_clone = dropped.clone();
2040 let files = Arc::new(Files::new(
2041 service.clone(),
2042 TestDeliveryHandler(move |_k, r| DroppingBuffer {
2043 data: vec![0u8; 4096],
2044 range: r,
2045 dropped: dropped_clone.clone(),
2046 }),
2047 zx::Port::create(),
2048 ));
2049
2050 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
2051 let meta_extents =
2052 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
2053 let mut payload_bytes = Vec::new();
2054 for w in
2055 Extents::encode_extents(&data_extents).chain(Extents::encode_extents(&meta_extents))
2056 {
2057 payload_bytes.extend_from_slice(&w.to_le_bytes());
2058 }
2059
2060 let vmo = zx::Vmo::create(65536).unwrap();
2061 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
2062 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2063 1024,
2064 16,
2065 )
2066 .unwrap();
2067 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2068 payload_buf.data().copy_from_slice(&payload_bytes);
2069 let cmd = crate::RawMappingCommand {
2070 opcode: crate::MAPPINGS_COMMAND,
2071 offset: payload_buf.offset(),
2072 key: 99,
2073 stored_size: 4096,
2074 device_offset: 0,
2075 metadata_count: 1,
2076 extent_count: 1,
2077 };
2078 payload_buf.commit(cmd).unwrap();
2079 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2080
2081 let msg = receiver.peek().unwrap();
2082 process_mapping_command(&msg, &files).unwrap();
2083
2084 assert!(files.is_loading(99));
2085
2086 files.handle_page_request(99, 0..4096);
2088 assert!(!dropped.load(Ordering::Relaxed));
2089
2090 service.wait_and_trigger_sync();
2092
2093 assert!(dropped.load(Ordering::Relaxed));
2095 assert!(!files.is_loading(99));
2096 }
2097
2098 #[fuchsia::test]
2099 async fn test_pager_concurrent_request_during_metadata_read() {
2100 use vmo_fifo::SyncSender;
2101 use zx::{Pager, PagerOptions, Port, Rights, Vmo, VmoOptions};
2102
2103 let metadata =
2104 BlobMetadata { merkle_leaves: MerkleLeaves::new(), format: BlobFormat::Uncompressed };
2105 let encoded_metadata = serialize_metadata(&metadata);
2106 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
2107 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
2109 .copy_from_slice(&encoded_metadata);
2110 device_data[..4].copy_from_slice(&[10, 20, 30, 40]);
2112
2113 let service = DelayedBlockService::new(device_data);
2114 let port = Port::create();
2115
2116 let (page_request, rx) = TestVecBuffer::new(BLOCK_SIZE as usize);
2117 let page_request_holder = Arc::new(Mutex::new(Some(page_request)));
2118 let page_request_clone = page_request_holder.clone();
2119
2120 let files = Arc::new(Files::new(
2121 service.clone(),
2122 TestDeliveryHandler(move |_key, _range| page_request_clone.lock().take().unwrap()),
2123 port.duplicate_handle(Rights::SAME_RIGHTS).unwrap(),
2124 ));
2125
2126 let _pager_thread = files.spawn_pager_thread();
2127
2128 let pager = Pager::create(PagerOptions::empty()).unwrap();
2129 let vmo_blob = pager.create_vmo(VmoOptions::empty(), &port, 42, BLOCK_SIZE).unwrap();
2130 let vmo_blob_clone = vmo_blob.duplicate_handle(Rights::SAME_RIGHTS).unwrap();
2131
2132 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
2134 let meta_extents =
2135 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
2136 let mut payload_bytes = Vec::new();
2137 for w in
2138 Extents::encode_extents(&data_extents).chain(Extents::encode_extents(&meta_extents))
2139 {
2140 payload_bytes.extend_from_slice(&w.to_le_bytes());
2141 }
2142
2143 let cmd = crate::RawMappingCommand {
2144 opcode: crate::MAPPINGS_COMMAND,
2145 offset: 0,
2146 key: 42,
2147 stored_size: BLOCK_SIZE,
2148 device_offset: 0,
2149 metadata_count: 1,
2150 extent_count: 1,
2151 };
2152
2153 let vmo = Vmo::create(65536).unwrap();
2154 let mut sender = SyncSender::<crate::RawMappingCommand>::new(
2155 vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap(),
2156 1024,
2157 16,
2158 )
2159 .unwrap();
2160 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2161 payload_buf.data().copy_from_slice(&payload_bytes);
2162 payload_buf.commit(cmd).unwrap();
2163 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2164
2165 std::thread::scope(|s| {
2166 let msg = receiver.peek().unwrap();
2167 let files_for_process = files.clone();
2168 s.spawn(move || {
2169 process_mapping_command(&msg, &files_for_process).unwrap();
2170 });
2171
2172 while service.pending.lock().is_empty() {
2175 std::thread::sleep(std::time::Duration::from_millis(5));
2176 }
2177
2178 std::thread::spawn(move || {
2180 let mut b = [0u8; 1];
2181 let _ = vmo_blob_clone.read(&mut b, 0);
2182 });
2183
2184 while !files.has_queued_page_requests(42) {
2187 std::thread::sleep(std::time::Duration::from_millis(5));
2188 }
2189
2190 service.wait_and_trigger_sync();
2193 });
2194
2195 service.wait_and_trigger_sync();
2197
2198 assert!(
2200 !rx.wait_for_commits(1).is_empty(),
2201 "Page request made during metadata read was dropped!"
2202 );
2203 }
2204
2205 #[fuchsia::test]
2206 async fn test_pager_request_arrives_before_mapping_command() {
2207 use vmo_fifo::SyncSender;
2208 use zx::{Pager, PagerOptions, Port, Rights, Vmo, VmoOptions};
2209
2210 let metadata =
2211 BlobMetadata { merkle_leaves: MerkleLeaves::new(), format: BlobFormat::Uncompressed };
2212 let encoded_metadata = serialize_metadata(&metadata);
2213 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
2214 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
2216 .copy_from_slice(&encoded_metadata);
2217 device_data[..4].copy_from_slice(&[10, 20, 30, 40]);
2219
2220 let service = DelayedBlockService::new(device_data);
2221 let port = Port::create();
2222
2223 let (page_request, rx) = TestVecBuffer::new(BLOCK_SIZE as usize);
2224 let page_request_holder = Arc::new(Mutex::new(Some(page_request)));
2225 let page_request_clone = page_request_holder.clone();
2226
2227 let files = Arc::new(Files::new(
2228 service.clone(),
2229 TestDeliveryHandler(move |_key, _range| page_request_clone.lock().take().unwrap()),
2230 port.duplicate_handle(Rights::SAME_RIGHTS).unwrap(),
2231 ));
2232
2233 let _pager_thread = files.spawn_pager_thread();
2234
2235 let pager = Pager::create(PagerOptions::empty()).unwrap();
2236 let vmo_blob = pager.create_vmo(VmoOptions::empty(), &port, 42, BLOCK_SIZE).unwrap();
2237 let vmo_blob_clone = vmo_blob.duplicate_handle(Rights::SAME_RIGHTS).unwrap();
2238
2239 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
2241 let meta_extents =
2242 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
2243 let mut payload_bytes = Vec::new();
2244 for w in
2245 Extents::encode_extents(&data_extents).chain(Extents::encode_extents(&meta_extents))
2246 {
2247 payload_bytes.extend_from_slice(&w.to_le_bytes());
2248 }
2249
2250 let cmd = crate::RawMappingCommand {
2251 opcode: crate::MAPPINGS_COMMAND,
2252 offset: 0,
2253 key: 42,
2254 stored_size: BLOCK_SIZE,
2255 device_offset: 0,
2256 metadata_count: 1,
2257 extent_count: 1,
2258 };
2259
2260 let vmo = Vmo::create(65536).unwrap();
2261 let mut sender = SyncSender::<crate::RawMappingCommand>::new(
2262 vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap(),
2263 1024,
2264 16,
2265 )
2266 .unwrap();
2267 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2268 payload_buf.data().copy_from_slice(&payload_bytes);
2269 payload_buf.commit(cmd).unwrap();
2270 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2271
2272 std::thread::spawn(move || {
2274 let mut b = [0u8; 1];
2275 let _ = vmo_blob_clone.read(&mut b, 0);
2276 });
2277
2278 while !files.has_queued_page_requests(42) {
2280 std::thread::sleep(std::time::Duration::from_millis(5));
2281 }
2282
2283 std::thread::scope(|s| {
2284 let msg = receiver.peek().unwrap();
2285 let files_for_process = files.clone();
2286 s.spawn(move || {
2287 process_mapping_command(&msg, &files_for_process).unwrap();
2288 });
2289
2290 service.wait_and_trigger_sync();
2292 });
2293
2294 service.wait_and_trigger_sync();
2296
2297 assert!(
2299 !rx.wait_for_commits(1).is_empty(),
2300 "Page request made before mapping command was dropped!"
2301 );
2302 }
2303
2304 #[fuchsia::test]
2305 fn test_process_mapping_command_close_blob() {
2306 let service = Arc::new(FakeBlockService::new(vec![0u8; 8192]));
2307 let files = Arc::new(Files::new(
2308 service,
2309 TestDeliveryHandler(|_k, _r| TestVecBuffer::new(4096).0),
2310 zx::Port::create(),
2311 ));
2312
2313 let vmo = zx::Vmo::create(65536).unwrap();
2314 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
2315 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2316 1024,
2317 16,
2318 )
2319 .unwrap();
2320
2321 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
2323 let mut payload_bytes = Vec::new();
2324 for w in Extents::encode_extents(&data_extents) {
2325 payload_bytes.extend_from_slice(&w.to_le_bytes());
2326 }
2327 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2328 payload_buf.data().copy_from_slice(&payload_bytes);
2329 let cmd = crate::RawMappingCommand {
2330 opcode: crate::MAPPINGS_COMMAND,
2331 offset: payload_buf.offset(),
2332 key: 123,
2333 stored_size: 4096,
2334 device_offset: 0,
2335 metadata_count: 0,
2336 extent_count: 1,
2337 };
2338 payload_buf.commit(cmd).unwrap();
2339
2340 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2341 let msg = receiver.peek().unwrap();
2342 process_mapping_command(&msg, &files).unwrap();
2343 msg.pop().unwrap();
2344
2345 assert!(files.is_loaded(123));
2346
2347 sender
2349 .push(crate::RawMappingCommand {
2350 opcode: crate::CLOSE_BLOB_COMMAND,
2351 offset: 0,
2352 key: 123,
2353 stored_size: 0,
2354 device_offset: 0,
2355 metadata_count: 0,
2356 extent_count: 0,
2357 })
2358 .unwrap();
2359
2360 let msg = receiver.peek().unwrap();
2361 process_mapping_command(&msg, &files).unwrap();
2362 msg.pop().unwrap();
2363
2364 assert!(!files.is_loaded(123));
2365 }
2366
2367 #[test]
2368 fn test_read_ahead_size_for_chunk_size() {
2369 assert_eq!(read_ahead_size_for_chunk_size(32 * 1024, 32 * 1024), 32 * 1024);
2370 assert_eq!(read_ahead_size_for_chunk_size(48 * 1024, 32 * 1024), 48 * 1024);
2371 assert_eq!(read_ahead_size_for_chunk_size(64 * 1024, 32 * 1024), 64 * 1024);
2372
2373 assert_eq!(read_ahead_size_for_chunk_size(32 * 1024, 64 * 1024), 64 * 1024);
2374 assert_eq!(read_ahead_size_for_chunk_size(48 * 1024, 64 * 1024), 48 * 1024);
2375 assert_eq!(read_ahead_size_for_chunk_size(64 * 1024, 64 * 1024), 64 * 1024);
2376 assert_eq!(read_ahead_size_for_chunk_size(96 * 1024, 64 * 1024), 96 * 1024);
2377
2378 assert_eq!(read_ahead_size_for_chunk_size(32 * 1024, 128 * 1024), 128 * 1024);
2379 assert_eq!(read_ahead_size_for_chunk_size(48 * 1024, 128 * 1024), 96 * 1024);
2380 assert_eq!(read_ahead_size_for_chunk_size(64 * 1024, 128 * 1024), 128 * 1024);
2381 assert_eq!(read_ahead_size_for_chunk_size(96 * 1024, 128 * 1024), 96 * 1024);
2382 }
2383
2384 #[test]
2385 fn test_deserialize_blob_metadata() {
2386 let metadata =
2387 BlobMetadata { merkle_leaves: MerkleLeaves::new(), format: BlobFormat::Uncompressed };
2388
2389 let bytes = serialize_metadata(&metadata);
2391 let deserialized = deserialize_blob_metadata(&bytes).unwrap();
2392 assert_eq!(deserialized, metadata);
2393
2394 assert!(deserialize_blob_metadata(&[1, 2, 3]).is_err());
2396
2397 let mut old_version_bytes = bytes.clone();
2399 (&mut old_version_bytes[..4]).write_u32::<LittleEndian>(52).unwrap();
2400 assert!(deserialize_blob_metadata(&old_version_bytes).is_err());
2401 }
2402
2403 #[fuchsia::test]
2404 async fn test_wait_for_file_before_and_after_insert() {
2405 let service = Arc::new(FakeBlockService::new(vec![]));
2406 let files = Arc::new(Files::new_without_pager(service));
2407
2408 let extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
2409 let file = Arc::new(File::new(extents, BLOCK_SIZE, Transform::None));
2410
2411 let files_clone = files.clone();
2413 let file_clone = file.clone();
2414 let wait_task =
2415 fasync::Task::spawn(async move { files_clone.wait_for_file(42).await.unwrap() });
2416
2417 files.insert(42, file_clone);
2419 let loaded_file = wait_task.await;
2420 assert_eq!(loaded_file.uncompressed_size(), BLOCK_SIZE);
2421
2422 let loaded_file2 = files.wait_for_file(42).await.unwrap();
2424 assert_eq!(loaded_file2.uncompressed_size(), BLOCK_SIZE);
2425 }
2426
2427 #[test]
2428 fn test_read_range_encrypted() {
2429 let block_count = 8;
2430 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
2431 for (i, byte) in expected_data.iter_mut().enumerate() {
2432 *byte = (i % 255) as u8;
2433 }
2434
2435 let key = UnwrappedKey::new(vec![0x42u8; 32]);
2436 let cipher: Arc<dyn Cipher> = Arc::new(FxfsCipher::new(&key));
2437
2438 let mut encrypted_data = expected_data.clone();
2439 cipher.encrypt(0, 0, 0, 0, MutPtrByteSlice::from(&mut encrypted_data[..])).unwrap();
2440 let service = FakeBlockService::new(encrypted_data);
2441
2442 let extents = Extents::try_new([Extent::new(0..(8 * BLOCK_SIZE), Some(0))], 0).unwrap();
2443 let file = Arc::new(File::new(extents, 8 * BLOCK_SIZE, cipher.into()));
2444
2445 let (page_request, rx) = TestVecBuffer::new_with_range(0..(8 * BLOCK_SIZE));
2446 file.read_range(&service, page_request);
2447
2448 assert_eq!(rx.commits(), vec![(0, (8 * BLOCK_SIZE) as usize)]);
2449 assert_eq!(rx.output(), expected_data);
2450 }
2451
2452 #[test]
2453 fn test_read_range_encrypted_with_offset() {
2454 let total_blocks = 64; let mut expected_data = vec![0u8; (total_blocks * BLOCK_SIZE) as usize];
2456 for (i, byte) in expected_data.iter_mut().enumerate() {
2457 *byte = ((i * 17) % 255) as u8;
2458 }
2459
2460 let key = UnwrappedKey::new(vec![0x5au8; 32]);
2461 let cipher: Arc<dyn Cipher> = Arc::new(FxfsCipher::new(&key));
2462
2463 let mut encrypted_data = expected_data.clone();
2464 cipher.encrypt(0, 0, 0, 0, MutPtrByteSlice::from(&mut encrypted_data[..])).unwrap();
2465 let service = FakeBlockService::new(encrypted_data);
2466
2467 let extents =
2468 Extents::try_new([Extent::new(0..(total_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
2469 let file = Arc::new(File::new(extents, total_blocks * BLOCK_SIZE, cipher.into()));
2470
2471 let (page_request, rx) = TestVecBuffer::new_with_range(135168..139264);
2475 file.read_range(&service, page_request);
2476
2477 assert_eq!(rx.commits(), vec![(TARGET_READ_AHEAD_SIZE, TARGET_READ_AHEAD_SIZE as usize)]);
2478 assert_eq!(
2479 &rx.output()[..TARGET_READ_AHEAD_SIZE as usize],
2480 &expected_data[TARGET_READ_AHEAD_SIZE as usize..2 * TARGET_READ_AHEAD_SIZE as usize]
2481 );
2482 }
2483
2484 #[test]
2485 fn test_read_range_encrypted_multi_chunk() {
2486 let block_count = 4;
2487 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
2488 for (i, byte) in expected_data.iter_mut().enumerate() {
2489 *byte = ((i * 31) % 255) as u8;
2490 }
2491
2492 let key = UnwrappedKey::new(vec![0x7fu8; 32]);
2493 let cipher: Arc<dyn Cipher> = Arc::new(FxfsCipher::new(&key));
2494
2495 let mut encrypted_data = expected_data.clone();
2496 cipher.encrypt(0, 0, 0, 0, MutPtrByteSlice::from(&mut encrypted_data[..])).unwrap();
2497 let service = FakeBlockService::new_with_cap(encrypted_data, Some(BLOCK_SIZE as usize));
2499
2500 let extents =
2501 Extents::try_new([Extent::new(0..(block_count * BLOCK_SIZE), Some(0))], 0).unwrap();
2502 let file = Arc::new(File::new(extents, block_count * BLOCK_SIZE, cipher.into()));
2503
2504 let (page_request, rx) = TestVecBuffer::new_with_range(0..(block_count * BLOCK_SIZE));
2505 file.read_range(&service, page_request);
2506
2507 assert_eq!(rx.commits().len(), 4);
2508 assert_eq!(rx.output(), expected_data);
2509 }
2510
2511 #[test]
2512 fn test_read_range_decryption_error() {
2513 #[derive(Debug)]
2514 struct FailingCipher(FxfsCipher);
2515 impl Cipher for FailingCipher {
2516 fn encrypt(
2517 &self,
2518 ino: u64,
2519 attr: u64,
2520 dev: u64,
2521 file: u64,
2522 buf: MutPtrByteSlice<'_>,
2523 ) -> Result<(), Error> {
2524 self.0.encrypt(ino, attr, dev, file, buf)
2525 }
2526 fn decrypt(
2527 &self,
2528 _ino: u64,
2529 _attribute_id: u64,
2530 _device_offset: u64,
2531 _file_offset: u64,
2532 _buffer: MutPtrByteSlice<'_>,
2533 ) -> Result<(), Error> {
2534 bail!("decrypt failed")
2535 }
2536 fn encrypt_filename(&self, ino: u64, name: &mut Vec<u8>) -> Result<(), Error> {
2537 self.0.encrypt_filename(ino, name)
2538 }
2539 fn decrypt_filename(&self, ino: u64, name: &mut Vec<u8>) -> Result<(), Error> {
2540 self.0.decrypt_filename(ino, name)
2541 }
2542 fn hash_code(&self, name: &[u8], filename: &str) -> Option<u32> {
2543 self.0.hash_code(name, filename)
2544 }
2545 fn hash_code_casefold(&self, filename: &str) -> u32 {
2546 self.0.hash_code_casefold(filename)
2547 }
2548 fn supports_inline_encryption(&self) -> bool {
2549 self.0.supports_inline_encryption()
2550 }
2551 fn crypt_ctx(&self, ino: u64, attr: u64, offset: u64) -> Option<(u64, u8)> {
2552 self.0.crypt_ctx(ino, attr, offset)
2553 }
2554 }
2555
2556 let key = UnwrappedKey::new(vec![0x42u8; 32]);
2557 let failing_cipher: Arc<dyn Cipher> = Arc::new(FailingCipher(FxfsCipher::new(&key)));
2558 let service = FakeBlockService::new(vec![0u8; 8192]);
2559 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
2560 let file = Arc::new(File::new(extents, 8192, failing_cipher.into()));
2561
2562 let (page_request, rx) = TestVecBuffer::new_with_range(0..8192);
2563 file.read_range(&service, page_request);
2564 assert_eq!(rx.commits().len(), 0);
2565 }
2566
2567 #[fuchsia::test]
2568 fn test_process_mapping_command_unencrypted_no_metadata() {
2569 let block_count = 2;
2570 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
2571 for (i, byte) in expected_data.iter_mut().enumerate() {
2572 *byte = (i % 251) as u8;
2573 }
2574 let service = Arc::new(FakeBlockService::new(expected_data.clone()));
2575 let files = Arc::new(Files::new(
2576 service.clone(),
2577 TestDeliveryHandler(|_k, r| TestVecBuffer::new_with_range(r).0),
2578 zx::Port::create(),
2579 ));
2580
2581 let data_extents =
2582 Extents::try_new([Extent::new(0..(block_count as u64 * BLOCK_SIZE), Some(0))], 0)
2583 .unwrap();
2584 let mut payload_bytes = Vec::new();
2585 for w in Extents::encode_extents(&data_extents) {
2586 payload_bytes.extend_from_slice(&w.to_le_bytes());
2587 }
2588
2589 let vmo = zx::Vmo::create(65536).unwrap();
2590 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
2591 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2592 1024,
2593 16,
2594 )
2595 .unwrap();
2596 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2597 payload_buf.data().copy_from_slice(&payload_bytes);
2598 let cmd = crate::RawMappingCommand {
2599 opcode: crate::MAPPINGS_COMMAND,
2600 offset: payload_buf.offset(),
2601 key: 100,
2602 stored_size: block_count as u64 * BLOCK_SIZE,
2603 device_offset: 0,
2604 metadata_count: 0,
2605 extent_count: 1,
2606 };
2607 payload_buf.commit(cmd).unwrap();
2608 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2609
2610 let msg = receiver.peek().unwrap();
2611 process_mapping_command(&msg, &files).unwrap();
2612
2613 let file = files.get_file(100).expect("File should be loaded immediately");
2614 let (page_request, rx) =
2615 TestVecBuffer::new_with_range(0..(block_count as u64 * BLOCK_SIZE));
2616 file.read_range(service.as_ref(), page_request);
2617 assert_eq!(rx.output(), expected_data);
2618 }
2619
2620 #[fuchsia::test]
2621 fn test_process_mapping_command_encrypted() {
2622 struct NoRegisterBlobHandler;
2623 impl DeliveryHandler for NoRegisterBlobHandler {
2624 type Request = TestVecBuffer;
2625 fn get_page_request(self: &Arc<Self>, _key: u64, range: Range<u64>) -> Self::Request {
2626 TestVecBuffer::new_with_range(range).0
2627 }
2628 fn register_blob(
2629 &self,
2630 _key: u64,
2631 _merkle_leaves: &[[u8; 32]],
2632 _read_alignment: usize,
2633 ) -> Result<(), Error> {
2634 panic!("register_blob should not be called for encrypted files");
2635 }
2636 }
2637
2638 let block_count = 2;
2639 let mut plaintext = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
2640 for (i, byte) in plaintext.iter_mut().enumerate() {
2641 *byte = (i % 251) as u8;
2642 }
2643
2644 let raw_key = [0x5au8; 32];
2645 let key = UnwrappedKey::new(raw_key.to_vec());
2646 let cipher = FxfsCipher::new(&key);
2647
2648 let mut ciphertext = plaintext.clone();
2649 cipher.encrypt(0, 0, 0, 0, MutPtrByteSlice::from(&mut ciphertext[..])).unwrap();
2650
2651 let service = Arc::new(FakeBlockService::new(ciphertext));
2652 let files =
2653 Arc::new(Files::new(service.clone(), NoRegisterBlobHandler, zx::Port::create()));
2654
2655 let data_extents =
2656 Extents::try_new([Extent::new(0..(block_count as u64 * BLOCK_SIZE), Some(0))], 0)
2657 .unwrap();
2658 let mut payload_bytes = Vec::new();
2659 for w in Extents::encode_extents(&data_extents) {
2660 payload_bytes.extend_from_slice(&w.to_le_bytes());
2661 }
2662 payload_bytes.extend_from_slice(&raw_key);
2663
2664 let vmo = zx::Vmo::create(65536).unwrap();
2665 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
2666 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2667 1024,
2668 16,
2669 )
2670 .unwrap();
2671 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2672 payload_buf.data().copy_from_slice(&payload_bytes);
2673 let cmd = crate::RawMappingCommand {
2674 opcode: crate::MAPPINGS_COMMAND | crate::MAPPINGS_FLAG_ENCRYPTED,
2675 offset: payload_buf.offset(),
2676 key: 101,
2677 stored_size: block_count as u64 * BLOCK_SIZE,
2678 device_offset: 0,
2679 metadata_count: 0,
2680 extent_count: 1,
2681 };
2682 payload_buf.commit(cmd).unwrap();
2683
2684 let invalid_cmd = crate::RawMappingCommand {
2685 opcode: crate::MAPPINGS_COMMAND | crate::MAPPINGS_FLAG_ENCRYPTED,
2686 offset: 0,
2687 key: 102,
2688 stored_size: block_count as u64 * BLOCK_SIZE,
2689 device_offset: 0,
2690 metadata_count: 1,
2691 extent_count: 1,
2692 };
2693 sender.reserve_payload(0).unwrap().commit(invalid_cmd).unwrap();
2694
2695 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2696
2697 let msg = receiver.peek().unwrap();
2698 process_mapping_command(&msg, &files).unwrap();
2699 msg.pop().unwrap();
2700
2701 let invalid_msg = receiver.peek().unwrap();
2702 assert!(process_mapping_command(&invalid_msg, &files).is_err());
2703
2704 let file = files.get_file(101).expect("File should be loaded immediately");
2705 assert!(matches!(file.transform(), Transform::Encrypted(_)));
2706
2707 let (page_request, rx) =
2708 TestVecBuffer::new_with_range(0..(block_count as u64 * BLOCK_SIZE));
2709 file.read_range(service.as_ref(), page_request);
2710 assert_eq!(rx.output(), plaintext);
2711 }
2712
2713 #[fuchsia::test]
2714 fn test_queued_page_request_does_not_deadlock_single_completion_thread() {
2715 let data_size = TARGET_READ_AHEAD_SIZE as usize;
2717 let encoded_metadata = serialize_metadata(&BlobMetadata {
2718 merkle_leaves: MerkleLeaves::new(),
2719 format: BlobFormat::Uncompressed,
2720 });
2721 let mut device_data = vec![0xabu8; data_size + BLOCK_SIZE as usize];
2722 device_data[data_size..data_size + encoded_metadata.len()]
2723 .copy_from_slice(&encoded_metadata);
2724
2725 let block_service = DelayedBlockService::new_with_pool_capacity(device_data, 64 * 1024);
2727
2728 let (page_request, rx) = TestVecBuffer::new_with_range(0..4096);
2729 let page_request = Mutex::new(Some(page_request));
2730 let files = Arc::new(Files::new(
2731 block_service.clone(),
2732 TestDeliveryHandler(move |_key, _range| page_request.lock().take().unwrap()),
2733 zx::Port::create(),
2734 ));
2735 let _pager_thread = files.spawn_pager_thread();
2736
2737 let data_extents =
2738 Extents::try_new([Extent::new(0..TARGET_READ_AHEAD_SIZE, Some(0))], 0).unwrap();
2739 let meta_extents =
2740 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(TARGET_READ_AHEAD_SIZE))], 0)
2741 .unwrap();
2742
2743 let blob_key = 200;
2744
2745 files.begin_loading(blob_key);
2748 let files_clone = files.clone();
2749 read_blob_metadata(
2750 block_service.as_ref(),
2751 &meta_extents,
2752 TARGET_READ_AHEAD_SIZE,
2753 move |metadata| {
2754 let file = Arc::new(File::new(
2755 data_extents,
2756 metadata.uncompressed_size,
2757 metadata.compression_info.into(),
2758 ));
2759 files_clone.insert(blob_key, file);
2760 },
2761 );
2762
2763 files.handle_page_request(blob_key, 0..4096);
2768
2769 let block_completion_thread = std::thread::spawn({
2773 let block_service = block_service.clone();
2774 move || {
2775 for _ in 0..3 {
2776 block_service.wait_and_trigger_sync();
2777 }
2778 }
2779 });
2780
2781 block_completion_thread.join().unwrap();
2782
2783 assert_eq!(rx.wait_for_commits(2), vec![(0, 65536), (65536, 65536)]);
2786 assert_eq!(rx.output(), vec![0xabu8; data_size]);
2788 }
2789
2790 #[fuchsia::test]
2791 fn test_pager_thread_lifecycle() {
2792 let port = zx::Port::create();
2793 let service = Arc::new(FakeBlockService::new(vec![0u8; 4096]));
2794 let files = Arc::new(Files::new(
2795 service,
2796 TestDeliveryHandler(|_key, _range| TestVecBuffer::new(4096).0),
2797 port,
2798 ));
2799 let thread = files.spawn_pager_thread();
2800 drop(thread);
2801 }
2802
2803 #[fuchsia::test]
2804 fn test_pager_packet() {
2805 let port = zx::Port::create();
2806 let pager = zx::Pager::create(zx::PagerOptions::empty()).expect("create pager");
2807 let vmo = pager.create_vmo(zx::VmoOptions::empty(), &port, 1234, 4096).expect("create vmo");
2808
2809 let vmo_clone = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("duplicate vmo");
2810 let _reader_thread = std::thread::spawn(move || {
2811 let mut b = [0u8; 1];
2812 let _ = vmo_clone.read(&mut b, 0);
2813 });
2814
2815 let packet = port.wait(zx::MonotonicInstant::INFINITE).expect("wait packet");
2816 assert_eq!(packet.key(), 1234);
2817 if let zx::PacketContents::Pager(pager_packet) = packet.contents() {
2818 assert_eq!(pager_packet.command(), ZX_PAGER_VMO_READ);
2819 assert_eq!(pager_packet.range(), 0..4096);
2820 } else {
2821 panic!("Expected pager packet");
2822 }
2823 }
2824
2825 #[fuchsia::test]
2826 fn test_files_record_profile() {
2827 let total_size = TARGET_READ_AHEAD_SIZE * 2;
2828 let uncompressed_data = vec![0x55u8; total_size as usize];
2829
2830 let compressed_uncompressed_size = 192 * 1024;
2833 let chunk_size = 48 * 1024;
2834 let options = ChunkedArchiveOptions::V2 {
2835 minimum_chunk_size: chunk_size,
2836 chunk_alignment: chunk_size,
2837 compression_level: 1,
2838 };
2839 let archive = delivery_blob::compression::ChunkedArchive::new(
2840 &uncompressed_data[..compressed_uncompressed_size],
2841 options,
2842 )
2843 .expect("ChunkedArchive::new failed");
2844 let mut compressed_offsets = vec![0];
2845 let mut compressed_bytes = Vec::new();
2846 for chunk in archive.chunks() {
2847 compressed_bytes.extend_from_slice(&chunk.compressed_data);
2848 compressed_offsets.push(compressed_bytes.len() as u64);
2849 }
2850 compressed_offsets.pop();
2851 let compressed_stored_size = compressed_bytes.len() as u64;
2852 let compressed_stored_aligned = compressed_stored_size.next_multiple_of(BLOCK_SIZE);
2853
2854 let mut device_data = vec![0x55u8; (total_size + compressed_stored_aligned) as usize];
2855 device_data[total_size as usize..total_size as usize + compressed_bytes.len()]
2856 .copy_from_slice(&compressed_bytes);
2857 let service = Arc::new(FakeBlockService::new(device_data));
2858
2859 let files = Arc::new(Files::new(
2860 service,
2861 TestDeliveryHandler(|_key, range| TestVecBuffer::new_with_range(range).0),
2862 zx::Port::create(),
2863 ));
2864
2865 let extents1 = Extents::try_new([Extent::new(0..total_size, Some(0))], 0)
2866 .expect("Extents::try_new failed");
2867 let file1 = Arc::new(File::new(extents1, total_size, Transform::None));
2868 files.insert(10, file1);
2869
2870 let extents2 =
2871 Extents::try_new([Extent::new(0..compressed_stored_aligned, Some(total_size))], 0)
2872 .expect("Extents::try_new failed");
2873 let compression_info = CompressionInfo::new(
2874 chunk_size as u64,
2875 compressed_stored_size,
2876 &compressed_offsets,
2877 CompressionAlgorithm::Zstd,
2878 )
2879 .expect("CompressionInfo::new failed");
2880 let file2 = Arc::new(File::new(
2881 extents2,
2882 compressed_uncompressed_size as u64,
2883 compression_info.into(),
2884 ));
2885 let compressed_read_ahead = file2.read_ahead_size();
2886 assert_eq!(compressed_read_ahead, 96 * 1024);
2887 files.insert(20, file2);
2888
2889 assert_eq!(
2891 files.stop_recording().expect_err("stop_recording succeeded without active recording"),
2892 zx::Status::BAD_STATE
2893 );
2894
2895 files.handle_page_request(10, 0..4096);
2897
2898 files.start_recording().expect("start_recording failed");
2899 assert_eq!(
2900 files.start_recording().expect_err("duplicate start_recording succeeded"),
2901 zx::Status::BAD_STATE
2902 );
2903
2904 files.handle_page_request(10, 4096..8192);
2906 files.handle_page_request(20, 100 * 1024..104 * 1024);
2908 files.handle_page_request(10, 0..total_size);
2910
2911 let vmo = files.stop_recording().expect("stop_recording failed");
2912 let entries: Vec<_> = FileMessage::decode_from_vmo(vmo, BLOCK_SIZE as usize)
2913 .expect("decode_from_vmo failed")
2914 .collect();
2915 assert_eq!(
2916 entries,
2917 vec![
2918 FileMessage { id: 10, offset: 0 },
2919 FileMessage { id: 20, offset: compressed_read_ahead },
2920 FileMessage { id: 10, offset: 0 },
2921 FileMessage { id: 10, offset: TARGET_READ_AHEAD_SIZE },
2922 ]
2923 );
2924
2925 files.handle_page_request(20, 0..4096);
2928 files.start_recording().expect("second start_recording failed");
2929 let empty_vmo = files.stop_recording().expect("second stop_recording failed");
2930 assert_eq!(
2931 FileMessage::decode_from_vmo(empty_vmo, BLOCK_SIZE as usize)
2932 .expect("decode_from_vmo failed")
2933 .collect::<Vec<_>>(),
2934 Vec::<FileMessage>::new()
2935 );
2936 }
2937
2938 #[fuchsia::test]
2939 fn test_files_record_profile_deferred_loading() {
2940 let total_size = TARGET_READ_AHEAD_SIZE;
2941 let device_data = vec![0x77u8; total_size as usize];
2942 let service = Arc::new(FakeBlockService::new(device_data));
2943
2944 let (page_request, rx) = TestVecBuffer::new_with_range(4096..8192);
2945 let page_request = Mutex::new(Some(page_request));
2946 let files = Arc::new(Files::new(
2947 service,
2948 TestDeliveryHandler(move |_key, _range| {
2949 page_request.lock().take().expect("page_request already taken")
2950 }),
2951 zx::Port::create(),
2952 ));
2953 let _pager_thread = files.spawn_pager_thread();
2954
2955 files.start_recording().expect("start_recording failed");
2956
2957 files.handle_page_request(55, 4096..8192);
2959 assert!(files.has_queued_page_requests(55));
2960
2961 let extents = Extents::try_new([Extent::new(0..total_size, Some(0))], 0)
2962 .expect("Extents::try_new failed");
2963 let file = Arc::new(File::new(extents, total_size, Transform::None));
2964 files.insert(55, file);
2965
2966 assert_eq!(rx.wait_for_commits(1), vec![(0, total_size as usize)]);
2967
2968 let vmo = files.stop_recording().expect("stop_recording failed");
2969 let entries: Vec<_> = FileMessage::decode_from_vmo(vmo, BLOCK_SIZE as usize)
2970 .expect("decode_from_vmo failed")
2971 .collect();
2972 assert_eq!(entries, vec![FileMessage { id: 55, offset: 0 }]);
2973 }
2974
2975 #[test]
2976 fn test_child_range_and_inline_tail() {
2977 #[derive(Debug, PartialEq)]
2978 enum RecordedEvent {
2979 InlineTail {
2980 parent_byte_offset: u64,
2981 child_key: u64,
2982 child_target_offset: u64,
2983 tail_length: u32,
2984 },
2985 ChildPageRequest {
2986 child_key: u64,
2987 child_target_offset: u64,
2988 parent_byte_range: Range<u64>,
2989 },
2990 }
2991
2992 struct RecordingChildHandler {
2993 events: Mutex<Vec<RecordedEvent>>,
2994 }
2995 impl DeliveryHandler for RecordingChildHandler {
2996 type Request = TestVecBuffer;
2997 fn get_page_request(self: &Arc<Self>, _key: u64, range: Range<u64>) -> Self::Request {
2998 TestVecBuffer::new_with_range(range).0
2999 }
3000 fn get_child_page_request(
3001 self: &Arc<Self>,
3002 child_key: u64,
3003 child_target_offset: u64,
3004 parent_byte_range: Range<u64>,
3005 ) -> Self::Request {
3006 self.events.lock().push(RecordedEvent::ChildPageRequest {
3007 child_key,
3008 child_target_offset,
3009 parent_byte_range: parent_byte_range.clone(),
3010 });
3011 TestVecBuffer::new_with_range(parent_byte_range).0
3012 }
3013 fn deliver_child_inline_tail(
3014 self: &Arc<Self>,
3015 parent_byte_offset: u64,
3016 child_key: u64,
3017 child_target_offset: u64,
3018 tail_length: u32,
3019 ) {
3020 self.events.lock().push(RecordedEvent::InlineTail {
3021 parent_byte_offset,
3022 child_key,
3023 child_target_offset,
3024 tail_length,
3025 });
3026 }
3027 }
3028
3029 let data_size = (TARGET_READ_AHEAD_SIZE * 2) as usize;
3030 let mut expected_data = vec![0u8; data_size];
3031 for (i, byte) in expected_data.iter_mut().enumerate() {
3032 *byte = ((i * 31 + 7) % 251) as u8;
3033 }
3034 let service = FakeBlockService::new(expected_data);
3035 let parent_extents =
3036 Extents::try_new([Extent::new(0..(data_size as u64), Some(0))], 0).unwrap();
3037 let parent_file = Arc::new(File::new(parent_extents, data_size as u64, Transform::None));
3038
3039 let child_extents = Extents::try_new(
3042 [
3043 Extent::new(0..4096, Some(16384)),
3044 Extent::new(4096..8192, crate::DeviceExtent::InlineTail(4128)),
3045 ],
3046 0,
3047 )
3048 .unwrap();
3049 let child_file = Arc::new(File::new(child_extents, 4608, Transform::None));
3050
3051 let handler = Arc::new(RecordingChildHandler { events: Mutex::new(Vec::new()) });
3052 let started_async =
3053 child_file.read_child_range(10, 0..4096, &parent_file, &service, &handler);
3054 assert!(started_async, "Expected Block extent to perform one aligned parent read");
3055 assert_eq!(service.read_count(), 1, "InlineTail must not perform any block I/O");
3056
3057 let events = handler.events.lock();
3058 assert_eq!(
3059 &*events,
3060 &[
3061 RecordedEvent::ChildPageRequest {
3062 child_key: 10,
3063 child_target_offset: 0,
3064 parent_byte_range: 16384..20480,
3065 },
3066 RecordedEvent::InlineTail {
3067 parent_byte_offset: 4128,
3068 child_key: 10,
3069 child_target_offset: 4096,
3070 tail_length: 512,
3071 },
3072 ]
3073 );
3074 }
3075}