Fence installation transaction durability (T-613)

This commit is contained in:
ila
2026-07-18 16:28:49 +08:00
parent 84befee70f
commit 20596a7de4
18 changed files with 697 additions and 64 deletions
+40 -3
View File
@@ -66,6 +66,14 @@ func (record transactionRecord) validate() error {
}
func writeTransaction(layout appLayout, record transactionRecord) error {
return writeTransactionWithFence(layout, record, defaultDurability())
}
func writeTransactionWithFence(
layout appLayout,
record transactionRecord,
fence durabilityFence,
) error {
if err := record.validate(); err != nil {
return err
}
@@ -79,6 +87,7 @@ func writeTransaction(layout appLayout, record transactionRecord) error {
layout.transaction,
layout.transactionBackup,
data,
fence,
); err != nil {
return fmt.Errorf("write install transaction: %w", err)
}
@@ -135,11 +144,23 @@ func ensureJSONEOF(decoder *json.Decoder) error {
}
func removeTransaction(layout appLayout) error {
return removeTransactionWithFence(layout, defaultDurability())
}
func removeTransactionWithFence(layout appLayout, fence durabilityFence) error {
removed := false
if err := os.Remove(layout.transaction); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("remove install transaction: %w", err)
} else if err == nil {
removed = true
}
if err := os.Remove(layout.transactionBackup); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("remove install transaction backup: %w", err)
} else if err == nil {
removed = true
}
if removed {
return syncDirectoryWithFence(fence, layout.root, "app root after transaction removal")
}
return nil
}
@@ -149,6 +170,7 @@ func replaceFileWithBackup(
target string,
backup string,
data []byte,
fence durabilityFence,
) error {
temporary, err := os.CreateTemp(directory, ".install-transaction-*.tmp")
if err != nil {
@@ -165,7 +187,7 @@ func replaceFileWithBackup(
temporary.Close()
return err
}
if err := temporary.Sync(); err != nil {
if err := syncFileWithFence(fence, temporary, "temporary install transaction"); err != nil {
temporary.Close()
return err
}
@@ -178,12 +200,19 @@ func replaceFileWithBackup(
if info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular() {
return fmt.Errorf("%w: transaction target is not a regular file", ErrUnsafeInstallLayout)
}
if err := os.Remove(backup); err != nil && !os.IsNotExist(err) {
if err := os.Remove(backup); err != nil {
if !os.IsNotExist(err) {
return err
}
} else if err := syncDirectoryWithFence(fence, directory, "app root after stale transaction backup removal"); err != nil {
return err
}
if err := os.Rename(target, backup); err != nil {
return err
}
if err := syncDirectoryWithFence(fence, directory, "app root after transaction backup rename"); err != nil {
return err
}
movedTarget = true
} else if !os.IsNotExist(err) {
return err
@@ -191,14 +220,22 @@ func replaceFileWithBackup(
if err := os.Rename(temporaryPath, target); err != nil {
if movedTarget {
_ = os.Rename(backup, target)
if restoreErr := os.Rename(backup, target); restoreErr == nil {
_ = syncDirectoryWithFence(fence, directory, "app root after transaction restore")
}
}
return err
}
if err := syncDirectoryWithFence(fence, directory, "app root after transaction replace"); err != nil {
return err
}
if movedTarget {
if err := os.Remove(backup); err != nil && !os.IsNotExist(err) {
return err
}
if err := syncDirectoryWithFence(fence, directory, "app root after transaction backup removal"); err != nil {
return err
}
}
return nil
}