layer: Fix orphan creation in registerWithDescriptor

Start the metadata transaction before creating the overlay2 directory.
This ensures that if driver.Create() fails, we can properly cancel the
transaction. Previously, if StartTransaction() failed after driver.Create()
succeeded, the defer cleanup would not run (not registered yet), leaving
an orphaned overlay2 directory.

The fix reorders operations so that:
1. Transaction is started first (no filesystem changes yet)
2. Overlay2 directory is created second (transaction ready for cleanup)
3. Defer is registered after both succeed (tx is guaranteed non-nil)

If driver.Create() fails, the transaction is explicitly cancelled before
returning. The nil check for tx in the defer is no longer needed since
tx is guaranteed to exist when the defer runs.

Related to moby/moby#45939

Signed-off-by: Jan Scheffler <jan.scheffler@qodev.ai>
(cherry picked from commit 70004549fb)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Jan Scheffler
2025-12-11 13:53:13 +00:00
committed by Paweł Gronowski
parent ecf8643fe1
commit 518779c90b

View File

@@ -294,12 +294,15 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descr
descriptor: descriptor,
}
if cErr = ls.driver.Create(layer.cacheID, pid, nil); cErr != nil {
tx, cErr := ls.store.StartTransaction()
if cErr != nil {
return nil, cErr
}
tx, cErr := ls.store.StartTransaction()
if cErr != nil {
if cErr = ls.driver.Create(layer.cacheID, pid, nil); cErr != nil {
if err := tx.Cancel(); err != nil {
log.G(context.TODO()).WithFields(log.Fields{"cache-id": layer.cacheID, "error": err}).Error("Error canceling metadata transaction")
}
return nil, cErr
}