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::{CompressionAlgorithm, CompressionInfo, StreamingDecompressor};
14use fuchsia_sync::Mutex;
15use futures::channel::oneshot;
16use fxfs_crypto::{Cipher, FxfsCipher, UnwrappedKey};
17use std::borrow::Borrow;
18use std::cmp::min;
19use std::collections::hash_map::{Entry, HashMap};
20use std::ops::{ControlFlow, Range};
21use std::sync::Arc;
22use storage_profile::{FileMessage, Message as _};
23use vmo_fifo::Message;
24use zx::sys::zx_page_request_command_t::ZX_PAGER_VMO_READ;
25
26pub const TARGET_READ_AHEAD_SIZE: u64 = 128 * 1024;
31
32pub const DEFAULT_VERIFICATION_ALIGNMENT: u64 = 32 * 1024;
35
36fn read_ahead_size_for_chunk_size(compression_chunk_size: u64, target_read_ahead_size: u64) -> u64 {
40 if compression_chunk_size >= target_read_ahead_size {
41 compression_chunk_size
42 } else {
43 (target_read_ahead_size / compression_chunk_size) * compression_chunk_size
44 }
45}
46
47#[derive(Default)]
49pub enum Transform {
50 #[default]
52 None,
53 Compressed(CompressionInfo),
55 Encrypted(Arc<dyn Cipher>),
57}
58
59impl std::fmt::Debug for Transform {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 match self {
62 Self::None => write!(f, "Transform::None"),
63 Self::Compressed(_) => write!(f, "Transform::Compressed(..)"),
64 Self::Encrypted(cipher) => f.debug_tuple("Transform::Encrypted").field(cipher).finish(),
65 }
66 }
67}
68
69impl From<Option<CompressionInfo>> for Transform {
70 fn from(info: Option<CompressionInfo>) -> Self {
71 match info {
72 Some(info) => Self::Compressed(info),
73 None => Self::None,
74 }
75 }
76}
77
78impl From<CompressionInfo> for Transform {
79 fn from(info: CompressionInfo) -> Self {
80 Self::Compressed(info)
81 }
82}
83
84impl From<Arc<dyn Cipher>> for Transform {
85 fn from(cipher: Arc<dyn Cipher>) -> Self {
86 Self::Encrypted(cipher)
87 }
88}
89
90#[derive(Clone)]
91struct FileCompression(Arc<File>);
92
93impl Borrow<CompressionInfo> for FileCompression {
94 fn borrow(&self) -> &CompressionInfo {
95 self.0.compression_info().expect("compression_info missing")
96 }
97}
98
99pub struct File {
101 extents: Extents,
102 uncompressed_size: u64,
103 transform: Transform,
104}
105
106impl File {
107 pub fn new(extents: Extents, uncompressed_size: u64, transform: Transform) -> Self {
108 Self { extents, uncompressed_size, transform }
109 }
110
111 pub fn extents(&self) -> &Extents {
113 &self.extents
114 }
115
116 pub fn uncompressed_size(&self) -> u64 {
118 self.uncompressed_size
119 }
120
121 pub fn transform(&self) -> &Transform {
123 &self.transform
124 }
125
126 pub fn compression_info(&self) -> Option<&CompressionInfo> {
128 match &self.transform {
129 Transform::Compressed(info) => Some(info),
130 _ => None,
131 }
132 }
133
134 pub fn cipher(&self) -> Option<&Arc<dyn Cipher>> {
136 match &self.transform {
137 Transform::Encrypted(cipher) => Some(cipher),
138 _ => None,
139 }
140 }
141
142 pub fn read_ahead_size(&self) -> u64 {
144 match &self.transform {
145 Transform::Compressed(info) => {
146 read_ahead_size_for_chunk_size(info.chunk_size(), TARGET_READ_AHEAD_SIZE)
147 }
148 Transform::None | Transform::Encrypted(_) => TARGET_READ_AHEAD_SIZE,
149 }
150 }
151
152 pub fn verification_alignment(&self) -> u64 {
155 match &self.transform {
156 Transform::Compressed(info) => info.chunk_size(),
157 Transform::None | Transform::Encrypted(_) => DEFAULT_VERIFICATION_ALIGNMENT,
158 }
159 }
160
161 pub fn read_range(
163 self: &Arc<Self>,
164 service: &(impl BlockService + ?Sized),
165 page_request: impl PageRequest,
166 ) {
167 self.read_range_with_recording(service, page_request, |_| {});
168 }
169
170 fn read_range_with_recording(
171 self: &Arc<Self>,
172 service: &(impl BlockService + ?Sized),
173 mut page_request: impl PageRequest,
174 record_offset: impl Fn(u64),
175 ) {
176 let page_size = zx::system_get_page_size() as u64;
177 let original_range = page_request.range();
178 if original_range.is_empty() {
179 return;
180 }
181
182 let page_aligned_size = self.uncompressed_size.next_multiple_of(page_size);
183 if original_range.start >= page_aligned_size {
184 return;
185 }
186
187 let read_ahead_size = self.read_ahead_size();
188
189 let read_range = (original_range.start / read_ahead_size) * read_ahead_size
190 ..std::cmp::min(
191 original_range.end.next_multiple_of(read_ahead_size),
192 page_aligned_size,
193 );
194 if page_request.prepare(read_range.clone(), self.verification_alignment() as usize).is_err()
195 {
196 return;
197 }
198
199 for offset in (read_range.start..read_range.end).step_by(read_ahead_size as usize) {
200 record_offset(offset);
201 }
202
203 match &self.transform {
204 Transform::None => {
205 let mut current_offset = read_range.start;
206 let uncompressed_size = self.uncompressed_size;
207
208 read_aligned_range(&self.extents, read_range, service, move |res| {
209 let buffer = match res {
210 Ok(buffer) => buffer,
211 Err(error) => {
212 log::error!(error:?; "Failed to read blocks for mapped file");
213 return ControlFlow::Break(());
214 }
215 };
216 let valid_len =
217 min(buffer.len() as u64, uncompressed_size.saturating_sub(current_offset))
218 as usize;
219 let dest = page_request.mut_ptr_slice().subslice_mut(0..buffer.len());
220 let (mut head, mut tail) = dest.split_at_mut(valid_len);
221 head.copy_from_ptr_slice(buffer.as_ptr_slice().subslice(0..valid_len));
222 tail.fill(0);
223 if page_request.commit(buffer.len()).is_err() {
224 return ControlFlow::Break(());
225 }
226 current_offset += buffer.len() as u64;
227 ControlFlow::Continue(())
228 });
229 }
230 Transform::Compressed(_) => {
231 let Ok((mut decompressor, aligned_range)) = StreamingDecompressor::new(
232 FileCompression(self.clone()),
233 self.uncompressed_size,
234 page_request,
235 ) else {
236 return;
239 };
240
241 read_aligned_range(&self.extents, aligned_range, service, move |res| {
242 let buffer = match res {
243 Ok(buffer) => buffer,
244 Err(error) => {
245 log::error!(
246 error:?;
247 "Failed to read blocks for compressed mapped file"
248 );
249 return ControlFlow::Break(());
250 }
251 };
252 if decompressor.push(buffer.as_ptr_slice()).is_err() {
253 return ControlFlow::Break(());
254 }
255 ControlFlow::Continue(())
256 });
257 }
258 Transform::Encrypted(cipher) => {
259 let mut current_offset = read_range.start;
260 let uncompressed_size = self.uncompressed_size;
261 let cipher = cipher.clone();
262
263 read_aligned_range(&self.extents, read_range, service, move |res| {
264 let buffer = match res {
265 Ok(buffer) => buffer,
266 Err(error) => {
267 log::error!(error:?; "Failed to read blocks for encrypted mapped file");
268 return ControlFlow::Break(());
269 }
270 };
271 let mut dest = page_request.mut_ptr_slice().subslice_mut(0..buffer.len());
272 if let Err(error) = cipher.decrypt_to(
274 0,
275 0,
276 0,
277 current_offset,
278 buffer.as_ptr_slice(),
279 dest.reborrow(),
280 ) {
281 log::error!(error:?; "Failed to decrypt buffer");
282 return ControlFlow::Break(());
283 }
284 let valid_len =
285 min(buffer.len() as u64, uncompressed_size.saturating_sub(current_offset))
286 as usize;
287 dest.subslice_mut(valid_len..buffer.len()).fill(0);
288 if page_request.commit(buffer.len()).is_err() {
289 return ControlFlow::Break(());
290 }
291 current_offset += buffer.len() as u64;
292 ControlFlow::Continue(())
293 });
294 }
295 }
296 }
297}
298
299struct LoadingSlot<R> {
300 requests: Vec<R>,
301 waiters: Vec<oneshot::Sender<Arc<File>>>,
302}
303
304impl<R> Default for LoadingSlot<R> {
305 fn default() -> Self {
306 Self { requests: Vec::new(), waiters: Vec::new() }
307 }
308}
309
310enum FileEntry<R> {
311 Loading(LoadingSlot<R>),
312 Loaded(Arc<File>),
313}
314
315impl<R> Default for FileEntry<R> {
316 fn default() -> Self {
317 Self::Loading(LoadingSlot::default())
318 }
319}
320
321pub trait DeliveryHandler: Send + Sync + 'static {
323 type Request: PageRequest;
324
325 fn get_page_request(self: &Arc<Self>, key: u64, range: Range<u64>) -> Self::Request;
327
328 fn register_blob(
331 &self,
332 _key: u64,
333 _merkle_leaves: &[[u8; 32]],
334 _read_alignment: usize,
335 ) -> Result<(), Error> {
336 Ok(())
337 }
338
339 fn unregister_file(&self, _key: u64) {}
341}
342
343pub struct NoopDeliveryHandler;
346
347impl DeliveryHandler for NoopDeliveryHandler {
348 type Request = NullPageRequest;
349
350 fn get_page_request(self: &Arc<Self>, _key: u64, _range: Range<u64>) -> NullPageRequest {
351 NullPageRequest
352 }
353}
354
355const PAGER_SHUTDOWN_KEY: u64 = 0;
356const PAGER_WAKE_KEY: u64 = 1;
357
358#[must_use = "Dropping PagerThread immediately stops the background pager thread"]
363pub struct PagerThread<S: ?Sized, D: DeliveryHandler> {
364 files: Arc<Files<S, D>>,
365 thread: Option<std::thread::JoinHandle<()>>,
366}
367
368impl<S: ?Sized, D: DeliveryHandler> Drop for PagerThread<S, D> {
369 fn drop(&mut self) {
370 let packet = zx::Packet::from_user_packet(
371 PAGER_SHUTDOWN_KEY,
372 0,
373 zx::UserPacket::from_u8_array([0; 32]),
374 );
375 let _ = self.files.port.queue(&packet);
376 if let Some(thread) = self.thread.take() {
377 let _ = thread.join();
378 }
379 }
380}
381
382const MAX_PROFILE_VMO_SIZE: u64 = 64 * 1024 * 1024;
385
386struct ProfileRecording {
387 vmo: zx::Vmo,
388 vmo_offset: u64,
389 buffer: Vec<u8>,
390}
391
392impl ProfileRecording {
393 fn new() -> Result<Self, zx::Status> {
394 let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0)?;
395 Ok(Self { vmo, vmo_offset: 0, buffer: Vec::with_capacity(BLOCK_SIZE as usize) })
396 }
397
398 fn record(&mut self, key: u64, offset: u64) {
399 const MSG_SIZE: usize = std::mem::size_of::<FileMessage>();
400 const { assert!((BLOCK_SIZE as usize).is_multiple_of(MSG_SIZE)) };
403
404 if self.vmo_offset >= MAX_PROFILE_VMO_SIZE {
405 return;
406 }
407 let mut buf = [0u8; MSG_SIZE];
408 FileMessage { id: key, offset }.encode_to((&mut buf[..]).into());
409 self.buffer.extend_from_slice(&buf);
410 if self.buffer.len() == BLOCK_SIZE as usize
411 && let Err(error) = self.flush()
412 {
413 log::warn!(error:?; "Failed to flush profile buffer to VMO");
414 }
415 }
416
417 fn flush(&mut self) -> Result<(), zx::Status> {
418 let new_size = self.vmo_offset + BLOCK_SIZE;
419 let res = self.vmo.set_size(new_size).and_then(|()| {
420 self.vmo.write(&self.buffer, self.vmo_offset)?;
421 self.vmo_offset = new_size;
422 Ok(())
423 });
424 self.buffer.clear();
425 res
426 }
427
428 fn finish(mut self) -> Result<zx::Vmo, zx::Status> {
429 if !self.buffer.is_empty() {
430 self.flush()?;
431 }
432 Ok(self.vmo)
433 }
434}
435
436pub struct Files<S: ?Sized, D: DeliveryHandler> {
438 service: Arc<S>,
439 delivery_handler: Arc<D>,
440 port: zx::Port,
441 map: Mutex<HashMap<u64, FileEntry<D::Request>>>,
442 deferred_page_requests: Mutex<Vec<(u64, Arc<File>, D::Request)>>,
443 recording: Mutex<Option<ProfileRecording>>,
445}
446
447impl<S: BlockService + ?Sized, D: DeliveryHandler> Files<S, D> {
448 pub fn new(service: Arc<S>, delivery_handler: D, port: zx::Port) -> Self {
450 Self {
451 service,
452 delivery_handler: Arc::new(delivery_handler),
453 port,
454 map: Mutex::new(HashMap::new()),
455 deferred_page_requests: Mutex::new(Vec::new()),
456 recording: Mutex::new(None),
457 }
458 }
459
460 pub fn start_recording(&self) -> Result<(), zx::Status> {
468 let mut recording = self.recording.lock();
469 if recording.is_some() {
470 return Err(zx::Status::BAD_STATE);
471 }
472 *recording = Some(ProfileRecording::new()?);
473 Ok(())
474 }
475
476 pub fn is_recording(&self) -> bool {
477 self.recording.lock().is_some()
478 }
479
480 pub fn stop_recording(&self) -> Result<zx::Vmo, zx::Status> {
485 let recording = self.recording.lock().take().ok_or(zx::Status::BAD_STATE)?;
486 recording.finish()
487 }
488
489 pub fn abort_recording(&self) {
491 *self.recording.lock() = None;
492 }
493
494 fn read_file(&self, key: u64, file: &Arc<File>, req: D::Request) {
497 file.read_range_with_recording(self.service.as_ref(), req, |offset| {
498 if let Some(recording) = self.recording.lock().as_mut() {
499 recording.record(key, offset);
500 }
501 });
502 }
503
504 pub fn spawn_pager_thread(self: &Arc<Self>) -> PagerThread<S, D>
506 where
507 S: 'static,
508 {
509 let files = self.clone();
510 let thread = std::thread::spawn(move || {
511 files.run_pager_loop();
512 });
513 PagerThread { files: self.clone(), thread: Some(thread) }
514 }
515
516 fn run_pager_loop(&self) {
519 while let Ok(packet) = self.port.wait(zx::MonotonicInstant::INFINITE) {
520 match packet.contents() {
521 zx::PacketContents::User(_) => match packet.key() {
522 PAGER_SHUTDOWN_KEY => break,
523 PAGER_WAKE_KEY => {
524 let requests = std::mem::take(&mut *self.deferred_page_requests.lock());
525 for (key, file, req) in requests {
526 self.read_file(key, &file, req);
527 }
528 }
529 _ => {}
530 },
531 zx::PacketContents::Pager(pager_packet)
532 if pager_packet.command() == ZX_PAGER_VMO_READ =>
533 {
534 self.handle_page_request(packet.key(), pager_packet.range());
535 }
536 _ => {}
537 }
538 }
539 }
540
541 pub fn service(&self) -> &Arc<S> {
543 &self.service
544 }
545
546 pub fn register_blob(
549 &self,
550 key: u64,
551 merkle_leaves: &[[u8; 32]],
552 read_alignment: usize,
553 ) -> Result<(), Error> {
554 self.delivery_handler.register_blob(key, merkle_leaves, read_alignment)
555 }
556
557 fn handle_page_request(&self, key: u64, range: Range<u64>) {
563 let req = self.delivery_handler.get_page_request(key, range);
564 let mut map = self.map.lock();
565 match map.entry(key) {
566 Entry::Occupied(mut entry) => match entry.get_mut() {
567 FileEntry::Loaded(file) => {
568 let file = Arc::clone(file);
569 drop(map);
570 self.read_file(key, &file, req);
571 }
572 FileEntry::Loading(slot) => {
573 slot.requests.push(req);
574 }
575 },
576 Entry::Vacant(entry) => {
577 entry.insert(FileEntry::Loading(LoadingSlot {
585 requests: vec![req],
586 waiters: Vec::new(),
587 }));
588 }
589 }
590 }
591
592 pub fn begin_loading(&self, key: u64) {
595 self.map.lock().entry(key).or_default();
596 }
597
598 fn insert(&self, key: u64, file: Arc<File>) {
601 let (reqs, waiters) = {
602 let mut map = self.map.lock();
603 let prev = map.insert(key, FileEntry::Loaded(file.clone()));
604 match prev {
605 Some(FileEntry::Loading(slot)) => (slot.requests, slot.waiters),
606 _ => (Vec::new(), Vec::new()),
607 }
608 };
609
610 for waiter in waiters {
611 let _ = waiter.send(file.clone());
612 }
613
614 if !reqs.is_empty() {
615 self.deferred_page_requests
622 .lock()
623 .extend(reqs.into_iter().map(|req| (key, file.clone(), req)));
624 let packet = zx::Packet::from_user_packet(
625 PAGER_WAKE_KEY,
626 0,
627 zx::UserPacket::from_u8_array([0; 32]),
628 );
629 let _ = self.port.queue(&packet);
630 }
631 }
632
633 pub fn get_file(&self, key: u64) -> Option<Arc<File>> {
635 let map = self.map.lock();
636 match map.get(&key) {
637 Some(FileEntry::Loaded(file)) => Some(file.clone()),
638 _ => None,
639 }
640 }
641
642 pub async fn wait_for_file(&self, key: u64) -> Result<Arc<File>, Error> {
645 let receiver = {
646 let mut map = self.map.lock();
647 match map.entry(key) {
648 Entry::Occupied(mut entry) => match entry.get_mut() {
649 FileEntry::Loaded(file) => return Ok(file.clone()),
650 FileEntry::Loading(slot) => {
651 let (sender, receiver) = oneshot::channel();
652 slot.waiters.push(sender);
653 receiver
654 }
655 },
656 Entry::Vacant(entry) => {
657 let (sender, receiver) = oneshot::channel();
658 entry.insert(FileEntry::Loading(LoadingSlot {
659 requests: Vec::new(),
660 waiters: vec![sender],
661 }));
662 receiver
663 }
664 }
665 };
666
667 receiver.await.map_err(|_| anyhow!("File loading cancelled"))
668 }
669
670 pub fn remove(&self, key: u64) {
672 self.delivery_handler.unregister_file(key);
673 self.map.lock().remove(&key);
674 }
675
676 #[cfg(test)]
678 pub fn is_loading(&self, key: u64) -> bool {
679 matches!(self.map.lock().get(&key), Some(FileEntry::Loading(_)))
680 }
681
682 #[cfg(test)]
685 pub fn has_queued_page_requests(&self, key: u64) -> bool {
686 matches!(
687 self.map.lock().get(&key),
688 Some(FileEntry::Loading(slot)) if !slot.requests.is_empty()
689 )
690 }
691
692 #[cfg(test)]
694 pub fn is_loaded(&self, key: u64) -> bool {
695 matches!(self.map.lock().get(&key), Some(FileEntry::Loaded(_)))
696 }
697}
698
699impl<S: BlockService + ?Sized> Files<S, NoopDeliveryHandler> {
700 pub fn new_without_pager(service: Arc<S>) -> Self {
702 Self::new(service, NoopDeliveryHandler, zx::Port::create())
703 }
704}
705
706const BLOB_METADATA_VERSION: u32 = 53;
709
710fn deserialize_blob_metadata(mut bytes: &[u8]) -> Result<BlobMetadata, anyhow::Error> {
712 use bincode::Options;
713 let options = bincode::DefaultOptions::new().allow_trailing_bytes();
714
715 let version = bytes.read_u32::<LittleEndian>()?;
716 if version < BLOB_METADATA_VERSION {
717 bail!(
718 "Unsupported blob metadata version: {} (expected >= {})",
719 version,
720 BLOB_METADATA_VERSION
721 );
722 }
723
724 options
725 .deserialize::<BlobMetadata>(bytes)
726 .map_err(|e| anyhow!("Failed to deserialize BlobMetadata: {e:?}"))
727}
728
729pub struct DecodedBlobMetadata {
732 pub uncompressed_size: u64,
733 pub compression_info: Option<CompressionInfo>,
734 pub merkle_leaves: MerkleLeaves,
735}
736
737pub fn read_blob_metadata(
743 service: &(impl BlockService + ?Sized),
744 metadata_extents: &Extents,
745 stored_data_size: u64,
746 callback: impl FnOnce(DecodedBlobMetadata) + Send + 'static,
747) {
748 let mut total_metadata_len = 0u64;
749 for extent in metadata_extents.iter_extents(0) {
750 total_metadata_len += extent.len();
751 }
752 if total_metadata_len == 0 {
753 callback(DecodedBlobMetadata {
754 uncompressed_size: stored_data_size,
755 compression_info: None,
756 merkle_leaves: MerkleLeaves::new(),
757 });
758 return;
759 }
760
761 let mut callback = Some(callback);
762 let mut metadata_bytes = Some(Vec::with_capacity(total_metadata_len as usize));
763 read_aligned_range(metadata_extents, 0..total_metadata_len, service, move |res| {
764 let Ok(buffer) = res else {
765 log::error!(error:? = res.unwrap_err(); "Failed to read metadata blocks");
766 return ControlFlow::Break(());
767 };
768 let bytes = metadata_bytes.as_mut().unwrap();
769 let copy_len = min(buffer.len(), total_metadata_len as usize - bytes.len());
770 buffer.as_ptr_slice().subslice(0..copy_len).append_to(bytes);
771 if bytes.len() < total_metadata_len as usize {
772 return ControlFlow::Continue(());
773 }
774
775 let metadata_bytes = metadata_bytes.take().unwrap();
776 let cb = callback.take().unwrap();
777 let metadata = match deserialize_blob_metadata(&metadata_bytes) {
778 Ok(metadata) => metadata,
779 Err(error) => {
780 log::error!(error:?; "Failed to deserialize BlobMetadata");
781 return ControlFlow::Break(());
782 }
783 };
784
785 let merkle_leaves = metadata.merkle_leaves;
786 let res = match metadata.format {
787 BlobFormat::Uncompressed => DecodedBlobMetadata {
788 uncompressed_size: stored_data_size,
789 compression_info: None,
790 merkle_leaves,
791 },
792 BlobFormat::ChunkedZstd { uncompressed_size, chunk_size, compressed_offsets } => {
793 match CompressionInfo::new(
794 chunk_size,
795 stored_data_size,
796 &compressed_offsets,
797 CompressionAlgorithm::Zstd,
798 ) {
799 Ok(compression_info) => DecodedBlobMetadata {
800 uncompressed_size,
801 compression_info: Some(compression_info),
802 merkle_leaves,
803 },
804 Err(error) => {
805 log::error!(error:?; "Failed to parse Zstd CompressionInfo");
806 return ControlFlow::Break(());
807 }
808 }
809 }
810 BlobFormat::ChunkedLz4 { uncompressed_size, chunk_size, compressed_offsets } => {
811 match CompressionInfo::new(
812 chunk_size,
813 stored_data_size,
814 &compressed_offsets,
815 CompressionAlgorithm::Lz4,
816 ) {
817 Ok(compression_info) => DecodedBlobMetadata {
818 uncompressed_size,
819 compression_info: Some(compression_info),
820 merkle_leaves,
821 },
822 Err(error) => {
823 log::error!(error:?; "Failed to parse Lz4 CompressionInfo");
824 return ControlFlow::Break(());
825 }
826 }
827 }
828 };
829 cb(res);
830 ControlFlow::Break(())
831 });
832}
833
834struct LoadingFileGuard<S: BlockService + ?Sized + 'static, D: DeliveryHandler> {
846 files: Option<Arc<Files<S, D>>>,
847 key: u64,
848}
849
850impl<S: BlockService + ?Sized + 'static, D: DeliveryHandler> LoadingFileGuard<S, D> {
851 fn commit(mut self, file: Arc<File>) {
854 self.files.take().unwrap().insert(self.key, file);
855 }
856}
857
858impl<S: BlockService + ?Sized + 'static, D: DeliveryHandler> Drop for LoadingFileGuard<S, D> {
859 fn drop(&mut self) {
860 if let Some(files) = self.files.take() {
861 files.remove(self.key);
862 }
863 }
864}
865
866pub fn process_mapping_command<S: BlockService + ?Sized + 'static, D: DeliveryHandler>(
870 msg: &Message<'_, RawMappingCommand>,
871 files: &Arc<Files<S, D>>,
872) -> Result<(), Error> {
873 let cmd = **msg;
874 match MappingCommand::try_from(cmd)? {
875 MappingCommand::Mappings {
876 key,
877 offset,
878 stored_size,
879 device_offset,
880 metadata_count,
881 extent_count,
882 encrypted,
883 } => {
884 if encrypted && metadata_count > 0 {
885 bail!("Encrypted files cannot have blob metadata");
886 }
887 let extent_bytes_len = (extent_count as usize)
888 .checked_mul(8)
889 .ok_or_else(|| anyhow!("Overflow calculating extent byte length"))?;
890 let metadata_bytes_len = (metadata_count as usize)
891 .checked_mul(8)
892 .ok_or_else(|| anyhow!("Overflow calculating metadata extent byte length"))?;
893 let key_bytes_len = if encrypted { ENCRYPTION_KEY_SIZE } else { 0 };
894 let total_bytes_len = extent_bytes_len
895 .checked_add(metadata_bytes_len)
896 .and_then(|len| len.checked_add(key_bytes_len))
897 .ok_or_else(|| anyhow!("Overflow calculating total extent byte length"))?;
898 let payload_len: u32 = total_bytes_len
899 .try_into()
900 .map_err(|_| anyhow!("Extent byte length exceeds u32"))?;
901
902 let payload_bytes = msg.payload_slice(offset, payload_len);
903 let data_bytes = payload_bytes.subslice(0..extent_bytes_len);
904 let data_extents = Extents::from_encoded(data_bytes.iter_as::<u64>(), device_offset)
905 .ok_or_else(|| anyhow!("Failed to decode data extents"))?;
906
907 if encrypted {
908 let key_bytes = payload_bytes.subslice(extent_bytes_len..total_bytes_len);
909 let cipher_key = UnwrappedKey::new(key_bytes.to_vec());
910 let cipher = Arc::new(FxfsCipher::new(&cipher_key)) as Arc<dyn Cipher>;
911 let file =
912 Arc::new(File::new(data_extents, stored_size, Transform::Encrypted(cipher)));
913 files.insert(key, file);
914 return Ok(());
915 }
916
917 let metadata_bytes = payload_bytes.subslice(extent_bytes_len..total_bytes_len);
918 let metadata_extents =
919 Extents::from_encoded(metadata_bytes.iter_as::<u64>(), device_offset)
920 .ok_or_else(|| anyhow!("Failed to decode metadata extents"))?;
921
922 files.begin_loading(key);
923 let service = files.service().clone();
924 let guard = LoadingFileGuard { files: Some(files.clone()), key };
925 let files_clone = files.clone();
926 read_blob_metadata(service.as_ref(), &metadata_extents, stored_size, move |metadata| {
927 let file = Arc::new(File::new(
928 data_extents,
929 metadata.uncompressed_size,
930 metadata.compression_info.into(),
931 ));
932 if let Err(error) = files_clone.register_blob(
933 key,
934 &metadata.merkle_leaves,
935 file.verification_alignment() as usize,
936 ) {
937 log::error!(error:?; "Failed to register blob {key}");
938 return;
939 }
940 guard.commit(file);
941 });
942 Ok(())
943 }
944 MappingCommand::CloseBlob { key } => {
945 files.remove(key);
946 Ok(())
947 }
948 }
949}
950
951#[cfg(test)]
952mod tests {
953 use super::*;
954 use crate::reader::tests::FakeBlockService;
955 use crate::reader::{MAX_READ_BUFFER_SIZE, OwnedBuffer};
956 use crate::testing::{TestDeliveryHandler, TestVecBuffer};
957 use crate::{BLOCK_SIZE, Extent};
958 use anyhow::Error;
959 use bincode::Options;
960 use byteorder::WriteBytesExt;
961 use delivery_blob::compression::{ChunkedArchiveOptions, CompressionAlgorithm};
962 use fuchsia_async as fasync;
963 use fxfs_crypto::{Cipher, FxfsCipher, UnwrappedKey};
964 use std::sync::Arc;
965 use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
966 use storage_ptr_slice::MutPtrByteSlice;
967
968 fn serialize_metadata(metadata: &BlobMetadata) -> Vec<u8> {
969 let mut bytes = Vec::new();
970 bytes.write_u32::<LittleEndian>(BLOB_METADATA_VERSION).unwrap();
971 bincode::DefaultOptions::new()
972 .allow_trailing_bytes()
973 .serialize_into(&mut bytes, metadata)
974 .unwrap();
975 bytes
976 }
977
978 #[test]
979 fn test_read_range_uncompressed() {
980 let block_count = 8;
981 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
982 for (i, byte) in expected_data.iter_mut().enumerate() {
983 *byte = (i % 255) as u8;
984 }
985 let service = FakeBlockService::new(expected_data.clone());
986
987 let extents = Extents::try_new([Extent::new(0..(8 * BLOCK_SIZE), Some(0))], 0).unwrap();
988 let file = Arc::new(File::new(extents, 8 * BLOCK_SIZE, Transform::None));
989
990 let (page_request, rx) = TestVecBuffer::new_with_range(0..(8 * BLOCK_SIZE));
991 file.read_range(&service, page_request);
992
993 assert_eq!(rx.commits(), vec![(0, (8 * BLOCK_SIZE) as usize)]);
994 assert_eq!(rx.output(), expected_data);
995 }
996
997 #[test]
998 fn test_read_range_compressed_zstd() {
999 let uncompressed_size = 32768 * 2 + 1024;
1000 let mut uncompressed_data = vec![0u8; uncompressed_size];
1001 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
1002 *byte = ((i * 7) % 255) as u8;
1003 }
1004
1005 let options =
1006 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Zstd };
1007 let archive =
1008 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
1009
1010 let mut compressed_offsets = vec![0];
1011 let mut compressed_data = vec![];
1012 for chunk in archive.chunks() {
1013 compressed_data.extend_from_slice(&chunk.compressed_data);
1014 compressed_offsets.push(compressed_data.len() as u64);
1015 }
1016 compressed_offsets.pop();
1017
1018 let chunk_size = archive.chunk_size();
1019 let stored_size = compressed_data.len() as u64;
1020 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
1021 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
1022 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
1023 let service = FakeBlockService::new(device_data);
1024
1025 let extents =
1026 Extents::try_new([Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1027 let compression_info = CompressionInfo::new(
1028 chunk_size as u64,
1029 stored_size,
1030 &compressed_offsets,
1031 CompressionAlgorithm::Zstd,
1032 )
1033 .unwrap();
1034 let file = Arc::new(File::new(extents, uncompressed_size as u64, compression_info.into()));
1035
1036 let dest_alloc_size = uncompressed_size.next_multiple_of(chunk_size);
1037 let (mut page_request, rx) = TestVecBuffer::new_with_range(0..(uncompressed_size as u64));
1038 page_request.data.resize(dest_alloc_size, 0);
1039 file.read_range(&service, page_request);
1040
1041 assert_eq!(
1042 rx.commits(),
1043 vec![
1044 (0, chunk_size),
1045 (chunk_size as u64, chunk_size),
1046 (chunk_size as u64 * 2, chunk_size)
1047 ]
1048 );
1049 assert_eq!(&rx.output()[..uncompressed_size], &uncompressed_data[..]);
1050 }
1051
1052 #[test]
1053 fn test_read_range_compressed_lz4_split_across_buffers() {
1054 let uncompressed_size = 32768 * 2;
1055 let mut uncompressed_data = vec![0u8; uncompressed_size];
1056 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
1057 *byte = ((i * 13) % 255) as u8;
1058 }
1059
1060 let options =
1061 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Lz4 };
1062 let archive =
1063 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
1064
1065 let mut compressed_offsets = vec![0];
1066 let mut compressed_data = vec![];
1067 for chunk in archive.chunks() {
1068 compressed_data.extend_from_slice(&chunk.compressed_data);
1069 compressed_offsets.push(compressed_data.len() as u64);
1070 }
1071 compressed_offsets.pop();
1072
1073 let chunk_size = archive.chunk_size();
1074 let stored_size = compressed_data.len() as u64;
1075 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
1076 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
1077 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
1078
1079 let service = FakeBlockService::new_with_cap(device_data, Some(4096));
1082
1083 let extents =
1084 Extents::try_new([Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1085 let compression_info = CompressionInfo::new(
1086 chunk_size as u64,
1087 stored_size,
1088 &compressed_offsets,
1089 CompressionAlgorithm::Lz4,
1090 )
1091 .unwrap();
1092 let file = Arc::new(File::new(extents, uncompressed_size as u64, compression_info.into()));
1093
1094 let (page_request, rx) = TestVecBuffer::new_with_range(0..(uncompressed_size as u64));
1095 file.read_range(&service, page_request);
1096
1097 assert_eq!(rx.commits(), vec![(0, chunk_size), (chunk_size as u64, chunk_size)]);
1098 assert_eq!(rx.output(), uncompressed_data);
1099 }
1100
1101 #[test]
1102 fn test_read_range_invalid_range_noop() {
1103 let service = FakeBlockService::new(vec![0u8; 8192]);
1104 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
1105 let file = Arc::new(File::new(extents, 8192, Transform::None));
1106
1107 let (page_request, rx) = TestVecBuffer::new_with_range(4096..4096);
1108 file.read_range(&service, page_request);
1110 assert_eq!(rx.commits().len(), 0);
1111 }
1112
1113 #[test]
1114 fn test_file_getters() {
1115 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
1116 let uncompressed_size = 8192u64;
1117
1118 let file_uncompressed = File::new(extents, uncompressed_size, Transform::None);
1119 assert_eq!(file_uncompressed.uncompressed_size(), 8192);
1120 assert!(matches!(file_uncompressed.transform(), Transform::None));
1121 assert!(file_uncompressed.compression_info().is_none());
1122 assert!(file_uncompressed.cipher().is_none());
1123
1124 let compression_info =
1125 CompressionInfo::new(32768, 4096, &[0], CompressionAlgorithm::Zstd).unwrap();
1126 let file_compressed = File::new(
1127 Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap(),
1128 uncompressed_size,
1129 compression_info.into(),
1130 );
1131 assert!(matches!(file_compressed.transform(), Transform::Compressed(_)));
1132 assert!(file_compressed.compression_info().is_some());
1133 assert!(file_compressed.cipher().is_none());
1134
1135 let key = UnwrappedKey::new(vec![0x42u8; 32]);
1136 let cipher: Arc<dyn Cipher> = Arc::new(FxfsCipher::new(&key));
1137 let file_encrypted = File::new(
1138 Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap(),
1139 uncompressed_size,
1140 cipher.into(),
1141 );
1142 assert!(matches!(file_encrypted.transform(), Transform::Encrypted(_)));
1143 assert!(file_encrypted.compression_info().is_none());
1144 assert!(file_encrypted.cipher().is_some());
1145 }
1146
1147 #[test]
1148 fn test_read_range_block_service_error_returns_err() {
1149 struct FailingBlockService;
1150 impl BlockService for FailingBlockService {
1151 fn allocate_buffer(&self, max_len: usize) -> storage_device::buffer::OwnedBuffer {
1152 FakeBlockService::new(vec![0u8; max_len]).allocate_buffer(max_len)
1153 }
1154 fn read_blocks(
1155 &self,
1156 _device_offset: u64,
1157 _dest_buffer: storage_device::buffer::OwnedBuffer,
1158 _on_complete: Box<
1159 dyn FnOnce(Result<storage_device::buffer::OwnedBuffer, Error>) + Send,
1160 >,
1161 ) -> Result<(), Error> {
1162 Err(anyhow::anyhow!("block read failure"))
1163 }
1164 }
1165
1166 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
1167 let file = Arc::new(File::new(extents, 8192, Transform::None));
1168
1169 let (page_request, rx) = TestVecBuffer::new_with_range(0..8192);
1170
1171 file.read_range(&FailingBlockService, page_request);
1172 assert_eq!(rx.commits().len(), 0);
1173 }
1174
1175 #[test]
1176 fn test_read_range_uncompressed_multi_chunk() {
1177 let block_count = 4;
1178 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
1179 for (i, byte) in expected_data.iter_mut().enumerate() {
1180 *byte = ((i * 11) % 255) as u8;
1181 }
1182 let service = FakeBlockService::new_with_cap(expected_data.clone(), Some(4096));
1185
1186 let extents =
1187 Extents::try_new([Extent::new(0..(block_count * BLOCK_SIZE), Some(0))], 0).unwrap();
1188 let file = Arc::new(File::new(extents, block_count * BLOCK_SIZE, Transform::None));
1189
1190 let (page_request, rx) = TestVecBuffer::new_with_range(0..(block_count * BLOCK_SIZE));
1191 file.read_range(&service, page_request);
1192
1193 assert_eq!(rx.commits().len(), 4);
1194 assert_eq!(rx.output(), expected_data);
1195 }
1196
1197 #[test]
1198 fn test_read_range_uncompressed_unaligned_uncompressed_size() {
1199 let uncompressed_size = 5000u64;
1200 let mut expected_data = vec![0u8; 8192];
1201 for (i, byte) in expected_data.iter_mut().enumerate() {
1202 *byte = (i % 251) as u8;
1203 }
1204 let service = FakeBlockService::new(expected_data.clone());
1205
1206 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
1207 let file = Arc::new(File::new(extents, uncompressed_size, Transform::None));
1208
1209 let (page_request, rx) = TestVecBuffer::new_with_range(0..8192);
1210 file.read_range(&service, page_request);
1211
1212 assert_eq!(rx.commits(), vec![(0, 8192)]);
1213 assert_eq!(&rx.output()[..5000], &expected_data[..5000]);
1214 }
1215
1216 #[test]
1217 fn test_read_range_uncompressed_readahead() {
1218 let total_blocks = 64; let mut expected_data = vec![0u8; (total_blocks * BLOCK_SIZE) as usize];
1220 for (i, byte) in expected_data.iter_mut().enumerate() {
1221 *byte = ((i * 17) % 251) as u8;
1222 }
1223 let service = FakeBlockService::new(expected_data.clone());
1224
1225 let extents =
1226 Extents::try_new([Extent::new(0..(total_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1227 let file = Arc::new(File::new(extents, total_blocks * BLOCK_SIZE, Transform::None));
1228
1229 let (page_request, rx) = TestVecBuffer::new_with_range(4096..8192);
1231 file.read_range(&service, page_request);
1232
1233 assert_eq!(rx.commits(), vec![(0, TARGET_READ_AHEAD_SIZE as usize)]);
1234 assert_eq!(
1235 &rx.output()[..TARGET_READ_AHEAD_SIZE as usize],
1236 &expected_data[..TARGET_READ_AHEAD_SIZE as usize]
1237 );
1238 }
1239
1240 #[test]
1241 fn test_read_range_uncompressed_readahead_second_window() {
1242 let total_blocks = 64; let mut expected_data = vec![0u8; (total_blocks * BLOCK_SIZE) as usize];
1244 for (i, byte) in expected_data.iter_mut().enumerate() {
1245 *byte = ((i * 19) % 251) as u8;
1246 }
1247 let service = FakeBlockService::new(expected_data.clone());
1248
1249 let extents =
1250 Extents::try_new([Extent::new(0..(total_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1251 let file = Arc::new(File::new(extents, total_blocks * BLOCK_SIZE, Transform::None));
1252
1253 let (page_request, rx) = TestVecBuffer::new_with_range(135168..139264);
1256 file.read_range(&service, page_request);
1257
1258 assert_eq!(rx.commits(), vec![(TARGET_READ_AHEAD_SIZE, TARGET_READ_AHEAD_SIZE as usize)]);
1259 assert_eq!(
1260 &rx.output()[..TARGET_READ_AHEAD_SIZE as usize],
1261 &expected_data[TARGET_READ_AHEAD_SIZE as usize..2 * TARGET_READ_AHEAD_SIZE as usize]
1262 );
1263 }
1264
1265 #[test]
1266 fn test_read_range_uncompressed_readahead_tail_capped() {
1267 let uncompressed_size = 140_000u64;
1268 let total_blocks = (uncompressed_size.next_multiple_of(BLOCK_SIZE) / BLOCK_SIZE) as u64;
1269 let mut expected_data = vec![0u8; (total_blocks * BLOCK_SIZE) as usize];
1270 for i in 0..uncompressed_size as usize {
1271 expected_data[i] = ((i * 23) % 251) as u8;
1272 }
1273 let service = FakeBlockService::new(expected_data.clone());
1274
1275 let extents =
1276 Extents::try_new([Extent::new(0..(total_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1277 let file = Arc::new(File::new(extents, uncompressed_size, Transform::None));
1278
1279 let (page_request, rx) = TestVecBuffer::new_with_range(135168..139264);
1283 file.read_range(&service, page_request);
1284
1285 let expected_start = TARGET_READ_AHEAD_SIZE;
1286 let page_aligned_size = uncompressed_size.next_multiple_of(BLOCK_SIZE);
1287 let expected_len = (page_aligned_size - expected_start) as usize;
1288 assert_eq!(rx.commits(), vec![(expected_start, expected_len)]);
1289
1290 let valid_len = (uncompressed_size - expected_start) as usize;
1291 assert_eq!(
1292 &rx.output()[..valid_len],
1293 &expected_data[expected_start as usize..uncompressed_size as usize]
1294 );
1295 assert!(rx.output()[valid_len..expected_len].iter().all(|&b| b == 0));
1297 }
1298
1299 #[test]
1300 fn test_read_range_compressed_second_readahead_window() {
1301 let chunk_count = 8;
1302 let chunk_size = 32768usize;
1303 let uncompressed_size = chunk_count * chunk_size;
1304 let mut uncompressed_data = vec![0u8; uncompressed_size];
1305 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
1306 *byte = ((i * 7) % 251) as u8;
1307 }
1308
1309 let options =
1310 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Zstd };
1311 let archive =
1312 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
1313
1314 let mut compressed_offsets = vec![0];
1315 let mut compressed_data = vec![];
1316 for chunk in archive.chunks() {
1317 compressed_data.extend_from_slice(&chunk.compressed_data);
1318 compressed_offsets.push(compressed_data.len() as u64);
1319 }
1320 compressed_offsets.pop();
1321
1322 let stored_size = compressed_data.len() as u64;
1323 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
1324 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
1325 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
1326 let service = FakeBlockService::new(device_data);
1327
1328 let extents =
1329 Extents::try_new([Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1330 let compression_info = CompressionInfo::new(
1331 chunk_size as u64,
1332 stored_size,
1333 &compressed_offsets,
1334 CompressionAlgorithm::Zstd,
1335 )
1336 .unwrap();
1337 let file = Arc::new(File::new(extents, uncompressed_size as u64, compression_info.into()));
1338
1339 let (page_request, rx) = TestVecBuffer::new_with_range(135168..139264);
1342 file.read_range(&service, page_request);
1343
1344 assert_eq!(
1345 rx.commits(),
1346 vec![
1347 (131072, chunk_size),
1348 (163840, chunk_size),
1349 (196608, chunk_size),
1350 (229376, chunk_size),
1351 ]
1352 );
1353 assert_eq!(&rx.output()[..131072], &uncompressed_data[131072..262144]);
1354 }
1355
1356 #[test]
1357 fn test_read_range_compressed_partial_final_chunk_zero_tail() {
1358 let uncompressed_size = 32768 + 1024;
1359 let mut uncompressed_data = vec![0u8; uncompressed_size];
1360 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
1361 *byte = ((i * 13) % 251) as u8;
1362 }
1363
1364 let options =
1365 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Zstd };
1366 let archive =
1367 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
1368
1369 let mut compressed_offsets = vec![0];
1370 let mut compressed_data = vec![];
1371 for chunk in archive.chunks() {
1372 compressed_data.extend_from_slice(&chunk.compressed_data);
1373 compressed_offsets.push(compressed_data.len() as u64);
1374 }
1375 compressed_offsets.pop();
1376
1377 let chunk_size = archive.chunk_size();
1378 let stored_size = compressed_data.len() as u64;
1379 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
1380 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
1381 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
1382 let service = FakeBlockService::new(device_data);
1383
1384 let extents =
1385 Extents::try_new([Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
1386 let compression_info = CompressionInfo::new(
1387 chunk_size as u64,
1388 stored_size,
1389 &compressed_offsets,
1390 CompressionAlgorithm::Zstd,
1391 )
1392 .unwrap();
1393 let file = Arc::new(File::new(extents, uncompressed_size as u64, compression_info.into()));
1394
1395 let (mut page_request, rx) = TestVecBuffer::new_with_range(0..(uncompressed_size as u64));
1397 page_request.data.resize(65536, 0);
1398 page_request.data.fill(0xFF);
1399 file.read_range(&service, page_request);
1400
1401 assert_eq!(rx.commits(), vec![(0, chunk_size), (chunk_size as u64, chunk_size)]);
1402 assert_eq!(&rx.output()[..uncompressed_size], &uncompressed_data[..]);
1403 assert_eq!(&rx.output()[uncompressed_size..65536], &[0u8; 31744]);
1404 }
1405
1406 #[test]
1407 fn test_files_registry() {
1408 let extents = Extents::try_new([Extent::new(0..4096, Some(0))], 0).unwrap();
1409 let file = Arc::new(File::new(extents, 4096, Transform::None));
1410 let service = Arc::new(FakeBlockService::new(vec![0u8; 4096]));
1411 let files = Files::new(
1412 service,
1413 TestDeliveryHandler(|_key, _range| TestVecBuffer::new(4096).0),
1414 zx::Port::create(),
1415 );
1416
1417 assert!(!files.is_loading(100));
1418 files.begin_loading(100);
1419 assert!(files.is_loading(100));
1420
1421 files.insert(100, file.clone());
1422 assert!(!files.is_loading(100));
1423 files.remove(100);
1424 assert!(!files.is_loading(100));
1425 }
1426
1427 #[test]
1428 fn test_files_new_without_pager() {
1429 let extents = Extents::try_new([Extent::new(0..4096, Some(0))], 0).unwrap();
1430 let file = Arc::new(File::new(extents, 4096, Transform::None));
1431 let service = Arc::new(FakeBlockService::new(vec![0u8; 4096]));
1432 let files = Files::new_without_pager(service);
1433
1434 files.insert(100, file.clone());
1435 assert_eq!(files.get_file(100).unwrap().uncompressed_size(), 4096);
1436 }
1437
1438 struct DelayedBlockService {
1439 device_data: Vec<u8>,
1440 pending: Mutex<Vec<Box<dyn FnOnce() + Send>>>,
1441 allocator: Option<Arc<BufferAllocator>>,
1442 }
1443
1444 impl DelayedBlockService {
1445 fn new(device_data: Vec<u8>) -> Arc<Self> {
1446 Arc::new(Self { device_data, pending: Mutex::new(Vec::new()), allocator: None })
1447 }
1448
1449 fn new_with_pool_capacity(device_data: Vec<u8>, pool_capacity: usize) -> Arc<Self> {
1450 Arc::new(Self {
1451 device_data,
1452 pending: Mutex::new(Vec::new()),
1453 allocator: Some(Arc::new(BufferAllocator::new(
1454 BLOCK_SIZE as usize,
1455 BufferSource::new(pool_capacity),
1456 ))),
1457 })
1458 }
1459
1460 fn wait_and_trigger_sync(&self) {
1461 loop {
1462 let callbacks = std::mem::take(&mut *self.pending.lock());
1463 if !callbacks.is_empty() {
1464 for cb in callbacks {
1465 cb();
1466 }
1467 break;
1468 }
1469 std::thread::sleep(std::time::Duration::from_millis(10));
1470 }
1471 }
1472 }
1473
1474 impl BlockService for DelayedBlockService {
1475 fn allocate_buffer(&self, max_len: usize) -> OwnedBuffer {
1476 if let Some(allocator) = &self.allocator {
1477 let max_len = std::cmp::min(
1478 std::cmp::min(max_len, MAX_READ_BUFFER_SIZE),
1479 allocator.buffer_source().size(),
1480 );
1481 allocator.allocate_buffer_sync_owned(max_len)
1482 } else {
1483 FakeBlockService::new(vec![0u8; max_len]).allocate_buffer(max_len)
1484 }
1485 }
1486
1487 fn read_blocks(
1488 &self,
1489 device_offset: u64,
1490 mut dest_buffer: OwnedBuffer,
1491 on_complete: Box<dyn FnOnce(Result<OwnedBuffer, Error>) + Send>,
1492 ) -> Result<(), Error> {
1493 let start = device_offset as usize;
1494 let end = (start + dest_buffer.len()).min(self.device_data.len());
1495 dest_buffer
1496 .as_mut_ptr_slice()
1497 .subslice_mut(0..end - start)
1498 .copy_from_slice(&self.device_data[start..end]);
1499 self.pending.lock().push(Box::new(move || {
1500 on_complete(Ok(dest_buffer));
1501 }));
1502 Ok(())
1503 }
1504 }
1505
1506 #[fuchsia::test]
1507 fn test_read_blob_metadata_callback() {
1508 let uncompressed_size = 65536u64;
1509 let chunk_size = 32768u64;
1510 let compressed_offsets = vec![0u64, 1200u64];
1511 let metadata = BlobMetadata {
1512 merkle_leaves: MerkleLeaves::new(),
1513 format: BlobFormat::ChunkedZstd {
1514 uncompressed_size,
1515 chunk_size,
1516 compressed_offsets: compressed_offsets.clone(),
1517 },
1518 };
1519 let encoded_metadata = serialize_metadata(&metadata);
1520 let mut device_data = vec![0u8; BLOCK_SIZE as usize];
1521 device_data[..encoded_metadata.len()].copy_from_slice(&encoded_metadata);
1522
1523 let service = DelayedBlockService::new(device_data);
1524 let metadata_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1525
1526 let (tx, rx) = std::sync::mpsc::channel();
1527 read_blob_metadata(service.as_ref(), &metadata_extents, 2400, move |res| {
1528 let _ = tx.send(res);
1529 });
1530
1531 service.wait_and_trigger_sync();
1533
1534 let metadata = rx.recv().unwrap();
1535 assert_eq!(metadata.uncompressed_size, uncompressed_size);
1536 assert!(metadata.compression_info.is_some());
1537 assert_eq!(metadata.merkle_leaves, MerkleLeaves::new());
1538 }
1539
1540 #[fuchsia::test]
1541 fn test_read_blob_metadata_error_drops_callback() {
1542 let metadata = BlobMetadata {
1544 merkle_leaves: MerkleLeaves::new(),
1545 format: BlobFormat::ChunkedZstd {
1546 uncompressed_size: 65536,
1547 chunk_size: 32768,
1548 compressed_offsets: vec![1000, 500],
1549 },
1550 };
1551 let encoded_metadata = serialize_metadata(&metadata);
1552 let mut device_data = vec![0u8; BLOCK_SIZE as usize];
1553 device_data[..encoded_metadata.len()].copy_from_slice(&encoded_metadata);
1554
1555 let service = DelayedBlockService::new(device_data);
1556 let metadata_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1557
1558 let (tx, rx) = std::sync::mpsc::channel();
1559 read_blob_metadata(service.as_ref(), &metadata_extents, 1200, move |res| {
1560 let _ = tx.send(res);
1561 });
1562
1563 service.wait_and_trigger_sync();
1564
1565 assert!(rx.recv().is_err());
1567 }
1568
1569 #[fuchsia::test]
1570 fn test_read_blob_metadata_corrupt_bincode_drops_callback() {
1571 let device_data = vec![0xFFu8; BLOCK_SIZE as usize];
1573 let service = DelayedBlockService::new(device_data);
1574 let metadata_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1575
1576 let (tx, rx) = std::sync::mpsc::channel();
1577 read_blob_metadata(service.as_ref(), &metadata_extents, 1200, move |res| {
1578 let _ = tx.send(res);
1579 });
1580
1581 service.wait_and_trigger_sync();
1582
1583 assert!(rx.recv().is_err());
1585 }
1586
1587 #[fuchsia::test]
1588 fn test_process_mapping_command_error_cleans_up_blob() {
1589 let metadata = BlobMetadata {
1590 merkle_leaves: MerkleLeaves::new(),
1591 format: BlobFormat::ChunkedZstd {
1592 uncompressed_size: 65536,
1593 chunk_size: 32768,
1594 compressed_offsets: vec![1000, 500],
1595 },
1596 };
1597 let encoded_metadata = serialize_metadata(&metadata);
1598 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
1599 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
1600 .copy_from_slice(&encoded_metadata);
1601
1602 let service = DelayedBlockService::new(device_data);
1603 let files = Arc::new(Files::new(
1604 service.clone(),
1605 TestDeliveryHandler(|_k, _r| TestVecBuffer::new(4096).0),
1606 zx::Port::create(),
1607 ));
1608
1609 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1610 let meta_extents =
1611 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
1612 let mut payload_bytes = Vec::new();
1613 for w in
1614 Extents::encode_extents(&data_extents).chain(Extents::encode_extents(&meta_extents))
1615 {
1616 payload_bytes.extend_from_slice(&w.to_le_bytes());
1617 }
1618
1619 let vmo = zx::Vmo::create(65536).unwrap();
1620 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
1621 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1622 1024,
1623 16,
1624 )
1625 .unwrap();
1626 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
1627 payload_buf.data().copy_from_slice(&payload_bytes);
1628 let cmd = crate::RawMappingCommand {
1629 opcode: crate::MAPPINGS_COMMAND,
1630 offset: payload_buf.offset(),
1631 key: 99,
1632 stored_size: 4096,
1633 device_offset: 0,
1634 metadata_count: 1,
1635 extent_count: 1,
1636 };
1637 payload_buf.commit(cmd).unwrap();
1638 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
1639
1640 let msg = receiver.peek().unwrap();
1641 process_mapping_command(&msg, &files).unwrap();
1642
1643 assert!(files.is_loading(99));
1645
1646 service.wait_and_trigger_sync();
1648
1649 assert!(!files.is_loading(99));
1651 }
1652
1653 #[fuchsia::test]
1654 fn test_process_mapping_command_registers_blob_merkle_leaves() {
1655 let leaf1 = [1u8; 32];
1656 let leaf2 = [2u8; 32];
1657
1658 let metadata =
1660 BlobMetadata { merkle_leaves: vec![leaf1, leaf2], format: BlobFormat::Uncompressed };
1661 let encoded_metadata = serialize_metadata(&metadata);
1662 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
1663 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
1664 .copy_from_slice(&encoded_metadata);
1665
1666 let service = DelayedBlockService::new(device_data);
1668 let registered = Arc::new(std::sync::Mutex::new(None));
1669 let registered_clone = registered.clone();
1670 struct TestRegisterHandler(Arc<std::sync::Mutex<Option<(u64, Vec<[u8; 32]>, usize)>>>);
1671 impl DeliveryHandler for TestRegisterHandler {
1672 type Request = TestVecBuffer;
1673 fn get_page_request(self: &Arc<Self>, _key: u64, _range: Range<u64>) -> Self::Request {
1674 TestVecBuffer::new(4096).0
1675 }
1676 fn register_blob(
1677 &self,
1678 key: u64,
1679 leaves: &[[u8; 32]],
1680 read_alignment: usize,
1681 ) -> Result<(), Error> {
1682 *self.0.lock().unwrap() = Some((key, leaves.to_vec(), read_alignment));
1683 Ok(())
1684 }
1685 }
1686 let files = Arc::new(Files::new(
1687 service.clone(),
1688 TestRegisterHandler(registered_clone),
1689 zx::Port::create(),
1690 ));
1691
1692 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1694 let meta_extents =
1695 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
1696 let data_extent_words = Extents::encode_extents(&data_extents);
1697 let meta_extent_words = Extents::encode_extents(&meta_extents);
1698 let mut payload_bytes = Vec::new();
1699 for w in data_extent_words.chain(meta_extent_words) {
1700 payload_bytes.extend_from_slice(&w.to_le_bytes());
1701 }
1702
1703 let vmo = zx::Vmo::create(65536).unwrap();
1705 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
1706 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1707 1024,
1708 16,
1709 )
1710 .unwrap();
1711 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
1712 payload_buf.data().copy_from_slice(&payload_bytes);
1713 let cmd = crate::RawMappingCommand {
1714 opcode: crate::MAPPINGS_COMMAND,
1715 offset: payload_buf.offset(),
1716 key: 42,
1717 stored_size: 4096,
1718 device_offset: 0,
1719 metadata_count: 1,
1720 extent_count: 1,
1721 };
1722 payload_buf.commit(cmd).unwrap();
1723 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
1724
1725 let msg = receiver.peek().unwrap();
1727 process_mapping_command(&msg, &files).unwrap();
1728
1729 assert!(files.is_loading(42));
1732 assert!(registered.lock().unwrap().is_none());
1733
1734 service.wait_and_trigger_sync();
1736
1737 assert!(!files.is_loading(42));
1742 assert!(files.get_file(42).is_some());
1743 let (reg_key, reg_leaves, reg_read_alignment) =
1744 registered.lock().unwrap().take().expect("registered callback called");
1745 assert_eq!(reg_key, 42);
1746 assert_eq!(reg_leaves, vec![leaf1, leaf2]);
1747 assert_eq!(reg_read_alignment, DEFAULT_VERIFICATION_ALIGNMENT as usize);
1748 }
1749
1750 #[fuchsia::test]
1751 fn test_loading_page_request_buffer_dropped_on_failure() {
1752 use delivery_blob::compression::{ChunkedArchiveError, DataBuffer};
1753 use std::sync::atomic::{AtomicBool, Ordering};
1754 use storage_ptr_slice::MutPtrByteSlice;
1755
1756 struct DroppingBuffer {
1757 data: Vec<u8>,
1758 range: Range<u64>,
1759 dropped: Arc<AtomicBool>,
1760 }
1761 impl Drop for DroppingBuffer {
1762 fn drop(&mut self) {
1763 self.dropped.store(true, Ordering::Relaxed);
1764 }
1765 }
1766 impl DataBuffer for DroppingBuffer {
1767 fn range(&self) -> Range<u64> {
1768 self.range.clone()
1769 }
1770 fn mut_ptr_slice(&mut self) -> MutPtrByteSlice<'_> {
1771 MutPtrByteSlice::from(&mut self.data[..])
1772 }
1773 fn commit(&mut self, _size: usize) -> Result<(), ChunkedArchiveError> {
1774 Ok(())
1775 }
1776 }
1777 impl PageRequest for DroppingBuffer {
1778 fn prepare(
1779 &mut self,
1780 _range: Range<u64>,
1781 _read_alignment: usize,
1782 ) -> Result<(), ChunkedArchiveError> {
1783 Ok(())
1784 }
1785 }
1786
1787 let metadata = BlobMetadata {
1788 merkle_leaves: MerkleLeaves::new(),
1789 format: BlobFormat::ChunkedZstd {
1790 uncompressed_size: 65536,
1791 chunk_size: 32768,
1792 compressed_offsets: vec![1000, 500],
1793 },
1794 };
1795 let encoded_metadata = serialize_metadata(&metadata);
1796 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
1797 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
1798 .copy_from_slice(&encoded_metadata);
1799
1800 let service = DelayedBlockService::new(device_data);
1801 let dropped = Arc::new(AtomicBool::new(false));
1802 let dropped_clone = dropped.clone();
1803 let files = Arc::new(Files::new(
1804 service.clone(),
1805 TestDeliveryHandler(move |_k, r| DroppingBuffer {
1806 data: vec![0u8; 4096],
1807 range: r,
1808 dropped: dropped_clone.clone(),
1809 }),
1810 zx::Port::create(),
1811 ));
1812
1813 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1814 let meta_extents =
1815 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
1816 let mut payload_bytes = Vec::new();
1817 for w in
1818 Extents::encode_extents(&data_extents).chain(Extents::encode_extents(&meta_extents))
1819 {
1820 payload_bytes.extend_from_slice(&w.to_le_bytes());
1821 }
1822
1823 let vmo = zx::Vmo::create(65536).unwrap();
1824 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
1825 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1826 1024,
1827 16,
1828 )
1829 .unwrap();
1830 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
1831 payload_buf.data().copy_from_slice(&payload_bytes);
1832 let cmd = crate::RawMappingCommand {
1833 opcode: crate::MAPPINGS_COMMAND,
1834 offset: payload_buf.offset(),
1835 key: 99,
1836 stored_size: 4096,
1837 device_offset: 0,
1838 metadata_count: 1,
1839 extent_count: 1,
1840 };
1841 payload_buf.commit(cmd).unwrap();
1842 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
1843
1844 let msg = receiver.peek().unwrap();
1845 process_mapping_command(&msg, &files).unwrap();
1846
1847 assert!(files.is_loading(99));
1848
1849 files.handle_page_request(99, 0..4096);
1851 assert!(!dropped.load(Ordering::Relaxed));
1852
1853 service.wait_and_trigger_sync();
1855
1856 assert!(dropped.load(Ordering::Relaxed));
1858 assert!(!files.is_loading(99));
1859 }
1860
1861 #[fuchsia::test]
1862 async fn test_pager_concurrent_request_during_metadata_read() {
1863 use vmo_fifo::SyncSender;
1864 use zx::{Pager, PagerOptions, Port, Rights, Vmo, VmoOptions};
1865
1866 let metadata =
1867 BlobMetadata { merkle_leaves: MerkleLeaves::new(), format: BlobFormat::Uncompressed };
1868 let encoded_metadata = serialize_metadata(&metadata);
1869 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
1870 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
1872 .copy_from_slice(&encoded_metadata);
1873 device_data[..4].copy_from_slice(&[10, 20, 30, 40]);
1875
1876 let service = DelayedBlockService::new(device_data);
1877 let port = Port::create();
1878
1879 let (page_request, rx) = TestVecBuffer::new(BLOCK_SIZE as usize);
1880 let page_request_holder = Arc::new(Mutex::new(Some(page_request)));
1881 let page_request_clone = page_request_holder.clone();
1882
1883 let files = Arc::new(Files::new(
1884 service.clone(),
1885 TestDeliveryHandler(move |_key, _range| page_request_clone.lock().take().unwrap()),
1886 port.duplicate_handle(Rights::SAME_RIGHTS).unwrap(),
1887 ));
1888
1889 let _pager_thread = files.spawn_pager_thread();
1890
1891 let pager = Pager::create(PagerOptions::empty()).unwrap();
1892 let vmo_blob = pager.create_vmo(VmoOptions::empty(), &port, 42, BLOCK_SIZE).unwrap();
1893 let vmo_blob_clone = vmo_blob.duplicate_handle(Rights::SAME_RIGHTS).unwrap();
1894
1895 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
1897 let meta_extents =
1898 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
1899 let mut payload_bytes = Vec::new();
1900 for w in
1901 Extents::encode_extents(&data_extents).chain(Extents::encode_extents(&meta_extents))
1902 {
1903 payload_bytes.extend_from_slice(&w.to_le_bytes());
1904 }
1905
1906 let cmd = crate::RawMappingCommand {
1907 opcode: crate::MAPPINGS_COMMAND,
1908 offset: 0,
1909 key: 42,
1910 stored_size: BLOCK_SIZE,
1911 device_offset: 0,
1912 metadata_count: 1,
1913 extent_count: 1,
1914 };
1915
1916 let vmo = Vmo::create(65536).unwrap();
1917 let mut sender = SyncSender::<crate::RawMappingCommand>::new(
1918 vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap(),
1919 1024,
1920 16,
1921 )
1922 .unwrap();
1923 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
1924 payload_buf.data().copy_from_slice(&payload_bytes);
1925 payload_buf.commit(cmd).unwrap();
1926 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
1927
1928 std::thread::scope(|s| {
1929 let msg = receiver.peek().unwrap();
1930 let files_for_process = files.clone();
1931 s.spawn(move || {
1932 process_mapping_command(&msg, &files_for_process).unwrap();
1933 });
1934
1935 while service.pending.lock().is_empty() {
1938 std::thread::sleep(std::time::Duration::from_millis(5));
1939 }
1940
1941 std::thread::spawn(move || {
1943 let mut b = [0u8; 1];
1944 let _ = vmo_blob_clone.read(&mut b, 0);
1945 });
1946
1947 while !files.has_queued_page_requests(42) {
1950 std::thread::sleep(std::time::Duration::from_millis(5));
1951 }
1952
1953 service.wait_and_trigger_sync();
1956 });
1957
1958 service.wait_and_trigger_sync();
1960
1961 assert!(
1963 !rx.wait_for_commits(1).is_empty(),
1964 "Page request made during metadata read was dropped!"
1965 );
1966 }
1967
1968 #[fuchsia::test]
1969 async fn test_pager_request_arrives_before_mapping_command() {
1970 use vmo_fifo::SyncSender;
1971 use zx::{Pager, PagerOptions, Port, Rights, Vmo, VmoOptions};
1972
1973 let metadata =
1974 BlobMetadata { merkle_leaves: MerkleLeaves::new(), format: BlobFormat::Uncompressed };
1975 let encoded_metadata = serialize_metadata(&metadata);
1976 let mut device_data = vec![0u8; (2 * BLOCK_SIZE) as usize];
1977 device_data[BLOCK_SIZE as usize..BLOCK_SIZE as usize + encoded_metadata.len()]
1979 .copy_from_slice(&encoded_metadata);
1980 device_data[..4].copy_from_slice(&[10, 20, 30, 40]);
1982
1983 let service = DelayedBlockService::new(device_data);
1984 let port = Port::create();
1985
1986 let (page_request, rx) = TestVecBuffer::new(BLOCK_SIZE as usize);
1987 let page_request_holder = Arc::new(Mutex::new(Some(page_request)));
1988 let page_request_clone = page_request_holder.clone();
1989
1990 let files = Arc::new(Files::new(
1991 service.clone(),
1992 TestDeliveryHandler(move |_key, _range| page_request_clone.lock().take().unwrap()),
1993 port.duplicate_handle(Rights::SAME_RIGHTS).unwrap(),
1994 ));
1995
1996 let _pager_thread = files.spawn_pager_thread();
1997
1998 let pager = Pager::create(PagerOptions::empty()).unwrap();
1999 let vmo_blob = pager.create_vmo(VmoOptions::empty(), &port, 42, BLOCK_SIZE).unwrap();
2000 let vmo_blob_clone = vmo_blob.duplicate_handle(Rights::SAME_RIGHTS).unwrap();
2001
2002 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
2004 let meta_extents =
2005 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(BLOCK_SIZE))], 0).unwrap();
2006 let mut payload_bytes = Vec::new();
2007 for w in
2008 Extents::encode_extents(&data_extents).chain(Extents::encode_extents(&meta_extents))
2009 {
2010 payload_bytes.extend_from_slice(&w.to_le_bytes());
2011 }
2012
2013 let cmd = crate::RawMappingCommand {
2014 opcode: crate::MAPPINGS_COMMAND,
2015 offset: 0,
2016 key: 42,
2017 stored_size: BLOCK_SIZE,
2018 device_offset: 0,
2019 metadata_count: 1,
2020 extent_count: 1,
2021 };
2022
2023 let vmo = Vmo::create(65536).unwrap();
2024 let mut sender = SyncSender::<crate::RawMappingCommand>::new(
2025 vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap(),
2026 1024,
2027 16,
2028 )
2029 .unwrap();
2030 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2031 payload_buf.data().copy_from_slice(&payload_bytes);
2032 payload_buf.commit(cmd).unwrap();
2033 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2034
2035 std::thread::spawn(move || {
2037 let mut b = [0u8; 1];
2038 let _ = vmo_blob_clone.read(&mut b, 0);
2039 });
2040
2041 while !files.has_queued_page_requests(42) {
2043 std::thread::sleep(std::time::Duration::from_millis(5));
2044 }
2045
2046 std::thread::scope(|s| {
2047 let msg = receiver.peek().unwrap();
2048 let files_for_process = files.clone();
2049 s.spawn(move || {
2050 process_mapping_command(&msg, &files_for_process).unwrap();
2051 });
2052
2053 service.wait_and_trigger_sync();
2055 });
2056
2057 service.wait_and_trigger_sync();
2059
2060 assert!(
2062 !rx.wait_for_commits(1).is_empty(),
2063 "Page request made before mapping command was dropped!"
2064 );
2065 }
2066
2067 #[fuchsia::test]
2068 fn test_process_mapping_command_close_blob() {
2069 let service = Arc::new(FakeBlockService::new(vec![0u8; 8192]));
2070 let files = Arc::new(Files::new(
2071 service,
2072 TestDeliveryHandler(|_k, _r| TestVecBuffer::new(4096).0),
2073 zx::Port::create(),
2074 ));
2075
2076 let vmo = zx::Vmo::create(65536).unwrap();
2077 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
2078 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2079 1024,
2080 16,
2081 )
2082 .unwrap();
2083
2084 let data_extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
2086 let mut payload_bytes = Vec::new();
2087 for w in Extents::encode_extents(&data_extents) {
2088 payload_bytes.extend_from_slice(&w.to_le_bytes());
2089 }
2090 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2091 payload_buf.data().copy_from_slice(&payload_bytes);
2092 let cmd = crate::RawMappingCommand {
2093 opcode: crate::MAPPINGS_COMMAND,
2094 offset: payload_buf.offset(),
2095 key: 123,
2096 stored_size: 4096,
2097 device_offset: 0,
2098 metadata_count: 0,
2099 extent_count: 1,
2100 };
2101 payload_buf.commit(cmd).unwrap();
2102
2103 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2104 let msg = receiver.peek().unwrap();
2105 process_mapping_command(&msg, &files).unwrap();
2106 msg.pop().unwrap();
2107
2108 assert!(files.is_loaded(123));
2109
2110 sender
2112 .push(crate::RawMappingCommand {
2113 opcode: crate::CLOSE_BLOB_COMMAND,
2114 offset: 0,
2115 key: 123,
2116 stored_size: 0,
2117 device_offset: 0,
2118 metadata_count: 0,
2119 extent_count: 0,
2120 })
2121 .unwrap();
2122
2123 let msg = receiver.peek().unwrap();
2124 process_mapping_command(&msg, &files).unwrap();
2125 msg.pop().unwrap();
2126
2127 assert!(!files.is_loaded(123));
2128 }
2129
2130 #[test]
2131 fn test_read_ahead_size_for_chunk_size() {
2132 assert_eq!(read_ahead_size_for_chunk_size(32 * 1024, 32 * 1024), 32 * 1024);
2133 assert_eq!(read_ahead_size_for_chunk_size(48 * 1024, 32 * 1024), 48 * 1024);
2134 assert_eq!(read_ahead_size_for_chunk_size(64 * 1024, 32 * 1024), 64 * 1024);
2135
2136 assert_eq!(read_ahead_size_for_chunk_size(32 * 1024, 64 * 1024), 64 * 1024);
2137 assert_eq!(read_ahead_size_for_chunk_size(48 * 1024, 64 * 1024), 48 * 1024);
2138 assert_eq!(read_ahead_size_for_chunk_size(64 * 1024, 64 * 1024), 64 * 1024);
2139 assert_eq!(read_ahead_size_for_chunk_size(96 * 1024, 64 * 1024), 96 * 1024);
2140
2141 assert_eq!(read_ahead_size_for_chunk_size(32 * 1024, 128 * 1024), 128 * 1024);
2142 assert_eq!(read_ahead_size_for_chunk_size(48 * 1024, 128 * 1024), 96 * 1024);
2143 assert_eq!(read_ahead_size_for_chunk_size(64 * 1024, 128 * 1024), 128 * 1024);
2144 assert_eq!(read_ahead_size_for_chunk_size(96 * 1024, 128 * 1024), 96 * 1024);
2145 }
2146
2147 #[test]
2148 fn test_deserialize_blob_metadata() {
2149 let metadata =
2150 BlobMetadata { merkle_leaves: MerkleLeaves::new(), format: BlobFormat::Uncompressed };
2151
2152 let bytes = serialize_metadata(&metadata);
2154 let deserialized = deserialize_blob_metadata(&bytes).unwrap();
2155 assert_eq!(deserialized, metadata);
2156
2157 assert!(deserialize_blob_metadata(&[1, 2, 3]).is_err());
2159
2160 let mut old_version_bytes = bytes.clone();
2162 (&mut old_version_bytes[..4]).write_u32::<LittleEndian>(52).unwrap();
2163 assert!(deserialize_blob_metadata(&old_version_bytes).is_err());
2164 }
2165
2166 #[fuchsia::test]
2167 async fn test_wait_for_file_before_and_after_insert() {
2168 let service = Arc::new(FakeBlockService::new(vec![]));
2169 let files = Arc::new(Files::new_without_pager(service));
2170
2171 let extents = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], 0).unwrap();
2172 let file = Arc::new(File::new(extents, BLOCK_SIZE, Transform::None));
2173
2174 let files_clone = files.clone();
2176 let file_clone = file.clone();
2177 let wait_task =
2178 fasync::Task::spawn(async move { files_clone.wait_for_file(42).await.unwrap() });
2179
2180 files.insert(42, file_clone);
2182 let loaded_file = wait_task.await;
2183 assert_eq!(loaded_file.uncompressed_size(), BLOCK_SIZE);
2184
2185 let loaded_file2 = files.wait_for_file(42).await.unwrap();
2187 assert_eq!(loaded_file2.uncompressed_size(), BLOCK_SIZE);
2188 }
2189
2190 #[test]
2191 fn test_read_range_encrypted() {
2192 let block_count = 8;
2193 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
2194 for (i, byte) in expected_data.iter_mut().enumerate() {
2195 *byte = (i % 255) as u8;
2196 }
2197
2198 let key = UnwrappedKey::new(vec![0x42u8; 32]);
2199 let cipher: Arc<dyn Cipher> = Arc::new(FxfsCipher::new(&key));
2200
2201 let mut encrypted_data = expected_data.clone();
2202 cipher.encrypt(0, 0, 0, 0, MutPtrByteSlice::from(&mut encrypted_data[..])).unwrap();
2203 let service = FakeBlockService::new(encrypted_data);
2204
2205 let extents = Extents::try_new([Extent::new(0..(8 * BLOCK_SIZE), Some(0))], 0).unwrap();
2206 let file = Arc::new(File::new(extents, 8 * BLOCK_SIZE, cipher.into()));
2207
2208 let (page_request, rx) = TestVecBuffer::new_with_range(0..(8 * BLOCK_SIZE));
2209 file.read_range(&service, page_request);
2210
2211 assert_eq!(rx.commits(), vec![(0, (8 * BLOCK_SIZE) as usize)]);
2212 assert_eq!(rx.output(), expected_data);
2213 }
2214
2215 #[test]
2216 fn test_read_range_encrypted_with_offset() {
2217 let total_blocks = 64; let mut expected_data = vec![0u8; (total_blocks * BLOCK_SIZE) as usize];
2219 for (i, byte) in expected_data.iter_mut().enumerate() {
2220 *byte = ((i * 17) % 255) as u8;
2221 }
2222
2223 let key = UnwrappedKey::new(vec![0x5au8; 32]);
2224 let cipher: Arc<dyn Cipher> = Arc::new(FxfsCipher::new(&key));
2225
2226 let mut encrypted_data = expected_data.clone();
2227 cipher.encrypt(0, 0, 0, 0, MutPtrByteSlice::from(&mut encrypted_data[..])).unwrap();
2228 let service = FakeBlockService::new(encrypted_data);
2229
2230 let extents =
2231 Extents::try_new([Extent::new(0..(total_blocks * BLOCK_SIZE), Some(0))], 0).unwrap();
2232 let file = Arc::new(File::new(extents, total_blocks * BLOCK_SIZE, cipher.into()));
2233
2234 let (page_request, rx) = TestVecBuffer::new_with_range(135168..139264);
2238 file.read_range(&service, page_request);
2239
2240 assert_eq!(rx.commits(), vec![(TARGET_READ_AHEAD_SIZE, TARGET_READ_AHEAD_SIZE as usize)]);
2241 assert_eq!(
2242 &rx.output()[..TARGET_READ_AHEAD_SIZE as usize],
2243 &expected_data[TARGET_READ_AHEAD_SIZE as usize..2 * TARGET_READ_AHEAD_SIZE as usize]
2244 );
2245 }
2246
2247 #[test]
2248 fn test_read_range_encrypted_multi_chunk() {
2249 let block_count = 4;
2250 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
2251 for (i, byte) in expected_data.iter_mut().enumerate() {
2252 *byte = ((i * 31) % 255) as u8;
2253 }
2254
2255 let key = UnwrappedKey::new(vec![0x7fu8; 32]);
2256 let cipher: Arc<dyn Cipher> = Arc::new(FxfsCipher::new(&key));
2257
2258 let mut encrypted_data = expected_data.clone();
2259 cipher.encrypt(0, 0, 0, 0, MutPtrByteSlice::from(&mut encrypted_data[..])).unwrap();
2260 let service = FakeBlockService::new_with_cap(encrypted_data, Some(BLOCK_SIZE as usize));
2262
2263 let extents =
2264 Extents::try_new([Extent::new(0..(block_count * BLOCK_SIZE), Some(0))], 0).unwrap();
2265 let file = Arc::new(File::new(extents, block_count * BLOCK_SIZE, cipher.into()));
2266
2267 let (page_request, rx) = TestVecBuffer::new_with_range(0..(block_count * BLOCK_SIZE));
2268 file.read_range(&service, page_request);
2269
2270 assert_eq!(rx.commits().len(), 4);
2271 assert_eq!(rx.output(), expected_data);
2272 }
2273
2274 #[test]
2275 fn test_read_range_decryption_error() {
2276 #[derive(Debug)]
2277 struct FailingCipher(FxfsCipher);
2278 impl Cipher for FailingCipher {
2279 fn encrypt(
2280 &self,
2281 ino: u64,
2282 attr: u64,
2283 dev: u64,
2284 file: u64,
2285 buf: MutPtrByteSlice<'_>,
2286 ) -> Result<(), Error> {
2287 self.0.encrypt(ino, attr, dev, file, buf)
2288 }
2289 fn decrypt(
2290 &self,
2291 _ino: u64,
2292 _attribute_id: u64,
2293 _device_offset: u64,
2294 _file_offset: u64,
2295 _buffer: MutPtrByteSlice<'_>,
2296 ) -> Result<(), Error> {
2297 bail!("decrypt failed")
2298 }
2299 fn encrypt_filename(&self, ino: u64, name: &mut Vec<u8>) -> Result<(), Error> {
2300 self.0.encrypt_filename(ino, name)
2301 }
2302 fn decrypt_filename(&self, ino: u64, name: &mut Vec<u8>) -> Result<(), Error> {
2303 self.0.decrypt_filename(ino, name)
2304 }
2305 fn hash_code(&self, name: &[u8], filename: &str) -> Option<u32> {
2306 self.0.hash_code(name, filename)
2307 }
2308 fn hash_code_casefold(&self, filename: &str) -> u32 {
2309 self.0.hash_code_casefold(filename)
2310 }
2311 fn supports_inline_encryption(&self) -> bool {
2312 self.0.supports_inline_encryption()
2313 }
2314 fn crypt_ctx(&self, ino: u64, attr: u64, offset: u64) -> Option<(u64, u8)> {
2315 self.0.crypt_ctx(ino, attr, offset)
2316 }
2317 }
2318
2319 let key = UnwrappedKey::new(vec![0x42u8; 32]);
2320 let failing_cipher: Arc<dyn Cipher> = Arc::new(FailingCipher(FxfsCipher::new(&key)));
2321 let service = FakeBlockService::new(vec![0u8; 8192]);
2322 let extents = Extents::try_new([Extent::new(0..8192, Some(0))], 0).unwrap();
2323 let file = Arc::new(File::new(extents, 8192, failing_cipher.into()));
2324
2325 let (page_request, rx) = TestVecBuffer::new_with_range(0..8192);
2326 file.read_range(&service, page_request);
2327 assert_eq!(rx.commits().len(), 0);
2328 }
2329
2330 #[fuchsia::test]
2331 fn test_process_mapping_command_unencrypted_no_metadata() {
2332 let block_count = 2;
2333 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
2334 for (i, byte) in expected_data.iter_mut().enumerate() {
2335 *byte = (i % 251) as u8;
2336 }
2337 let service = Arc::new(FakeBlockService::new(expected_data.clone()));
2338 let files = Arc::new(Files::new(
2339 service.clone(),
2340 TestDeliveryHandler(|_k, r| TestVecBuffer::new_with_range(r).0),
2341 zx::Port::create(),
2342 ));
2343
2344 let data_extents =
2345 Extents::try_new([Extent::new(0..(block_count as u64 * BLOCK_SIZE), Some(0))], 0)
2346 .unwrap();
2347 let mut payload_bytes = Vec::new();
2348 for w in Extents::encode_extents(&data_extents) {
2349 payload_bytes.extend_from_slice(&w.to_le_bytes());
2350 }
2351
2352 let vmo = zx::Vmo::create(65536).unwrap();
2353 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
2354 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2355 1024,
2356 16,
2357 )
2358 .unwrap();
2359 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2360 payload_buf.data().copy_from_slice(&payload_bytes);
2361 let cmd = crate::RawMappingCommand {
2362 opcode: crate::MAPPINGS_COMMAND,
2363 offset: payload_buf.offset(),
2364 key: 100,
2365 stored_size: block_count as u64 * BLOCK_SIZE,
2366 device_offset: 0,
2367 metadata_count: 0,
2368 extent_count: 1,
2369 };
2370 payload_buf.commit(cmd).unwrap();
2371 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2372
2373 let msg = receiver.peek().unwrap();
2374 process_mapping_command(&msg, &files).unwrap();
2375
2376 let file = files.get_file(100).expect("File should be loaded immediately");
2377 let (page_request, rx) =
2378 TestVecBuffer::new_with_range(0..(block_count as u64 * BLOCK_SIZE));
2379 file.read_range(service.as_ref(), page_request);
2380 assert_eq!(rx.output(), expected_data);
2381 }
2382
2383 #[fuchsia::test]
2384 fn test_process_mapping_command_encrypted() {
2385 struct NoRegisterBlobHandler;
2386 impl DeliveryHandler for NoRegisterBlobHandler {
2387 type Request = TestVecBuffer;
2388 fn get_page_request(self: &Arc<Self>, _key: u64, range: Range<u64>) -> Self::Request {
2389 TestVecBuffer::new_with_range(range).0
2390 }
2391 fn register_blob(
2392 &self,
2393 _key: u64,
2394 _merkle_leaves: &[[u8; 32]],
2395 _read_alignment: usize,
2396 ) -> Result<(), Error> {
2397 panic!("register_blob should not be called for encrypted files");
2398 }
2399 }
2400
2401 let block_count = 2;
2402 let mut plaintext = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
2403 for (i, byte) in plaintext.iter_mut().enumerate() {
2404 *byte = (i % 251) as u8;
2405 }
2406
2407 let raw_key = [0x5au8; 32];
2408 let key = UnwrappedKey::new(raw_key.to_vec());
2409 let cipher = FxfsCipher::new(&key);
2410
2411 let mut ciphertext = plaintext.clone();
2412 cipher.encrypt(0, 0, 0, 0, MutPtrByteSlice::from(&mut ciphertext[..])).unwrap();
2413
2414 let service = Arc::new(FakeBlockService::new(ciphertext));
2415 let files =
2416 Arc::new(Files::new(service.clone(), NoRegisterBlobHandler, zx::Port::create()));
2417
2418 let data_extents =
2419 Extents::try_new([Extent::new(0..(block_count as u64 * BLOCK_SIZE), Some(0))], 0)
2420 .unwrap();
2421 let mut payload_bytes = Vec::new();
2422 for w in Extents::encode_extents(&data_extents) {
2423 payload_bytes.extend_from_slice(&w.to_le_bytes());
2424 }
2425 payload_bytes.extend_from_slice(&raw_key);
2426
2427 let vmo = zx::Vmo::create(65536).unwrap();
2428 let mut sender = vmo_fifo::SyncSender::<crate::RawMappingCommand>::new(
2429 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2430 1024,
2431 16,
2432 )
2433 .unwrap();
2434 let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
2435 payload_buf.data().copy_from_slice(&payload_bytes);
2436 let cmd = crate::RawMappingCommand {
2437 opcode: crate::MAPPINGS_COMMAND | crate::MAPPINGS_FLAG_ENCRYPTED,
2438 offset: payload_buf.offset(),
2439 key: 101,
2440 stored_size: block_count as u64 * BLOCK_SIZE,
2441 device_offset: 0,
2442 metadata_count: 0,
2443 extent_count: 1,
2444 };
2445 payload_buf.commit(cmd).unwrap();
2446
2447 let invalid_cmd = crate::RawMappingCommand {
2448 opcode: crate::MAPPINGS_COMMAND | crate::MAPPINGS_FLAG_ENCRYPTED,
2449 offset: 0,
2450 key: 102,
2451 stored_size: block_count as u64 * BLOCK_SIZE,
2452 device_offset: 0,
2453 metadata_count: 1,
2454 extent_count: 1,
2455 };
2456 sender.reserve_payload(0).unwrap().commit(invalid_cmd).unwrap();
2457
2458 let mut receiver = vmo_fifo::Receiver::<crate::RawMappingCommand>::new(vmo, 16).unwrap();
2459
2460 let msg = receiver.peek().unwrap();
2461 process_mapping_command(&msg, &files).unwrap();
2462 msg.pop().unwrap();
2463
2464 let invalid_msg = receiver.peek().unwrap();
2465 assert!(process_mapping_command(&invalid_msg, &files).is_err());
2466
2467 let file = files.get_file(101).expect("File should be loaded immediately");
2468 assert!(matches!(file.transform(), Transform::Encrypted(_)));
2469
2470 let (page_request, rx) =
2471 TestVecBuffer::new_with_range(0..(block_count as u64 * BLOCK_SIZE));
2472 file.read_range(service.as_ref(), page_request);
2473 assert_eq!(rx.output(), plaintext);
2474 }
2475
2476 #[fuchsia::test]
2477 fn test_queued_page_request_does_not_deadlock_single_completion_thread() {
2478 let data_size = TARGET_READ_AHEAD_SIZE as usize;
2480 let encoded_metadata = serialize_metadata(&BlobMetadata {
2481 merkle_leaves: MerkleLeaves::new(),
2482 format: BlobFormat::Uncompressed,
2483 });
2484 let mut device_data = vec![0xabu8; data_size + BLOCK_SIZE as usize];
2485 device_data[data_size..data_size + encoded_metadata.len()]
2486 .copy_from_slice(&encoded_metadata);
2487
2488 let block_service = DelayedBlockService::new_with_pool_capacity(device_data, 64 * 1024);
2490
2491 let (page_request, rx) = TestVecBuffer::new_with_range(0..4096);
2492 let page_request = Mutex::new(Some(page_request));
2493 let files = Arc::new(Files::new(
2494 block_service.clone(),
2495 TestDeliveryHandler(move |_key, _range| page_request.lock().take().unwrap()),
2496 zx::Port::create(),
2497 ));
2498 let _pager_thread = files.spawn_pager_thread();
2499
2500 let data_extents =
2501 Extents::try_new([Extent::new(0..TARGET_READ_AHEAD_SIZE, Some(0))], 0).unwrap();
2502 let meta_extents =
2503 Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(TARGET_READ_AHEAD_SIZE))], 0)
2504 .unwrap();
2505
2506 let blob_key = 200;
2507
2508 files.begin_loading(blob_key);
2511 let files_clone = files.clone();
2512 read_blob_metadata(
2513 block_service.as_ref(),
2514 &meta_extents,
2515 TARGET_READ_AHEAD_SIZE,
2516 move |metadata| {
2517 let file = Arc::new(File::new(
2518 data_extents,
2519 metadata.uncompressed_size,
2520 metadata.compression_info.into(),
2521 ));
2522 files_clone.insert(blob_key, file);
2523 },
2524 );
2525
2526 files.handle_page_request(blob_key, 0..4096);
2531
2532 let block_completion_thread = std::thread::spawn({
2536 let block_service = block_service.clone();
2537 move || {
2538 for _ in 0..3 {
2539 block_service.wait_and_trigger_sync();
2540 }
2541 }
2542 });
2543
2544 block_completion_thread.join().unwrap();
2545
2546 assert_eq!(rx.wait_for_commits(2), vec![(0, 65536), (65536, 65536)]);
2549 assert_eq!(rx.output(), vec![0xabu8; data_size]);
2551 }
2552
2553 #[fuchsia::test]
2554 fn test_pager_thread_lifecycle() {
2555 let port = zx::Port::create();
2556 let service = Arc::new(FakeBlockService::new(vec![0u8; 4096]));
2557 let files = Arc::new(Files::new(
2558 service,
2559 TestDeliveryHandler(|_key, _range| TestVecBuffer::new(4096).0),
2560 port,
2561 ));
2562 let thread = files.spawn_pager_thread();
2563 drop(thread);
2564 }
2565
2566 #[fuchsia::test]
2567 fn test_pager_packet() {
2568 let port = zx::Port::create();
2569 let pager = zx::Pager::create(zx::PagerOptions::empty()).expect("create pager");
2570 let vmo = pager.create_vmo(zx::VmoOptions::empty(), &port, 1234, 4096).expect("create vmo");
2571
2572 let vmo_clone = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("duplicate vmo");
2573 let _reader_thread = std::thread::spawn(move || {
2574 let mut b = [0u8; 1];
2575 let _ = vmo_clone.read(&mut b, 0);
2576 });
2577
2578 let packet = port.wait(zx::MonotonicInstant::INFINITE).expect("wait packet");
2579 assert_eq!(packet.key(), 1234);
2580 if let zx::PacketContents::Pager(pager_packet) = packet.contents() {
2581 assert_eq!(pager_packet.command(), ZX_PAGER_VMO_READ);
2582 assert_eq!(pager_packet.range(), 0..4096);
2583 } else {
2584 panic!("Expected pager packet");
2585 }
2586 }
2587
2588 #[fuchsia::test]
2589 fn test_files_record_profile() {
2590 let total_size = TARGET_READ_AHEAD_SIZE * 2;
2591 let uncompressed_data = vec![0x55u8; total_size as usize];
2592
2593 let compressed_uncompressed_size = 192 * 1024;
2596 let chunk_size = 48 * 1024;
2597 let options = ChunkedArchiveOptions::V2 {
2598 minimum_chunk_size: chunk_size,
2599 chunk_alignment: chunk_size,
2600 compression_level: 1,
2601 };
2602 let archive = delivery_blob::compression::ChunkedArchive::new(
2603 &uncompressed_data[..compressed_uncompressed_size],
2604 options,
2605 )
2606 .expect("ChunkedArchive::new failed");
2607 let mut compressed_offsets = vec![0];
2608 let mut compressed_bytes = Vec::new();
2609 for chunk in archive.chunks() {
2610 compressed_bytes.extend_from_slice(&chunk.compressed_data);
2611 compressed_offsets.push(compressed_bytes.len() as u64);
2612 }
2613 compressed_offsets.pop();
2614 let compressed_stored_size = compressed_bytes.len() as u64;
2615 let compressed_stored_aligned = compressed_stored_size.next_multiple_of(BLOCK_SIZE);
2616
2617 let mut device_data = vec![0x55u8; (total_size + compressed_stored_aligned) as usize];
2618 device_data[total_size as usize..total_size as usize + compressed_bytes.len()]
2619 .copy_from_slice(&compressed_bytes);
2620 let service = Arc::new(FakeBlockService::new(device_data));
2621
2622 let files = Arc::new(Files::new(
2623 service,
2624 TestDeliveryHandler(|_key, range| TestVecBuffer::new_with_range(range).0),
2625 zx::Port::create(),
2626 ));
2627
2628 let extents1 = Extents::try_new([Extent::new(0..total_size, Some(0))], 0)
2629 .expect("Extents::try_new failed");
2630 let file1 = Arc::new(File::new(extents1, total_size, Transform::None));
2631 files.insert(10, file1);
2632
2633 let extents2 =
2634 Extents::try_new([Extent::new(0..compressed_stored_aligned, Some(total_size))], 0)
2635 .expect("Extents::try_new failed");
2636 let compression_info = CompressionInfo::new(
2637 chunk_size as u64,
2638 compressed_stored_size,
2639 &compressed_offsets,
2640 CompressionAlgorithm::Zstd,
2641 )
2642 .expect("CompressionInfo::new failed");
2643 let file2 = Arc::new(File::new(
2644 extents2,
2645 compressed_uncompressed_size as u64,
2646 compression_info.into(),
2647 ));
2648 let compressed_read_ahead = file2.read_ahead_size();
2649 assert_eq!(compressed_read_ahead, 96 * 1024);
2650 files.insert(20, file2);
2651
2652 assert_eq!(
2654 files.stop_recording().expect_err("stop_recording succeeded without active recording"),
2655 zx::Status::BAD_STATE
2656 );
2657
2658 files.handle_page_request(10, 0..4096);
2660
2661 files.start_recording().expect("start_recording failed");
2662 assert_eq!(
2663 files.start_recording().expect_err("duplicate start_recording succeeded"),
2664 zx::Status::BAD_STATE
2665 );
2666
2667 files.handle_page_request(10, 4096..8192);
2669 files.handle_page_request(20, 100 * 1024..104 * 1024);
2671 files.handle_page_request(10, 0..total_size);
2673
2674 let vmo = files.stop_recording().expect("stop_recording failed");
2675 let entries: Vec<_> = FileMessage::decode_from_vmo(vmo, BLOCK_SIZE as usize)
2676 .expect("decode_from_vmo failed")
2677 .collect();
2678 assert_eq!(
2679 entries,
2680 vec![
2681 FileMessage { id: 10, offset: 0 },
2682 FileMessage { id: 20, offset: compressed_read_ahead },
2683 FileMessage { id: 10, offset: 0 },
2684 FileMessage { id: 10, offset: TARGET_READ_AHEAD_SIZE },
2685 ]
2686 );
2687
2688 files.handle_page_request(20, 0..4096);
2691 files.start_recording().expect("second start_recording failed");
2692 let empty_vmo = files.stop_recording().expect("second stop_recording failed");
2693 assert_eq!(
2694 FileMessage::decode_from_vmo(empty_vmo, BLOCK_SIZE as usize)
2695 .expect("decode_from_vmo failed")
2696 .collect::<Vec<_>>(),
2697 Vec::<FileMessage>::new()
2698 );
2699 }
2700
2701 #[fuchsia::test]
2702 fn test_files_record_profile_deferred_loading() {
2703 let total_size = TARGET_READ_AHEAD_SIZE;
2704 let device_data = vec![0x77u8; total_size as usize];
2705 let service = Arc::new(FakeBlockService::new(device_data));
2706
2707 let (page_request, rx) = TestVecBuffer::new_with_range(4096..8192);
2708 let page_request = Mutex::new(Some(page_request));
2709 let files = Arc::new(Files::new(
2710 service,
2711 TestDeliveryHandler(move |_key, _range| {
2712 page_request.lock().take().expect("page_request already taken")
2713 }),
2714 zx::Port::create(),
2715 ));
2716 let _pager_thread = files.spawn_pager_thread();
2717
2718 files.start_recording().expect("start_recording failed");
2719
2720 files.handle_page_request(55, 4096..8192);
2722 assert!(files.has_queued_page_requests(55));
2723
2724 let extents = Extents::try_new([Extent::new(0..total_size, Some(0))], 0)
2725 .expect("Extents::try_new failed");
2726 let file = Arc::new(File::new(extents, total_size, Transform::None));
2727 files.insert(55, file);
2728
2729 assert_eq!(rx.wait_for_commits(1), vec![(0, total_size as usize)]);
2730
2731 let vmo = files.stop_recording().expect("stop_recording failed");
2732 let entries: Vec<_> = FileMessage::decode_from_vmo(vmo, BLOCK_SIZE as usize)
2733 .expect("decode_from_vmo failed")
2734 .collect();
2735 assert_eq!(entries, vec![FileMessage { id: 55, offset: 0 }]);
2736 }
2737}