Skip to main content

shelljougahara/
fs.rs

1//! The virtual file system used by the shell.
2
3use std::{
4    path::PathBuf,
5    sync::{Arc, Mutex},
6};
7
8use inode::{Inode, content::Directory};
9use users::{GroupStore, UserStore};
10
11use crate::{
12    FilePermissions, Group, GroupId, InodeContent, InodeMetadata, User, UserId,
13    errors::{FileSystemError, ShellError},
14    fs::inode::content::File,
15};
16
17pub mod inode;
18pub mod permissions;
19pub mod resolver;
20pub mod users;
21
22/// The file system
23#[derive(Debug, Clone)]
24pub struct FileSystem {
25    pub root: Arc<Mutex<Inode>>,
26    users: UserStore,
27    groups: GroupStore,
28}
29
30impl FileSystem {
31    #[must_use]
32    pub fn new() -> Self {
33        let mut groups = GroupStore::new();
34        let mut users = UserStore::new();
35
36        // Create the root user and group
37        let root_group_id = groups.add_group("root".to_string());
38        let root_user_id = users.add_user("root".to_string());
39        let user = users.user_mut(root_user_id).expect("User not found");
40        user.add_group(root_group_id);
41
42        // Create the root directory
43        let root = Arc::new(Mutex::new(
44            Inode::new(
45                String::new(),
46                InodeContent::Directory(Directory::new()),
47                InodeMetadata::new(
48                    FilePermissions::from_mode(0o755),
49                    root_user_id,
50                    root_group_id,
51                ),
52                None,
53            )
54            .expect("Failed to create root inode"),
55        ));
56
57        Self {
58            root,
59            users,
60            groups,
61        }
62    }
63
64    pub fn get_user(&self, user_id: UserId) -> Option<&User> {
65        self.users.user(user_id)
66    }
67
68    pub fn get_group(&self, group_id: GroupId) -> Option<&Group> {
69        self.groups.group(group_id)
70    }
71
72    pub fn create_file(&mut self, path: &str) -> Result<Arc<Mutex<Inode>>, ShellError> {
73        let (components, last_component) = path.split_at(path.rfind('/').unwrap_or(0) + 1);
74        let current_inode = self.path_from_components(components)?;
75        current_inode
76            .lock()
77            .expect("Failed to lock inode")
78            .add_child(
79                last_component,
80                InodeContent::File(File::new()),
81                Arc::downgrade(&current_inode),
82            )
83    }
84
85    pub fn create_directory(&mut self, path: &str) -> Result<Arc<Mutex<Inode>>, ShellError> {
86        let (components, last_component) = path.split_at(path.rfind('/').unwrap_or(0) + 1);
87        let current_inode = self.path_from_components(components)?;
88        current_inode
89            .lock()
90            .expect("Failed to lock inode")
91            .add_child(
92                last_component,
93                InodeContent::Directory(Directory::new()),
94                Arc::downgrade(&current_inode),
95            )
96    }
97
98    fn path_from_components(&self, path: &str) -> Result<Arc<Mutex<Inode>>, ShellError> {
99        let mut current_inode = Some(self.root.clone());
100        // We separate the path into components (which should be directories) and the last component (which is the path)
101        let (components, _) = path.split_at(path.rfind('/').unwrap_or(0) + 1);
102        let mut components_iter = components.split('/').peekable();
103        while let Some(component) = components_iter.next() {
104            let inode = current_inode.take().expect("Inode is empty");
105            let inode_copy = inode.clone();
106            let inner_inode = inode.lock().expect("Failed to lock inner inode");
107            match component {
108                "" | "." => {
109                    current_inode = Some(inode_copy);
110                }
111                ".." => match inner_inode.parent() {
112                    Some(parent) => {
113                        let parent_arc = parent.upgrade().expect("Failed to upgrade parent");
114                        current_inode = Some(parent_arc);
115                    }
116                    None => {
117                        let path = inner_inode.path()?;
118                        if path == PathBuf::from("/") {
119                            current_inode = Some(inode_copy);
120                        } else {
121                            return Err(ShellError::FileSystem(
122                                FileSystemError::DirectoryNotFound(format!(
123                                    "Failed to get parent of {}",
124                                    path.display()
125                                )),
126                            ));
127                        }
128                    }
129                },
130                path => match inner_inode.find_child(path) {
131                    Some(child_inode) => {
132                        let child_content = child_inode
133                            .lock()
134                            .expect("Failed to lock child inode")
135                            .content
136                            .clone();
137                        match child_content {
138                            InodeContent::Directory(_) => {
139                                current_inode = Some(child_inode);
140                            }
141                            _ => {
142                                if components_iter.peek().is_some() {
143                                    return Err(ShellError::FileSystem(
144                                        FileSystemError::NotADirectory(path.to_string()),
145                                    ));
146                                } else {
147                                    return Err(ShellError::FileSystem(
148                                        FileSystemError::EntryAlreadyExists(path.to_string()),
149                                    ));
150                                }
151                            }
152                        }
153                    }
154                    None => {
155                        return Err(ShellError::FileSystem(FileSystemError::EntryNotFound(
156                            path.to_string(),
157                        )));
158                    }
159                },
160            }
161        }
162
163        Ok(current_inode.expect("Inode is empty"))
164    }
165
166    pub fn add_user(&mut self, username: &str) -> Result<UserId, ShellError> {
167        if self.find_absolute_inode("/home").is_none() {
168            self.create_directory("/home")?;
169        }
170
171        if self.users.find_by_username(username).is_some() {
172            return Err(ShellError::FileSystem(FileSystemError::EntryAlreadyExists(
173                username.to_string(),
174            )));
175        }
176
177        let user_id = self.users.add_user(username.to_string());
178        let user_group_id = self.groups.add_group(username.to_string());
179        let user = self.users.user_mut(user_id).expect("User not found");
180        user.add_group(user_group_id);
181        let user_home_dir = self.create_directory(&format!("/home/{username}"))?;
182        let mut user_home_dir_inode = user_home_dir
183            .lock()
184            .expect("Failed to lock user home directory");
185        user_home_dir_inode.metadata.group = user_group_id;
186        user_home_dir_inode.metadata.owner = user_id;
187        Ok(user_id)
188    }
189
190    #[must_use]
191    pub fn find_absolute_inode(&self, path: &str) -> Option<Arc<Mutex<Inode>>> {
192        find_relative_inode(self.root.clone(), path)
193    }
194
195    pub fn remove_inode(&mut self, path: &str) -> Result<(), ShellError> {
196        let (parent, child) = path.split_at(path.rfind('/').unwrap_or(0) + 1);
197
198        if let Some(parent_inode) = self.find_absolute_inode(parent) {
199            let mut parent_inode = parent_inode.lock().expect("Failed to lock parent inode");
200            if let InodeContent::Directory(parent_dir) = &mut parent_inode.content {
201                println!("Removing child: {child:?} from {parent:?}");
202                parent_dir.remove_child(child);
203            }
204        } else {
205            return Err(ShellError::FileSystem(FileSystemError::DirectoryNotFound(
206                "Parent directory does not exist".to_string(),
207            )));
208        }
209
210        Ok(())
211    }
212}
213
214impl Default for FileSystem {
215    fn default() -> Self {
216        Self::new()
217    }
218}
219
220pub fn find_relative_inode(
221    base: Arc<Mutex<Inode>>,
222    relative_path: &str,
223) -> Option<Arc<Mutex<Inode>>> {
224    let mut current_inode = base;
225    for component in relative_path.split('/') {
226        if component.is_empty() {
227            continue;
228        }
229        let child_inode = current_inode
230            .lock()
231            .expect("Failed to lock inode")
232            .find_child(component);
233        match child_inode {
234            Some(inode) => current_inode = inode,
235            None => return None,
236        }
237    }
238    Some(current_inode)
239}