diff --git a/daemon/commit.go b/daemon/commit.go index 3e5744c0c2..ba083598b0 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -145,7 +145,7 @@ func (daemon *Daemon) exportContainerRw(container *container.Container) (archive } return ioutils.NewReadCloserWrapper(archive, func() error { archive.Close() - return daemon.layerStore.Unmount(container.ID) + return container.RWLayer.Unmount() }), nil } diff --git a/daemon/container_operations_windows.go b/daemon/container_operations_windows.go index ebbefa3eb4..2c4a24c463 100644 --- a/daemon/container_operations_windows.go +++ b/daemon/container_operations_windows.go @@ -98,7 +98,7 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro } } - m, err := daemon.layerStore.Metadata(c.ID) + m, err := c.RWLayer.Metadata() if err != nil { return derr.ErrorCodeGetLayerMetadata.WithArgs(err) } diff --git a/daemon/create.go b/daemon/create.go index aa3d97896e..bebf1ac29e 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/container" derr "github.com/docker/docker/errors" "github.com/docker/docker/image" + "github.com/docker/docker/layer" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/volume" @@ -41,13 +42,12 @@ func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (types } // Create creates a new container from the given configuration with a given name. -func (daemon *Daemon) create(params types.ContainerCreateConfig) (*container.Container, error) { +func (daemon *Daemon) create(params types.ContainerCreateConfig) (retC *container.Container, retErr error) { var ( container *container.Container img *image.Image imgID image.ID err error - retErr error ) if params.Config.Image != "" { @@ -73,6 +73,15 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig) (*container.Con } }() + if err := daemon.setSecurityOptions(container, params.HostConfig); err != nil { + return nil, err + } + + // Set RWLayer for container after mount labels have been set + if err := daemon.setRWLayer(container); err != nil { + return nil, err + } + if err := daemon.Register(container); err != nil { return nil, err } @@ -126,6 +135,24 @@ func (daemon *Daemon) generateSecurityOpt(ipcMode containertypes.IpcMode, pidMod return nil, nil } +func (daemon *Daemon) setRWLayer(container *container.Container) error { + var layerID layer.ChainID + if container.ImageID != "" { + img, err := daemon.imageStore.Get(container.ImageID) + if err != nil { + return err + } + layerID = img.RootFS.ChainID() + } + rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, container.MountLabel, daemon.setupInitLayer) + if err != nil { + return err + } + container.RWLayer = rwLayer + + return nil +} + // VolumeCreate creates a volume with the specified name, driver, and opts // This is called directly from the remote API func (daemon *Daemon) VolumeCreate(name, driverName string, opts map[string]string) (*types.Volume, error) { diff --git a/daemon/daemon.go b/daemon/daemon.go index 7f84de7c4f..f858e73dd2 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -226,18 +226,30 @@ func (daemon *Daemon) load(id string) (*container.Container, error) { return container, nil } -// Register makes a container object usable by the daemon as -func (daemon *Daemon) Register(container *container.Container) error { +func (daemon *Daemon) registerName(container *container.Container) error { if daemon.Exists(container.ID) { return fmt.Errorf("Container is already loaded") } if err := validateID(container.ID); err != nil { return err } - if err := daemon.ensureName(container); err != nil { - return err + if container.Name == "" { + name, err := daemon.generateNewName(container.ID) + if err != nil { + return err + } + container.Name = name + + if err := container.ToDiskLocking(); err != nil { + logrus.Errorf("Error saving container name to disk: %v", err) + } } + return nil +} + +// Register makes a container object usable by the daemon as +func (daemon *Daemon) Register(container *container.Container) error { // Attach to stdout and stderr if container.Config.OpenStdin { container.NewInputPipes() @@ -277,21 +289,6 @@ func (daemon *Daemon) Register(container *container.Container) error { return nil } -func (daemon *Daemon) ensureName(container *container.Container) error { - if container.Name == "" { - name, err := daemon.generateNewName(container.ID) - if err != nil { - return err - } - container.Name = name - - if err := container.ToDiskLocking(); err != nil { - logrus.Errorf("Error saving container name to disk: %v", err) - } - } - return nil -} - func (daemon *Daemon) restore() error { type cr struct { container *container.Container @@ -323,6 +320,13 @@ func (daemon *Daemon) restore() error { continue } + rwlayer, err := daemon.layerStore.GetRWLayer(container.ID) + if err != nil { + logrus.Errorf("Failed to load container mount %v: %v", id, err) + continue + } + container.RWLayer = rwlayer + // Ignore the container if it does not support the current driver being used by the graph if (container.Driver == "" && currentDriver == "aufs") || container.Driver == currentDriver { logrus.Debugf("Loaded container %v", container.ID) @@ -361,6 +365,10 @@ func (daemon *Daemon) restore() error { logrus.Debugf("Setting default id - %s", err) } } + if err := daemon.registerName(container); err != nil { + logrus.Errorf("Failed to register container %s: %s", container.ID, err) + return + } if err := daemon.Register(container); err != nil { logrus.Errorf("Failed to register container %s: %s", container.ID, err) @@ -961,19 +969,7 @@ func (daemon *Daemon) Shutdown() error { // Mount sets container.BaseFS // (is it not set coming in? why is it unset?) func (daemon *Daemon) Mount(container *container.Container) error { - var layerID layer.ChainID - if container.ImageID != "" { - img, err := daemon.imageStore.Get(container.ImageID) - if err != nil { - return err - } - layerID = img.RootFS.ChainID() - } - rwlayer, err := daemon.layerStore.Mount(container.ID, layerID, container.GetMountLabel(), daemon.setupInitLayer) - if err != nil { - return err - } - dir, err := rwlayer.Path() + dir, err := container.RWLayer.Mount(container.GetMountLabel()) if err != nil { return err } @@ -990,13 +986,12 @@ func (daemon *Daemon) Mount(container *container.Container) error { } } container.BaseFS = dir // TODO: combine these fields - container.RWLayer = rwlayer return nil } // Unmount unsets the container base filesystem func (daemon *Daemon) Unmount(container *container.Container) { - if err := daemon.layerStore.Unmount(container.ID); err != nil { + if err := container.RWLayer.Unmount(); err != nil { logrus.Errorf("Error unmounting container %s: %s", container.ID, err) } } @@ -1029,7 +1024,7 @@ func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch cha } func (daemon *Daemon) changes(container *container.Container) ([]archive.Change, error) { - return daemon.layerStore.Changes(container.ID) + return container.RWLayer.Changes() } // TagImage creates a tag in the repository reponame, pointing to the image named @@ -1398,14 +1393,13 @@ func tempDir(rootDir string, rootUID, rootGID int) (string, error) { return tmpDir, idtools.MkdirAllAs(tmpDir, 0700, rootUID, rootGID) } -func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error { +func (daemon *Daemon) setSecurityOptions(container *container.Container, hostConfig *containertypes.HostConfig) error { container.Lock() - if err := parseSecurityOpt(container, hostConfig); err != nil { - container.Unlock() - return err - } - container.Unlock() + defer container.Unlock() + return parseSecurityOpt(container, hostConfig) +} +func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error { // Do not lock while creating volumes since this could be calling out to external plugins // Don't want to block other actions, like `docker ps` because we're waiting on an external plugin if err := daemon.registerMountPoints(container, hostConfig); err != nil { diff --git a/daemon/delete.go b/daemon/delete.go index a282f3a9f9..e2ce1a8568 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -130,7 +130,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo return derr.ErrorCodeRmFS.WithArgs(container.ID, err) } - metadata, err := daemon.layerStore.DeleteMount(container.ID) + metadata, err := daemon.layerStore.ReleaseRWLayer(container.RWLayer) layer.LogReleaseMetadata(metadata) if err != nil && err != layer.ErrMountDoesNotExist { return derr.ErrorCodeRmDriverFS.WithArgs(daemon.driver, container.ID, err) diff --git a/daemon/inspect.go b/daemon/inspect.go index 60bebcc598..b1e4eb43a7 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -163,7 +163,7 @@ func (daemon *Daemon) getInspectData(container *container.Container, size bool) contJSONBase.GraphDriver.Name = container.Driver - graphDriverData, err := daemon.layerStore.Metadata(container.ID) + graphDriverData, err := container.RWLayer.Metadata() if err != nil { return nil, err } diff --git a/daemon/start.go b/daemon/start.go index 27279d6b37..8ea3f2db01 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -31,6 +31,9 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos // creating a container, not during start. if hostConfig != nil { logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and will be removed in Docker 1.12") + if err := daemon.setSecurityOptions(container, hostConfig); err != nil { + return err + } if err := daemon.setHostConfig(container, hostConfig); err != nil { return err } diff --git a/distribution/xfer/download_test.go b/distribution/xfer/download_test.go index a09cd244e1..474e523694 100644 --- a/distribution/xfer/download_test.go +++ b/distribution/xfer/download_test.go @@ -12,7 +12,6 @@ import ( "github.com/docker/distribution/digest" "github.com/docker/docker/image" "github.com/docker/docker/layer" - "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/progress" "golang.org/x/net/context" ) @@ -115,25 +114,18 @@ func (ls *mockLayerStore) Get(chainID layer.ChainID) (layer.Layer, error) { func (ls *mockLayerStore) Release(l layer.Layer) ([]layer.Metadata, error) { return []layer.Metadata{}, nil } - -func (ls *mockLayerStore) Mount(id string, parent layer.ChainID, label string, init layer.MountInit) (layer.RWLayer, error) { +func (ls *mockLayerStore) CreateRWLayer(string, layer.ChainID, string, layer.MountInit) (layer.RWLayer, error) { return nil, errors.New("not implemented") } -func (ls *mockLayerStore) Unmount(id string) error { - return errors.New("not implemented") +func (ls *mockLayerStore) GetRWLayer(string) (layer.RWLayer, error) { + return nil, errors.New("not implemented") + } -func (ls *mockLayerStore) DeleteMount(id string) ([]layer.Metadata, error) { +func (ls *mockLayerStore) ReleaseRWLayer(layer.RWLayer) ([]layer.Metadata, error) { return nil, errors.New("not implemented") -} -func (ls *mockLayerStore) Changes(id string) ([]archive.Change, error) { - return nil, errors.New("not implemented") -} - -func (ls *mockLayerStore) Metadata(id string) (map[string]string, error) { - return nil, errors.New("not implemented") } type mockDownloadDescriptor struct { diff --git a/layer/layer.go b/layer/layer.go index eed5937e1e..f2fa5bc6c9 100644 --- a/layer/layer.go +++ b/layer/layer.go @@ -31,6 +31,11 @@ var ( // attempted on a mount layer which does not exist. ErrMountDoesNotExist = errors.New("mount does not exist") + // ErrMountNameConflict is used when a mount is attempted + // to be created but there is already a mount with the name + // used for creation. + ErrMountNameConflict = errors.New("mount already exists with name") + // ErrActiveMount is used when an operation on a // mount is attempted but the layer is still // mounted and the operation cannot be performed. @@ -103,18 +108,33 @@ type Layer interface { type RWLayer interface { TarStreamer - // Path returns the filesystem path to the writable - // layer. - Path() (string, error) + // Name of mounted layer + Name() string // Parent returns the layer which the writable // layer was created from. Parent() Layer + // Mount mounts the RWLayer and returns the filesystem path + // the to the writable layer. + Mount(mountLabel string) (string, error) + + // Unmount unmounts the RWLayer. This should be called + // for every mount. If there are multiple mount calls + // this operation will only decrement the internal mount counter. + Unmount() error + // Size represents the size of the writable layer // as calculated by the total size of the files // changed in the mutable layer. Size() (int64, error) + + // Changes returns the set of changes for the mutable layer + // from the base layer. + Changes() ([]archive.Change, error) + + // Metadata returns the low level metadata for the mutable layer + Metadata() (map[string]string, error) } // Metadata holds information about a @@ -147,11 +167,9 @@ type Store interface { Get(ChainID) (Layer, error) Release(Layer) ([]Metadata, error) - Mount(id string, parent ChainID, label string, init MountInit) (RWLayer, error) - Unmount(id string) error - DeleteMount(id string) ([]Metadata, error) - Changes(id string) ([]archive.Change, error) - Metadata(id string) (map[string]string, error) + CreateRWLayer(id string, parent ChainID, mountLabel string, initFunc MountInit) (RWLayer, error) + GetRWLayer(id string) (RWLayer, error) + ReleaseRWLayer(RWLayer) ([]Metadata, error) } // MetadataTransaction represents functions for setting layer metadata diff --git a/layer/layer_store.go b/layer/layer_store.go index d67092f7d9..80dedfbfbc 100644 --- a/layer/layer_store.go +++ b/layer/layer_store.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "io/ioutil" - "runtime" "sync" "github.com/Sirupsen/logrus" @@ -144,6 +143,7 @@ func (ls *layerStore) loadMount(mount string) error { mountID: mountID, initID: initID, layerStore: ls, + references: map[RWLayer]*referencedRWLayer{}, } if parent != "" { @@ -382,15 +382,114 @@ func (ls *layerStore) Release(l Layer) ([]Metadata, error) { return ls.releaseLayer(layer) } -func (ls *layerStore) mount(m *mountedLayer, mountLabel string) error { - dir, err := ls.driver.Get(m.mountID, mountLabel) - if err != nil { - return err +func (ls *layerStore) CreateRWLayer(name string, parent ChainID, mountLabel string, initFunc MountInit) (RWLayer, error) { + ls.mountL.Lock() + defer ls.mountL.Unlock() + m, ok := ls.mounts[name] + if ok { + return nil, ErrMountNameConflict } - m.path = dir - m.activityCount++ - return nil + var err error + var pid string + var p *roLayer + if string(parent) != "" { + p = ls.get(parent) + if p == nil { + return nil, ErrLayerDoesNotExist + } + pid = p.cacheID + + // Release parent chain if error + defer func() { + if err != nil { + ls.layerL.Lock() + ls.releaseLayer(p) + ls.layerL.Unlock() + } + }() + } + + m = &mountedLayer{ + name: name, + parent: p, + mountID: ls.mountID(name), + layerStore: ls, + references: map[RWLayer]*referencedRWLayer{}, + } + + if initFunc != nil { + pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc) + if err != nil { + return nil, err + } + m.initID = pid + } + + if err = ls.driver.Create(m.mountID, pid, ""); err != nil { + return nil, err + } + + if err = ls.saveMount(m); err != nil { + return nil, err + } + + return m.getReference(), nil +} + +func (ls *layerStore) GetRWLayer(id string) (RWLayer, error) { + ls.mountL.Lock() + defer ls.mountL.Unlock() + mount, ok := ls.mounts[id] + if !ok { + return nil, ErrMountDoesNotExist + } + + return mount.getReference(), nil +} + +func (ls *layerStore) ReleaseRWLayer(l RWLayer) ([]Metadata, error) { + ls.mountL.Lock() + defer ls.mountL.Unlock() + m, ok := ls.mounts[l.Name()] + if !ok { + return []Metadata{}, nil + } + + if err := m.deleteReference(l); err != nil { + return nil, err + } + + if m.hasReferences() { + return []Metadata{}, nil + } + + if err := ls.driver.Remove(m.mountID); err != nil { + logrus.Errorf("Error removing mounted layer %s: %s", m.name, err) + return nil, err + } + + if m.initID != "" { + if err := ls.driver.Remove(m.initID); err != nil { + logrus.Errorf("Error removing init layer %s: %s", m.name, err) + return nil, err + } + } + + if err := ls.store.RemoveMount(m.name); err != nil { + logrus.Errorf("Error removing mount metadata: %s: %s", m.name, err) + return nil, err + } + + delete(ls.mounts, m.Name()) + + ls.layerL.Lock() + defer ls.layerL.Unlock() + if m.parent != nil { + return ls.releaseLayer(m.parent) + } + + return []Metadata{}, nil } func (ls *layerStore) saveMount(mount *mountedLayer) error { @@ -442,145 +541,6 @@ func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc Mou return initID, nil } -func (ls *layerStore) Mount(name string, parent ChainID, mountLabel string, initFunc MountInit) (l RWLayer, err error) { - ls.mountL.Lock() - defer ls.mountL.Unlock() - m, ok := ls.mounts[name] - if ok { - // Check if has path - if err := ls.mount(m, mountLabel); err != nil { - return nil, err - } - return m, nil - } - - var pid string - var p *roLayer - if string(parent) != "" { - p = ls.get(parent) - if p == nil { - return nil, ErrLayerDoesNotExist - } - pid = p.cacheID - - // Release parent chain if error - defer func() { - if err != nil { - ls.layerL.Lock() - ls.releaseLayer(p) - ls.layerL.Unlock() - } - }() - } - - mountID := name - if runtime.GOOS != "windows" { - // windows has issues if container ID doesn't match mount ID - mountID = stringid.GenerateRandomID() - } - - m = &mountedLayer{ - name: name, - parent: p, - mountID: mountID, - layerStore: ls, - } - - if initFunc != nil { - pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc) - if err != nil { - return nil, err - } - m.initID = pid - } - - if err = ls.driver.Create(m.mountID, pid, ""); err != nil { - return nil, err - } - - if err = ls.saveMount(m); err != nil { - return nil, err - } - - if err = ls.mount(m, mountLabel); err != nil { - return nil, err - } - - return m, nil -} - -func (ls *layerStore) Unmount(name string) error { - ls.mountL.Lock() - defer ls.mountL.Unlock() - - m := ls.mounts[name] - if m == nil { - return ErrMountDoesNotExist - } - - m.activityCount-- - - if err := ls.driver.Put(m.mountID); err != nil { - return err - } - - return nil -} - -func (ls *layerStore) DeleteMount(name string) ([]Metadata, error) { - ls.mountL.Lock() - defer ls.mountL.Unlock() - - m := ls.mounts[name] - if m == nil { - return nil, ErrMountDoesNotExist - } - if m.activityCount > 0 { - return nil, ErrActiveMount - } - - delete(ls.mounts, name) - - if err := ls.driver.Remove(m.mountID); err != nil { - logrus.Errorf("Error removing mounted layer %s: %s", m.name, err) - return nil, err - } - - if m.initID != "" { - if err := ls.driver.Remove(m.initID); err != nil { - logrus.Errorf("Error removing init layer %s: %s", m.name, err) - return nil, err - } - } - - if err := ls.store.RemoveMount(m.name); err != nil { - logrus.Errorf("Error removing mount metadata: %s: %s", m.name, err) - return nil, err - } - - ls.layerL.Lock() - defer ls.layerL.Unlock() - if m.parent != nil { - return ls.releaseLayer(m.parent) - } - - return []Metadata{}, nil -} - -func (ls *layerStore) Changes(name string) ([]archive.Change, error) { - ls.mountL.Lock() - m := ls.mounts[name] - ls.mountL.Unlock() - if m == nil { - return nil, ErrMountDoesNotExist - } - pid := m.initID - if pid == "" && m.parent != nil { - pid = m.parent.cacheID - } - return ls.driver.Changes(m.mountID, pid) -} - func (ls *layerStore) assembleTar(graphID string, metadata io.ReadCloser, size *int64) (io.ReadCloser, error) { type diffPathDriver interface { DiffPath(string) (string, func() error, error) @@ -621,17 +581,6 @@ func (ls *layerStore) assembleTar(graphID string, metadata io.ReadCloser, size * return pR, nil } -// Metadata returns the low level metadata from the mount with the given name -func (ls *layerStore) Metadata(name string) (map[string]string, error) { - ls.mountL.Lock() - m := ls.mounts[name] - ls.mountL.Unlock() - if m == nil { - return nil, ErrMountDoesNotExist - } - return ls.driver.GetMetadata(m.mountID) -} - type naiveDiffPathDriver struct { graphdriver.Driver } diff --git a/layer/layer_test.go b/layer/layer_test.go index c3e4cf1d52..65cc15b2ae 100644 --- a/layer/layer_test.go +++ b/layer/layer_test.go @@ -82,12 +82,12 @@ type layerInit func(root string) error func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) { containerID := stringid.GenerateRandomID() - mount, err := ls.Mount(containerID, parent, "", nil) + mount, err := ls.CreateRWLayer(containerID, parent, "", nil) if err != nil { return nil, err } - path, err := mount.Path() + path, err := mount.Mount("") if err != nil { return nil, err } @@ -107,11 +107,11 @@ func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) { return nil, err } - if err := ls.Unmount(containerID); err != nil { + if err := mount.Unmount(); err != nil { return nil, err } - if _, err := ls.DeleteMount(containerID); err != nil { + if _, err := ls.ReleaseRWLayer(mount); err != nil { return nil, err } @@ -171,6 +171,13 @@ func getCachedLayer(l Layer) *roLayer { return l.(*roLayer) } +func getMountLayer(l RWLayer) *mountedLayer { + if rl, ok := l.(*referencedRWLayer); ok { + return rl.mountedLayer + } + return l.(*mountedLayer) +} + func createMetadata(layers ...Layer) []Metadata { metadata := make([]Metadata, len(layers)) for i := range layers { @@ -270,12 +277,12 @@ func TestMountAndRegister(t *testing.T) { size, _ := layer.Size() t.Logf("Layer size: %d", size) - mount2, err := ls.Mount("new-test-mount", layer.ChainID(), "", nil) + mount2, err := ls.CreateRWLayer("new-test-mount", layer.ChainID(), "", nil) if err != nil { t.Fatal(err) } - path2, err := mount2.Path() + path2, err := mount2.Mount("") if err != nil { t.Fatal(err) } @@ -289,11 +296,11 @@ func TestMountAndRegister(t *testing.T) { t.Fatalf("Wrong file data, expected %q, got %q", expected, string(b)) } - if err := ls.Unmount("new-test-mount"); err != nil { + if err := mount2.Unmount(); err != nil { t.Fatal(err) } - if _, err := ls.DeleteMount("new-test-mount"); err != nil { + if _, err := ls.ReleaseRWLayer(mount2); err != nil { t.Fatal(err) } } @@ -370,12 +377,12 @@ func TestStoreRestore(t *testing.T) { t.Fatal(err) } - m, err := ls.Mount("some-mount_name", layer3.ChainID(), "", nil) + m, err := ls.CreateRWLayer("some-mount_name", layer3.ChainID(), "", nil) if err != nil { t.Fatal(err) } - path, err := m.Path() + path, err := m.Mount("") if err != nil { t.Fatal(err) } @@ -383,11 +390,14 @@ func TestStoreRestore(t *testing.T) { if err := ioutil.WriteFile(filepath.Join(path, "testfile.txt"), []byte("nothing here"), 0644); err != nil { t.Fatal(err) } + assertActivityCount(t, m, 1) - if err := ls.Unmount("some-mount_name"); err != nil { + if err := m.Unmount(); err != nil { t.Fatal(err) } + assertActivityCount(t, m, 0) + ls2, err := NewStore(ls.(*layerStore).store, ls.(*layerStore).driver) if err != nil { t.Fatal(err) @@ -400,18 +410,39 @@ func TestStoreRestore(t *testing.T) { assertLayerEqual(t, layer3b, layer3) - // Mount again with same name, should already be loaded - m2, err := ls2.Mount("some-mount_name", layer3b.ChainID(), "", nil) + // Create again with same name, should return error + if _, err := ls2.CreateRWLayer("some-mount_name", layer3b.ChainID(), "", nil); err == nil { + t.Fatal("Expected error creating mount with same name") + } else if err != ErrMountNameConflict { + t.Fatal(err) + } + + m2, err := ls2.GetRWLayer("some-mount_name") if err != nil { t.Fatal(err) } - path2, err := m2.Path() - if err != nil { + if mountPath, err := m2.Mount(""); err != nil { + t.Fatal(err) + } else if path != mountPath { + t.Fatalf("Unexpected path %s, expected %s", mountPath, path) + } + + assertActivityCount(t, m2, 1) + + if mountPath, err := m2.Mount(""); err != nil { + t.Fatal(err) + } else if path != mountPath { + t.Fatalf("Unexpected path %s, expected %s", mountPath, path) + } + assertActivityCount(t, m2, 2) + if err := m2.Unmount(); err != nil { t.Fatal(err) } - b, err := ioutil.ReadFile(filepath.Join(path2, "testfile.txt")) + assertActivityCount(t, m2, 1) + + b, err := ioutil.ReadFile(filepath.Join(path, "testfile.txt")) if err != nil { t.Fatal(err) } @@ -419,11 +450,19 @@ func TestStoreRestore(t *testing.T) { t.Fatalf("Unexpected content %q, expected %q", string(b), expected) } - if err := ls2.Unmount("some-mount_name"); err != nil { + if err := m2.Unmount(); err != nil { t.Fatal(err) } - if metadata, err := ls2.DeleteMount("some-mount_name"); err != nil { + assertActivityCount(t, m2, 0) + + if metadata, err := ls2.ReleaseRWLayer(m2); err != nil { + t.Fatal(err) + } else if len(metadata) != 0 { + t.Fatalf("Unexpectedly deleted layers: %#v", metadata) + } + + if metadata, err := ls2.ReleaseRWLayer(m2); err != nil { t.Fatal(err) } else if len(metadata) != 0 { t.Fatalf("Unexpectedly deleted layers: %#v", metadata) @@ -627,6 +666,13 @@ func assertReferences(t *testing.T, references ...Layer) { } } +func assertActivityCount(t *testing.T, l RWLayer, expected int) { + rl := l.(*referencedRWLayer) + if rl.activityCount != expected { + t.Fatalf("Unexpected activity count %d, expected %d", rl.activityCount, expected) + } +} + func TestRegisterExistingLayer(t *testing.T) { ls, cleanup := newTestStore(t) defer cleanup() diff --git a/layer/layer_unix.go b/layer/layer_unix.go new file mode 100644 index 0000000000..524b97e8d1 --- /dev/null +++ b/layer/layer_unix.go @@ -0,0 +1,9 @@ +// +build linux freebsd darwin + +package layer + +import "github.com/docker/docker/pkg/stringid" + +func (ls *layerStore) mountID(name string) string { + return stringid.GenerateRandomID() +} diff --git a/layer/layer_windows.go b/layer/layer_windows.go index 369281448a..c40144d860 100644 --- a/layer/layer_windows.go +++ b/layer/layer_windows.go @@ -89,3 +89,8 @@ func (ls *layerStore) RegisterDiffID(graphID string, size int64) (Layer, error) return layer.getReference(), nil } + +func (ls *layerStore) mountID(name string) string { + // windows has issues if container ID doesn't match mount ID + return name +} diff --git a/layer/migration.go b/layer/migration.go index bc448a59a5..50dbcaec80 100644 --- a/layer/migration.go +++ b/layer/migration.go @@ -14,30 +14,33 @@ import ( "github.com/vbatts/tar-split/tar/storage" ) -func (ls *layerStore) MountByGraphID(name string, graphID string, parent ChainID) (l RWLayer, err error) { +// CreateRWLayerByGraphID creates a RWLayer in the layer store using +// the provided name with the given graphID. To get the RWLayer +// after migration the layer may be retrieved by the given name. +func (ls *layerStore) CreateRWLayerByGraphID(name string, graphID string, parent ChainID) (err error) { ls.mountL.Lock() defer ls.mountL.Unlock() m, ok := ls.mounts[name] if ok { if m.parent.chainID != parent { - return nil, errors.New("name conflict, mismatched parent") + return errors.New("name conflict, mismatched parent") } if m.mountID != graphID { - return nil, errors.New("mount already exists") + return errors.New("mount already exists") } - return m, nil + return nil } if !ls.driver.Exists(graphID) { - return nil, errors.New("graph ID does not exist") + return errors.New("graph ID does not exist") } var p *roLayer if string(parent) != "" { p = ls.get(parent) if p == nil { - return nil, ErrLayerDoesNotExist + return ErrLayerDoesNotExist } // Release parent chain if error @@ -57,6 +60,7 @@ func (ls *layerStore) MountByGraphID(name string, graphID string, parent ChainID parent: p, mountID: graphID, layerStore: ls, + references: map[RWLayer]*referencedRWLayer{}, } // Check for existing init layer @@ -66,15 +70,10 @@ func (ls *layerStore) MountByGraphID(name string, graphID string, parent ChainID } if err = ls.saveMount(m); err != nil { - return nil, err + return err } - // TODO: provide a mount label - if err = ls.mount(m, ""); err != nil { - return nil, err - } - - return m, nil + return nil } func (ls *layerStore) migrateLayer(tx MetadataTransaction, tarDataFile string, layer *roLayer) error { diff --git a/layer/migration_test.go b/layer/migration_test.go index 11614ffde0..509e0c4eb5 100644 --- a/layer/migration_test.go +++ b/layer/migration_test.go @@ -303,12 +303,20 @@ func TestMountMigration(t *testing.T) { t.Fatal(err) } - rwLayer1, err := ls.(*layerStore).MountByGraphID("migration-mount", containerID, layer1.ChainID()) + if err := ls.(*layerStore).CreateRWLayerByGraphID("migration-mount", containerID, layer1.ChainID()); err != nil { + t.Fatal(err) + } + + rwLayer1, err := ls.GetRWLayer("migration-mount") if err != nil { t.Fatal(err) } - changes, err := ls.Changes("migration-mount") + if _, err := rwLayer1.Mount(""); err != nil { + t.Fatal(err) + } + + changes, err := rwLayer1.Changes() if err != nil { t.Fatal(err) } @@ -341,39 +349,63 @@ func TestMountMigration(t *testing.T) { Kind: archive.ChangeAdd, }) - if expectedCount := 1; rwLayer1.(*mountedLayer).activityCount != expectedCount { - t.Fatalf("Wrong activity count %d, expected %d", rwLayer1.(*mountedLayer).activityCount, expectedCount) + assertActivityCount(t, rwLayer1, 1) + + if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), "", nil); err == nil { + t.Fatal("Expected error creating mount with same name") + } else if err != ErrMountNameConflict { + t.Fatal(err) } - rwLayer2, err := ls.Mount("migration-mount", layer1.ChainID(), "", nil) + rwLayer2, err := ls.GetRWLayer("migration-mount") if err != nil { t.Fatal(err) } - if rwLayer1 != rwLayer2 { - t.Fatalf("Wrong rwlayer %v, expected %v", rwLayer2, rwLayer1) + if getMountLayer(rwLayer1) != getMountLayer(rwLayer2) { + t.Fatal("Expected same layer from get with same name as from migrate") } - if expectedCount := 2; rwLayer2.(*mountedLayer).activityCount != expectedCount { - t.Fatalf("Wrong activity count %d, expected %d", rwLayer2.(*mountedLayer).activityCount, expectedCount) + if _, err := rwLayer2.Mount(""); err != nil { + t.Fatal(err) } + assertActivityCount(t, rwLayer2, 1) + assertActivityCount(t, rwLayer1, 1) + + if _, err := rwLayer2.Mount(""); err != nil { + t.Fatal(err) + } + + assertActivityCount(t, rwLayer2, 2) + assertActivityCount(t, rwLayer1, 1) + if metadata, err := ls.Release(layer1); err != nil { t.Fatal(err) } else if len(metadata) > 0 { t.Fatalf("Expected no layers to be deleted, deleted %#v", metadata) } - if err := ls.Unmount("migration-mount"); err != nil { + if err := rwLayer1.Unmount(); err != nil { t.Fatal(err) } - if _, err := ls.DeleteMount("migration-mount"); err == nil { + assertActivityCount(t, rwLayer2, 2) + assertActivityCount(t, rwLayer1, 0) + + if _, err := ls.ReleaseRWLayer(rwLayer1); err != nil { + t.Fatal(err) + } + + if err := rwLayer2.Unmount(); err != nil { + t.Fatal(err) + } + if _, err := ls.ReleaseRWLayer(rwLayer2); err == nil { t.Fatal("Expected error deleting active mount") } - if err := ls.Unmount("migration-mount"); err != nil { + if err := rwLayer2.Unmount(); err != nil { t.Fatal(err) } - metadata, err := ls.DeleteMount("migration-mount") + metadata, err := ls.ReleaseRWLayer(rwLayer2) if err != nil { t.Fatal(err) } diff --git a/layer/mount_test.go b/layer/mount_test.go index 195f81193d..6889912e6d 100644 --- a/layer/mount_test.go +++ b/layer/mount_test.go @@ -27,12 +27,12 @@ func TestMountInit(t *testing.T) { return initfile.ApplyFile(root) } - m, err := ls.Mount("fun-mount", layer.ChainID(), "", mountInit) + m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), "", mountInit) if err != nil { t.Fatal(err) } - path, err := m.Path() + path, err := m.Mount("") if err != nil { t.Fatal(err) } @@ -80,12 +80,12 @@ func TestMountSize(t *testing.T) { return newTestFile("file-init", contentInit, 0777).ApplyFile(root) } - m, err := ls.Mount("mount-size", layer.ChainID(), "", mountInit) + m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), "", mountInit) if err != nil { t.Fatal(err) } - path, err := m.Path() + path, err := m.Mount("") if err != nil { t.Fatal(err) } @@ -125,12 +125,12 @@ func TestMountChanges(t *testing.T) { return initfile.ApplyFile(root) } - m, err := ls.Mount("mount-changes", layer.ChainID(), "", mountInit) + m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), "", mountInit) if err != nil { t.Fatal(err) } - path, err := m.Path() + path, err := m.Mount("") if err != nil { t.Fatal(err) } @@ -155,7 +155,7 @@ func TestMountChanges(t *testing.T) { t.Fatal(err) } - changes, err := ls.Changes("mount-changes") + changes, err := m.Changes() if err != nil { t.Fatal(err) } diff --git a/layer/mounted_layer.go b/layer/mounted_layer.go index 35f43609c6..b3d6568833 100644 --- a/layer/mounted_layer.go +++ b/layer/mounted_layer.go @@ -1,15 +1,20 @@ package layer -import "io" +import ( + "io" + "sync" + + "github.com/docker/docker/pkg/archive" +) type mountedLayer struct { - name string - mountID string - initID string - parent *roLayer - path string - layerStore *layerStore - activityCount int + name string + mountID string + initID string + parent *roLayer + layerStore *layerStore + + references map[RWLayer]*referencedRWLayer } func (ml *mountedLayer) cacheParent() string { @@ -30,11 +35,8 @@ func (ml *mountedLayer) TarStream() (io.ReadCloser, error) { return archiver, nil } -func (ml *mountedLayer) Path() (string, error) { - if ml.path == "" { - return "", ErrNotMounted - } - return ml.path, nil +func (ml *mountedLayer) Name() string { + return ml.name } func (ml *mountedLayer) Parent() Layer { @@ -47,6 +49,96 @@ func (ml *mountedLayer) Parent() Layer { return nil } +func (ml *mountedLayer) Mount(mountLabel string) (string, error) { + return ml.layerStore.driver.Get(ml.mountID, mountLabel) +} + +func (ml *mountedLayer) Unmount() error { + return ml.layerStore.driver.Put(ml.mountID) +} + func (ml *mountedLayer) Size() (int64, error) { return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent()) } + +func (ml *mountedLayer) Changes() ([]archive.Change, error) { + return ml.layerStore.driver.Changes(ml.mountID, ml.cacheParent()) +} + +func (ml *mountedLayer) Metadata() (map[string]string, error) { + return ml.layerStore.driver.GetMetadata(ml.mountID) +} + +func (ml *mountedLayer) getReference() RWLayer { + ref := &referencedRWLayer{ + mountedLayer: ml, + } + ml.references[ref] = ref + + return ref +} + +func (ml *mountedLayer) hasReferences() bool { + return len(ml.references) > 0 +} + +func (ml *mountedLayer) deleteReference(ref RWLayer) error { + rl, ok := ml.references[ref] + if !ok { + return ErrLayerNotRetained + } + + if err := rl.release(); err != nil { + return err + } + delete(ml.references, ref) + + return nil +} + +type referencedRWLayer struct { + *mountedLayer + + activityL sync.Mutex + activityCount int +} + +func (rl *referencedRWLayer) release() error { + rl.activityL.Lock() + defer rl.activityL.Unlock() + + if rl.activityCount > 0 { + return ErrActiveMount + } + + rl.activityCount = -1 + + return nil +} + +func (rl *referencedRWLayer) Mount(mountLabel string) (string, error) { + rl.activityL.Lock() + defer rl.activityL.Unlock() + + if rl.activityCount == -1 { + return "", ErrLayerNotRetained + } + + rl.activityCount++ + return rl.mountedLayer.Mount(mountLabel) +} + +func (rl *referencedRWLayer) Unmount() error { + rl.activityL.Lock() + defer rl.activityL.Unlock() + + if rl.activityCount == 0 { + return ErrNotMounted + } + if rl.activityCount == -1 { + return ErrLayerNotRetained + } + rl.activityCount-- + + return rl.mountedLayer.Unmount() +} diff --git a/migrate/v1/migratev1.go b/migrate/v1/migratev1.go index a9110bca9c..c92c0f2107 100644 --- a/migrate/v1/migratev1.go +++ b/migrate/v1/migratev1.go @@ -24,8 +24,7 @@ type graphIDRegistrar interface { } type graphIDMounter interface { - MountByGraphID(string, string, layer.ChainID) (layer.RWLayer, error) - Unmount(string) error + CreateRWLayerByGraphID(string, string, layer.ChainID) error } const ( @@ -172,13 +171,7 @@ func migrateContainers(root string, ls graphIDMounter, is image.Store, imageMapp return err } - _, err = ls.MountByGraphID(id, id, img.RootFS.ChainID()) - if err != nil { - return err - } - - err = ls.Unmount(id) - if err != nil { + if err := ls.CreateRWLayerByGraphID(id, id, img.RootFS.ChainID()); err != nil { return err } diff --git a/migrate/v1/migratev1_test.go b/migrate/v1/migratev1_test.go index 067d04c382..5ee8206fdf 100644 --- a/migrate/v1/migratev1_test.go +++ b/migrate/v1/migratev1_test.go @@ -338,10 +338,9 @@ type mockMounter struct { count int } -func (r *mockMounter) MountByGraphID(name string, graphID string, parent layer.ChainID) (layer.RWLayer, error) { +func (r *mockMounter) CreateRWLayerByGraphID(name string, graphID string, parent layer.ChainID) error { r.mounts = append(r.mounts, mountInfo{name, graphID, string(parent)}) - r.count++ - return nil, nil + return nil } func (r *mockMounter) Unmount(string) error { r.count--