Fix the dependency issue (#231)

This commit is contained in:
Robbie Zhang
2018-06-21 12:09:42 -07:00
committed by GitHub
parent 027b76651d
commit 6ec1098bb8
16629 changed files with 74837 additions and 4975021 deletions

View File

@@ -1,6 +0,0 @@
Package symlink implements EvalSymlinksInScope which is an extension of filepath.EvalSymlinks,
as well as a Windows long-path aware version of filepath.EvalSymlinks
from the [Go standard library](https://golang.org/pkg/path/filepath).
The code from filepath.EvalSymlinks has been adapted in fs.go.
Please read the LICENSE.BSD file that governs fs.go and LICENSE.APACHE for fs_test.go.

View File

@@ -1,143 +0,0 @@
// 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.BSD file.
// This code is a modified version of path/filepath/symlink.go from the Go standard library.
package symlink
import (
"bytes"
"errors"
"os"
"path/filepath"
"strings"
"github.com/hyperhq/hypercli/pkg/system"
)
// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an
// absolute path. This function handles paths in a platform-agnostic manner.
func FollowSymlinkInScope(path, root string) (string, error) {
path, err := filepath.Abs(filepath.FromSlash(path))
if err != nil {
return "", err
}
root, err = filepath.Abs(filepath.FromSlash(root))
if err != nil {
return "", err
}
return evalSymlinksInScope(path, root)
}
// evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return
// a result guaranteed to be contained within the scope `root`, at the time of the call.
// Symlinks in `root` are not evaluated and left as-is.
// Errors encountered while attempting to evaluate symlinks in path will be returned.
// Non-existing paths are valid and do not constitute an error.
// `path` has to contain `root` as a prefix, or else an error will be returned.
// Trying to break out from `root` does not constitute an error.
//
// Example:
// If /foo/bar -> /outside,
// FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/oustide"
//
// IMPORTANT: it is the caller's responsibility to call evalSymlinksInScope *after* relevant symlinks
// are created and not to create subsequently, additional symlinks that could potentially make a
// previously-safe path, unsafe. Example: if /foo/bar does not exist, evalSymlinksInScope("/foo/bar", "/foo")
// would return "/foo/bar". If one makes /foo/bar a symlink to /baz subsequently, then "/foo/bar" should
// no longer be considered safely contained in "/foo".
func evalSymlinksInScope(path, root string) (string, error) {
root = filepath.Clean(root)
if path == root {
return path, nil
}
if !strings.HasPrefix(path, root) {
return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root)
}
const maxIter = 255
originalPath := path
// given root of "/a" and path of "/a/b/../../c" we want path to be "/b/../../c"
path = path[len(root):]
if root == string(filepath.Separator) {
path = string(filepath.Separator) + path
}
if !strings.HasPrefix(path, string(filepath.Separator)) {
return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root)
}
path = filepath.Clean(path)
// consume path by taking each frontmost path element,
// expanding it if it's a symlink, and appending it to b
var b bytes.Buffer
// b here will always be considered to be the "current absolute path inside
// root" when we append paths to it, we also append a slash and use
// filepath.Clean after the loop to trim the trailing slash
for n := 0; path != ""; n++ {
if n > maxIter {
return "", errors.New("evalSymlinksInScope: too many links in " + originalPath)
}
// find next path component, p
i := strings.IndexRune(path, filepath.Separator)
var p string
if i == -1 {
p, path = path, ""
} else {
p, path = path[:i], path[i+1:]
}
if p == "" {
continue
}
// this takes a b.String() like "b/../" and a p like "c" and turns it
// into "/b/../c" which then gets filepath.Cleaned into "/c" and then
// root gets prepended and we Clean again (to remove any trailing slash
// if the first Clean gave us just "/")
cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p)
if cleanP == string(filepath.Separator) {
// never Lstat "/" itself
b.Reset()
continue
}
fullP := filepath.Clean(root + cleanP)
fi, err := os.Lstat(fullP)
if os.IsNotExist(err) {
// if p does not exist, accept it
b.WriteString(p)
b.WriteRune(filepath.Separator)
continue
}
if err != nil {
return "", err
}
if fi.Mode()&os.ModeSymlink == 0 {
b.WriteString(p + string(filepath.Separator))
continue
}
// it's a symlink, put it at the front of path
dest, err := os.Readlink(fullP)
if err != nil {
return "", err
}
if system.IsAbs(dest) {
b.Reset()
}
path = dest + string(filepath.Separator) + path
}
// see note above on "fullP := ..." for why this is double-cleaned and
// what's happening here
return filepath.Clean(root + filepath.Clean(string(filepath.Separator)+b.String())), nil
}
// EvalSymlinks returns the path name after the evaluation of any symbolic
// links.
// If path is relative the result will be relative to the current directory,
// unless one of the components is an absolute symbolic link.
// This version has been updated to support long paths prepended with `\\?\`.
func EvalSymlinks(path string) (string, error) {
return evalSymlinks(path)
}

