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}