@@ -995,38 +995,22 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr
995995 return d .create (id , parent , opts , true )
996996}
997997
998- func (d * Driver ) create (id , parent string , opts * graphdriver.CreateOpts , readOnly bool ) (retErr error ) {
999- dir , homedir , _ := d .dir2 (id , readOnly )
1000-
1001- disableQuota := readOnly
1002-
1003- var uidMaps []idtools.IDMap
1004- var gidMaps []idtools.IDMap
1005-
1006- if opts != nil && opts .IDMappings != nil {
1007- uidMaps = opts .IDMappings .UIDs ()
1008- gidMaps = opts .IDMappings .GIDs ()
1009- }
1010-
1011- // Make the link directory if it does not exist
1012- if err := idtools .MkdirAllAs (path .Join (homedir , linkDir ), 0o755 , 0 , 0 ); err != nil {
1013- return err
1014- }
1015-
998+ // getLayerPermissions returns the base permissions to use for the layer directories.
999+ // The first return value is the idPair to create the possible parent directories with.
1000+ // The second return value is the mode how it should be stored on disk.
1001+ // The third return value is the mode the layer expects to have which may be stored
1002+ // in an xattr when using forceMask, without forceMask both values are the same.
1003+ func (d * Driver ) getLayerPermissions (parent string , uidMaps , gidMaps []idtools.IDMap ) (idtools.IDPair , idtools.Stat , idtools.Stat , error ) {
10161004 rootUID , rootGID , err := idtools .GetRootUIDGID (uidMaps , gidMaps )
10171005 if err != nil {
1018- return err
1006+ return idtools. IDPair {}, idtools. Stat {}, idtools. Stat {}, err
10191007 }
10201008
10211009 idPair := idtools.IDPair {
10221010 UID : rootUID ,
10231011 GID : rootGID ,
10241012 }
10251013
1026- if err := idtools .MkdirAllAndChownNew (path .Dir (dir ), 0o755 , idPair ); err != nil {
1027- return err
1028- }
1029-
10301014 st := idtools.Stat {IDs : idPair , Mode : defaultPerms }
10311015
10321016 if parent != "" {
@@ -1037,14 +1021,50 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl
10371021 } else {
10381022 systemSt , err := system .Stat (parentDiff )
10391023 if err != nil {
1040- return err
1024+ return idtools. IDPair {}, idtools. Stat {}, idtools. Stat {}, err
10411025 }
10421026 st .IDs .UID = int (systemSt .UID ())
10431027 st .IDs .GID = int (systemSt .GID ())
10441028 st .Mode = os .FileMode (systemSt .Mode ())
10451029 }
10461030 }
10471031
1032+ forcedSt := st
1033+ if d .options .forceMask != nil {
1034+ forcedSt .IDs = idPair
1035+ forcedSt .Mode = * d .options .forceMask
1036+ }
1037+
1038+ return idPair , forcedSt , st , nil
1039+ }
1040+
1041+ func (d * Driver ) create (id , parent string , opts * graphdriver.CreateOpts , readOnly bool ) (retErr error ) {
1042+ dir , homedir , _ := d .dir2 (id , readOnly )
1043+
1044+ disableQuota := readOnly
1045+
1046+ var uidMaps []idtools.IDMap
1047+ var gidMaps []idtools.IDMap
1048+
1049+ if opts != nil && opts .IDMappings != nil {
1050+ uidMaps = opts .IDMappings .UIDs ()
1051+ gidMaps = opts .IDMappings .GIDs ()
1052+ }
1053+
1054+ // Make the link directory if it does not exist
1055+ if err := idtools .MkdirAllAs (path .Join (homedir , linkDir ), 0o755 , 0 , 0 ); err != nil {
1056+ return err
1057+ }
1058+
1059+ idPair , forcedSt , st , err := d .getLayerPermissions (parent , uidMaps , gidMaps )
1060+ if err != nil {
1061+ return err
1062+ }
1063+
1064+ if err := idtools .MkdirAllAndChownNew (path .Dir (dir ), 0o755 , idPair ); err != nil {
1065+ return err
1066+ }
1067+
10481068 if err := fileutils .Lexists (dir ); err == nil {
10491069 logrus .Warnf ("Trying to create a layer %#v while directory %q already exists; removing it first" , id , dir )
10501070 // Don’t just os.RemoveAll(dir) here; d.Remove also removes the link in linkDir,
@@ -1088,12 +1108,6 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl
10881108 }
10891109 }
10901110
1091- forcedSt := st
1092- if d .options .forceMask != nil {
1093- forcedSt .IDs = idPair
1094- forcedSt .Mode = * d .options .forceMask
1095- }
1096-
10971111 diff := path .Join (dir , "diff" )
10981112 if err := idtools .MkdirAs (diff , forcedSt .Mode , forcedSt .IDs .UID , forcedSt .IDs .GID ); err != nil {
10991113 return err
@@ -1356,6 +1370,14 @@ func (d *Driver) getTempDirRoot(id string) string {
13561370 return filepath .Join (d .home , tempDirName )
13571371}
13581372
1373+ // getTempDirRootForNewLayer returns the correct temp directory root based on where
1374+ // the layer should be created.
1375+ //
1376+ // This must be kept in sync with GetTempDirRootDirs().
1377+ func (d * Driver ) getTempDirRootForNewLayer () string {
1378+ return filepath .Join (d .homeDirForImageStore (), tempDirName )
1379+ }
1380+
13591381func (d * Driver ) DeferredRemove (id string ) (tempdir.CleanupTempDirFunc , error ) {
13601382 tempDirRoot := d .getTempDirRoot (id )
13611383 t , err := tempdir .NewTempDir (tempDirRoot )
@@ -2369,31 +2391,94 @@ func (d *Driver) DifferTarget(id string) (string, error) {
23692391 return d .getDiffPath (id )
23702392}
23712393
2372- // ApplyDiff applies the new layer into a root
2373- func (d * Driver ) ApplyDiff (id , parent string , options graphdriver.ApplyDiffOpts ) (size int64 , err error ) {
2374- if ! d .isParent (id , parent ) {
2375- if d .options .ignoreChownErrors {
2376- options .IgnoreChownErrors = d .options .ignoreChownErrors
2394+ // StartStagingDiffToApply applies the new layer into a temporary directory.
2395+ // It returns a CleanupTempDirFunc which can be nil or set regardless if the function return an error or not.
2396+ // StagedAddition is only set when there is no error returned and the int64 value returns the size of the layer.
2397+ // This can be done without holding the storage lock, if a parent is given the caller must check for existence
2398+ // beforehand while holding a lock.
2399+ //
2400+ // This API is experimental and can be changed without bumping the major version number.
2401+ func (d * Driver ) StartStagingDiffToApply (parent string , options graphdriver.ApplyDiffOpts ) (tempdir.CleanupTempDirFunc , * tempdir.StagedAddition , int64 , error ) {
2402+ tempDirRoot := d .getTempDirRootForNewLayer ()
2403+ t , err := tempdir .NewTempDir (tempDirRoot )
2404+ if err != nil {
2405+ return nil , nil , - 1 , err
2406+ }
2407+
2408+ sa , err := t .StageAddition ()
2409+ if err != nil {
2410+ return t .Cleanup , nil , - 1 , err
2411+ }
2412+
2413+ _ , forcedSt , st , err := d .getLayerPermissions (parent , options .Mappings .UIDs (), options .Mappings .GIDs ())
2414+ if err != nil {
2415+ // If we have a ENOENT it means the parent was removed which can happen as we are unlocked here.
2416+ // In this case also wrap ErrLayerUnknown which some callers can handle to retry after recreating the parent.
2417+ if errors .Is (err , fs .ErrNotExist ) {
2418+ err = fmt .Errorf ("parent layer %q: %w: %w" , parent , graphdriver .ErrLayerUnknown , err )
23772419 }
2378- if d .options .forceMask != nil {
2379- options .ForceMask = d .options .forceMask
2420+ return t .Cleanup , nil , - 1 , err
2421+ }
2422+
2423+ if err := idtools .MkdirAs (sa .Path , forcedSt .Mode , forcedSt .IDs .UID , forcedSt .IDs .GID ); err != nil {
2424+ return t .Cleanup , nil , - 1 , err
2425+ }
2426+
2427+ if d .options .forceMask != nil {
2428+ st .Mode |= os .ModeDir
2429+ if err := idtools .SetContainersOverrideXattr (sa .Path , st ); err != nil {
2430+ return t .Cleanup , nil , - 1 , err
23802431 }
2381- return d .naiveDiff .ApplyDiff (id , parent , options )
23822432 }
23832433
2384- idMappings := options .Mappings
2385- if idMappings == nil {
2386- idMappings = & idtools.IDMappings {}
2434+ size , err := d .applyDiff (sa .Path , options )
2435+ if err != nil {
2436+ return t .Cleanup , nil , - 1 , err
2437+ }
2438+
2439+ return t .Cleanup , sa , size , nil
2440+ }
2441+
2442+ // CommitStagedLayer that was created with StartStagingDiffToApply().
2443+ //
2444+ // This API is experimental and can be changed without bumping the major version number.
2445+ func (d * Driver ) CommitStagedLayer (id string , sa * tempdir.StagedAddition ) error {
2446+ applyDir , err := d .getDiffPath (id )
2447+ if err != nil {
2448+ return err
2449+ }
2450+
2451+ // The os.Rename() function used by CommitFunc errors when the target directory already
2452+ // exists, as such delete the dir. The create() function creates it and it would be more
2453+ // complicated to code in a way that it didn't create it.
2454+ if err := os .Remove (applyDir ); err != nil {
2455+ return err
23872456 }
23882457
2458+ return sa .Commit (applyDir )
2459+ }
2460+
2461+ // ApplyDiff applies the new layer into a root
2462+ func (d * Driver ) ApplyDiff (id string , options graphdriver.ApplyDiffOpts ) (size int64 , err error ) {
23892463 applyDir , err := d .getDiffPath (id )
23902464 if err != nil {
23912465 return 0 , err
23922466 }
2467+ return d .applyDiff (applyDir , options )
2468+ }
2469+
2470+ // ApplyDiff applies the new layer into a root.
2471+ // This can run concurrently with any other driver operations, as such it is the
2472+ // callers responsibility to ensure the target path passed is safe to use if that is the case.
2473+ func (d * Driver ) applyDiff (target string , options graphdriver.ApplyDiffOpts ) (size int64 , err error ) {
2474+ idMappings := options .Mappings
2475+ if idMappings == nil {
2476+ idMappings = & idtools.IDMappings {}
2477+ }
23932478
2394- logrus .Debugf ("Applying tar in %s" , applyDir )
2479+ logrus .Debugf ("Applying tar in %s" , target )
23952480 // Overlay doesn't need the parent id to apply the diff
2396- if err := untar (options .Diff , applyDir , & archive.TarOptions {
2481+ if err := untar (options .Diff , target , & archive.TarOptions {
23972482 UIDMaps : idMappings .UIDs (),
23982483 GIDMaps : idMappings .GIDs (),
23992484 IgnoreChownErrors : d .options .ignoreChownErrors ,
@@ -2404,7 +2489,7 @@ func (d *Driver) ApplyDiff(id, parent string, options graphdriver.ApplyDiffOpts)
24042489 return 0 , err
24052490 }
24062491
2407- return directory .Size (applyDir )
2492+ return directory .Size (target )
24082493}
24092494
24102495func (d * Driver ) getComposefsData (id string ) string {
0 commit comments