Skip to main content

disklayer_ram/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! RAM-backed disk layer implementation.
5
6#![forbid(unsafe_code)]
7
8pub mod resolver;
9
10use anyhow::Context;
11use disk_backend::Disk;
12use disk_backend::DiskError;
13use disk_backend::UnmapBehavior;
14use disk_layered::DiskLayer;
15use disk_layered::LayerAttach;
16use disk_layered::LayerConfiguration;
17use disk_layered::LayerIo;
18use disk_layered::LayeredDisk;
19use disk_layered::SectorMarker;
20use disk_layered::WriteNoOverwrite;
21use guestmem::MemoryRead;
22use guestmem::MemoryWrite;
23use inspect::Inspect;
24use parking_lot::RwLock;
25use scsi_buffers::RequestBuffers;
26use std::collections::BTreeMap;
27use std::collections::btree_map::Entry;
28use std::fmt;
29use std::fmt::Debug;
30use std::sync::atomic::AtomicU64;
31use std::sync::atomic::Ordering;
32use thiserror::Error;
33
34/// The default sector size in bytes.
35pub const DEFAULT_SECTOR_SIZE: u32 = 512;
36
37/// A RAM-backed disk layer that infers unspecified topology parameters from
38/// the lower layer at attach time.
39pub struct LazyRamDiskLayer {
40    len: Option<u64>,
41    sector_size: Option<u32>,
42}
43
44impl LazyRamDiskLayer {
45    /// Create a new lazy RAM-backed disk layer.
46    ///
47    /// By default, both size and sector size are inferred from the lower layer.
48    /// Use [`with_len`](Self::with_len) and
49    /// [`with_sector_size`](Self::with_sector_size) to override.
50    pub fn new() -> Self {
51        Self {
52            len: None,
53            sector_size: None,
54        }
55    }
56
57    /// Set the layer size in bytes.
58    ///
59    /// If not set, the size is inferred from the lower layer at attach time.
60    pub fn with_len(mut self, len: u64) -> Self {
61        self.len = Some(len);
62        self
63    }
64
65    /// Set the sector size in bytes.
66    ///
67    /// If not set, the sector size is inferred from the lower layer at attach
68    /// time, defaulting to 512 if there is no lower layer.
69    pub fn with_sector_size(mut self, sector_size: u32) -> Self {
70        self.sector_size = Some(sector_size);
71        self
72    }
73}
74
75/// A disk layer backed entirely by RAM.
76#[derive(Inspect)]
77#[inspect(extra = "Self::inspect_extra")]
78pub struct RamDiskLayer {
79    #[inspect(flatten)]
80    state: RwLock<RamState>,
81    #[inspect(skip)]
82    sector_count: AtomicU64,
83    #[inspect(skip)]
84    resize_event: event_listener::Event,
85    sector_size: u32,
86    #[inspect(skip)]
87    sector_shift: u32,
88}
89
90#[derive(Inspect)]
91struct RamState {
92    #[inspect(skip)]
93    data: BTreeMap<u64, Box<[u8]>>,
94    #[inspect(skip)] // handled in inspect_extra()
95    sector_count: u64,
96    zero_after: u64,
97}
98
99impl RamDiskLayer {
100    fn inspect_extra(&self, resp: &mut inspect::Response<'_>) {
101        resp.field_with("committed_size", || {
102            self.state.read().data.len() * self.sector_size as usize
103        })
104        .field_mut_with("sector_count", |new_count| {
105            if let Some(new_count) = new_count {
106                self.resize(new_count.parse().context("invalid sector count")?)?;
107            }
108            anyhow::Ok(self.sector_count())
109        });
110    }
111}
112
113impl Debug for RamDiskLayer {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        f.debug_struct("RamDiskLayer")
116            .field("sector_count", &self.sector_count)
117            .finish()
118    }
119}
120
121/// An error creating a RAM disk.
122#[derive(Error, Debug)]
123pub enum Error {
124    /// The disk size is not a multiple of the sector size.
125    #[error("disk size {disk_size:#x} is not a multiple of the sector size {sector_size}")]
126    NotSectorMultiple {
127        /// The disk size.
128        disk_size: u64,
129        /// The sector size.
130        sector_size: u32,
131    },
132    /// The disk has no sectors.
133    #[error("disk has no sectors")]
134    EmptyDisk,
135    /// The sector size is invalid.
136    #[error("sector size {0} is not a power-of-two >= 512")]
137    InvalidSectorSize(u32),
138}
139
140impl RamDiskLayer {
141    /// Makes a new RAM disk layer of `size` bytes with 512-byte sectors.
142    pub fn new(size: u64) -> Result<Self, Error> {
143        Self::new_with_sector_size(size, DEFAULT_SECTOR_SIZE)
144    }
145
146    /// Makes a new RAM disk layer of `size` bytes with the given sector size.
147    ///
148    /// `sector_size` must be a power-of-two >= 512.
149    pub fn new_with_sector_size(size: u64, sector_size: u32) -> Result<Self, Error> {
150        if !sector_size.is_power_of_two() || sector_size < DEFAULT_SECTOR_SIZE {
151            return Err(Error::InvalidSectorSize(sector_size));
152        }
153        let sector_shift = sector_size.trailing_zeros();
154        let sector_count = {
155            if size == 0 {
156                return Err(Error::EmptyDisk);
157            }
158            if !size.is_multiple_of(sector_size as u64) {
159                return Err(Error::NotSectorMultiple {
160                    disk_size: size,
161                    sector_size,
162                });
163            }
164            size >> sector_shift
165        };
166        Ok(Self {
167            state: RwLock::new(RamState {
168                data: BTreeMap::new(),
169                sector_count,
170                zero_after: sector_count,
171            }),
172            sector_count: sector_count.into(),
173            resize_event: Default::default(),
174            sector_size,
175            sector_shift,
176        })
177    }
178
179    fn resize(&self, new_sector_count: u64) -> anyhow::Result<()> {
180        if new_sector_count == 0 {
181            anyhow::bail!("invalid sector count");
182        }
183        // Remove any truncated data and update the sector count under the lock.
184        let _removed = {
185            let mut state = self.state.write();
186            // Remember that any non-present sectors after this point need to be zeroed.
187            state.zero_after = new_sector_count.min(state.zero_after);
188            state.sector_count = new_sector_count;
189            // Cache the sector count in an atomic for the fast path.
190            //
191            // FUTURE: remove uses of .sector_count() in the IO path,
192            // eliminating the need for this.
193            self.sector_count.store(new_sector_count, Ordering::Relaxed);
194            state.data.split_off(&new_sector_count)
195        };
196        self.resize_event.notify(usize::MAX);
197        Ok(())
198    }
199
200    fn write_maybe_overwrite(
201        &self,
202        buffers: &RequestBuffers<'_>,
203        sector: u64,
204        overwrite: bool,
205    ) -> Result<(), DiskError> {
206        let sector_bytes = self.sector_size as usize;
207        let count = buffers.len() >> self.sector_shift;
208        tracing::trace!(sector, count, "write");
209        let mut state = self.state.write();
210        if sector + count as u64 > state.sector_count {
211            return Err(DiskError::IllegalBlock);
212        }
213        for i in 0..count {
214            let cur = sector + i as u64;
215            let buf = buffers.subrange(i << self.sector_shift, sector_bytes);
216            let mut reader = buf.reader();
217            match state.data.entry(cur) {
218                Entry::Vacant(entry) => {
219                    let mut data = vec![0u8; sector_bytes].into_boxed_slice();
220                    reader.read(&mut data)?;
221                    entry.insert(data);
222                }
223                Entry::Occupied(mut entry) => {
224                    if overwrite {
225                        reader.read(entry.get_mut())?;
226                    }
227                }
228            }
229        }
230        Ok(())
231    }
232}
233
234impl LayerAttach for LazyRamDiskLayer {
235    type Error = Error;
236    type Layer = RamDiskLayer;
237
238    async fn attach(
239        self,
240        lower_layer_metadata: Option<disk_layered::DiskLayerMetadata>,
241    ) -> Result<Self::Layer, Self::Error> {
242        let sector_size = self
243            .sector_size
244            .or(lower_layer_metadata.as_ref().map(|meta| meta.sector_size))
245            .unwrap_or(DEFAULT_SECTOR_SIZE);
246        let total_size = match self.len {
247            Some(len) => len,
248            None => {
249                let meta = lower_layer_metadata.ok_or(Error::EmptyDisk)?;
250                meta.sector_count * meta.sector_size as u64
251            }
252        };
253        RamDiskLayer::new_with_sector_size(total_size, sector_size)
254    }
255}
256
257impl LayerIo for RamDiskLayer {
258    fn layer_type(&self) -> &str {
259        "ram"
260    }
261
262    fn sector_count(&self) -> u64 {
263        self.sector_count.load(Ordering::Relaxed)
264    }
265
266    fn sector_size(&self) -> u32 {
267        self.sector_size
268    }
269
270    fn is_logically_read_only(&self) -> bool {
271        false
272    }
273
274    fn disk_id(&self) -> Option<[u8; 16]> {
275        None
276    }
277
278    fn physical_sector_size(&self) -> u32 {
279        self.sector_size
280    }
281
282    fn is_fua_respected(&self) -> bool {
283        true
284    }
285
286    async fn read(
287        &self,
288        buffers: &RequestBuffers<'_>,
289        sector: u64,
290        mut marker: SectorMarker<'_>,
291    ) -> Result<(), DiskError> {
292        let sector_bytes = self.sector_size as usize;
293        let count = (buffers.len() >> self.sector_shift) as u64;
294        let end = sector + count;
295        tracing::trace!(sector, count, "read");
296        let state = self.state.read();
297        if end > state.sector_count {
298            return Err(DiskError::IllegalBlock);
299        }
300        let mut range = state.data.range(sector..end);
301        let mut last = sector;
302        while last < end {
303            let r = range.next();
304            let next = r.map(|(&s, _)| s).unwrap_or(end);
305            if next > last && next > state.zero_after {
306                // Some non-present sectors need to be zeroed, since they are
307                // after the zero-after point (due to a resize).
308                let zero_start = last.max(state.zero_after);
309                let zero_count = next - zero_start;
310                let offset = ((zero_start - sector) as usize) << self.sector_shift;
311                let len = (zero_count as usize) << self.sector_shift;
312                buffers.subrange(offset, len).writer().zero(len)?;
313                marker.set_range(zero_start..next);
314            }
315            if let Some((&s, buf)) = r {
316                let offset = ((s - sector) as usize) << self.sector_shift;
317                buffers.subrange(offset, sector_bytes).writer().write(buf)?;
318
319                marker.set(s);
320            }
321            last = next;
322        }
323        Ok(())
324    }
325
326    async fn write(
327        &self,
328        buffers: &RequestBuffers<'_>,
329        sector: u64,
330        _fua: bool,
331    ) -> Result<(), DiskError> {
332        self.write_maybe_overwrite(buffers, sector, true)
333    }
334
335    fn write_no_overwrite(&self) -> Option<impl WriteNoOverwrite> {
336        Some(self)
337    }
338
339    async fn sync_cache(&self) -> Result<(), DiskError> {
340        tracing::trace!("sync_cache");
341        Ok(())
342    }
343
344    async fn wait_resize(&self, sector_count: u64) -> u64 {
345        loop {
346            let listen = self.resize_event.listen();
347            let current = self.sector_count();
348            if current != sector_count {
349                break current;
350            }
351            listen.await;
352        }
353    }
354
355    async fn unmap(
356        &self,
357        sector_offset: u64,
358        sector_count: u64,
359        _block_level_only: bool,
360        next_is_zero: bool,
361    ) -> Result<(), DiskError> {
362        tracing::trace!(sector_offset, sector_count, "unmap");
363        let mut state = self.state.write();
364        if sector_offset + sector_count > state.sector_count {
365            return Err(DiskError::IllegalBlock);
366        }
367        if !next_is_zero {
368            // This would create a hole of zeroes, which we cannot represent in
369            // the tree. Ignore the unmap.
370            if sector_offset + sector_count < state.zero_after {
371                return Ok(());
372            }
373            // The unmap is within or will extend the not-present-is-zero
374            // region, so allow it.
375            state.zero_after = state.zero_after.min(sector_offset);
376        }
377        // Sadly, there appears to be no way to remove a range of entries
378        // from a btree map.
379        let mut next_sector = sector_offset;
380        let end = sector_offset + sector_count;
381        while next_sector < end {
382            let Some((&sector, _)) = state.data.range_mut(next_sector..).next() else {
383                break;
384            };
385            if sector >= end {
386                break;
387            }
388            state.data.remove(&sector);
389            next_sector = sector + 1;
390        }
391        Ok(())
392    }
393
394    fn unmap_behavior(&self) -> UnmapBehavior {
395        // This layer zeroes if the lower layer is zero, but otherwise does
396        // nothing, so we must report unspecified.
397        UnmapBehavior::Unspecified
398    }
399
400    fn optimal_unmap_sectors(&self) -> u32 {
401        1
402    }
403}
404
405impl WriteNoOverwrite for RamDiskLayer {
406    async fn write_no_overwrite(
407        &self,
408        buffers: &RequestBuffers<'_>,
409        sector: u64,
410    ) -> Result<(), DiskError> {
411        self.write_maybe_overwrite(buffers, sector, false)
412    }
413}
414
415/// Create a RAM disk of `size` bytes.
416///
417/// This is a convenience function for creating a layered disk with a single RAM
418/// layer. It is useful since non-layered RAM disks are used all over the place,
419/// especially in tests.
420pub fn ram_disk(size: u64, read_only: bool) -> anyhow::Result<Disk> {
421    ram_disk_with_sector_size(size, read_only, DEFAULT_SECTOR_SIZE)
422}
423
424/// Create a RAM disk of `size` bytes with the given sector size.
425///
426/// This is a convenience function for creating a layered disk with a single RAM
427/// layer and a custom sector size.
428pub fn ram_disk_with_sector_size(
429    size: u64,
430    read_only: bool,
431    sector_size: u32,
432) -> anyhow::Result<Disk> {
433    use futures::future::FutureExt;
434
435    let disk = Disk::new(
436        LayeredDisk::new(
437            read_only,
438            vec![LayerConfiguration {
439                layer: DiskLayer::new(RamDiskLayer::new_with_sector_size(size, sector_size)?),
440                write_through: false,
441                read_cache: false,
442            }],
443        )
444        .now_or_never()
445        .expect("RamDiskLayer won't block")?,
446    )?;
447
448    Ok(disk)
449}
450
451#[cfg(test)]
452mod tests {
453    use super::RamDiskLayer;
454    use disk_backend::DiskIo;
455    use disk_layered::DiskLayer;
456    use disk_layered::LayerConfiguration;
457    use disk_layered::LayerIo;
458    use disk_layered::LayeredDisk;
459    use guestmem::GuestMemory;
460    use pal_async::async_test;
461    use scsi_buffers::OwnedRequestBuffers;
462    use test_with_tracing::test;
463    use zerocopy::IntoBytes;
464
465    const SECTOR_SIZE: u32 = 512;
466    const SECTOR_U64: u64 = SECTOR_SIZE as u64;
467    const SECTOR_USIZE: usize = SECTOR_SIZE as usize;
468
469    const CONFORMANCE_DISK_SIZE: u64 = 1024 * 1024;
470
471    #[async_test]
472    async fn sector_range_conformance() {
473        let disk = crate::ram_disk(CONFORMANCE_DISK_SIZE, false).unwrap();
474        storage_tests::sector_range::test_disk_sector_range_conformance(&disk).await;
475    }
476
477    /// The representability guarantee is not specific to 512-byte sectors.
478    #[async_test]
479    async fn sector_range_conformance_4k() {
480        let disk = crate::ram_disk_with_sector_size(CONFORMANCE_DISK_SIZE, false, 4096).unwrap();
481        storage_tests::sector_range::test_disk_representability(&disk).await;
482    }
483
484    /// `LayeredDisk` does not reach `write_no_overwrite` or both values of
485    /// `unmap`'s `next_is_zero`, so the layer is also tested directly.
486    #[async_test]
487    async fn layer_sector_range_conformance() {
488        let layer = RamDiskLayer::new(CONFORMANCE_DISK_SIZE).unwrap();
489        storage_tests::sector_range::test_layer_sector_range(&layer).await;
490    }
491
492    fn check(mem: &GuestMemory, sector: u64, start: usize, count: usize, high: u8) {
493        let mut buf = vec![0u32; count * SECTOR_USIZE / 4];
494        mem.read_at(start as u64 * SECTOR_U64, buf.as_mut_bytes())
495            .unwrap();
496        for (i, &b) in buf.iter().enumerate() {
497            let offset = sector * SECTOR_U64 + i as u64 * 4;
498            let expected = (offset as u32 / 4) | ((high as u32) << 24);
499            assert!(
500                b == expected,
501                "at sector {}, word {}, got {:#x}, expected {:#x}",
502                offset / SECTOR_U64,
503                (offset % SECTOR_U64) / 4,
504                b,
505                expected
506            );
507        }
508    }
509
510    async fn read(mem: &GuestMemory, disk: &mut impl DiskIo, sector: u64, count: usize) {
511        disk.read_vectored(
512            &OwnedRequestBuffers::linear(0, count * SECTOR_USIZE, true).buffer(mem),
513            sector,
514        )
515        .await
516        .unwrap();
517    }
518
519    async fn write_layer(
520        mem: &GuestMemory,
521        disk: &mut impl LayerIo,
522        sector: u64,
523        count: usize,
524        high: u8,
525    ) {
526        let buf: Vec<_> = (sector * SECTOR_U64 / 4..(sector + count as u64) * SECTOR_U64 / 4)
527            .map(|x| x as u32 | ((high as u32) << 24))
528            .collect();
529        let len = SECTOR_USIZE * count;
530        mem.write_at(0, &buf.as_bytes()[..len]).unwrap();
531
532        disk.write(
533            &OwnedRequestBuffers::linear(0, len, false).buffer(mem),
534            sector,
535            false,
536        )
537        .await
538        .unwrap();
539    }
540
541    async fn write(mem: &GuestMemory, disk: &mut impl DiskIo, sector: u64, count: usize, high: u8) {
542        let buf: Vec<_> = (sector * SECTOR_U64 / 4..(sector + count as u64) * SECTOR_U64 / 4)
543            .map(|x| x as u32 | ((high as u32) << 24))
544            .collect();
545        let len = SECTOR_USIZE * count;
546        mem.write_at(0, &buf.as_bytes()[..len]).unwrap();
547
548        disk.write_vectored(
549            &OwnedRequestBuffers::linear(0, len, false).buffer(mem),
550            sector,
551            false,
552        )
553        .await
554        .unwrap();
555    }
556
557    async fn prep_disk(size: usize) -> (GuestMemory, LayeredDisk) {
558        let guest_mem = GuestMemory::allocate(size);
559        let mut lower = RamDiskLayer::new(size as u64).unwrap();
560        write_layer(&guest_mem, &mut lower, 0, size / SECTOR_USIZE, 0).await;
561        let upper = RamDiskLayer::new(size as u64).unwrap();
562        let upper = LayeredDisk::new(
563            false,
564            Vec::from_iter([upper, lower].map(|layer| LayerConfiguration {
565                layer: DiskLayer::new(layer),
566                write_through: false,
567                read_cache: false,
568            })),
569        )
570        .await
571        .unwrap();
572        (guest_mem, upper)
573    }
574
575    #[async_test]
576    async fn diff() {
577        const SIZE: usize = 1024 * 1024;
578
579        let (guest_mem, mut upper) = prep_disk(SIZE).await;
580        read(&guest_mem, &mut upper, 10, 2).await;
581        check(&guest_mem, 10, 0, 2, 0);
582        write(&guest_mem, &mut upper, 10, 2, 1).await;
583        write(&guest_mem, &mut upper, 11, 1, 2).await;
584        read(&guest_mem, &mut upper, 9, 5).await;
585        check(&guest_mem, 9, 0, 1, 0);
586        check(&guest_mem, 10, 1, 1, 1);
587        check(&guest_mem, 11, 2, 1, 2);
588        check(&guest_mem, 12, 3, 1, 0);
589    }
590
591    async fn resize(disk: &LayeredDisk, new_size: u64) {
592        let inspect::ValueKind::Unsigned(v) =
593            inspect::update("layers/0/backing/sector_count", &new_size.to_string(), disk)
594                .await
595                .unwrap()
596                .kind
597        else {
598            panic!("bad inspect value")
599        };
600        assert_eq!(new_size, v);
601    }
602
603    #[async_test]
604    async fn test_resize() {
605        const SIZE: usize = 1024 * 1024;
606        const SECTORS: usize = SIZE / SECTOR_USIZE;
607
608        let (guest_mem, mut upper) = prep_disk(SIZE).await;
609        check(&guest_mem, 0, 0, SECTORS, 0);
610        resize(&upper, SECTORS as u64 / 2).await;
611        resize(&upper, SECTORS as u64).await;
612        read(&guest_mem, &mut upper, 0, SECTORS).await;
613        check(&guest_mem, 0, 0, SECTORS / 2, 0);
614        for s in SECTORS / 2..SECTORS {
615            let mut buf = [0u8; SECTOR_USIZE];
616            guest_mem.read_at(s as u64 * SECTOR_U64, &mut buf).unwrap();
617            assert_eq!(buf, [0u8; SECTOR_USIZE]);
618        }
619    }
620
621    #[async_test]
622    async fn test_unmap() {
623        const SIZE: usize = 1024 * 1024;
624        const SECTORS: usize = SIZE / SECTOR_USIZE;
625
626        let (guest_mem, mut upper) = prep_disk(SIZE).await;
627        upper.unmap(0, SECTORS as u64 - 1, false).await.unwrap();
628        read(&guest_mem, &mut upper, 0, SECTORS).await;
629        check(&guest_mem, 0, 0, SECTORS, 0);
630        upper
631            .unmap(SECTORS as u64 / 2, SECTORS as u64 / 2, false)
632            .await
633            .unwrap();
634        read(&guest_mem, &mut upper, 0, SECTORS).await;
635        check(&guest_mem, 0, 0, SECTORS / 2, 0);
636        for s in SECTORS / 2..SECTORS {
637            let mut buf = [0u8; SECTOR_USIZE];
638            guest_mem.read_at(s as u64 * SECTOR_U64, &mut buf).unwrap();
639            assert_eq!(buf, [0u8; SECTOR_USIZE]);
640        }
641    }
642
643    #[async_test]
644    async fn test_4096_sector_write_read() {
645        const SECTOR_4K: usize = 4096;
646        const DISK_SIZE: u64 = 1024 * 1024; // 1 MiB
647        const SECTOR_COUNT: u64 = DISK_SIZE / SECTOR_4K as u64;
648
649        let layer = RamDiskLayer::new_with_sector_size(DISK_SIZE, 4096).unwrap();
650        assert_eq!(layer.sector_size(), 4096);
651        assert_eq!(layer.physical_sector_size(), 4096);
652        assert_eq!(layer.sector_count(), SECTOR_COUNT);
653
654        let guest_mem = GuestMemory::allocate(SECTOR_4K * 2);
655
656        // Write pattern to sector 0
657        let pattern: Vec<u8> = (0..SECTOR_4K).map(|i| (i % 251) as u8).collect();
658        guest_mem.write_at(0, &pattern).unwrap();
659        let bufs = OwnedRequestBuffers::linear(0, SECTOR_4K, false);
660        layer
661            .write(&bufs.buffer(&guest_mem), 0, false)
662            .await
663            .unwrap();
664
665        // Build a single-layer disk to test read-back through DiskIo
666        let disk = LayeredDisk::new(
667            false,
668            vec![LayerConfiguration {
669                layer: DiskLayer::new(layer),
670                write_through: false,
671                read_cache: false,
672            }],
673        )
674        .await
675        .unwrap();
676
677        // Clear guest memory and read back
678        guest_mem.write_at(0, &vec![0u8; SECTOR_4K]).unwrap();
679        disk.read_vectored(
680            &OwnedRequestBuffers::linear(0, SECTOR_4K, true).buffer(&guest_mem),
681            0,
682        )
683        .await
684        .unwrap();
685
686        let mut readback = vec![0u8; SECTOR_4K];
687        guest_mem.read_at(0, &mut readback).unwrap();
688        assert_eq!(readback, pattern);
689    }
690
691    #[test]
692    fn test_sector_count_4096() {
693        let layer = RamDiskLayer::new_with_sector_size(1024 * 1024, 4096).unwrap();
694        assert_eq!(layer.sector_count(), 256); // 1 MiB / 4096 = 256
695    }
696
697    #[test]
698    fn test_invalid_sector_size_not_power_of_two() {
699        let err = RamDiskLayer::new_with_sector_size(4096, 1000).unwrap_err();
700        assert!(matches!(err, super::Error::InvalidSectorSize(1000)));
701    }
702
703    #[test]
704    fn test_invalid_sector_size_too_small() {
705        let err = RamDiskLayer::new_with_sector_size(4096, 256).unwrap_err();
706        assert!(matches!(err, super::Error::InvalidSectorSize(256)));
707    }
708
709    #[test]
710    fn test_invalid_disk_size_not_multiple() {
711        let err = RamDiskLayer::new_with_sector_size(5000, 4096).unwrap_err();
712        assert!(matches!(
713            err,
714            super::Error::NotSectorMultiple {
715                disk_size: 5000,
716                sector_size: 4096
717            }
718        ));
719    }
720
721    #[async_test]
722    async fn test_lazy_inherits_sector_size() {
723        let lower = RamDiskLayer::new_with_sector_size(1024 * 1024, 4096).unwrap();
724        let upper = LayeredDisk::new(
725            false,
726            vec![
727                LayerConfiguration {
728                    layer: DiskLayer::new(super::LazyRamDiskLayer::new()),
729                    write_through: false,
730                    read_cache: false,
731                },
732                LayerConfiguration {
733                    layer: DiskLayer::new(lower),
734                    write_through: false,
735                    read_cache: false,
736                },
737            ],
738        )
739        .await
740        .unwrap();
741
742        assert_eq!(upper.sector_size(), 4096);
743        assert_eq!(upper.sector_count(), 256);
744    }
745
746    #[async_test]
747    async fn test_mismatched_sector_sizes_rejected() {
748        const DISK_SIZE: u64 = 1024 * 1024;
749
750        // 4K upper on 512B lower should be rejected.
751        let result = LayeredDisk::new(
752            false,
753            vec![
754                LayerConfiguration {
755                    layer: DiskLayer::new(
756                        RamDiskLayer::new_with_sector_size(DISK_SIZE, 4096).unwrap(),
757                    ),
758                    write_through: false,
759                    read_cache: false,
760                },
761                LayerConfiguration {
762                    layer: DiskLayer::new(RamDiskLayer::new(DISK_SIZE).unwrap()),
763                    write_through: false,
764                    read_cache: false,
765                },
766            ],
767        )
768        .await;
769        assert!(result.is_err());
770
771        // 512B upper on 4K lower should be rejected.
772        let result = LayeredDisk::new(
773            false,
774            vec![
775                LayerConfiguration {
776                    layer: DiskLayer::new(RamDiskLayer::new(DISK_SIZE).unwrap()),
777                    write_through: false,
778                    read_cache: false,
779                },
780                LayerConfiguration {
781                    layer: DiskLayer::new(
782                        RamDiskLayer::new_with_sector_size(DISK_SIZE, 4096).unwrap(),
783                    ),
784                    write_through: false,
785                    read_cache: false,
786                },
787            ],
788        )
789        .await;
790        assert!(result.is_err());
791    }
792}