summaryrefslogtreecommitdiff
path: root/mount.go
diff options
context:
space:
mode:
authorJesse Dunietz2018-10-06 15:49:33 -0400
committerJakob Unterwurzacher2018-10-11 20:16:45 +0200
commit87d3ed9187cb9caccaa8df0769d4cb722cc5bd00 (patch)
tree14963c3f66b1277f0bf7904650665156e102929a /mount.go
parent57a5a8791f8a8c957aef87aecfa4fa318873b744 (diff)
Add option for autounmount
Even though filesystem notifications aren't implemented for FUSE, I decided to try my hand at implementing the autounmount feature (#128). I based it on the EncFS autounmount code, which records filesystem accesses and checks every X seconds whether it's idled long enough to unmount. I've tested the feature locally, but I haven't added any tests for this flag. I also haven't worked with Go before. So please let me know if there's anything that should be done differently. One particular concern: I worked from the assumption that the open files table is unique per-filesystem. If that's not true, I'll need to add an open file count and associated lock to the Filesystem type instead. https://github.com/rfjakob/gocryptfs/pull/265
Diffstat (limited to 'mount.go')
-rw-r--r--mount.go72
1 files changed, 59 insertions, 13 deletions
diff --git a/mount.go b/mount.go
index f473e1e..b4b6e61 100644
--- a/mount.go
+++ b/mount.go
@@ -5,6 +5,7 @@ import (
"fmt"
"log"
"log/syslog"
+ "math"
"net"
"os"
"os/exec"
@@ -14,6 +15,7 @@ import (
"runtime"
"runtime/debug"
"strings"
+ "sync/atomic"
"syscall"
"time"
@@ -29,6 +31,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/fusefrontend"
"github.com/rfjakob/gocryptfs/internal/fusefrontend_reverse"
"github.com/rfjakob/gocryptfs/internal/nametransform"
+ "github.com/rfjakob/gocryptfs/internal/openfiletable"
"github.com/rfjakob/gocryptfs/internal/tlog"
)
@@ -98,7 +101,7 @@ func doMount(args *argContainer) {
fs, wipeKeys := initFuseFrontend(args)
// Initialize go-fuse FUSE server
srv := initGoFuse(fs, args)
- // Try to wipe secrect keys from memory after unmount
+ // Try to wipe secret keys from memory after unmount
defer wipeKeys()
tlog.Info.Println(tlog.ColorGreen + "Filesystem mounted and ready." + tlog.ColorReset)
@@ -137,10 +140,49 @@ func doMount(args *argContainer) {
// Return memory that was allocated for scrypt (64M by default!) and other
// stuff that is no longer needed to the OS
debug.FreeOSMemory()
+ // Set up autounmount, if requested.
+ if args.idle > 0 && !args.reverse {
+ // Not being in reverse mode means we always have a forward file system.
+ fwdFs := fs.(*fusefrontend.FS)
+ go idleMonitor(args.idle, fwdFs, srv, args.mountpoint)
+ }
// Jump into server loop. Returns when it gets an umount request from the kernel.
srv.Serve()
}
+// Based on the EncFS idle monitor:
+// https://github.com/vgough/encfs/blob/1974b417af189a41ffae4c6feb011d2a0498e437/encfs/main.cpp#L851
+// idleMonitor is a function to be run as a thread that checks for
+// filesystem idleness and unmounts if we've been idle for long enough.
+const checksDuringTimeoutPeriod = 4
+
+func idleMonitor(idleTimeout time.Duration, fs *fusefrontend.FS, srv *fuse.Server, mountpoint string) {
+ sleepTimeBetweenChecks := contentenc.MinUint64(
+ uint64(idleTimeout/checksDuringTimeoutPeriod),
+ uint64(2*time.Minute))
+ timeoutCycles := int(math.Ceil(float64(idleTimeout) / float64(sleepTimeBetweenChecks)))
+ idleCount := 0
+ for {
+ // Atomically check whether the access flag is set and reset it to 0 if so.
+ recentAccess := atomic.CompareAndSwapUint32(&fs.AccessedSinceLastCheck, 1, 0)
+ // Any form of current or recent access resets the idle counter.
+ openFileCount := openfiletable.CountOpenFiles()
+ if recentAccess || openFileCount > 0 {
+ idleCount = 0
+ } else {
+ idleCount++
+ }
+ tlog.Debug.Printf(
+ "Checking for idle (recentAccess = %t, open = %d): %s",
+ recentAccess, openFileCount, time.Now().String())
+ if idleCount > 0 && idleCount%timeoutCycles == 0 {
+ tlog.Info.Printf("Filesystem idle; unmounting: %s", mountpoint)
+ unmount(srv, mountpoint)
+ }
+ time.Sleep(time.Duration(sleepTimeBetweenChecks))
+ }
+}
+
// setOpenFileLimit tries to increase the open file limit to 4096 (the default hard
// limit on Linux).
func setOpenFileLimit() {
@@ -379,18 +421,22 @@ func handleSigint(srv *fuse.Server, mountpoint string) {
signal.Notify(ch, syscall.SIGTERM)
go func() {
<-ch
- err := srv.Unmount()
- if err != nil {
- tlog.Warn.Printf("handleSigint: srv.Unmount returned %v", err)
- if runtime.GOOS == "linux" {
- // MacOSX does not support lazy unmount
- tlog.Info.Printf("Trying lazy unmount")
- cmd := exec.Command("fusermount", "-u", "-z", mountpoint)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- cmd.Run()
- }
- }
+ unmount(srv, mountpoint)
os.Exit(exitcodes.SigInt)
}()
}
+
+func unmount(srv *fuse.Server, mountpoint string) {
+ err := srv.Unmount()
+ if err != nil {
+ tlog.Warn.Printf("unmount: srv.Unmount returned %v", err)
+ if runtime.GOOS == "linux" {
+ // MacOSX does not support lazy unmount
+ tlog.Info.Printf("Trying lazy unmount")
+ cmd := exec.Command("fusermount", "-u", "-z", mountpoint)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Run()
+ }
+ }
+}