-idle: don't lazy-unmount

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 https://github.com/rfjakob/gocryptfs/issues/533
This commit is contained in:
Jakob Unterwurzacher 2021-01-02 18:11:18 +01:00
parent c20c7992a0
commit de108d3fc0
3 changed files with 25 additions and 7 deletions

View File

@ -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". for the specified duration. Durations can be specified like "500s" or "2h45m".
0 (the default) means stay mounted indefinitely. 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 #### -kernel_cache
Enable the kernel_cache option of the FUSE filesystem, see fuse(8) for details. Enable the kernel_cache option of the FUSE filesystem, see fuse(8) for details.

View File

@ -192,6 +192,7 @@ Changelog
vNEXT, in progress vNEXT, in progress
* MANPAGE: Split options into sections acc. to where they apply ([#517](https://github.com/rfjakob/gocryptfs/issues/517)) * 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 v2.0-beta2, 2020-11-14
* Improve [performance](Documentation/performance.txt#L69) * Improve [performance](Documentation/performance.txt#L69)

View File

@ -175,11 +175,15 @@ func doMount(args *argContainer) {
const checksDuringTimeoutPeriod = 4 const checksDuringTimeoutPeriod = 4
func idleMonitor(idleTimeout time.Duration, fs *fusefrontend.RootNode, srv *fuse.Server, mountpoint string) { 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(idleTimeout/checksDuringTimeoutPeriod),
uint64(2*time.Minute)) uint64(2*time.Minute))
timeoutCycles := int(math.Ceil(float64(idleTimeout) / float64(sleepTimeBetweenChecks))) timeoutCycles := int(math.Ceil(float64(idleTimeout) / float64(sleepNs)))
idleCount := 0 idleCount := 0
idleTime := func() time.Duration {
return time.Duration(sleepNs * uint64(idleCount))
}
for { for {
// Atomically check whether the flag is 0 and reset it to 1 if so. // Atomically check whether the flag is 0 and reset it to 1 if so.
isIdle := !atomic.CompareAndSwapUint32(&fs.IsIdle, 0, 1) isIdle := !atomic.CompareAndSwapUint32(&fs.IsIdle, 0, 1)
@ -191,13 +195,21 @@ func idleMonitor(idleTimeout time.Duration, fs *fusefrontend.RootNode, srv *fuse
idleCount++ idleCount++
} }
tlog.Debug.Printf( tlog.Debug.Printf(
"Checking for idle (isIdle = %t, open = %d): %s", "idleMonitor: idle for %v (idleCount = %d, isIdle = %t, open = %d)",
isIdle, openFileCount, time.Now().String()) idleTime(), idleCount, isIdle, openFileCount)
if idleCount > 0 && idleCount%timeoutCycles == 0 { if idleCount > 0 && idleCount%timeoutCycles == 0 {
tlog.Info.Printf("Filesystem idle; unmounting: %s", mountpoint) tlog.Info.Printf("idleMonitor: filesystem idle; unmounting: %s", mountpoint)
unmount(srv, 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))
} }
} }
@ -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) { func unmount(srv *fuse.Server, mountpoint string) {
err := srv.Unmount() err := srv.Unmount()
if err != nil { if err != nil {