fuse_backend_rs/api/filesystem/
mod.rs

1// Copyright (C) 2020-2022 Alibaba Cloud. All rights reserved.
2// Copyright 2019 The Chromium OS Authors. 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
6//! Structs and Traits for filesystem server to implement a concrete Fuse filesystem.
7//!
8//! The [FileSystem](trait.FileSystem.html) trait is the connection between the transport layer
9//! and the backend filesystem server. Other structs are used to pass information from the
10
11use std::convert::TryInto;
12use std::io;
13use std::time::Duration;
14
15use crate::abi::fuse_abi as fuse;
16use crate::file_traits::FileReadWriteVolatile;
17
18pub use fuse::FsOptions;
19pub use fuse::OpenOptions;
20pub use fuse::SetattrValid;
21pub use fuse::ROOT_ID;
22
23use crate::abi::fuse_abi::{ino64_t, stat64};
24
25#[cfg(feature = "async-io")]
26mod async_io;
27#[cfg(feature = "async-io")]
28pub use async_io::{AsyncFileSystem, AsyncZeroCopyReader, AsyncZeroCopyWriter};
29
30mod sync_io;
31pub use sync_io::FileSystem;
32
33#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))]
34mod overlay;
35#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))]
36pub use overlay::Layer;
37
38/// Information about a path in the filesystem.
39#[derive(Copy, Clone, Debug)]
40pub struct Entry {
41    /// An `Inode` that uniquely identifies this path. During `lookup`, setting this to `0` means a
42    /// negative entry. Returning `ENOENT` also means a negative entry but setting this to `0`
43    /// allows the kernel to cache the negative result for `entry_timeout`. The value should be
44    /// produced by converting a `FileSystem::Inode` into a `u64`.
45    pub inode: u64,
46
47    /// The generation number for this `Entry`. Typically used for network file systems. An `inode`
48    /// / `generation` pair must be unique over the lifetime of the file system (rather than just
49    /// the lifetime of the mount). In other words, if a `FileSystem` implementation re-uses an
50    /// `Inode` after it has been deleted then it must assign a new, previously unused generation
51    /// number to the `Inode` at the same time.
52    pub generation: u64,
53
54    /// Inode attributes. Even if `attr_timeout` is zero, `attr` must be correct. For example, for
55    /// `open()`, FUSE uses `attr.st_size` from `lookup()` to determine how many bytes to request.
56    /// If this value is not correct, incorrect data will be returned.
57    pub attr: stat64,
58
59    /// Flags for 'fuse::Attr.flags'
60    pub attr_flags: u32,
61
62    /// How long the values in `attr` should be considered valid. If the attributes of the `Entry`
63    /// are only modified by the FUSE client, then this should be set to a very large value.
64    pub attr_timeout: Duration,
65
66    /// How long the name associated with this `Entry` should be considered valid. If directory
67    /// entries are only changed or deleted by the FUSE client, then this should be set to a very
68    /// large value.
69    pub entry_timeout: Duration,
70}
71
72impl From<Entry> for fuse::EntryOut {
73    fn from(entry: Entry) -> fuse::EntryOut {
74        fuse::EntryOut {
75            nodeid: entry.inode,
76            generation: entry.generation,
77            entry_valid: entry.entry_timeout.as_secs(),
78            attr_valid: entry.attr_timeout.as_secs(),
79            entry_valid_nsec: entry.entry_timeout.subsec_nanos(),
80            attr_valid_nsec: entry.attr_timeout.subsec_nanos(),
81            attr: fuse::Attr::with_flags(entry.attr, entry.attr_flags),
82        }
83    }
84}
85
86impl Default for Entry {
87    fn default() -> Self {
88        Entry {
89            inode: 0,
90            generation: 0,
91            attr: unsafe { std::mem::zeroed() },
92            attr_flags: 0,
93            attr_timeout: Duration::default(),
94            entry_timeout: Duration::default(),
95        }
96    }
97}
98
99/// Represents information about an entry in a directory.
100#[derive(Copy, Clone)]
101pub struct DirEntry<'a> {
102    /// The inode number for this entry. This does NOT have to be the same as the `Inode` for this
103    /// directory entry. However, it must be the same as the `attr.st_ino` field of the `Entry` that
104    /// would be returned by a `lookup` request in the parent directory for `name`.
105    pub ino: ino64_t,
106
107    /// Any non-zero value that the kernel can use to identify the current point in the directory
108    /// entry stream. It does not need to be the actual physical position. A value of `0` is
109    /// reserved to mean "from the beginning" and should never be used. The `offset` value of the
110    /// first entry in a stream should point to the beginning of the second entry and so on.
111    pub offset: u64,
112
113    /// The type of this directory entry. Valid values are any of the `libc::DT_*` constants.
114    pub type_: u32,
115
116    /// The name of this directory entry. There are no requirements for the contents of this field
117    /// and any sequence of bytes is considered valid.
118    pub name: &'a [u8],
119}
120
121/// Represents a fuse lock
122#[derive(Copy, Clone)]
123pub struct FileLock {
124    /// Lock range start
125    pub start: u64,
126    /// Lock range end, exclusive?
127    pub end: u64,
128    /// Lock type
129    pub lock_type: u32,
130    /// thread id who owns the lock
131    pub pid: u32,
132}
133
134impl From<fuse::FileLock> for FileLock {
135    fn from(l: fuse::FileLock) -> FileLock {
136        FileLock {
137            start: l.start,
138            end: l.end,
139            lock_type: l.type_,
140            pid: l.pid,
141        }
142    }
143}
144
145impl From<FileLock> for fuse::FileLock {
146    fn from(l: FileLock) -> fuse::FileLock {
147        fuse::FileLock {
148            start: l.start,
149            end: l.end,
150            type_: l.lock_type,
151            pid: l.pid,
152        }
153    }
154}
155
156/// ioctl data and result
157#[derive(Default, Clone)]
158pub struct IoctlData<'a> {
159    /// ioctl result
160    pub result: i32,
161    /// ioctl data
162    pub data: Option<&'a [u8]>,
163}
164
165/// A reply to a `getxattr` method call.
166pub enum GetxattrReply {
167    /// The value of the requested extended attribute. This can be arbitrary textual or binary data
168    /// and does not need to be nul-terminated.
169    Value(Vec<u8>),
170
171    /// The size of the buffer needed to hold the value of the requested extended attribute. Should
172    /// be returned when the `size` parameter is 0. Callers should note that it is still possible
173    /// for the size of the value to change in between `getxattr` calls and should not assume that a
174    /// subsequent call to `getxattr` with the returned count will always succeed.
175    Count(u32),
176}
177
178/// A reply to a `listxattr` method call.
179pub enum ListxattrReply {
180    /// A buffer containing a nul-separated list of the names of all the extended attributes
181    /// associated with this `Inode`. This list of names may be unordered and includes a namespace
182    /// prefix. There may be several disjoint namespaces associated with a single `Inode`.
183    Names(Vec<u8>),
184
185    /// This size of the buffer needed to hold the full list of extended attribute names associated
186    /// with this `Inode`. Should be returned when the `size` parameter is 0. Callers should note
187    /// that it is still possible for the set of extended attributes to change between `listxattr`
188    /// calls and so should not assume that a subsequent call to `listxattr` with the returned count
189    /// will always succeed.
190    Count(u32),
191}
192
193/// A trait for directly copying data from the fuse transport into a `File` without first storing it
194/// in an intermediate buffer.
195pub trait ZeroCopyReader: io::Read {
196    /// Copies at most `count` bytes from `self` directly into `f` at offset `off` without storing
197    /// it in any intermediate buffers. If the return value is `Ok(n)` then it must be guaranteed
198    /// that `0 <= n <= count`. If `n` is `0`, then it can indicate one of 3 possibilities:
199    ///
200    /// 1. There is no more data left in `self`.
201    /// 2. There is no more space in `f`.
202    /// 3. `count` was `0`.
203    ///
204    /// # Errors
205    ///
206    /// If any error is returned then the implementation must guarantee that no bytes were copied
207    /// from `self`. If the underlying write to `f` returns `0` then the implementation must return
208    /// an error of the kind `io::ErrorKind::WriteZero`.
209    fn read_to(
210        &mut self,
211        f: &mut dyn FileReadWriteVolatile,
212        count: usize,
213        off: u64,
214    ) -> io::Result<usize>;
215
216    /// Copies exactly `count` bytes of data from `self` into `f` at offset `off`. `off + count`
217    /// must be less than `u64::MAX`.
218    ///
219    /// # Errors
220    ///
221    /// If an error is returned then the number of bytes copied from `self` is unspecified but it
222    /// will never be more than `count`.
223    fn read_exact_to(
224        &mut self,
225        f: &mut dyn FileReadWriteVolatile,
226        mut count: usize,
227        mut off: u64,
228    ) -> io::Result<()> {
229        let c = count
230            .try_into()
231            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
232        if off.checked_add(c).is_none() {
233            return Err(io::Error::new(
234                io::ErrorKind::InvalidInput,
235                "`off` + `count` must be less than u64::MAX",
236            ));
237        }
238
239        while count > 0 {
240            match self.read_to(f, count, off) {
241                Ok(0) => {
242                    return Err(io::Error::new(
243                        io::ErrorKind::WriteZero,
244                        "failed to fill whole buffer",
245                    ))
246                }
247                Ok(n) => {
248                    count -= n;
249                    off += n as u64;
250                }
251                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
252                Err(e) => return Err(e),
253            }
254        }
255
256        Ok(())
257    }
258
259    /// Copies all remaining bytes from `self` into `f` at offset `off`. Equivalent to repeatedly
260    /// calling `read_to` until it returns either `Ok(0)` or a non-`ErrorKind::Interrupted` error.
261    ///
262    /// # Errors
263    ///
264    /// If an error is returned then the number of bytes copied from `self` is unspecified.
265    fn copy_to_end(
266        &mut self,
267        f: &mut dyn FileReadWriteVolatile,
268        mut off: u64,
269    ) -> io::Result<usize> {
270        let mut out = 0;
271        loop {
272            match self.read_to(f, ::std::usize::MAX, off) {
273                Ok(0) => return Ok(out),
274                Ok(n) => {
275                    off = off.saturating_add(n as u64);
276                    out += n;
277                }
278                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
279                Err(e) => return Err(e),
280            }
281        }
282    }
283}
284
285/// A trait for directly copying data from a `File` into the fuse transport without first storing
286/// it in an intermediate buffer.
287pub trait ZeroCopyWriter: io::Write {
288    /// Copies at most `count` bytes from `f` at offset `off` directly into `self` without storing
289    /// it in any intermediate buffers. If the return value is `Ok(n)` then it must be guaranteed
290    /// that `0 <= n <= count`. If `n` is `0`, then it can indicate one of 3 possibilities:
291    ///
292    /// 1. There is no more data left in `f`.
293    /// 2. There is no more space in `self`.
294    /// 3. `count` was `0`.
295    ///
296    /// # Errors
297    ///
298    /// If any error is returned then the implementation must guarantee that no bytes were copied
299    /// from `f`. If the underlying read from `f` returns `0` then the implementation must return an
300    /// error of the kind `io::ErrorKind::UnexpectedEof`.
301    fn write_from(
302        &mut self,
303        f: &mut dyn FileReadWriteVolatile,
304        count: usize,
305        off: u64,
306    ) -> io::Result<usize>;
307
308    /// Copies exactly `count` bytes of data from `f` at offset `off` into `self`. `off + count`
309    /// must be less than `u64::MAX`.
310    ///
311    /// # Errors
312    ///
313    /// If an error is returned then the number of bytes copied from `self` is unspecified but it
314    /// well never be more than `count`.
315    fn write_all_from(
316        &mut self,
317        f: &mut dyn FileReadWriteVolatile,
318        mut count: usize,
319        mut off: u64,
320    ) -> io::Result<()> {
321        let c = count
322            .try_into()
323            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
324        if off.checked_add(c).is_none() {
325            return Err(io::Error::new(
326                io::ErrorKind::InvalidInput,
327                "`off` + `count` must be less than u64::MAX",
328            ));
329        }
330
331        while count > 0 {
332            match self.write_from(f, count, off) {
333                Ok(0) => {
334                    return Err(io::Error::new(
335                        io::ErrorKind::UnexpectedEof,
336                        "failed to write whole buffer",
337                    ))
338                }
339                Ok(n) => {
340                    // No need for checked math here because we verified that `off + count` will not
341                    // overflow and `n` must be <= `count`.
342                    count -= n;
343                    off += n as u64;
344                }
345                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
346                Err(e) => return Err(e),
347            }
348        }
349
350        Ok(())
351    }
352
353    /// Copies all remaining bytes from `f` at offset `off` into `self`. Equivalent to repeatedly
354    /// calling `write_from` until it returns either `Ok(0)` or a non-`ErrorKind::Interrupted`
355    /// error.
356    ///
357    /// # Errors
358    ///
359    /// If an error is returned then the number of bytes copied from `f` is unspecified.
360    fn copy_to_end(
361        &mut self,
362        f: &mut dyn FileReadWriteVolatile,
363        mut off: u64,
364    ) -> io::Result<usize> {
365        let mut out = 0;
366        loop {
367            match self.write_from(f, ::std::usize::MAX, off) {
368                Ok(0) => return Ok(out),
369                Ok(n) => {
370                    off = off.saturating_add(n as u64);
371                    out += n;
372                }
373                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
374                Err(e) => return Err(e),
375            }
376        }
377    }
378
379    /// Return number of bytes available for writing.
380    ///
381    /// Useful for buffer fixed writers, such as FuseDevWriter, VirtioFsWriter
382    fn available_bytes(&self) -> usize;
383}
384
385/// Additional context associated with requests.
386#[derive(Default, Clone, Copy, Debug)]
387pub struct Context {
388    /// The user ID of the calling process.
389    pub uid: libc::uid_t,
390
391    /// The group ID of the calling process.
392    pub gid: libc::gid_t,
393
394    /// The thread group ID of the calling process.
395    pub pid: libc::pid_t,
396}
397
398impl Context {
399    /// Create a new 'Context' object.
400    pub fn new() -> Self {
401        Self::default()
402    }
403}
404
405impl From<&fuse::InHeader> for Context {
406    fn from(source: &fuse::InHeader) -> Self {
407        Context {
408            uid: source.uid,
409            gid: source.gid,
410            pid: source.pid as i32,
411        }
412    }
413}
414
415#[cfg(test)]
416mod tests {
417    use super::*;
418    use crate::abi::fuse_abi::Attr;
419
420    #[test]
421    fn test_from_fuse_header() {
422        let fuse_header = &fuse::InHeader {
423            len: 16,
424            opcode: 0,
425            unique: 1,
426            nodeid: 2,
427            uid: 3,
428            gid: 4,
429            pid: 5,
430            padding: 0,
431        };
432        let header: Context = fuse_header.into();
433
434        assert_eq!(header.uid, 3);
435        assert_eq!(header.gid, 4);
436        assert_eq!(header.pid, 5);
437    }
438
439    #[test]
440    fn test_into_fuse_entry() {
441        let attr = Attr {
442            ..Default::default()
443        };
444        let entry = Entry {
445            inode: 1,
446            generation: 2,
447            attr: attr.into(),
448            ..Default::default()
449        };
450        let fuse_entry: fuse::EntryOut = entry.into();
451
452        assert_eq!(fuse_entry.nodeid, 1);
453        assert_eq!(fuse_entry.generation, 2);
454    }
455}