View File

@@ -1,402 +0,0 @@
// Licensed under the Apache License, Version 2.0; See LICENSE.APACHE
package symlink
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
type dirOrLink struct {
path string
target string
}
func makeFs(tmpdir string, fs []dirOrLink) error {
for _, s := range fs {
s.path = filepath.Join(tmpdir, s.path)
if s.target == "" {
os.MkdirAll(s.path, 0755)
continue
}
if err := os.MkdirAll(filepath.Dir(s.path), 0755); err != nil {
return err
}
if err := os.Symlink(s.target, s.path); err != nil && !os.IsExist(err) {
return err
}
}
return nil
}
func testSymlink(tmpdir, path, expected, scope string) error {
rewrite, err := FollowSymlinkInScope(filepath.Join(tmpdir, path), filepath.Join(tmpdir, scope))
if err != nil {
return err
}
expected, err = filepath.Abs(filepath.Join(tmpdir, expected))
if err != nil {
return err
}
if expected != rewrite {
return fmt.Errorf("Expected %q got %q", expected, rewrite)
}
return nil
}
func TestFollowSymlinkAbsolute(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkAbsolute")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "testdata/fs/a/d/c/data", "testdata/b/c/data", "testdata"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkRelativePath(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativePath")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/i", target: "a"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "testdata/fs/i", "testdata/fs/a", "testdata"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkSkipSymlinksOutsideScope(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkSkipSymlinksOutsideScope")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{
{path: "linkdir", target: "realdir"},
{path: "linkdir/foo/bar"},
}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "linkdir/foo/bar", "linkdir/foo/bar", "linkdir/foo"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkInvalidScopePathPair(t *testing.T) {
if _, err := FollowSymlinkInScope("toto", "testdata"); err == nil {
t.Fatal("expected an error")
}
}
func TestFollowSymlinkLastLink(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkLastLink")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "testdata/fs/a/d", "testdata/b", "testdata"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkRelativeLinkChangeScope(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativeLinkChangeScope")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/e", target: "../b"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "testdata/fs/a/e/c/data", "testdata/fs/b/c/data", "testdata"); err != nil {
t.Fatal(err)
}
// avoid letting allowing symlink e lead us to ../b
// normalize to the "testdata/fs/a"
if err := testSymlink(tmpdir, "testdata/fs/a/e", "testdata/fs/a/b", "testdata/fs/a"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkDeepRelativeLinkChangeScope(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkDeepRelativeLinkChangeScope")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/f", target: "../../../../test"}}); err != nil {
t.Fatal(err)
}
// avoid letting symlink f lead us out of the "testdata" scope
// we don't normalize because symlink f is in scope and there is no
// information leak
if err := testSymlink(tmpdir, "testdata/fs/a/f", "testdata/test", "testdata"); err != nil {
t.Fatal(err)
}
// avoid letting symlink f lead us out of the "testdata/fs" scope
// we don't normalize because symlink f is in scope and there is no
// information leak
if err := testSymlink(tmpdir, "testdata/fs/a/f", "testdata/fs/test", "testdata/fs"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkRelativeLinkChain(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativeLinkChain")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
// avoid letting symlink g (pointed at by symlink h) take out of scope
// TODO: we should probably normalize to scope here because ../[....]/root
// is out of scope and we leak information
if err := makeFs(tmpdir, []dirOrLink{
{path: "testdata/fs/b/h", target: "../g"},
{path: "testdata/fs/g", target: "../../../../../../../../../../../../root"},
}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "testdata/fs/b/h", "testdata/root", "testdata"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkBreakoutPath(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkBreakoutPath")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
// avoid letting symlink -> ../directory/file escape from scope
// normalize to "testdata/fs/j"
if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/j/k", target: "../i/a"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "testdata/fs/j/k", "testdata/fs/j/i/a", "testdata/fs/j"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkToRoot(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkToRoot")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
// make sure we don't allow escaping to /
// normalize to dir
if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "/"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "foo", "", ""); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkSlashDotdot(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkSlashDotdot")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
tmpdir = filepath.Join(tmpdir, "dir", "subdir")
// make sure we don't allow escaping to /
// normalize to dir
if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "/../../"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "foo", "", ""); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkDotdot(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkDotdot")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
tmpdir = filepath.Join(tmpdir, "dir", "subdir")
// make sure we stay in scope without leaking information
// this also checks for escaping to /
// normalize to dir
if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "../../"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "foo", "", ""); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkRelativePath2(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativePath2")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{{path: "bar/foo", target: "baz/target"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "bar/foo", "bar/baz/target", ""); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkScopeLink(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkScopeLink")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{
{path: "root2"},
{path: "root", target: "root2"},
{path: "root2/foo", target: "../bar"},
}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "root/foo", "root/bar", "root"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkRootScope(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRootScope")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
expected, err := filepath.EvalSymlinks(tmpdir)
if err != nil {
t.Fatal(err)
}
rewrite, err := FollowSymlinkInScope(tmpdir, "/")
if err != nil {
t.Fatal(err)
}
if rewrite != expected {
t.Fatalf("expected %q got %q", expected, rewrite)
}
}
func TestFollowSymlinkEmpty(t *testing.T) {
res, err := FollowSymlinkInScope("", "")
if err != nil {
t.Fatal(err)
}
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
if res != wd {
t.Fatalf("expected %q got %q", wd, res)
}
}
func TestFollowSymlinkCircular(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkCircular")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{{path: "root/foo", target: "foo"}}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "root/foo", "", "root"); err == nil {
t.Fatal("expected an error for foo -> foo")
}
if err := makeFs(tmpdir, []dirOrLink{
{path: "root/bar", target: "baz"},
{path: "root/baz", target: "../bak"},
{path: "root/bak", target: "/bar"},
}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "root/foo", "", "root"); err == nil {
t.Fatal("expected an error for bar -> baz -> bak -> bar")
}
}
func TestFollowSymlinkComplexChainWithTargetPathsContainingLinks(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkComplexChainWithTargetPathsContainingLinks")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{
{path: "root2"},
{path: "root", target: "root2"},
{path: "root/a", target: "r/s"},
{path: "root/r", target: "../root/t"},
{path: "root/root/t/s/b", target: "/../u"},
{path: "root/u/c", target: "."},
{path: "root/u/x/y", target: "../v"},
{path: "root/u/v", target: "/../w"},
}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "root/a/b/c/x/y/z", "root/w/z", "root"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkBreakoutNonExistent(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkBreakoutNonExistent")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{
{path: "root/slash", target: "/"},
{path: "root/sym", target: "/idontexist/../slash"},
}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "root/sym/file", "root/file", "root"); err != nil {
t.Fatal(err)
}
}
func TestFollowSymlinkNoLexicalCleaning(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkNoLexicalCleaning")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := makeFs(tmpdir, []dirOrLink{
{path: "root/sym", target: "/foo/bar"},
{path: "root/hello", target: "/sym/../baz"},
}); err != nil {
t.Fatal(err)
}
if err := testSymlink(tmpdir, "root/hello", "root/foo/baz", "root"); err != nil {
t.Fatal(err)
}
}

