path/filepath: walkSymlinks: return correct error for file with trailing slash

Rather than return os.ErrNotExist for /path/to/existing_file/,
walkSymLinks now returns syscall.ENOTDIR.

This is consistent with behavior of os.Lstat.

Fixes #29372

Change-Id: Id5c471d901db04b2f35d60f60a81b2a0be93cae9
Reviewed-on: https://go-review.googlesource.com/c/155597
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
LE Manh Cuong 2018-12-21 11:21:02 +07:00 committed by Ian Lance Taylor
parent 3b66c00857
commit b32ee0a3c0
3 changed files with 60 additions and 4 deletions

View file

@ -15,6 +15,7 @@ import (
"runtime" "runtime"
"sort" "sort"
"strings" "strings"
"syscall"
"testing" "testing"
) )
@ -1371,3 +1372,39 @@ func TestWalkSymlink(t *testing.T) {
testenv.MustHaveSymlink(t) testenv.MustHaveSymlink(t)
testWalkSymlink(t, os.Symlink) testWalkSymlink(t, os.Symlink)
} }
func TestIssue29372(t *testing.T) {
f, err := ioutil.TempFile("", "issue29372")
if err != nil {
t.Fatal(err)
}
f.Close()
path := f.Name()
defer os.Remove(path)
isWin := runtime.GOOS == "windows"
pathSeparator := string(filepath.Separator)
tests := []struct {
path string
skip bool
}{
{path + strings.Repeat(pathSeparator, 1), false},
{path + strings.Repeat(pathSeparator, 2), false},
{path + strings.Repeat(pathSeparator, 1) + ".", false},
{path + strings.Repeat(pathSeparator, 2) + ".", false},
// windows.GetFinalPathNameByHandle return the directory part with trailing dot dot
// C:\path\to\existing_dir\existing_file\.. returns C:\path\to\existing_dir
{path + strings.Repeat(pathSeparator, 1) + "..", isWin},
{path + strings.Repeat(pathSeparator, 2) + "..", isWin},
}
for i, test := range tests {
if test.skip {
continue
}
_, err = filepath.EvalSymlinks(test.path)
if err != syscall.ENOTDIR {
t.Fatalf("test#%d: want %q, got %q", i, syscall.ENOTDIR, err)
}
}
}

View file

@ -8,10 +8,13 @@ import (
"errors" "errors"
"os" "os"
"runtime" "runtime"
"syscall"
) )
func walkSymlinks(path string) (string, error) { func walkSymlinks(path string) (string, error) {
volLen := volumeNameLen(path) volLen := volumeNameLen(path)
pathSeparator := string(os.PathSeparator)
if volLen < len(path) && os.IsPathSeparator(path[volLen]) { if volLen < len(path) && os.IsPathSeparator(path[volLen]) {
volLen++ volLen++
} }
@ -50,7 +53,7 @@ func walkSymlinks(path string) (string, error) {
} }
if r < volLen { if r < volLen {
if len(dest) > volLen { if len(dest) > volLen {
dest += string(os.PathSeparator) dest += pathSeparator
} }
dest += ".." dest += ".."
} else { } else {
@ -62,7 +65,7 @@ func walkSymlinks(path string) (string, error) {
// Ordinary path component. Add it to result. // Ordinary path component. Add it to result.
if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) { if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
dest += string(os.PathSeparator) dest += pathSeparator
} }
dest += path[start:end] dest += path[start:end]
@ -76,7 +79,7 @@ func walkSymlinks(path string) (string, error) {
if fi.Mode()&os.ModeSymlink == 0 { if fi.Mode()&os.ModeSymlink == 0 {
if !fi.Mode().IsDir() && end < len(path) { if !fi.Mode().IsDir() && end < len(path) {
return "", os.ErrNotExist return "", syscall.ENOTDIR
} }
continue continue
} }

View file

@ -159,6 +159,18 @@ func evalSymlinksUsingGetFinalPathNameByHandle(path string) (string, error) {
return "", errors.New("GetFinalPathNameByHandle returned unexpected path=" + s) return "", errors.New("GetFinalPathNameByHandle returned unexpected path=" + s)
} }
func symlinkOrDir(path string) (string, error) {
fi, err := os.Lstat(path)
if err != nil {
return "", err
}
if fi.Mode()&os.ModeSymlink == 0 && !fi.Mode().IsDir() {
return "", syscall.ENOTDIR
}
return path, nil
}
func samefile(path1, path2 string) bool { func samefile(path1, path2 string) bool {
fi1, err := os.Lstat(path1) fi1, err := os.Lstat(path1)
if err != nil { if err != nil {
@ -176,7 +188,11 @@ func evalSymlinks(path string) (string, error) {
if err != nil { if err != nil {
newpath2, err2 := evalSymlinksUsingGetFinalPathNameByHandle(path) newpath2, err2 := evalSymlinksUsingGetFinalPathNameByHandle(path)
if err2 == nil { if err2 == nil {
return toNorm(newpath2, normBase) normPath, toNormErr := toNorm(newpath2, normBase)
if toNormErr != nil {
return "", toNormErr
}
return symlinkOrDir(normPath)
} }
return "", err return "", err
} }