Skip to content

Commit d0b4954

Browse files
committed
storage: preserve metadata for implicit dirs
1 parent 8af7873 commit d0b4954

10 files changed

Lines changed: 957 additions & 140 deletions

File tree

storage/drivers/driver.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ type DriverWithDifferOutput struct {
269269
TOCDigest digest.Digest
270270
// RootDirMode is the mode of the root directory of the layer, if specified.
271271
RootDirMode *os.FileMode
272+
// ImplicitDirs lists directories that had to be synthesized while staging
273+
// the layer contents and were never explicitly described by the layer.
274+
ImplicitDirs []string
272275
// Artifacts is a collection of additional artifacts
273276
// generated by the differ that the storage driver can use.
274277
Artifacts map[string]any

storage/drivers/overlay/overlay.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
graphdriver "go.podman.io/storage/drivers"
2828
"go.podman.io/storage/drivers/overlayutils"
2929
"go.podman.io/storage/drivers/quota"
30+
"go.podman.io/storage/drivers/unionbackfill"
3031
"go.podman.io/storage/internal/dedup"
3132
"go.podman.io/storage/internal/driver"
3233
"go.podman.io/storage/internal/staging_lockfile"
@@ -40,6 +41,7 @@ import (
4041
"go.podman.io/storage/pkg/idtools"
4142
"go.podman.io/storage/pkg/mount"
4243
"go.podman.io/storage/pkg/system"
44+
"go.podman.io/storage/pkg/tarbackfill"
4345
"go.podman.io/storage/pkg/unshare"
4446
"golang.org/x/sys/unix"
4547
)
@@ -2392,6 +2394,51 @@ func (d *Driver) ApplyDiffFromStagingDirectory(id, parent string, diffOutput *gr
23922394
return err
23932395
}
23942396
}
2397+
if d.options.forceMask == nil && parent != "" && len(diffOutput.ImplicitDirs) > 0 {
2398+
lowerDiffDirs, err := d.getLowerDiffPaths(id)
2399+
if err != nil {
2400+
return err
2401+
}
2402+
if len(lowerDiffDirs) > 0 {
2403+
backfiller := unionbackfill.NewBackfiller(options.Mappings, lowerDiffDirs)
2404+
for _, implicitDir := range diffOutput.ImplicitDirs {
2405+
hdr, err := backfiller.Backfill(implicitDir)
2406+
if err != nil {
2407+
return err
2408+
}
2409+
if hdr == nil {
2410+
continue
2411+
}
2412+
2413+
path := filepath.Join(stagingDirectory, implicitDir)
2414+
idPair := idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
2415+
if options.Mappings != nil {
2416+
if mapped, err := options.Mappings.ToHost(idPair); err == nil {
2417+
idPair = mapped
2418+
}
2419+
}
2420+
if err := os.Chown(path, idPair.UID, idPair.GID); err != nil {
2421+
return err
2422+
}
2423+
for xattr, xval := range hdr.Xattrs {
2424+
if err := system.Lsetxattr(path, xattr, []byte(xval), 0); err != nil {
2425+
return err
2426+
}
2427+
}
2428+
if err := os.Chmod(path, os.FileMode(hdr.Mode)&os.ModePerm); err != nil {
2429+
return err
2430+
}
2431+
atime := hdr.AccessTime
2432+
mtime := hdr.ModTime
2433+
if atime.IsZero() {
2434+
atime = mtime
2435+
}
2436+
if err := os.Chtimes(path, atime, mtime); err != nil {
2437+
return err
2438+
}
2439+
}
2440+
}
2441+
}
23952442

