hermit/fs/
uhyve.rs

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