From 546959fe6d9b3448a289b0ecfc402bfe9c566dc3 Mon Sep 17 00:00:00 2001 From: Ibrahim AshShohail Date: Wed, 2 Aug 2017 15:32:20 +0300 Subject: [PATCH 1/6] internal/fs: add EqualPaths() Signed-off-by: Ibrahim AshShohail --- context.go | 4 ++-- internal/fs/fs.go | 31 ++++++++++++++++++++++++++++++- internal/fs/fs_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index f149df106b..84476a1d82 100644 --- a/context.go +++ b/context.go @@ -179,7 +179,7 @@ func (c *Ctx) DetectProjectGOPATH(p *Project) (string, error) { pGOPATH, perr := c.detectGOPATH(p.AbsRoot) // If p.AbsRoot is a not symlink, attempt to detect GOPATH for p.AbsRoot only. - if p.AbsRoot == p.ResolvedAbsRoot { + if equal, _ := fs.EqualPaths(p.AbsRoot, p.ResolvedAbsRoot); equal { return pGOPATH, perr } @@ -191,7 +191,7 @@ func (c *Ctx) DetectProjectGOPATH(p *Project) (string, error) { } // If pGOPATH equals rGOPATH, then both are within the same GOPATH. - if pGOPATH == rGOPATH { + if equal, _ := fs.EqualPaths(pGOPATH, rGOPATH); equal { return "", errors.Errorf("both %s and %s are in the same GOPATH %s", p.AbsRoot, p.ResolvedAbsRoot, pGOPATH) } diff --git a/internal/fs/fs.go b/internal/fs/fs.go index 90d6f87908..69a1fe0536 100644 --- a/internal/fs/fs.go +++ b/internal/fs/fs.go @@ -100,6 +100,36 @@ func HasFilepathPrefix(path, prefix string) bool { return true } +// EqualPaths compares the paths passed to check if the are equivalent. +// It respects the case-sensitivity of the underlaying filesysyems. +func EqualPaths(p1, p2 string) (bool, error) { + p1 = filepath.Clean(p1) + p2 = filepath.Clean(p2) + + if isDir, err := IsDir(p2); err != nil && !strings.HasSuffix(err.Error(), "is not a directory") { + return false, err + } else if !isDir { + // If p2 is not a directory, compare the bases of the paths to ensure + // they are equivalent. + var p1Base, p2Base string + + p1, p1Base = filepath.Split(p1) + p2, p2Base = filepath.Split(p2) + + if isCaseSensitiveFilesystem(p1) { + if p1Base != p2Base { + return false, nil + } + } else { + if strings.ToLower(p1Base) != strings.ToLower(p2Base) { + return false, nil + } + } + } + + return len(p1) == len(p2) && HasFilepathPrefix(p1, p2), nil +} + // RenameWithFallback attempts to rename a file or directory, but falls back to // copying in the event of a cross-device link error. If the fallback copy // succeeds, src is still removed, emulating normal rename behavior. @@ -343,7 +373,6 @@ func cloneSymlink(sl, dst string) error { // IsDir determines is the path given is a directory or not. func IsDir(name string) (bool, error) { - // TODO: lstat? fi, err := os.Stat(name) if err != nil { return false, err diff --git a/internal/fs/fs_test.go b/internal/fs/fs_test.go index e16f772c9c..f6849fddd6 100644 --- a/internal/fs/fs_test.go +++ b/internal/fs/fs_test.go @@ -134,6 +134,46 @@ func TestHasFilepathPrefix_Files(t *testing.T) { } } +func TestEqualPaths(t *testing.T) { + h := test.NewHelper(t) + h.TempDir("dir") + h.TempDir("dir2") + + h.TempFile("file", "") + h.TempFile("file2", "") + + h.TempDir("DIR") + h.TempFile("FILE", "") + + // caseInsensitive flag ensures that these cases are only equivalent on a + // case-insensitive filesystem. + caseInsensitive := isCaseSensitiveFilesystem(h.Path("dir")) + + testcases := []struct { + p1, p2 string + want bool + err bool + }{ + {h.Path("dir"), h.Path("dir"), true, false}, + {h.Path("file"), h.Path("file"), true, false}, + {h.Path("dir"), h.Path("dir2"), false, false}, + {h.Path("file"), h.Path("file2"), false, false}, + {h.Path("dir"), h.Path("file"), false, false}, + {h.Path("dir"), h.Path("DIR"), caseInsensitive, false}, + {strings.ToLower(h.Path("dir")), strings.ToUpper(h.Path("dir")), caseInsensitive, true}, + } + + for _, tc := range testcases { + got, err := EqualPaths(tc.p1, tc.p2) + if err != nil && !tc.err { + t.Error("unexpected error:", err) + } + if got != tc.want { + t.Errorf("expected EqualPaths(%q, %q) to be %t, got %t", tc.p1, tc.p2, tc.want, got) + } + } +} + func TestRenameWithFallback(t *testing.T) { dir, err := ioutil.TempDir("", "dep") if err != nil { From 8878146855a3223925e9ec0eb54d5507ff94181f Mon Sep 17 00:00:00 2001 From: Ibrahim AshShohail Date: Fri, 4 Aug 2017 14:39:34 +0300 Subject: [PATCH 2/6] internal/fs: fix isCaseSensitiveFilesystem bug isCaseSensitiveFilesystem returns true when os.Stat fails on the dir passed. This caused HasFilepathPrefix to always treat *nix systems as case-sensitive, since it passed a relative path (missing the root) to isCaseSensitiveFilesystem. This commit updates isCaseSensitiveFilesystem to return an error in addtion to the boolean check. Also, HasFilepathPrefix now passes absolute paths to isCaseSensitiveFilesystem. Signed-off-by: Ibrahim AshShohail --- internal/fs/fs.go | 72 ++++++++++++++++++++++++++---------------- internal/fs/fs_test.go | 40 +++++++++++++---------- 2 files changed, 69 insertions(+), 43 deletions(-) diff --git a/internal/fs/fs.go b/internal/fs/fs.go index 69a1fe0536..01dadf1bda 100644 --- a/internal/fs/fs.go +++ b/internal/fs/fs.go @@ -45,13 +45,11 @@ func HasFilepathPrefix(path, prefix string) bool { return false } - // because filepath.Join("c:","dir") returns "c:dir", we have to manually add path separator to drive letters - if strings.HasSuffix(vnPath, ":") { - vnPath += string(os.PathSeparator) - } - if strings.HasSuffix(vnPrefix, ":") { - vnPrefix += string(os.PathSeparator) - } + // Because filepath.Join("c:","dir") returns "c:dir", we have to manually + // add path separator to drive letters. Also, we need to set the path root + // on *nix systems, since filepath.Join("", "dir") returns a relative path. + vnPath += string(os.PathSeparator) + vnPrefix += string(os.PathSeparator) var dn string @@ -74,7 +72,7 @@ func HasFilepathPrefix(path, prefix string) bool { return false } - // d,p are initialized with "" on *nix and volume name on Windows + // d,p are initialized with "/" on *nix and volume name on Windows d := vnPath p := vnPrefix @@ -84,7 +82,11 @@ func HasFilepathPrefix(path, prefix string) bool { // something like ext4 filesystem mounted on FAT // mountpoint, mounted on ext4 filesystem, i.e. the // problematic filesystem is not the last one. - if isCaseSensitiveFilesystem(filepath.Join(d, dirs[i])) { + caseSensitive, err := isCaseSensitiveFilesystem(filepath.Join(d, dirs[i])) + if err != nil { + return false + } + if caseSensitive { d = filepath.Join(d, dirs[i]) p = filepath.Join(p, prefixes[i]) } else { @@ -106,28 +108,45 @@ func EqualPaths(p1, p2 string) (bool, error) { p1 = filepath.Clean(p1) p2 = filepath.Clean(p2) - if isDir, err := IsDir(p2); err != nil && !strings.HasSuffix(err.Error(), "is not a directory") { - return false, err - } else if !isDir { - // If p2 is not a directory, compare the bases of the paths to ensure - // they are equivalent. - var p1Base, p2Base string + fi1, err := os.Stat(p1) + if err != nil { + return false, errors.Wrapf(err, "could not check for path equivalence") + } + fi2, err := os.Stat(p2) + if err != nil { + return false, errors.Wrapf(err, "could not check for path equivalence") + } - p1, p1Base = filepath.Split(p1) - p2, p2Base = filepath.Split(p2) + p1Filename, p2Filename := "", "" - if isCaseSensitiveFilesystem(p1) { - if p1Base != p2Base { + if !fi1.IsDir() { + p1, p1Filename = filepath.Split(p1) + } + if !fi2.IsDir() { + p2, p2Filename = filepath.Split(p2) + } + + if !HasFilepathPrefix(p1, p2) || !HasFilepathPrefix(p2, p1) { + return false, nil + } + + if p1Filename != "" || p2Filename != "" { + caseSensitive, err := isCaseSensitiveFilesystem(filepath.Join(p1, p1Filename)) + if err != nil { + return false, errors.Wrap(err, "could not check for filesystem case-sensitivity") + } + if caseSensitive { + if p1Filename != p2Filename { return false, nil } } else { - if strings.ToLower(p1Base) != strings.ToLower(p2Base) { + if strings.ToLower(p1Filename) != strings.ToLower(p2Filename) { return false, nil } } } - return len(p1) == len(p2) && HasFilepathPrefix(p1, p2), nil + return true, nil } // RenameWithFallback attempts to rename a file or directory, but falls back to @@ -189,21 +208,20 @@ func renameByCopy(src, dst string) error { // If the input directory is such that the last component is composed // exclusively of case-less codepoints (e.g. numbers), this function will // return false. -func isCaseSensitiveFilesystem(dir string) bool { - alt := filepath.Join(filepath.Dir(dir), - genTestFilename(filepath.Base(dir))) +func isCaseSensitiveFilesystem(dir string) (bool, error) { + alt := filepath.Join(filepath.Dir(dir), genTestFilename(filepath.Base(dir))) dInfo, err := os.Stat(dir) if err != nil { - return true + return false, errors.Wrap(err, "could not determine the case-sensitivity of the filesystem") } aInfo, err := os.Stat(alt) if err != nil { - return true + return false, errors.Wrap(err, "could not determine the case-sensitivity of the filesystem") } - return !os.SameFile(dInfo, aInfo) + return !os.SameFile(dInfo, aInfo), nil } // genTestFilename returns a string with at most one rune case-flipped. diff --git a/internal/fs/fs_test.go b/internal/fs/fs_test.go index f6849fddd6..a52cefacb6 100644 --- a/internal/fs/fs_test.go +++ b/internal/fs/fs_test.go @@ -145,22 +145,24 @@ func TestEqualPaths(t *testing.T) { h.TempDir("DIR") h.TempFile("FILE", "") - // caseInsensitive flag ensures that these cases are only equivalent on a - // case-insensitive filesystem. - caseInsensitive := isCaseSensitiveFilesystem(h.Path("dir")) - testcases := []struct { - p1, p2 string - want bool - err bool + p1, p2 string + caseSensitiveEquivalent bool + caseInensitiveEquivalent bool + err bool }{ - {h.Path("dir"), h.Path("dir"), true, false}, - {h.Path("file"), h.Path("file"), true, false}, - {h.Path("dir"), h.Path("dir2"), false, false}, - {h.Path("file"), h.Path("file2"), false, false}, - {h.Path("dir"), h.Path("file"), false, false}, - {h.Path("dir"), h.Path("DIR"), caseInsensitive, false}, - {strings.ToLower(h.Path("dir")), strings.ToUpper(h.Path("dir")), caseInsensitive, true}, + {h.Path("dir"), h.Path("dir"), true, true, false}, + {h.Path("file"), h.Path("file"), true, true, false}, + {h.Path("dir"), h.Path("dir2"), false, false, false}, + {h.Path("file"), h.Path("file2"), false, false, false}, + {h.Path("dir"), h.Path("file"), false, false, false}, + {h.Path("dir"), h.Path("DIR"), false, true, false}, + {strings.ToLower(h.Path("dir")), strings.ToUpper(h.Path("dir")), false, true, true}, + } + + caseSensitive, err := isCaseSensitiveFilesystem(h.Path("dir")) + if err != nil { + t.Fatal("unexpcted error:", err) } for _, tc := range testcases { @@ -168,8 +170,14 @@ func TestEqualPaths(t *testing.T) { if err != nil && !tc.err { t.Error("unexpected error:", err) } - if got != tc.want { - t.Errorf("expected EqualPaths(%q, %q) to be %t, got %t", tc.p1, tc.p2, tc.want, got) + if caseSensitive { + if tc.caseSensitiveEquivalent != got { + t.Errorf("expected EqualPaths(%q, %q) to be %t on case-sensitive filesystem, got %t", tc.p1, tc.p2, tc.caseSensitiveEquivalent, got) + } + } else { + if tc.caseInensitiveEquivalent != got { + t.Errorf("expected EqualPaths(%q, %q) to be %t on case-insensitive filesystem, got %t", tc.p1, tc.p2, tc.caseInensitiveEquivalent, got) + } } } } From d94a0f3b370ebbb1a3c2632f6b1294ce961e1381 Mon Sep 17 00:00:00 2001 From: Ibrahim AshShohail Date: Fri, 4 Aug 2017 16:21:05 +0300 Subject: [PATCH 3/6] fix tests Signed-off-by: Ibrahim AshShohail --- context_test.go | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/context_test.go b/context_test.go index e522d25b4d..503ed1beaf 100644 --- a/context_test.go +++ b/context_test.go @@ -314,23 +314,16 @@ func TestDetectProjectGOPATH(t *testing.T) { h := test.NewHelper(t) defer h.Cleanup() - h.TempDir("go") - h.TempDir("go-two") + h.TempDir(filepath.Join("sym", "symlink")) + h.TempDir(filepath.Join("go", "src", "sym", "path")) + h.TempDir(filepath.Join("go", "src", "real", "path")) + h.TempDir(filepath.Join("go-two", "src", "real", "path")) + h.TempDir(filepath.Join("go-two", "src", "sym")) ctx := &Ctx{ GOPATHs: []string{h.Path("go"), h.Path("go-two")}, } - h.TempDir("go/src/real/path") - - // Another directory used as a GOPATH - h.TempDir("go-two/src/sym") - - h.TempDir(filepath.Join(".", "sym/symlink")) // Directory for symlinks - h.TempDir(filepath.Join("go", "src", "sym", "path")) - h.TempDir(filepath.Join("go", "src", " real", "path")) - h.TempDir(filepath.Join("go-two", "src", "real", "path")) - testcases := []struct { name string root string @@ -383,7 +376,7 @@ func TestDetectProjectGOPATH(t *testing.T) { { name: "AbsRoot-is-a-symlink-to-ResolvedAbsRoot", root: filepath.Join(h.Path("."), "sym", "symlink"), - resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", " real", "path"), + resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"), GOPATH: ctx.GOPATHs[0], }, } @@ -412,36 +405,33 @@ func TestDetectGOPATH(t *testing.T) { th := test.NewHelper(t) defer th.Cleanup() - th.TempDir("go") - th.TempDir("gotwo") + th.TempDir(filepath.Join("code", "src", "github.com", "username", "package")) + th.TempDir(filepath.Join("go", "src", "github.com", "username", "package")) + th.TempDir(filepath.Join("gotwo", "src", "github.com", "username", "package")) ctx := &Ctx{GOPATHs: []string{ th.Path("go"), th.Path("gotwo"), }} - th.TempDir(filepath.Join("code", "src", "github.com", "username", "package")) - th.TempDir(filepath.Join("go", "src", "github.com", "username", "package")) - th.TempDir(filepath.Join("gotwo", "src", "github.com", "username", "package")) - testcases := []struct { GOPATH string path string err bool }{ - {th.Path("go"), filepath.Join(th.Path("go"), "src/github.com/username/package"), false}, - {th.Path("go"), filepath.Join(th.Path("go"), "src/github.com/username/package"), false}, - {th.Path("gotwo"), filepath.Join(th.Path("gotwo"), "src/github.com/username/package"), false}, - {"", filepath.Join(th.Path("."), "code/src/github.com/username/package"), true}, + {th.Path("go"), th.Path(filepath.Join("go", "src", "github.com", "username", "package")), false}, + {th.Path("go"), th.Path(filepath.Join("go", "src", "github.com", "username", "package")), false}, + {th.Path("gotwo"), th.Path(filepath.Join("gotwo", "src", "github.com", "username", "package")), false}, + {"", th.Path(filepath.Join("code", "src", "github.com", "username", "package")), true}, } for _, tc := range testcases { GOPATH, err := ctx.detectGOPATH(tc.path) if tc.err && err == nil { - t.Error("Expected error but got none") + t.Error("expected error but got none") } if GOPATH != tc.GOPATH { - t.Errorf("Expected GOPATH to be %s, got %s", GOPATH, tc.GOPATH) + t.Errorf("expected GOPATH to be %s, got %s", GOPATH, tc.GOPATH) } } } From a0e33dd19b39a31c5008af3e0285336e50c90efd Mon Sep 17 00:00:00 2001 From: Ibrahim AshShohail Date: Tue, 8 Aug 2017 21:54:08 +0300 Subject: [PATCH 4/6] internal/fs: rename EqualPaths to EquivalentPaths Signed-off-by: Ibrahim AshShohail --- context.go | 4 ++-- internal/fs/fs.go | 4 ++-- internal/fs/fs_test.go | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/context.go b/context.go index 84476a1d82..4426be4c46 100644 --- a/context.go +++ b/context.go @@ -179,7 +179,7 @@ func (c *Ctx) DetectProjectGOPATH(p *Project) (string, error) { pGOPATH, perr := c.detectGOPATH(p.AbsRoot) // If p.AbsRoot is a not symlink, attempt to detect GOPATH for p.AbsRoot only. - if equal, _ := fs.EqualPaths(p.AbsRoot, p.ResolvedAbsRoot); equal { + if equal, _ := fs.EquivalentPaths(p.AbsRoot, p.ResolvedAbsRoot); equal { return pGOPATH, perr } @@ -191,7 +191,7 @@ func (c *Ctx) DetectProjectGOPATH(p *Project) (string, error) { } // If pGOPATH equals rGOPATH, then both are within the same GOPATH. - if equal, _ := fs.EqualPaths(pGOPATH, rGOPATH); equal { + if equal, _ := fs.EquivalentPaths(pGOPATH, rGOPATH); equal { return "", errors.Errorf("both %s and %s are in the same GOPATH %s", p.AbsRoot, p.ResolvedAbsRoot, pGOPATH) } diff --git a/internal/fs/fs.go b/internal/fs/fs.go index 01dadf1bda..2e8b11656a 100644 --- a/internal/fs/fs.go +++ b/internal/fs/fs.go @@ -102,9 +102,9 @@ func HasFilepathPrefix(path, prefix string) bool { return true } -// EqualPaths compares the paths passed to check if the are equivalent. +// EquivalentPaths compares the paths passed to check if the are equivalent. // It respects the case-sensitivity of the underlaying filesysyems. -func EqualPaths(p1, p2 string) (bool, error) { +func EquivalentPaths(p1, p2 string) (bool, error) { p1 = filepath.Clean(p1) p2 = filepath.Clean(p2) diff --git a/internal/fs/fs_test.go b/internal/fs/fs_test.go index a52cefacb6..3c4c38596d 100644 --- a/internal/fs/fs_test.go +++ b/internal/fs/fs_test.go @@ -134,7 +134,7 @@ func TestHasFilepathPrefix_Files(t *testing.T) { } } -func TestEqualPaths(t *testing.T) { +func TestEquivalentPaths(t *testing.T) { h := test.NewHelper(t) h.TempDir("dir") h.TempDir("dir2") @@ -166,17 +166,17 @@ func TestEqualPaths(t *testing.T) { } for _, tc := range testcases { - got, err := EqualPaths(tc.p1, tc.p2) + got, err := EquivalentPaths(tc.p1, tc.p2) if err != nil && !tc.err { t.Error("unexpected error:", err) } if caseSensitive { if tc.caseSensitiveEquivalent != got { - t.Errorf("expected EqualPaths(%q, %q) to be %t on case-sensitive filesystem, got %t", tc.p1, tc.p2, tc.caseSensitiveEquivalent, got) + t.Errorf("expected EquivalentPaths(%q, %q) to be %t on case-sensitive filesystem, got %t", tc.p1, tc.p2, tc.caseSensitiveEquivalent, got) } } else { if tc.caseInensitiveEquivalent != got { - t.Errorf("expected EqualPaths(%q, %q) to be %t on case-insensitive filesystem, got %t", tc.p1, tc.p2, tc.caseInensitiveEquivalent, got) + t.Errorf("expected EquivalentPaths(%q, %q) to be %t on case-insensitive filesystem, got %t", tc.p1, tc.p2, tc.caseInensitiveEquivalent, got) } } } From d93ac9874d6809e6a44412c7ed2d8291b53d86dc Mon Sep 17 00:00:00 2001 From: Ibrahim AshShohail Date: Tue, 15 Aug 2017 07:44:51 +0300 Subject: [PATCH 5/6] fix typos Signed-off-by: Ibrahim AshShohail --- internal/fs/fs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/fs/fs.go b/internal/fs/fs.go index 2e8b11656a..9b0525e6ad 100644 --- a/internal/fs/fs.go +++ b/internal/fs/fs.go @@ -102,8 +102,8 @@ func HasFilepathPrefix(path, prefix string) bool { return true } -// EquivalentPaths compares the paths passed to check if the are equivalent. -// It respects the case-sensitivity of the underlaying filesysyems. +// EquivalentPaths compares the paths passed to check if they are equivalent. +// It respects the case-sensitivity of the underlying filesysyems. func EquivalentPaths(p1, p2 string) (bool, error) { p1 = filepath.Clean(p1) p2 = filepath.Clean(p2) From 67f83fc6938f540922a3037ff090490165357253 Mon Sep 17 00:00:00 2001 From: Ibrahim AshShohail Date: Mon, 21 Aug 2017 22:06:27 +0300 Subject: [PATCH 6/6] internal/fs: fix bug in isCaseSensitiveFilesystem Signed-off-by: Ibrahim AshShohail --- cmd/dep/gopath_scanner.go | 2 +- context.go | 12 ++++++++++-- internal/fs/fs.go | 25 +++++++++++++++++-------- internal/fs/fs_test.go | 17 +++++++++++++---- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/cmd/dep/gopath_scanner.go b/cmd/dep/gopath_scanner.go index 99dbb1f6ed..48d3189ac9 100644 --- a/cmd/dep/gopath_scanner.go +++ b/cmd/dep/gopath_scanner.go @@ -134,7 +134,7 @@ func (g *gopathScanner) overlay(rootM *dep.Manifest, rootL *dep.Lock) { } func trimPathPrefix(p1, p2 string) string { - if fs.HasFilepathPrefix(p1, p2) { + if isPrefix, _ := fs.HasFilepathPrefix(p1, p2); isPrefix { return p1[len(p2):] } return p1 diff --git a/context.go b/context.go index 4426be4c46..2227be8a0a 100644 --- a/context.go +++ b/context.go @@ -210,7 +210,11 @@ func (c *Ctx) DetectProjectGOPATH(p *Project) (string, error) { // detectGOPATH detects the GOPATH for a given path from ctx.GOPATHs. func (c *Ctx) detectGOPATH(path string) (string, error) { for _, gp := range c.GOPATHs { - if fs.HasFilepathPrefix(path, gp) { + isPrefix, err := fs.HasFilepathPrefix(path, gp) + if err != nil { + return "", errors.Wrap(err, "failed to detect GOPATH") + } + if isPrefix { return gp, nil } } @@ -221,7 +225,11 @@ func (c *Ctx) detectGOPATH(path string) (string, error) { // `$GOPATH/src/` prefix. Returns an error for paths equal to, or without this prefix. func (c *Ctx) ImportForAbs(path string) (string, error) { srcprefix := filepath.Join(c.GOPATH, "src") + string(filepath.Separator) - if fs.HasFilepathPrefix(path, srcprefix) { + isPrefix, err := fs.HasFilepathPrefix(path, srcprefix) + if err != nil { + return "", errors.Wrap(err, "failed to find import path") + } + if isPrefix { if len(path) <= len(srcprefix) { return "", errors.New("dep does not currently support using GOPATH/src as the project root") } diff --git a/internal/fs/fs.go b/internal/fs/fs.go index 9b0525e6ad..928cc0b4f5 100644 --- a/internal/fs/fs.go +++ b/internal/fs/fs.go @@ -30,7 +30,7 @@ import ( // same file. In that situation HasFilepathPrefix("/Foo/Bar", "/foo") // will return true. The implementation is *not* OS-specific, so a FAT32 // filesystem mounted on Linux will be handled correctly. -func HasFilepathPrefix(path, prefix string) bool { +func HasFilepathPrefix(path, prefix string) (bool, error) { // this function is more convoluted then ideal due to need for special // handling of volume name/drive letter on Windows. vnPath and vnPrefix // are first compared, and then used to initialize initial values of p and @@ -42,7 +42,7 @@ func HasFilepathPrefix(path, prefix string) bool { vnPath := strings.ToLower(filepath.VolumeName(path)) vnPrefix := strings.ToLower(filepath.VolumeName(prefix)) if vnPath != vnPrefix { - return false + return false, nil } // Because filepath.Join("c:","dir") returns "c:dir", we have to manually @@ -54,7 +54,7 @@ func HasFilepathPrefix(path, prefix string) bool { var dn string if isDir, err := IsDir(path); err != nil { - return false + return false, errors.Wrap(err, "failed to check filepath prefix") } else if isDir { dn = path } else { @@ -69,7 +69,7 @@ func HasFilepathPrefix(path, prefix string) bool { prefixes := strings.Split(prefix, string(os.PathSeparator))[1:] if len(prefixes) > len(dirs) { - return false + return false, nil } // d,p are initialized with "/" on *nix and volume name on Windows @@ -84,7 +84,7 @@ func HasFilepathPrefix(path, prefix string) bool { // problematic filesystem is not the last one. caseSensitive, err := isCaseSensitiveFilesystem(filepath.Join(d, dirs[i])) if err != nil { - return false + return false, errors.Wrap(err, "failed to check filepath prefix") } if caseSensitive { d = filepath.Join(d, dirs[i]) @@ -95,11 +95,11 @@ func HasFilepathPrefix(path, prefix string) bool { } if p != d { - return false + return false, nil } } - return true + return true, nil } // EquivalentPaths compares the paths passed to check if they are equivalent. @@ -126,7 +126,11 @@ func EquivalentPaths(p1, p2 string) (bool, error) { p2, p2Filename = filepath.Split(p2) } - if !HasFilepathPrefix(p1, p2) || !HasFilepathPrefix(p2, p1) { + if isPrefix1, err := HasFilepathPrefix(p1, p2); err != nil { + return false, errors.Wrap(err, "failed to check for path equivalence") + } else if isPrefix2, err := HasFilepathPrefix(p2, p1); err != nil { + return false, errors.Wrap(err, "failed to check for path equivalence") + } else if !isPrefix1 || !isPrefix2 { return false, nil } @@ -218,6 +222,11 @@ func isCaseSensitiveFilesystem(dir string) (bool, error) { aInfo, err := os.Stat(alt) if err != nil { + // If the file doesn't exists, assume we are on a case-sensitive filesystem. + if os.IsNotExist(err) { + return true, nil + } + return false, errors.Wrap(err, "could not determine the case-sensitivity of the filesystem") } diff --git a/internal/fs/fs_test.go b/internal/fs/fs_test.go index 3c4c38596d..920d925bb1 100644 --- a/internal/fs/fs_test.go +++ b/internal/fs/fs_test.go @@ -75,7 +75,11 @@ func TestHasFilepathPrefix(t *testing.T) { t.Fatal(err) } - if got := HasFilepathPrefix(c.path, c.prefix); c.want != got { + got, err := HasFilepathPrefix(c.path, c.prefix) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if c.want != got { t.Fatalf("dir: %q, prefix: %q, expected: %v, got: %v", c.path, c.prefix, c.want, got) } } @@ -122,13 +126,18 @@ func TestHasFilepathPrefix_Files(t *testing.T) { path string prefix string want bool + err bool }{ - {existingFile, filepath.Join(dir2), true}, - {nonExistingFile, filepath.Join(dir2), false}, + {existingFile, filepath.Join(dir2), true, false}, + {nonExistingFile, filepath.Join(dir2), false, true}, } for _, c := range cases { - if got := HasFilepathPrefix(c.path, c.prefix); c.want != got { + got, err := HasFilepathPrefix(c.path, c.prefix) + if err != nil && !c.err { + t.Fatalf("unexpected error: %s", err) + } + if c.want != got { t.Fatalf("dir: %q, prefix: %q, expected: %v, got: %v", c.path, c.prefix, c.want, got) } }