-
Notifications
You must be signed in to change notification settings - Fork 42
Staged layer creation #378
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
base: main
Are you sure you want to change the base?
Changes from all commits
a52b014
c6ab589
e482186
aadd1eb
8c29997
74665e8
0e52095
f8878bf
dcc6f44
142c05e
cdf0713
7e91b0e
be42227
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 |
|---|---|---|
|
|
@@ -995,6 +995,49 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr | |
| return d.create(id, parent, opts, true) | ||
| } | ||
|
|
||
| // getLayerPermissions returns the base permissions to use for the layer directories. | ||
| // The first return value is the idPair to create the possible parent directories with. | ||
| // The second return value is the mode how it should be stored on disk. | ||
| // The third return value is the mode the layer expects to have which may be stored | ||
| // in an xattr when using forceMask, without forceMask both values are the same. | ||
| func (d *Driver) getLayerPermissions(parent string, uidMaps, gidMaps []idtools.IDMap) (idtools.IDPair, idtools.Stat, idtools.Stat, error) { | ||
| rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) | ||
| if err != nil { | ||
| return idtools.IDPair{}, idtools.Stat{}, idtools.Stat{}, err | ||
| } | ||
|
|
||
| idPair := idtools.IDPair{ | ||
| UID: rootUID, | ||
| GID: rootGID, | ||
| } | ||
|
|
||
| st := idtools.Stat{IDs: idPair, Mode: defaultPerms} | ||
|
|
||
| if parent != "" { | ||
| parentBase := d.dir(parent) | ||
| parentDiff := filepath.Join(parentBase, "diff") | ||
| if xSt, err := idtools.GetContainersOverrideXattr(parentDiff); err == nil { | ||
| st = xSt | ||
| } else { | ||
| systemSt, err := system.Stat(parentDiff) | ||
| if err != nil { | ||
| return idtools.IDPair{}, idtools.Stat{}, idtools.Stat{}, err | ||
| } | ||
| st.IDs.UID = int(systemSt.UID()) | ||
| st.IDs.GID = int(systemSt.GID()) | ||
| st.Mode = os.FileMode(systemSt.Mode()) | ||
| } | ||
| } | ||
|
|
||
| forcedSt := st | ||
| if d.options.forceMask != nil { | ||
| forcedSt.IDs = idPair | ||
| forcedSt.Mode = *d.options.forceMask | ||
| } | ||
|
|
||
| return idPair, forcedSt, st, nil | ||
| } | ||
|
|
||
| func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnly bool) (retErr error) { | ||
| dir, homedir, _ := d.dir2(id, readOnly) | ||
|
|
||
|
|
@@ -1013,22 +1056,15 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl | |
| return err | ||
| } | ||
|
|
||
| rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) | ||
| idPair, forcedSt, st, err := d.getLayerPermissions(parent, uidMaps, gidMaps) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| idPair := idtools.IDPair{ | ||
| UID: rootUID, | ||
| GID: rootGID, | ||
| } | ||
|
|
||
| if err := idtools.MkdirAllAndChownNew(path.Dir(dir), 0o755, idPair); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| st := idtools.Stat{IDs: idPair, Mode: defaultPerms} | ||
|
|
||
| if parent != "" { | ||
| parentBase := d.dir(parent) | ||
| parentDiff := filepath.Join(parentBase, "diff") | ||
|
|
@@ -1088,12 +1124,6 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl | |
| } | ||
| } | ||
|
|
||
| forcedSt := st | ||
| if d.options.forceMask != nil { | ||
| forcedSt.IDs = idPair | ||
| forcedSt.Mode = *d.options.forceMask | ||
| } | ||
|
|
||
| diff := path.Join(dir, "diff") | ||
| if err := idtools.MkdirAs(diff, forcedSt.Mode, forcedSt.IDs.UID, forcedSt.IDs.GID); err != nil { | ||
| return err | ||
|
|
@@ -1356,6 +1386,14 @@ func (d *Driver) getTempDirRoot(id string) string { | |
| return filepath.Join(d.home, tempDirName) | ||
| } | ||
|
|
||
| // getTempDirRootForNewLayer returns the correct temp directory root based on where | ||
| // the layer should be created. | ||
| // | ||
| // This must be kept in sync with GetTempDirRootDirs(). | ||
| func (d *Driver) getTempDirRootForNewLayer() string { | ||
| return filepath.Join(d.homeDirForImageStore(), tempDirName) | ||
| } | ||
|
|
||
| func (d *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) { | ||
| tempDirRoot := d.getTempDirRoot(id) | ||
| t, err := tempdir.NewTempDir(tempDirRoot) | ||
|
|
@@ -2369,31 +2407,88 @@ func (d *Driver) DifferTarget(id string) (string, error) { | |
| return d.getDiffPath(id) | ||
| } | ||
|
|
||
| // ApplyDiff applies the new layer into a root | ||
| func (d *Driver) ApplyDiff(id, parent string, options graphdriver.ApplyDiffOpts) (size int64, err error) { | ||
| if !d.isParent(id, parent) { | ||
| if d.options.ignoreChownErrors { | ||
| options.IgnoreChownErrors = d.options.ignoreChownErrors | ||
| } | ||
| if d.options.forceMask != nil { | ||
| options.ForceMask = d.options.forceMask | ||
| // StartStagingDiffToApply applies the new layer into a temporary directory. | ||
| // It returns a CleanupTempDirFunc which can nil or set regardless if the function return an error or not. | ||
| // StagedAddition is only set when there is no error returned and the int64 value returns the size of the layer. | ||
| // This can be done without holding the storage lock. | ||
| // | ||
| // This API is experimental and can be changed without bumping the major version number. | ||
| func (d *Driver) StartStagingDiffToApply(parent string, options graphdriver.ApplyDiffOpts) (tempdir.CleanupTempDirFunc, *tempdir.StagedAddition, int64, error) { | ||
| tempDirRoot := d.getTempDirRootForNewLayer() | ||
| t, err := tempdir.NewTempDir(tempDirRoot) | ||
| if err != nil { | ||
| return nil, nil, -1, err | ||
| } | ||
|
|
||
| sa, err := t.StageAddition() | ||
| if err != nil { | ||
| return t.Cleanup, nil, -1, err | ||
| } | ||
|
|
||
| _, forcedSt, st, err := d.getLayerPermissions(parent, options.Mappings.UIDs(), options.Mappings.GIDs()) | ||
|
Contributor
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. This calls I think that’s fine if we know that the parent layer existed = was previously fully created — otherwise we might race against (If the layer existed and is later removed, we can fail with an error, but we won’t see invalid data. Failing with an error is semantically correct, creating a layer against a missing parent should fail.) |
||
| if err != nil { | ||
| return t.Cleanup, nil, -1, err | ||
| } | ||
|
|
||
| if err := idtools.MkdirAs(sa.Path, forcedSt.Mode, forcedSt.IDs.UID, forcedSt.IDs.GID); err != nil { | ||
| return t.Cleanup, nil, -1, err | ||
| } | ||
|
|
||
| if d.options.forceMask != nil { | ||
| st.Mode |= os.ModeDir | ||
| if err := idtools.SetContainersOverrideXattr(sa.Path, st); err != nil { | ||
| return t.Cleanup, nil, -1, err | ||
| } | ||
| return d.naiveDiff.ApplyDiff(id, parent, options) | ||
| } | ||
|
|
||
| idMappings := options.Mappings | ||
| if idMappings == nil { | ||
| idMappings = &idtools.IDMappings{} | ||
| size, err := d.applyDiff(sa.Path, options) | ||
| if err != nil { | ||
| return t.Cleanup, nil, -1, err | ||
| } | ||
|
|
||
| return t.Cleanup, sa, size, nil | ||
| } | ||
|
|
||
| // CommitStagedLayer that was created with StartStagingDiffToApply(). | ||
| // | ||
| // This API is experimental and can be changed without bumping the major version number. | ||
| func (d *Driver) CommitStagedLayer(id string, sa *tempdir.StagedAddition) error { | ||
| applyDir, err := d.getDiffPath(id) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // The os.Rename() function used by CommitFunc errors when the target directory already | ||
| // exists, as such delete the dir. The create() function creates it and it would be more | ||
| // complicated to code in a way that it didn't create it. | ||
| if err := os.Remove(applyDir); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return sa.Commit(applyDir) | ||
| } | ||
|
|
||
| // ApplyDiff applies the new layer into a root | ||
| func (d *Driver) ApplyDiff(id string, options graphdriver.ApplyDiffOpts) (size int64, err error) { | ||
| applyDir, err := d.getDiffPath(id) | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
| return d.applyDiff(applyDir, options) | ||
| } | ||
|
|
||
| // ApplyDiff applies the new layer into a root. | ||
| // This can run concurrently with any other driver operations, as such it is the | ||
| // callers responsibility to ensure the target path passed is safe to use if that is the case. | ||
| func (d *Driver) applyDiff(target string, options graphdriver.ApplyDiffOpts) (size int64, err error) { | ||
mtrmac marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| idMappings := options.Mappings | ||
|
Contributor
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. (Non-blocking: So far, all callers were setting |
||
| if idMappings == nil { | ||
| idMappings = &idtools.IDMappings{} | ||
| } | ||
|
|
||
| logrus.Debugf("Applying tar in %s", applyDir) | ||
| logrus.Debugf("Applying tar in %s", target) | ||
| // Overlay doesn't need the parent id to apply the diff | ||
| if err := untar(options.Diff, applyDir, &archive.TarOptions{ | ||
| if err := untar(options.Diff, target, &archive.TarOptions{ | ||
| UIDMaps: idMappings.UIDs(), | ||
| GIDMaps: idMappings.GIDs(), | ||
| IgnoreChownErrors: d.options.ignoreChownErrors, | ||
|
|
@@ -2404,7 +2499,7 @@ func (d *Driver) ApplyDiff(id, parent string, options graphdriver.ApplyDiffOpts) | |
| return 0, err | ||
| } | ||
|
|
||
| return directory.Size(applyDir) | ||
| return directory.Size(target) | ||
| } | ||
|
|
||
| func (d *Driver) getComposefsData(id string) string { | ||
|
|
||
This file was deleted.
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.
This part was copied into
getLayerPermissions, so I think it can be deleted here.