Move Follow symlink to pkg

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
Michael Crosby 2014-05-13 10:34:30 -07:00
parent 4086e37e27
commit e8a8022ba3
7 changed files with 185 additions and 0 deletions

72
symlink/fs.go Normal file
View file

@ -0,0 +1,72 @@
package symlink
import (
"fmt"
"os"
"path/filepath"
"strings"
)
// FollowSymlink will follow an existing link and scope it to the root
// path provided.
func FollowSymlinkInScope(link, root string) (string, error) {
prev := "/"
root, err := filepath.Abs(root)
if err != nil {
return "", err
}
link, err = filepath.Abs(link)
if err != nil {
return "", err
}
if !strings.HasPrefix(filepath.Dir(link), root) {
return "", fmt.Errorf("%s is not within %s", link, root)
}
for _, p := range strings.Split(link, "/") {
prev = filepath.Join(prev, p)
prev = filepath.Clean(prev)
for {
if !strings.HasPrefix(prev, root) {
// Don't resolve symlinks outside of root. For example,
// we don't have to check /home in the below.
//
// /home -> usr/home
// FollowSymlinkInScope("/home/bob/foo/bar", "/home/bob/foo")
break
}
stat, err := os.Lstat(prev)
if err != nil {
if os.IsNotExist(err) {
break
}
return "", err
}
if stat.Mode()&os.ModeSymlink == os.ModeSymlink {
dest, err := os.Readlink(prev)
if err != nil {
return "", err
}
switch dest[0] {
case '/':
prev = filepath.Join(root, dest)
case '.':
prev, _ = filepath.Abs(prev)
if prev = filepath.Clean(filepath.Join(filepath.Dir(prev), dest)); len(prev) < len(root) {
prev = filepath.Join(root, filepath.Base(dest))
}
}
} else {
break
}
}
}
return prev, nil
}

108
symlink/fs_test.go Normal file
View file

@ -0,0 +1,108 @@
package symlink
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func abs(t *testing.T, p string) string {
o, err := filepath.Abs(p)
if err != nil {
t.Fatal(err)
}
return o
}
func TestFollowSymLinkNormal(t *testing.T) {
link := "testdata/fs/a/d/c/data"
rewrite, err := FollowSymlinkInScope(link, "testdata")
if err != nil {
t.Fatal(err)
}
if expected := abs(t, "testdata/b/c/data"); expected != rewrite {
t.Fatalf("Expected %s got %s", expected, rewrite)
}
}
func TestFollowSymLinkUnderLinkedDir(t *testing.T) {
dir, err := ioutil.TempDir("", "docker-fs-test")
if err != nil {
t.Fatal(err)
}
os.Mkdir(filepath.Join(dir, "realdir"), 0700)
os.Symlink("realdir", filepath.Join(dir, "linkdir"))
linkDir := filepath.Join(dir, "linkdir", "foo")
dirUnderLinkDir := filepath.Join(dir, "linkdir", "foo", "bar")
os.MkdirAll(dirUnderLinkDir, 0700)
rewrite, err := FollowSymlinkInScope(dirUnderLinkDir, linkDir)
if err != nil {
t.Fatal(err)
}
if rewrite != dirUnderLinkDir {
t.Fatalf("Expected %s got %s", dirUnderLinkDir, rewrite)
}
}
func TestFollowSymLinkRandomString(t *testing.T) {
if _, err := FollowSymlinkInScope("toto", "testdata"); err == nil {
t.Fatal("Random string should fail but didn't")
}
}
func TestFollowSymLinkLastLink(t *testing.T) {
link := "testdata/fs/a/d"
rewrite, err := FollowSymlinkInScope(link, "testdata")
if err != nil {
t.Fatal(err)
}
if expected := abs(t, "testdata/b"); expected != rewrite {
t.Fatalf("Expected %s got %s", expected, rewrite)
}
}
func TestFollowSymLinkRelativeLink(t *testing.T) {
link := "testdata/fs/a/e/c/data"
rewrite, err := FollowSymlinkInScope(link, "testdata")
if err != nil {
t.Fatal(err)
}
if expected := abs(t, "testdata/fs/b/c/data"); expected != rewrite {
t.Fatalf("Expected %s got %s", expected, rewrite)
}
}
func TestFollowSymLinkRelativeLinkScope(t *testing.T) {
link := "testdata/fs/a/f"
rewrite, err := FollowSymlinkInScope(link, "testdata")
if err != nil {
t.Fatal(err)
}
if expected := abs(t, "testdata/test"); expected != rewrite {
t.Fatalf("Expected %s got %s", expected, rewrite)
}
link = "testdata/fs/b/h"
rewrite, err = FollowSymlinkInScope(link, "testdata")
if err != nil {
t.Fatal(err)
}
if expected := abs(t, "testdata/root"); expected != rewrite {
t.Fatalf("Expected %s got %s", expected, rewrite)
}
}

1
symlink/testdata/fs/a/d vendored Symbolic link
View file

@ -0,0 +1 @@
/b

1
symlink/testdata/fs/a/e vendored Symbolic link
View file

@ -0,0 +1 @@
../b

1
symlink/testdata/fs/a/f vendored Symbolic link
View file

@ -0,0 +1 @@
../../../../test

1
symlink/testdata/fs/b/h vendored Symbolic link
View file

@ -0,0 +1 @@
../g

1
symlink/testdata/fs/g vendored Symbolic link
View file

@ -0,0 +1 @@
../../../../../../../../../../../../root