mount: check for more requisite mountpoint conditions (#5718)

* mount: check for more requisite mountpoint conditions

In order to be able to mount a repository over a mountpoint target
directory via FUSE, that target directory needs to be both writeable and
executable for the UID performing the mount.

Without this patch, `restic mount` only checks for the target pathname's
existence, which can lead to a lot of data transfer and/or computation
for large repos to be performed before eventually croaking with a fatal
"fusermount: failed to chdir to mountpoint: Permission denied" (or
similar) error.

FUSE does allow for mounting over a target path that refers to a regular
(writeable) file, but the result is not accessible via chdir(), so we
prevent that as well, and accept only directory inodes as the intended
target mountpoint path.

* Don't use snake_case identifiers

* Add changelog entry

* tweak changelog summary

---------

Co-authored-by: Michael Eischer <michael.eischer@fau.de>
This commit is contained in:
Johannes Truschnigg
2026-02-19 18:11:49 +01:00
committed by GitHub
parent 4c56384481
commit a8f0ad5cc4
2 changed files with 24 additions and 4 deletions

View File

@@ -0,0 +1,9 @@
Enhancement: stricter early mountpoint validation in `mount`
`restic mount` accepted parameters that would lead to a FUSE mount operation
failing after having done computationally intensive work to prepare the mount.
The `mountpoint` argument supplied must now refer to the name of a directory
that the current user can access and write to, otherwise `restic mount` will
exit with an error before interacting with the repository.
https://github.com/restic/restic/pull/5718

View File

@@ -11,6 +11,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"golang.org/x/sys/unix"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug"
@@ -35,8 +36,8 @@ func newMountCommand(globalOptions *global.Options) *cobra.Command {
Use: "mount [flags] mountpoint",
Short: "Mount the repository",
Long: `
The "mount" command mounts the repository via fuse to a directory. This is a
read-only mount.
The "mount" command mounts the repository via fuse over a writeable directory.
The repository will be mounted read-only.
Snapshot Directories
====================
@@ -133,9 +134,19 @@ func runMount(ctx context.Context, opts MountOptions, gopts global.Options, args
// Check the existence of the mount point at the earliest stage to
// prevent unnecessary computations while opening the repository.
if _, err := os.Stat(mountpoint); errors.Is(err, os.ErrNotExist) {
stat, err := os.Stat(mountpoint)
if errors.Is(err, os.ErrNotExist) {
printer.P("Mountpoint %s doesn't exist", mountpoint)
return err
return errors.Fatal("invalid mountpoint")
} else if !stat.IsDir() {
printer.P("Mountpoint %s is not a directory", mountpoint)
return errors.Fatal("invalid mountpoint")
}
err = unix.Access(mountpoint, unix.W_OK|unix.X_OK)
if err != nil {
printer.P("Mountpoint %s is not writeable or not excutable", mountpoint)
return errors.Fatal("inaccessible mountpoint")
}
debug.Log("start mount")