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)
    }

}