fuse_backend_rs/passthrough/
file_handle.rs

1// Copyright (C) 2023 Alibaba Cloud. All rights reserved.
2// Copyright 2021 Red Hat, Inc. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE-BSD-3-Clause file.
5
6use 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
20/// An arbitrary maximum size for CFileHandle::f_handle.
21///
22/// According to Linux ABI, struct file_handle has a flexible array member 'f_handle', with
23/// maximum value of 128 bytes defined in file include/linux/exportfs.h
24pub const MAX_HANDLE_SIZE: usize = 128;
25
26/// Dynamically allocated array.
27#[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/// The structure to transfer file_handle struct between user space and kernel space.
50/// ```c
51/// struct file_handle {
52///     __u32 handle_bytes;
53///     int handle_type;
54///     /* file identifier */
55///     unsigned char f_handle[];
56/// }
57/// ```
58#[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
90// Safe because f_handle is readonly once FileHandle is initialized.
91unsafe 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/// Struct to maintain information for a file handle.
144#[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    // Technically `file_handle` should be a `mut` pointer, but `open_by_handle_at()` is specified
169    // not to change it, so we can declare it `const`.
170    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    /// Create a file handle for the given file.
179    ///
180    /// Return `Ok(None)` if no file handle can be generated for this file: Either because the
181    /// filesystem does not support it, or because it would require a larger file handle than we
182    /// can store.  These are not intermittent failures, i.e. if this function returns `Ok(None)`
183    /// for a specific file, it will always return `Ok(None)` for it.  Conversely, if this function
184    /// returns `Ok(Some)` at some point, it will never return `Ok(None)` later.
185    ///
186    /// Return an `io::Error` for all other errors.
187    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        // Per name_to_handle_at(2), the caller can discover the required size
192        // for the file_handle structure by making a call in which
193        // handle->handle_bytes is zero.  In this case, the call fails with the
194        // error EOVERFLOW and handle->handle_bytes is set to indicate the
195        // required size; the caller can then use this information to allocate a
196        // structure of the correct size.
197        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                // Got the needed buffer size.
210                Some(libc::EOVERFLOW) => {}
211                // Filesystem does not support file handles
212                Some(libc::EOPNOTSUPP) => return Ok(None),
213                // Other error
214                _ => 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        // name_to_handle_at() does not trigger a mount when the final component of the pathname is
224        // an automount point. When a filesystem supports both file handles and automount points,
225        // a name_to_handle_at() call on an automount point will return with error EOVERFLOW
226        // without having increased handle_bytes.  This can happen since Linux 4.13 with NFS
227        // when accessing a directory which is on a separate filesystem on the server. In this case,
228        // the automount can be triggered by adding a "/" to the end of the pathname.
229        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    /// Create a file handle from a `fd`.
249    /// This is a wrapper around `from_name_at()` and so has the same interface.
250    pub fn from_fd(fd: &impl AsRawFd) -> io::Result<Option<Self>> {
251        // Safe because this is a constant value and a valid C string.
252        let empty_path = unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) };
253        Self::from_name_at(fd, empty_path)
254    }
255
256    /// Return an openable copy of the file handle by ensuring that `mount_fd` contains a valid fd
257    /// for the mount the file handle is for.
258    ///
259    /// `reopen_fd` will be invoked to duplicate an `O_PATH` fd with custom `libc::open()` flags.
260    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    /// Open a file from an openable file handle.
296    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            // Safe because `open_by_handle_at()` guarantees this is a valid fd
306            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}