1use alloc::borrow::ToOwned;
2use alloc::boxed::Box;
3use alloc::ffi::CString;
4use alloc::string::String;
5use alloc::sync::Arc;
6
7use async_lock::Mutex;
8use embedded_io::{ErrorType, Read, Write};
9use memory_addresses::VirtAddr;
10use uhyve_interface::parameters::{
11 CloseParams, LseekParams, OpenParams, ReadParams, UnlinkParams, WriteParams,
12};
13use uhyve_interface::{GuestPhysAddr, GuestVirtAddr, Hypercall};
14
15use crate::arch::mm::paging;
16use crate::env::fdt;
17use crate::errno::Errno;
18use crate::fd::Fd;
19use crate::fs::{
20 self, AccessPermission, FileAttr, NodeKind, ObjectInterface, OpenOption, SeekWhence, VfsNode,
21 create_dir_recursive,
22};
23use crate::io;
24use crate::uhyve::uhyve_hypercall;
25
26#[derive(Debug)]
27struct UhyveFileHandleInner(i32);
28
29impl UhyveFileHandleInner {
30 pub fn new(fd: i32) -> Self {
31 Self(fd)
32 }
33
34 fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
35 let mut lseek_params = LseekParams {
36 fd: self.0,
37 offset,
38 whence: u8::from(whence).into(),
39 };
40 uhyve_hypercall(Hypercall::FileLseek(&mut lseek_params));
41
42 if lseek_params.offset < 0 {
43 return Err(Errno::Inval);
44 }
45
46 Ok(lseek_params.offset)
47 }
48}
49
50impl ErrorType for UhyveFileHandleInner {
51 type Error = Errno;
52}
53
54impl Read for UhyveFileHandleInner {
55 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
56 let mut read_params = ReadParams {
57 fd: self.0,
58 buf: GuestVirtAddr::from_ptr(buf.as_mut_ptr()),
59 len: buf.len(),
60 ret: 0,
61 };
62 uhyve_hypercall(Hypercall::FileRead(&mut read_params));
63
64 if read_params.ret < 0 {
65 return Err(Errno::Io);
66 }
67
68 Ok(read_params.ret.try_into().unwrap())
69 }
70}
71
72impl Write for UhyveFileHandleInner {
73 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
74 let write_params = WriteParams {
75 fd: self.0,
76 buf: GuestVirtAddr::from_ptr(buf.as_ptr()),
77 len: buf.len(),
78 };
79 uhyve_hypercall(Hypercall::FileWrite(&write_params));
80
81 Ok(write_params.len)
82 }
83
84 fn flush(&mut self) -> Result<(), Self::Error> {
85 Ok(())
86 }
87}
88
89impl Drop for UhyveFileHandleInner {
90 fn drop(&mut self) {
91 let mut close_params = CloseParams { fd: self.0, ret: 0 };
92 uhyve_hypercall(Hypercall::FileClose(&mut close_params));
93 if close_params.ret != 0 {
94 let ret = close_params.ret; panic!("Can't close fd {} - return value {ret}", self.0);
96 }
97 }
98}
99
100pub struct UhyveFileHandle(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
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: String,
135}
136
137impl UhyveDirectory {
138 pub const fn new(prefix: String) -> Self {
139 UhyveDirectory { prefix }
140 }
141
142 fn traversal_path(&self, path: &str) -> CString {
143 let prefix = self.prefix.as_str();
144 let prefix = prefix.strip_suffix("/").unwrap_or(prefix);
145 let path = [prefix, path].join("/");
146 CString::new(path).unwrap()
147 }
148}
149
150impl VfsNode for UhyveDirectory {
151 fn get_kind(&self) -> NodeKind {
153 NodeKind::Directory
154 }
155
156 fn traverse_stat(&self, _path: &str) -> io::Result<FileAttr> {
157 Err(Errno::Nosys)
158 }
159
160 fn traverse_lstat(&self, _path: &str) -> io::Result<FileAttr> {
161 Err(Errno::Nosys)
162 }
163
164 fn traverse_open(
165 &self,
166 path: &str,
167 opt: OpenOption,
168 mode: AccessPermission,
169 ) -> io::Result<Arc<async_lock::RwLock<Fd>>> {
170 let path = self.traversal_path(path);
171
172 let mut open_params = OpenParams {
173 name: GuestPhysAddr::new(
174 paging::virtual_to_physical(VirtAddr::from_ptr(path.as_ptr()))
175 .unwrap()
176 .as_u64(),
177 ),
178 flags: opt.bits(),
179 mode: mode.bits() as i32,
180 ret: -1,
181 };
182 uhyve_hypercall(Hypercall::FileOpen(&mut open_params));
183
184 if open_params.ret <= 0 {
185 return Err(Errno::Io);
186 }
187
188 Ok(Arc::new(async_lock::RwLock::new(
189 UhyveFileHandle::new(open_params.ret).into(),
190 )))
191 }
192
193 fn traverse_unlink(&self, path: &str) -> io::Result<()> {
194 let path = self.traversal_path(path);
195
196 let mut unlink_params = UnlinkParams {
197 name: GuestPhysAddr::new(
198 paging::virtual_to_physical(VirtAddr::from_ptr(path.as_ptr()))
199 .unwrap()
200 .as_u64(),
201 ),
202 ret: -1,
203 };
204 uhyve_hypercall(Hypercall::FileUnlink(&mut unlink_params));
205
206 if unlink_params.ret != 0 {
207 return Err(Errno::Io);
208 }
209
210 Ok(())
211 }
212
213 fn traverse_rmdir(&self, _path: &str) -> io::Result<()> {
214 Err(Errno::Nosys)
215 }
216
217 fn traverse_mkdir(&self, _path: &str, _mode: AccessPermission) -> io::Result<()> {
218 Err(Errno::Nosys)
219 }
220}
221
222pub(crate) fn init() {
223 info!("Try to initialize uhyve filesystem");
224
225 let mount_str = fdt().and_then(|fdt| {
226 fdt.find_node("/uhyve,mounts")
227 .and_then(|node| node.property("mounts"))
228 .and_then(|property| property.as_str())
229 });
230
231 let Some(mount_str) = mount_str else {
232 let mount_point = hermit_var_or!("UHYVE_MOUNT", "/root").to_owned();
234 info!("Mounting uhyve filesystem at {mount_point}");
235 fs::FILESYSTEM
236 .get()
237 .unwrap()
238 .mount(
239 &mount_point,
240 Box::new(UhyveDirectory::new(mount_point.clone())),
241 )
242 .expect("Mount failed. Duplicate mount_point?");
243 return;
244 };
245
246 assert_ne!(mount_str.len(), 0, "Invalid /uhyve,mounts node in FDT");
247 for mount_point in mount_str.split('\0') {
248 info!("Mounting uhyve filesystem at {mount_point}");
249
250 let obj = Box::new(UhyveDirectory::new(mount_point.to_owned()));
251 let Err(errno) = fs::FILESYSTEM.get().unwrap().mount(mount_point, obj) else {
252 continue;
253 };
254
255 assert_eq!(errno, Errno::Badf);
256 debug!("Mounting of {mount_point} failed with {errno:?}. Creating missing parent folders");
257 let (parent_path, _file_name) = mount_point.rsplit_once('/').unwrap();
258 create_dir_recursive(parent_path, AccessPermission::S_IRWXU).unwrap();
259
260 let obj = Box::new(UhyveDirectory::new(mount_point.to_owned()));
261 fs::FILESYSTEM
262 .get()
263 .unwrap()
264 .mount(mount_point, obj)
265 .unwrap();
266 }
267}