1#[cfg(any(test, feature = "testing"))]
5use std::mem;
6use std::{
7 collections::{BTreeMap, BTreeSet},
8 path::{Path, PathBuf},
9 sync::Arc,
10};
11
12use crate::{
13 io::fs::{
14 Create, Filesystem, FsError, Len, Mkdir, Open, OpenMut, Pread, Pwrite, ReadDir, Rename, Result,
15 SyncData, SyncDir, Truncate, Unlink,
16 },
17 sync::mutex::Mutex,
18};
19
20pub const SECTOR_BYTES: usize = 512;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum SectorState {
24 Unwritten,
25 Dirty,
26 Durable,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct SectorMask(Vec<bool>);
31
32impl SectorMask {
33 pub fn full(sectors: usize) -> Self {
34 Self(vec![true; sectors])
35 }
36
37 pub fn empty(sectors: usize) -> Self {
38 Self(vec![false; sectors])
39 }
40
41 pub fn set(&mut self, sector: usize) {
42 if sector < self.0.len() {
43 self.0[sector] = true;
44 }
45 }
46
47 pub fn clear(&mut self, sector: usize) {
48 if sector < self.0.len() {
49 self.0[sector] = false;
50 }
51 }
52
53 pub fn is_set(&self, sector: usize) -> bool {
54 self.0.get(sector).copied().unwrap_or(false)
55 }
56
57 pub fn sectors(&self) -> usize {
58 self.0.len()
59 }
60}
61
62pub(crate) fn sector_span(offset: u64, len: usize, sector_bytes: usize) -> usize {
63 if len == 0 {
64 return 0;
65 }
66 let first = offset as usize / sector_bytes;
67 let last = (offset as usize + len - 1) / sector_bytes;
68 last - first + 1
69}
70
71struct FileData {
72 bytes: Vec<u8>,
73 sectors: Vec<SectorState>,
74 shadow: BTreeMap<usize, (SectorState, Vec<u8>)>,
75}
76
77impl FileData {
78 fn new(len: usize, sector_bytes: usize) -> Self {
79 Self {
80 bytes: vec![0u8; len],
81 sectors: vec![SectorState::Unwritten; len.div_ceil(sector_bytes)],
82 shadow: BTreeMap::new(),
83 }
84 }
85
86 fn resize(&mut self, len: usize, sector_bytes: usize) {
87 self.bytes.resize(len, 0);
88 self.sectors.resize(len.div_ceil(sector_bytes), SectorState::Unwritten);
89 let sectors = self.sectors.len();
90 self.shadow.retain(|sector, _| *sector < sectors);
91 }
92
93 fn dirty(&mut self, sector: usize, sector_bytes: usize) {
94 if !self.shadow.contains_key(§or) {
95 let lo = sector * sector_bytes;
96 let hi = (lo + sector_bytes).min(self.bytes.len());
97 self.shadow.insert(sector, (self.sectors[sector], self.bytes[lo..hi].to_vec()));
98 }
99 self.sectors[sector] = SectorState::Dirty;
100 }
101
102 fn sync(&mut self) {
103 for state in self.sectors.iter_mut() {
104 if *state == SectorState::Dirty {
105 *state = SectorState::Durable;
106 }
107 }
108 self.shadow.clear();
109 }
110
111 #[cfg(any(test, feature = "testing"))]
112 fn revert(&mut self, sector_bytes: usize) {
113 while let Some((sector, (state, bytes))) = self.shadow.pop_first() {
114 let lo = sector * sector_bytes;
115 if lo >= self.bytes.len() {
116 continue;
117 }
118 let hi = (lo + bytes.len()).min(self.bytes.len());
119 self.bytes[lo..hi].copy_from_slice(&bytes[..hi - lo]);
120 if sector < self.sectors.len() {
121 self.sectors[sector] = state;
122 }
123 }
124 }
125}
126
127pub(crate) struct MemoryFileState {
128 sector_bytes: usize,
129 data: Mutex<FileData>,
130}
131
132impl MemoryFileState {
133 fn new(len: usize, sector_bytes: usize) -> Self {
134 Self {
135 sector_bytes,
136 data: Mutex::new(FileData::new(len, sector_bytes)),
137 }
138 }
139
140 fn pread(&self, offset: u64, buf: &mut [u8]) -> Result<usize> {
141 let data = self.data.lock();
142 let offset = offset as usize;
143 if offset >= data.bytes.len() {
144 return Ok(0);
145 }
146 let read = buf.len().min(data.bytes.len() - offset);
147 buf[..read].copy_from_slice(&data.bytes[offset..offset + read]);
148 Ok(read)
149 }
150
151 fn write_masked(&self, offset: u64, buf: &[u8], mask: &SectorMask) -> Result<usize> {
152 if buf.is_empty() {
153 return Ok(0);
154 }
155 let sector_bytes = self.sector_bytes;
156 let offset = offset as usize;
157 let end = offset + buf.len();
158 let mut data = self.data.lock();
159 if end > data.bytes.len() {
160 data.resize(end, sector_bytes);
161 }
162 let first = offset / sector_bytes;
163 let last = (end - 1) / sector_bytes;
164 for sector in first..=last {
165 if !mask.is_set(sector - first) {
166 continue;
167 }
168 let lo = (sector * sector_bytes).max(offset);
169 let hi = (sector * sector_bytes + sector_bytes).min(end);
170 data.dirty(sector, sector_bytes);
171 data.bytes[lo..hi].copy_from_slice(&buf[lo - offset..hi - offset]);
172 }
173 Ok(buf.len())
174 }
175
176 fn sync_data(&self) -> Result<()> {
177 self.data.lock().sync();
178 Ok(())
179 }
180
181 fn truncate(&self, len: u64) -> Result<()> {
182 self.data.lock().resize(len as usize, self.sector_bytes);
183 Ok(())
184 }
185
186 fn len(&self) -> Result<u64> {
187 Ok(self.data.lock().bytes.len() as u64)
188 }
189
190 #[cfg(any(test, feature = "testing"))]
191 fn revert(&self) {
192 self.data.lock().revert(self.sector_bytes);
193 }
194
195 #[cfg(any(test, feature = "testing"))]
196 fn sector_states(&self) -> Vec<SectorState> {
197 self.data.lock().sectors.clone()
198 }
199}
200
201struct State {
202 files: BTreeMap<PathBuf, Arc<MemoryFileState>>,
203 dirs: BTreeSet<PathBuf>,
204 dirty_dirs: BTreeSet<PathBuf>,
205 sector_bytes: usize,
206}
207
208impl Default for State {
209 fn default() -> Self {
210 Self {
211 files: BTreeMap::new(),
212 dirs: BTreeSet::from([PathBuf::from("/")]),
213 dirty_dirs: BTreeSet::new(),
214 sector_bytes: SECTOR_BYTES,
215 }
216 }
217}
218
219impl State {
220 fn exists(&self, path: &Path) -> bool {
221 self.files.contains_key(path) || self.dirs.contains(path)
222 }
223
224 fn parent_is_dir(&self, path: &Path) -> bool {
225 match path.parent() {
226 Some(parent) => self.dirs.contains(parent),
227 None => false,
228 }
229 }
230}
231
232#[derive(Default)]
233struct Inner {
234 state: Mutex<State>,
235}
236
237#[derive(Clone, Default)]
238pub struct MemoryFs(Arc<Inner>);
239
240impl MemoryFs {
241 pub fn new() -> Self {
242 Self::default()
243 }
244
245 pub fn with_sector_bytes(sector_bytes: usize) -> Self {
246 Self(Arc::new(Inner {
247 state: Mutex::new(State {
248 sector_bytes,
249 ..State::default()
250 }),
251 }))
252 }
253
254 #[cfg(any(test, feature = "testing"))]
255 pub fn crash(&self) {
256 {
257 let mut state = self.0.state.lock();
258 let dirty: Vec<PathBuf> = mem::take(&mut state.dirty_dirs).into_iter().collect();
259 for path in dirty {
260 state.dirs.retain(|dir| !dir.starts_with(&path));
261 state.files.retain(|key, _| !key.starts_with(&path));
262 }
263 }
264 let files: Vec<Arc<MemoryFileState>> = self.0.state.lock().files.values().cloned().collect();
265 for file in files {
266 file.revert();
267 }
268 }
269
270 #[cfg(feature = "testing")]
271 pub(crate) fn detach(&self, path: &Path) -> Result<Arc<MemoryFileState>> {
272 self.0.state.lock().files.remove(path).ok_or_else(|| FsError::NotFound(path.to_path_buf()))
273 }
274
275 #[cfg(feature = "testing")]
276 pub(crate) fn attach(&self, path: &Path, state: Arc<MemoryFileState>) {
277 self.0.state.lock().files.insert(path.to_path_buf(), state);
278 }
279}
280
281pub struct MemoryFile {
282 path: PathBuf,
283 state: Arc<MemoryFileState>,
284}
285
286pub struct MemoryFileMut {
287 path: PathBuf,
288 state: Arc<MemoryFileState>,
289}
290
291impl MemoryFileMut {
292 pub fn write_masked(&self, offset: u64, buf: &[u8], mask: &SectorMask) -> Result<usize> {
293 self.state.write_masked(offset, buf, mask)
294 }
295
296 pub fn path(&self) -> &Path {
297 &self.path
298 }
299
300 pub fn sector_bytes(&self) -> usize {
301 self.state.sector_bytes
302 }
303
304 #[cfg(any(test, feature = "testing"))]
305 pub fn sector_states(&self) -> Vec<SectorState> {
306 self.state.sector_states()
307 }
308}
309
310impl MemoryFile {
311 pub fn path(&self) -> &Path {
312 &self.path
313 }
314
315 pub fn sector_bytes(&self) -> usize {
316 self.state.sector_bytes
317 }
318
319 #[cfg(any(test, feature = "testing"))]
320 pub fn sector_states(&self) -> Vec<SectorState> {
321 self.state.sector_states()
322 }
323}
324
325impl Filesystem for MemoryFs {
326 type File = MemoryFile;
327 type FileMut = MemoryFileMut;
328}
329
330impl Mkdir for MemoryFs {
331 fn mkdir(&self, path: &Path) -> Result<()> {
332 let mut state = self.0.state.lock();
333 if !state.parent_is_dir(path) {
334 return Err(FsError::NotFound(path.to_path_buf()));
335 }
336 if state.exists(path) {
337 return Err(FsError::AlreadyExists(path.to_path_buf()));
338 }
339 state.dirs.insert(path.to_path_buf());
340 state.dirty_dirs.insert(path.to_path_buf());
341 Ok(())
342 }
343}
344
345impl Create for MemoryFs {
346 fn create(&self, path: &Path, len: u64) -> Result<MemoryFileMut> {
347 let mut state = self.0.state.lock();
348 if !state.parent_is_dir(path) {
349 return Err(FsError::NotFound(path.to_path_buf()));
350 }
351 if state.exists(path) {
352 return Err(FsError::AlreadyExists(path.to_path_buf()));
353 }
354 let file = Arc::new(MemoryFileState::new(len as usize, state.sector_bytes));
355 state.files.insert(path.to_path_buf(), Arc::clone(&file));
356 Ok(MemoryFileMut {
357 path: path.to_path_buf(),
358 state: file,
359 })
360 }
361}
362
363impl Open for MemoryFs {
364 fn open(&self, path: &Path) -> Result<MemoryFile> {
365 let state = self.0.state.lock();
366 state.files
367 .get(path)
368 .map(|file| MemoryFile {
369 path: path.to_path_buf(),
370 state: Arc::clone(file),
371 })
372 .ok_or_else(|| FsError::NotFound(path.to_path_buf()))
373 }
374}
375
376impl OpenMut for MemoryFs {
377 fn open_mut(&self, path: &Path) -> Result<MemoryFileMut> {
378 let state = self.0.state.lock();
379 state.files
380 .get(path)
381 .map(|file| MemoryFileMut {
382 path: path.to_path_buf(),
383 state: Arc::clone(file),
384 })
385 .ok_or_else(|| FsError::NotFound(path.to_path_buf()))
386 }
387}
388
389impl ReadDir for MemoryFs {
390 fn read_dir(&self, path: &Path) -> Result<Vec<PathBuf>> {
391 let state = self.0.state.lock();
392 if !state.dirs.contains(path) {
393 if state.files.contains_key(path) {
394 return Err(FsError::NotADirectory(path.to_path_buf()));
395 }
396 return Err(FsError::NotFound(path.to_path_buf()));
397 }
398 let mut entries: Vec<PathBuf> = state
399 .files
400 .keys()
401 .chain(state.dirs.iter())
402 .filter(|entry| entry.parent() == Some(path))
403 .cloned()
404 .collect();
405 entries.sort();
406 Ok(entries)
407 }
408}
409
410impl Rename for MemoryFs {
411 fn rename(&self, from: &Path, to: &Path) -> Result<()> {
412 let mut state = self.0.state.lock();
413 let Some(file) = state.files.remove(from) else {
414 return Err(FsError::NotFound(from.to_path_buf()));
415 };
416 if !state.parent_is_dir(to) {
417 state.files.insert(from.to_path_buf(), file);
418 return Err(FsError::NotFound(to.to_path_buf()));
419 }
420 state.files.insert(to.to_path_buf(), file);
421 Ok(())
422 }
423}
424
425impl Unlink for MemoryFs {
426 fn unlink(&self, path: &Path) -> Result<()> {
427 let mut state = self.0.state.lock();
428 state.files.remove(path).map(|_| ()).ok_or_else(|| FsError::NotFound(path.to_path_buf()))
429 }
430}
431
432impl SyncDir for MemoryFs {
433 fn sync_dir(&self, path: &Path) -> Result<()> {
434 let mut state = self.0.state.lock();
435 if !state.dirs.contains(path) {
436 return Err(FsError::NotFound(path.to_path_buf()));
437 }
438 state.dirty_dirs.retain(|dirty| dirty.parent() != Some(path));
439 Ok(())
440 }
441}
442
443impl Pread for MemoryFile {
444 fn pread(&self, offset: u64, buf: &mut [u8]) -> Result<usize> {
445 self.state.pread(offset, buf)
446 }
447}
448
449impl Len for MemoryFile {
450 fn len(&self) -> Result<u64> {
451 self.state.len()
452 }
453}
454
455impl Pread for MemoryFileMut {
456 fn pread(&self, offset: u64, buf: &mut [u8]) -> Result<usize> {
457 self.state.pread(offset, buf)
458 }
459}
460
461impl Pwrite for MemoryFileMut {
462 fn pwrite(&self, offset: u64, buf: &[u8]) -> Result<usize> {
463 let mask = SectorMask::full(sector_span(offset, buf.len(), self.state.sector_bytes));
464 self.state.write_masked(offset, buf, &mask)
465 }
466}
467
468impl SyncData for MemoryFileMut {
469 fn sync_data(&self) -> Result<()> {
470 self.state.sync_data()
471 }
472}
473
474impl Truncate for MemoryFileMut {
475 fn truncate(&self, len: u64) -> Result<()> {
476 self.state.truncate(len)
477 }
478}
479
480impl Len for MemoryFileMut {
481 fn len(&self) -> Result<u64> {
482 self.state.len()
483 }
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489
490 fn read_all(file: &MemoryFileMut) -> Vec<u8> {
491 let len = file.len().unwrap() as usize;
492 let mut buf = vec![0u8; len];
493 assert_eq!(file.pread(0, &mut buf).unwrap(), len);
494 buf
495 }
496
497 #[test]
498 fn create_leaves_every_sector_unwritten_and_zeroed() {
499 let fs = MemoryFs::new();
501 let file = fs.create(Path::new("/a"), 1024).unwrap();
502 assert_eq!(file.sector_states(), vec![SectorState::Unwritten; 2]);
503 assert_eq!(read_all(&file), vec![0u8; 1024]);
504 }
505
506 #[test]
507 fn write_dirties_every_grazed_sector_whole() {
508 let fs = MemoryFs::new();
510 let file = fs.create(Path::new("/a"), 2048).unwrap();
511 assert_eq!(file.pwrite(500, &[7u8; 20]).unwrap(), 20);
512 assert_eq!(
513 file.sector_states(),
514 vec![SectorState::Dirty, SectorState::Dirty, SectorState::Unwritten, SectorState::Unwritten]
515 );
516 }
517
518 #[test]
519 fn sync_promotes_every_dirty_sector() {
520 let fs = MemoryFs::new();
522 let file = fs.create(Path::new("/a"), 2048).unwrap();
523 file.pwrite(0, &[1u8; 600]).unwrap();
524 file.sync_data().unwrap();
525 assert_eq!(
526 file.sector_states(),
527 vec![
528 SectorState::Durable,
529 SectorState::Durable,
530 SectorState::Unwritten,
531 SectorState::Unwritten
532 ]
533 );
534 }
535
536 #[test]
537 fn write_masked_skips_unmasked_sectors_but_reports_full_length() {
538 let fs = MemoryFs::new();
540 let file = fs.create(Path::new("/a"), 1536).unwrap();
541 let mut mask = SectorMask::empty(3);
542 mask.set(0);
543 mask.set(2);
544 assert_eq!(file.write_masked(0, &[0xABu8; 1536], &mask).unwrap(), 1536);
545 let bytes = read_all(&file);
546 assert!(bytes[0..512].iter().all(|byte| *byte == 0xAB));
547 assert!(bytes[512..1024].iter().all(|byte| *byte == 0));
548 assert!(bytes[1024..1536].iter().all(|byte| *byte == 0xAB));
549 assert_eq!(file.sector_states(), vec![SectorState::Dirty, SectorState::Unwritten, SectorState::Dirty]);
550 }
551
552 #[test]
553 fn mask_ignores_out_of_range_indices() {
554 let mut mask = SectorMask::empty(2);
556 mask.set(5);
557 assert_eq!(mask.sectors(), 2);
558 assert!(!mask.is_set(5));
559 let mut full = SectorMask::full(2);
560 full.clear(1);
561 assert!(full.is_set(0));
562 assert!(!full.is_set(1));
563 }
564
565 #[test]
566 fn pread_round_trips_clamps_and_stops_at_the_end() {
567 let fs = MemoryFs::new();
569 let file = fs.create(Path::new("/a"), 10).unwrap();
570 file.pwrite(0, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).unwrap();
571 let mut buf = [0u8; 4];
572 assert_eq!(file.pread(2, &mut buf).unwrap(), 4);
573 assert_eq!(buf, [3, 4, 5, 6]);
574 let mut tail = [0u8; 16];
575 assert_eq!(file.pread(5, &mut tail).unwrap(), 5);
576 assert_eq!(&tail[..5], &[6, 7, 8, 9, 10]);
577 assert_eq!(file.pread(10, &mut tail).unwrap(), 0);
578 assert_eq!(file.pread(9999, &mut tail).unwrap(), 0);
579 }
580
581 #[test]
582 fn write_past_the_end_grows_bytes_and_sectors_together() {
583 let fs = MemoryFs::new();
585 let file = fs.create(Path::new("/a"), 0).unwrap();
586 assert!(file.sector_states().is_empty());
587 assert_eq!(file.pwrite(1000, &[9u8; 10]).unwrap(), 10);
588 assert_eq!(file.len().unwrap(), 1010);
589 assert_eq!(file.sector_states(), vec![SectorState::Unwritten, SectorState::Dirty]);
590 assert_eq!(fs.open(Path::new("/a")).unwrap().sector_states().len(), 2);
591 }
592
593 #[test]
594 fn truncate_shortens_bytes_and_sector_states_together() {
595 let fs = MemoryFs::new();
597 let file = fs.create(Path::new("/a"), 2048).unwrap();
598 file.pwrite(0, &[3u8; 2048]).unwrap();
599 file.truncate(600).unwrap();
600 assert_eq!(file.len().unwrap(), 600);
601 assert_eq!(file.sector_states().len(), 2);
602 file.truncate(100).unwrap();
603 assert_eq!(file.sector_states().len(), 1);
604 assert_eq!(read_all(&file), vec![3u8; 100]);
605 file.truncate(1024).unwrap();
606 assert_eq!(file.sector_states(), vec![SectorState::Dirty, SectorState::Unwritten]);
607 }
608
609 #[test]
610 fn crash_reverts_dirty_sectors_to_their_prior_bytes_and_state() {
611 let fs = MemoryFs::new();
613 let file = fs.create(Path::new("/a"), 1024).unwrap();
614 file.pwrite(0, &[1u8; 512]).unwrap();
615 file.sync_data().unwrap();
616 file.pwrite(0, &[2u8; 512]).unwrap();
617 file.pwrite(512, &[2u8; 512]).unwrap();
618 fs.crash();
619 let bytes = read_all(&file);
620 assert!(bytes[0..512].iter().all(|byte| *byte == 1));
621 assert!(bytes[512..1024].iter().all(|byte| *byte == 0));
622 assert_eq!(file.sector_states(), vec![SectorState::Durable, SectorState::Unwritten]);
623 }
624
625 #[test]
626 fn crash_after_truncate_drops_shadow_beyond_the_new_end() {
627 let fs = MemoryFs::new();
629 let file = fs.create(Path::new("/a"), 2048).unwrap();
630 file.pwrite(1536, &[8u8; 512]).unwrap();
631 file.truncate(512).unwrap();
632 fs.crash();
633 assert_eq!(file.len().unwrap(), 512);
634 assert_eq!(file.sector_states(), vec![SectorState::Unwritten]);
635 }
636
637 #[test]
638 fn sync_clears_the_shadow_so_a_later_crash_keeps_the_data() {
639 let fs = MemoryFs::new();
641 let file = fs.create(Path::new("/a"), 512).unwrap();
642 file.pwrite(0, &[5u8; 512]).unwrap();
643 file.sync_data().unwrap();
644 fs.crash();
645 assert_eq!(read_all(&file), vec![5u8; 512]);
646 assert_eq!(file.sector_states(), vec![SectorState::Durable]);
647 }
648
649 #[test]
650 fn sector_size_is_configurable_and_consistent() {
651 let fs = MemoryFs::with_sector_bytes(64);
653 let file = fs.create(Path::new("/a"), 100).unwrap();
654 assert_eq!(file.sector_bytes(), 64);
655 assert_eq!(file.sector_states().len(), 2);
656 file.pwrite(0, &[1u8; 100]).unwrap();
657 assert_eq!(file.sector_states(), vec![SectorState::Dirty, SectorState::Dirty]);
658
659 let wide = MemoryFs::new();
660 let file = wide.create(Path::new("/a"), 100).unwrap();
661 assert_eq!(file.sector_bytes(), SECTOR_BYTES);
662 file.pwrite(0, &[1u8; 100]).unwrap();
663 assert_eq!(file.sector_states(), vec![SectorState::Dirty]);
664 }
665
666 #[test]
667 fn mkdir_needs_a_parent_directory_and_rejects_an_existing_path() {
668 let fs = MemoryFs::new();
670 assert_eq!(fs.mkdir(Path::new("/a/b")), Err(FsError::NotFound(PathBuf::from("/a/b"))));
671 fs.mkdir(Path::new("/a")).unwrap();
672 assert_eq!(fs.mkdir(Path::new("/a")), Err(FsError::AlreadyExists(PathBuf::from("/a"))));
673 fs.create(Path::new("/a/f"), 0).unwrap();
674 assert_eq!(fs.mkdir(Path::new("/a/f")), Err(FsError::AlreadyExists(PathBuf::from("/a/f"))));
675 }
676
677 #[test]
678 fn create_needs_a_parent_directory_and_rejects_an_existing_path() {
679 let fs = MemoryFs::new();
680 assert!(fs.create(Path::new("/a/f"), 0).is_err());
681 fs.mkdir(Path::new("/a")).unwrap();
682 fs.create(Path::new("/a/f"), 0).unwrap();
683 assert_eq!(fs.create(Path::new("/a/f"), 0).err(), Some(FsError::AlreadyExists(PathBuf::from("/a/f"))));
684 }
685
686 #[test]
687 fn open_and_open_mut_report_a_missing_file() {
688 let fs = MemoryFs::new();
689 assert_eq!(fs.open(Path::new("/a")).err(), Some(FsError::NotFound(PathBuf::from("/a"))));
690 assert_eq!(fs.open_mut(Path::new("/a")).err(), Some(FsError::NotFound(PathBuf::from("/a"))));
691 fs.create(Path::new("/a"), 8).unwrap();
692 let file = fs.open(Path::new("/a")).unwrap();
693 assert_eq!(file.path(), Path::new("/a"));
694 assert_eq!(file.len().unwrap(), 8);
695 assert_eq!(file.sector_bytes(), SECTOR_BYTES);
696 assert_eq!(file.sector_states(), vec![SectorState::Unwritten]);
697 }
698
699 #[test]
700 fn open_shares_the_state_with_the_creating_handle() {
701 let fs = MemoryFs::new();
703 let writer = fs.create(Path::new("/a"), 512).unwrap();
704 writer.pwrite(0, &[4u8; 8]).unwrap();
705 let reader = fs.open(Path::new("/a")).unwrap();
706 let mut buf = [0u8; 8];
707 assert_eq!(reader.pread(0, &mut buf).unwrap(), 8);
708 assert_eq!(buf, [4u8; 8]);
709 assert_eq!(reader.sector_states(), vec![SectorState::Dirty]);
710 }
711
712 #[test]
713 fn read_dir_lists_direct_children_in_ascending_order() {
714 let fs = MemoryFs::new();
716 fs.mkdir(Path::new("/d")).unwrap();
717 fs.mkdir(Path::new("/d/sub")).unwrap();
718 fs.create(Path::new("/d/c"), 0).unwrap();
719 fs.create(Path::new("/d/a"), 0).unwrap();
720 fs.create(Path::new("/d/sub/deep"), 0).unwrap();
721 assert_eq!(
722 fs.read_dir(Path::new("/d")).unwrap(),
723 vec![PathBuf::from("/d/a"), PathBuf::from("/d/c"), PathBuf::from("/d/sub")]
724 );
725 assert_eq!(fs.read_dir(Path::new("/nope")).err(), Some(FsError::NotFound(PathBuf::from("/nope"))));
726 assert_eq!(fs.read_dir(Path::new("/d/a")).err(), Some(FsError::NotADirectory(PathBuf::from("/d/a"))));
727 }
728
729 #[test]
730 fn rename_moves_the_entry_and_keeps_the_contents() {
731 let fs = MemoryFs::new();
732 let file = fs.create(Path::new("/a"), 512).unwrap();
733 file.pwrite(0, &[6u8; 4]).unwrap();
734 fs.rename(Path::new("/a"), Path::new("/b")).unwrap();
735 assert_eq!(fs.open(Path::new("/a")).err(), Some(FsError::NotFound(PathBuf::from("/a"))));
736 let moved = fs.open(Path::new("/b")).unwrap();
737 let mut buf = [0u8; 4];
738 assert_eq!(moved.pread(0, &mut buf).unwrap(), 4);
739 assert_eq!(buf, [6u8; 4]);
740 assert_eq!(
741 fs.rename(Path::new("/a"), Path::new("/c")).err(),
742 Some(FsError::NotFound(PathBuf::from("/a")))
743 );
744 }
745
746 #[test]
747 fn rename_into_a_missing_directory_leaves_the_source_in_place() {
748 let fs = MemoryFs::new();
750 fs.create(Path::new("/a"), 0).unwrap();
751 assert_eq!(
752 fs.rename(Path::new("/a"), Path::new("/d/b")).err(),
753 Some(FsError::NotFound(PathBuf::from("/d/b")))
754 );
755 assert!(fs.open(Path::new("/a")).is_ok());
756 }
757
758 #[test]
759 fn unlink_removes_the_entry_or_reports_it_missing() {
760 let fs = MemoryFs::new();
761 fs.create(Path::new("/a"), 0).unwrap();
762 fs.unlink(Path::new("/a")).unwrap();
763 assert_eq!(fs.unlink(Path::new("/a")).err(), Some(FsError::NotFound(PathBuf::from("/a"))));
764 }
765
766 #[test]
767 fn sync_dir_checks_the_directory_exists() {
768 let fs = MemoryFs::new();
769 fs.sync_dir(Path::new("/")).unwrap();
770 assert_eq!(fs.sync_dir(Path::new("/d")).err(), Some(FsError::NotFound(PathBuf::from("/d"))));
771 fs.mkdir(Path::new("/d")).unwrap();
772 fs.sync_dir(Path::new("/d")).unwrap();
773 }
774
775 #[test]
776 fn a_cloned_handle_shares_one_filesystem() {
777 let fs = MemoryFs::new();
779 let clone = fs.clone();
780 clone.mkdir(Path::new("/d")).unwrap();
781 assert_eq!(fs.read_dir(Path::new("/")).unwrap(), vec![PathBuf::from("/d")]);
782 }
783}