Skip to content

Commit

Permalink
-idle: don't lazy-unmount
Browse files Browse the repository at this point in the history
When a process has its working dir inside the mount,
the only way we notice is that we get EBUSY when trying
to unmount.

We used to lazy-unmount in this case, but this means
pulling the rug from under the process.

For example, bash will start throwing

  cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory

messages.

Fixes #533
  • Loading branch information
rfjakob committed Jan 2, 2021
1 parent c20c799 commit de108d3
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 7 deletions.
3 changes: 3 additions & 0 deletions Documentation/MANPAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ Only for forward mode: automatically unmount the filesystem if it has been idle
for the specified duration. Durations can be specified like "500s" or "2h45m".
0 (the default) means stay mounted indefinitely.

When a process has open files or its working directory in the mount,
this will keep it not idle indefinitely.

#### -kernel_cache
Enable the kernel_cache option of the FUSE filesystem, see fuse(8) for details.

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ Changelog

vNEXT, in progress
* MANPAGE: Split options into sections acc. to where they apply ([#517](https://github.com/rfjakob/gocryptfs/issues/517))
* `-idle`: count cwd inside the mount as busy ([#533](https://github.com/rfjakob/gocryptfs/issues/533))

v2.0-beta2, 2020-11-14
* Improve [performance](Documentation/performance.txt#L69)
Expand Down
28 changes: 21 additions & 7 deletions mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,15 @@ func doMount(args *argContainer) {
const checksDuringTimeoutPeriod = 4

func idleMonitor(idleTimeout time.Duration, fs *fusefrontend.RootNode, srv *fuse.Server, mountpoint string) {
sleepTimeBetweenChecks := contentenc.MinUint64(
// sleepNs is the sleep time between checks, in nanoseconds.
sleepNs := contentenc.MinUint64(
uint64(idleTimeout/checksDuringTimeoutPeriod),
uint64(2*time.Minute))
timeoutCycles := int(math.Ceil(float64(idleTimeout) / float64(sleepTimeBetweenChecks)))
timeoutCycles := int(math.Ceil(float64(idleTimeout) / float64(sleepNs)))
idleCount := 0
idleTime := func() time.Duration {
return time.Duration(sleepNs * uint64(idleCount))
}
for {
// Atomically check whether the flag is 0 and reset it to 1 if so.
isIdle := !atomic.CompareAndSwapUint32(&fs.IsIdle, 0, 1)
Expand All @@ -191,13 +195,21 @@ func idleMonitor(idleTimeout time.Duration, fs *fusefrontend.RootNode, srv *fuse
idleCount++
}
tlog.Debug.Printf(
"Checking for idle (isIdle = %t, open = %d): %s",
isIdle, openFileCount, time.Now().String())
"idleMonitor: idle for %v (idleCount = %d, isIdle = %t, open = %d)",
idleTime(), idleCount, isIdle, openFileCount)
if idleCount > 0 && idleCount%timeoutCycles == 0 {
tlog.Info.Printf("Filesystem idle; unmounting: %s", mountpoint)
unmount(srv, mountpoint)
tlog.Info.Printf("idleMonitor: filesystem idle; unmounting: %s", mountpoint)
err := srv.Unmount()
if err != nil {
// We get "Device or resource busy" when a process has its
// working directory on the mount. Log the event at Info level
// so the user finds out why their filesystem does not get
// unmounted.
tlog.Info.Printf("idleMonitor: unmount failed: %v. Resetting idle time.", err)
idleCount = 0
}
}
time.Sleep(time.Duration(sleepTimeBetweenChecks))
time.Sleep(time.Duration(sleepNs))
}
}

Expand Down Expand Up @@ -483,6 +495,8 @@ func handleSigint(srv *fuse.Server, mountpoint string) {
}()
}

// unmount() calls srv.Unmount(), and if that fails, calls "fusermount -u -z"
// (lazy unmount).
func unmount(srv *fuse.Server, mountpoint string) {
err := srv.Unmount()
if err != nil {
Expand Down

0 comments on commit de108d3

Please sign in to comment.