1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
use fuse::FileType; use libc::{self, ENOSYS}; use time::Timespec; use std::path::Path; use std::ffi::{OsStr, OsString}; /// libc Error Code pub type LibcError = libc::c_int; /// Metadata representing a file pub struct Metadata { pub size: u64, pub atime: Timespec, pub mtime: Timespec, pub ctime: Timespec, pub crtime: Timespec, pub kind: FileType, pub perm: u16, } /// Entry from a directory listing pub struct DirEntry { pub filename: OsString, pub metadata: Metadata, } impl DirEntry { pub fn new<S: AsRef<OsStr>>(filename: S, metadata: Metadata) -> DirEntry { DirEntry { filename: filename.as_ref().to_owned(), metadata: metadata } } } /// Trait to implement to provide a backend store for a `NetFuse` filesystem /// /// The methods in this trait are abstractions over the low level method provided /// by the FUSE library's `Filesystem` trait. The `NetFuse` implementation of `FileSystem` /// will manage many of the low-level details like inode numbers, offsets, sizes, /// and lazy persistence. /// /// In doing so, implementing a backend store for `NetFuse` (ie. implementing `NetworkFilesystem`) /// is mostly a matter of making network network calls that map to very common filesystem operations. /// /// The default implementation is just enough to mount a filesystem that supports no read or write operations pub trait NetworkFilesystem { /// Any arbitrary code to run when mounting /// /// Returning an error will result in an error during mounting (TODO: verify) fn init(&mut self) -> Result<(), LibcError> { Ok(()) } /// Returns the metadata for a file or directory associated with a given /// /// This typically corresponds to operations like `stat`. /// This method is called when the `NetFuse` inode store did not find /// cached inode data for this `path`. Any returned `Metadata` will be heavily cached. /// /// See `man 2 stat` for more information including appropriate errors to return. fn lookup(&mut self, _path: &Path) -> Result<Metadata, LibcError> { Err(ENOSYS) } /// Reads the contents of a file associated with a given path /// /// This is called on the first filesystem attempt to `read` a file, /// since reading happens in chunks, the underlying `NetFuse` implemenation /// will cache the result returned and read it in from the cache in chunks /// without additional calls to this method. /// /// The cached data will be freed when there are no remaining open handles on this file. /// /// See `man 2 read` for more information including appropriate errors to return. fn read(&mut self, _path: &Path, _buffer: &mut Vec<u8> ) -> Result<usize, LibcError> { Err(ENOSYS) } /// Write data back to the network backend /// /// This is not actually called when the filesystem calls `write`. /// Instead this is called when `NetFuse` handles an `fsync` /// or during `release` for a file handle that modified the cached copy. /// /// Note: the `release` implementation might change in the future in favor of /// a configurable defferred commit /// /// This method will only be called if: /// - a previous `lookup` has confirmed a file exists at this path /// - the volume was mounted with the `rw` option /// /// See `man 2 fsync` for more information including appropriate errors to return. fn write(&mut self, _path: &Path, _data: &[u8]) -> Result<(), LibcError> { Err(ENOSYS) } /// List contents of a directory /// /// This method should return an iterator over the contents of the directory /// specified by `path`. By returning an iterator, `NetFuse` can begin listing /// contents sooner in the cases where listing may require multiple paged network requests. /// /// These value will be cached to prevent additional listing. The current cache implementation /// will likely change as it is not friendly to cases where the data changes outside the filesystem. /// Until then, unmount and re-mount the volume to clear the directory listing cache. /// /// See `man 2 readdir` for more information including appropriate errors to return. /// /// Note: this method will likely return `impl Iterator<Item=Result<DirEntry, LibcError>>` once `impl Trait` lands in nightly fn readdir(&mut self, _path: &Path) -> Box<Iterator<Item=Result<DirEntry, LibcError>>> { Box::new(vec![Err(ENOSYS)].into_iter()) } /// Creates an empty directory for the given path /// /// This method is only called if: /// - a previous `lookup` has confirmed the parent path was a directory /// - the volume is mounted with the `rw` option /// /// See `man 2 mkdir` for more information including appropriate errors to return. fn mkdir(&mut self, _path: &Path) -> Result<(), LibcError> { Err(ENOSYS) } /// Removes the directory that corresponds to a given path /// /// This method is only called if: /// - a previous `lookup` has confirmed a directory exists at this path /// - the volume is mounted with the `rw` option /// /// See `man 2 rmdir` for more information including appropriate errors to return. /// Namely: you'll generally want to return ENOTEMPTY if the directory is not empty fn rmdir(&mut self, _path: &Path) -> Result<(), LibcError> { Err(ENOSYS) } /// Removes the file that corresponds to a given path /// /// This method is only called if: /// - a previous `lookup` has confirmed a file exists at this path /// - the volume is mounted with the `rw` option /// /// See `man 2 unlink` for more information including appropriate errors to return. fn unlink(&mut self, _path: &Path) -> Result<(), LibcError>{ Err(ENOSYS) } }