fuse_backend_rs/passthrough/
file_handle.rs1use std::cmp::Ordering;
7use std::ffi::CStr;
8use std::fmt::{Debug, Formatter};
9use std::fs::File;
10use std::io;
11use std::os::fd::AsFd;
12use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
13use std::sync::Arc;
14
15use vmm_sys_util::fam::{FamStruct, FamStructWrapper};
16
17use super::mount_fd::{MPRResult, MountFd, MountFds, MountId};
18use crate::api::EMPTY_CSTR;
19
20pub const MAX_HANDLE_SIZE: usize = 128;
25
26#[derive(Default)]
28#[repr(C)]
29pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
30impl<T> __IncompleteArrayField<T> {
31 #[inline]
32 pub unsafe fn as_ptr(&self) -> *const T {
33 self as *const __IncompleteArrayField<T> as *const T
34 }
35 #[inline]
36 pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
37 self as *mut __IncompleteArrayField<T> as *mut T
38 }
39 #[inline]
40 pub unsafe fn as_slice(&self, len: usize) -> &[T] {
41 ::std::slice::from_raw_parts(self.as_ptr(), len)
42 }
43 #[inline]
44 pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
45 ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
46 }
47}
48
49#[derive(Default)]
59#[repr(C)]
60pub struct CFileHandleInner {
61 pub handle_bytes: libc::c_uint,
62 pub handle_type: libc::c_int,
63 pub f_handle: __IncompleteArrayField<libc::c_char>,
64}
65
66vmm_sys_util::generate_fam_struct_impl!(
67 CFileHandleInner,
68 libc::c_char,
69 f_handle,
70 libc::c_uint,
71 handle_bytes,
72 MAX_HANDLE_SIZE
73);
74
75type CFileHandleWrapper = FamStructWrapper<CFileHandleInner>;
76
77#[derive(Clone)]
78struct CFileHandle {
79 pub wrapper: CFileHandleWrapper,
80}
81
82impl CFileHandle {
83 fn new(size: usize) -> Self {
84 CFileHandle {
85 wrapper: CFileHandleWrapper::new(size).unwrap(),
86 }
87 }
88}
89
90unsafe impl Send for CFileHandle {}
92unsafe impl Sync for CFileHandle {}
93
94impl Ord for CFileHandle {
95 fn cmp(&self, other: &Self) -> Ordering {
96 let s_fh = self.wrapper.as_fam_struct_ref();
97 let o_fh = other.wrapper.as_fam_struct_ref();
98 if s_fh.handle_bytes != o_fh.handle_bytes {
99 return s_fh.handle_bytes.cmp(&o_fh.handle_bytes);
100 }
101 let length = s_fh.handle_bytes as usize;
102 if s_fh.handle_type != o_fh.handle_type {
103 return s_fh.handle_type.cmp(&o_fh.handle_type);
104 }
105 unsafe {
106 if s_fh.f_handle.as_ptr() != o_fh.f_handle.as_ptr() {
107 return s_fh
108 .f_handle
109 .as_slice(length)
110 .cmp(o_fh.f_handle.as_slice(length));
111 }
112 }
113
114 Ordering::Equal
115 }
116}
117
118impl PartialOrd for CFileHandle {
119 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
120 Some(self.cmp(other))
121 }
122}
123
124impl PartialEq for CFileHandle {
125 fn eq(&self, other: &Self) -> bool {
126 self.cmp(other) == Ordering::Equal
127 }
128}
129
130impl Eq for CFileHandle {}
131
132impl Debug for CFileHandle {
133 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
134 let fh = self.wrapper.as_fam_struct_ref();
135 write!(
136 f,
137 "File handle: type {}, len {}",
138 fh.handle_type, fh.handle_bytes
139 )
140 }
141}
142
143#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
145pub struct FileHandle {
146 pub(crate) mnt_id: u64,
147 handle: CFileHandle,
148}
149
150impl Default for FileHandle {
151 fn default() -> Self {
152 Self {
153 mnt_id: 0,
154 handle: CFileHandle::new(0),
155 }
156 }
157}
158
159extern "C" {
160 fn name_to_handle_at(
161 dirfd: libc::c_int,
162 pathname: *const libc::c_char,
163 file_handle: *mut CFileHandleInner,
164 mount_id: *mut libc::c_int,
165 flags: libc::c_int,
166 ) -> libc::c_int;
167
168 fn open_by_handle_at(
171 mount_fd: libc::c_int,
172 file_handle: *const CFileHandleInner,
173 flags: libc::c_int,
174 ) -> libc::c_int;
175}
176
177impl FileHandle {
178 pub fn from_name_at(dir_fd: &impl AsRawFd, path: &CStr) -> io::Result<Option<Self>> {
188 let mut mount_id: libc::c_int = 0;
189 let mut c_fh = CFileHandle::new(0);
190
191 let ret = unsafe {
198 name_to_handle_at(
199 dir_fd.as_raw_fd(),
200 path.as_ptr(),
201 c_fh.wrapper.as_mut_fam_struct_ptr(),
202 &mut mount_id,
203 libc::AT_EMPTY_PATH,
204 )
205 };
206 if ret == -1 {
207 let err = io::Error::last_os_error();
208 match err.raw_os_error() {
209 Some(libc::EOVERFLOW) => {}
211 Some(libc::EOPNOTSUPP) => return Ok(None),
213 _ => return Err(err),
215 }
216 } else {
217 return Err(io::Error::from(io::ErrorKind::InvalidData));
218 }
219
220 let needed = c_fh.wrapper.as_fam_struct_ref().handle_bytes as usize;
221 let mut c_fh = CFileHandle::new(needed);
222
223 let ret = unsafe {
230 name_to_handle_at(
231 dir_fd.as_raw_fd(),
232 path.as_ptr(),
233 c_fh.wrapper.as_mut_fam_struct_ptr(),
234 &mut mount_id,
235 libc::AT_EMPTY_PATH,
236 )
237 };
238 if ret == -1 {
239 return Err(io::Error::last_os_error());
240 }
241
242 Ok(Some(FileHandle {
243 mnt_id: mount_id as MountId,
244 handle: c_fh,
245 }))
246 }
247
248 pub fn from_fd(fd: &impl AsRawFd) -> io::Result<Option<Self>> {
251 let empty_path = unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) };
253 Self::from_name_at(fd, empty_path)
254 }
255
256 pub fn into_openable<F>(
261 self,
262 mount_fds: &MountFds,
263 reopen_fd: F,
264 ) -> MPRResult<OpenableFileHandle>
265 where
266 F: FnOnce(RawFd, libc::c_int, u32) -> io::Result<File>,
267 {
268 let mount_fd = mount_fds.get(self.mnt_id, reopen_fd)?;
269 Ok(OpenableFileHandle {
270 handle: Arc::new(self),
271 mount_fd,
272 })
273 }
274}
275
276pub struct OpenableFileHandle {
277 handle: Arc<FileHandle>,
278 mount_fd: Arc<MountFd>,
279}
280
281impl Debug for OpenableFileHandle {
282 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
283 let fh = self.handle.handle.wrapper.as_fam_struct_ref();
284 write!(
285 f,
286 "Openable file handle: mountfd {}, type {}, len {}",
287 self.mount_fd.as_fd().as_raw_fd(),
288 fh.handle_type,
289 fh.handle_bytes
290 )
291 }
292}
293
294impl OpenableFileHandle {
295 pub fn open(&self, flags: libc::c_int) -> io::Result<File> {
297 let ret = unsafe {
298 open_by_handle_at(
299 self.mount_fd.as_fd().as_raw_fd(),
300 self.handle.handle.wrapper.as_fam_struct_ptr(),
301 flags,
302 )
303 };
304 if ret >= 0 {
305 let file = unsafe { File::from_raw_fd(ret) };
307 Ok(file)
308 } else {
309 let e = io::Error::last_os_error();
310 error!("open_by_handle_at failed error {:?}", e);
311 Err(e)
312 }
313 }
314
315 pub fn file_handle(&self) -> &Arc<FileHandle> {
316 &self.handle
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323 use std::ffi::CString;
324
325 fn generate_c_file_handle(
326 handle_bytes: usize,
327 handle_type: libc::c_int,
328 buf: Vec<libc::c_char>,
329 ) -> CFileHandle {
330 let mut wrapper = CFileHandle::new(handle_bytes);
331 let fh = wrapper.wrapper.as_mut_fam_struct();
332 fh.handle_type = handle_type;
333 unsafe {
334 fh.f_handle
335 .as_mut_slice(handle_bytes)
336 .copy_from_slice(buf.as_slice());
337 }
338
339 wrapper
340 }
341
342 #[test]
343 fn test_file_handle_derives() {
344 let h1 = generate_c_file_handle(128, 3, vec![0; 128]);
345 let mut fh1 = FileHandle {
346 mnt_id: 0,
347 handle: h1,
348 };
349
350 let h2 = generate_c_file_handle(127, 3, vec![0; 127]);
351 let fh2 = FileHandle {
352 mnt_id: 0,
353 handle: h2,
354 };
355
356 let h3 = generate_c_file_handle(128, 4, vec![0; 128]);
357 let fh3 = FileHandle {
358 mnt_id: 0,
359 handle: h3,
360 };
361
362 let h4 = generate_c_file_handle(128, 3, vec![1; 128]);
363 let fh4 = FileHandle {
364 mnt_id: 0,
365 handle: h4,
366 };
367
368 let h5 = generate_c_file_handle(128, 3, vec![0; 128]);
369 let mut fh5 = FileHandle {
370 mnt_id: 0,
371 handle: h5,
372 };
373
374 assert!(fh1 > fh2);
375 assert_ne!(fh1, fh2);
376 assert!(fh1 < fh3);
377 assert_ne!(fh1, fh3);
378 assert!(fh1 < fh4);
379 assert_ne!(fh1, fh4);
380 assert_eq!(fh1, fh5);
381
382 unsafe {
383 fh1.handle
384 .wrapper
385 .as_mut_fam_struct()
386 .f_handle
387 .as_mut_slice(128)[0] = 1;
388 }
389 assert!(fh1 > fh5);
390 unsafe {
391 fh5.handle
392 .wrapper
393 .as_mut_fam_struct()
394 .f_handle
395 .as_mut_slice(128)[0] = 1;
396 }
397 assert_eq!(fh1, fh5);
398 }
399
400 #[test]
401 fn test_c_file_handle_wrapper() {
402 let buf = (0..=127).collect::<Vec<libc::c_char>>();
403 let mut wrapper = generate_c_file_handle(MAX_HANDLE_SIZE, 3, buf.clone());
404 let fh = wrapper.wrapper.as_mut_fam_struct();
405
406 assert_eq!(fh.handle_bytes as usize, MAX_HANDLE_SIZE);
407 assert_eq!(fh.handle_type, 3);
408 assert_eq!(
409 unsafe { fh.f_handle.as_slice(MAX_HANDLE_SIZE) },
410 buf.as_slice(),
411 );
412 }
413
414 #[test]
415 fn test_file_handle_from_name_at() {
416 let topdir = env!("CARGO_MANIFEST_DIR");
417 let dir = File::open(topdir).unwrap();
418 let filename = CString::new("build.rs").unwrap();
419
420 let dir_handle = FileHandle::from_name_at(&dir, &CString::new("").unwrap())
421 .unwrap()
422 .unwrap();
423 let file_handle = FileHandle::from_name_at(&dir, &filename).unwrap().unwrap();
424
425 assert_eq!(dir_handle.mnt_id, file_handle.mnt_id);
426 assert_ne!(
427 dir_handle.handle.wrapper.as_fam_struct_ref().handle_bytes,
428 0
429 );
430 assert_ne!(
431 file_handle.handle.wrapper.as_fam_struct_ref().handle_bytes,
432 0
433 );
434 }
435}