Avoid filepath.Abs

filepath.Abs is surprisingly expensive, it calls os.Getwd every
time, which involves multiple syscalls, a lock, and and allocations.
Use IsAbs and prefix matching instead.

Test: paths_test.go
Change-Id: Ia6cf34d6bef24c694702af1e7a6ff08ffd2d822b
This commit is contained in:
Colin Cross 2019-01-24 13:14:39 -08:00
parent 590b1ae37c
commit 7b3dcc31eb
2 changed files with 67 additions and 20 deletions

View File

@ -503,16 +503,9 @@ func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, e
return ret, err return ret, err
} }
abs, err := filepath.Abs(ret.String()) // absolute path already checked by validateSafePath
if err != nil { if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
return ret, err return ret, fmt.Errorf("source path %s is in output", ret.String())
}
buildroot, err := filepath.Abs(ctx.Config().buildDir)
if err != nil {
return ret, err
}
if strings.HasPrefix(abs, buildroot) {
return ret, fmt.Errorf("source path %s is in output", abs)
} }
return ret, err return ret, err
@ -526,16 +519,9 @@ func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error
return ret, err return ret, err
} }
abs, err := filepath.Abs(ret.String()) // absolute path already checked by validatePath
if err != nil { if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
return ret, err return ret, fmt.Errorf("source path %s is in output", ret.String())
}
buildroot, err := filepath.Abs(ctx.Config().buildDir)
if err != nil {
return ret, err
}
if strings.HasPrefix(abs, buildroot) {
return ret, fmt.Errorf("source path %s is in output", abs)
} }
return ret, nil return ret, nil

View File

@ -630,3 +630,64 @@ func TestMaybeRel(t *testing.T) {
}) })
} }
} }
func TestPathForSource(t *testing.T) {
testCases := []struct {
name string
buildDir string
src string
err string
}{
{
name: "normal",
buildDir: "out",
src: "a/b/c",
},
{
name: "abs",
buildDir: "out",
src: "/a/b/c",
err: "is outside directory",
},
{
name: "in out dir",
buildDir: "out",
src: "out/a/b/c",
err: "is in output",
},
}
funcs := []struct {
name string
f func(ctx PathContext, pathComponents ...string) (SourcePath, error)
}{
{"pathForSource", pathForSource},
{"safePathForSource", safePathForSource},
}
for _, f := range funcs {
t.Run(f.name, func(t *testing.T) {
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
testConfig := TestConfig(test.buildDir, nil)
ctx := &configErrorWrapper{config: testConfig}
_, err := f.f(ctx, test.src)
if len(ctx.errors) > 0 {
t.Fatalf("unexpected errors %v", ctx.errors)
}
if err != nil {
if test.err == "" {
t.Fatalf("unexpected error %q", err.Error())
} else if !strings.Contains(err.Error(), test.err) {
t.Fatalf("incorrect error, want substring %q got %q", test.err, err.Error())
}
} else {
if test.err != "" {
t.Fatalf("missing error %q", test.err)
}
}
})
}
})
}
}