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}