| // Package layer is package for managing read-only | |
| // and read-write mounts on the union file system | |
| // driver. Read-only mounts are referenced using a | |
| // content hash and are protected from mutation in | |
| // the exposed interface. The tar format is used | |
| // to create read-only layers and export both | |
| // read-only and writable layers. The exported | |
| // tar data for a read-only layer should match | |
| // the tar used to create the layer. | |
| package layer // import "github.com/docker/docker/layer" | |
| import ( | |
| "context" | |
| "errors" | |
| "io" | |
| "github.com/containerd/log" | |
| "github.com/docker/distribution" | |
| "github.com/docker/docker/pkg/archive" | |
| "github.com/opencontainers/go-digest" | |
| ) | |
| var ( | |
| // ErrLayerDoesNotExist is used when an operation is | |
| // attempted on a layer which does not exist. | |
| ErrLayerDoesNotExist = errors.New("layer does not exist") | |
| // ErrLayerNotRetained is used when a release is | |
| // attempted on a layer which is not retained. | |
| ErrLayerNotRetained = errors.New("layer not retained") | |
| // ErrMountDoesNotExist is used when an operation is | |
| // 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") | |
| // ErrMaxDepthExceeded is used when a layer is attempted | |
| // to be created which would result in a layer depth | |
| // greater than the 125 max. | |
| ErrMaxDepthExceeded = errors.New("max depth exceeded") | |
| ) | |
| // ChainID is the content-addressable ID of a layer. | |
| type ChainID digest.Digest | |
| // String returns a string rendition of a layer ID | |
| func (id ChainID) String() string { | |
| return string(id) | |
| } | |
| // DiffID is the hash of an individual layer tar. | |
| type DiffID digest.Digest | |
| // String returns a string rendition of a layer DiffID | |
| func (diffID DiffID) String() string { | |
| return string(diffID) | |
| } | |
| // TarStreamer represents an object which may | |
| // have its contents exported as a tar stream. | |
| type TarStreamer interface { | |
| // TarStream returns a tar archive stream | |
| // for the contents of a layer. | |
| TarStream() (io.ReadCloser, error) | |
| } | |
| // Layer represents a read-only layer | |
| type Layer interface { | |
| TarStreamer | |
| // TarStreamFrom returns a tar archive stream for all the layer chain with | |
| // arbitrary depth. | |
| TarStreamFrom(ChainID) (io.ReadCloser, error) | |
| // ChainID returns the content hash of the entire layer chain. The hash | |
| // chain is made up of DiffID of top layer and all of its parents. | |
| ChainID() ChainID | |
| // DiffID returns the content hash of the layer | |
| // tar stream used to create this layer. | |
| DiffID() DiffID | |
| // Parent returns the next layer in the layer chain. | |
| Parent() Layer | |
| // Size returns the size of the entire layer chain. The size | |
| // is calculated from the total size of all files in the layers. | |
| Size() int64 | |
| // DiffSize returns the size difference of the top layer | |
| // from parent layer. | |
| DiffSize() int64 | |
| // Metadata returns the low level storage metadata associated | |
| // with layer. | |
| Metadata() (map[string]string, error) | |
| } | |
| // RWLayer represents a layer which is | |
| // read and writable | |
| type RWLayer interface { | |
| TarStreamer | |
| // 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 | |
| // 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) | |
| // ApplyDiff applies the diff to the RW layer | |
| ApplyDiff(diff io.Reader) (int64, error) | |
| } | |
| // Metadata holds information about a | |
| // read-only layer | |
| type Metadata struct { | |
| // ChainID is the content hash of the layer | |
| ChainID ChainID | |
| // DiffID is the hash of the tar data used to | |
| // create the layer | |
| DiffID DiffID | |
| // Size is the size of the layer and all parents | |
| Size int64 | |
| // DiffSize is the size of the top layer | |
| DiffSize int64 | |
| } | |
| // MountInit is a function to initialize a | |
| // writable mount. Changes made here will | |
| // not be included in the Tar stream of the | |
| // RWLayer. | |
| type MountInit func(root string) error | |
| // CreateRWLayerOpts contains optional arguments to be passed to CreateRWLayer | |
| type CreateRWLayerOpts struct { | |
| MountLabel string | |
| InitFunc MountInit | |
| StorageOpt map[string]string | |
| } | |
| // Store represents a backend for managing both | |
| // read-only and read-write layers. | |
| type Store interface { | |
| Register(io.Reader, ChainID) (Layer, error) | |
| Get(ChainID) (Layer, error) | |
| Map() map[ChainID]Layer | |
| Release(Layer) ([]Metadata, error) | |
| CreateRWLayer(id string, parent ChainID, opts *CreateRWLayerOpts) (RWLayer, error) | |
| GetRWLayer(id string) (RWLayer, error) | |
| GetMountID(id string) (string, error) | |
| ReleaseRWLayer(RWLayer) ([]Metadata, error) | |
| Cleanup() error | |
| DriverStatus() [][2]string | |
| DriverName() string | |
| } | |
| // DescribableStore represents a layer store capable of storing | |
| // descriptors for layers. | |
| type DescribableStore interface { | |
| RegisterWithDescriptor(io.Reader, ChainID, distribution.Descriptor) (Layer, error) | |
| } | |
| // CreateChainID returns ID for a layerDigest slice | |
| func CreateChainID(dgsts []DiffID) ChainID { | |
| return createChainIDFromParent("", dgsts...) | |
| } | |
| func createChainIDFromParent(parent ChainID, dgsts ...DiffID) ChainID { | |
| if len(dgsts) == 0 { | |
| return parent | |
| } | |
| if parent == "" { | |
| return createChainIDFromParent(ChainID(dgsts[0]), dgsts[1:]...) // #nosec G602 -- slice index out of range, which is a false positive | |
| } | |
| // H = "H(n-1) SHA256(n)" | |
| dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0]))) // #nosec G602 -- slice index out of range, which is a false positive | |
| return createChainIDFromParent(ChainID(dgst), dgsts[1:]...) // #nosec G602 -- slice index out of range, which is a false positive | |
| } | |
| // ReleaseAndLog releases the provided layer from the given layer | |
| // store, logging any error and release metadata | |
| func ReleaseAndLog(ls Store, l Layer) { | |
| metadata, err := ls.Release(l) | |
| if err != nil { | |
| log.G(context.TODO()).Errorf("Error releasing layer %s: %v", l.ChainID(), err) | |
| } | |
| LogReleaseMetadata(metadata) | |
| } | |
| // LogReleaseMetadata logs a metadata array, uses this to | |
| // ensure consistent logging for release metadata | |
| func LogReleaseMetadata(metadatas []Metadata) { | |
| for _, metadata := range metadatas { | |
| log.G(context.TODO()).Infof("Layer %s cleaned up", metadata.ChainID) | |
| } | |
| } |