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
#[macro_use] extern crate serde_derive;
extern crate fuse_mt;
use self::fuse_mt::*;
extern crate crossbeam;

use std::io::{Error, ErrorKind};
use std::ffi::{OsStr};
use std::time;
use std::mem;
use std::sync::mpsc;

mod filesystem;
mod backingstore;
mod settings;
mod rwhashes;

use self::backingstore::BackingStore;
use self::filesystem::FS;

// This is a hack while FuseMT requires 'static for the FilesystemMT instance
// See the github issue for discussion: https://github.com/wfraser/fuse-mt/issues/26
fn fix_lifetime<'a>(t: FS<'a>) -> FS<'static> {
  unsafe { mem::transmute(t) }
}

struct BackgroundThread {
  handle: crossbeam::ScopedJoinHandle<()>,
  tx: std::sync::mpsc::Sender<u8>,
}

impl BackgroundThread {
  fn new<'a, F: 'a>(scope: &crossbeam::Scope<'a>, secs: u64, closure: F) -> Self
  where F: Fn()+Send {
    let (tx, rx) = mpsc::channel();

    let handle = scope.spawn(move || {
      // Sync the backing store to disk/remote every so often
      let dur = time::Duration::from_secs(secs);
      loop {
        match rx.recv_timeout(dur) {
          Err(mpsc::RecvTimeoutError::Timeout) => {
            closure();
          },
          _ => break,
        }
      }
    });

    Self {
      handle,
      tx,
    }
  }

  fn join(self) {
    self.tx.send(0).unwrap(); // Signal the thread to die
    self.handle.join();
  }
}

pub fn run(source: &str, server: &str, mount: &str, maxbytes: u64) -> Result<(), Error> {
  let bs = match BackingStore::new(source, server, maxbytes) {
    Ok(bs) => bs,
    Err(_) => return Err(Error::new(ErrorKind::Other, "Couldn't create the backing store")),
  };
  let fs = match filesystem::FS::new(&bs) {
    Ok(fs) => fs,
    Err(_) => return Err(Error::new(ErrorKind::Other, "Couldn't create the filesystem")),
  };
  let fs = fix_lifetime(fs);
  let bsref = &bs;

  crossbeam::scope(|scope| {
    let sync   = BackgroundThread::new(&scope, 60, move || bsref.sync_all().unwrap());
    let upload = BackgroundThread::new(&scope, 10, move || bsref.do_uploads());
    let remove = BackgroundThread::new(&scope, 10, move || bsref.do_removals());

    let fshandle = scope.spawn(move || {
      let fs_mt = FuseMT::new(fs, 16);
      let options = [OsStr::new("-o"), OsStr::new("auto_unmount,default_permissions")];
      fuse_mt::mount(fs_mt, &mount, &options[..])
    });

    let ret = fshandle.join();
    sync.join();
    upload.join();
    remove.join();
    ret
  })
}