1use alloc::borrow::ToOwned;
2use alloc::boxed::Box;
3use alloc::ffi::CString;
4use alloc::string::{String, ToString};
5use alloc::sync::Arc;
6use alloc::vec::Vec;
7use core::mem::MaybeUninit;
8
9use async_lock::Mutex;
10use async_trait::async_trait;
11use memory_addresses::VirtAddr;
12use uhyve_interface::parameters::{
13 CloseParams, LseekParams, OpenParams, ReadParams, UnlinkParams, WriteParams,
14};
15use uhyve_interface::{GuestPhysAddr, GuestVirtAddr, Hypercall};
16
17use crate::arch::mm::paging;
18use crate::env::is_uhyve;
19use crate::fs::{
20 self, AccessPermission, FileAttr, NodeKind, ObjectInterface, OpenOption, SeekWhence, VfsNode,
21};
22use crate::io;
23use crate::syscalls::interfaces::uhyve::uhyve_hypercall;
24
25#[derive(Debug)]
26struct UhyveFileHandleInner(i32);
27
28impl UhyveFileHandleInner {
29 pub fn new(fd: i32) -> Self {
30 Self(fd)
31 }
32
33 fn read(&mut self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
34 let mut read_params = ReadParams {
35 fd: self.0,
36 buf: GuestVirtAddr::new(buf.as_mut_ptr() as u64),
37 len: buf.len(),
38 ret: 0,
39 };
40 uhyve_hypercall(Hypercall::FileRead(&mut read_params));
41
42 if read_params.ret >= 0 {
43 Ok(read_params.ret.try_into().unwrap())
44 } else {
45 Err(io::Error::EIO)
46 }
47 }
48
49 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
50 let write_params = WriteParams {
51 fd: self.0,
52 buf: GuestVirtAddr::new(buf.as_ptr() as u64),
53 len: buf.len(),
54 };
55 uhyve_hypercall(Hypercall::FileWrite(&write_params));
56
57 Ok(write_params.len)
58 }
59
60 fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
61 let mut lseek_params = LseekParams {
62 fd: self.0,
63 offset,
64 whence: num::ToPrimitive::to_i32(&whence).unwrap(),
65 };
66 uhyve_hypercall(Hypercall::FileLseek(&mut lseek_params));
67
68 if lseek_params.offset >= 0 {
69 Ok(lseek_params.offset)
70 } else {
71 Err(io::Error::EINVAL)
72 }
73 }
74}
75
76impl Drop for UhyveFileHandleInner {
77 fn drop(&mut self) {
78 let mut close_params = CloseParams { fd: self.0, ret: 0 };
79 uhyve_hypercall(Hypercall::FileClose(&mut close_params));
80 if close_params.ret != 0 {
81 let ret = close_params.ret; panic!("Can't close fd {} - return value {ret}", self.0,);
83 }
84 }
85}
86
87#[derive(Debug)]
88struct UhyveFileHandle(pub Arc<Mutex<UhyveFileHandleInner>>);
89
90impl UhyveFileHandle {
91 pub fn new(fd: i32) -> Self {
92 Self(Arc::new(Mutex::new(UhyveFileHandleInner::new(fd))))
93 }
94}
95
96#[async_trait]
97impl ObjectInterface for UhyveFileHandle {
98 async fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
99 self.0.lock().await.read(buf)
100 }
101
102 async fn write(&self, buf: &[u8]) -> io::Result<usize> {
103 self.0.lock().await.write(buf)
104 }
105
106 async fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
107 self.0.lock().await.lseek(offset, whence)
108 }
109}
110
111impl Clone for UhyveFileHandle {
112 fn clone(&self) -> Self {
113 Self(self.0.clone())
114 }
115}
116
117#[derive(Debug)]
118pub(crate) struct UhyveDirectory {
119 prefix: Option<String>,
120}
121
122impl UhyveDirectory {
123 pub const fn new(prefix: Option<String>) -> Self {
124 UhyveDirectory { prefix }
125 }
126
127 fn traversal_path(&self, components: &[&str]) -> CString {
128 let prefix_deref = self.prefix.as_deref();
129 let components_with_prefix = prefix_deref.iter().chain(components.iter().rev());
130 let path: String = components_with_prefix
132 .flat_map(|component| ["/", component])
133 .skip(1)
134 .collect();
135 if path.is_empty() {
136 CString::new("/").unwrap()
137 } else {
138 CString::new(path).unwrap()
139 }
140 }
141}
142
143impl VfsNode for UhyveDirectory {
144 fn get_kind(&self) -> NodeKind {
146 NodeKind::Directory
147 }
148
149 fn traverse_stat(&self, _components: &mut Vec<&str>) -> io::Result<FileAttr> {
150 Err(io::Error::ENOSYS)
151 }
152
153 fn traverse_lstat(&self, _components: &mut Vec<&str>) -> io::Result<FileAttr> {
154 Err(io::Error::ENOSYS)
155 }
156
157 fn traverse_open(
158 &self,
159 components: &mut Vec<&str>,
160 opt: OpenOption,
161 mode: AccessPermission,
162 ) -> io::Result<Arc<dyn ObjectInterface>> {
163 let path = self.traversal_path(components);
164
165 let mut open_params = OpenParams {
166 name: GuestPhysAddr::new(
167 paging::virtual_to_physical(VirtAddr::from_ptr(path.as_ptr()))
168 .unwrap()
169 .as_u64(),
170 ),
171 flags: opt.bits(),
172 mode: mode.bits() as i32,
173 ret: -1,
174 };
175 uhyve_hypercall(Hypercall::FileOpen(&mut open_params));
176
177 if open_params.ret > 0 {
178 Ok(Arc::new(UhyveFileHandle::new(open_params.ret)))
179 } else {
180 Err(io::Error::EIO)
181 }
182 }
183
184 fn traverse_unlink(&self, components: &mut Vec<&str>) -> io::Result<()> {
185 let path = self.traversal_path(components);
186
187 let mut unlink_params = UnlinkParams {
188 name: GuestPhysAddr::new(
189 paging::virtual_to_physical(VirtAddr::from_ptr(path.as_ptr()))
190 .unwrap()
191 .as_u64(),
192 ),
193 ret: -1,
194 };
195 uhyve_hypercall(Hypercall::FileUnlink(&mut unlink_params));
196
197 if unlink_params.ret == 0 {
198 Ok(())
199 } else {
200 Err(io::Error::EIO)
201 }
202 }
203
204 fn traverse_rmdir(&self, _components: &mut Vec<&str>) -> io::Result<()> {
205 Err(io::Error::ENOSYS)
206 }
207
208 fn traverse_mkdir(
209 &self,
210 _components: &mut Vec<&str>,
211 _mode: AccessPermission,
212 ) -> io::Result<()> {
213 Err(io::Error::ENOSYS)
214 }
215}
216
217pub(crate) fn init() {
218 info!("Try to initialize uhyve filesystem");
219 if is_uhyve() {
220 let mount_point = hermit_var_or!("UHYVE_MOUNT", "/root").to_string();
221 info!("Mounting uhyve filesystem at {mount_point}");
222 fs::FILESYSTEM
223 .get()
224 .unwrap()
225 .mount(
226 &mount_point,
227 Box::new(UhyveDirectory::new(Some(mount_point.to_owned()))),
228 )
229 .expect("Mount failed. Duplicate mount_point?");
230 }
231}