From 5d8efc107d2c7b7da61a6d22657190c6f13713d2 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Fri, 14 Jun 2013 16:56:08 -0700 Subject: [PATCH 1/2] + Runtime: inject dockerinit at /.dockerinit instead of overwriting /sbin/init. This makes it possible to run /sbin/init inside a container. --- container.go | 2 +- docker/docker.go | 2 +- graph.go | 33 ++++++++++++++++++++++++++++++++- image.go | 14 ++++++++++++++ lxc_template.go | 2 +- runtime_test.go | 2 +- 6 files changed, 50 insertions(+), 5 deletions(-) diff --git a/container.go b/container.go index 3772cf29d2..e8159aae48 100644 --- a/container.go +++ b/container.go @@ -615,7 +615,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 fb7c465369..d609a64a51 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 42d1bdbd4c..1e60d6dbe8 100644 --- a/graph.go +++ b/graph.go @@ -193,8 +193,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, 0700); 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 e1b1ac0418..af7b5030be 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 93b795e901..e00b2f88c5 100644 --- a/lxc_template.go +++ b/lxc_template.go @@ -79,7 +79,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 9d43bd46e5..7c30322fc5 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -69,7 +69,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 } From 945033f1ccbc4896e1a784d00f1f2b465ed9e04b Mon Sep 17 00:00:00 2001 From: Sridatta Thatipamala Date: Mon, 22 Jul 2013 14:55:07 -0700 Subject: [PATCH 2/2] change permissions of initLayer to be readable by non-root users --- graph.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph.go b/graph.go index 1e60d6dbe8..b75ddb7ec0 100644 --- a/graph.go +++ b/graph.go @@ -205,7 +205,7 @@ func (graph *Graph) getDockerInitLayer() (string, error) { return "", err } initLayer := tmp.imageRoot("_dockerinit") - if err := os.Mkdir(initLayer, 0700); err != nil && !os.IsExist(err) { + if err := os.Mkdir(initLayer, 0755); err != nil && !os.IsExist(err) { // If directory already existed, keep going. // For all other errors, abort. return "", err