diff --git a/container.go b/container.go index 56a4e191a1..6c92973920 100644 --- a/container.go +++ b/container.go @@ -631,7 +631,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { "-n", container.ID, "-f", container.lxcConfigPath(), "--", - "/sbin/init", + "/.dockerinit", } // Networking diff --git a/docker/docker.go b/docker/docker.go index 2db50bf328..8d7bf9b42a 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -19,7 +19,7 @@ var ( ) func main() { - if utils.SelfPath() == "/sbin/init" { + if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" { // Running in init mode docker.SysInit() return diff --git a/graph.go b/graph.go index bf27f93ed4..1492735d7e 100644 --- a/graph.go +++ b/graph.go @@ -177,8 +177,39 @@ func (graph *Graph) Mktemp(id string) (string, error) { return tmp.imageRoot(id), nil } +// getDockerInitLayer returns the path of a layer containing a mountpoint suitable +// for bind-mounting dockerinit into the container. The mountpoint is simply an +// empty file at /.dockerinit +// +// This extra layer is used by all containers as the top-most ro layer. It protects +// the container from unwanted side-effects on the rw layer. +func (graph *Graph) getDockerInitLayer() (string, error) { + tmp, err := graph.tmp() + if err != nil { + return "", err + } + initLayer := tmp.imageRoot("_dockerinit") + if err := os.Mkdir(initLayer, 0755); err != nil && !os.IsExist(err) { + // If directory already existed, keep going. + // For all other errors, abort. + return "", err + } + // FIXME: how the hell do I break down this line in a way + // that is idiomatic and not ugly as hell? + if f, err := os.OpenFile(path.Join(initLayer, ".dockerinit"), os.O_CREATE|os.O_TRUNC, 0700); err != nil && !os.IsExist(err) { + // If file already existed, keep going. + // For all other errors, abort. + return "", err + } else { + f.Close() + } + // Layer is ready to use, if it wasn't before. + return initLayer, nil +} + func (graph *Graph) tmp() (*Graph, error) { - return NewGraph(path.Join(graph.Root, ":tmp:")) + // Changed to _tmp from :tmp:, because it messed with ":" separators in aufs branch syntax... + return NewGraph(path.Join(graph.Root, "_tmp")) } // Check if given error is "not empty". diff --git a/image.go b/image.go index 220f1c70a5..9f34160b80 100644 --- a/image.go +++ b/image.go @@ -263,6 +263,13 @@ func (img *Image) layers() ([]string, error) { if len(list) == 0 { return nil, fmt.Errorf("No layer found for image %s\n", img.ID) } + + // Inject the dockerinit layer (empty place-holder for mount-binding dockerinit) + if dockerinitLayer, err := img.getDockerInitLayer(); err != nil { + return nil, err + } else { + list = append([]string{dockerinitLayer}, list...) + } return list, nil } @@ -292,6 +299,13 @@ func (img *Image) GetParent() (*Image, error) { return img.graph.Get(img.Parent) } +func (img *Image) getDockerInitLayer() (string, error) { + if img.graph == nil { + return "", fmt.Errorf("Can't lookup dockerinit layer of unregistered image") + } + return img.graph.getDockerInitLayer() +} + func (img *Image) root() (string, error) { if img.graph == nil { return "", fmt.Errorf("Can't lookup root of unregistered image") diff --git a/lxc_template.go b/lxc_template.go index d1f67e3376..d49d66b4d9 100644 --- a/lxc_template.go +++ b/lxc_template.go @@ -84,7 +84,7 @@ lxc.mount.entry = devpts {{$ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,no #lxc.mount.entry = shm {{$ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0 # Inject docker-init -lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/sbin/init none bind,ro 0 0 +lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/.dockerinit none bind,ro 0 0 # In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0 diff --git a/runtime_test.go b/runtime_test.go index 0b0f62f199..512fc13782 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -73,7 +73,7 @@ func layerArchive(tarfile string) (io.Reader, error) { func init() { // Hack to run sys init during unit testing - if utils.SelfPath() == "/sbin/init" { + if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" { SysInit() return }