Update containers/storage to pick up overlay driver fix

New pinned commit is ff8a6d2bf496daf46ab1a153f783a0f6b8762a54

This includes a fix to error reporting with overlayfs, and will
produce more verbose errors when initializing overlayfs fails.

Signed-off-by: Matthew Heon <matthew.heon@gmail.com>

Closes: #546
Approved by: baude
This commit is contained in:
Matthew Heon
2018-03-25 10:44:21 -04:00
committed by Atomic Bot
parent 1d4f40bd1a
commit f2894f243b
15 changed files with 321 additions and 54 deletions

View File

@ -3,7 +3,7 @@ github.com/sirupsen/logrus v1.0.0
github.com/containers/image b327f751c16e4a189fdcde4ea36be67cc964c605 github.com/containers/image b327f751c16e4a189fdcde4ea36be67cc964c605
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1 github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
github.com/ostreedev/ostree-go master github.com/ostreedev/ostree-go master
github.com/containers/storage 1e5ce40cdb84ab66e26186435b1273e04b879fef github.com/containers/storage ff8a6d2bf496daf46ab1a153f783a0f6b8762a54
github.com/containernetworking/cni v0.4.0 github.com/containernetworking/cni v0.4.0
google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go
github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd

View File

@ -106,10 +106,25 @@ type containerStore struct {
byname map[string]*Container byname map[string]*Container
} }
func copyContainer(c *Container) *Container {
return &Container{
ID: c.ID,
Names: copyStringSlice(c.Names),
ImageID: c.ImageID,
LayerID: c.LayerID,
Metadata: c.Metadata,
BigDataNames: copyStringSlice(c.BigDataNames),
BigDataSizes: copyStringInt64Map(c.BigDataSizes),
BigDataDigests: copyStringDigestMap(c.BigDataDigests),
Created: c.Created,
Flags: copyStringInterfaceMap(c.Flags),
}
}
func (r *containerStore) Containers() ([]Container, error) { func (r *containerStore) Containers() ([]Container, error) {
containers := make([]Container, len(r.containers)) containers := make([]Container, len(r.containers))
for i := range r.containers { for i := range r.containers {
containers[i] = *(r.containers[i]) containers[i] = *copyContainer(r.containers[i])
} }
return containers, nil return containers, nil
} }
@ -277,7 +292,7 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
} }
err = r.Save() err = r.Save()
} }
return container, err return copyContainer(container), err
} }
func (r *containerStore) Metadata(id string) (string, error) { func (r *containerStore) Metadata(id string) (string, error) {
@ -355,7 +370,7 @@ func (r *containerStore) Delete(id string) error {
func (r *containerStore) Get(id string) (*Container, error) { func (r *containerStore) Get(id string) (*Container, error) {
if container, ok := r.lookup(id); ok { if container, ok := r.lookup(id); ok {
return container, nil return copyContainer(container), nil
} }
return nil, ErrContainerUnknown return nil, ErrContainerUnknown
} }
@ -444,7 +459,7 @@ func (r *containerStore) BigDataNames(id string) ([]string, error) {
if !ok { if !ok {
return nil, ErrContainerUnknown return nil, ErrContainerUnknown
} }
return c.BigDataNames, nil return copyStringSlice(c.BigDataNames), nil
} }
func (r *containerStore) SetBigData(id, key string, data []byte) error { func (r *containerStore) SetBigData(id, key string, data []byte) error {

View File

@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. // Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: containers.go // source: ./containers.go
package storage package storage

View File

@ -114,8 +114,8 @@ func init() {
} }
// Init returns the a native diff driver for overlay filesystem. // Init returns the a native diff driver for overlay filesystem.
// If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. // If overlay filesystem is not supported on the host, a wrapped graphdriver.ErrNotSupported is returned as error.
// If an overlay filesystem is not supported over an existing filesystem then error graphdriver.ErrIncompatibleFS is returned. // If an overlay filesystem is not supported over an existing filesystem then a wrapped graphdriver.ErrIncompatibleFS is returned.
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
opts, err := parseOptions(options) opts, err := parseOptions(options)
if err != nil { if err != nil {
@ -151,7 +151,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
if err != nil { if err != nil {
os.Remove(filepath.Join(home, linkDir)) os.Remove(filepath.Join(home, linkDir))
os.Remove(home) os.Remove(home)
return nil, errors.Wrap(graphdriver.ErrNotSupported, "kernel does not support overlay fs") return nil, errors.Wrap(err, "kernel does not support overlay fs")
} }
if err := mount.MakePrivate(home); err != nil { if err := mount.MakePrivate(home); err != nil {
@ -740,6 +740,11 @@ func (d *Driver) Diff(id, parent, mountLabel string) (io.ReadCloser, error) {
return d.naiveDiff.Diff(id, parent, mountLabel) return d.naiveDiff.Diff(id, parent, mountLabel)
} }
lowerDirs, err := d.getLowerDirs(id)
if err != nil {
return nil, err
}
diffPath := d.getDiffPath(id) diffPath := d.getDiffPath(id)
logrus.Debugf("Tar with options on %s", diffPath) logrus.Debugf("Tar with options on %s", diffPath)
return archive.TarWithOptions(diffPath, &archive.TarOptions{ return archive.TarWithOptions(diffPath, &archive.TarOptions{
@ -747,6 +752,7 @@ func (d *Driver) Diff(id, parent, mountLabel string) (io.ReadCloser, error) {
UIDMaps: d.uidMaps, UIDMaps: d.uidMaps,
GIDMaps: d.gidMaps, GIDMaps: d.gidMaps,
WhiteoutFormat: archive.OverlayWhiteoutFormat, WhiteoutFormat: archive.OverlayWhiteoutFormat,
WhiteoutData: lowerDirs,
}) })
} }

View File

@ -3,8 +3,10 @@
package overlayutils package overlayutils
import ( import (
"errors"
"fmt" "fmt"
"github.com/containers/storage/drivers"
"github.com/pkg/errors"
) )
// ErrDTypeNotSupported denotes that the backing filesystem doesn't support d_type. // ErrDTypeNotSupported denotes that the backing filesystem doesn't support d_type.
@ -14,5 +16,5 @@ func ErrDTypeNotSupported(driver, backingFs string) error {
msg += " Reformat the filesystem with ftype=1 to enable d_type support." msg += " Reformat the filesystem with ftype=1 to enable d_type support."
} }
msg += " Running without d_type is not supported." msg += " Running without d_type is not supported."
return errors.New(msg) return errors.Wrap(graphdriver.ErrNotSupported, msg)
} }

View File

@ -124,10 +124,25 @@ type imageStore struct {
bydigest map[digest.Digest][]*Image bydigest map[digest.Digest][]*Image
} }
func copyImage(i *Image) *Image {
return &Image{
ID: i.ID,
Digest: i.Digest,
Names: copyStringSlice(i.Names),
TopLayer: i.TopLayer,
Metadata: i.Metadata,
BigDataNames: copyStringSlice(i.BigDataNames),
BigDataSizes: copyStringInt64Map(i.BigDataSizes),
BigDataDigests: copyStringDigestMap(i.BigDataDigests),
Created: i.Created,
Flags: copyStringInterfaceMap(i.Flags),
}
}
func (r *imageStore) Images() ([]Image, error) { func (r *imageStore) Images() ([]Image, error) {
images := make([]Image, len(r.images)) images := make([]Image, len(r.images))
for i := range r.images { for i := range r.images {
images[i] = *(r.images[i]) images[i] = *copyImage(r.images[i])
} }
return images, nil return images, nil
} }
@ -343,7 +358,7 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c
} }
err = r.Save() err = r.Save()
} }
return image, err return copyImage(image), err
} }
func (r *imageStore) Metadata(id string) (string, error) { func (r *imageStore) Metadata(id string) (string, error) {
@ -450,7 +465,7 @@ func (r *imageStore) Delete(id string) error {
func (r *imageStore) Get(id string) (*Image, error) { func (r *imageStore) Get(id string) (*Image, error) {
if image, ok := r.lookup(id); ok { if image, ok := r.lookup(id); ok {
return image, nil return copyImage(image), nil
} }
return nil, ErrImageUnknown return nil, ErrImageUnknown
} }
@ -546,7 +561,7 @@ func (r *imageStore) BigDataNames(id string) ([]string, error) {
if !ok { if !ok {
return nil, ErrImageUnknown return nil, ErrImageUnknown
} }
return image.BigDataNames, nil return copyStringSlice(image.BigDataNames), nil
} }
func imageSliceWithoutValue(slice []*Image, value *Image) []*Image { func imageSliceWithoutValue(slice []*Image, value *Image) []*Image {

View File

@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. // Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: images.go // source: ./images.go
package storage package storage

View File

@ -223,10 +223,29 @@ type layerStore struct {
byuncompressedsum map[digest.Digest][]string byuncompressedsum map[digest.Digest][]string
} }
func copyLayer(l *Layer) *Layer {
return &Layer{
ID: l.ID,
Names: copyStringSlice(l.Names),
Parent: l.Parent,
Metadata: l.Metadata,
MountLabel: l.MountLabel,
MountPoint: l.MountPoint,
MountCount: l.MountCount,
Created: l.Created,
CompressedDigest: l.CompressedDigest,
CompressedSize: l.CompressedSize,
UncompressedDigest: l.UncompressedDigest,
UncompressedSize: l.UncompressedSize,
CompressionType: l.CompressionType,
Flags: copyStringInterfaceMap(l.Flags),
}
}
func (r *layerStore) Layers() ([]Layer, error) { func (r *layerStore) Layers() ([]Layer, error) {
layers := make([]Layer, len(r.layers)) layers := make([]Layer, len(r.layers))
for i := range r.layers { for i := range r.layers {
layers[i] = *(r.layers[i]) layers[i] = *copyLayer(r.layers[i])
} }
return layers, nil return layers, nil
} }
@ -558,7 +577,7 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o
return nil, -1, err return nil, -1, err
} }
} }
return layer, size, err return copyLayer(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) { 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) {
@ -731,7 +750,7 @@ func (r *layerStore) Exists(id string) bool {
func (r *layerStore) Get(id string) (*Layer, error) { func (r *layerStore) Get(id string) (*Layer, error) {
if layer, ok := r.lookup(id); ok { if layer, ok := r.lookup(id); ok {
return layer, nil return copyLayer(layer), nil
} }
return nil, ErrLayerUnknown return nil, ErrLayerUnknown
} }
@ -1003,7 +1022,7 @@ func (r *layerStore) layersByDigestMap(m map[digest.Digest][]string, d digest.Di
if !ok { if !ok {
return nil, ErrLayerUnknown return nil, ErrLayerUnknown
} }
layers = append(layers, *layer) layers = append(layers, *copyLayer(layer))
} }
return layers, nil return layers, nil
} }

