1#[cfg(all(feature = "fuse", feature = "pci"))]
2pub(crate) mod fuse;
3mod mem;
4mod uhyve;
5
6use alloc::boxed::Box;
7use alloc::string::{String, ToString};
8use alloc::sync::Arc;
9use alloc::vec::Vec;
10
11use async_trait::async_trait;
12use hermit_sync::OnceCell;
13use mem::MemDirectory;
14use num_enum::{IntoPrimitive, TryFromPrimitive};
15
16use crate::fd::{AccessPermission, ObjectInterface, OpenOption, insert_object, remove_object};
17use crate::io;
18use crate::io::Write;
19use crate::time::{SystemTime, timespec};
20
21static FILESYSTEM: OnceCell<Filesystem> = OnceCell::new();
22
23#[derive(Debug, Clone)]
24pub struct DirectoryEntry {
25 pub name: String,
26}
27
28impl DirectoryEntry {
29 pub fn new(name: String) -> Self {
30 Self { name }
31 }
32}
33
34#[derive(Copy, Clone, Debug, PartialEq, Eq)]
36pub(crate) enum NodeKind {
37 File,
39 Directory,
41}
42
43pub(crate) trait VfsNode: core::fmt::Debug {
45 fn get_kind(&self) -> NodeKind;
47
48 fn get_file_attributes(&self) -> io::Result<FileAttr> {
50 Err(io::Error::ENOSYS)
51 }
52
53 fn get_object(&self) -> io::Result<Arc<dyn ObjectInterface>> {
55 Err(io::Error::ENOSYS)
56 }
57
58 fn traverse_mkdir(
60 &self,
61 _components: &mut Vec<&str>,
62 _mode: AccessPermission,
63 ) -> io::Result<()> {
64 Err(io::Error::ENOSYS)
65 }
66
67 fn traverse_rmdir(&self, _components: &mut Vec<&str>) -> io::Result<()> {
69 Err(io::Error::ENOSYS)
70 }
71
72 fn traverse_unlink(&self, _components: &mut Vec<&str>) -> io::Result<()> {
74 Err(io::Error::ENOSYS)
75 }
76
77 fn traverse_readdir(&self, _components: &mut Vec<&str>) -> io::Result<Vec<DirectoryEntry>> {
79 Err(io::Error::ENOSYS)
80 }
81
82 fn traverse_lstat(&self, _components: &mut Vec<&str>) -> io::Result<FileAttr> {
84 Err(io::Error::ENOSYS)
85 }
86
87 fn traverse_stat(&self, _components: &mut Vec<&str>) -> io::Result<FileAttr> {
89 Err(io::Error::ENOSYS)
90 }
91
92 fn traverse_mount(
94 &self,
95 _components: &mut Vec<&str>,
96 _obj: Box<dyn VfsNode + core::marker::Send + core::marker::Sync>,
97 ) -> io::Result<()> {
98 Err(io::Error::ENOSYS)
99 }
100
101 fn traverse_open(
103 &self,
104 _components: &mut Vec<&str>,
105 _option: OpenOption,
106 _mode: AccessPermission,
107 ) -> io::Result<Arc<dyn ObjectInterface>> {
108 Err(io::Error::ENOSYS)
109 }
110
111 fn traverse_create_file(
113 &self,
114 _components: &mut Vec<&str>,
115 _data: &'static [u8],
116 _mode: AccessPermission,
117 ) -> io::Result<()> {
118 Err(io::Error::ENOSYS)
119 }
120}
121
122#[derive(Debug, Clone)]
123struct DirectoryReader(Vec<DirectoryEntry>);
124
125impl DirectoryReader {
126 pub fn new(data: Vec<DirectoryEntry>) -> Self {
127 Self(data)
128 }
129}
130
131#[async_trait]
132impl ObjectInterface for DirectoryReader {
133 async fn readdir(&self) -> io::Result<Vec<DirectoryEntry>> {
134 Ok(self.0.clone())
135 }
136}
137
138#[derive(Debug)]
139pub(crate) struct Filesystem {
140 root: MemDirectory,
141}
142
143impl Filesystem {
144 pub fn new() -> Self {
145 Self {
146 root: MemDirectory::new(AccessPermission::from_bits(0o777).unwrap()),
147 }
148 }
149
150 pub fn open(
152 &self,
153 path: &str,
154 opt: OpenOption,
155 mode: AccessPermission,
156 ) -> io::Result<Arc<dyn ObjectInterface>> {
157 debug!("Open file {path} with {opt:?}");
158 let mut components: Vec<&str> = path.split('/').collect();
159
160 components.reverse();
161 components.pop();
162
163 self.root.traverse_open(&mut components, opt, mode)
164 }
165
166 pub fn unlink(&self, path: &str) -> io::Result<()> {
168 debug!("Unlinking file {path}");
169 let mut components: Vec<&str> = path.split('/').collect();
170
171 components.reverse();
172 components.pop();
173
174 self.root.traverse_unlink(&mut components)
175 }
176
177 pub fn rmdir(&self, path: &str) -> io::Result<()> {
179 debug!("Removing directory {path}");
180 let mut components: Vec<&str> = path.split('/').collect();
181
182 components.reverse();
183 components.pop();
184
185 self.root.traverse_rmdir(&mut components)
186 }
187
188 pub fn mkdir(&self, path: &str, mode: AccessPermission) -> io::Result<()> {
190 debug!("Create directory {path}");
191 let mut components: Vec<&str> = path.split('/').collect();
192
193 components.reverse();
194 components.pop();
195
196 self.root.traverse_mkdir(&mut components, mode)
197 }
198
199 pub fn opendir(&self, path: &str) -> io::Result<Arc<dyn ObjectInterface>> {
200 debug!("Open directory {path}");
201 Ok(Arc::new(DirectoryReader::new(self.readdir(path)?)))
202 }
203
204 pub fn readdir(&self, path: &str) -> io::Result<Vec<DirectoryEntry>> {
206 if path.trim() == "/" {
207 let mut components: Vec<&str> = Vec::new();
208 self.root.traverse_readdir(&mut components)
209 } else {
210 let mut components: Vec<&str> = path.split('/').collect();
211
212 components.reverse();
213 components.pop();
214
215 self.root.traverse_readdir(&mut components)
216 }
217 }
218
219 pub fn stat(&self, path: &str) -> io::Result<FileAttr> {
221 debug!("Getting stats {path}");
222
223 let mut components: Vec<&str> = path.split('/').collect();
224 components.reverse();
225 components.pop();
226
227 self.root.traverse_stat(&mut components)
228 }
229
230 pub fn lstat(&self, path: &str) -> io::Result<FileAttr> {
232 debug!("Getting lstats {path}");
233
234 let mut components: Vec<&str> = path.split('/').collect();
235 components.reverse();
236 components.pop();
237
238 self.root.traverse_lstat(&mut components)
239 }
240
241 pub fn mount(
243 &self,
244 path: &str,
245 obj: Box<dyn VfsNode + core::marker::Send + core::marker::Sync>,
246 ) -> io::Result<()> {
247 debug!("Mounting {path}");
248
249 let mut components: Vec<&str> = path.split('/').collect();
250
251 components.reverse();
252 components.pop();
253
254 self.root.traverse_mount(&mut components, obj)
255 }
256
257 pub fn create_file(
259 &self,
260 path: &str,
261 data: &'static [u8],
262 mode: AccessPermission,
263 ) -> io::Result<()> {
264 debug!("Create read-only file {path}");
265
266 let mut components: Vec<&str> = path.split('/').collect();
267
268 components.reverse();
269 components.pop();
270
271 self.root.traverse_create_file(&mut components, data, mode)
272 }
273}
274
275#[repr(C)]
276#[derive(Debug, Default, Copy, Clone)]
277pub struct FileAttr {
278 pub st_dev: u64,
279 pub st_ino: u64,
280 pub st_nlink: u64,
281 pub st_mode: AccessPermission,
283 pub st_uid: u32,
285 pub st_gid: u32,
287 pub st_rdev: u64,
289 pub st_size: i64,
291 pub st_blksize: i64,
293 pub st_blocks: i64,
295 pub st_atim: timespec,
297 pub st_mtim: timespec,
299 pub st_ctim: timespec,
301}
302
303#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
304#[repr(u8)]
305pub enum FileType {
306 Unknown = 0, Fifo = 1, CharacterDevice = 2, Directory = 4, BlockDevice = 6, RegularFile = 8, SymbolicLink = 10, Socket = 12, Whiteout = 14, }
316
317#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
318#[repr(u8)]
319pub enum SeekWhence {
320 Set = 0,
321 Cur = 1,
322 End = 2,
323 Data = 3,
324 Hole = 4,
325}
326
327pub(crate) fn init() {
328 const VERSION: &str = env!("CARGO_PKG_VERSION");
329 const UTC_BUILT_TIME: &str = build_time::build_time_utc!();
330
331 FILESYSTEM.set(Filesystem::new()).unwrap();
332 FILESYSTEM
333 .get()
334 .unwrap()
335 .mkdir("/tmp", AccessPermission::from_bits(0o777).unwrap())
336 .expect("Unable to create /tmp");
337 FILESYSTEM
338 .get()
339 .unwrap()
340 .mkdir("/proc", AccessPermission::from_bits(0o777).unwrap())
341 .expect("Unable to create /proc");
342
343 if let Ok(mut file) = File::create("/proc/version") {
344 if write!(file, "HermitOS version {VERSION} # UTC {UTC_BUILT_TIME}").is_err() {
345 error!("Unable to write in /proc/version");
346 }
347 } else {
348 error!("Unable to create /proc/version");
349 }
350
351 #[cfg(all(feature = "fuse", feature = "pci"))]
352 fuse::init();
353 uhyve::init();
354}
355
356pub fn create_file(name: &str, data: &'static [u8], mode: AccessPermission) -> io::Result<()> {
357 FILESYSTEM
358 .get()
359 .ok_or(io::Error::EINVAL)?
360 .create_file(name, data, mode)
361}
362
363pub fn remove_dir(path: &str) -> io::Result<()> {
365 FILESYSTEM.get().ok_or(io::Error::EINVAL)?.rmdir(path)
366}
367
368pub fn unlink(path: &str) -> io::Result<()> {
369 FILESYSTEM.get().ok_or(io::Error::EINVAL)?.unlink(path)
370}
371
372pub fn create_dir(path: &str, mode: AccessPermission) -> io::Result<()> {
374 FILESYSTEM.get().ok_or(io::Error::EINVAL)?.mkdir(path, mode)
375}
376
377pub fn readdir(name: &str) -> io::Result<Vec<DirectoryEntry>> {
379 debug!("Read directory {name}");
380
381 FILESYSTEM.get().ok_or(io::Error::EINVAL)?.readdir(name)
382}
383
384pub fn read_stat(name: &str) -> io::Result<FileAttr> {
385 FILESYSTEM.get().ok_or(io::Error::EINVAL)?.stat(name)
386}
387
388pub fn read_lstat(name: &str) -> io::Result<FileAttr> {
389 FILESYSTEM.get().ok_or(io::Error::EINVAL)?.lstat(name)
390}
391
392pub fn open(name: &str, flags: OpenOption, mode: AccessPermission) -> io::Result<FileDescriptor> {
393 debug!("Open {name}, {flags:?}, {mode:?}");
398
399 let fs = FILESYSTEM.get().ok_or(io::Error::EINVAL)?;
400 if let Ok(file) = fs.open(name, flags, mode) {
401 let fd = insert_object(file)?;
402 Ok(fd)
403 } else {
404 Err(io::Error::EINVAL)
405 }
406}
407
408pub(crate) fn opendir(name: &str) -> io::Result<FileDescriptor> {
410 let obj = FILESYSTEM.get().ok_or(io::Error::EINVAL)?.opendir(name)?;
411 insert_object(obj)
412}
413
414use crate::fd::{self, FileDescriptor};
415
416pub fn file_attributes(path: &str) -> io::Result<FileAttr> {
417 FILESYSTEM.get().ok_or(io::Error::EINVAL)?.lstat(path)
418}
419
420#[allow(clippy::len_without_is_empty)]
421#[derive(Debug, Copy, Clone)]
422pub struct Metadata(FileAttr);
423
424impl Metadata {
425 pub fn len(&self) -> usize {
427 self.0.st_size.try_into().unwrap()
428 }
429
430 pub fn is_file(&self) -> bool {
432 self.0.st_mode.contains(AccessPermission::S_IFREG)
433 }
434
435 pub fn is_dir(&self) -> bool {
437 self.0.st_mode.contains(AccessPermission::S_IFDIR)
438 }
439
440 pub fn modified(&self) -> io::Result<SystemTime> {
442 Ok(SystemTime::from(self.0.st_mtim))
443 }
444
445 pub fn accessed(&self) -> io::Result<SystemTime> {
447 Ok(SystemTime::from(self.0.st_atim))
448 }
449}
450
451pub fn metadata(path: &str) -> io::Result<Metadata> {
453 Ok(Metadata(file_attributes(path)?))
454}
455
456#[derive(Debug)]
457pub struct File {
458 fd: FileDescriptor,
459 path: String,
460}
461
462impl File {
463 pub fn create(path: &str) -> io::Result<Self> {
469 let fd = open(
470 path,
471 OpenOption::O_CREAT | OpenOption::O_RDWR,
472 AccessPermission::from_bits(0o666).unwrap(),
473 )?;
474
475 Ok(File {
476 fd,
477 path: path.to_string(),
478 })
479 }
480
481 pub fn open(path: &str) -> io::Result<Self> {
483 let fd = open(
484 path,
485 OpenOption::O_RDWR,
486 AccessPermission::from_bits(0o666).unwrap(),
487 )?;
488
489 Ok(File {
490 fd,
491 path: path.to_string(),
492 })
493 }
494
495 pub fn metadata(&self) -> io::Result<Metadata> {
496 metadata(&self.path)
497 }
498}
499
500impl crate::io::Read for File {
501 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
502 let buf = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len()) };
503 fd::read(self.fd, buf)
504 }
505}
506
507impl crate::io::Write for File {
508 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
509 fd::write(self.fd, buf)
510 }
511}
512
513impl Drop for File {
514 fn drop(&mut self) {
515 let _ = remove_object(self.fd);
516 }
517}