aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/path
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/path')
-rw-r--r--libgo/go/path/filepath/example_unix_test.go15
-rw-r--r--libgo/go/path/filepath/match_test.go2
-rw-r--r--libgo/go/path/filepath/path.go15
-rw-r--r--libgo/go/path/filepath/path_test.go56
-rw-r--r--libgo/go/path/filepath/path_windows.go37
-rw-r--r--libgo/go/path/filepath/symlink.go191
-rw-r--r--libgo/go/path/filepath/symlink_unix.go9
-rw-r--r--libgo/go/path/filepath/symlink_windows.go10
8 files changed, 239 insertions, 96 deletions
diff --git a/libgo/go/path/filepath/example_unix_test.go b/libgo/go/path/filepath/example_unix_test.go
index cd8233c..20ec892 100644
--- a/libgo/go/path/filepath/example_unix_test.go
+++ b/libgo/go/path/filepath/example_unix_test.go
@@ -79,3 +79,18 @@ func ExampleJoin() {
// a/b/c
// a/b/c
}
+
+func ExampleMatch() {
+ fmt.Println("On Unix:")
+ fmt.Println(filepath.Match("/home/catch/*", "/home/catch/foo"))
+ fmt.Println(filepath.Match("/home/catch/*", "/home/catch/foo/bar"))
+ fmt.Println(filepath.Match("/home/?opher", "/home/gopher"))
+ fmt.Println(filepath.Match("/home/\\*", "/home/*"))
+
+ // Output:
+ // On Unix:
+ // true <nil>
+ // false <nil>
+ // true <nil>
+ // true <nil>
+}
diff --git a/libgo/go/path/filepath/match_test.go b/libgo/go/path/filepath/match_test.go
index b73d6d2..70db359 100644
--- a/libgo/go/path/filepath/match_test.go
+++ b/libgo/go/path/filepath/match_test.go
@@ -106,7 +106,7 @@ func TestMatch(t *testing.T) {
}
}
-// contains returns true if vector contains the string s.
+// contains reports whether vector contains the string s.
func contains(vector []string, s string) bool {
for _, elem := range vector {
if elem == s {
diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go
index 1508137..bbb9030 100644
--- a/libgo/go/path/filepath/path.go
+++ b/libgo/go/path/filepath/path.go
@@ -96,14 +96,19 @@ func Clean(path string) string {
}
return originalPath + "."
}
+
+ n := len(path)
+ if volLen > 2 && n == 1 && os.IsPathSeparator(path[0]) {
+ // UNC volume name with trailing slash.
+ return FromSlash(originalPath[:volLen])
+ }
rooted := os.IsPathSeparator(path[0])
// Invariants:
// reading from path; r is index of next byte to process.
- // writing to buf; w is index of next byte to write.
- // dotdot is index in buf where .. must stop, either because
+ // writing to out; w is index of next byte to write.
+ // dotdot is index in out where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix.
- n := len(path)
out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
r, dotdot := 0, 0
if rooted {
@@ -166,7 +171,7 @@ func ToSlash(path string) string {
if Separator == '/' {
return path
}
- return strings.Replace(path, string(Separator), "/", -1)
+ return strings.ReplaceAll(path, string(Separator), "/")
}
// FromSlash returns the result of replacing each slash ('/') character
@@ -176,7 +181,7 @@ func FromSlash(path string) string {
if Separator == '/' {
return path
}
- return strings.Replace(path, "/", string(Separator), -1)
+ return strings.ReplaceAll(path, "/", string(Separator))
}
// SplitList splits a list of paths joined by the OS-specific ListSeparator,
diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go
index 5983a94..4840f9d 100644
--- a/libgo/go/path/filepath/path_test.go
+++ b/libgo/go/path/filepath/path_test.go
@@ -15,6 +15,7 @@ import (
"runtime"
"sort"
"strings"
+ "syscall"
"testing"
)
@@ -92,6 +93,9 @@ var wincleantests = []PathTest{
{`//host/share/foo/../baz`, `\\host\share\baz`},
{`\\a\b\..\c`, `\\a\b\c`},
{`\\a\b`, `\\a\b`},
+ {`\\a\b\`, `\\a\b`},
+ {`\\folder\share\foo`, `\\folder\share\foo`},
+ {`\\folder\share\foo\`, `\\folder\share\foo`},
}
func TestClean(t *testing.T) {
@@ -274,6 +278,10 @@ var winjointests = []JoinTest{
{[]string{`C:`, `a`}, `C:a`},
{[]string{`C:`, `a\b`}, `C:a\b`},
{[]string{`C:`, `a`, `b`}, `C:a\b`},
+ {[]string{`C:`, ``, `b`}, `C:b`},
+ {[]string{`C:`, ``, ``, `b`}, `C:b`},
+ {[]string{`C:`, ``}, `C:.`},
+ {[]string{`C:`, ``, ``}, `C:.`},
{[]string{`C:.`, `a`}, `C:a`},
{[]string{`C:a`, `b`}, `C:a\b`},
{[]string{`C:a`, `b`, `d`}, `C:a\b\d`},
@@ -747,6 +755,11 @@ func TestIsAbs(t *testing.T) {
for _, test := range isabstests {
tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
}
+ // Test reserved names.
+ tests = append(tests, IsAbsTest{os.DevNull, true})
+ tests = append(tests, IsAbsTest{"NUL", true})
+ tests = append(tests, IsAbsTest{"nul", true})
+ tests = append(tests, IsAbsTest{"CON", true})
} else {
tests = isabstests
}
@@ -770,6 +783,18 @@ var EvalSymlinksTestDirs = []EvalSymlinksTest{
{"test/link1", "../test"},
{"test/link2", "dir"},
{"test/linkabs", "/"},
+ {"test/link4", "../test2"},
+ {"test2", "test/dir"},
+ // Issue 23444.
+ {"src", ""},
+ {"src/pool", ""},
+ {"src/pool/test", ""},
+ {"src/versions", ""},
+ {"src/versions/current", "../../version"},
+ {"src/versions/v1", ""},
+ {"src/versions/v1/modules", ""},
+ {"src/versions/v1/modules/test", "../../../pool/test"},
+ {"version", "src/versions/v1"},
}
var EvalSymlinksTests = []EvalSymlinksTest{
@@ -783,6 +808,8 @@ var EvalSymlinksTests = []EvalSymlinksTest{
{"test/dir/link3", "."},
{"test/link2/link3/test", "test"},
{"test/linkabs", "/"},
+ {"test/link4/..", "test"},
+ {"src/versions/current/modules/test", "src/pool/test"},
}
// simpleJoin builds a file name from the directory and path.
@@ -1047,7 +1074,7 @@ func TestAbs(t *testing.T) {
}
for _, path := range absTests {
- path = strings.Replace(path, "$", root, -1)
+ path = strings.ReplaceAll(path, "$", root)
info, err := os.Stat(path)
if err != nil {
t.Errorf("%s: %s", path, err)
@@ -1349,3 +1376,30 @@ func TestWalkSymlink(t *testing.T) {
testenv.MustHaveSymlink(t)
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)
+
+ pathSeparator := string(filepath.Separator)
+ tests := []string{
+ path + strings.Repeat(pathSeparator, 1),
+ path + strings.Repeat(pathSeparator, 2),
+ path + strings.Repeat(pathSeparator, 1) + ".",
+ path + strings.Repeat(pathSeparator, 2) + ".",
+ path + strings.Repeat(pathSeparator, 1) + "..",
+ path + strings.Repeat(pathSeparator, 2) + "..",
+ }
+
+ for i, test := range tests {
+ _, err = filepath.EvalSymlinks(test)
+ if err != syscall.ENOTDIR {
+ t.Fatalf("test#%d: want %q, got %q", i, syscall.ENOTDIR, err)
+ }
+ }
+}
diff --git a/libgo/go/path/filepath/path_windows.go b/libgo/go/path/filepath/path_windows.go
index 409e8d6..445c868 100644
--- a/libgo/go/path/filepath/path_windows.go
+++ b/libgo/go/path/filepath/path_windows.go
@@ -13,8 +13,34 @@ func isSlash(c uint8) bool {
return c == '\\' || c == '/'
}
+// reservedNames lists reserved Windows names. Search for PRN in
+// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
+// for details.
+var reservedNames = []string{
+ "CON", "PRN", "AUX", "NUL",
+ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+ "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
+}
+
+// isReservedName returns true, if path is Windows reserved name.
+// See reservedNames for the full list.
+func isReservedName(path string) bool {
+ if len(path) == 0 {
+ return false
+ }
+ for _, reserved := range reservedNames {
+ if strings.EqualFold(path, reserved) {
+ return true
+ }
+ }
+ return false
+}
+
// IsAbs reports whether the path is absolute.
func IsAbs(path string) (b bool) {
+ if isReservedName(path) {
+ return true
+ }
l := volumeNameLen(path)
if l == 0 {
return false
@@ -100,7 +126,7 @@ func splitList(path string) []string {
// Remove quotes.
for i, s := range list {
- list[i] = strings.Replace(s, `"`, ``, -1)
+ list[i] = strings.ReplaceAll(s, `"`, ``)
}
return list
@@ -134,7 +160,14 @@ func joinNonEmpty(elem []string) string {
if len(elem[0]) == 2 && elem[0][1] == ':' {
// First element is drive letter without terminating slash.
// Keep path relative to current directory on that drive.
- return Clean(elem[0] + strings.Join(elem[1:], string(Separator)))
+ // Skip empty elements.
+ i := 1
+ for ; i < len(elem); i++ {
+ if elem[i] != "" {
+ break
+ }
+ }
+ return Clean(elem[0] + strings.Join(elem[i:], string(Separator)))
}
// The following logic prevents Join from inadvertently creating a
// UNC path on Windows. Unless the first element is a UNC path, Join
diff --git a/libgo/go/path/filepath/symlink.go b/libgo/go/path/filepath/symlink.go
index 824aee4..4b41039 100644
--- a/libgo/go/path/filepath/symlink.go
+++ b/libgo/go/path/filepath/symlink.go
@@ -10,109 +10,128 @@ import (
"runtime"
)
-// isRoot returns true if path is root of file system
-// (`/` on unix and `/`, `\`, `c:\` or `c:/` on windows).
-func isRoot(path string) bool {
- if runtime.GOOS != "windows" {
- return path == "/"
- }
- switch len(path) {
- case 1:
- return os.IsPathSeparator(path[0])
- case 3:
- return path[1] == ':' && os.IsPathSeparator(path[2])
- }
- return false
-}
+func walkSymlinks(path string) (string, error) {
+ volLen := volumeNameLen(path)
+ pathSeparator := string(os.PathSeparator)
-// isDriveLetter returns true if path is Windows drive letter (like "c:").
-func isDriveLetter(path string) bool {
- if runtime.GOOS != "windows" {
- return false
+ if volLen < len(path) && os.IsPathSeparator(path[volLen]) {
+ volLen++
}
- return len(path) == 2 && path[1] == ':'
-}
+ vol := path[:volLen]
+ dest := vol
+ linksWalked := 0
+ for start, end := volLen, volLen; start < len(path); start = end {
+ for start < len(path) && os.IsPathSeparator(path[start]) {
+ start++
+ }
+ end = start
+ for end < len(path) && !os.IsPathSeparator(path[end]) {
+ end++
+ }
-func walkLink(path string, linksWalked *int) (newpath string, islink bool, err error) {
- if *linksWalked > 255 {
- return "", false, errors.New("EvalSymlinks: too many links")
- }
- fi, err := os.Lstat(path)
- if err != nil {
- return "", false, err
- }
- if fi.Mode()&os.ModeSymlink == 0 {
- return path, false, nil
- }
- newpath, err = os.Readlink(path)
- if err != nil {
- return "", false, err
- }
- *linksWalked++
- return newpath, true, nil
-}
+ // On Windows, "." can be a symlink.
+ // We look it up, and use the value if it is absolute.
+ // If not, we just return ".".
+ isWindowsDot := runtime.GOOS == "windows" && path[volumeNameLen(path):] == "."
-func walkLinks(path string, linksWalked *int) (string, error) {
- switch dir, file := Split(path); {
- case dir == "":
- newpath, _, err := walkLink(file, linksWalked)
- return newpath, err
- case file == "":
- if isDriveLetter(dir) {
- return dir, nil
- }
- if os.IsPathSeparator(dir[len(dir)-1]) {
- if isRoot(dir) {
- return dir, nil
+ // The next path component is in path[start:end].
+ if end == start {
+ // No more path components.
+ break
+ } else if path[start:end] == "." && !isWindowsDot {
+ // Ignore path component ".".
+ continue
+ } else if path[start:end] == ".." {
+ // Back up to previous component if possible.
+ // Note that volLen includes any leading slash.
+ var r int
+ for r = len(dest) - 1; r >= volLen; r-- {
+ if os.IsPathSeparator(dest[r]) {
+ break
+ }
+ }
+ if r < volLen {
+ if len(dest) > volLen {
+ dest += pathSeparator
+ }
+ dest += ".."
+ } else {
+ dest = dest[:r]
}
- return walkLinks(dir[:len(dir)-1], linksWalked)
+ continue
}
- newpath, _, err := walkLink(dir, linksWalked)
- return newpath, err
- default:
- newdir, err := walkLinks(dir, linksWalked)
- if err != nil {
- return "", err
+
+ // Ordinary path component. Add it to result.
+
+ if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
+ dest += pathSeparator
}
- newpath, islink, err := walkLink(Join(newdir, file), linksWalked)
+
+ dest += path[start:end]
+
+ // Resolve symlink.
+
+ fi, err := os.Lstat(dest)
if err != nil {
return "", err
}
- if !islink {
- return newpath, nil
+
+ if fi.Mode()&os.ModeSymlink == 0 {
+ if !fi.Mode().IsDir() && end < len(path) {
+ return "", slashAfterFilePathError
+ }
+ continue
}
- if IsAbs(newpath) || os.IsPathSeparator(newpath[0]) {
- return newpath, nil
+
+ // Found symlink.
+
+ linksWalked++
+ if linksWalked > 255 {
+ return "", errors.New("EvalSymlinks: too many links")
}
- return Join(newdir, newpath), nil
- }
-}
-func walkSymlinks(path string) (string, error) {
- if path == "" {
- return path, nil
- }
- var linksWalked int // to protect against cycles
- for {
- i := linksWalked
- newpath, err := walkLinks(path, &linksWalked)
+ link, err := os.Readlink(dest)
if err != nil {
return "", err
}
- if runtime.GOOS == "windows" {
- // walkLinks(".", ...) always returns "." on unix.
- // But on windows it returns symlink target, if current
- // directory is a symlink. Stop the walk, if symlink
- // target is not absolute path, and return "."
- // to the caller (just like unix does).
- // Same for "C:.".
- if path[volumeNameLen(path):] == "." && !IsAbs(newpath) {
- return path, nil
- }
+
+ if isWindowsDot && !IsAbs(link) {
+ // On Windows, if "." is a relative symlink,
+ // just return ".".
+ break
}
- if i == linksWalked {
- return Clean(newpath), nil
+
+ path = link + path[end:]
+
+ v := volumeNameLen(link)
+ if v > 0 {
+ // Symlink to drive name is an absolute path.
+ if v < len(link) && os.IsPathSeparator(link[v]) {
+ v++
+ }
+ vol = link[:v]
+ dest = vol
+ end = len(vol)
+ } else if len(link) > 0 && os.IsPathSeparator(link[0]) {
+ // Symlink to absolute path.
+ dest = link[:1]
+ end = 1
+ } else {
+ // Symlink to relative path; replace last
+ // path component in dest.
+ var r int
+ for r = len(dest) - 1; r >= volLen; r-- {
+ if os.IsPathSeparator(dest[r]) {
+ break
+ }
+ }
+ if r < volLen {
+ dest = vol
+ } else {
+ dest = dest[:r]
+ }
+ end = 0
}
- path = newpath
}
+ return Clean(dest), nil
}
diff --git a/libgo/go/path/filepath/symlink_unix.go b/libgo/go/path/filepath/symlink_unix.go
index d20e63a..b57e7f2 100644
--- a/libgo/go/path/filepath/symlink_unix.go
+++ b/libgo/go/path/filepath/symlink_unix.go
@@ -2,6 +2,15 @@
package filepath
+import (
+ "syscall"
+)
+
+// walkSymlinks returns slashAfterFilePathError error for paths like
+// //path/to/existing_file/ and /path/to/existing_file/. and /path/to/existing_file/..
+
+var slashAfterFilePathError = syscall.ENOTDIR
+
func evalSymlinks(path string) (string, error) {
return walkSymlinks(path)
}
diff --git a/libgo/go/path/filepath/symlink_windows.go b/libgo/go/path/filepath/symlink_windows.go
index 78cde4a..531dc26 100644
--- a/libgo/go/path/filepath/symlink_windows.go
+++ b/libgo/go/path/filepath/symlink_windows.go
@@ -43,7 +43,7 @@ func normBase(path string) (string, error) {
return syscall.UTF16ToString(data.FileName[:]), nil
}
-// baseIsDotDot returns whether the last element of path is "..".
+// baseIsDotDot reports whether the last element of path is "..".
// The given path should be 'Clean'-ed in advance.
func baseIsDotDot(path string) bool {
i := strings.LastIndexByte(path, Separator)
@@ -171,8 +171,16 @@ func samefile(path1, path2 string) bool {
return os.SameFile(fi1, fi2)
}
+// walkSymlinks returns slashAfterFilePathError error for paths like
+// //path/to/existing_file/ and /path/to/existing_file/. and /path/to/existing_file/..
+
+var slashAfterFilePathError = errors.New("attempting to walk past file path.")
+
func evalSymlinks(path string) (string, error) {
newpath, err := walkSymlinks(path)
+ if err == slashAfterFilePathError {
+ return "", syscall.ENOTDIR
+ }
if err != nil {
newpath2, err2 := evalSymlinksUsingGetFinalPathNameByHandle(path)
if err2 == nil {