inmemory_fs/
inmemory_fs.rs

1//! Complete in-memory filesystem reference implementation.
2//!
3//! This example provides a full implementation of ALL anyfs-backend traits
4//! up to `FsPosix`. Use this as a reference when implementing your own
5//! filesystem backend.
6//!
7//! Run with: `cargo run --example inmemory_fs`
8//!
9//! This implementation is suitable as a starting point for:
10//! - Testing and mocking
11//! - In-memory caching layers
12//! - Learning how each trait method should behave
13
14use anyfs_backend::*;
15use std::collections::HashMap;
16use std::ffi::OsStr;
17use std::io::{Read, Write};
18use std::path::{Path, PathBuf};
19use std::sync::atomic::{AtomicU64, Ordering};
20use std::sync::RwLock;
21use std::time::SystemTime;
22
23// =============================================================================
24// Complete In-Memory Filesystem Implementation
25// =============================================================================
26
27/// A complete in-memory filesystem implementing all traits up to FsPosix.
28///
29/// ## Thread Safety
30///
31/// This implementation uses `RwLock` for interior mutability, making it
32/// safe to use from multiple threads. All trait methods take `&self`,
33/// following the anyfs-backend design requirement.
34///
35/// ## Trait Hierarchy
36///
37/// This type implements:
38/// - Layer 1: `Fs` = `FsRead` + `FsWrite` + `FsDir`
39/// - Layer 2: `FsFull` = `Fs` + `FsLink` + `FsPermissions` + `FsSync` + `FsStats`
40/// - Layer 3: `FsFuse` = `FsFull` + `FsInode`
41/// - Layer 4: `FsPosix` = `FsFuse` + `FsHandles` + `FsLock` + `FsXattr`
42pub struct InMemoryFs {
43    /// File contents, keyed by path
44    files: RwLock<HashMap<PathBuf, Vec<u8>>>,
45
46    /// Set of directories
47    dirs: RwLock<std::collections::HashSet<PathBuf>>,
48
49    /// Symlinks: link_path -> target_path
50    symlinks: RwLock<HashMap<PathBuf, PathBuf>>,
51
52    /// Extended attributes: path -> (name -> value)
53    xattrs: RwLock<HashMap<PathBuf, HashMap<String, Vec<u8>>>>,
54
55    /// Inode mapping: path -> inode number
56    inodes: RwLock<HashMap<PathBuf, u64>>,
57
58    /// Reverse inode mapping: inode number -> path
59    inode_to_path: RwLock<HashMap<u64, PathBuf>>,
60
61    /// Open file handles: handle_id -> (path, flags)
62    handles: RwLock<HashMap<u64, OpenFile>>,
63
64    /// File locks: handle_id -> lock state
65    locks: RwLock<HashMap<u64, LockState>>,
66
67    /// Counter for generating unique inode numbers
68    next_inode: AtomicU64,
69
70    /// Counter for generating unique handle IDs
71    next_handle: AtomicU64,
72}
73
74/// Information about an open file.
75struct OpenFile {
76    path: PathBuf,
77    flags: OpenFlags,
78}
79
80/// Current lock state for a file handle.
81#[derive(Clone, Copy, PartialEq)]
82enum LockState {
83    Unlocked,
84    Shared(usize), // Count of shared locks
85    Exclusive,
86}
87
88impl InMemoryFs {
89    /// Create a new empty in-memory filesystem.
90    ///
91    /// The root directory `/` is created automatically.
92    pub fn new() -> Self {
93        let fs = Self {
94            files: RwLock::new(HashMap::new()),
95            dirs: RwLock::new(std::collections::HashSet::new()),
96            symlinks: RwLock::new(HashMap::new()),
97            xattrs: RwLock::new(HashMap::new()),
98            inodes: RwLock::new(HashMap::new()),
99            inode_to_path: RwLock::new(HashMap::new()),
100            handles: RwLock::new(HashMap::new()),
101            locks: RwLock::new(HashMap::new()),
102            next_inode: AtomicU64::new(2), // 1 is reserved for root
103            next_handle: AtomicU64::new(1),
104        };
105
106        // Create root directory with inode 1
107        fs.dirs.write().unwrap().insert(PathBuf::from("/"));
108        fs.assign_inode(Path::new("/"));
109
110        fs
111    }
112
113    /// Assign an inode to a path (or return existing inode).
114    fn assign_inode(&self, path: &Path) -> u64 {
115        let mut inodes = self.inodes.write().unwrap();
116
117        // Return existing inode if path already has one
118        if let Some(&inode) = inodes.get(path) {
119            return inode;
120        }
121
122        // Root always gets inode 1
123        let inode = if path == Path::new("/") {
124            ROOT_INODE
125        } else {
126            self.next_inode.fetch_add(1, Ordering::SeqCst)
127        };
128
129        // Store bidirectional mapping
130        inodes.insert(path.to_path_buf(), inode);
131        self.inode_to_path
132            .write()
133            .unwrap()
134            .insert(inode, path.to_path_buf());
135
136        inode
137    }
138
139    /// Get the file type at a path (without following symlinks).
140    fn get_file_type(&self, path: &Path) -> Option<FileType> {
141        if self.symlinks.read().unwrap().contains_key(path) {
142            Some(FileType::Symlink)
143        } else if self.dirs.read().unwrap().contains(path) {
144            Some(FileType::Directory)
145        } else if self.files.read().unwrap().contains_key(path) {
146            Some(FileType::File)
147        } else {
148            None
149        }
150    }
151}
152
153impl Default for InMemoryFs {
154    fn default() -> Self {
155        Self::new()
156    }
157}
158
159// =============================================================================
160// Layer 1: FsRead - Reading files and metadata
161// =============================================================================
162
163impl FsRead for InMemoryFs {
164    fn read(&self, path: &Path) -> Result<Vec<u8>, FsError> {
165        self.files
166            .read()
167            .unwrap()
168            .get(path)
169            .cloned()
170            .ok_or_else(|| FsError::NotFound {
171                path: path.to_path_buf(),
172            })
173    }
174
175    fn read_to_string(&self, path: &Path) -> Result<String, FsError> {
176        let bytes = self.read(path)?;
177        String::from_utf8(bytes).map_err(|_| FsError::InvalidData {
178            path: path.to_path_buf(),
179            details: "file contents are not valid UTF-8".into(),
180        })
181    }
182
183    fn read_range(&self, path: &Path, offset: u64, len: usize) -> Result<Vec<u8>, FsError> {
184        let data = self.read(path)?;
185        let start = offset as usize;
186
187        if start >= data.len() {
188            return Ok(Vec::new());
189        }
190
191        let end = (start + len).min(data.len());
192        Ok(data[start..end].to_vec())
193    }
194
195    fn exists(&self, path: &Path) -> Result<bool, FsError> {
196        Ok(self.get_file_type(path).is_some())
197    }
198
199    fn metadata(&self, path: &Path) -> Result<Metadata, FsError> {
200        let file_type = self.get_file_type(path).ok_or_else(|| FsError::NotFound {
201            path: path.to_path_buf(),
202        })?;
203
204        let size = if file_type == FileType::File {
205            self.files
206                .read()
207                .unwrap()
208                .get(path)
209                .map(|d| d.len() as u64)
210                .unwrap_or(0)
211        } else {
212            0
213        };
214
215        let inode = self.inodes.read().unwrap().get(path).copied().unwrap_or(0);
216
217        Ok(Metadata {
218            file_type,
219            size,
220            permissions: Permissions::default_file(),
221            created: SystemTime::UNIX_EPOCH,
222            modified: SystemTime::UNIX_EPOCH,
223            accessed: SystemTime::UNIX_EPOCH,
224            inode,
225            nlink: 1,
226        })
227    }
228
229    fn open_read(&self, path: &Path) -> Result<Box<dyn Read + Send>, FsError> {
230        let data = self.read(path)?;
231        Ok(Box::new(std::io::Cursor::new(data)))
232    }
233}
234
235// =============================================================================
236// Layer 1: FsWrite - Writing and modifying files
237// =============================================================================
238
239impl FsWrite for InMemoryFs {
240    fn write(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
241        self.assign_inode(path);
242        self.files
243            .write()
244            .unwrap()
245            .insert(path.to_path_buf(), data.to_vec());
246        Ok(())
247    }
248
249    fn append(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
250        let mut files = self.files.write().unwrap();
251        files
252            .entry(path.to_path_buf())
253            .or_default()
254            .extend_from_slice(data);
255        drop(files);
256        self.assign_inode(path);
257        Ok(())
258    }
259
260    fn remove_file(&self, path: &Path) -> Result<(), FsError> {
261        self.files
262            .write()
263            .unwrap()
264            .remove(path)
265            .ok_or_else(|| FsError::NotFound {
266                path: path.to_path_buf(),
267            })?;
268
269        // Clean up inode mapping
270        if let Some(inode) = self.inodes.write().unwrap().remove(path) {
271            self.inode_to_path.write().unwrap().remove(&inode);
272        }
273
274        // Clean up xattrs
275        self.xattrs.write().unwrap().remove(path);
276
277        Ok(())
278    }
279
280    fn rename(&self, from: &Path, to: &Path) -> Result<(), FsError> {
281        let mut files = self.files.write().unwrap();
282        let data = files.remove(from).ok_or_else(|| FsError::NotFound {
283            path: from.to_path_buf(),
284        })?;
285        files.insert(to.to_path_buf(), data);
286        drop(files);
287
288        // Update inode mappings
289        if let Some(inode) = self.inodes.write().unwrap().remove(from) {
290            self.inodes.write().unwrap().insert(to.to_path_buf(), inode);
291            self.inode_to_path
292                .write()
293                .unwrap()
294                .insert(inode, to.to_path_buf());
295        } else {
296            self.assign_inode(to);
297        }
298
299        // Move xattrs
300        if let Some(attrs) = self.xattrs.write().unwrap().remove(from) {
301            self.xattrs.write().unwrap().insert(to.to_path_buf(), attrs);
302        }
303
304        Ok(())
305    }
306
307    fn copy(&self, from: &Path, to: &Path) -> Result<(), FsError> {
308        let data = self.read(from)?;
309        self.write(to, &data)
310    }
311
312    fn truncate(&self, path: &Path, size: u64) -> Result<(), FsError> {
313        let mut files = self.files.write().unwrap();
314        let data = files.get_mut(path).ok_or_else(|| FsError::NotFound {
315            path: path.to_path_buf(),
316        })?;
317        data.resize(size as usize, 0);
318        Ok(())
319    }
320
321    fn open_write(&self, path: &Path) -> Result<Box<dyn Write + Send>, FsError> {
322        // Create file if it doesn't exist
323        if !self.files.read().unwrap().contains_key(path) {
324            self.write(path, &[])?;
325        }
326
327        // Return a cursor that writes to a buffer
328        // Note: In a real implementation, this would write back on drop
329        Ok(Box::new(std::io::Cursor::new(Vec::new())))
330    }
331}
332
333// =============================================================================
334// Layer 1: FsDir - Directory operations
335// =============================================================================
336
337impl FsDir for InMemoryFs {
338    fn read_dir(&self, path: &Path) -> Result<ReadDirIter, FsError> {
339        if !self.dirs.read().unwrap().contains(path) {
340            return Err(FsError::NotFound {
341                path: path.to_path_buf(),
342            });
343        }
344
345        let mut entries = Vec::new();
346
347        // Collect files in this directory
348        for (file_path, data) in self.files.read().unwrap().iter() {
349            if let Some(parent) = file_path.parent() {
350                if parent == path {
351                    if let Some(name) = file_path.file_name() {
352                        entries.push(Ok(DirEntry {
353                            name: name.to_string_lossy().into_owned(),
354                            path: file_path.clone(),
355                            file_type: FileType::File,
356                            size: data.len() as u64,
357                            inode: self
358                                .inodes
359                                .read()
360                                .unwrap()
361                                .get(file_path)
362                                .copied()
363                                .unwrap_or(0),
364                        }));
365                    }
366                }
367            }
368        }
369
370        // Collect subdirectories
371        for dir_path in self.dirs.read().unwrap().iter() {
372            if let Some(parent) = dir_path.parent() {
373                if parent == path && dir_path != path {
374                    if let Some(name) = dir_path.file_name() {
375                        entries.push(Ok(DirEntry {
376                            name: name.to_string_lossy().into_owned(),
377                            path: dir_path.clone(),
378                            file_type: FileType::Directory,
379                            size: 0,
380                            inode: self
381                                .inodes
382                                .read()
383                                .unwrap()
384                                .get(dir_path)
385                                .copied()
386                                .unwrap_or(0),
387                        }));
388                    }
389                }
390            }
391        }
392
393        // Collect symlinks
394        for link_path in self.symlinks.read().unwrap().keys() {
395            if let Some(parent) = link_path.parent() {
396                if parent == path {
397                    if let Some(name) = link_path.file_name() {
398                        entries.push(Ok(DirEntry {
399                            name: name.to_string_lossy().into_owned(),
400                            path: link_path.clone(),
401                            file_type: FileType::Symlink,
402                            size: 0,
403                            inode: self
404                                .inodes
405                                .read()
406                                .unwrap()
407                                .get(link_path)
408                                .copied()
409                                .unwrap_or(0),
410                        }));
411                    }
412                }
413            }
414        }
415
416        Ok(ReadDirIter::from_vec(entries))
417    }
418
419    fn create_dir(&self, path: &Path) -> Result<(), FsError> {
420        let mut dirs = self.dirs.write().unwrap();
421
422        if dirs.contains(path) {
423            return Err(FsError::AlreadyExists {
424                path: path.to_path_buf(),
425                operation: "create_dir",
426            });
427        }
428
429        // Check parent exists
430        if let Some(parent) = path.parent() {
431            if parent != Path::new("") && parent != Path::new("/") && !dirs.contains(parent) {
432                return Err(FsError::NotFound {
433                    path: parent.to_path_buf(),
434                });
435            }
436        }
437
438        dirs.insert(path.to_path_buf());
439        drop(dirs);
440        self.assign_inode(path);
441
442        Ok(())
443    }
444
445    fn create_dir_all(&self, path: &Path) -> Result<(), FsError> {
446        let mut current = PathBuf::new();
447
448        for component in path.components() {
449            current.push(component);
450
451            let mut dirs = self.dirs.write().unwrap();
452            if !dirs.contains(&current) {
453                dirs.insert(current.clone());
454                drop(dirs);
455                self.assign_inode(&current);
456            }
457        }
458
459        Ok(())
460    }
461
462    fn remove_dir(&self, path: &Path) -> Result<(), FsError> {
463        // Check if directory is empty
464        let has_files = self
465            .files
466            .read()
467            .unwrap()
468            .keys()
469            .any(|p| p.parent() == Some(path));
470
471        let has_subdirs = self
472            .dirs
473            .read()
474            .unwrap()
475            .iter()
476            .any(|p| p.parent() == Some(path) && p != path);
477
478        if has_files || has_subdirs {
479            return Err(FsError::DirectoryNotEmpty {
480                path: path.to_path_buf(),
481            });
482        }
483
484        if !self.dirs.write().unwrap().remove(path) {
485            return Err(FsError::NotFound {
486                path: path.to_path_buf(),
487            });
488        }
489
490        // Clean up inode
491        if let Some(inode) = self.inodes.write().unwrap().remove(path) {
492            self.inode_to_path.write().unwrap().remove(&inode);
493        }
494
495        Ok(())
496    }
497
498    fn remove_dir_all(&self, path: &Path) -> Result<(), FsError> {
499        // Remove all files under this path
500        self.files
501            .write()
502            .unwrap()
503            .retain(|p, _| !p.starts_with(path));
504
505        // Remove all subdirectories under this path
506        self.dirs.write().unwrap().retain(|p| !p.starts_with(path));
507
508        // Remove all symlinks under this path
509        self.symlinks
510            .write()
511            .unwrap()
512            .retain(|p, _| !p.starts_with(path));
513
514        // Clean up inodes
515        let paths_to_remove: Vec<_> = self
516            .inodes
517            .read()
518            .unwrap()
519            .keys()
520            .filter(|p| p.starts_with(path))
521            .cloned()
522            .collect();
523
524        for p in paths_to_remove {
525            if let Some(inode) = self.inodes.write().unwrap().remove(&p) {
526                self.inode_to_path.write().unwrap().remove(&inode);
527            }
528        }
529
530        Ok(())
531    }
532}
533
534// =============================================================================
535// Layer 2: FsLink - Symbolic and hard links
536// =============================================================================
537
538impl FsLink for InMemoryFs {
539    fn symlink(&self, target: &Path, link: &Path) -> Result<(), FsError> {
540        if self.get_file_type(link).is_some() {
541            return Err(FsError::AlreadyExists {
542                path: link.to_path_buf(),
543                operation: "symlink",
544            });
545        }
546
547        self.symlinks
548            .write()
549            .unwrap()
550            .insert(link.to_path_buf(), target.to_path_buf());
551        self.assign_inode(link);
552
553        Ok(())
554    }
555
556    fn hard_link(&self, original: &Path, link: &Path) -> Result<(), FsError> {
557        // For in-memory fs, we just copy the data (true hard links would share data)
558        let data = self.read(original)?;
559        self.write(link, &data)?;
560
561        // In a real implementation, you'd update nlink count
562        Ok(())
563    }
564
565    fn read_link(&self, path: &Path) -> Result<PathBuf, FsError> {
566        self.symlinks
567            .read()
568            .unwrap()
569            .get(path)
570            .cloned()
571            .ok_or_else(|| FsError::InvalidData {
572                path: path.to_path_buf(),
573                details: "not a symbolic link".into(),
574            })
575    }
576
577    fn symlink_metadata(&self, path: &Path) -> Result<Metadata, FsError> {
578        let file_type = self.get_file_type(path).ok_or_else(|| FsError::NotFound {
579            path: path.to_path_buf(),
580        })?;
581
582        Ok(Metadata {
583            file_type,
584            size: 0,
585            permissions: Permissions::default_file(),
586            created: SystemTime::UNIX_EPOCH,
587            modified: SystemTime::UNIX_EPOCH,
588            accessed: SystemTime::UNIX_EPOCH,
589            inode: self.inodes.read().unwrap().get(path).copied().unwrap_or(0),
590            nlink: 1,
591        })
592    }
593}
594
595// =============================================================================
596// Layer 2: FsPermissions - Permission management
597// =============================================================================
598
599impl FsPermissions for InMemoryFs {
600    fn set_permissions(&self, path: &Path, _perm: Permissions) -> Result<(), FsError> {
601        // Verify path exists
602        if self.get_file_type(path).is_none() {
603            return Err(FsError::NotFound {
604                path: path.to_path_buf(),
605            });
606        }
607
608        // In a real implementation, you'd store and enforce permissions
609        Ok(())
610    }
611}
612
613// =============================================================================
614// Layer 2: FsSync - Synchronization operations
615// =============================================================================
616
617impl FsSync for InMemoryFs {
618    fn sync(&self) -> Result<(), FsError> {
619        // In-memory filesystem is always synchronized
620        Ok(())
621    }
622
623    fn fsync(&self, path: &Path) -> Result<(), FsError> {
624        // Verify path exists
625        if self.get_file_type(path).is_none() {
626            return Err(FsError::NotFound {
627                path: path.to_path_buf(),
628            });
629        }
630
631        // In-memory filesystem is always synchronized
632        Ok(())
633    }
634}
635
636// =============================================================================
637// Layer 2: FsStats - Filesystem statistics
638// =============================================================================
639
640impl FsStats for InMemoryFs {
641    fn statfs(&self) -> Result<StatFs, FsError> {
642        let files = self.files.read().unwrap();
643        let used_bytes: u64 = files.values().map(|d| d.len() as u64).sum();
644        let used_inodes = files.len() + self.dirs.read().unwrap().len();
645
646        Ok(StatFs {
647            total_bytes: 1024 * 1024 * 100, // 100 MB
648            used_bytes,
649            available_bytes: 1024 * 1024 * 100 - used_bytes,
650            total_inodes: 100_000,
651            used_inodes: used_inodes as u64,
652            available_inodes: 100_000 - used_inodes as u64,
653            block_size: 4096,
654            max_name_len: 255,
655        })
656    }
657}
658
659// =============================================================================
660// Layer 3: FsInode - Inode-based operations (for FUSE)
661// =============================================================================
662
663impl FsInode for InMemoryFs {
664    fn path_to_inode(&self, path: &Path) -> Result<u64, FsError> {
665        self.inodes
666            .read()
667            .unwrap()
668            .get(path)
669            .copied()
670            .ok_or_else(|| FsError::NotFound {
671                path: path.to_path_buf(),
672            })
673    }
674
675    fn inode_to_path(&self, inode: u64) -> Result<PathBuf, FsError> {
676        self.inode_to_path
677            .read()
678            .unwrap()
679            .get(&inode)
680            .cloned()
681            .ok_or(FsError::InodeNotFound { inode })
682    }
683
684    fn lookup(&self, parent_inode: u64, name: &OsStr) -> Result<u64, FsError> {
685        let parent_path = self.inode_to_path(parent_inode)?;
686        let child_path = parent_path.join(name);
687        self.path_to_inode(&child_path)
688    }
689
690    fn metadata_by_inode(&self, inode: u64) -> Result<Metadata, FsError> {
691        let path = self.inode_to_path(inode)?;
692        self.metadata(&path)
693    }
694}
695
696// =============================================================================
697// Layer 4: FsHandles - Handle-based I/O
698// =============================================================================
699
700impl FsHandles for InMemoryFs {
701    fn open(&self, path: &Path, flags: OpenFlags) -> Result<Handle, FsError> {
702        // Create file if requested
703        if flags.create {
704            if !self.files.read().unwrap().contains_key(path) {
705                self.write(path, &[])?;
706            }
707        } else if !self.files.read().unwrap().contains_key(path) {
708            return Err(FsError::NotFound {
709                path: path.to_path_buf(),
710            });
711        }
712
713        let handle_id = self.next_handle.fetch_add(1, Ordering::SeqCst);
714
715        self.handles.write().unwrap().insert(
716            handle_id,
717            OpenFile {
718                path: path.to_path_buf(),
719                flags,
720            },
721        );
722
723        Ok(Handle(handle_id))
724    }
725
726    fn read_at(&self, handle: Handle, buf: &mut [u8], offset: u64) -> Result<usize, FsError> {
727        let handles = self.handles.read().unwrap();
728        let open_file = handles
729            .get(&handle.0)
730            .ok_or(FsError::InvalidHandle { handle })?;
731
732        // Check read permission
733        if !open_file.flags.read {
734            return Err(FsError::PermissionDenied {
735                path: open_file.path.clone(),
736                operation: "read",
737            });
738        }
739
740        let files = self.files.read().unwrap();
741        let data = files.get(&open_file.path).ok_or(FsError::NotFound {
742            path: open_file.path.clone(),
743        })?;
744
745        let start = offset as usize;
746        if start >= data.len() {
747            return Ok(0);
748        }
749
750        let end = (start + buf.len()).min(data.len());
751        let bytes_read = end - start;
752        buf[..bytes_read].copy_from_slice(&data[start..end]);
753
754        Ok(bytes_read)
755    }
756
757    fn write_at(&self, handle: Handle, data: &[u8], offset: u64) -> Result<usize, FsError> {
758        let path = {
759            let handles = self.handles.read().unwrap();
760            let open_file = handles
761                .get(&handle.0)
762                .ok_or(FsError::InvalidHandle { handle })?;
763
764            // Check write permission
765            if !open_file.flags.write {
766                return Err(FsError::PermissionDenied {
767                    path: open_file.path.clone(),
768                    operation: "write",
769                });
770            }
771
772            open_file.path.clone()
773        };
774
775        let mut files = self.files.write().unwrap();
776        let file_data = files.entry(path).or_default();
777
778        let start = offset as usize;
779        if start + data.len() > file_data.len() {
780            file_data.resize(start + data.len(), 0);
781        }
782        file_data[start..start + data.len()].copy_from_slice(data);
783
784        Ok(data.len())
785    }
786
787    fn close(&self, handle: Handle) -> Result<(), FsError> {
788        // Release any locks
789        self.locks.write().unwrap().remove(&handle.0);
790
791        // Remove handle
792        self.handles
793            .write()
794            .unwrap()
795            .remove(&handle.0)
796            .map(|_| ())
797            .ok_or(FsError::InvalidHandle { handle })
798    }
799}
800
801// =============================================================================
802// Layer 4: FsLock - File locking
803// =============================================================================
804
805impl FsLock for InMemoryFs {
806    fn lock(&self, handle: Handle, lock_type: LockType) -> Result<(), FsError> {
807        if !self.handles.read().unwrap().contains_key(&handle.0) {
808            return Err(FsError::InvalidHandle { handle });
809        }
810
811        let mut locks = self.locks.write().unwrap();
812        let state = locks.entry(handle.0).or_insert(LockState::Unlocked);
813
814        match (*state, lock_type) {
815            (LockState::Unlocked, LockType::Shared) => {
816                *state = LockState::Shared(1);
817                Ok(())
818            }
819            (LockState::Unlocked, LockType::Exclusive) => {
820                *state = LockState::Exclusive;
821                Ok(())
822            }
823            (LockState::Shared(n), LockType::Shared) => {
824                *state = LockState::Shared(n + 1);
825                Ok(())
826            }
827            _ => Err(FsError::Conflict {
828                path: PathBuf::new(),
829            }),
830        }
831    }
832
833    fn try_lock(&self, handle: Handle, lock_type: LockType) -> Result<bool, FsError> {
834        if !self.handles.read().unwrap().contains_key(&handle.0) {
835            return Err(FsError::InvalidHandle { handle });
836        }
837
838        let mut locks = self.locks.write().unwrap();
839        let state = locks.entry(handle.0).or_insert(LockState::Unlocked);
840
841        match (*state, lock_type) {
842            (LockState::Unlocked, LockType::Shared) => {
843                *state = LockState::Shared(1);
844                Ok(true)
845            }
846            (LockState::Unlocked, LockType::Exclusive) => {
847                *state = LockState::Exclusive;
848                Ok(true)
849            }
850            (LockState::Shared(n), LockType::Shared) => {
851                *state = LockState::Shared(n + 1);
852                Ok(true)
853            }
854            _ => Ok(false),
855        }
856    }
857
858    fn unlock(&self, handle: Handle) -> Result<(), FsError> {
859        if !self.handles.read().unwrap().contains_key(&handle.0) {
860            return Err(FsError::InvalidHandle { handle });
861        }
862
863        let mut locks = self.locks.write().unwrap();
864        if let Some(state) = locks.get_mut(&handle.0) {
865            match *state {
866                LockState::Shared(1) => *state = LockState::Unlocked,
867                LockState::Shared(n) => *state = LockState::Shared(n - 1),
868                LockState::Exclusive => *state = LockState::Unlocked,
869                LockState::Unlocked => {}
870            }
871        }
872
873        Ok(())
874    }
875}
876
877// =============================================================================
878// Layer 4: FsXattr - Extended attributes
879// =============================================================================
880
881impl FsXattr for InMemoryFs {
882    fn get_xattr(&self, path: &Path, name: &str) -> Result<Vec<u8>, FsError> {
883        // Check file exists
884        if self.get_file_type(path).is_none() {
885            return Err(FsError::NotFound {
886                path: path.to_path_buf(),
887            });
888        }
889
890        self.xattrs
891            .read()
892            .unwrap()
893            .get(path)
894            .and_then(|attrs| attrs.get(name).cloned())
895            .ok_or_else(|| FsError::XattrNotFound {
896                path: path.to_path_buf(),
897                name: name.to_string(),
898            })
899    }
900
901    fn set_xattr(&self, path: &Path, name: &str, value: &[u8]) -> Result<(), FsError> {
902        // Check file exists
903        if self.get_file_type(path).is_none() {
904            return Err(FsError::NotFound {
905                path: path.to_path_buf(),
906            });
907        }
908
909        self.xattrs
910            .write()
911            .unwrap()
912            .entry(path.to_path_buf())
913            .or_default()
914            .insert(name.to_string(), value.to_vec());
915
916        Ok(())
917    }
918
919    fn remove_xattr(&self, path: &Path, name: &str) -> Result<(), FsError> {
920        // Check file exists
921        if self.get_file_type(path).is_none() {
922            return Err(FsError::NotFound {
923                path: path.to_path_buf(),
924            });
925        }
926
927        self.xattrs
928            .write()
929            .unwrap()
930            .get_mut(path)
931            .and_then(|attrs| attrs.remove(name))
932            .ok_or_else(|| FsError::XattrNotFound {
933                path: path.to_path_buf(),
934                name: name.to_string(),
935            })?;
936
937        Ok(())
938    }
939
940    fn list_xattr(&self, path: &Path) -> Result<Vec<String>, FsError> {
941        // Check file exists
942        if self.get_file_type(path).is_none() {
943            return Err(FsError::NotFound {
944                path: path.to_path_buf(),
945            });
946        }
947
948        Ok(self
949            .xattrs
950            .read()
951            .unwrap()
952            .get(path)
953            .map(|attrs| attrs.keys().cloned().collect())
954            .unwrap_or_default())
955    }
956}
957
958// =============================================================================
959// Main: Demonstrate the complete implementation
960// =============================================================================
961
962fn main() {
963    println!("=== In-Memory Filesystem Reference Implementation ===\n");
964
965    let fs = InMemoryFs::new();
966
967    // --- Layer 1: Fs operations ---
968    println!("--- Layer 1: Fs (FsRead + FsWrite + FsDir) ---\n");
969
970    fs.create_dir_all(Path::new("/project/src")).unwrap();
971    fs.write(Path::new("/project/README.md"), b"# My Project\n\nHello!")
972        .unwrap();
973    fs.write(Path::new("/project/src/main.rs"), b"fn main() {}")
974        .unwrap();
975
976    println!("Created project structure:");
977    for entry in fs.read_dir(Path::new("/project")).unwrap() {
978        let entry = entry.unwrap();
979        println!("  {} ({:?})", entry.name, entry.file_type);
980    }
981
982    let readme = fs.read_to_string(Path::new("/project/README.md")).unwrap();
983    println!("\nREADME.md content:\n{readme}");
984
985    // --- Layer 2: FsFull operations ---
986    println!("\n--- Layer 2: FsFull (+FsLink, +FsStats, +FsSync) ---\n");
987
988    fs.symlink(Path::new("/project/README.md"), Path::new("/project/docs"))
989        .unwrap();
990    let target = fs.read_link(Path::new("/project/docs")).unwrap();
991    println!("Symlink /project/docs -> {}", target.display());
992
993    let stats = fs.statfs().unwrap();
994    println!(
995        "Filesystem: {} bytes used, {} available",
996        stats.used_bytes, stats.available_bytes
997    );
998
999    fs.sync().unwrap();
1000    println!("Filesystem synced");
1001
1002    // --- Layer 3: FsFuse operations ---
1003    println!("\n--- Layer 3: FsFuse (+FsInode) ---\n");
1004
1005    let root_inode = fs.path_to_inode(Path::new("/")).unwrap();
1006    println!("Root inode: {root_inode} (should be {})", ROOT_INODE);
1007
1008    let project_inode = fs.lookup(root_inode, OsStr::new("project")).unwrap();
1009    println!("Project inode: {project_inode}");
1010
1011    let meta = fs.metadata_by_inode(project_inode).unwrap();
1012    println!("Project metadata: {:?}", meta.file_type);
1013
1014    // --- Layer 4: FsPosix operations ---
1015    println!("\n--- Layer 4: FsPosix (+FsHandles, +FsLock, +FsXattr) ---\n");
1016
1017    // Handle-based I/O
1018    let handle = fs
1019        .open(Path::new("/project/data.bin"), OpenFlags::WRITE)
1020        .unwrap();
1021    fs.write_at(handle, b"HEADER", 0).unwrap();
1022    fs.write_at(handle, b"DATA", 6).unwrap();
1023    fs.close(handle).unwrap();
1024    println!("Wrote data.bin using handle-based I/O");
1025
1026    let handle = fs
1027        .open(Path::new("/project/data.bin"), OpenFlags::READ)
1028        .unwrap();
1029    let mut buf = [0u8; 10];
1030    let n = fs.read_at(handle, &mut buf, 0).unwrap();
1031    println!("Read {} bytes: {:?}", n, String::from_utf8_lossy(&buf[..n]));
1032    fs.close(handle).unwrap();
1033
1034    // Locking
1035    let handle = fs
1036        .open(Path::new("/project/data.bin"), OpenFlags::READ)
1037        .unwrap();
1038    fs.lock(handle, LockType::Shared).unwrap();
1039    println!("Acquired shared lock");
1040    fs.unlock(handle).unwrap();
1041    println!("Released lock");
1042    fs.close(handle).unwrap();
1043
1044    // Extended attributes
1045    fs.set_xattr(Path::new("/project/README.md"), "user.author", b"Alice")
1046        .unwrap();
1047    let author = fs
1048        .get_xattr(Path::new("/project/README.md"), "user.author")
1049        .unwrap();
1050    println!("xattr user.author = {:?}", String::from_utf8_lossy(&author));
1051
1052    let attrs = fs.list_xattr(Path::new("/project/README.md")).unwrap();
1053    println!("All xattrs: {:?}", attrs);
1054
1055    // --- Using as trait objects ---
1056    println!("\n--- Using as Trait Objects ---\n");
1057
1058    fn use_as_fs(fs: &dyn Fs) {
1059        println!(
1060            "  Works as &dyn Fs: {} files in root",
1061            fs.read_dir(Path::new("/")).unwrap().count()
1062        );
1063    }
1064
1065    fn use_as_full(fs: &dyn FsFull) {
1066        println!(
1067            "  Works as &dyn FsFull: {} bytes total",
1068            fs.statfs().unwrap().total_bytes
1069        );
1070    }
1071
1072    fn use_as_fuse(fs: &dyn FsFuse) {
1073        println!(
1074            "  Works as &dyn FsFuse: root inode = {}",
1075            fs.path_to_inode(Path::new("/")).unwrap()
1076        );
1077    }
1078
1079    fn use_as_posix(fs: &dyn FsPosix) {
1080        let handle = fs
1081            .open(Path::new("/project/README.md"), OpenFlags::READ)
1082            .unwrap();
1083        let mut buf = [0u8; 5];
1084        let n = fs.read_at(handle, &mut buf, 0).unwrap();
1085        fs.close(handle).unwrap();
1086        println!("  Works as &dyn FsPosix: read {} bytes", n);
1087    }
1088
1089    use_as_fs(&fs);
1090    use_as_full(&fs);
1091    use_as_fuse(&fs);
1092    use_as_posix(&fs);
1093
1094    println!("\n=== Reference implementation demonstration complete! ===");
1095}