Skip to content

Commit

Permalink
handle symlinks for Docker's root dir & TMPDIR
Browse files Browse the repository at this point in the history
This removes the incomplete symlink handling from engine.go and it adds
it one place in docker.go.

It also enables handling symlinks for TMPDIR.

Docker-DCO-1.1-Signed-off-by: Cristian Staretu <cristian.staretu@gmail.com> (github: unclejack)
  • Loading branch information
unclejack committed Mar 3, 2014
1 parent aac9542 commit 611acf7
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 20 deletions.
24 changes: 22 additions & 2 deletions docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,27 @@ func main() {
return
}

eng, err := engine.New(*flRoot)
// set up the TempDir to use a canonical path
tmp := os.TempDir()
realTmp, err := utils.ReadSymlinkedDirectory(tmp)
if err != nil {
log.Fatalf("Unable to get the full path to the TempDir (%s): %s", tmp, err)
}
os.Setenv("TMPDIR", realTmp)

// get the canonical path to the Docker root directory
root := *flRoot
var realRoot string
if _, err := os.Stat(root); err != nil && os.IsNotExist(err) {
realRoot = root
} else {
realRoot, err = utils.ReadSymlinkedDirectory(root)
if err != nil {
log.Fatalf("Unable to get the full path to root (%s): %s", root, err)
}
}

eng, err := engine.New(realRoot)
if err != nil {
log.Fatal(err)
}
Expand All @@ -91,7 +111,7 @@ func main() {
// Load plugin: httpapi
job := eng.Job("initserver")
job.Setenv("Pidfile", *pidfile)
job.Setenv("Root", *flRoot)
job.Setenv("Root", realRoot)
job.SetenvBool("AutoRestart", *flAutoRestart)
job.SetenvList("Dns", flDns.GetAll())
job.SetenvBool("EnableIptables", *flEnableIptables)
Expand Down
12 changes: 8 additions & 4 deletions docs/sources/reference/commandline/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,15 @@ Using ``fd://`` will work perfectly for most setups but you can also specify ind
If the specified socket activated files aren't found then docker will exit.
You can find examples of using systemd socket activation with docker and systemd in the `docker source tree <https://github.com/dotcloud/docker/blob/master/contrib/init/systemd/socket-activation/>`_.

.. warning::
Docker and LXC do not support the use of softlinks for either the Docker data directory (``/var/lib/docker``) or for ``/tmp``.
If your system is likely to be set up in that way, you can use ``readlink -f`` to canonicalise the links:
Docker supports softlinks for the Docker data directory (``/var/lib/docker``) and for ``/tmp``.
TMPDIR and the data directory can be set like this:

``TMPDIR=$(readlink -f /tmp) /usr/local/bin/docker -d -D -g $(readlink -f /var/lib/docker) -H unix:// $EXPOSE_ALL > /var/lib/boot2docker/docker.log 2>&1``
::

TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
# or
export TMPDIR=/mnt/disk2/tmp
/usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1

.. _cli_attach:

Expand Down
14 changes: 0 additions & 14 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"io"
"log"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
Expand Down Expand Up @@ -90,19 +89,6 @@ func New(root string) (*Engine, error) {
return nil, err
}

// Docker makes some assumptions about the "absoluteness" of root
// ... so let's make sure it has no symlinks
if p, err := filepath.Abs(root); err != nil {
log.Fatalf("Unable to get absolute root (%s): %s", root, err)
} else {
root = p
}
if p, err := filepath.EvalSymlinks(root); err != nil {
log.Fatalf("Unable to canonicalize root (%s): %s", root, err)
} else {
root = p
}

eng := &Engine{
root: root,
handlers: make(map[string]Handler),
Expand Down
21 changes: 21 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -997,3 +997,24 @@ func ReplaceOrAppendEnvValues(defaults, overrides []string) []string {
}
return defaults
}

// ReadSymlinkedDirectory returns the target directory of a symlink.
// The target of the symbolic link may not be a file.
func ReadSymlinkedDirectory(path string) (string, error) {
var realPath string
var err error
if realPath, err = filepath.Abs(path); err != nil {
return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err)
}
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err)
}
realPathInfo, err := os.Stat(realPath)
if err != nil {
return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err)
}
if !realPathInfo.Mode().IsDir() {
return "", fmt.Errorf("canonical path points to a file '%s'", realPath)
}
return realPath, nil
}
76 changes: 76 additions & 0 deletions utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"io"
"io/ioutil"
"os"
"strings"
"testing"
)
Expand Down Expand Up @@ -498,3 +499,78 @@ func TestReplaceAndAppendEnvVars(t *testing.T) {
t.Fatalf("expected TERM=xterm got '%s'", env[1])
}
}

// Reading a symlink to a directory must return the directory
func TestReadSymlinkedDirectoryExistingDirectory(t *testing.T) {
var err error
if err = os.Mkdir("/tmp/testReadSymlinkToExistingDirectory", 0777); err != nil {
t.Errorf("failed to create directory: %s", err)
}

if err = os.Symlink("/tmp/testReadSymlinkToExistingDirectory", "/tmp/dirLinkTest"); err != nil {
t.Errorf("failed to create symlink: %s", err)
}

var path string
if path, err = ReadSymlinkedDirectory("/tmp/dirLinkTest"); err != nil {
t.Fatalf("failed to read symlink to directory: %s", err)
}

if path != "/tmp/testReadSymlinkToExistingDirectory" {
t.Fatalf("symlink returned unexpected directory: %s", path)
}

if err = os.Remove("/tmp/testReadSymlinkToExistingDirectory"); err != nil {
t.Errorf("failed to remove temporary directory: %s", err)
}

if err = os.Remove("/tmp/dirLinkTest"); err != nil {
t.Errorf("failed to remove symlink: %s", err)
}
}

// Reading a non-existing symlink must fail
func TestReadSymlinkedDirectoryNonExistingSymlink(t *testing.T) {
var path string
var err error
if path, err = ReadSymlinkedDirectory("/tmp/test/foo/Non/ExistingPath"); err == nil {
t.Fatalf("error expected for non-existing symlink")
}

if path != "" {
t.Fatalf("expected empty path, but '%s' was returned", path)
}
}

// Reading a symlink to a file must fail
func TestReadSymlinkedDirectoryToFile(t *testing.T) {
var err error
var file *os.File

if file, err = os.Create("/tmp/testReadSymlinkToFile"); err != nil {
t.Fatalf("failed to create file: %s", err)
}

file.Close()

if err = os.Symlink("/tmp/testReadSymlinkToFile", "/tmp/fileLinkTest"); err != nil {
t.Errorf("failed to create symlink: %s", err)
}

var path string
if path, err = ReadSymlinkedDirectory("/tmp/fileLinkTest"); err == nil {
t.Fatalf("ReadSymlinkedDirectory on a symlink to a file should've failed")
}

if path != "" {
t.Fatalf("path should've been empty: %s", path)
}

if err = os.Remove("/tmp/testReadSymlinkToFile"); err != nil {
t.Errorf("failed to remove file: %s", err)
}

if err = os.Remove("/tmp/fileLinkTest"); err != nil {
t.Errorf("failed to remove symlink: %s", err)
}
}

0 comments on commit 611acf7

Please sign in to comment.