-
Notifications
You must be signed in to change notification settings - Fork 231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
overlay: support AdditionalImagesStore on vfs #1899
base: main
Are you sure you want to change the base?
Changes from all commits
85c7e53
01a0bc2
ef0f37e
4791448
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ import ( | |
graphdriver "github.com/containers/storage/drivers" | ||
"github.com/containers/storage/drivers/overlayutils" | ||
"github.com/containers/storage/drivers/quota" | ||
"github.com/containers/storage/drivers/vfs" | ||
"github.com/containers/storage/pkg/archive" | ||
"github.com/containers/storage/pkg/chrootarchive" | ||
"github.com/containers/storage/pkg/directory" | ||
|
@@ -977,7 +978,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr | |
} | ||
|
||
func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnly bool) (retErr error) { | ||
dir, homedir, _ := d.dir2(id, readOnly) | ||
dir, homedir, _, _ := d.dir2(id, false, false) | ||
|
||
disableQuota := readOnly | ||
|
||
|
@@ -1007,15 +1008,6 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl | |
if err := idtools.MkdirAllAndChownNew(path.Dir(dir), 0o755, idPair); err != nil { | ||
return err | ||
} | ||
if parent != "" { | ||
parentBase := d.dir(parent) | ||
st, err := system.Stat(filepath.Join(parentBase, "diff")) | ||
if err != nil { | ||
return err | ||
} | ||
rootUID = int(st.UID()) | ||
rootGID = int(st.GID()) | ||
} | ||
|
||
if err := fileutils.Lexists(dir); err == nil { | ||
logrus.Warnf("Trying to create a layer %#v while directory %q already exists; removing it first", id, dir) | ||
|
@@ -1066,11 +1058,19 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl | |
} | ||
|
||
if parent != "" { | ||
parentBase := d.dir(parent) | ||
st, err := system.Stat(filepath.Join(parentBase, "diff")) | ||
parentBase, _, singleLayer, _ := d.dir2(parent, false, true) | ||
var d string | ||
if singleLayer { | ||
d = parentBase | ||
} else { | ||
d = filepath.Join(parentBase, "diff") | ||
} | ||
st, err := system.Stat(d) | ||
mtrmac marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return err | ||
} | ||
rootUID = int(st.UID()) | ||
rootGID = int(st.GID()) | ||
perms = os.FileMode(st.Mode()) | ||
} | ||
|
||
|
@@ -1142,13 +1142,17 @@ func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) e | |
} | ||
|
||
func (d *Driver) getLower(parent string) (string, error) { | ||
parentDir := d.dir(parent) | ||
parentDir, _, singleLayer, _ := d.dir2(parent, false, true) | ||
|
||
// Ensure parent exists | ||
if err := fileutils.Lexists(parentDir); err != nil { | ||
return "", err | ||
} | ||
|
||
if singleLayer { | ||
return parent, nil | ||
} | ||
|
||
// Read Parent link fileA | ||
parentLink, err := os.ReadFile(path.Join(parentDir, "link")) | ||
if err != nil { | ||
|
@@ -1175,19 +1179,27 @@ func (d *Driver) getLower(parent string) (string, error) { | |
} | ||
|
||
func (d *Driver) dir(id string) string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From a quick skim it seems to me that various other callers of this also need updating:
There seem to be enough examples that I didn’t look into that exhaustively. Worrying about every caller handling all the cases is becoming unwieldy … I’m starting to think there should be a layer abstraction, to handle the differences between native overlay / VFS / FUSE additionalLayerStore / composeFS. |
||
p, _, _ := d.dir2(id, false) | ||
p, _, _, _ := d.dir2(id, false, false) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So it seems that https://github.com/containers/storage/pull/1899/files#r1576924079 was resolved by just not finding a VFS layer when Why is that safe and consistent? After looking a bit, it seems that only parent layers can be VFS layers (because non-parent layers would presumably be handled by the VFS driver directly, not overlay?) … but then I’m sorry, I just can’t deal with the complexity. Too many options, too many interactions, too many things that seem like they might be possible even if something elsewhere in the call stack probably prevents them. Most of that with documentation of neither intent nor maintained invariants. I straight can’t approve any PR adding a feature adding any more indirections or alternatives to the overlay driver any more. I’m not smart enough to keep up. It seems to me that most non-trivial PRs here would require me to spend something like 2 days just graphing out and understanding the interactions and possibilities. At some point, it must pay off to instead spend 2-3 weeks to restructure and explicitly document, so that PRs are again a matter of a small number of hours, and I’m now past the point where I’m willing to enable the current state to persist, and to keep paying for the complexity those 2 days at a time, every time. I don’t know what is the path forward. Probably easiest, find some other reviewer who is up to the task. Or, maybe, aggressively simplify. Build abstractions where they can be built and don’t leak. Find identifiable and isolated utilities that can be cut out of the >400-line functions. Rename things to be clear ( *shrug* it’s also possible that I’m overreacting and that a few sentences can make the picture clear. I’m not smart enough to figure that out myself. |
||
return p | ||
} | ||
|
||
func (d *Driver) getAllImageStores() []string { | ||
additionalImageStores := d.AdditionalImageStores() | ||
additionalImageStores, _ := d.AdditionalImageStores() | ||
if d.imageStore != "" { | ||
additionalImageStores = append([]string{d.imageStore}, additionalImageStores...) | ||
} | ||
return additionalImageStores | ||
} | ||
|
||
func (d *Driver) dir2(id string, useImageStore bool) (string, string, bool) { | ||
// dir2 returns the directory path and home directory path associated with the given layer ID. | ||
// It also returns two boolean values indicating if the store is using the VFS graph driver, and | ||
// whether the directory is found in an image store. | ||
// | ||
// - string: The directory path for the layer. | ||
// - string: The storage home directory path. | ||
// - bool: A boolean indicating whether the store is using VFS, thus doesn't require stacking of layers. | ||
// - bool: A boolean indicating whether the directory is found in an additional image store. | ||
func (d *Driver) dir2(id string, useImageStore, useVFS bool) (string, string, bool, bool) { | ||
var homedir string | ||
|
||
if useImageStore && d.imageStore != "" { | ||
|
@@ -1196,18 +1208,27 @@ func (d *Driver) dir2(id string, useImageStore bool) (string, string, bool) { | |
homedir = d.home | ||
} | ||
|
||
newpath := path.Join(homedir, id) | ||
primaryPath := path.Join(homedir, id) | ||
if err := fileutils.Exists(primaryPath); err == nil { | ||
return primaryPath, homedir, false, false | ||
} | ||
|
||
if err := fileutils.Exists(newpath); err != nil { | ||
for _, p := range d.getAllImageStores() { | ||
l := path.Join(p, d.name, id) | ||
err = fileutils.Exists(l) | ||
if err == nil { | ||
return l, homedir, true | ||
allImageStores := d.getAllImageStores() | ||
mtrmac marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for _, p := range allImageStores { | ||
l := path.Join(p, d.name, id) | ||
if err := fileutils.Exists(l); err == nil { | ||
return l, homedir, false, true | ||
} | ||
if useVFS { | ||
l = path.Join(p, vfs.Name, "dir", id) | ||
if err := fileutils.Exists(l); err == nil { | ||
return l, homedir, true, true | ||
} | ||
} | ||
} | ||
return newpath, homedir, false | ||
|
||
// It does not exist, return where to create it. | ||
return primaryPath, homedir, false, false | ||
} | ||
|
||
func (d *Driver) getLowerDirs(id string) ([]string, error) { | ||
|
@@ -1417,7 +1438,7 @@ func (d *Driver) Get(id string, options graphdriver.MountOpts) (string, error) { | |
} | ||
|
||
func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountOpts) (_ string, retErr error) { | ||
dir, _, inAdditionalStore := d.dir2(id, false) | ||
dir, _, _, inAdditionalStore := d.dir2(id, false, true) | ||
if err := fileutils.Exists(dir); err != nil { | ||
return "", err | ||
} | ||
|
@@ -1529,6 +1550,9 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO | |
composeFsLayersDir := filepath.Join(dir, "composefs-layers") | ||
maybeAddComposefsMount := func(lowerID string, i int, readWrite bool) (string, error) { | ||
composefsBlob := d.getComposefsData(lowerID) | ||
if composefsBlob == "" { | ||
return "", nil | ||
} | ||
if err := fileutils.Exists(composefsBlob); err != nil { | ||
if os.IsNotExist(err) { | ||
return "", nil | ||
|
@@ -1577,7 +1601,8 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO | |
lower := "" | ||
newpath := path.Join(d.home, l) | ||
if st, err := os.Stat(newpath); err != nil { | ||
for _, p := range d.getAllImageStores() { | ||
allImageStores := d.getAllImageStores() | ||
for _, p := range allImageStores { | ||
lower = path.Join(p, d.name, l) | ||
if st2, err2 := os.Stat(lower); err2 == nil { | ||
if !permsKnown { | ||
|
@@ -1586,6 +1611,15 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO | |
} | ||
break | ||
} | ||
// now try in the vfs store | ||
lower = path.Join(p, vfs.Name, "dir", l) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overall, I’d very much like the interdependencies between this and VFS to be transparent to IDEs (to make it clear what needs to be updated on format changes). Maybe create |
||
if st2, err2 := os.Stat(lower); err2 == nil { | ||
if !permsKnown { | ||
perms = os.FileMode(st2.Mode()) | ||
permsKnown = true | ||
} | ||
break | ||
} | ||
lower = "" | ||
} | ||
// if it is a "not found" error, that means the symlinks were lost in a sudden reboot | ||
|
@@ -1610,7 +1644,10 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO | |
|
||
linkContent, err := os.Readlink(lower) | ||
if err != nil { | ||
return "", err | ||
if !errors.Is(err, unix.EINVAL) { // Not a symlink, a raw path | ||
return "", err | ||
} | ||
linkContent = lower | ||
} | ||
lowerID := filepath.Base(filepath.Dir(linkContent)) | ||
composefsMount, err := maybeAddComposefsMount(lowerID, i+1, readWrite) | ||
|
@@ -1835,10 +1872,14 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO | |
|
||
// Put unmounts the mount path created for the give id. | ||
func (d *Driver) Put(id string) error { | ||
dir, _, inAdditionalStore := d.dir2(id, false) | ||
dir, _, singleLayer, inAdditionalStore := d.dir2(id, false, true) | ||
if err := fileutils.Exists(dir); err != nil { | ||
return err | ||
} | ||
if singleLayer { | ||
return nil | ||
} | ||
|
||
mountpoint := path.Join(dir, "merged") | ||
if count := d.ctr.Decrement(mountpoint); count > 0 { | ||
return nil | ||
|
@@ -2005,7 +2046,7 @@ func (g *overlayFileGetter) Close() error { | |
} | ||
|
||
func (d *Driver) getStagingDir(id string) string { | ||
_, homedir, _ := d.dir2(id, d.imageStore != "") | ||
_, homedir, _, _ := d.dir2(id, d.imageStore != "", false) | ||
return filepath.Join(homedir, stagingDir) | ||
} | ||
|
||
|
@@ -2282,8 +2323,8 @@ func (d *Driver) Changes(id string, idMappings *idtools.IDMappings, parent strin | |
} | ||
|
||
// AdditionalImageStores returns additional image stores supported by the driver | ||
func (d *Driver) AdditionalImageStores() []string { | ||
return d.options.imageStores | ||
func (d *Driver) AdditionalImageStores() ([]string, []string) { | ||
return d.options.imageStores, []string{d.name, vfs.Name} | ||
} | ||
|
||
// UpdateLayerIDMap updates ID mappings in a from matching the ones specified | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, I’m being annoying:
drivers.Driver
is a publicly visible API, reachable by external callers viaStore.GraphDriver()
.OTOH, yes, neither Podman nor CRI-O call this function. Meh. I’ll defer to actual c/storage owners.