Skip to content

Commit 2a0c3c2

Browse files
committed
aufs,overlay: backfill intermediate directories when possible
When extracting layer contents, if we find ourselves needing to implicitly create a directory (due to its not being included in the layer diff), try to give it the permissions, ownership, attributes, and datestamp of the corresponding directory from the layer which would be stacked below it. When a layer omits any of the directories which contain items which that layer adds or modifies, this should prevent the default values that we would use from overriding those which correspond to the same directory in a lower layer, which could later be mistaken as an indication that one or more of those was intentionally changed, forcing the directory to be pulled up. Signed-off-by: Nalin Dahyabhai <[email protected]>
1 parent dc79a0d commit 2a0c3c2

File tree

7 files changed

+1104
-1
lines changed

7 files changed

+1104
-1
lines changed

drivers/aufs/aufs.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"time"
3939

4040
graphdriver "github.com/containers/storage/drivers"
41+
"github.com/containers/storage/drivers/unionbackfill"
4142
"github.com/containers/storage/pkg/archive"
4243
"github.com/containers/storage/pkg/chrootarchive"
4344
"github.com/containers/storage/pkg/directory"
@@ -46,6 +47,7 @@ import (
4647
mountpk "github.com/containers/storage/pkg/mount"
4748
"github.com/containers/storage/pkg/parsers"
4849
"github.com/containers/storage/pkg/system"
50+
"github.com/containers/storage/pkg/tarbackfill"
4951
"github.com/containers/storage/pkg/unshare"
5052
"github.com/opencontainers/selinux/go-selinux/label"
5153
"github.com/sirupsen/logrus"
@@ -564,6 +566,16 @@ func (a *Driver) applyDiff(id string, idMappings *idtools.IDMappings, diff io.Re
564566
if idMappings == nil {
565567
idMappings = &idtools.IDMappings{}
566568
}
569+
parentDiffDirs, err := a.getParentLayerPaths(id)
570+
if err != nil {
571+
return err
572+
}
573+
if len(parentDiffDirs) > 0 {
574+
backfiller := unionbackfill.NewBackfiller(idMappings, parentDiffDirs)
575+
rc := tarbackfill.NewIOReaderWithBackfiller(diff, backfiller)
576+
defer rc.Close()
577+
diff = rc
578+
}
567579
return chrootarchive.UntarUncompressed(diff, path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
568580
UIDMaps: idMappings.UIDs(),
569581
GIDMaps: idMappings.GIDs(),

drivers/overlay/overlay.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
graphdriver "github.com/containers/storage/drivers"
2222
"github.com/containers/storage/drivers/overlayutils"
2323
"github.com/containers/storage/drivers/quota"
24+
"github.com/containers/storage/drivers/unionbackfill"
2425
"github.com/containers/storage/pkg/archive"
2526
"github.com/containers/storage/pkg/chrootarchive"
2627
"github.com/containers/storage/pkg/directory"
@@ -30,6 +31,7 @@ import (
3031
"github.com/containers/storage/pkg/mount"
3132
"github.com/containers/storage/pkg/parsers"
3233
"github.com/containers/storage/pkg/system"
34+
"github.com/containers/storage/pkg/tarbackfill"
3335
"github.com/containers/storage/pkg/unshare"
3436
units "github.com/docker/go-units"
3537
"github.com/hashicorp/go-multierror"
@@ -2116,7 +2118,18 @@ func (d *Driver) ApplyDiff(id, parent string, options graphdriver.ApplyDiffOpts)
21162118

21172119
logrus.Debugf("Applying tar in %s", applyDir)
21182120
// Overlay doesn't need the parent id to apply the diff
2119-
if err := untar(options.Diff, applyDir, &archive.TarOptions{
2121+
diff := options.Diff
2122+
lowerDiffDirs, err := d.getLowerDiffPaths(id)
2123+
if err != nil {
2124+
return 0, err
2125+
}
2126+
if len(lowerDiffDirs) > 0 {
2127+
backfiller := unionbackfill.NewBackfiller(idMappings, lowerDiffDirs)
2128+
rc := tarbackfill.NewIOReaderWithBackfiller(diff, backfiller)
2129+
defer rc.Close()
2130+
diff = rc
2131+
}
2132+
if err := untar(diff, applyDir, &archive.TarOptions{
21202133
UIDMaps: idMappings.UIDs(),
21212134
GIDMaps: idMappings.GIDs(),
21222135
IgnoreChownErrors: d.options.ignoreChownErrors,

drivers/unionbackfill/backfill.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package unionbackfill
2+
3+
import (
4+
"archive/tar"
5+
"io/fs"
6+
"os"
7+
"path"
8+
"path/filepath"
9+
"strings"
10+
11+
"github.com/containers/storage/pkg/archive"
12+
"github.com/containers/storage/pkg/idtools"
13+
"github.com/containers/storage/pkg/system"
14+
)
15+
16+
// NewBackfiller supplies a backfiller whose Backfill method provides the
17+
// ownership/permissions/attributes of a directory from a lower layer so that
18+
// we don't have to create it in an upper layer using default values that will
19+
// be mistaken for a reason that the directory was pulled up to that layer.
20+
func NewBackfiller(idmap *idtools.IDMappings, lowerDiffDirs []string) *backfiller {
21+
if idmap != nil {
22+
uidMaps, gidMaps := idmap.UIDs(), idmap.GIDs()
23+
if len(uidMaps) > 0 || len(gidMaps) > 0 {
24+
idmap = idtools.NewIDMappingsFromMaps(append([]idtools.IDMap{}, uidMaps...), append([]idtools.IDMap{}, gidMaps...))
25+
}
26+
}
27+
return &backfiller{idmap: idmap, lowerDiffDirs: append([]string{}, lowerDiffDirs...)}
28+
}
29+
30+
type backfiller struct {
31+
idmap *idtools.IDMappings
32+
lowerDiffDirs []string
33+
}
34+
35+
// Backfill supplies the ownership/permissions/attributes of a directory from a
36+
// lower layer so that we don't have to create it in an upper layer using
37+
// default values that will be mistaken for a reason that the directory was
38+
// pulled up to that layer.
39+
func (b *backfiller) Backfill(pathname string) (*tar.Header, error) {
40+
for _, lowerDiffDir := range b.lowerDiffDirs {
41+
candidate := filepath.Join(lowerDiffDir, pathname)
42+
// if the asked-for path is in this lower, return a tar header for it
43+
if st, err := os.Lstat(candidate); err == nil {
44+
var linkTarget string
45+
if st.Mode()&fs.ModeType == fs.ModeSymlink {
46+
target, err := os.Readlink(candidate)
47+
if err != nil {
48+
return nil, err
49+
}
50+
linkTarget = target
51+
}
52+
hdr, err := tar.FileInfoHeader(st, linkTarget)
53+
if err != nil {
54+
return nil, err
55+
}
56+
// this is where we'd delete "opaque" from the header, if FileInfoHeader read xattrs
57+
hdr.Name = strings.Trim(filepath.ToSlash(pathname), "/")
58+
if st.Mode()&fs.ModeType == fs.ModeDir {
59+
hdr.Name += "/"
60+
}
61+
if b.idmap != nil && !b.idmap.Empty() {
62+
if uid, gid, err := b.idmap.ToContainer(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}); err == nil {
63+
hdr.Uid, hdr.Gid = uid, gid
64+
}
65+
}
66+
return hdr, nil
67+
}
68+
// if the directory or any of its parents is marked opaque, we're done looking at lowers
69+
p := strings.Trim(pathname, "/")
70+
subpathname := ""
71+
for {
72+
dir, subdir := filepath.Split(p)
73+
dir = strings.Trim(dir, "/")
74+
if dir == p {
75+
break
76+
}
77+
// kernel overlay style
78+
xval, err := system.Lgetxattr(filepath.Join(lowerDiffDir, dir), archive.GetOverlayXattrName("opaque"))
79+
if err == nil && len(xval) == 1 && xval[0] == 'y' {
80+
return nil, nil
81+
}
82+
// aufs or fuse-overlayfs using aufs-like whiteouts
83+
if _, err := os.Stat(filepath.Join(lowerDiffDir, dir, archive.WhiteoutOpaqueDir)); err == nil {
84+
return nil, nil
85+
}
86+
// kernel overlay "redirect" - starting with the next lower layer, we'll need to look elsewhere
87+
subpathname = strings.Trim(path.Join(subdir, subpathname), "/")
88+
xval, err = system.Lgetxattr(filepath.Join(lowerDiffDir, dir), archive.GetOverlayXattrName("redirect"))
89+
if err == nil && len(xval) > 0 {
90+
subdir := string(xval)
91+
if path.IsAbs(subdir) {
92+
// path is relative to the root of the mount point
93+
pathname = path.Join(subdir, subpathname)
94+
} else {
95+
// path is relative to the current directory
96+
parent, _ := filepath.Split(dir)
97+
parent = strings.Trim(parent, "/")
98+
pathname = path.Join(parent, subdir, subpathname)
99+
}
100+
break
101+
}
102+
p = dir
103+
}
104+
}
105+
return nil, nil
106+
}

0 commit comments

Comments
 (0)