From c9b825c58a9f996379108926754513bca03bb306 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Fri, 10 Sep 2021 17:17:16 +0200 Subject: [PATCH] inomap: deterministically set root device We used to have "first Translate() wins". This is not deterministic, as the LOOKUP for the root directory does not seem to reach us, so the first user LOOKUP would win, which may be on a mountpoint. --- internal/fusefrontend/root_node.go | 12 +++++++++++- internal/fusefrontend_reverse/root_node.go | 17 +++++++++++------ internal/inomap/inomap.go | 13 +++++++++++-- internal/inomap/inomap_test.go | 17 ++++++----------- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/internal/fusefrontend/root_node.go b/internal/fusefrontend/root_node.go index 7d37520..7221be6 100644 --- a/internal/fusefrontend/root_node.go +++ b/internal/fusefrontend/root_node.go @@ -62,18 +62,28 @@ type RootNode struct { } func NewRootNode(args Args, c *contentenc.ContentEnc, n *nametransform.NameTransform) *RootNode { + var rootDev uint64 + var st syscall.Stat_t + if err := syscall.Stat(args.Cipherdir, &st); err != nil { + tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, err) + } else { + rootDev = uint64(st.Dev) + } + if len(args.Exclude) > 0 { tlog.Warn.Printf("Forward mode does not support -exclude") } + ivLen := nametransform.DirIVLen if args.PlaintextNames { ivLen = 0 } + rn := &RootNode{ args: args, nameTransform: n, contentEnc: c, - inoMap: inomap.New(), + inoMap: inomap.New(rootDev), dirCache: dirCache{ivLen: ivLen}, quirks: syscallcompat.DetectQuirks(args.Cipherdir), } diff --git a/internal/fusefrontend_reverse/root_node.go b/internal/fusefrontend_reverse/root_node.go index d4c1e37..e15ddb0 100644 --- a/internal/fusefrontend_reverse/root_node.go +++ b/internal/fusefrontend_reverse/root_node.go @@ -2,10 +2,13 @@ package fusefrontend_reverse import ( "log" + "os" "path/filepath" "strings" "syscall" + "github.com/rfjakob/gocryptfs/v2/internal/exitcodes" + "github.com/rfjakob/gocryptfs/v2/internal/tlog" "golang.org/x/sys/unix" @@ -46,12 +49,14 @@ type RootNode struct { // ReverseFS provides an encrypted view. func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n *nametransform.NameTransform) *RootNode { var rootDev uint64 - if args.OneFileSystem { - var st syscall.Stat_t - err := syscall.Stat(args.Cipherdir, &st) - if err != nil { - log.Panicf("Could not stat backing directory %q: %v", args.Cipherdir, err) + var st syscall.Stat_t + if err := syscall.Stat(args.Cipherdir, &st); err != nil { + tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, err) + if args.OneFileSystem { + tlog.Fatal.Printf("This is a fatal error in combination with -one-file-system") + os.Exit(exitcodes.CipherDir) } + } else { rootDev = uint64(st.Dev) } @@ -59,7 +64,7 @@ func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n *nametransf args: args, nameTransform: n, contentEnc: c, - inoMap: inomap.New(), + inoMap: inomap.New(rootDev), rootDev: rootDev, } if len(args.Exclude) > 0 || len(args.ExcludeWildcard) > 0 || len(args.ExcludeFrom) > 0 { diff --git a/internal/inomap/inomap.go b/internal/inomap/inomap.go index 97f9b61..997ea9b 100644 --- a/internal/inomap/inomap.go +++ b/internal/inomap/inomap.go @@ -49,13 +49,22 @@ type InoMap struct { } // New returns a new InoMap. -func New() *InoMap { - return &InoMap{ +// Inode numbers on device `rootDev` will be passed through as-is. +// If `rootDev` is zero, the first Translate() call decides the effective +// rootDev. +func New(rootDev uint64) *InoMap { + m := &InoMap{ namespaceMap: make(map[namespaceData]uint16), namespaceNext: 0, spillMap: make(map[QIno]uint64), spillNext: 0, } + if rootDev > 0 { + // Reserve namespace 0 for rootDev + m.namespaceMap[namespaceData{rootDev, 0}] = 0 + m.namespaceNext = 1 + } + return m } var spillWarn sync.Once diff --git a/internal/inomap/inomap_test.go b/internal/inomap/inomap_test.go index 6fbb4ef..9ec2932 100644 --- a/internal/inomap/inomap_test.go +++ b/internal/inomap/inomap_test.go @@ -6,7 +6,7 @@ import ( ) func TestTranslate(t *testing.T) { - m := New() + m := New(0) q := QIno{Ino: 1} out := m.Translate(q) if out != 1 { @@ -25,12 +25,7 @@ func TestTranslate(t *testing.T) { func TestTranslateStress(t *testing.T) { const baseDev = 12345 - m := New() - - // Make sure baseDev gets namespace id zero - var q QIno - q.Dev = baseDev - m.Translate(q) + m := New(baseDev) var wg sync.WaitGroup wg.Add(4) @@ -100,7 +95,7 @@ func TestTranslateStress(t *testing.T) { } func TestSpill(t *testing.T) { - m := New() + m := New(0) var q QIno q.Ino = maxPassthruIno + 1 out1 := m.Translate(q) @@ -119,7 +114,7 @@ func TestSpill(t *testing.T) { // TestUniqueness checks that unique (Dev, Flags, Ino) tuples get unique inode // numbers func TestUniqueness(t *testing.T) { - m := New() + m := New(0) var q QIno outMap := make(map[uint64]struct{}) for q.Dev = 0; q.Dev < 10; q.Dev++ { @@ -141,7 +136,7 @@ func TestUniqueness(t *testing.T) { } func BenchmarkTranslateSingleDev(b *testing.B) { - m := New() + m := New(0) var q QIno for n := 0; n < b.N; n++ { q.Ino = uint64(n % 1000) @@ -150,7 +145,7 @@ func BenchmarkTranslateSingleDev(b *testing.B) { } func BenchmarkTranslateManyDevs(b *testing.B) { - m := New() + m := New(0) var q QIno for n := 0; n < b.N; n++ { q.Dev = uint64(n % 10)