View File

@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. // Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: layers.go. Hack to make this work on github.com // source: ./layers.go
package storage package storage

View File

@ -45,6 +45,10 @@ type (
// This format will be converted to the standard format on pack // This format will be converted to the standard format on pack
// and from the standard format on unpack. // and from the standard format on unpack.
WhiteoutFormat WhiteoutFormat WhiteoutFormat WhiteoutFormat
// This is additional data to be used by the converter. It will
// not survive a round trip through JSON, so it's primarily
// intended for generating archives (i.e., converting writes).
WhiteoutData interface{}
// When unpacking, specifies whether overwriting a directory with a // When unpacking, specifies whether overwriting a directory with a
// non-directory is allowed and vice versa. // non-directory is allowed and vice versa.
NoOverwriteDirNonDir bool NoOverwriteDirNonDir bool
@ -702,7 +706,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
compressWriter, compressWriter,
options.ChownOpts, options.ChownOpts,
) )
ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat) ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat, options.WhiteoutData)
defer func() { defer func() {
// Make sure to check the error on Close. // Make sure to check the error on Close.
@ -860,7 +864,7 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err
var dirs []*tar.Header var dirs []*tar.Header
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
rootIDs := idMappings.RootPair() rootIDs := idMappings.RootPair()
whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat) whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat, options.WhiteoutData)
// Iterate through the files in the archive. // Iterate through the files in the archive.
loop: loop:

