Skip to main content

fuser_ng/
types.rs

1// Public types exported by FuserNG.
2//
3// Copyright (c) 2016-2022 by William R. Fraser
4//
5
6use crate::KernelConfig;
7use std::ffi::{OsStr, OsString};
8use std::ops::Deref;
9use std::path::{Path, PathBuf};
10use std::sync::Arc;
11use std::time::{Duration, SystemTime};
12
13pub type Inode = u64;
14
15/// Info about a request.
16#[derive(Clone, Copy, Debug)]
17pub struct RequestInfo {
18    /// The unique ID assigned to this request by FUSE.
19    pub unique: u64,
20    /// The user ID of the process making the request.
21    pub uid: u32,
22    /// The group ID of the process making the request.
23    pub gid: u32,
24    /// The process ID of the process making the request.
25    pub pid: u32,
26}
27
28/// A directory entry.
29#[derive(Clone, Debug)]
30pub struct DirectoryEntry {
31    /// Name of the entry
32    pub name: OsString,
33    /// Kind of file (directory, file, pipe, etc.)
34    pub kind: crate::FileType,
35}
36
37/// Filesystem statistics.
38#[derive(Clone, Copy, Debug)]
39pub struct Statfs {
40    /// Total data blocks in the filesystem
41    pub blocks: u64,
42    /// Free blocks in filesystem
43    pub bfree: u64,
44    /// Free blocks available to unprivileged user
45    pub bavail: u64,
46    /// Total file nodes in filesystem
47    pub files: u64,
48    /// Free file nodes in filesystem
49    pub ffree: u64,
50    /// Optimal transfer block size
51    pub bsize: u32,
52    /// Maximum length of filenames
53    pub namelen: u32,
54    /// Fragment size
55    pub frsize: u32,
56}
57
58/// File attributes.
59#[derive(Clone, Copy, Debug)]
60pub struct FileAttr {
61    /// Size in bytes
62    pub size: u64,
63    /// Size in blocks
64    pub blocks: u64,
65    /// Time of last access
66    pub atime: SystemTime,
67    /// Time of last modification
68    pub mtime: SystemTime,
69    /// Time of last metadata change
70    pub ctime: SystemTime,
71    /// Time of creation (macOS only)
72    pub crtime: SystemTime,
73    /// Kind of file (directory, file, pipe, etc.)
74    pub kind: crate::FileType,
75    /// Permissions
76    pub perm: u16,
77    /// Number of hard links
78    pub nlink: u32,
79    /// User ID
80    pub uid: u32,
81    /// Group ID
82    pub gid: u32,
83    /// Device ID (if special file)
84    pub rdev: u32,
85    /// block size
86    pub blksize: u32,
87    /// Flags (macOS only; see chflags(2))
88    pub flags: u32,
89}
90
91/// The return value for `create`: contains info on the newly-created file, as well as a handle to
92/// the opened file.
93#[derive(Clone, Debug)]
94pub struct CreatedEntry {
95    /// Entry cache time-to-live.
96    pub ttl: Duration,
97    /// Attributes returned for the created file.
98    pub attr: FileAttr,
99    /// File handle returned for the opened file.
100    pub fh: u64,
101    /// Open response flags returned to fuser.
102    pub flags: u32,
103}
104
105/// Represents the return value from the `listxattr` and `getxattr` calls, which can be either a
106/// size or contain data, depending on how they are called.
107#[derive(Clone, Debug)]
108pub enum Xattr {
109    /// Size needed to read the attribute value or name list.
110    Size(u32),
111    /// Attribute data or null-terminated attribute name list.
112    Data(Vec<u8>),
113}
114
115#[cfg(target_os = "macos")]
116#[derive(Clone, Debug)]
117pub struct XTimes {
118    pub bkuptime: SystemTime,
119    pub crtime: SystemTime,
120}
121
122pub type ResultEmpty = std::io::Result<()>;
123pub type ResultEntry = std::io::Result<(Duration, FileAttr)>;
124pub type ResultOpen = std::io::Result<(u64, u32)>;
125pub type ResultReaddir = std::io::Result<Vec<DirectoryEntry>>;
126pub type ResultData = std::io::Result<Vec<u8>>;
127pub type ResultSlice<'a> = std::io::Result<&'a [u8]>;
128pub type ResultWrite = std::io::Result<u32>;
129pub type ResultStatfs = std::io::Result<Statfs>;
130pub type ResultCreate = std::io::Result<CreatedEntry>;
131pub type ResultXattr = std::io::Result<Xattr>;
132
133#[cfg(target_os = "macos")]
134pub type ResultXTimes = std::io::Result<XTimes>;
135
136#[deprecated(since = "0.3.0", note = "use ResultEntry instead")]
137pub type ResultGetattr = ResultEntry;
138
139/// Dummy struct returned by the callback in the `read()` method. Cannot be constructed outside
140/// this crate, `read()` requires you to return it, thus ensuring that you don't forget to call the
141/// callback.
142pub struct CallbackResult {
143    pub(crate) _private: std::marker::PhantomData<()>,
144}
145
146/// Path resolved from an inode with the current inode number attached.
147#[derive(Debug)]
148pub struct ResolvedPath {
149    parent: Arc<PathBuf>,
150    name: OsString,
151    ino: Inode,
152}
153
154impl ResolvedPath {
155    /// Creates a resolved path from a parent path, entry name, and inode.
156    pub fn new(parent: Arc<PathBuf>, name: OsString, ino: Inode) -> Self {
157        Self { parent, name, ino }
158    }
159    /// Returns the full path by joining the parent path and entry name.
160    pub fn full_path(&self) -> PathBuf {
161        self.parent.join(&self.name)
162    }
163    /// Returns the final path component.
164    pub fn name(&self) -> &OsStr {
165        &self.name
166    }
167    /// Returns the inode associated with this resolved path.
168    pub fn ino(&self) -> Inode {
169        self.ino
170    }
171    /// Returns the parent directory path.
172    pub fn parent_path(&self) -> Arc<PathBuf> {
173        self.parent.clone()
174    }
175    /// Returns the same path without the inode context.
176    pub fn entry_name(&self) -> EntryName {
177        EntryName {
178            parent: self.parent.clone(),
179            name: self.name.clone(),
180        }
181    }
182}
183
184/// Entry name resolved relative to a parent directory path.
185#[derive(Debug)]
186pub struct EntryName {
187    parent: Arc<PathBuf>,
188    //parent_ino: Inode,
189    name: OsString,
190}
191
192impl EntryName {
193    /// Attaches an inode to this entry name.
194    pub fn with(self, ino: Inode) -> ResolvedPath {
195        ResolvedPath {
196            parent: self.parent,
197            name: self.name,
198            ino,
199        }
200    }
201    /// Creates an entry name from a parent folder path and child name.
202    pub fn new(parent: FolderPath, name: OsString) -> Self {
203        Self {
204            parent: parent.0,
205            name,
206        }
207    }
208    /// Returns the full path by joining the parent path and entry name.
209    pub fn full_path(&self) -> PathBuf {
210        self.parent.join(&self.name)
211    }
212    /// Returns the final path component.
213    pub fn name(&self) -> &OsStr {
214        &self.name
215    }
216
217    /// Returns the parent directory path.
218    pub fn parent_path(&self) -> Arc<PathBuf> {
219        self.parent.clone()
220    }
221}
222
223/// Shared path for a directory entry in the inode table.
224#[repr(transparent)]
225#[derive(Debug)]
226pub struct FolderPath(Arc<PathBuf>);
227
228impl From<Arc<PathBuf>> for FolderPath {
229    fn from(value: Arc<PathBuf>) -> Self {
230        Self(value)
231    }
232}
233
234impl From<&OsStr> for FolderPath {
235    fn from(value: &OsStr) -> Self {
236        Self(Arc::new(value.into()))
237    }
238}
239
240impl Deref for FolderPath {
241    type Target = Arc<PathBuf>;
242    fn deref(&self) -> &Self::Target {
243        &self.0
244    }
245}
246
247fn enosys_error<T>() -> std::io::Result<T> {
248    Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
249}
250
251/// This trait must be implemented to implement a filesystem with FuserNG.
252///
253/// Methods have default ENOSYS implementations, so a filesystem can implement only the operations
254/// it supports.
255///
256/// ```no_run
257/// use std::io;
258/// use std::time::{Duration, SystemTime};
259///
260/// use fuser_ng::{EntryName, FileAttr, FileType, Filesystem, RequestInfo, ResultEntry};
261///
262/// struct RootOnly;
263///
264/// impl Filesystem for RootOnly {
265///     fn getattr(&self, _req: RequestInfo, path: &EntryName, _fh: Option<u64>) -> ResultEntry {
266///         if path.full_path() != std::path::Path::new("/") {
267///             return Err(io::Error::from_raw_os_error(libc::ENOENT));
268///         }
269///
270///         let now = SystemTime::now();
271///
272///         Ok((
273///             Duration::from_secs(1),
274///             FileAttr {
275///                 size: 0,
276///                 blocks: 0,
277///                 atime: now,
278///                 mtime: now,
279///                 ctime: now,
280///                 crtime: now,
281///                 kind: FileType::Directory,
282///                 perm: 0o755,
283///                 nlink: 2,
284///                 uid: 0,
285///                 gid: 0,
286///                 rdev: 0,
287///                 blksize: 512,
288///                 flags: 0,
289///             },
290///         ))
291///     }
292/// }
293/// ```
294pub trait Filesystem {
295    /// Called on mount, before any other function.
296    fn init(&self, _req: RequestInfo, _config: &mut KernelConfig) -> ResultEmpty {
297        Ok(())
298    }
299
300    /// Called on filesystem unmount.
301    fn destroy(&self) {
302        // Nothing.
303    }
304
305    /// Get the attributes of a filesystem entry.
306    ///
307    /// * `fh`: a file handle if this is called on an open file.
308    fn getattr(&self, _req: RequestInfo, _path: &EntryName, _fh: Option<u64>) -> ResultEntry {
309        enosys_error()
310    }
311
312    // The following operations in the FUSE C API are all one kernel call: setattr
313    // We split them out to match the C API's behavior.
314
315    /// Change the mode of a filesystem entry.
316    ///
317    /// * `fh`: a file handle if this is called on an open file.
318    /// * `mode`: the mode to change the file to.
319    fn chmod(
320        &self,
321        _req: RequestInfo,
322        _path: &ResolvedPath,
323        _fh: Option<u64>,
324        _mode: u32,
325    ) -> ResultEmpty {
326        enosys_error()
327    }
328
329    /// Change the owner UID and/or group GID of a filesystem entry.
330    ///
331    /// * `fh`: a file handle if this is called on an open file.
332    /// * `uid`: user ID to change the file's owner to. If `None`, leave the UID unchanged.
333    /// * `gid`: group ID to change the file's group to. If `None`, leave the GID unchanged.
334    fn chown(
335        &self,
336        _req: RequestInfo,
337        _path: &ResolvedPath,
338        _fh: Option<u64>,
339        _uid: Option<u32>,
340        _gid: Option<u32>,
341    ) -> ResultEmpty {
342        enosys_error()
343    }
344
345    /// Set the length of a file.
346    ///
347    /// * `fh`: a file handle if this is called on an open file.
348    /// * `size`: size in bytes to set as the file's length.
349    fn truncate(
350        &self,
351        _req: RequestInfo,
352        _path: &ResolvedPath,
353        _fh: Option<u64>,
354        _size: u64,
355    ) -> ResultEmpty {
356        enosys_error()
357    }
358
359    /// Set timestamps of a filesystem entry.
360    ///
361    /// * `fh`: a file handle if this is called on an open file.
362    /// * `atime`: the time of last access.
363    /// * `mtime`: the time of last modification.
364    fn utimens(
365        &self,
366        _req: RequestInfo,
367        _path: &ResolvedPath,
368        _fh: Option<u64>,
369        _atime: Option<SystemTime>,
370        _mtime: Option<SystemTime>,
371    ) -> ResultEmpty {
372        enosys_error()
373    }
374
375    /// Set timestamps of a filesystem entry (with extra options only used on MacOS).
376    #[allow(clippy::too_many_arguments)]
377    fn utimens_macos(
378        &self,
379        _req: RequestInfo,
380        _path: &ResolvedPath,
381        _fh: Option<u64>,
382        _crtime: Option<SystemTime>,
383        _chgtime: Option<SystemTime>,
384        _bkuptime: Option<SystemTime>,
385        _flags: Option<u32>,
386    ) -> ResultEmpty {
387        enosys_error()
388    }
389
390    // END OF SETATTR FUNCTIONS
391
392    /// Read a symbolic link.
393    fn readlink(&self, _req: RequestInfo, _path: &ResolvedPath) -> ResultData {
394        enosys_error()
395    }
396
397    /// Create a special file.
398    ///
399    /// * `parent`: path to the directory to make the entry under.
400    /// * `name`: name of the entry.
401    /// * `mode`: mode for the new entry.
402    /// * `rdev`: if mode has the bits `S_IFCHR` or `S_IFBLK` set, this is the major and minor numbers for the device file. Otherwise it should be ignored.
403    fn mknod(&self, _req: RequestInfo, _entry: &EntryName, _mode: u32, _rdev: u32) -> ResultEntry {
404        enosys_error()
405    }
406
407    /// Create a directory.
408    ///
409    /// * `parent`: path to the directory to make the directory under.
410    /// * `name`: name of the directory.
411    /// * `mode`: permissions for the new directory.
412    fn mkdir(&self, _req: RequestInfo, _entry: &EntryName, _mode: u32) -> ResultEntry {
413        enosys_error()
414    }
415
416    /// Remove a file.
417    ///
418    /// * `parent`: path to the directory containing the file to delete.
419    /// * `name`: name of the file to delete.
420    fn unlink(&self, _req: RequestInfo, _entry: &EntryName) -> ResultEmpty {
421        enosys_error()
422    }
423
424    /// Remove a directory.
425    ///
426    /// * `parent`: path to the directory containing the directory to delete.
427    /// * `name`: name of the directory to delete.
428    fn rmdir(&self, _req: RequestInfo, _entry: &EntryName) -> ResultEmpty {
429        enosys_error()
430    }
431
432    /// Create a symbolic link.
433    ///
434    /// * `parent`: path to the directory to make the link in.
435    /// * `name`: name of the symbolic link.
436    /// * `target`: path (may be relative or absolute) to the target of the link.
437    fn symlink(&self, _req: RequestInfo, _entry: &EntryName, _target: &Path) -> ResultEntry {
438        enosys_error()
439    }
440
441    /// Rename a filesystem entry.
442    ///
443    /// * `parent`: path to the directory containing the existing entry.
444    /// * `name`: name of the existing entry.
445    /// * `newparent`: path to the directory it should be renamed into (may be the same as `parent`).
446    /// * `newname`: name of the new entry.
447    fn rename(&self, _req: RequestInfo, _entry: &EntryName, _new_entry: &EntryName) -> ResultEmpty {
448        enosys_error()
449    }
450
451    /// Create a hard link.
452    ///
453    /// * `path`: path to an existing file.
454    /// * `newparent`: path to the directory for the new link.
455    /// * `newname`: name for the new link.
456    fn link(&self, _req: RequestInfo, _path: &ResolvedPath, _new_entry: &EntryName) -> ResultEntry {
457        enosys_error()
458    }
459
460    /// Open a file.
461    ///
462    /// * `path`: path to the file.
463    /// * `flags`: one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`, plus maybe additional flags.
464    ///
465    /// Return a tuple of (file handle, flags). The file handle will be passed to any subsequent
466    /// calls that operate on the file, and can be any value you choose, though it should allow
467    /// your filesystem to identify the file opened even without any path info.
468    fn open(&self, _req: RequestInfo, _path: &ResolvedPath, _flags: u32) -> ResultOpen {
469        enosys_error()
470    }
471
472    /// Read from a file.
473    ///
474    /// Note that it is not an error for this call to request to read past the end of the file, and
475    /// you should only return data up to the end of the file (i.e. the number of bytes returned
476    /// will be fewer than requested; possibly even zero). Do not extend the file in this case.
477    ///
478    /// * `path`: path to the file.
479    /// * `fh`: file handle returned from the `open` call.
480    /// * `offset`: offset into the file to start reading.
481    /// * `size`: number of bytes to read.
482    /// * `callback`: a callback that must be invoked to return the result of the operation: either
483    ///   the result data as a slice, or an error code.
484    ///
485    /// Return the return value from the `callback` function.
486    fn read(
487        &self,
488        _req: RequestInfo,
489        _path: &ResolvedPath,
490        _fh: u64,
491        _offset: u64,
492        _size: u32,
493        callback: impl FnOnce(ResultSlice<'_>) -> CallbackResult,
494    ) -> CallbackResult {
495        callback(enosys_error())
496    }
497
498    /// Write to a file.
499    ///
500    /// * `path`: path to the file.
501    /// * `fh`: file handle returned from the `open` call.
502    /// * `offset`: offset into the file to start writing.
503    /// * `data`: the data to write
504    /// * `flags`:
505    ///
506    /// Return the number of bytes written.
507    fn write(
508        &self,
509        _req: RequestInfo,
510        _path: &ResolvedPath,
511        _fh: u64,
512        _offset: u64,
513        _data: Vec<u8>,
514        _flags: u32,
515    ) -> ResultWrite {
516        enosys_error()
517    }
518
519    /// Called each time a program calls `close` on an open file.
520    ///
521    /// Note that because file descriptors can be duplicated (by `dup`, `dup2`, `fork`) this may be
522    /// called multiple times for a given file handle. The main use of this function is if the
523    /// filesystem would like to return an error to the `close` call. Note that most programs
524    /// ignore the return value of `close`, though.
525    ///
526    /// * `path`: path to the file.
527    /// * `fh`: file handle returned from the `open` call.
528    /// * `lock_owner`: if the filesystem supports locking (`setlk`, `getlk`), remove all locks
529    ///   belonging to this lock owner.
530    fn flush(
531        &self,
532        _req: RequestInfo,
533        _path: &ResolvedPath,
534        _fh: u64,
535        _lock_owner: u64,
536    ) -> ResultEmpty {
537        enosys_error()
538    }
539
540    /// Called when an open file is closed.
541    ///
542    /// There will be one of these for each `open` call. After `release`, no more calls will be
543    /// made with the given file handle.
544    ///
545    /// * `path`: path to the file.
546    /// * `fh`: file handle returned from the `open` call.
547    /// * `flags`: the flags passed when the file was opened.
548    /// * `lock_owner`: if the filesystem supports locking (`setlk`, `getlk`), remove all locks
549    ///   belonging to this lock owner.
550    /// * `flush`: whether pending data must be flushed or not.
551    fn release(
552        &self,
553        _req: RequestInfo,
554        _path: &ResolvedPath,
555        _fh: u64,
556        _flags: u32,
557        _lock_owner: u64,
558        _flush: bool,
559    ) -> ResultEmpty {
560        enosys_error()
561    }
562
563    /// Write out any pending changes of a file.
564    ///
565    /// When this returns, data should be written to persistent storage.
566    ///
567    /// * `path`: path to the file.
568    /// * `fh`: file handle returned from the `open` call.
569    /// * `datasync`: if `false`, also write metadata, otherwise just write file data.
570    fn fsync(
571        &self,
572        _req: RequestInfo,
573        _path: &ResolvedPath,
574        _fh: u64,
575        _datasync: bool,
576    ) -> ResultEmpty {
577        enosys_error()
578    }
579
580    /// Open a directory.
581    ///
582    /// Analogous to the `opend` call.
583    ///
584    /// * `path`: path to the directory.
585    /// * `flags`: file access flags. Will contain `O_DIRECTORY` at least.
586    ///
587    /// Return a tuple of (file handle, flags). The file handle will be passed to any subsequent
588    /// calls that operate on the directory, and can be any value you choose, though it should
589    /// allow your filesystem to identify the directory opened even without any path info.
590    fn opendir(&self, _req: RequestInfo, _path: &ResolvedPath, _flags: u32) -> ResultOpen {
591        enosys_error()
592    }
593
594    /// Get the entries of a directory.
595    ///
596    /// * `path`: path to the directory.
597    /// * `fh`: file handle returned from the `opendir` call.
598    ///
599    /// Return all the entries of the directory.
600    fn readdir(&self, _req: RequestInfo, _path: &ResolvedPath, _fh: u64) -> ResultReaddir {
601        enosys_error()
602    }
603
604    /// Close an open directory.
605    ///
606    /// This will be called exactly once for each `opendir` call.
607    ///
608    /// * `path`: path to the directory.
609    /// * `fh`: file handle returned from the `opendir` call.
610    /// * `flags`: the file access flags passed to the `opendir` call.
611    fn releasedir(
612        &self,
613        _req: RequestInfo,
614        _path: &ResolvedPath,
615        _fh: u64,
616        _flags: u32,
617    ) -> ResultEmpty {
618        enosys_error()
619    }
620
621    /// Write out any pending changes to a directory.
622    ///
623    /// Analogous to the `fsync` call.
624    fn fsyncdir(
625        &self,
626        _req: RequestInfo,
627        _path: &ResolvedPath,
628        _fh: u64,
629        _datasync: bool,
630    ) -> ResultEmpty {
631        enosys_error()
632    }
633
634    /// Get filesystem statistics.
635    ///
636    /// * `path`: path to some folder in the filesystem.
637    ///
638    /// See the `Statfs` struct for more details.
639    fn statfs(&self, _req: RequestInfo, _path: &ResolvedPath) -> ResultStatfs {
640        enosys_error()
641    }
642
643    /// Set a file extended attribute.
644    ///
645    /// * `path`: path to the file.
646    /// * `name`: attribute name.
647    /// * `value`: the data to set the value to.
648    /// * `flags`: can be either `XATTR_CREATE` or `XATTR_REPLACE`.
649    /// * `position`: offset into the attribute value to write data.
650    fn setxattr(
651        &self,
652        _req: RequestInfo,
653        _path: &ResolvedPath,
654        _name: &OsStr,
655        _value: &[u8],
656        _flags: u32,
657        _position: u32,
658    ) -> ResultEmpty {
659        enosys_error()
660    }
661
662    /// Get a file extended attribute.
663    ///
664    /// * `path`: path to the file
665    /// * `name`: attribute name.
666    /// * `size`: the maximum number of bytes to read.
667    ///
668    /// If `size` is 0, return `Xattr::Size(n)` where `n` is the size of the attribute data.
669    /// Otherwise, return `Xattr::Data(data)` with the requested data.
670    fn getxattr(
671        &self,
672        _req: RequestInfo,
673        _path: &ResolvedPath,
674        _name: &OsStr,
675        _size: u32,
676    ) -> ResultXattr {
677        enosys_error()
678    }
679
680    /// List extended attributes for a file.
681    ///
682    /// * `path`: path to the file.
683    /// * `size`: maximum number of bytes to return.
684    ///
685    /// If `size` is 0, return `Xattr::Size(n)` where `n` is the size required for the list of
686    /// attribute names.
687    /// Otherwise, return `Xattr::Data(data)` where `data` is all the null-terminated attribute
688    /// names.
689    fn listxattr(&self, _req: RequestInfo, _path: &ResolvedPath, _size: u32) -> ResultXattr {
690        enosys_error()
691    }
692
693    /// Remove an extended attribute for a file.
694    ///
695    /// * `path`: path to the file.
696    /// * `name`: name of the attribute to remove.
697    fn removexattr(&self, _req: RequestInfo, _path: &ResolvedPath, _name: &OsStr) -> ResultEmpty {
698        enosys_error()
699    }
700
701    /// Check for access to a file.
702    ///
703    /// * `path`: path to the file.
704    /// * `mask`: mode bits to check for access to.
705    ///
706    /// Return `Ok(())` if all requested permissions are allowed, otherwise return `Err(EACCES)`
707    /// or other error code as appropriate (e.g. `ENOENT` if the file doesn't exist).
708    fn access(&self, _req: RequestInfo, _path: &ResolvedPath, _mask: u32) -> ResultEmpty {
709        enosys_error()
710    }
711
712    /// Create and open a new file.
713    ///
714    /// * `parent`: path to the directory to create the file in.
715    /// * `name`: name of the file to be created.
716    /// * `mode`: the mode to set on the new file.
717    /// * `flags`: flags like would be passed to `open`.
718    ///
719    /// Return a `CreatedEntry` (which contains the new file's attributes as well as a file handle
720    /// -- see documentation on `open` for more info on that).
721    fn create(
722        &self,
723        _req: RequestInfo,
724        _entry: &EntryName,
725        _mode: u32,
726        _flags: u32,
727    ) -> ResultCreate {
728        enosys_error()
729    }
730
731    // getlk
732
733    // setlk
734
735    // bmap
736
737    /// macOS only: Rename the volume.
738    ///
739    /// * `name`: new name for the volume
740    #[cfg(target_os = "macos")]
741    fn setvolname(&self, _req: RequestInfo, _name: &OsStr) -> ResultEmpty {
742        enosys_error()
743    }
744
745    // exchange (macOS only, undocumented)
746
747    /// macOS only: Query extended times (bkuptime and crtime).
748    ///
749    /// * `path`: path to the file to get the times for.
750    ///
751    /// Return an `XTimes` struct with the times, or other error code as appropriate.
752    #[cfg(target_os = "macos")]
753    fn getxtimes(&self, _req: RequestInfo, _path: &ResolvedPath) -> ResultXTimes {
754        Err(libc::ENOSYS)
755    }
756}