From 7f9e0262adf118f1bf2fa5fae51aea2b8307ce9d Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Wed, 14 Jun 2017 01:57:02 +0000 Subject: [PATCH 1/3] Unmount /dev/shm when stop sandbox. Signed-off-by: Lantao Liu --- pkg/os/os.go | 8 +++++++- pkg/server/sandbox_remove.go | 1 - pkg/server/sandbox_run.go | 18 ++++++++++++------ pkg/server/sandbox_stop.go | 5 +++++ pkg/server/sandbox_stop_test.go | 13 +++++++++++++ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/pkg/os/os.go b/pkg/os/os.go index 54a650a262c5..6c562d42776c 100644 --- a/pkg/os/os.go +++ b/pkg/os/os.go @@ -22,6 +22,7 @@ import ( "os" "github.com/containerd/fifo" + "github.com/docker/docker/pkg/mount" "golang.org/x/net/context" "golang.org/x/sys/unix" ) @@ -90,7 +91,12 @@ func (RealOS) Mount(source string, target string, fstype string, flags uintptr, return unix.Mount(source, target, fstype, flags, data) } -// Unmount will call unix.Unmount to unmount the file. +// Unmount will call unix.Unmount to unmount the file. The function doesn't +// return error if target is not mounted. func (RealOS) Unmount(target string, flags int) error { + // TODO(random-liu): Follow symlink to make sure the result is correct. + if mounted, err := mount.Mounted(target); err != nil || !mounted { + return err + } return unix.Unmount(target, flags) } diff --git a/pkg/server/sandbox_remove.go b/pkg/server/sandbox_remove.go index d490175e2455..d7d018aa326b 100644 --- a/pkg/server/sandbox_remove.go +++ b/pkg/server/sandbox_remove.go @@ -73,7 +73,6 @@ func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime. glog.V(5).Infof("Remove called for snapshot %q that does not exist", id) } - // TODO(random-liu): [P0] Cleanup shm created in RunPodSandbox. // TODO(random-liu): [P1] Remove permanent namespace once used. // Cleanup the sandbox root directory. diff --git a/pkg/server/sandbox_run.go b/pkg/server/sandbox_run.go index 1099257ae6be..3e15e44b76a3 100644 --- a/pkg/server/sandbox_run.go +++ b/pkg/server/sandbox_run.go @@ -183,7 +183,10 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run } defer func() { if retErr != nil { - c.cleanupSandboxFiles(sandboxRootDir, config) + if err = c.unmountSandboxFiles(sandboxRootDir, config); err != nil { + glog.Errorf("Failed to unmount sandbox files in %q: %v", + sandboxRootDir, err) + } } }() @@ -403,12 +406,15 @@ func parseDNSOptions(servers, searches, options []string) (string, error) { return resolvContent, nil } -// cleanupSandboxFiles only unmount files, we rely on the removal of sandbox root directory to remove files. -// Each cleanup task should log error instead of returning, so as to keep on cleanup on error. -func (c *criContainerdService) cleanupSandboxFiles(rootDir string, config *runtime.PodSandboxConfig) { +// unmountSandboxFiles unmount some sandbox files, we rely on the removal of sandbox root directory to +// remove these files. Unmount should *NOT* return error when: +// 1) The mount point is already unmounted. +// 2) The mount point doesn't exist. +func (c *criContainerdService) unmountSandboxFiles(rootDir string, config *runtime.PodSandboxConfig) error { if !config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostIpc() { - if err := c.os.Unmount(getSandboxDevShm(rootDir), unix.MNT_DETACH); err != nil && os.IsNotExist(err) { - glog.Errorf("failed to unmount sandbox shm: %v", err) + if err := c.os.Unmount(getSandboxDevShm(rootDir), unix.MNT_DETACH); err != nil && !os.IsNotExist(err) { + return err } } + return nil } diff --git a/pkg/server/sandbox_stop.go b/pkg/server/sandbox_stop.go index 5d6cf9498c8b..8482353dab73 100644 --- a/pkg/server/sandbox_stop.go +++ b/pkg/server/sandbox_stop.go @@ -58,6 +58,11 @@ func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.St } glog.V(2).Infof("TearDown network for sandbox %q successfully", id) + sandboxRoot := getSandboxRootDir(c.rootDir, id) + if err = c.unmountSandboxFiles(sandboxRoot, sandbox.Config); err != nil { + return nil, fmt.Errorf("failed to unmount sandbox files in %q: %v", sandboxRoot, err) + } + // TODO(random-liu): [P1] Handle sandbox container graceful deletion. // Delete the sandbox container from containerd. _, err = c.taskService.Delete(ctx, &execution.DeleteRequest{ContainerID: id}) diff --git a/pkg/server/sandbox_stop_test.go b/pkg/server/sandbox_stop_test.go index cb361d734539..d039da477c58 100644 --- a/pkg/server/sandbox_stop_test.go +++ b/pkg/server/sandbox_stop_test.go @@ -58,6 +58,7 @@ func TestStopPodSandbox(t *testing.T) { injectErr error injectStatErr error injectCNIErr error + injectUnmountErr error expectErr bool expectCalls []string expectedCNICalls []string @@ -115,6 +116,15 @@ func TestStopPodSandbox(t *testing.T) { injectCNIErr: errors.New("arbitrary error"), expectCalls: []string{}, }, + "stop sandbox with unmount error": { + sandboxTasks: []task.Task{testContainer}, + injectSandbox: true, + expectErr: true, + expectedCNICalls: []string{"TearDownPod"}, + injectCNIErr: errors.New("arbitrary error"), + injectUnmountErr: errors.New("arbitrary error"), + expectCalls: []string{}, + }, } { t.Logf("TestCase %q", desc) c := newTestCRIContainerdService() @@ -136,6 +146,9 @@ func TestStopPodSandbox(t *testing.T) { if test.injectStatErr != nil { fakeOS.InjectError("Stat", test.injectStatErr) } + if test.injectUnmountErr != nil { + fakeOS.InjectError("Unmount", test.injectUnmountErr) + } fakeCNIPlugin.SetFakePodNetwork(testSandbox.NetNS, testSandbox.Config.GetMetadata().GetNamespace(), testSandbox.Config.GetMetadata().GetName(), testID, sandboxStatusTestIP) From 53367bbd142472ec3919aa8103996ce5c77d30af Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Wed, 14 Jun 2017 02:02:20 +0000 Subject: [PATCH 2/3] Stop/remove all containers when stop/remove sandbox. Signed-off-by: Lantao Liu --- pkg/server/container_stop.go | 33 +++++---- pkg/server/sandbox_remove.go | 21 +++++- pkg/server/sandbox_remove_test.go | 55 +++++++++++++++ pkg/server/sandbox_stop.go | 20 +++++- pkg/server/sandbox_stop_test.go | 107 ++++++++++++++++++++++++++++++ 5 files changed, 221 insertions(+), 15 deletions(-) diff --git a/pkg/server/container_stop.go b/pkg/server/container_stop.go index 6fd49aa95ad2..95abc7968f4f 100644 --- a/pkg/server/container_stop.go +++ b/pkg/server/container_stop.go @@ -55,59 +55,68 @@ func (c *criContainerdService) StopContainer(ctx context.Context, r *runtime.Sto if err != nil { return nil, fmt.Errorf("an error occurred when try to find container %q: %v", r.GetContainerId(), err) } - id := r.GetContainerId() + + if err := c.stopContainer(ctx, meta, time.Duration(r.GetTimeout())*time.Second); err != nil { + return nil, err + } + + return &runtime.StopContainerResponse{}, nil +} + +// stopContainer stops a container based on the container metadata. +func (c *criContainerdService) stopContainer(ctx context.Context, meta *metadata.ContainerMetadata, timeout time.Duration) error { + id := meta.ID // Return without error if container is not running. This makes sure that // stop only takes real action after the container is started. if meta.State() != runtime.ContainerState_CONTAINER_RUNNING { glog.V(2).Infof("Container to stop %q is not running, current state %q", id, criContainerStateToString(meta.State())) - return &runtime.StopContainerResponse{}, nil + return nil } - if r.GetTimeout() > 0 { + if timeout > 0 { // TODO(random-liu): [P1] Get stop signal from image config. stopSignal := unix.SIGTERM glog.V(2).Infof("Stop container %q with signal %v", id, stopSignal) - _, err = c.taskService.Kill(ctx, &execution.KillRequest{ + _, err := c.taskService.Kill(ctx, &execution.KillRequest{ ContainerID: id, Signal: uint32(stopSignal), PidOrAll: &execution.KillRequest_All{All: true}, }) if err != nil { if !isContainerdGRPCNotFoundError(err) && !isRuncProcessAlreadyFinishedError(err) { - return nil, fmt.Errorf("failed to stop container %q: %v", id, err) + return fmt.Errorf("failed to stop container %q: %v", id, err) } // Move on to make sure container status is updated. } - err = c.waitContainerStop(ctx, id, time.Duration(r.GetTimeout())*time.Second) + err = c.waitContainerStop(ctx, id, timeout) if err == nil { - return &runtime.StopContainerResponse{}, nil + return nil } glog.Errorf("Stop container %q timed out: %v", id, err) } // Event handler will Delete the container from containerd after it handles the Exited event. glog.V(2).Infof("Kill container %q", id) - _, err = c.taskService.Kill(ctx, &execution.KillRequest{ + _, err := c.taskService.Kill(ctx, &execution.KillRequest{ ContainerID: id, Signal: uint32(unix.SIGKILL), PidOrAll: &execution.KillRequest_All{All: true}, }) if err != nil { if !isContainerdGRPCNotFoundError(err) && !isRuncProcessAlreadyFinishedError(err) { - return nil, fmt.Errorf("failed to kill container %q: %v", id, err) + return fmt.Errorf("failed to kill container %q: %v", id, err) } // Move on to make sure container status is updated. } // Wait for a fixed timeout until container stop is observed by event monitor. if err := c.waitContainerStop(ctx, id, killContainerTimeout); err != nil { - return nil, fmt.Errorf("an error occurs during waiting for container %q to stop: %v", - id, err) + return fmt.Errorf("an error occurs during waiting for container %q to stop: %v", id, err) } - return &runtime.StopContainerResponse{}, nil + return nil } // waitContainerStop polls container state until timeout exceeds or container is stopped. diff --git a/pkg/server/sandbox_remove.go b/pkg/server/sandbox_remove.go index d7d018aa326b..298bb888e22b 100644 --- a/pkg/server/sandbox_remove.go +++ b/pkg/server/sandbox_remove.go @@ -53,8 +53,6 @@ func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime. // Use the full sandbox id. id := sandbox.ID - // TODO(random-liu): [P2] Remove all containers in the sandbox. - // Return error if sandbox container is not fully stopped. // TODO(random-liu): [P0] Make sure network is torn down, may need to introduce a state. _, err = c.taskService.Info(ctx, &execution.InfoRequest{ContainerID: id}) @@ -73,6 +71,25 @@ func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime. glog.V(5).Infof("Remove called for snapshot %q that does not exist", id) } + // Remove all containers inside the sandbox. + // NOTE(random-liu): container could still be created after this point, Kubelet should + // not rely on this behavior. + // TODO(random-liu): Introduce an intermediate state to avoid container creation after + // this point. + cntrs, err := c.containerStore.List() + if err != nil { + return nil, fmt.Errorf("failed to list all containers: %v", err) + } + for _, cntr := range cntrs { + if cntr.SandboxID != id { + continue + } + _, err = c.RemoveContainer(ctx, &runtime.RemoveContainerRequest{ContainerId: cntr.ID}) + if err != nil { + return nil, fmt.Errorf("failed to remove container %q: %v", cntr.ID, err) + } + } + // TODO(random-liu): [P1] Remove permanent namespace once used. // Cleanup the sandbox root directory. diff --git a/pkg/server/sandbox_remove_test.go b/pkg/server/sandbox_remove_test.go index 378d6fee5764..13ffae32ca8f 100644 --- a/pkg/server/sandbox_remove_test.go +++ b/pkg/server/sandbox_remove_test.go @@ -17,6 +17,7 @@ package server import ( "fmt" "testing" + "time" "github.com/containerd/containerd/api/services/containers" snapshotapi "github.com/containerd/containerd/api/services/snapshot" @@ -176,3 +177,57 @@ func TestRemovePodSandbox(t *testing.T) { assert.NotNil(t, res, "remove should be idempotent") } } + +func TestRemoveContainersInSandbox(t *testing.T) { + testID := "test-id" + testName := "test-name" + testMetadata := metadata.SandboxMetadata{ + ID: testID, + Name: testName, + } + testContainersMetadata := []*metadata.ContainerMetadata{ + { + ID: "test-cid-1", + Name: "test-cname-1", + SandboxID: testID, + FinishedAt: time.Now().UnixNano(), + }, + { + ID: "test-cid-2", + Name: "test-cname-2", + SandboxID: testID, + FinishedAt: time.Now().UnixNano(), + }, + { + ID: "test-cid-3", + Name: "test-cname-3", + SandboxID: "other-sandbox-id", + FinishedAt: time.Now().UnixNano(), + }, + } + + c := newTestCRIContainerdService() + WithFakeSnapshotClient(c) + assert.NoError(t, c.sandboxNameIndex.Reserve(testName, testID)) + assert.NoError(t, c.sandboxIDIndex.Add(testID)) + assert.NoError(t, c.sandboxStore.Create(testMetadata)) + for _, cntr := range testContainersMetadata { + assert.NoError(t, c.containerNameIndex.Reserve(cntr.Name, cntr.ID)) + assert.NoError(t, c.containerStore.Create(*cntr)) + } + + res, err := c.RemovePodSandbox(context.Background(), &runtime.RemovePodSandboxRequest{ + PodSandboxId: testID, + }) + assert.NoError(t, err) + assert.NotNil(t, res) + + meta, err := c.sandboxStore.Get(testID) + assert.Error(t, err) + assert.True(t, metadata.IsNotExistError(err)) + assert.Nil(t, meta, "sandbox metadata should be removed") + + cntrs, err := c.containerStore.List() + assert.NoError(t, err) + assert.Equal(t, testContainersMetadata[2:], cntrs, "container metadata should be removed") +} diff --git a/pkg/server/sandbox_stop.go b/pkg/server/sandbox_stop.go index 8482353dab73..2474951a08dc 100644 --- a/pkg/server/sandbox_stop.go +++ b/pkg/server/sandbox_stop.go @@ -46,6 +46,25 @@ func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.St // Use the full sandbox id. id := sandbox.ID + // Stop all containers inside the sandbox. This terminates the container forcibly, + // and container may still be so production should not rely on this behavior. + // TODO(random-liu): Delete the sandbox container before this after permanent network namespace + // is introduced, so that no container will be started after that. + containers, err := c.containerStore.List() + if err != nil { + return nil, fmt.Errorf("failed to list all containers: %v", err) + } + for _, container := range containers { + if container.SandboxID != id { + continue + } + // Forcibly stop the container. Do not use `StopContainer`, because it introduces a race + // if a container is removed after list. + if err = c.stopContainer(ctx, container, 0); err != nil { + return nil, fmt.Errorf("failed to stop container %q: %v", container.ID, err) + } + } + // Teardown network for sandbox. _, err = c.os.Stat(sandbox.NetNS) if err == nil { @@ -70,6 +89,5 @@ func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.St return nil, fmt.Errorf("failed to delete sandbox container %q: %v", id, err) } - // TODO(random-liu): [P2] Stop all containers inside the sandbox. return &runtime.StopPodSandboxResponse{}, nil } diff --git a/pkg/server/sandbox_stop_test.go b/pkg/server/sandbox_stop_test.go index d039da477c58..e23ee146853e 100644 --- a/pkg/server/sandbox_stop_test.go +++ b/pkg/server/sandbox_stop_test.go @@ -20,7 +20,9 @@ import ( "errors" "os" "testing" + "time" + "github.com/containerd/containerd/api/services/execution" "github.com/containerd/containerd/api/types/task" "github.com/stretchr/testify/assert" "golang.org/x/net/context" @@ -166,3 +168,108 @@ func TestStopPodSandbox(t *testing.T) { assert.Equal(t, test.expectedCNICalls, fakeCNIPlugin.GetCalledNames()) } } + +func TestStopContainersInSandbox(t *testing.T) { + testID := "test-id" + testSandbox := metadata.SandboxMetadata{ + ID: testID, + Name: "test-name", + Config: &runtime.PodSandboxConfig{ + Metadata: &runtime.PodSandboxMetadata{ + Name: "test-name", + Uid: "test-uid", + Namespace: "test-ns", + }}, + NetNS: "test-netns", + } + testContainers := []metadata.ContainerMetadata{ + { + ID: "test-cid-1", + Name: "test-cname-1", + SandboxID: testID, + Pid: 2, + StartedAt: time.Now().UnixNano(), + }, + { + ID: "test-cid-2", + Name: "test-cname-2", + SandboxID: testID, + Pid: 3, + StartedAt: time.Now().UnixNano(), + }, + { + ID: "test-cid-3", + Name: "test-cname-3", + SandboxID: "other-sandbox-id", + Pid: 4, + StartedAt: time.Now().UnixNano(), + }, + } + testContainerdContainers := []task.Task{ + { + ID: testID, + Pid: 1, + Status: task.StatusRunning, + }, + { + ID: "test-cid-1", + Pid: 2, + Status: task.StatusRunning, + }, + { + ID: "test-cid-2", + Pid: 3, + Status: task.StatusRunning, + }, + { + ID: "test-cid-3", + Pid: 4, + Status: task.StatusRunning, + }, + } + + c := newTestCRIContainerdService() + fake := servertesting.NewFakeExecutionClient().WithEvents() + defer fake.Stop() + c.taskService = fake + fake.SetFakeTasks(testContainerdContainers) + assert.NoError(t, c.sandboxStore.Create(testSandbox)) + assert.NoError(t, c.sandboxIDIndex.Add(testID)) + for _, cntr := range testContainers { + assert.NoError(t, c.containerStore.Create(cntr)) + } + + fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin) + fakeCNIPlugin.SetFakePodNetwork(testSandbox.NetNS, testSandbox.Config.GetMetadata().GetNamespace(), + testSandbox.Config.GetMetadata().GetName(), testID, sandboxStatusTestIP) + + eventClient, err := fake.Events(context.Background(), &execution.EventsRequest{}) + assert.NoError(t, err) + // Start a simple test event monitor. + go func(e execution.Tasks_EventsClient) { + for { + if err := c.handleEventStream(e); err != nil { // nolint: vetshadow + return + } + } + }(eventClient) + res, err := c.StopPodSandbox(context.Background(), &runtime.StopPodSandboxRequest{ + PodSandboxId: testID, + }) + assert.NoError(t, err) + assert.NotNil(t, res) + + cntrs, err := c.containerStore.List() + assert.NoError(t, err) + assert.Len(t, cntrs, 3) + expectedStates := map[string]runtime.ContainerState{ + "test-cid-1": runtime.ContainerState_CONTAINER_EXITED, + "test-cid-2": runtime.ContainerState_CONTAINER_EXITED, + "test-cid-3": runtime.ContainerState_CONTAINER_RUNNING, + } + for id, expected := range expectedStates { + cntr, err := c.containerStore.Get(id) + assert.NoError(t, err) + assert.Equal(t, expected, cntr.State()) + } +} From 57b8b4358d443cd10cc7b4f540ca32f60c83037e Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Wed, 14 Jun 2017 17:12:05 +0000 Subject: [PATCH 3/3] Update godeps. Signed-off-by: Lantao Liu --- Godeps/Godeps.json | 5 + .../docker/docker/pkg/mount/flags.go | 149 ++++++++++++++++++ .../docker/docker/pkg/mount/flags_freebsd.go | 48 ++++++ .../docker/docker/pkg/mount/flags_linux.go | 85 ++++++++++ .../docker/pkg/mount/flags_unsupported.go | 30 ++++ .../docker/docker/pkg/mount/mount.go | 74 +++++++++ .../docker/pkg/mount/mounter_freebsd.go | 59 +++++++ .../docker/docker/pkg/mount/mounter_linux.go | 21 +++ .../docker/pkg/mount/mounter_solaris.go | 33 ++++ .../docker/pkg/mount/mounter_unsupported.go | 11 ++ .../docker/docker/pkg/mount/mountinfo.go | 40 +++++ .../docker/pkg/mount/mountinfo_freebsd.go | 41 +++++ .../docker/pkg/mount/mountinfo_linux.go | 95 +++++++++++ .../docker/pkg/mount/mountinfo_solaris.go | 37 +++++ .../docker/pkg/mount/mountinfo_unsupported.go | 12 ++ .../docker/pkg/mount/mountinfo_windows.go | 6 + .../docker/pkg/mount/sharedsubtree_linux.go | 69 ++++++++ .../docker/pkg/mount/sharedsubtree_solaris.go | 58 +++++++ 18 files changed, 873 insertions(+) create mode 100644 vendor/github.com/docker/docker/pkg/mount/flags.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/flags_freebsd.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/flags_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/flags_unsupported.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mount.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mounter_freebsd.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mounter_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mounter_solaris.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mounter_unsupported.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mountinfo.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mountinfo_freebsd.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mountinfo_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mountinfo_solaris.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mountinfo_unsupported.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/mountinfo_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/mount/sharedsubtree_solaris.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 262359403f4a..b7763c3f6784 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -219,6 +219,11 @@ "Comment": "v2.6.0-rc.1-130-gb38e583", "Rev": "b38e5838b7b2f2ad48e06ec4b500011976080621" }, + { + "ImportPath": "github.com/docker/docker/pkg/mount", + "Comment": "v1.13.1", + "Rev": "092cba3727bb9b4a2f0e922cd6c0f93ea270e363" + }, { "ImportPath": "github.com/docker/docker/pkg/random", "Comment": "v1.13.1", diff --git a/vendor/github.com/docker/docker/pkg/mount/flags.go b/vendor/github.com/docker/docker/pkg/mount/flags.go new file mode 100644 index 000000000000..607dbed43a0a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/flags.go @@ -0,0 +1,149 @@ +package mount + +import ( + "fmt" + "strings" +) + +var flags = map[string]struct { + clear bool + flag int +}{ + "defaults": {false, 0}, + "ro": {false, RDONLY}, + "rw": {true, RDONLY}, + "suid": {true, NOSUID}, + "nosuid": {false, NOSUID}, + "dev": {true, NODEV}, + "nodev": {false, NODEV}, + "exec": {true, NOEXEC}, + "noexec": {false, NOEXEC}, + "sync": {false, SYNCHRONOUS}, + "async": {true, SYNCHRONOUS}, + "dirsync": {false, DIRSYNC}, + "remount": {false, REMOUNT}, + "mand": {false, MANDLOCK}, + "nomand": {true, MANDLOCK}, + "atime": {true, NOATIME}, + "noatime": {false, NOATIME}, + "diratime": {true, NODIRATIME}, + "nodiratime": {false, NODIRATIME}, + "bind": {false, BIND}, + "rbind": {false, RBIND}, + "unbindable": {false, UNBINDABLE}, + "runbindable": {false, RUNBINDABLE}, + "private": {false, PRIVATE}, + "rprivate": {false, RPRIVATE}, + "shared": {false, SHARED}, + "rshared": {false, RSHARED}, + "slave": {false, SLAVE}, + "rslave": {false, RSLAVE}, + "relatime": {false, RELATIME}, + "norelatime": {true, RELATIME}, + "strictatime": {false, STRICTATIME}, + "nostrictatime": {true, STRICTATIME}, +} + +var validFlags = map[string]bool{ + "": true, + "size": true, + "mode": true, + "uid": true, + "gid": true, + "nr_inodes": true, + "nr_blocks": true, + "mpol": true, +} + +var propagationFlags = map[string]bool{ + "bind": true, + "rbind": true, + "unbindable": true, + "runbindable": true, + "private": true, + "rprivate": true, + "shared": true, + "rshared": true, + "slave": true, + "rslave": true, +} + +// MergeTmpfsOptions merge mount options to make sure there is no duplicate. +func MergeTmpfsOptions(options []string) ([]string, error) { + // We use collisions maps to remove duplicates. + // For flag, the key is the flag value (the key for propagation flag is -1) + // For data=value, the key is the data + flagCollisions := map[int]bool{} + dataCollisions := map[string]bool{} + + var newOptions []string + // We process in reverse order + for i := len(options) - 1; i >= 0; i-- { + option := options[i] + if option == "defaults" { + continue + } + if f, ok := flags[option]; ok && f.flag != 0 { + // There is only one propagation mode + key := f.flag + if propagationFlags[option] { + key = -1 + } + // Check to see if there is collision for flag + if !flagCollisions[key] { + // We prepend the option and add to collision map + newOptions = append([]string{option}, newOptions...) + flagCollisions[key] = true + } + continue + } + opt := strings.SplitN(option, "=", 2) + if len(opt) != 2 || !validFlags[opt[0]] { + return nil, fmt.Errorf("Invalid tmpfs option %q", opt) + } + if !dataCollisions[opt[0]] { + // We prepend the option and add to collision map + newOptions = append([]string{option}, newOptions...) + dataCollisions[opt[0]] = true + } + } + + return newOptions, nil +} + +// Parse fstab type mount options into mount() flags +// and device specific data +func parseOptions(options string) (int, string) { + var ( + flag int + data []string + ) + + for _, o := range strings.Split(options, ",") { + // If the option does not exist in the flags table or the flag + // is not supported on the platform, + // then it is a data value for a specific fs type + if f, exists := flags[o]; exists && f.flag != 0 { + if f.clear { + flag &= ^f.flag + } else { + flag |= f.flag + } + } else { + data = append(data, o) + } + } + return flag, strings.Join(data, ",") +} + +// ParseTmpfsOptions parse fstab type mount options into flags and data +func ParseTmpfsOptions(options string) (int, string, error) { + flags, data := parseOptions(options) + for _, o := range strings.Split(data, ",") { + opt := strings.SplitN(o, "=", 2) + if !validFlags[opt[0]] { + return 0, "", fmt.Errorf("Invalid tmpfs option %q", opt) + } + } + return flags, data, nil +} diff --git a/vendor/github.com/docker/docker/pkg/mount/flags_freebsd.go b/vendor/github.com/docker/docker/pkg/mount/flags_freebsd.go new file mode 100644 index 000000000000..f166cb2f7786 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/flags_freebsd.go @@ -0,0 +1,48 @@ +// +build freebsd,cgo + +package mount + +/* +#include +*/ +import "C" + +const ( + // RDONLY will mount the filesystem as read-only. + RDONLY = C.MNT_RDONLY + + // NOSUID will not allow set-user-identifier or set-group-identifier bits to + // take effect. + NOSUID = C.MNT_NOSUID + + // NOEXEC will not allow execution of any binaries on the mounted file system. + NOEXEC = C.MNT_NOEXEC + + // SYNCHRONOUS will allow any I/O to the file system to be done synchronously. + SYNCHRONOUS = C.MNT_SYNCHRONOUS + + // NOATIME will not update the file access time when reading from a file. + NOATIME = C.MNT_NOATIME +) + +// These flags are unsupported. +const ( + BIND = 0 + DIRSYNC = 0 + MANDLOCK = 0 + NODEV = 0 + NODIRATIME = 0 + UNBINDABLE = 0 + RUNBINDABLE = 0 + PRIVATE = 0 + RPRIVATE = 0 + SHARED = 0 + RSHARED = 0 + SLAVE = 0 + RSLAVE = 0 + RBIND = 0 + RELATIVE = 0 + RELATIME = 0 + REMOUNT = 0 + STRICTATIME = 0 +) diff --git a/vendor/github.com/docker/docker/pkg/mount/flags_linux.go b/vendor/github.com/docker/docker/pkg/mount/flags_linux.go new file mode 100644 index 000000000000..dc696dce9075 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/flags_linux.go @@ -0,0 +1,85 @@ +package mount + +import ( + "syscall" +) + +const ( + // RDONLY will mount the file system read-only. + RDONLY = syscall.MS_RDONLY + + // NOSUID will not allow set-user-identifier or set-group-identifier bits to + // take effect. + NOSUID = syscall.MS_NOSUID + + // NODEV will not interpret character or block special devices on the file + // system. + NODEV = syscall.MS_NODEV + + // NOEXEC will not allow execution of any binaries on the mounted file system. + NOEXEC = syscall.MS_NOEXEC + + // SYNCHRONOUS will allow I/O to the file system to be done synchronously. + SYNCHRONOUS = syscall.MS_SYNCHRONOUS + + // DIRSYNC will force all directory updates within the file system to be done + // synchronously. This affects the following system calls: create, link, + // unlink, symlink, mkdir, rmdir, mknod and rename. + DIRSYNC = syscall.MS_DIRSYNC + + // REMOUNT will attempt to remount an already-mounted file system. This is + // commonly used to change the mount flags for a file system, especially to + // make a readonly file system writeable. It does not change device or mount + // point. + REMOUNT = syscall.MS_REMOUNT + + // MANDLOCK will force mandatory locks on a filesystem. + MANDLOCK = syscall.MS_MANDLOCK + + // NOATIME will not update the file access time when reading from a file. + NOATIME = syscall.MS_NOATIME + + // NODIRATIME will not update the directory access time. + NODIRATIME = syscall.MS_NODIRATIME + + // BIND remounts a subtree somewhere else. + BIND = syscall.MS_BIND + + // RBIND remounts a subtree and all possible submounts somewhere else. + RBIND = syscall.MS_BIND | syscall.MS_REC + + // UNBINDABLE creates a mount which cannot be cloned through a bind operation. + UNBINDABLE = syscall.MS_UNBINDABLE + + // RUNBINDABLE marks the entire mount tree as UNBINDABLE. + RUNBINDABLE = syscall.MS_UNBINDABLE | syscall.MS_REC + + // PRIVATE creates a mount which carries no propagation abilities. + PRIVATE = syscall.MS_PRIVATE + + // RPRIVATE marks the entire mount tree as PRIVATE. + RPRIVATE = syscall.MS_PRIVATE | syscall.MS_REC + + // SLAVE creates a mount which receives propagation from its master, but not + // vice versa. + SLAVE = syscall.MS_SLAVE + + // RSLAVE marks the entire mount tree as SLAVE. + RSLAVE = syscall.MS_SLAVE | syscall.MS_REC + + // SHARED creates a mount which provides the ability to create mirrors of + // that mount such that mounts and unmounts within any of the mirrors + // propagate to the other mirrors. + SHARED = syscall.MS_SHARED + + // RSHARED marks the entire mount tree as SHARED. + RSHARED = syscall.MS_SHARED | syscall.MS_REC + + // RELATIME updates inode access times relative to modify or change time. + RELATIME = syscall.MS_RELATIME + + // STRICTATIME allows to explicitly request full atime updates. This makes + // it possible for the kernel to default to relatime or noatime but still + // allow userspace to override it. + STRICTATIME = syscall.MS_STRICTATIME +) diff --git a/vendor/github.com/docker/docker/pkg/mount/flags_unsupported.go b/vendor/github.com/docker/docker/pkg/mount/flags_unsupported.go new file mode 100644 index 000000000000..5564f7b3cdea --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/flags_unsupported.go @@ -0,0 +1,30 @@ +// +build !linux,!freebsd freebsd,!cgo solaris,!cgo + +package mount + +// These flags are unsupported. +const ( + BIND = 0 + DIRSYNC = 0 + MANDLOCK = 0 + NOATIME = 0 + NODEV = 0 + NODIRATIME = 0 + NOEXEC = 0 + NOSUID = 0 + UNBINDABLE = 0 + RUNBINDABLE = 0 + PRIVATE = 0 + RPRIVATE = 0 + SHARED = 0 + RSHARED = 0 + SLAVE = 0 + RSLAVE = 0 + RBIND = 0 + RELATIME = 0 + RELATIVE = 0 + REMOUNT = 0 + STRICTATIME = 0 + SYNCHRONOUS = 0 + RDONLY = 0 +) diff --git a/vendor/github.com/docker/docker/pkg/mount/mount.go b/vendor/github.com/docker/docker/pkg/mount/mount.go new file mode 100644 index 000000000000..66ac4bf4723e --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mount.go @@ -0,0 +1,74 @@ +package mount + +import ( + "time" +) + +// GetMounts retrieves a list of mounts for the current running process. +func GetMounts() ([]*Info, error) { + return parseMountTable() +} + +// Mounted determines if a specified mountpoint has been mounted. +// On Linux it looks at /proc/self/mountinfo and on Solaris at mnttab. +func Mounted(mountpoint string) (bool, error) { + entries, err := parseMountTable() + if err != nil { + return false, err + } + + // Search the table for the mountpoint + for _, e := range entries { + if e.Mountpoint == mountpoint { + return true, nil + } + } + return false, nil +} + +// Mount will mount filesystem according to the specified configuration, on the +// condition that the target path is *not* already mounted. Options must be +// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See +// flags.go for supported option flags. +func Mount(device, target, mType, options string) error { + flag, _ := parseOptions(options) + if flag&REMOUNT != REMOUNT { + if mounted, err := Mounted(target); err != nil || mounted { + return err + } + } + return ForceMount(device, target, mType, options) +} + +// ForceMount will mount a filesystem according to the specified configuration, +// *regardless* if the target path is not already mounted. Options must be +// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See +// flags.go for supported option flags. +func ForceMount(device, target, mType, options string) error { + flag, data := parseOptions(options) + if err := mount(device, target, mType, uintptr(flag), data); err != nil { + return err + } + return nil +} + +// Unmount will unmount the target filesystem, so long as it is mounted. +func Unmount(target string) error { + if mounted, err := Mounted(target); err != nil || !mounted { + return err + } + return ForceUnmount(target) +} + +// ForceUnmount will force an unmount of the target filesystem, regardless if +// it is mounted or not. +func ForceUnmount(target string) (err error) { + // Simple retry logic for unmount + for i := 0; i < 10; i++ { + if err = unmount(target, 0); err == nil { + return nil + } + time.Sleep(100 * time.Millisecond) + } + return +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mounter_freebsd.go b/vendor/github.com/docker/docker/pkg/mount/mounter_freebsd.go new file mode 100644 index 000000000000..bb870e6f59b9 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mounter_freebsd.go @@ -0,0 +1,59 @@ +package mount + +/* +#include +#include +#include +#include +#include +#include +*/ +import "C" + +import ( + "fmt" + "strings" + "syscall" + "unsafe" +) + +func allocateIOVecs(options []string) []C.struct_iovec { + out := make([]C.struct_iovec, len(options)) + for i, option := range options { + out[i].iov_base = unsafe.Pointer(C.CString(option)) + out[i].iov_len = C.size_t(len(option) + 1) + } + return out +} + +func mount(device, target, mType string, flag uintptr, data string) error { + isNullFS := false + + xs := strings.Split(data, ",") + for _, x := range xs { + if x == "bind" { + isNullFS = true + } + } + + options := []string{"fspath", target} + if isNullFS { + options = append(options, "fstype", "nullfs", "target", device) + } else { + options = append(options, "fstype", mType, "from", device) + } + rawOptions := allocateIOVecs(options) + for _, rawOption := range rawOptions { + defer C.free(rawOption.iov_base) + } + + if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 { + reason := C.GoString(C.strerror(*C.__error())) + return fmt.Errorf("Failed to call nmount: %s", reason) + } + return nil +} + +func unmount(target string, flag int) error { + return syscall.Unmount(target, flag) +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mounter_linux.go b/vendor/github.com/docker/docker/pkg/mount/mounter_linux.go new file mode 100644 index 000000000000..dd4280c77786 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mounter_linux.go @@ -0,0 +1,21 @@ +package mount + +import ( + "syscall" +) + +func mount(device, target, mType string, flag uintptr, data string) error { + if err := syscall.Mount(device, target, mType, flag, data); err != nil { + return err + } + + // If we have a bind mount or remount, remount... + if flag&syscall.MS_BIND == syscall.MS_BIND && flag&syscall.MS_RDONLY == syscall.MS_RDONLY { + return syscall.Mount(device, target, mType, flag|syscall.MS_REMOUNT, data) + } + return nil +} + +func unmount(target string, flag int) error { + return syscall.Unmount(target, flag) +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mounter_solaris.go b/vendor/github.com/docker/docker/pkg/mount/mounter_solaris.go new file mode 100644 index 000000000000..c684aa81fcc1 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mounter_solaris.go @@ -0,0 +1,33 @@ +// +build solaris,cgo + +package mount + +import ( + "golang.org/x/sys/unix" + "unsafe" +) + +// #include +// #include +// #include +// int Mount(const char *spec, const char *dir, int mflag, +// char *fstype, char *dataptr, int datalen, char *optptr, int optlen) { +// return mount(spec, dir, mflag, fstype, dataptr, datalen, optptr, optlen); +// } +import "C" + +func mount(device, target, mType string, flag uintptr, data string) error { + spec := C.CString(device) + dir := C.CString(target) + fstype := C.CString(mType) + _, err := C.Mount(spec, dir, C.int(flag), fstype, nil, 0, nil, 0) + C.free(unsafe.Pointer(spec)) + C.free(unsafe.Pointer(dir)) + C.free(unsafe.Pointer(fstype)) + return err +} + +func unmount(target string, flag int) error { + err := unix.Unmount(target, flag) + return err +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mounter_unsupported.go b/vendor/github.com/docker/docker/pkg/mount/mounter_unsupported.go new file mode 100644 index 000000000000..a2a3bb457fcb --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mounter_unsupported.go @@ -0,0 +1,11 @@ +// +build !linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo + +package mount + +func mount(device, target, mType string, flag uintptr, data string) error { + panic("Not implemented") +} + +func unmount(target string, flag int) error { + panic("Not implemented") +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mountinfo.go b/vendor/github.com/docker/docker/pkg/mount/mountinfo.go new file mode 100644 index 000000000000..e3fc3535e934 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mountinfo.go @@ -0,0 +1,40 @@ +package mount + +// Info reveals information about a particular mounted filesystem. This +// struct is populated from the content in the /proc//mountinfo file. +type Info struct { + // ID is a unique identifier of the mount (may be reused after umount). + ID int + + // Parent indicates the ID of the mount parent (or of self for the top of the + // mount tree). + Parent int + + // Major indicates one half of the device ID which identifies the device class. + Major int + + // Minor indicates one half of the device ID which identifies a specific + // instance of device. + Minor int + + // Root of the mount within the filesystem. + Root string + + // Mountpoint indicates the mount point relative to the process's root. + Mountpoint string + + // Opts represents mount-specific options. + Opts string + + // Optional represents optional fields. + Optional string + + // Fstype indicates the type of filesystem, such as EXT3. + Fstype string + + // Source indicates filesystem specific information or "none". + Source string + + // VfsOpts represents per super block options. + VfsOpts string +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mountinfo_freebsd.go b/vendor/github.com/docker/docker/pkg/mount/mountinfo_freebsd.go new file mode 100644 index 000000000000..4f32edcd906a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mountinfo_freebsd.go @@ -0,0 +1,41 @@ +package mount + +/* +#include +#include +#include +*/ +import "C" + +import ( + "fmt" + "reflect" + "unsafe" +) + +// Parse /proc/self/mountinfo because comparing Dev and ino does not work from +// bind mounts. +func parseMountTable() ([]*Info, error) { + var rawEntries *C.struct_statfs + + count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT)) + if count == 0 { + return nil, fmt.Errorf("Failed to call getmntinfo") + } + + var entries []C.struct_statfs + header := (*reflect.SliceHeader)(unsafe.Pointer(&entries)) + header.Cap = count + header.Len = count + header.Data = uintptr(unsafe.Pointer(rawEntries)) + + var out []*Info + for _, entry := range entries { + var mountinfo Info + mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0]) + mountinfo.Source = C.GoString(&entry.f_mntfromname[0]) + mountinfo.Fstype = C.GoString(&entry.f_fstypename[0]) + out = append(out, &mountinfo) + } + return out, nil +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mountinfo_linux.go b/vendor/github.com/docker/docker/pkg/mount/mountinfo_linux.go new file mode 100644 index 000000000000..be69fee1d7bb --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mountinfo_linux.go @@ -0,0 +1,95 @@ +// +build linux + +package mount + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" +) + +const ( + /* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue + (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) + + (1) mount ID: unique identifier of the mount (may be reused after umount) + (2) parent ID: ID of parent (or of self for the top of the mount tree) + (3) major:minor: value of st_dev for files on filesystem + (4) root: root of the mount within the filesystem + (5) mount point: mount point relative to the process's root + (6) mount options: per mount options + (7) optional fields: zero or more fields of the form "tag[:value]" + (8) separator: marks the end of the optional fields + (9) filesystem type: name of filesystem of the form "type[.subtype]" + (10) mount source: filesystem specific information or "none" + (11) super options: per super block options*/ + mountinfoFormat = "%d %d %d:%d %s %s %s %s" +) + +// Parse /proc/self/mountinfo because comparing Dev and ino does not work from +// bind mounts +func parseMountTable() ([]*Info, error) { + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return nil, err + } + defer f.Close() + + return parseInfoFile(f) +} + +func parseInfoFile(r io.Reader) ([]*Info, error) { + var ( + s = bufio.NewScanner(r) + out = []*Info{} + ) + + for s.Scan() { + if err := s.Err(); err != nil { + return nil, err + } + + var ( + p = &Info{} + text = s.Text() + optionalFields string + ) + + if _, err := fmt.Sscanf(text, mountinfoFormat, + &p.ID, &p.Parent, &p.Major, &p.Minor, + &p.Root, &p.Mountpoint, &p.Opts, &optionalFields); err != nil { + return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err) + } + // Safe as mountinfo encodes mountpoints with spaces as \040. + index := strings.Index(text, " - ") + postSeparatorFields := strings.Fields(text[index+3:]) + if len(postSeparatorFields) < 3 { + return nil, fmt.Errorf("Error found less than 3 fields post '-' in %q", text) + } + + if optionalFields != "-" { + p.Optional = optionalFields + } + + p.Fstype = postSeparatorFields[0] + p.Source = postSeparatorFields[1] + p.VfsOpts = strings.Join(postSeparatorFields[2:], " ") + out = append(out, p) + } + return out, nil +} + +// PidMountInfo collects the mounts for a specific process ID. If the process +// ID is unknown, it is better to use `GetMounts` which will inspect +// "/proc/self/mountinfo" instead. +func PidMountInfo(pid int) ([]*Info, error) { + f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) + if err != nil { + return nil, err + } + defer f.Close() + + return parseInfoFile(f) +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mountinfo_solaris.go b/vendor/github.com/docker/docker/pkg/mount/mountinfo_solaris.go new file mode 100644 index 000000000000..ad9ab57f8b8e --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mountinfo_solaris.go @@ -0,0 +1,37 @@ +// +build solaris,cgo + +package mount + +/* +#include +#include +*/ +import "C" + +import ( + "fmt" +) + +func parseMountTable() ([]*Info, error) { + mnttab := C.fopen(C.CString(C.MNTTAB), C.CString("r")) + if mnttab == nil { + return nil, fmt.Errorf("Failed to open %s", C.MNTTAB) + } + + var out []*Info + var mp C.struct_mnttab + + ret := C.getmntent(mnttab, &mp) + for ret == 0 { + var mountinfo Info + mountinfo.Mountpoint = C.GoString(mp.mnt_mountp) + mountinfo.Source = C.GoString(mp.mnt_special) + mountinfo.Fstype = C.GoString(mp.mnt_fstype) + mountinfo.Opts = C.GoString(mp.mnt_mntopts) + out = append(out, &mountinfo) + ret = C.getmntent(mnttab, &mp) + } + + C.fclose(mnttab) + return out, nil +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mountinfo_unsupported.go b/vendor/github.com/docker/docker/pkg/mount/mountinfo_unsupported.go new file mode 100644 index 000000000000..7fbcf19214b9 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mountinfo_unsupported.go @@ -0,0 +1,12 @@ +// +build !windows,!linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo + +package mount + +import ( + "fmt" + "runtime" +) + +func parseMountTable() ([]*Info, error) { + return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} diff --git a/vendor/github.com/docker/docker/pkg/mount/mountinfo_windows.go b/vendor/github.com/docker/docker/pkg/mount/mountinfo_windows.go new file mode 100644 index 000000000000..dab8a37ed01d --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/mountinfo_windows.go @@ -0,0 +1,6 @@ +package mount + +func parseMountTable() ([]*Info, error) { + // Do NOT return an error! + return nil, nil +} diff --git a/vendor/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go b/vendor/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go new file mode 100644 index 000000000000..8ceec84bc6c8 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go @@ -0,0 +1,69 @@ +// +build linux + +package mount + +// MakeShared ensures a mounted filesystem has the SHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeShared(mountPoint string) error { + return ensureMountedAs(mountPoint, "shared") +} + +// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRShared(mountPoint string) error { + return ensureMountedAs(mountPoint, "rshared") +} + +// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled. +// See the supported options in flags.go for further reference. +func MakePrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, "private") +} + +// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeRPrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, "rprivate") +} + +// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, "slave") +} + +// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, "rslave") +} + +// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, "unbindable") +} + +// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount +// option enabled. See the supported options in flags.go for further reference. +func MakeRUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, "runbindable") +} + +func ensureMountedAs(mountPoint, options string) error { + mounted, err := Mounted(mountPoint) + if err != nil { + return err + } + + if !mounted { + if err := Mount(mountPoint, mountPoint, "none", "bind,rw"); err != nil { + return err + } + } + if _, err = Mounted(mountPoint); err != nil { + return err + } + + return ForceMount("", mountPoint, "none", options) +} diff --git a/vendor/github.com/docker/docker/pkg/mount/sharedsubtree_solaris.go b/vendor/github.com/docker/docker/pkg/mount/sharedsubtree_solaris.go new file mode 100644 index 000000000000..09f6b03cbc0c --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/mount/sharedsubtree_solaris.go @@ -0,0 +1,58 @@ +// +build solaris + +package mount + +// MakeShared ensures a mounted filesystem has the SHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeShared(mountPoint string) error { + return ensureMountedAs(mountPoint, "shared") +} + +// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRShared(mountPoint string) error { + return ensureMountedAs(mountPoint, "rshared") +} + +// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled. +// See the supported options in flags.go for further reference. +func MakePrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, "private") +} + +// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeRPrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, "rprivate") +} + +// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, "slave") +} + +// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, "rslave") +} + +// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, "unbindable") +} + +// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount +// option enabled. See the supported options in flags.go for further reference. +func MakeRUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, "runbindable") +} + +func ensureMountedAs(mountPoint, options string) error { + // TODO: Solaris does not support bind mounts. + // Evaluate lofs and also look at the relevant + // mount flags to be supported. + return nil +}