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