diff --git a/archive/changes.go b/archive/changes.go index eefc4b67e4..231f3064c1 100644 --- a/archive/changes.go +++ b/archive/changes.go @@ -136,6 +136,7 @@ type FileInfo struct { stat syscall.Stat_t children map[string]*FileInfo capability []byte + added bool } func (root *FileInfo) LookUp(path string) *FileInfo { @@ -169,6 +170,9 @@ func (info *FileInfo) isDir() bool { } func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) { + + sizeAtEntry := len(*changes) + if oldInfo == nil { // add change := Change{ @@ -176,6 +180,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) { Kind: ChangeAdd, } *changes = append(*changes, change) + info.added = true } // We make a copy so we can modify it to detect additions @@ -213,6 +218,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) { Kind: ChangeModify, } *changes = append(*changes, change) + newChild.added = true } // Remove from copy so we can detect deletions @@ -230,6 +236,19 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) { *changes = append(*changes, change) } + // If there were changes inside this directory, we need to add it, even if the directory + // itself wasn't changed. This is needed to properly save and restore filesystem permissions. + if len(*changes) > sizeAtEntry && info.isDir() && !info.added && info.path() != "/" { + change := Change{ + Path: info.path(), + Kind: ChangeModify, + } + // Let's insert the directory entry before the recently added entries located inside this dir + *changes = append(*changes, change) // just to resize the slice, will be overwritten + copy((*changes)[sizeAtEntry+1:], (*changes)[sizeAtEntry:]) + (*changes)[sizeAtEntry] = change + } + } func (info *FileInfo) Changes(oldInfo *FileInfo) []Change {