1#[cfg(all(feature = "fuse", feature = "pci"))]
2pub(crate) mod fuse;
3mod mem;
4mod uhyve;
5
6use alloc::borrow::ToOwned;
7use alloc::boxed::Box;
8use alloc::string::String;
9use alloc::sync::Arc;
10use alloc::vec::Vec;
11use core::ops::BitAnd;
12
13use async_trait::async_trait;
14use embedded_io::{Read, Write};
15use hermit_sync::{InterruptSpinMutex, OnceCell};
16use mem::MemDirectory;
17use num_enum::{IntoPrimitive, TryFromPrimitive};
18
19use crate::errno::Errno;
20use crate::executor::block_on;
21use crate::fd::{AccessPermission, ObjectInterface, OpenOption, insert_object, remove_object};
22use crate::io;
23use crate::time::{SystemTime, timespec};
24
25static FILESYSTEM: OnceCell<Filesystem> = OnceCell::new();
26
27static WORKING_DIRECTORY: InterruptSpinMutex<Option<String>> = InterruptSpinMutex::new(None);
28
29static UMASK: InterruptSpinMutex<AccessPermission> =
30 InterruptSpinMutex::new(AccessPermission::from_bits_retain(0o777));
31
32#[derive(Debug, Clone)]
33pub struct DirectoryEntry {
34 pub name: String,
35}
36
37impl DirectoryEntry {
38 pub fn new(name: String) -> Self {
39 Self { name }
40 }
41}
42
43#[derive(Copy, Clone, Debug, PartialEq, Eq)]
45pub(crate) enum NodeKind {
46 File,
48 Directory,
50}
51
52pub(crate) trait VfsNode: core::fmt::Debug {
54 fn get_kind(&self) -> NodeKind;
56
57 fn get_file_attributes(&self) -> io::Result<FileAttr> {
59 Err(Errno::Nosys)
60 }
61
62 fn get_object(&self) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
64 Err(Errno::Nosys)
65 }
66
67 fn traverse_mkdir(
69 &self,
70 _components: &mut Vec<&str>,
71 _mode: AccessPermission,
72 ) -> io::Result<()> {
73 Err(Errno::Nosys)
74 }
75
76 fn traverse_rmdir(&self, _components: &mut Vec<&str>) -> io::Result<()> {
78 Err(Errno::Nosys)
79 }
80
81 fn traverse_unlink(&self, _components: &mut Vec<&str>) -> io::Result<()> {
83 Err(Errno::Nosys)
84 }
85
86 fn traverse_readdir(&self, _components: &mut Vec<&str>) -> io::Result<Vec<DirectoryEntry>> {
88 Err(Errno::Nosys)
89 }
90
91 fn traverse_lstat(&self, _components: &mut Vec<&str>) -> io::Result<FileAttr> {
93 Err(Errno::Nosys)
94 }
95
96 fn traverse_stat(&self, _components: &mut Vec<&str>) -> io::Result<FileAttr> {
98 Err(Errno::Nosys)
99 }
100
101 fn traverse_mount(
103 &self,
104 _components: &mut Vec<&str>,
105 _obj: Box<dyn VfsNode + core::marker::Send + core::marker::Sync>,
106 ) -> io::Result<()> {
107 Err(Errno::Nosys)
108 }
109
110 fn traverse_open(
112 &self,
113 _components: &mut Vec<&str>,
114 _option: OpenOption,
115 _mode: AccessPermission,
116 ) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
117 Err(Errno::Nosys)
118 }
119
120 fn traverse_create_file(
122 &self,
123 _components: &mut Vec<&str>,
124 _data: &'static [u8],
125 _mode: AccessPermission,
126 ) -> io::Result<()> {
127 Err(Errno::Nosys)
128 }
129}
130
131#[derive(Clone)]
132struct DirectoryReader(Vec<DirectoryEntry>);
133
134impl DirectoryReader {
135 pub fn new(data: Vec<DirectoryEntry>) -> Self {
136 Self(data)
137 }
138}
139
140#[async_trait]
141impl ObjectInterface for DirectoryReader {
142 async fn getdents(&self, _buf: &mut [core::mem::MaybeUninit<u8>]) -> io::Result<usize> {
143 let _ = &self.0; unimplemented!()
145 }
146}
147
148#[derive(Debug)]
149pub(crate) struct Filesystem {
150 root: MemDirectory,
151}
152
153impl Filesystem {
154 pub fn new() -> Self {
155 Self {
156 root: MemDirectory::new(AccessPermission::from_bits(0o777).unwrap()),
157 }
158 }
159
160 pub fn open(
162 &self,
163 path: &str,
164 opt: OpenOption,
165 mode: AccessPermission,
166 ) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
167 debug!("Open file {path} with {opt:?}");
168 let mut components: Vec<&str> = path.split('/').collect();
169
170 components.reverse();
171 components.pop();
172
173 self.root.traverse_open(&mut components, opt, mode)
174 }
175
176 pub fn unlink(&self, path: &str) -> io::Result<()> {
178 debug!("Unlinking file {path}");
179 let mut components: Vec<&str> = path.split('/').collect();
180
181 components.reverse();
182 components.pop();
183
184 self.root.traverse_unlink(&mut components)
185 }
186
187 pub fn rmdir(&self, path: &str) -> io::Result<()> {
189 debug!("Removing directory {path}");
190 let mut components: Vec<&str> = path.split('/').collect();
191
192 components.reverse();
193 components.pop();
194
195 self.root.traverse_rmdir(&mut components)
196 }
197
198 pub fn mkdir(&self, path: &str, mode: AccessPermission) -> io::Result<()> {
200 debug!("Create directory {path}");
201 let mut components: Vec<&str> = path.split('/').collect();
202
203 components.reverse();
204 components.pop();
205
206 self.root.traverse_mkdir(&mut components, mode)
207 }
208
209 pub fn opendir(&self, path: &str) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
210 debug!("Open directory {path}");
211 Ok(Arc::new(async_lock::RwLock::new(DirectoryReader::new(
212 self.readdir(path)?,
213 ))))
214 }
215
216 pub fn readdir(&self, path: &str) -> io::Result<Vec<DirectoryEntry>> {
218 if path.trim() == "/" {
219 let mut components: Vec<&str> = Vec::new();
220 self.root.traverse_readdir(&mut components)
221 } else {
222 let mut components: Vec<&str> = path.split('/').collect();
223
224 components.reverse();
225 components.pop();
226
227 self.root.traverse_readdir(&mut components)
228 }
229 }
230
231 pub fn stat(&self, path: &str) -> io::Result<FileAttr> {
233 debug!("Getting stats {path}");
234
235 let mut components: Vec<&str> = path.split('/').collect();
236 components.reverse();
237 components.pop();
238
239 self.root.traverse_stat(&mut components)
240 }
241
242 pub fn lstat(&self, path: &str) -> io::Result<FileAttr> {
244 debug!("Getting lstats {path}");
245
246 let mut components: Vec<&str> = path.split('/').collect();
247 components.reverse();
248 components.pop();
249
250 self.root.traverse_lstat(&mut components)
251 }
252
253 pub fn mount(
255 &self,
256 path: &str,
257 obj: Box<dyn VfsNode + core::marker::Send + core::marker::Sync>,
258 ) -> io::Result<()> {
259 debug!("Mounting {path}");
260
261 let mut components: Vec<&str> = path.split('/').collect();
262
263 components.reverse();
264 components.pop();
265
266 self.root.traverse_mount(&mut components, obj)
267 }
268
269 pub fn create_file(
271 &self,
272 path: &str,
273 data: &'static [u8],
274 mode: AccessPermission,
275 ) -> io::Result<()> {
276 debug!("Create read-only file {path}");
277
278 let mut components: Vec<&str> = path.split('/').collect();
279
280 components.reverse();
281 components.pop();
282
283 self.root.traverse_create_file(&mut components, data, mode)
284 }
285}
286
287#[repr(C)]
288#[derive(Debug, Default, Copy, Clone)]
289pub struct FileAttr {
290 pub st_dev: u64,
291 pub st_ino: u64,
292 pub st_nlink: u64,
293 pub st_mode: AccessPermission,
295 pub st_uid: u32,
297 pub st_gid: u32,
299 pub st_rdev: u64,
301 pub st_size: i64,
303 pub st_blksize: i64,
305 pub st_blocks: i64,
307 pub st_atim: timespec,
309 pub st_mtim: timespec,
311 pub st_ctim: timespec,
313}
314
315#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
316#[repr(u8)]
317pub enum FileType {
318 Unknown = 0, Fifo = 1, CharacterDevice = 2, Directory = 4, BlockDevice = 6, RegularFile = 8, SymbolicLink = 10, Socket = 12, Whiteout = 14, }
328
329#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
330#[repr(u8)]
331pub enum SeekWhence {
332 Set = 0,
333 Cur = 1,
334 End = 2,
335 Data = 3,
336 Hole = 4,
337}
338
339pub(crate) fn init() {
340 const VERSION: &str = env!("CARGO_PKG_VERSION");
341 const UTC_BUILT_TIME: &str = build_time::build_time_utc!();
342
343 FILESYSTEM.set(Filesystem::new()).unwrap();
344 FILESYSTEM
345 .get()
346 .unwrap()
347 .mkdir("/tmp", AccessPermission::from_bits(0o777).unwrap())
348 .expect("Unable to create /tmp");
349 FILESYSTEM
350 .get()
351 .unwrap()
352 .mkdir("/proc", AccessPermission::from_bits(0o777).unwrap())
353 .expect("Unable to create /proc");
354
355 if let Ok(mut file) = File::create("/proc/version") {
356 if write!(file, "HermitOS version {VERSION} # UTC {UTC_BUILT_TIME}").is_err() {
357 error!("Unable to write in /proc/version");
358 }
359 } else {
360 error!("Unable to create /proc/version");
361 }
362
363 let mut cwd = WORKING_DIRECTORY.lock();
364 *cwd = Some("/tmp".to_owned());
365 drop(cwd);
366
367 #[cfg(all(feature = "fuse", feature = "pci"))]
368 fuse::init();
369 if crate::env::is_uhyve() {
370 uhyve::init();
371 }
372}
373
374pub fn create_file(name: &str, data: &'static [u8], mode: AccessPermission) -> io::Result<()> {
375 with_relative_filename(name, |name| {
376 FILESYSTEM
377 .get()
378 .ok_or(Errno::Inval)?
379 .create_file(name, data, mode)
380 })
381}
382
383pub fn remove_dir(path: &str) -> io::Result<()> {
385 with_relative_filename(path, |path| {
386 FILESYSTEM.get().ok_or(Errno::Inval)?.rmdir(path)
387 })
388}
389
390pub fn unlink(path: &str) -> io::Result<()> {
391 with_relative_filename(path, |path| {
392 FILESYSTEM.get().ok_or(Errno::Inval)?.unlink(path)
393 })
394}
395
396pub fn create_dir(path: &str, mode: AccessPermission) -> io::Result<()> {
398 let mask = *UMASK.lock();
399
400 with_relative_filename(path, |path| {
401 FILESYSTEM
402 .get()
403 .ok_or(Errno::Inval)?
404 .mkdir(path, mode.bitand(mask))
405 })
406}
407
408fn create_dir_recursive(path: &str, mode: AccessPermission) -> io::Result<()> {
410 trace!("create_dir_recursive: {path}");
411 create_dir(path, mode).or_else(|errno| {
412 if errno != Errno::Badf {
413 return Err(errno);
414 }
415 let (parent_path, _file_name) = path.rsplit_once('/').unwrap();
416 create_dir_recursive(parent_path, mode)?;
417 create_dir(path, mode)
418 })
419}
420
421pub fn readdir(name: &str) -> io::Result<Vec<DirectoryEntry>> {
423 debug!("Read directory {name}");
424
425 with_relative_filename(name, |name| {
426 FILESYSTEM.get().ok_or(Errno::Inval)?.readdir(name)
427 })
428}
429
430pub fn read_stat(name: &str) -> io::Result<FileAttr> {
431 with_relative_filename(name, |name| {
432 FILESYSTEM.get().ok_or(Errno::Inval)?.stat(name)
433 })
434}
435
436pub fn read_lstat(name: &str) -> io::Result<FileAttr> {
437 with_relative_filename(name, |name| {
438 FILESYSTEM.get().ok_or(Errno::Inval)?.lstat(name)
439 })
440}
441
442fn with_relative_filename<F, T>(name: &str, callback: F) -> io::Result<T>
443where
444 F: FnOnce(&str) -> io::Result<T>,
445{
446 if name.starts_with("/") {
447 callback(name)
448 } else {
449 let cwd = WORKING_DIRECTORY.lock();
450 if let Some(cwd) = cwd.as_ref() {
451 let mut path = String::with_capacity(cwd.len() + name.len() + 1);
452 path.push_str(cwd);
453 path.push('/');
454 path.push_str(name);
455
456 callback(&path)
457 } else {
458 Err(Errno::Badf)
460 }
461 }
462}
463
464pub fn truncate(name: &str, size: usize) -> io::Result<()> {
465 with_relative_filename(name, |name| {
466 let fs = FILESYSTEM.get().ok_or(Errno::Inval)?;
467 if let Ok(file) = fs.open(name, OpenOption::O_TRUNC, AccessPermission::empty()) {
468 block_on(async { file.read().await.truncate(size).await }, None)
469 } else {
470 Err(Errno::Badf)
471 }
472 })
473}
474
475pub fn open(name: &str, flags: OpenOption, mode: AccessPermission) -> io::Result<FileDescriptor> {
476 let mask = *UMASK.lock();
480
481 with_relative_filename(name, |name| {
482 debug!("Open {name}, {flags:?}, {mode:?}");
483
484 let fs = FILESYSTEM.get().ok_or(Errno::Inval)?;
485 let file = fs.open(name, flags, mode.bitand(mask))?;
486 let fd = insert_object(file)?;
487 Ok(fd)
488 })
489}
490
491pub fn get_cwd() -> io::Result<String> {
492 let cwd = WORKING_DIRECTORY.lock();
493 if let Some(cwd) = cwd.as_ref() {
494 Ok(cwd.clone())
495 } else {
496 Err(Errno::Noent)
497 }
498}
499
500pub fn set_cwd(cwd: &str) -> io::Result<()> {
501 let mut working_dir = WORKING_DIRECTORY.lock();
504 if cwd.starts_with("/") {
505 *working_dir = Some(cwd.to_owned());
506 } else {
507 let Some(working_dir) = working_dir.as_mut() else {
508 return Err(Errno::Badf);
509 };
510 working_dir.push('/');
511 working_dir.push_str(cwd);
512 }
513
514 Ok(())
515}
516
517pub fn umask(new_mask: AccessPermission) -> AccessPermission {
518 let mut lock = UMASK.lock();
519 let old = *lock;
520 *lock = new_mask;
521 old
522}
523
524pub(crate) fn opendir(name: &str) -> io::Result<FileDescriptor> {
526 let obj = FILESYSTEM.get().ok_or(Errno::Inval)?.opendir(name)?;
527 insert_object(obj)
528}
529
530use crate::fd::{self, FileDescriptor};
531
532pub fn file_attributes(path: &str) -> io::Result<FileAttr> {
533 FILESYSTEM.get().ok_or(Errno::Inval)?.lstat(path)
534}
535
536#[allow(clippy::len_without_is_empty)]
537#[derive(Debug, Copy, Clone)]
538pub struct Metadata(FileAttr);
539
540impl Metadata {
541 pub fn len(&self) -> usize {
543 self.0.st_size.try_into().unwrap()
544 }
545
546 pub fn is_file(&self) -> bool {
548 self.0.st_mode.contains(AccessPermission::S_IFREG)
549 }
550
551 pub fn is_dir(&self) -> bool {
553 self.0.st_mode.contains(AccessPermission::S_IFDIR)
554 }
555
556 pub fn modified(&self) -> io::Result<SystemTime> {
558 Ok(SystemTime::from(self.0.st_mtim))
559 }
560
561 pub fn accessed(&self) -> io::Result<SystemTime> {
563 Ok(SystemTime::from(self.0.st_atim))
564 }
565}
566
567pub fn metadata(path: &str) -> io::Result<Metadata> {
569 Ok(Metadata(file_attributes(path)?))
570}
571
572#[derive(Debug)]
573pub struct File {
574 fd: FileDescriptor,
575 path: String,
576}
577
578impl File {
579 pub fn create(path: &str) -> io::Result<Self> {
585 let fd = open(
586 path,
587 OpenOption::O_CREAT | OpenOption::O_RDWR,
588 AccessPermission::from_bits(0o666).unwrap(),
589 )?;
590
591 Ok(File {
592 fd,
593 path: path.to_owned(),
594 })
595 }
596
597 pub fn open(path: &str) -> io::Result<Self> {
599 let fd = open(
600 path,
601 OpenOption::O_RDWR,
602 AccessPermission::from_bits(0o666).unwrap(),
603 )?;
604
605 Ok(File {
606 fd,
607 path: path.to_owned(),
608 })
609 }
610
611 pub fn metadata(&self) -> io::Result<Metadata> {
612 metadata(&self.path)
613 }
614}
615
616impl embedded_io::ErrorType for File {
617 type Error = crate::errno::Errno;
618}
619
620impl Read for File {
621 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
622 let buf = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len()) };
623 fd::read(self.fd, buf)
624 }
625}
626
627impl Write for File {
628 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
629 fd::write(self.fd, buf)
630 }
631
632 fn flush(&mut self) -> Result<(), Self::Error> {
633 Ok(())
634 }
635}
636
637impl Drop for File {
638 fn drop(&mut self) {
639 let _ = remove_object(self.fd);
640 }
641}