View File

@@ -1,11 +0,0 @@
// +build !windows
package symlink
import (
"path/filepath"
)
func evalSymlinks(path string) (string, error) {
return filepath.EvalSymlinks(path)
}

View File

@@ -1,156 +0,0 @@
package symlink
import (
"bytes"
"errors"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/hyperhq/hypercli/pkg/longpath"
)
func toShort(path string) (string, error) {
p, err := syscall.UTF16FromString(path)
if err != nil {
return "", err
}
b := p // GetShortPathName says we can reuse buffer
n, err := syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
if err != nil {
return "", err
}
if n > uint32(len(b)) {
b = make([]uint16, n)
n, err = syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
if err != nil {
return "", err
}
}
return syscall.UTF16ToString(b), nil
}
func toLong(path string) (string, error) {
p, err := syscall.UTF16FromString(path)
if err != nil {
return "", err
}
b := p // GetLongPathName says we can reuse buffer
n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
if err != nil {
return "", err
}
if n > uint32(len(b)) {
b = make([]uint16, n)
n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
if err != nil {
return "", err
}
}
b = b[:n]
return syscall.UTF16ToString(b), nil
}
func evalSymlinks(path string) (string, error) {
path, err := walkSymlinks(path)
if err != nil {
return "", err
}
p, err := toShort(path)
if err != nil {
return "", err
}
p, err = toLong(p)
if err != nil {
return "", err
}
// syscall.GetLongPathName does not change the case of the drive letter,
// but the result of EvalSymlinks must be unique, so we have
// EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
// Make drive letter upper case.
if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' {
p = string(p[0]+'A'-'a') + p[1:]
} else if len(p) >= 6 && p[5] == ':' && 'a' <= p[4] && p[4] <= 'z' {
p = p[:3] + string(p[4]+'A'-'a') + p[5:]
}
return filepath.Clean(p), nil
}
const utf8RuneSelf = 0x80
func walkSymlinks(path string) (string, error) {
const maxIter = 255
originalPath := path
// consume path by taking each frontmost path element,
// expanding it if it's a symlink, and appending it to b
var b bytes.Buffer
for n := 0; path != ""; n++ {
if n > maxIter {
return "", errors.New("EvalSymlinks: too many links in " + originalPath)
}
// A path beginning with `\\?\` represents the root, so automatically
// skip that part and begin processing the next segment.
if strings.HasPrefix(path, longpath.Prefix) {
b.WriteString(longpath.Prefix)
path = path[4:]
continue
}
// find next path component, p
var i = -1
for j, c := range path {
if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) {
i = j
break
}
}
var p string
if i == -1 {
p, path = path, ""
} else {
p, path = path[:i], path[i+1:]
}
if p == "" {
if b.Len() == 0 {
// must be absolute path
b.WriteRune(filepath.Separator)
}
continue
}
// If this is the first segment after the long path prefix, accept the
// current segment as a volume root or UNC share and move on to the next.
if b.String() == longpath.Prefix {
b.WriteString(p)
b.WriteRune(filepath.Separator)
continue
}
fi, err := os.Lstat(b.String() + p)
if err != nil {
return "", err
}
if fi.Mode()&os.ModeSymlink == 0 {
b.WriteString(p)
if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') {
b.WriteRune(filepath.Separator)
}
continue
}
// it's a symlink, put it at the front of path
dest, err := os.Readlink(b.String() + p)
if err != nil {
return "", err
}
if filepath.IsAbs(dest) || os.IsPathSeparator(dest[0]) {
b.Reset()
}
path = dest + string(filepath.Separator) + path
}
return filepath.Clean(b.String()), nil
}