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