View File

@ -5,21 +5,27 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/system"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter { func getWhiteoutConverter(format WhiteoutFormat, data interface{}) tarWhiteoutConverter {
if format == OverlayWhiteoutFormat { if format == OverlayWhiteoutFormat {
return overlayWhiteoutConverter{} if rolayers, ok := data.([]string); ok && len(rolayers) > 0 {
return overlayWhiteoutConverter{rolayers: rolayers}
}
return overlayWhiteoutConverter{rolayers: nil}
} }
return nil return nil
} }
type overlayWhiteoutConverter struct{} type overlayWhiteoutConverter struct {
rolayers []string
}
func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) { func (o overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) {
// convert whiteouts to AUFS format // convert whiteouts to AUFS format
if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 { if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
// we just rename the file and make it normal // we just rename the file and make it normal
@ -31,7 +37,7 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
} }
if fi.Mode()&os.ModeDir != 0 { if fi.Mode()&os.ModeDir != 0 {
// convert opaque dirs to AUFS format by writing an empty file with the prefix // convert opaque dirs to AUFS format by writing an empty file with the whiteout prefix
opaque, err := system.Lgetxattr(path, "trusted.overlay.opaque") opaque, err := system.Lgetxattr(path, "trusted.overlay.opaque")
if err != nil { if err != nil {
return nil, err return nil, err
@ -40,7 +46,29 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
if hdr.Xattrs != nil { if hdr.Xattrs != nil {
delete(hdr.Xattrs, "trusted.overlay.opaque") delete(hdr.Xattrs, "trusted.overlay.opaque")
} }
// If there are no lower layers, then it can't have been deleted in this layer.
if len(o.rolayers) == 0 {
return nil, nil
}
// At this point, we have a directory that's opaque. If it appears in one of the lower
// layers, then it was newly-created here, so it wasn't also deleted here.
for _, rolayer := range o.rolayers {
stat, statErr := os.Stat(filepath.Join(rolayer, hdr.Name))
if statErr != nil && !os.IsNotExist(statErr) && !isENOTDIR(statErr) {
// Not sure what happened here.
return nil, statErr
}
if statErr == nil {
if stat.Mode()&os.ModeCharDevice != 0 {
// It's a whiteout for this directory, so it can't have been
// both deleted and recreated in the layer we're diffing.
s := stat.Sys().(*syscall.Stat_t)
if major(s.Rdev) == 0 && minor(s.Rdev) == 0 {
return nil, nil
}
}
// It's not whiteout, so it was there in the older layer, so we need to
// add a whiteout for this item in this layer.
// create a header for the whiteout file // create a header for the whiteout file
// it should inherit some properties from the parent, but be a regular file // it should inherit some properties from the parent, but be a regular file
wo = &tar.Header{ wo = &tar.Header{
@ -55,6 +83,28 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
AccessTime: hdr.AccessTime, AccessTime: hdr.AccessTime,
ChangeTime: hdr.ChangeTime, ChangeTime: hdr.ChangeTime,
} }
break
}
for dir := filepath.Dir(hdr.Name); dir != "" && dir != "." && dir != string(os.PathSeparator); dir = filepath.Dir(dir) {
// Check for whiteout for a parent directory in a parent layer.
stat, statErr := os.Stat(filepath.Join(rolayer, dir))
if statErr != nil && !os.IsNotExist(statErr) && !isENOTDIR(statErr) {
// Not sure what happened here.
return nil, statErr
}
if statErr == nil {
if stat.Mode()&os.ModeCharDevice != 0 {
// If it's whiteout for a parent directory, then the
// original directory wasn't inherited into this layer,
// so we don't need to emit whiteout for it.
s := stat.Sys().(*syscall.Stat_t)
if major(s.Rdev) == 0 && minor(s.Rdev) == 0 {
return nil, nil
}
}
}
}
}
} }
} }

View File

@ -2,6 +2,6 @@
package archive package archive
func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter { func getWhiteoutConverter(format WhiteoutFormat, data interface{}) tarWhiteoutConverter {
return nil return nil
} }

View File

@ -81,7 +81,7 @@ func sameFsTimeSpec(a, b syscall.Timespec) bool {
// Changes walks the path rw and determines changes for the files in the path, // Changes walks the path rw and determines changes for the files in the path,
// with respect to the parent layers // with respect to the parent layers
func Changes(layers []string, rw string) ([]Change, error) { func Changes(layers []string, rw string) ([]Change, error) {
return changes(layers, rw, aufsDeletedFile, aufsMetadataSkip) return changes(layers, rw, aufsDeletedFile, aufsMetadataSkip, aufsWhiteoutPresent)
} }
func aufsMetadataSkip(path string) (skip bool, err error) { func aufsMetadataSkip(path string) (skip bool, err error) {
@ -104,10 +104,35 @@ func aufsDeletedFile(root, path string, fi os.FileInfo) (string, error) {
return "", nil return "", nil
} }
func aufsWhiteoutPresent(root, path string) (bool, error) {
f := filepath.Join(filepath.Dir(path), WhiteoutPrefix+filepath.Base(path))
_, err := os.Stat(filepath.Join(root, f))
if err == nil {
return true, nil
}
if os.IsNotExist(err) || isENOTDIR(err) {
return false, nil
}
return false, err
}
func isENOTDIR(err error) bool {
if err == nil {
return false
}
if perror, ok := err.(*os.PathError); ok {
if errno, ok := perror.Err.(syscall.Errno); ok {
return errno == syscall.ENOTDIR
}
}
return false
}
type skipChange func(string) (bool, error) type skipChange func(string) (bool, error)
type deleteChange func(string, string, os.FileInfo) (string, error) type deleteChange func(string, string, os.FileInfo) (string, error)
type whiteoutChange func(string, string) (bool, error)
func changes(layers []string, rw string, dc deleteChange, sc skipChange) ([]Change, error) { func changes(layers []string, rw string, dc deleteChange, sc skipChange, wc whiteoutChange) ([]Change, error) {
var ( var (
changes []Change changes []Change
changedDirs = make(map[string]struct{}) changedDirs = make(map[string]struct{})
@ -156,7 +181,28 @@ func changes(layers []string, rw string, dc deleteChange, sc skipChange) ([]Chan
change.Kind = ChangeAdd change.Kind = ChangeAdd
// ...Unless it already existed in a top layer, in which case, it's a modification // ...Unless it already existed in a top layer, in which case, it's a modification
layerScan:
for _, layer := range layers { for _, layer := range layers {
if wc != nil {
// ...Unless a lower layer also had whiteout for this directory or one of its parents,
// in which case, it's new
ignore, err := wc(layer, path)
if err != nil {
return err
}
if ignore {
break layerScan
}
for dir := filepath.Dir(path); dir != "" && dir != string(os.PathSeparator); dir = filepath.Dir(dir) {
ignore, err = wc(layer, dir)
if err != nil {
return err
}
if ignore {
break layerScan
}
}
}
stat, err := os.Stat(filepath.Join(layer, path)) stat, err := os.Stat(filepath.Join(layer, path))
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err return err
@ -187,10 +233,15 @@ func changes(layers []string, rw string, dc deleteChange, sc skipChange) ([]Chan
} }
if change.Kind == ChangeAdd || change.Kind == ChangeDelete { if change.Kind == ChangeAdd || change.Kind == ChangeDelete {
parent := filepath.Dir(path) parent := filepath.Dir(path)
tail := []Change{}
for parent != "/" {
if _, ok := changedDirs[parent]; !ok && parent != "/" { if _, ok := changedDirs[parent]; !ok && parent != "/" {
changes = append(changes, Change{Path: parent, Kind: ChangeModify}) tail = append([]Change{{Path: parent, Kind: ChangeModify}}, tail...)
changedDirs[parent] = struct{}{} changedDirs[parent] = struct{}{}
} }
parent = filepath.Dir(parent)
}
changes = append(changes, tail...)
} }
// Record change // Record change

View File

@ -288,26 +288,96 @@ func clen(n []byte) int {
// OverlayChanges walks the path rw and determines changes for the files in the path, // OverlayChanges walks the path rw and determines changes for the files in the path,
// with respect to the parent layers // with respect to the parent layers
func OverlayChanges(layers []string, rw string) ([]Change, error) { func OverlayChanges(layers []string, rw string) ([]Change, error) {
return changes(layers, rw, overlayDeletedFile, nil) dc := func(root, path string, fi os.FileInfo) (string, error) {
return overlayDeletedFile(layers, root, path, fi)
}
return changes(layers, rw, dc, nil, overlayLowerContainsWhiteout)
} }
func overlayDeletedFile(root, path string, fi os.FileInfo) (string, error) { func overlayLowerContainsWhiteout(root, path string) (bool, error) {
// Whiteout for a file or directory has the same name, but is for a character
// device with major/minor of 0/0.
stat, err := os.Stat(filepath.Join(root, path))
if err != nil && !os.IsNotExist(err) && !isENOTDIR(err) {
// Not sure what happened here.
return false, err
}
if err == nil && stat.Mode()&os.ModeCharDevice != 0 {
// Check if there's whiteout for the specified item in the specified layer.
s := stat.Sys().(*syscall.Stat_t)
if major(s.Rdev) == 0 && minor(s.Rdev) == 0 {
return true, nil
}
}
return false, nil
}
func overlayDeletedFile(layers []string, root, path string, fi os.FileInfo) (string, error) {
// If it's a whiteout item, then a file or directory with that name is removed by this layer.
if fi.Mode()&os.ModeCharDevice != 0 { if fi.Mode()&os.ModeCharDevice != 0 {
s := fi.Sys().(*syscall.Stat_t) s := fi.Sys().(*syscall.Stat_t)
if major(s.Rdev) == 0 && minor(s.Rdev) == 0 { if major(s.Rdev) == 0 && minor(s.Rdev) == 0 {
return path, nil return path, nil
} }
} }
if fi.Mode()&os.ModeDir != 0 { // After this we only need to pay attention to directories.
if !fi.IsDir() {
return "", nil
}
// If the directory isn't marked as opaque, then it's just a normal directory.
opaque, err := system.Lgetxattr(filepath.Join(root, path), "trusted.overlay.opaque") opaque, err := system.Lgetxattr(filepath.Join(root, path), "trusted.overlay.opaque")
if err != nil { if err != nil {
return "", err return "", err
} }
if len(opaque) == 1 && opaque[0] == 'y' { if len(opaque) != 1 || opaque[0] != 'y' {
return "", err
}
// If there are no lower layers, then it can't have been deleted and recreated in this layer.
if len(layers) == 0 {
return "", err
}
// At this point, we have a directory that's opaque. If it appears in one of the lower
// layers, then it was newly-created here, so it wasn't also deleted here.
for _, layer := range layers {
stat, err := os.Stat(filepath.Join(layer, path))
if err != nil && !os.IsNotExist(err) && !isENOTDIR(err) {
// Not sure what happened here.
return "", err
}
if err == nil {
if stat.Mode()&os.ModeCharDevice != 0 {
// It's a whiteout for this directory, so it can't have been
// deleted in this layer.
s := stat.Sys().(*syscall.Stat_t)
if major(s.Rdev) == 0 && minor(s.Rdev) == 0 {
return "", nil
}
}
// It's not whiteout, so it was there in the older layer, so it has to be
// marked as deleted in this layer.
return path, nil return path, nil
} }
for dir := filepath.Dir(path); dir != "" && dir != string(os.PathSeparator); dir = filepath.Dir(dir) {
// Check for whiteout for a parent directory.
stat, err := os.Stat(filepath.Join(layer, dir))
if err != nil && !os.IsNotExist(err) && !isENOTDIR(err) {
// Not sure what happened here.
return "", err
}
if err == nil {
if stat.Mode()&os.ModeCharDevice != 0 {
// If it's whiteout for a parent directory, then the
// original directory wasn't inherited into the top layer.
s := stat.Sys().(*syscall.Stat_t)
if major(s.Rdev) == 0 && minor(s.Rdev) == 0 {
return "", nil
}
}
}
}
} }
// We didn't find the same path in any older layers, so it was new in this one.
return "", nil return "", nil
} }

View File

@ -2350,6 +2350,41 @@ func stringSliceWithoutValue(slice []string, value string) []string {
return modified return modified
} }
func copyStringSlice(slice []string) []string {
if len(slice) == 0 {
return nil
}
ret := make([]string, len(slice))
copy(ret, slice)
return ret
}
func copyStringInt64Map(m map[string]int64) map[string]int64 {
ret := make(map[string]int64, len(m))
for k, v := range m {
ret[k] = v
}
return ret
}
func copyStringDigestMap(m map[string]digest.Digest) map[string]digest.Digest {
ret := make(map[string]digest.Digest, len(m))
for k, v := range m {
ret[k] = v
}
return ret
}
// copyStringInterfaceMap still forces us to assume that the interface{} is
// a non-pointer scalar value
func copyStringInterfaceMap(m map[string]interface{}) map[string]interface{} {
ret := make(map[string]interface{}, len(m))
for k, v := range m {
ret[k] = v
}
return ret
}
const configFile = "/etc/containers/storage.conf" const configFile = "/etc/containers/storage.conf"
// OptionsConfig represents the "storage.options" TOML config table. // OptionsConfig represents the "storage.options" TOML config table.