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