23962443
if d.usingComposefs {
23972444
toc := diffOutput.Artifacts[tocArtifact]
@@ -2498,8 +2545,21 @@ func (d *Driver) applyDiff(target string, options graphdriver.ApplyDiffOpts) (si
24982545
}
24992546

25002547
logrus.Debugf("Applying tar in %s", target)
2501-
// Overlay doesn't need the parent id to apply the diff
2502-
if err := untar(options.Diff, target, &archive.TarOptions{
2548+
diff := options.Diff
2549+
if id := filepath.Base(filepath.Dir(target)); id != "" {
2550+
lowerDiffDirs, err := d.getLowerDiffPaths(id)
2551+
if err != nil {
2552+
return 0, err
2553+
}
2554+
if len(lowerDiffDirs) > 0 {
2555+
backfiller := unionbackfill.NewBackfiller(idMappings, lowerDiffDirs)
2556+
rc := tarbackfill.NewIOReaderWithBackfiller(diff, backfiller)
2557+
defer rc.Close()
2558+
diff = rc
2559+
}
2560+
}
2561+
2562+
if err := untar(diff, target, &archive.TarOptions{
25032563
UIDMaps: idMappings.UIDs(),
25042564
GIDMaps: idMappings.GIDs(),
25052565
IgnoreChownErrors: d.options.ignoreChownErrors,
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package unionbackfill
2+
3+
import (
4+
"archive/tar"
5+
"io/fs"
6+
"os"
7+
"path"
8+
"path/filepath"
9+
"strings"
10+
11+
"go.podman.io/storage/pkg/archive"
12+
"go.podman.io/storage/pkg/idtools"
13+
"go.podman.io/storage/pkg/system"
14+
)
15+
16+
// NewBackfiller returns a helper that can synthesize metadata for directories
17+
// which exist only in lower layers.
18+
func NewBackfiller(idmap *idtools.IDMappings, lowerDiffDirs []string) *backfiller {
19+
if idmap != nil {
20+
uidMaps, gidMaps := idmap.UIDs(), idmap.GIDs()
21+
if len(uidMaps) > 0 || len(gidMaps) > 0 {
22+
idmap = idtools.NewIDMappingsFromMaps(append([]idtools.IDMap{}, uidMaps...), append([]idtools.IDMap{}, gidMaps...))
23+
}
24+
}
25+
return &backfiller{
26+
idmap: idmap,
27+
lowerDiffDirs: append([]string{}, lowerDiffDirs...),
28+
}
29+
}
30+
31+
type backfiller struct {
32+
idmap *idtools.IDMappings
33+
lowerDiffDirs []string
34+
}
35+
36+
// Backfill returns a tar header for pathname if it exists in a lower layer.
37+
func (b *backfiller) Backfill(pathname string) (*tar.Header, error) {
38+
for _, lowerDiffDir := range b.lowerDiffDirs {
39+
candidate := filepath.Join(lowerDiffDir, pathname)
40+
if st, err := os.Lstat(candidate); err == nil {
41+
var linkTarget string
42+
if st.Mode()&fs.ModeType == fs.ModeSymlink {
43+
target, err := os.Readlink(candidate)
44+
if err != nil {
45+
return nil, err
46+
}
47+
linkTarget = target
48+
}
49+
hdr, err := tar.FileInfoHeader(st, linkTarget)
50+
if err != nil {
51+
return nil, err
52+
}
53+
hdr.Name = strings.Trim(filepath.ToSlash(pathname), "/")
54+
if st.Mode()&fs.ModeType == fs.ModeDir {
55+
hdr.Name += "/"
56+
}
57+
if b.idmap != nil && !b.idmap.Empty() {
58+
if uid, gid, err := b.idmap.ToContainer(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}); err == nil {
59+
hdr.Uid, hdr.Gid = uid, gid
60+
}
61+
}
62+
return hdr, nil
63+
}
64+
65+
// If this path is hidden by an opaque directory in this lower, stop here.
66+
p := strings.Trim(pathname, "/")
67+
subpathname := ""
68+
for {
69+
dir, subdir := filepath.Split(p)
70+
dir = strings.Trim(dir, "/")
71+
if dir == p {
72+
break
73+
}
74+
75+
xval, err := system.Lgetxattr(filepath.Join(lowerDiffDir, dir), archive.GetOverlayXattrName("opaque"))
76+
if err == nil && len(xval) == 1 && xval[0] == 'y' {
77+
return nil, nil
78+
}
79+
if _, err := os.Stat(filepath.Join(lowerDiffDir, dir, archive.WhiteoutOpaqueDir)); err == nil {
80+
return nil, nil
81+
}
82+
83+
subpathname = strings.Trim(path.Join(subdir, subpathname), "/")
84+
xval, err = system.Lgetxattr(filepath.Join(lowerDiffDir, dir), archive.GetOverlayXattrName("redirect"))
85+
if err == nil && len(xval) > 0 {
86+
subdir := string(xval)
87+
if path.IsAbs(subdir) {
88+
pathname = path.Join(subdir, subpathname)
89+
} else {
90+
parent, _ := filepath.Split(dir)
91+
parent = strings.Trim(parent, "/")
92+
pathname = path.Join(parent, subdir, subpathname)
93+
}
94+
break
95+
}
96+
97+
p = dir
98+
}
99+
}
100+
101+
return nil, nil
102+
}

0 commit comments

Comments
 (0)