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