diff --git a/container.go b/container.go index 36330909ce..5959ec7600 100644 --- a/container.go +++ b/container.go @@ -774,14 +774,14 @@ func (container *Container) getBindMap() (map[string]BindMap, error) { } binds[path.Clean(dst)] = bindMap } - return binds, nil + return binds, nil } func (container *Container) createVolumes() error { - binds, err := container.getBindMap() - if err != nil { - return err - } + binds, err := container.getBindMap() + if err != nil { + return err + } volumesDriver := container.runtime.volumes.driver // Create the requested volumes if they don't exist for volPath := range container.Config.Volumes { @@ -824,26 +824,25 @@ func (container *Container) createVolumes() error { } container.Volumes[volPath] = srcPath container.VolumesRW[volPath] = srcRW + // Create the mountpoint - rootVolPath := path.Join(container.RootfsPath(), volPath) - if volIsDir { - if err := os.MkdirAll(rootVolPath, 0755); err != nil { - return err - } + volPath = path.Join(container.RootfsPath(), volPath) + rootVolPath, err := utils.FollowSymlink(volPath, container.RootfsPath()) + if err != nil { + panic(err) } - volPath = path.Join(container.RootfsPath(), volPath) - if _, err := os.Stat(volPath); err != nil { + if _, err := os.Stat(rootVolPath); err != nil { if os.IsNotExist(err) { if volIsDir { - if err := os.MkdirAll(volPath, 0755); err != nil { + if err := os.MkdirAll(rootVolPath, 0755); err != nil { return err } } else { - if err := os.MkdirAll(path.Dir(volPath), 0755); err != nil { + if err := os.MkdirAll(path.Dir(rootVolPath), 0755); err != nil { return err } - if f, err := os.OpenFile(volPath, os.O_CREATE, 0755); err != nil { + if f, err := os.OpenFile(rootVolPath, os.O_CREATE, 0755); err != nil { return err } else { f.Close() diff --git a/utils/fs.go b/utils/fs.go index a7bed8679a..e4897506d9 100644 --- a/utils/fs.go +++ b/utils/fs.go @@ -3,6 +3,7 @@ package utils import ( "os" "path/filepath" + "strings" "syscall" ) @@ -33,3 +34,49 @@ func TreeSize(dir string) (size int64, err error) { }) return } + +// 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 + } + root = filepath.Clean(root) + link, err := filepath.Abs(link) + if err != nil { + return "", err + } + link = filepath.Clean(link) + + for _, p := range strings.Split(link, "/") { + prev = filepath.Join(prev, p) + prev = filepath.Clean(prev) + + stat, err := os.Lstat(prev) + if err != nil { + if os.IsNotExist(err) { + continue + } + 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 '.': + if prev = filepath.Clean(filepath.Join(filepath.Dir(prev), dest)); len(prev) < len(root) { + prev = filepath.Join(root, filepath.Base(dest)) + } + } + } + } + return prev, nil +} diff --git a/utils/fs_test.go b/utils/fs_test.go new file mode 100644 index 0000000000..5f99ea771d --- /dev/null +++ b/utils/fs_test.go @@ -0,0 +1,84 @@ +package utils + +import ( + "os" + "path/filepath" + "testing" +) + +func abs(p string) string { + o, err := filepath.Abs(p) + if err != nil { + panic(err) + } + return o +} + +func TestFollowSymLinkNormal(t *testing.T) { + link := "testdata/fs/a/d/c/data" + + rewrite, err := FollowSymlink(link, "test") + if err != nil { + t.Fatal(err) + } + + if expected := abs("test/b/c/data"); expected != rewrite { + t.Fatalf("Expected %s got %s", expected, rewrite) + } +} + +func TestFollowSymLinkRandomString(t *testing.T) { + rewrite, err := FollowSymlink("toto", "test") + if err != nil { + t.Fatal(err) + } + + if rewrite != "toto" { + t.Fatalf("Expected toto got %s", rewrite) + } +} + +func TestFollowSymLinkLastLink(t *testing.T) { + link := "testdata/fs/a/d" + + rewrite, err := FollowSymlink(link, "test") + if err != nil { + t.Fatal(err) + } + + if expected := abs("test/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 := FollowSymlink(link, "test") + if err != nil { + t.Fatal(err) + } + + if expected := abs("testdata/fs/a/e/c/data"); expected != rewrite { + t.Fatalf("Expected %s got %s", expected, rewrite) + } +} + +func TestFollowSymLinkRelativeLinkScope(t *testing.T) { + link := "testdata/fs/a/f" + pwd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + root := filepath.Join(pwd, "testdata") + + rewrite, err := FollowSymlink(link, root) + if err != nil { + t.Fatal(err) + } + + if expected := abs("testdata/test"); expected != rewrite { + t.Fatalf("Expected %s got %s", expected, rewrite) + } +} diff --git a/utils/testdata/fs/a/d b/utils/testdata/fs/a/d new file mode 120000 index 0000000000..28abc96048 --- /dev/null +++ b/utils/testdata/fs/a/d @@ -0,0 +1 @@ +/b \ No newline at end of file diff --git a/utils/testdata/fs/a/e b/utils/testdata/fs/a/e new file mode 120000 index 0000000000..42532fe13c --- /dev/null +++ b/utils/testdata/fs/a/e @@ -0,0 +1 @@ +../b \ No newline at end of file diff --git a/utils/testdata/fs/a/f b/utils/testdata/fs/a/f new file mode 120000 index 0000000000..21de7edc0a --- /dev/null +++ b/utils/testdata/fs/a/f @@ -0,0 +1 @@ +../../../../test \ No newline at end of file