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