hermit/fs/
uhyve.rs

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; // circumvent packed field access
93			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		// Unlike src/fs/fuse.rs, we skip the first element here so as to not prepend / before /root
142		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	/// Returns the node type
156	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}