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