Skip to content

Commit a7a2938

Browse files
committed
storage: when creating layer apply diff unlocked
Signed-off-by: Paul Holzinger <[email protected]>
1 parent 4cc2aa3 commit a7a2938

File tree

1 file changed

+128
-15
lines changed

1 file changed

+128
-15
lines changed

storage/layers.go

Lines changed: 128 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,19 @@ func (r *layerStore) pickStoreLocation(volatile, writeable bool) layerLocations
13781378
}
13791379
}
13801380

1381+
func checkIdAndNameConflict(r *layerStore, id string, names []string) error {
1382+
if _, idInUse := r.byid[id]; idInUse {
1383+
return ErrDuplicateID
1384+
}
1385+
names = dedupeStrings(names)
1386+
for _, name := range names {
1387+
if _, nameInUse := r.byname[name]; nameInUse {
1388+
return ErrDuplicateName
1389+
}
1390+
}
1391+
return nil
1392+
}
1393+
13811394
// Requires startWriting.
13821395
func (r *layerStore) create(id string, parentLayer *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, diff io.Reader, slo *stagedLayerOptions) (layer *Layer, size int64, err error) {
13831396
if moreOptions == nil {
@@ -1400,14 +1413,9 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
14001413
_, idInUse = r.byid[id]
14011414
}
14021415
}
1403-
if duplicateLayer, idInUse := r.byid[id]; idInUse {
1404-
return duplicateLayer, -1, ErrDuplicateID
1405-
}
14061416
names = dedupeStrings(names)
1407-
for _, name := range names {
1408-
if _, nameInUse := r.byname[name]; nameInUse {
1409-
return nil, -1, ErrDuplicateName
1410-
}
1417+
if err := checkIdAndNameConflict(r, id, names); err != nil {
1418+
return nil, -1, err
14111419
}
14121420
parent := ""
14131421
if parentLayer != nil {
@@ -1448,9 +1456,6 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
14481456
selinux.ReserveLabel(mountLabel)
14491457
}
14501458

1451-
// Before actually creating the layer, make a persistent record of it
1452-
// with the incomplete flag set, so that future processes have a chance
1453-
// to clean up after it.
14541459
layer = &Layer{
14551460
ID: id,
14561461
Parent: parent,
@@ -1474,6 +1479,45 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
14741479
}
14751480
layer.Flags[incompleteFlag] = true
14761481

1482+
var (
1483+
cleanupFunctions []tempdir.CleanupTempDirFunc
1484+
applyDiffFunc func() error
1485+
applyDiffSize int64
1486+
)
1487+
1488+
applyDiffTemporaryDriver, ok := r.driver.(drivers.ApplyDiffStaging)
1489+
if ok && diff != nil {
1490+
// CRITICAL, this releases the lock so we can extract this unlocked
1491+
r.stopWriting()
1492+
1493+
var applyDiffError error
1494+
cleanupFunctions, applyDiffFunc, applyDiffSize, applyDiffError = r.applyDiffUnlocked(applyDiffTemporaryDriver, layer, moreOptions, diff)
1495+
// We must try to cleanup regardless of if an error was returned or not.
1496+
defer func() {
1497+
if err := tempdir.CleanupTemporaryDirectories(cleanupFunctions...); err != nil {
1498+
logrus.Errorf("Error cleaning up temporary directories: %v", err)
1499+
}
1500+
}()
1501+
// Acquire the lock again, this releases the lock so we can extract this unlocked.
1502+
// This must be done before checking for the error from applyDiffUnlocked() so the caller doesn't try to unlock again.
1503+
// FIXME: if startWriting() fails we return unlocked and then the parent unlocks again causing a panic. We should try to avoid that case somehow.
1504+
if err := r.startWriting(); err != nil {
1505+
return nil, -1, fmt.Errorf("re-acquire lock after applying layer diff: %w", err)
1506+
}
1507+
if applyDiffError != nil {
1508+
return nil, -1, applyDiffError
1509+
}
1510+
1511+
if err := checkIdAndNameConflict(r, id, names); err != nil {
1512+
logrus.Debugf("Layer id %s or name from %v was created while staging the layer content unlocked, aborting layer creation", id, names)
1513+
return nil, -1, err
1514+
}
1515+
}
1516+
1517+
// Before actually creating the layer, make a persistent record of it
1518+
// with the incomplete flag set, so that future processes have a chance
1519+
// to clean up after it.
1520+
14771521
r.layers = append(r.layers, layer)
14781522
// This can only fail if the ID is already missing, which shouldn’t
14791523
// happen — and in that case the index is already in the desired state
@@ -1568,7 +1612,13 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
15681612
}
15691613

