lib/fs: Properly handle case insensitive systems (fixes #1787, fixes #2739, fixes #5708)

With this change we emulate a case sensitive filesystem on top of
insensitive filesystems. This means we correctly pick up case-only renames
and throw a case conflict error when there would be multiple files differing
only in case.

This safety check has a small performance hit (about 20% more filesystem
operations when scanning for changes). The new advanced folder option
`caseSensitiveFS` can be used to disable the safety checks, retaining the
previous behavior on systems known to be fully case sensitive.

Co-authored-by: Jakob Borg <jakob@kastelo.net>
This commit is contained in:
Simon Frei
2020-07-28 11:15:11 +02:00
committed by Jakob Borg
co-authored by Jakob Borg
parent 21dd9d6b43
commit 932d8c69de
27 changed files with 1241 additions and 187 deletions
+7 -7
View File
@@ -24,9 +24,9 @@ func TestTraversesSymlink(t *testing.T) {
}
defer os.RemoveAll(tmpDir)
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
fs.MkdirAll("a/b/c", 0755)
if err = osutil.DebugSymlinkForTestsOnly(filepath.Join(fs.URI(), "a", "b"), filepath.Join(fs.URI(), "a", "l")); err != nil {
testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
testFs.MkdirAll("a/b/c", 0755)
if err = fs.DebugSymlinkForTestsOnly(testFs, testFs, filepath.Join("a", "b"), filepath.Join("a", "l")); err != nil {
if runtime.GOOS == "windows" {
t.Skip("Symlinks aren't working")
}
@@ -34,7 +34,7 @@ func TestTraversesSymlink(t *testing.T) {
}
// a/l -> b, so a/l/c should resolve by normal stat
info, err := fs.Lstat("a/l/c")
info, err := testFs.Lstat("a/l/c")
if err != nil {
t.Fatal("unexpected error", err)
}
@@ -64,7 +64,7 @@ func TestTraversesSymlink(t *testing.T) {
}
for _, tc := range cases {
if res := osutil.TraversesSymlink(fs, tc.name); tc.traverses == (res == nil) {
if res := osutil.TraversesSymlink(testFs, tc.name); tc.traverses == (res == nil) {
t.Errorf("TraversesSymlink(%q) = %v, should be %v", tc.name, res, tc.traverses)
}
}
@@ -78,8 +78,8 @@ func TestIssue4875(t *testing.T) {
defer os.RemoveAll(tmpDir)
testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
testFs.MkdirAll("a/b/c", 0755)
if err = osutil.DebugSymlinkForTestsOnly(filepath.Join(testFs.URI(), "a", "b"), filepath.Join(testFs.URI(), "a", "l")); err != nil {
testFs.MkdirAll(filepath.Join("a", "b", "c"), 0755)
if err = fs.DebugSymlinkForTestsOnly(testFs, testFs, filepath.Join("a", "b"), filepath.Join("a", "l")); err != nil {
if runtime.GOOS == "windows" {
t.Skip("Symlinks aren't working")
}