vendor: upgrade containers/storage
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
5addc8caf0
commit
ecd0006e80
114 changed files with 11464 additions and 1003 deletions
827
vendor/github.com/containers/storage/layers.go
generated
vendored
Normal file
827
vendor/github.com/containers/storage/layers.go
generated
vendored
Normal file
|
@ -0,0 +1,827 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
drivers "github.com/containers/storage/drivers"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
"github.com/containers/storage/pkg/truncindex"
|
||||
"github.com/vbatts/tar-split/tar/asm"
|
||||
"github.com/vbatts/tar-split/tar/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
tarSplitSuffix = ".tar-split.gz"
|
||||
incompleteFlag = "incomplete"
|
||||
compressionFlag = "diff-compression"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrParentUnknown indicates that we didn't record the ID of the parent of the specified layer
|
||||
ErrParentUnknown = errors.New("parent of layer not known")
|
||||
// ErrLayerUnknown indicates that there was no layer with the specified name or ID
|
||||
ErrLayerUnknown = errors.New("layer not known")
|
||||
)
|
||||
|
||||
// A Layer is a record of a copy-on-write layer that's stored by the lower
|
||||
// level graph driver.
|
||||
type Layer struct {
|
||||
// ID is either one which was specified at create-time, or a random
|
||||
// value which was generated by the library.
|
||||
ID string `json:"id"`
|
||||
|
||||
// Names is an optional set of user-defined convenience values. The
|
||||
// layer can be referred to by its ID or any of its names. Names are
|
||||
// unique among layers.
|
||||
Names []string `json:"names,omitempty"`
|
||||
|
||||
// Parent is the ID of a layer from which this layer inherits data.
|
||||
Parent string `json:"parent,omitempty"`
|
||||
|
||||
// Metadata is data we keep for the convenience of the caller. It is not
|
||||
// expected to be large, since it is kept in memory.
|
||||
Metadata string `json:"metadata,omitempty"`
|
||||
|
||||
// MountLabel is an SELinux label which should be used when attempting to mount
|
||||
// the layer.
|
||||
MountLabel string `json:"mountlabel,omitempty"`
|
||||
|
||||
// MountPoint is the path where the layer is mounted, or where it was most
|
||||
// recently mounted. This can change between subsequent Unmount() and
|
||||
// Mount() calls, so the caller should consult this value after Mount()
|
||||
// succeeds to find the location of the container's root filesystem.
|
||||
MountPoint string `json:"-"`
|
||||
|
||||
// MountCount is used as a reference count for the container's layer being
|
||||
// mounted at the mount point.
|
||||
MountCount int `json:"-"`
|
||||
|
||||
Flags map[string]interface{} `json:"flags,omitempty"`
|
||||
}
|
||||
|
||||
type layerMountPoint struct {
|
||||
ID string `json:"id"`
|
||||
MountPoint string `json:"path"`
|
||||
MountCount int `json:"count"`
|
||||
}
|
||||
|
||||
// LayerStore wraps a graph driver, adding the ability to refer to layers by
|
||||
// name, and keeping track of parent-child relationships, along with a list of
|
||||
// all known layers.
|
||||
type LayerStore interface {
|
||||
FileBasedStore
|
||||
MetadataStore
|
||||
FlaggableStore
|
||||
|
||||
// Create creates a new layer, optionally giving it a specified ID rather than
|
||||
// a randomly-generated one, either inheriting data from another specified
|
||||
// layer or the empty base layer. The new layer can optionally be given names
|
||||
// and have an SELinux label specified for use when mounting it. Some
|
||||
// underlying drivers can accept a "size" option. At this time, most
|
||||
// underlying drivers do not themselves distinguish between writeable
|
||||
// and read-only layers.
|
||||
Create(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool) (*Layer, error)
|
||||
|
||||
// CreateWithFlags combines the functions of Create and SetFlag.
|
||||
CreateWithFlags(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}) (layer *Layer, err error)
|
||||
|
||||
// Put combines the functions of CreateWithFlags and ApplyDiff.
|
||||
Put(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}, diff archive.Reader) (*Layer, int64, error)
|
||||
|
||||
// Exists checks if a layer with the specified name or ID is known.
|
||||
Exists(id string) bool
|
||||
|
||||
// Get retrieves information about a layer given an ID or name.
|
||||
Get(id string) (*Layer, error)
|
||||
|
||||
// SetNames replaces the list of names associated with a layer with the
|
||||
// supplied values.
|
||||
SetNames(id string, names []string) error
|
||||
|
||||
// Status returns an slice of key-value pairs, suitable for human consumption,
|
||||
// relaying whatever status information the underlying driver can share.
|
||||
Status() ([][2]string, error)
|
||||
|
||||
// Delete deletes a layer with the specified name or ID.
|
||||
Delete(id string) error
|
||||
|
||||
// Wipe deletes all layers.
|
||||
Wipe() error
|
||||
|
||||
// Mount mounts a layer for use. If the specified layer is the parent of other
|
||||
// layers, it should not be written to. An SELinux label to be applied to the
|
||||
// mount can be specified to override the one configured for the layer.
|
||||
Mount(id, mountLabel string) (string, error)
|
||||
|
||||
// Unmount unmounts a layer when it is no longer in use.
|
||||
Unmount(id string) error
|
||||
|
||||
// Changes returns a slice of Change structures, which contain a pathname
|
||||
// (Path) and a description of what sort of change (Kind) was made by the
|
||||
// layer (either ChangeModify, ChangeAdd, or ChangeDelete), relative to a
|
||||
// specified layer. By default, the layer's parent is used as a reference.
|
||||
Changes(from, to string) ([]archive.Change, error)
|
||||
|
||||
// Diff produces a tarstream which can be applied to a layer with the contents
|
||||
// of the first layer to produce a layer with the contents of the second layer.
|
||||
// By default, the parent of the second layer is used as the first
|
||||
// layer, so it need not be specified.
|
||||
Diff(from, to string) (io.ReadCloser, error)
|
||||
|
||||
// DiffSize produces an estimate of the length of the tarstream which would be
|
||||
// produced by Diff.
|
||||
DiffSize(from, to string) (int64, error)
|
||||
|
||||
// ApplyDiff reads a tarstream which was created by a previous call to Diff and
|
||||
// applies its changes to a specified layer.
|
||||
ApplyDiff(to string, diff archive.Reader) (int64, error)
|
||||
|
||||
// Lookup attempts to translate a name to an ID. Most methods do this
|
||||
// implicitly.
|
||||
Lookup(name string) (string, error)
|
||||
|
||||
// Layers returns a slice of the known layers.
|
||||
Layers() ([]Layer, error)
|
||||
}
|
||||
|
||||
type layerStore struct {
|
||||
lockfile Locker
|
||||
rundir string
|
||||
driver drivers.Driver
|
||||
layerdir string
|
||||
layers []Layer
|
||||
idindex *truncindex.TruncIndex
|
||||
byid map[string]*Layer
|
||||
byname map[string]*Layer
|
||||
bymount map[string]*Layer
|
||||
}
|
||||
|
||||
func (r *layerStore) Layers() ([]Layer, error) {
|
||||
return r.layers, nil
|
||||
}
|
||||
|
||||
func (r *layerStore) mountspath() string {
|
||||
return filepath.Join(r.rundir, "mountpoints.json")
|
||||
}
|
||||
|
||||
func (r *layerStore) layerspath() string {
|
||||
return filepath.Join(r.layerdir, "layers.json")
|
||||
}
|
||||
|
||||
func (r *layerStore) Load() error {
|
||||
needSave := false
|
||||
rpath := r.layerspath()
|
||||
data, err := ioutil.ReadFile(rpath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
layers := []Layer{}
|
||||
idlist := []string{}
|
||||
ids := make(map[string]*Layer)
|
||||
names := make(map[string]*Layer)
|
||||
mounts := make(map[string]*Layer)
|
||||
parents := make(map[string][]*Layer)
|
||||
if err = json.Unmarshal(data, &layers); len(data) == 0 || err == nil {
|
||||
for n, layer := range layers {
|
||||
ids[layer.ID] = &layers[n]
|
||||
idlist = append(idlist, layer.ID)
|
||||
for _, name := range layer.Names {
|
||||
if conflict, ok := names[name]; ok {
|
||||
r.removeName(conflict, name)
|
||||
needSave = true
|
||||
}
|
||||
names[name] = &layers[n]
|
||||
}
|
||||
if pslice, ok := parents[layer.Parent]; ok {
|
||||
parents[layer.Parent] = append(pslice, &layers[n])
|
||||
} else {
|
||||
parents[layer.Parent] = []*Layer{&layers[n]}
|
||||
}
|
||||
}
|
||||
}
|
||||
mpath := r.mountspath()
|
||||
data, err = ioutil.ReadFile(mpath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
layerMounts := []layerMountPoint{}
|
||||
if err = json.Unmarshal(data, &layerMounts); len(data) == 0 || err == nil {
|
||||
for _, mount := range layerMounts {
|
||||
if mount.MountPoint != "" {
|
||||
if layer, ok := ids[mount.ID]; ok {
|
||||
mounts[mount.MountPoint] = layer
|
||||
layer.MountPoint = mount.MountPoint
|
||||
layer.MountCount = mount.MountCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
r.layers = layers
|
||||
r.idindex = truncindex.NewTruncIndex(idlist)
|
||||
r.byid = ids
|
||||
r.byname = names
|
||||
r.bymount = mounts
|
||||
err = nil
|
||||
// Last step: try to remove anything that a previous user of this
|
||||
// storage area marked for deletion but didn't manage to actually
|
||||
// delete.
|
||||
for _, layer := range r.layers {
|
||||
if cleanup, ok := layer.Flags[incompleteFlag]; ok {
|
||||
if b, ok := cleanup.(bool); ok && b {
|
||||
err = r.Delete(layer.ID)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
needSave = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if needSave {
|
||||
r.Touch()
|
||||
return r.Save()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *layerStore) Save() error {
|
||||
rpath := r.layerspath()
|
||||
if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
jldata, err := json.Marshal(&r.layers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mpath := r.mountspath()
|
||||
if err := os.MkdirAll(filepath.Dir(mpath), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
mounts := []layerMountPoint{}
|
||||
for _, layer := range r.layers {
|
||||
if layer.MountPoint != "" && layer.MountCount > 0 {
|
||||
mounts = append(mounts, layerMountPoint{
|
||||
ID: layer.ID,
|
||||
MountPoint: layer.MountPoint,
|
||||
MountCount: layer.MountCount,
|
||||
})
|
||||
}
|
||||
}
|
||||
jmdata, err := json.Marshal(&mounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutils.AtomicWriteFile(rpath, jldata, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutils.AtomicWriteFile(mpath, jmdata, 0600)
|
||||
}
|
||||
|
||||
func newLayerStore(rundir string, layerdir string, driver drivers.Driver) (LayerStore, error) {
|
||||
if err := os.MkdirAll(rundir, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(layerdir, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockfile, err := GetLockfile(filepath.Join(layerdir, "layers.lock"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockfile.Lock()
|
||||
defer lockfile.Unlock()
|
||||
rlstore := layerStore{
|
||||
lockfile: lockfile,
|
||||
driver: driver,
|
||||
rundir: rundir,
|
||||
layerdir: layerdir,
|
||||
byid: make(map[string]*Layer),
|
||||
bymount: make(map[string]*Layer),
|
||||
byname: make(map[string]*Layer),
|
||||
}
|
||||
if err := rlstore.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rlstore, nil
|
||||
}
|
||||
|
||||
func (r *layerStore) lookup(id string) (*Layer, bool) {
|
||||
if layer, ok := r.byid[id]; ok {
|
||||
return layer, ok
|
||||
} else if layer, ok := r.byname[id]; ok {
|
||||
return layer, ok
|
||||
} else if longid, err := r.idindex.Get(id); err == nil {
|
||||
layer, ok := r.byid[longid]
|
||||
return layer, ok
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r *layerStore) ClearFlag(id string, flag string) error {
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
delete(layer.Flags, flag)
|
||||
return r.Save()
|
||||
}
|
||||
|
||||
func (r *layerStore) SetFlag(id string, flag string, value interface{}) error {
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
layer.Flags[flag] = value
|
||||
return r.Save()
|
||||
}
|
||||
|
||||
func (r *layerStore) Status() ([][2]string, error) {
|
||||
return r.driver.Status(), nil
|
||||
}
|
||||
|
||||
func (r *layerStore) Put(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}, diff archive.Reader) (layer *Layer, size int64, err error) {
|
||||
size = -1
|
||||
if err := os.MkdirAll(r.rundir, 0700); err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if err := os.MkdirAll(r.layerdir, 0700); err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if parent != "" {
|
||||
if parentLayer, ok := r.lookup(parent); ok {
|
||||
parent = parentLayer.ID
|
||||
}
|
||||
}
|
||||
if id == "" {
|
||||
id = stringid.GenerateRandomID()
|
||||
_, idInUse := r.byid[id]
|
||||
for idInUse {
|
||||
id = stringid.GenerateRandomID()
|
||||
_, idInUse = r.byid[id]
|
||||
}
|
||||
}
|
||||
if _, idInUse := r.byid[id]; idInUse {
|
||||
return nil, -1, ErrDuplicateID
|
||||
}
|
||||
for _, name := range names {
|
||||
if _, nameInUse := r.byname[name]; nameInUse {
|
||||
return nil, -1, ErrDuplicateName
|
||||
}
|
||||
}
|
||||
if writeable {
|
||||
err = r.driver.CreateReadWrite(id, parent, mountLabel, options)
|
||||
} else {
|
||||
err = r.driver.Create(id, parent, mountLabel, options)
|
||||
}
|
||||
if err == nil {
|
||||
newLayer := Layer{
|
||||
ID: id,
|
||||
Parent: parent,
|
||||
Names: names,
|
||||
MountLabel: mountLabel,
|
||||
Flags: make(map[string]interface{}),
|
||||
}
|
||||
r.layers = append(r.layers, newLayer)
|
||||
layer = &r.layers[len(r.layers)-1]
|
||||
r.idindex.Add(id)
|
||||
r.byid[id] = layer
|
||||
for _, name := range names {
|
||||
r.byname[name] = layer
|
||||
}
|
||||
for flag, value := range flags {
|
||||
layer.Flags[flag] = value
|
||||
}
|
||||
if diff != nil {
|
||||
layer.Flags[incompleteFlag] = true
|
||||
err = r.Save()
|
||||
if err != nil {
|
||||
// We don't have a record of this layer, but at least
|
||||
// try to clean it up underneath us.
|
||||
r.driver.Remove(id)
|
||||
return nil, -1, err
|
||||
}
|
||||
size, err = r.ApplyDiff(layer.ID, diff)
|
||||
if err != nil {
|
||||
if r.Delete(layer.ID) != nil {
|
||||
// Either a driver error or an error saving.
|
||||
// We now have a layer that's been marked for
|
||||
// deletion but which we failed to remove.
|
||||
}
|
||||
return nil, -1, err
|
||||
}
|
||||
delete(layer.Flags, incompleteFlag)
|
||||
}
|
||||
err = r.Save()
|
||||
if err != nil {
|
||||
// We don't have a record of this layer, but at least
|
||||
// try to clean it up underneath us.
|
||||
r.driver.Remove(id)
|
||||
return nil, -1, err
|
||||
}
|
||||
}
|
||||
return layer, size, err
|
||||
}
|
||||
|
||||
func (r *layerStore) CreateWithFlags(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}) (layer *Layer, err error) {
|
||||
layer, _, err = r.Put(id, parent, names, mountLabel, options, writeable, flags, nil)
|
||||
return layer, err
|
||||
}
|
||||
|
||||
func (r *layerStore) Create(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool) (layer *Layer, err error) {
|
||||
return r.CreateWithFlags(id, parent, names, mountLabel, options, writeable, nil)
|
||||
}
|
||||
|
||||
func (r *layerStore) Mount(id, mountLabel string) (string, error) {
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return "", ErrLayerUnknown
|
||||
}
|
||||
if layer.MountCount > 0 {
|
||||
layer.MountCount++
|
||||
return layer.MountPoint, r.Save()
|
||||
}
|
||||
if mountLabel == "" {
|
||||
mountLabel = layer.MountLabel
|
||||
}
|
||||
mountpoint, err := r.driver.Get(id, mountLabel)
|
||||
if mountpoint != "" && err == nil {
|
||||
if layer.MountPoint != "" {
|
||||
delete(r.bymount, layer.MountPoint)
|
||||
}
|
||||
layer.MountPoint = filepath.Clean(mountpoint)
|
||||
layer.MountCount++
|
||||
r.bymount[layer.MountPoint] = layer
|
||||
err = r.Save()
|
||||
}
|
||||
return mountpoint, err
|
||||
}
|
||||
|
||||
func (r *layerStore) Unmount(id string) error {
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
layerByMount, ok := r.bymount[filepath.Clean(id)]
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
layer = layerByMount
|
||||
}
|
||||
if layer.MountCount > 1 {
|
||||
layer.MountCount--
|
||||
return r.Save()
|
||||
}
|
||||
err := r.driver.Put(id)
|
||||
if err == nil || os.IsNotExist(err) {
|
||||
if layer.MountPoint != "" {
|
||||
delete(r.bymount, layer.MountPoint)
|
||||
}
|
||||
layer.MountCount--
|
||||
layer.MountPoint = ""
|
||||
err = r.Save()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *layerStore) removeName(layer *Layer, name string) {
|
||||
layer.Names = stringSliceWithoutValue(layer.Names, name)
|
||||
}
|
||||
|
||||
func (r *layerStore) SetNames(id string, names []string) error {
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
for _, name := range layer.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
for _, name := range names {
|
||||
if otherLayer, ok := r.byname[name]; ok {
|
||||
r.removeName(otherLayer, name)
|
||||
}
|
||||
r.byname[name] = layer
|
||||
}
|
||||
layer.Names = names
|
||||
return r.Save()
|
||||
}
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
|
||||
func (r *layerStore) Metadata(id string) (string, error) {
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
return layer.Metadata, nil
|
||||
}
|
||||
return "", ErrLayerUnknown
|
||||
}
|
||||
|
||||
func (r *layerStore) SetMetadata(id, metadata string) error {
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
layer.Metadata = metadata
|
||||
return r.Save()
|
||||
}
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
|
||||
func (r *layerStore) tspath(id string) string {
|
||||
return filepath.Join(r.layerdir, id+tarSplitSuffix)
|
||||
}
|
||||
|
||||
func (r *layerStore) Delete(id string) error {
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
id = layer.ID
|
||||
for layer.MountCount > 0 {
|
||||
if err := r.Unmount(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := r.driver.Remove(id)
|
||||
if err == nil {
|
||||
os.Remove(r.tspath(id))
|
||||
delete(r.byid, id)
|
||||
r.idindex.Delete(id)
|
||||
if layer.MountPoint != "" {
|
||||
delete(r.bymount, layer.MountPoint)
|
||||
}
|
||||
newLayers := []Layer{}
|
||||
for _, candidate := range r.layers {
|
||||
if candidate.ID != id {
|
||||
newLayers = append(newLayers, candidate)
|
||||
}
|
||||
}
|
||||
r.layers = newLayers
|
||||
if err = r.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *layerStore) Lookup(name string) (id string, err error) {
|
||||
if layer, ok := r.lookup(name); ok {
|
||||
return layer.ID, nil
|
||||
}
|
||||
return "", ErrLayerUnknown
|
||||
}
|
||||
|
||||
func (r *layerStore) Exists(id string) bool {
|
||||
_, ok := r.lookup(id)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *layerStore) Get(id string) (*Layer, error) {
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
return layer, nil
|
||||
}
|
||||
return nil, ErrLayerUnknown
|
||||
}
|
||||
|
||||
func (r *layerStore) Wipe() error {
|
||||
ids := []string{}
|
||||
for id := range r.byid {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
for _, id := range ids {
|
||||
if err := r.Delete(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *layerStore) findParentAndLayer(from, to string) (fromID string, toID string, toLayer *Layer, err error) {
|
||||
var ok bool
|
||||
var fromLayer *Layer
|
||||
toLayer, ok = r.lookup(to)
|
||||
if !ok {
|
||||
return "", "", nil, ErrLayerUnknown
|
||||
}
|
||||
to = toLayer.ID
|
||||
if from == "" {
|
||||
from = toLayer.Parent
|
||||
}
|
||||
if from != "" {
|
||||
fromLayer, ok = r.lookup(from)
|
||||
if ok {
|
||||
from = fromLayer.ID
|
||||
} else {
|
||||
fromLayer, ok = r.lookup(toLayer.Parent)
|
||||
if ok {
|
||||
from = fromLayer.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
return from, to, toLayer, nil
|
||||
}
|
||||
|
||||
func (r *layerStore) Changes(from, to string) ([]archive.Change, error) {
|
||||
from, to, _, err := r.findParentAndLayer(from, to)
|
||||
if err != nil {
|
||||
return nil, ErrLayerUnknown
|
||||
}
|
||||
return r.driver.Changes(to, from)
|
||||
}
|
||||
|
||||
type simpleGetCloser struct {
|
||||
r *layerStore
|
||||
path string
|
||||
id string
|
||||
}
|
||||
|
||||
func (s *simpleGetCloser) Get(path string) (io.ReadCloser, error) {
|
||||
return os.Open(filepath.Join(s.path, path))
|
||||
}
|
||||
|
||||
func (s *simpleGetCloser) Close() error {
|
||||
return s.r.Unmount(s.id)
|
||||
}
|
||||
|
||||
func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) {
|
||||
if getter, ok := r.driver.(drivers.DiffGetterDriver); ok {
|
||||
return getter.DiffGetter(id)
|
||||
}
|
||||
path, err := r.Mount(id, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &simpleGetCloser{
|
||||
r: r,
|
||||
path: path,
|
||||
id: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *layerStore) Diff(from, to string) (io.ReadCloser, error) {
|
||||
var metadata storage.Unpacker
|
||||
|
||||
from, to, toLayer, err := r.findParentAndLayer(from, to)
|
||||
if err != nil {
|
||||
return nil, ErrLayerUnknown
|
||||
}
|
||||
compression := archive.Uncompressed
|
||||
if cflag, ok := toLayer.Flags[compressionFlag]; ok {
|
||||
if ctype, ok := cflag.(float64); ok {
|
||||
compression = archive.Compression(ctype)
|
||||
} else if ctype, ok := cflag.(archive.Compression); ok {
|
||||
compression = archive.Compression(ctype)
|
||||
}
|
||||
}
|
||||
if from != toLayer.Parent {
|
||||
diff, err := r.driver.Diff(to, from)
|
||||
if err == nil && (compression != archive.Uncompressed) {
|
||||
preader, pwriter := io.Pipe()
|
||||
compressor, err := archive.CompressStream(pwriter, compression)
|
||||
if err != nil {
|
||||
diff.Close()
|
||||
pwriter.Close()
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
io.Copy(compressor, diff)
|
||||
diff.Close()
|
||||
compressor.Close()
|
||||
pwriter.Close()
|
||||
}()
|
||||
diff = preader
|
||||
}
|
||||
return diff, err
|
||||
}
|
||||
|
||||
tsfile, err := os.Open(r.tspath(to))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return r.driver.Diff(to, from)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer tsfile.Close()
|
||||
|
||||
decompressor, err := gzip.NewReader(tsfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer decompressor.Close()
|
||||
|
||||
tsbytes, err := ioutil.ReadAll(decompressor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metadata = storage.NewJSONUnpacker(bytes.NewBuffer(tsbytes))
|
||||
|
||||
fgetter, err := r.newFileGetter(to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var stream io.ReadCloser
|
||||
if compression != archive.Uncompressed {
|
||||
preader, pwriter := io.Pipe()
|
||||
compressor, err := archive.CompressStream(pwriter, compression)
|
||||
if err != nil {
|
||||
fgetter.Close()
|
||||
pwriter.Close()
|
||||
preader.Close()
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
asm.WriteOutputTarStream(fgetter, metadata, compressor)
|
||||
compressor.Close()
|
||||
pwriter.Close()
|
||||
}()
|
||||
stream = preader
|
||||
} else {
|
||||
stream = asm.NewOutputTarStream(fgetter, metadata)
|
||||
}
|
||||
return ioutils.NewReadCloserWrapper(stream, func() error {
|
||||
err1 := stream.Close()
|
||||
err2 := fgetter.Close()
|
||||
if err2 == nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (r *layerStore) DiffSize(from, to string) (size int64, err error) {
|
||||
from, to, _, err = r.findParentAndLayer(from, to)
|
||||
if err != nil {
|
||||
return -1, ErrLayerUnknown
|
||||
}
|
||||
return r.driver.DiffSize(to, from)
|
||||
}
|
||||
|
||||
func (r *layerStore) ApplyDiff(to string, diff archive.Reader) (size int64, err error) {
|
||||
layer, ok := r.lookup(to)
|
||||
if !ok {
|
||||
return -1, ErrLayerUnknown
|
||||
}
|
||||
|
||||
header := make([]byte, 10240)
|
||||
n, err := diff.Read(header)
|
||||
if err != nil && err != io.EOF {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
compression := archive.DetectCompression(header[:n])
|
||||
defragmented := io.MultiReader(bytes.NewBuffer(header[:n]), diff)
|
||||
|
||||
tsdata := bytes.Buffer{}
|
||||
compressor, err := gzip.NewWriterLevel(&tsdata, gzip.BestSpeed)
|
||||
if err != nil {
|
||||
compressor = gzip.NewWriter(&tsdata)
|
||||
}
|
||||
metadata := storage.NewJSONPacker(compressor)
|
||||
decompressed, err := archive.DecompressStream(defragmented)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
payload, err := asm.NewInputTarStream(decompressed, metadata, storage.NewDiscardFilePutter())
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
size, err = r.driver.ApplyDiff(layer.ID, layer.Parent, payload)
|
||||
compressor.Close()
|
||||
if err == nil {
|
||||
if err := os.MkdirAll(filepath.Dir(r.tspath(layer.ID)), 0700); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if err := ioutils.AtomicWriteFile(r.tspath(layer.ID), tsdata.Bytes(), 0600); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
if compression != archive.Uncompressed {
|
||||
layer.Flags[compressionFlag] = compression
|
||||
} else {
|
||||
delete(layer.Flags, compressionFlag)
|
||||
}
|
||||
|
||||
return size, err
|
||||
}
|
||||
|
||||
func (r *layerStore) Lock() {
|
||||
r.lockfile.Lock()
|
||||
}
|
||||
|
||||
func (r *layerStore) Unlock() {
|
||||
r.lockfile.Unlock()
|
||||
}
|
||||
|
||||
func (r *layerStore) Touch() error {
|
||||
return r.lockfile.Touch()
|
||||
}
|
||||
|
||||
func (r *layerStore) Modified() (bool, error) {
|
||||
return r.lockfile.Modified()
|
||||
}
|
||||
|
||||
func (r *layerStore) TouchedSince(when time.Time) bool {
|
||||
return r.lockfile.TouchedSince(when)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue