From db31d21e9cab31dff152082a4e88217d447970c4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 1 Oct 2019 09:43:36 -0700 Subject: filesystem: allow .fscrypt to be a symlink Support the case where the user has a read-only root filesystem (e.g. with OSTree) and had previously created a symlink /.fscrypt pointing to a writable location, so that login protectors can be created there. Resolves https://github.com/google/fscrypt/issues/131 --- filesystem/filesystem_test.go | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'filesystem/filesystem_test.go') diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go index 2394f68..50c3920 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -20,6 +20,7 @@ package filesystem import ( + "io/ioutil" "os" "path/filepath" "testing" @@ -109,6 +110,68 @@ func TestRemoveAllMetadata(t *testing.T) { } } +// Test that when MOUNTPOINT/.fscrypt is a pre-created symlink, fscrypt will +// create/delete the metadata at the location pointed to by the symlink. +// +// This is a helper function that is called twice: once to test an absolute +// symlink and once to test a relative symlink. +func testSetupWithSymlink(t *testing.T, mnt *Mount, symlinkTarget string, realDir string) { + rawBaseDir := filepath.Join(mnt.Path, baseDirName) + if err := os.Symlink(symlinkTarget, rawBaseDir); err != nil { + t.Fatal(err) + } + defer os.Remove(rawBaseDir) + + if err := mnt.Setup(); err != nil { + t.Fatal(err) + } + defer mnt.RemoveAllMetadata() + if err := mnt.CheckSetup(); err != nil { + t.Fatal(err) + } + if !isSymlink(rawBaseDir) { + t.Fatal("base dir should still be a symlink") + } + if !isDir(realDir) { + t.Fatal("real base dir should exist") + } + if err := mnt.RemoveAllMetadata(); err != nil { + t.Fatal(err) + } + if !isSymlink(rawBaseDir) { + t.Fatal("base dir should still be a symlink") + } + if isDir(realDir) { + t.Fatal("real base dir should no longer exist") + } +} + +func TestSetupWithAbsoluteSymlink(t *testing.T) { + mnt, err := getTestMount(t) + if err != nil { + t.Fatal(err) + } + tempDir, err := ioutil.TempDir("", "fscrypt") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + realDir := filepath.Join(tempDir, "realDir") + if realDir, err = filepath.Abs(realDir); err != nil { + t.Fatal(err) + } + testSetupWithSymlink(t, mnt, realDir, realDir) +} + +func TestSetupWithRelativeSymlink(t *testing.T) { + mnt, err := getTestMount(t) + if err != nil { + t.Fatal(err) + } + realDir := filepath.Join(mnt.Path, ".fscrypt-real") + testSetupWithSymlink(t, mnt, ".fscrypt-real", realDir) +} + // Adding a good Protector should succeed, adding a bad one should fail func TestAddProtector(t *testing.T) { mnt, err := getSetupMount(t) -- cgit v1.3 From 83828388b4195cb5b2de5dad937d19208c1fa7ff Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Wed, 23 Oct 2019 22:06:13 -0700 Subject: filesystem: Move test-only code to test files This makes it easier to understand which code is actually invoked by the command-line tool. --- filesystem/filesystem_test.go | 17 +++++++++++++++++ filesystem/path.go | 16 ---------------- 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'filesystem/filesystem_test.go') diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go index 50c3920..b85ead5 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -21,6 +21,7 @@ package filesystem import ( "io/ioutil" + "log" "os" "path/filepath" "testing" @@ -110,6 +111,22 @@ func TestRemoveAllMetadata(t *testing.T) { } } +// loggedLstat runs os.Lstat (doesn't dereference trailing symlink), but it logs +// the error if lstat returns any error other than nil or IsNotExist. +func loggedLstat(name string) (os.FileInfo, error) { + info, err := os.Lstat(name) + if err != nil && !os.IsNotExist(err) { + log.Print(err) + } + return info, err +} + +// isSymlink returns true if the path exists and is that of a symlink. +func isSymlink(path string) bool { + info, err := loggedLstat(path) + return err == nil && info.Mode()&os.ModeSymlink != 0 +} + // Test that when MOUNTPOINT/.fscrypt is a pre-created symlink, fscrypt will // create/delete the metadata at the location pointed to by the symlink. // diff --git a/filesystem/path.go b/filesystem/path.go index b65bcb9..5fd3fdf 100644 --- a/filesystem/path.go +++ b/filesystem/path.go @@ -56,16 +56,6 @@ func loggedStat(name string) (os.FileInfo, error) { return info, err } -// loggedLstat runs os.Lstat (doesn't dereference trailing symlink), but it logs -// the error if lstat returns any error other than nil or IsNotExist. -func loggedLstat(name string) (os.FileInfo, error) { - info, err := os.Lstat(name) - if err != nil && !os.IsNotExist(err) { - log.Print(err) - } - return info, err -} - // isDir returns true if the path exists and is that of a directory. func isDir(path string) bool { info, err := loggedStat(path) @@ -78,12 +68,6 @@ func isDevice(path string) bool { return err == nil && info.Mode()&os.ModeDevice != 0 } -// isSymlink returns true if the path exists and is that of a symlink. -func isSymlink(path string) bool { - info, err := loggedLstat(path) - return err == nil && info.Mode()&os.ModeSymlink != 0 -} - // isDirCheckPerm returns true if the path exists and is a directory. If the // specified permissions and sticky bit of mode do not match the path, an error // is logged. -- cgit v1.3