Skip to content

Commit b32ee0a

Browse files
cuonglmianlancetaylor
authored andcommitted
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 <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 3b66c00 commit b32ee0a

File tree

3 files changed

+60
-4
lines changed

3 files changed

+60
-4
lines changed

src/path/filepath/path_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"runtime"
1616
"sort"
1717
"strings"
18+
"syscall"
1819
"testing"
1920
)
2021

@@ -1371,3 +1372,39 @@ func TestWalkSymlink(t *testing.T) {
13711372
testenv.MustHaveSymlink(t)
13721373
testWalkSymlink(t, os.Symlink)
13731374
}
1375+
1376+
func TestIssue29372(t *testing.T) {
1377+
f, err := ioutil.TempFile("", "issue29372")
1378+
if err != nil {
1379+
t.Fatal(err)
1380+
}
1381+
f.Close()
1382+
path := f.Name()
1383+
defer os.Remove(path)
1384+
1385+
isWin := runtime.GOOS == "windows"
1386+
pathSeparator := string(filepath.Separator)
1387+
tests := []struct {
1388+
path string
1389+
skip bool
1390+
}{
1391+
{path + strings.Repeat(pathSeparator, 1), false},
1392+
{path + strings.Repeat(pathSeparator, 2), false},
1393+
{path + strings.Repeat(pathSeparator, 1) + ".", false},
1394+
{path + strings.Repeat(pathSeparator, 2) + ".", false},
1395+
// windows.GetFinalPathNameByHandle return the directory part with trailing dot dot
1396+
// C:\path\to\existing_dir\existing_file\.. returns C:\path\to\existing_dir
1397+
{path + strings.Repeat(pathSeparator, 1) + "..", isWin},
1398+
{path + strings.Repeat(pathSeparator, 2) + "..", isWin},
1399+
}
1400+
1401+
for i, test := range tests {
1402+
if test.skip {
1403+
continue
1404+
}
1405+
_, err = filepath.EvalSymlinks(test.path)
1406+
if err != syscall.ENOTDIR {
1407+
t.Fatalf("test#%d: want %q, got %q", i, syscall.ENOTDIR, err)
1408+
}
1409+
}
1410+
}

src/path/filepath/symlink.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import (
88
"errors"
99
"os"
1010
"runtime"
11+
"syscall"
1112
)
1213

1314
func walkSymlinks(path string) (string, error) {
1415
volLen := volumeNameLen(path)
16+
pathSeparator := string(os.PathSeparator)
17+
1518
if volLen < len(path) && os.IsPathSeparator(path[volLen]) {
1619
volLen++
1720
}
@@ -50,7 +53,7 @@ func walkSymlinks(path string) (string, error) {
5053
}
5154
if r < volLen {
5255
if len(dest) > volLen {
53-
dest += string(os.PathSeparator)
56+
dest += pathSeparator
5457
}
5558
dest += ".."
5659
} else {
@@ -62,7 +65,7 @@ func walkSymlinks(path string) (string, error) {
6265
// Ordinary path component. Add it to result.
6366

6467
if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
65-
dest += string(os.PathSeparator)
68+
dest += pathSeparator
6669
}
6770

6871
dest += path[start:end]
@@ -76,7 +79,7 @@ func walkSymlinks(path string) (string, error) {
7679

7780
if fi.Mode()&os.ModeSymlink == 0 {
7881
if !fi.Mode().IsDir() && end < len(path) {
79-
return "", os.ErrNotExist
82+
return "", syscall.ENOTDIR
8083
}
8184
continue
8285
}

src/path/filepath/symlink_windows.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,18 @@ func evalSymlinksUsingGetFinalPathNameByHandle(path string) (string, error) {
159159
return "", errors.New("GetFinalPathNameByHandle returned unexpected path=" + s)
160160
}
161161

162+
func symlinkOrDir(path string) (string, error) {
163+
fi, err := os.Lstat(path)
164+
if err != nil {
165+
return "", err
166+
}
167+
168+
if fi.Mode()&os.ModeSymlink == 0 && !fi.Mode().IsDir() {
169+
return "", syscall.ENOTDIR
170+
}
171+
return path, nil
172+
}
173+
162174
func samefile(path1, path2 string) bool {
163175
fi1, err := os.Lstat(path1)
164176
if err != nil {
@@ -176,7 +188,11 @@ func evalSymlinks(path string) (string, error) {
176188
if err != nil {
177189
newpath2, err2 := evalSymlinksUsingGetFinalPathNameByHandle(path)
178190
if err2 == nil {
179-
return toNorm(newpath2, normBase)
191+
normPath, toNormErr := toNorm(newpath2, normBase)
192+
if toNormErr != nil {
193+
return "", toNormErr
194+
}
195+
return symlinkOrDir(normPath)
180196
}
181197
return "", err
182198
}

0 commit comments

Comments
 (0)