15701614
size = -1
1571-
if diff != nil {
1615+
if applyDiffFunc != nil {
1616+
size = applyDiffSize
1617+
if err := applyDiffFunc(); err != nil {
1618+
cleanupFailureContext = "applying staged diff"
1619+
return nil, -1, err
1620+
}
1621+
} else if diff != nil {
15721622
if size, err = r.applyDiffWithOptions(layer, moreOptions, diff); err != nil {
15731623
cleanupFailureContext = "applying layer diff"
15741624
return nil, -1, err
@@ -2404,6 +2454,66 @@ func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error
24042454

24052455
type diffApplyFunc func(id string, parent string, options drivers.ApplyDiffOpts, tsBytes *bytes.Buffer) error
24062456

2457+
func (r *layerStore) applyDiffUnlocked(dd drivers.ApplyDiffStaging, layer *Layer, layerOptions *LayerOptions, diff io.Reader) ([]tempdir.CleanupTempDirFunc, func() error, int64, error) {
2458+
var (
2459+
size int64
2460+
cleanFunctions []tempdir.CleanupTempDirFunc
2461+
applyDiff func() error
2462+
)
2463+
2464+
f := func(id string, parent string, options drivers.ApplyDiffOpts, tsBytes *bytes.Buffer) error {
2465+
var (
2466+
err error
2467+
cleanup tempdir.CleanupTempDirFunc
2468+
commit tempdir.CommitFunc
2469+
)
2470+
cleanup, commit, size, err = dd.StartStagingDiffToApply(layer.ID, layer.Parent, options)
2471+
cleanFunctions = append(cleanFunctions, cleanup)
2472+
if err != nil {
2473+
return err
2474+
}
2475+
2476+
td, err := tempdir.NewTempDir(filepath.Join(r.layerdir, tempDirPath))
2477+
cleanFunctions = append(cleanFunctions, td.Cleanup)
2478+
if err != nil {
2479+
return err
2480+
}
2481+
2482+
sa, err := td.StageFileAddition(func(path string) error {
2483+
return os.WriteFile(path, tsBytes.Bytes(), 0o600)
2484+
})
2485+
cleanFunctions = append(cleanFunctions, td.Cleanup)
2486+
if err != nil {
2487+
return err
2488+
}
2489+
2490+
applyDiff = func() error {
2491+
err := dd.CommitStagedLayer(layer.ID, commit)
2492+
if err != nil {
2493+
return fmt.Errorf("commit temporary layer: %w", err)
2494+
}
2495+
2496+
if err := os.MkdirAll(filepath.Dir(r.tspath(layer.ID)), 0o700); err != nil {
2497+
return err
2498+
}
2499+
2500+
err = sa.Commit(r.tspath(layer.ID))
2501+
if err != nil {
2502+
return fmt.Errorf("commit tar split file: %w", err)
2503+
}
2504+
return nil
2505+
}
2506+
return nil
2507+
}
2508+
2509+
err := r.applyDiffWithOptionsInner(layer, layerOptions, diff, f)
2510+
if err != nil {
2511+
return cleanFunctions, nil, 0, err
2512+
}
2513+
2514+
return cleanFunctions, applyDiff, size, nil
2515+
}
2516+
24072517
// Requires startWriting.
24082518
func (r *layerStore) applyDiffWithOptions(layer *Layer, layerOptions *LayerOptions, diff io.Reader) (int64, error) {
24092519
var size int64
@@ -2422,7 +2532,12 @@ func (r *layerStore) applyDiffWithOptions(layer *Layer, layerOptions *LayerOptio
24222532
return nil
24232533
}
24242534

2425-
return size, r.applyDiffWithOptionsInner(layer, layerOptions, diff, f)
2535+
err := r.applyDiffWithOptionsInner(layer, layerOptions, diff, f)
2536+
if err != nil {
2537+
return 0, err
2538+
}
2539+
2540+
return size, r.saveFor(layer)
24262541
}
24272542

24282543
func (r *layerStore) applyDiffWithOptionsInner(layer *Layer, layerOptions *LayerOptions, diff io.Reader, applyFunc diffApplyFunc) (err error) {
@@ -2545,9 +2660,7 @@ func (r *layerStore) applyDiffWithOptionsInner(layer *Layer, layerOptions *Layer
25452660
}
25462661
slices.Sort(layer.GIDs)
25472662

2548-
err = r.saveFor(layer)
2549-
2550-
return err
2663+
return nil
25512664
}
25522665

25532666
// Requires (startReading or?) startWriting.

0 commit comments

Comments
 (0)