1use 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#[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 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 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(¤t_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(¤t_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 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}