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