Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-pages.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Shushlin <vshushlin@gitlab.com>2020-08-11 14:48:39 +0300
committerVladimir Shushlin <vshushlin@gitlab.com>2020-08-11 14:48:39 +0300
commit8617aaebb1e45b9c222f3c67f93112d7dc37ad61 (patch)
tree45327f84ab9848f19564f0180a1f8fbe3f97a161
parent04e50760b07febf8174143ae7c110702e26a07b9 (diff)
parent334bda2bd7405e10705debc577b7b737e9ce8f4d (diff)
Merge branch 'jv-vendor-symlink-code' into 'master'
Vendor Go 1.14 filepath.EvalSymlinks code See merge request gitlab-org/gitlab-pages!321
-rw-r--r--.golangci.yml1
-rw-r--r--internal/serving/disk/symlink/LICENSE27
-rw-r--r--internal/serving/disk/symlink/PATENTS22
-rw-r--r--internal/serving/disk/symlink/README.md7
-rw-r--r--internal/serving/disk/symlink/path_test.go262
-rw-r--r--internal/serving/disk/symlink/shims.go13
-rw-r--r--internal/serving/disk/symlink/symlink.go146
7 files changed, 478 insertions, 0 deletions
diff --git a/.golangci.yml b/.golangci.yml
index 5c60dcfb..2b97de09 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -7,6 +7,7 @@ run:
skip-dirs:
- vendor
- internal/httputil # from github.com/golang/gddo
+ - internal/serving/disk/symlink
skip-files:
- mock_*.go
diff --git a/internal/serving/disk/symlink/LICENSE b/internal/serving/disk/symlink/LICENSE
new file mode 100644
index 00000000..6a66aea5
--- /dev/null
+++ b/internal/serving/disk/symlink/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/internal/serving/disk/symlink/PATENTS b/internal/serving/disk/symlink/PATENTS
new file mode 100644
index 00000000..73309904
--- /dev/null
+++ b/internal/serving/disk/symlink/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/internal/serving/disk/symlink/README.md b/internal/serving/disk/symlink/README.md
new file mode 100644
index 00000000..2b3678c2
--- /dev/null
+++ b/internal/serving/disk/symlink/README.md
@@ -0,0 +1,7 @@
+# Symlink code extracted from Go 1.14 stdlib
+
+This directory contains part of the Go standard library
+`filepath.EvalSymlinks` code. It was vendored from Go 1.14.6.
+
+- `symlink.go` is based on https://github.com/golang/go/blob/go1.14.6/src/path/filepath/symlink.go
+- `path_test.go` is based on https://github.com/golang/go/blob/go1.14.6/src/path/filepath/path_test.go#L768-L1000
diff --git a/internal/serving/disk/symlink/path_test.go b/internal/serving/disk/symlink/path_test.go
new file mode 100644
index 00000000..077b0ad2
--- /dev/null
+++ b/internal/serving/disk/symlink/path_test.go
@@ -0,0 +1,262 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package filepath_test
+
+import (
+ "io/ioutil"
+ "os"
+ "runtime"
+ "testing"
+
+ filepath "gitlab.com/gitlab-org/gitlab-pages/internal/serving/disk/symlink"
+)
+
+func chtmpdir(t *testing.T) (restore func()) {
+ oldwd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("chtmpdir: %v", err)
+ }
+ d, err := ioutil.TempDir("", "test")
+ if err != nil {
+ t.Fatalf("chtmpdir: %v", err)
+ }
+ if err := os.Chdir(d); err != nil {
+ t.Fatalf("chtmpdir: %v", err)
+ }
+ return func() {
+ if err := os.Chdir(oldwd); err != nil {
+ t.Fatalf("chtmpdir: %v", err)
+ }
+ os.RemoveAll(d)
+ }
+}
+
+type EvalSymlinksTest struct {
+ // If dest is empty, the path is created; otherwise the dest is symlinked to the path.
+ path, dest string
+}
+
+var EvalSymlinksTestDirs = []EvalSymlinksTest{
+ {"test", ""},
+ {"test/dir", ""},
+ {"test/dir/link3", "../../"},
+ {"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{
+ {"test", "test"},
+ {"test/dir", "test/dir"},
+ {"test/dir/../..", "."},
+ {"test/link1", "test"},
+ {"test/link2", "test/dir"},
+ {"test/link1/dir", "test/dir"},
+ {"test/link2/..", "test"},
+ {"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.
+// It does not use Join because we don't want ".." to be evaluated.
+func simpleJoin(dir, path string) string {
+ return dir + string(filepath.Separator) + path
+}
+
+func testEvalSymlinks(t *testing.T, path, want string) {
+ have, err := filepath.EvalSymlinks(path)
+ if err != nil {
+ t.Errorf("EvalSymlinks(%q) error: %v", path, err)
+ return
+ }
+ if filepath.Clean(have) != filepath.Clean(want) {
+ t.Errorf("EvalSymlinks(%q) returns %q, want %q", path, have, want)
+ }
+}
+
+func testEvalSymlinksAfterChdir(t *testing.T, wd, path, want string) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ err := os.Chdir(cwd)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ err = os.Chdir(wd)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ have, err := filepath.EvalSymlinks(path)
+ if err != nil {
+ t.Errorf("EvalSymlinks(%q) in %q directory error: %v", path, wd, err)
+ return
+ }
+ if filepath.Clean(have) != filepath.Clean(want) {
+ t.Errorf("EvalSymlinks(%q) in %q directory returns %q, want %q", path, wd, have, want)
+ }
+}
+
+func TestEvalSymlinks(t *testing.T) {
+ tmpDir, err := ioutil.TempDir("", "evalsymlink")
+ if err != nil {
+ t.Fatal("creating temp dir:", err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ // /tmp may itself be a symlink! Avoid the confusion, although
+ // it means trusting the thing we're testing.
+ tmpDir, err = filepath.EvalSymlinks(tmpDir)
+ if err != nil {
+ t.Fatal("eval symlink for tmp dir:", err)
+ }
+
+ // Create the symlink farm using relative paths.
+ for _, d := range EvalSymlinksTestDirs {
+ var err error
+ path := simpleJoin(tmpDir, d.path)
+ if d.dest == "" {
+ err = os.Mkdir(path, 0755)
+ } else {
+ err = os.Symlink(d.dest, path)
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // Evaluate the symlink farm.
+ for _, test := range EvalSymlinksTests {
+ path := simpleJoin(tmpDir, test.path)
+
+ dest := simpleJoin(tmpDir, test.dest)
+ if filepath.IsAbs(test.dest) || os.IsPathSeparator(test.dest[0]) {
+ dest = test.dest
+ }
+ testEvalSymlinks(t, path, dest)
+
+ // test EvalSymlinks(".")
+ testEvalSymlinksAfterChdir(t, path, ".", ".")
+
+ // test EvalSymlinks("C:.") on Windows
+ if runtime.GOOS == "windows" {
+ volDot := filepath.VolumeName(tmpDir) + "."
+ testEvalSymlinksAfterChdir(t, path, volDot, volDot)
+ }
+
+ // test EvalSymlinks(".."+path)
+ dotdotPath := simpleJoin("..", test.dest)
+ if filepath.IsAbs(test.dest) || os.IsPathSeparator(test.dest[0]) {
+ dotdotPath = test.dest
+ }
+ testEvalSymlinksAfterChdir(t,
+ simpleJoin(tmpDir, "test"),
+ simpleJoin("..", test.path),
+ dotdotPath)
+
+ // test EvalSymlinks(p) where p is relative path
+ testEvalSymlinksAfterChdir(t, tmpDir, test.path, test.dest)
+ }
+}
+
+func TestEvalSymlinksIsNotExist(t *testing.T) {
+ defer chtmpdir(t)()
+
+ _, err := filepath.EvalSymlinks("notexist")
+ if !os.IsNotExist(err) {
+ t.Errorf("expected the file is not found, got %v\n", err)
+ }
+
+ err = os.Symlink("notexist", "link")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove("link")
+
+ _, err = filepath.EvalSymlinks("link")
+ if !os.IsNotExist(err) {
+ t.Errorf("expected the file is not found, got %v\n", err)
+ }
+}
+
+func TestIssue13582(t *testing.T) {
+ tmpDir, err := ioutil.TempDir("", "issue13582")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ dir := filepath.Join(tmpDir, "dir")
+ err = os.Mkdir(dir, 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ linkToDir := filepath.Join(tmpDir, "link_to_dir")
+ err = os.Symlink(dir, linkToDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ file := filepath.Join(linkToDir, "file")
+ err = ioutil.WriteFile(file, nil, 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ link1 := filepath.Join(linkToDir, "link1")
+ err = os.Symlink(file, link1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ link2 := filepath.Join(linkToDir, "link2")
+ err = os.Symlink(link1, link2)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // /tmp may itself be a symlink!
+ realTmpDir, err := filepath.EvalSymlinks(tmpDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ realDir := filepath.Join(realTmpDir, "dir")
+ realFile := filepath.Join(realDir, "file")
+
+ tests := []struct {
+ path, want string
+ }{
+ {dir, realDir},
+ {linkToDir, realDir},
+ {file, realFile},
+ {link1, realFile},
+ {link2, realFile},
+ }
+ for i, test := range tests {
+ have, err := filepath.EvalSymlinks(test.path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if have != test.want {
+ t.Errorf("test#%d: EvalSymlinks(%q) returns %q, want %q", i, test.path, have, test.want)
+ }
+ }
+}
diff --git a/internal/serving/disk/symlink/shims.go b/internal/serving/disk/symlink/shims.go
new file mode 100644
index 00000000..4b344180
--- /dev/null
+++ b/internal/serving/disk/symlink/shims.go
@@ -0,0 +1,13 @@
+package filepath
+
+import stdlib "path/filepath"
+
+func volumeNameLen(s string) int { return 0 }
+
+func IsAbs(path string) bool { return stdlib.IsAbs(path) }
+func Clean(path string) string { return stdlib.Clean(path) }
+func Join(elem ...string) string { return stdlib.Join(elem...) }
+func VolumeName(path string) string { return stdlib.VolumeName(path) }
+func EvalSymlinks(path string) (string, error) { return walkSymlinks(path) }
+
+const Separator = stdlib.Separator
diff --git a/internal/serving/disk/symlink/symlink.go b/internal/serving/disk/symlink/symlink.go
new file mode 100644
index 00000000..335b315a
--- /dev/null
+++ b/internal/serving/disk/symlink/symlink.go
@@ -0,0 +1,146 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package filepath
+
+import (
+ "errors"
+ "os"
+ "runtime"
+ "syscall"
+)
+
+func walkSymlinks(path string) (string, error) {
+ volLen := volumeNameLen(path)
+ pathSeparator := string(os.PathSeparator)
+
+ if volLen < len(path) && os.IsPathSeparator(path[volLen]) {
+ volLen++
+ }
+ 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++
+ }
+
+ // 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):] == "."
+
+ // 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.
+
+ // Set r to the index of the last slash in dest,
+ // after the volume.
+ var r int
+ for r = len(dest) - 1; r >= volLen; r-- {
+ if os.IsPathSeparator(dest[r]) {
+ break
+ }
+ }
+ if r < volLen || dest[r+1:] == ".." {
+ // Either path has no slashes
+ // (it's empty or just "C:")
+ // or it ends in a ".." we had to keep.
+ // Either way, keep this "..".
+ if len(dest) > volLen {
+ dest += pathSeparator
+ }
+ dest += ".."
+ } else {
+ // Discard everything since the last slash.
+ dest = dest[:r]
+ }
+ continue
+ }
+
+ // Ordinary path component. Add it to result.
+
+ if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
+ dest += pathSeparator
+ }
+
+ dest += path[start:end]
+
+ // Resolve symlink.
+
+ fi, err := os.Lstat(dest)
+ if err != nil {
+ return "", err
+ }
+
+ if fi.Mode()&os.ModeSymlink == 0 {
+ if !fi.Mode().IsDir() && end < len(path) {
+ return "", syscall.ENOTDIR
+ }
+ continue
+ }
+
+ // Found symlink.
+
+ linksWalked++
+ if linksWalked > 255 {
+ return "", errors.New("EvalSymlinks: too many links")
+ }
+
+ link, err := os.Readlink(dest)
+ if err != nil {
+ return "", err
+ }
+
+ if isWindowsDot && !IsAbs(link) {
+ // On Windows, if "." is a relative symlink,
+ // just return ".".
+ break
+ }
+
+ 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
+ }
+ }
+ return Clean(dest), nil
+}