From 1a03e993cf898bd66125d88dcd682dc056a398d6 Mon Sep 17 00:00:00 2001 From: Simon Pilkington Date: Sat, 18 Apr 2026 14:58:26 +0200 Subject: mount: mount ro if cipherdir is on a ro filesystem (don't allow rw) --- mount.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/mount.go b/mount.go index f7378aa..2508409 100644 --- a/mount.go +++ b/mount.go @@ -449,9 +449,18 @@ func initGoFuse(rootNode fs.InodeEmbedder, args *argContainer) *fuse.Server { if runtime.GOOS == "darwin" { opts["volname"] = strings.Replace(path.Base(args.mountpoint), ",", "_", -1) } + underlyingFilesystemRo, err := isReadOnlyFilesystem(args.cipherdir) + if err != nil { + tlog.Debug.Printf("Error checking if cipherdir is on a read-only filesystem: %s", err) + } else if underlyingFilesystemRo && args.rw { + tlog.Fatal.Printf("Writeable mount explicitly requested but cipherdir %s is on a read-only filesystem, refusing.", args.cipherdir) + os.Exit(exitcodes.Usage) + } else if underlyingFilesystemRo && !args.ro { + tlog.Info.Printf("Cipherdir %s is on a read-only filesystem, mounting as read-only.", args.cipherdir) + } // The kernel enforces read-only operation, we just have to pass "ro". - // Reverse mounts are always read-only. - if args.ro || args.reverse { + // Reverse mounts and mounts with cipherdirs on read-only filesystems are always read-only. + if args.ro || args.reverse || underlyingFilesystemRo { opts["ro"] = "" } else if args.rw { opts["rw"] = "" @@ -562,3 +571,16 @@ func unmount(srv *fuse.Server, mountpoint string) { } } } + +const ( + ST_RDONLY = 0x1 +) + +func isReadOnlyFilesystem(path string) (bool, error) { + var stat syscall.Statfs_t + if err := syscall.Statfs(path, &stat); err != nil { + return false, err + } + + return (stat.Flags & ST_RDONLY) != 0, nil +} -- cgit v1.2.3