mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Fix change detection when applying tar layers
The default gnu tar format has no sub-second precision mtime support, and the golang tar writer currently doesn't support that either. This means if we export the changes from a container we will not get zeron in the sub-second precision field when the change is applied. This means we can't compare that to the original without getting a spurious change. So, we detect this case by treating a case where the seconds match and either of the two nanoseconds are zero as equal.
This commit is contained in:
parent
818c249bae
commit
10cd902f90
1 changed files with 18 additions and 2 deletions
|
@ -6,6 +6,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChangeType int
|
type ChangeType int
|
||||||
|
@ -34,6 +35,21 @@ func (change *Change) String() string {
|
||||||
return fmt.Sprintf("%s %s", kind, change.Path)
|
return fmt.Sprintf("%s %s", kind, change.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gnu tar and the go tar writer don't have sub-second mtime
|
||||||
|
// precision, which is problematic when we apply changes via tar
|
||||||
|
// files, we handle this by comparing for exact times, *or* same
|
||||||
|
// second count and either a or b having exactly 0 nanoseconds
|
||||||
|
func sameFsTime(a, b time.Time) bool {
|
||||||
|
return a == b ||
|
||||||
|
(a.Unix() == b.Unix() &&
|
||||||
|
(a.Nanosecond() == 0 || b.Nanosecond() == 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameFsTimeSpec(a, b syscall.Timespec) bool {
|
||||||
|
return a.Sec == b.Sec &&
|
||||||
|
(a.Nsec == b.Nsec || a.Nsec == 0 || b.Nsec == 0)
|
||||||
|
}
|
||||||
|
|
||||||
func Changes(layers []string, rw string) ([]Change, error) {
|
func Changes(layers []string, rw string) ([]Change, error) {
|
||||||
var changes []Change
|
var changes []Change
|
||||||
err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error {
|
err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error {
|
||||||
|
@ -85,7 +101,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
|
||||||
// However, if it's a directory, maybe it wasn't actually modified.
|
// However, if it's a directory, maybe it wasn't actually modified.
|
||||||
// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
|
// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
|
||||||
if stat.IsDir() && f.IsDir() {
|
if stat.IsDir() && f.IsDir() {
|
||||||
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
|
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) {
|
||||||
// Both directories are the same, don't record the change
|
// Both directories are the same, don't record the change
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -181,7 +197,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
|
||||||
oldStat.Rdev != newStat.Rdev ||
|
oldStat.Rdev != newStat.Rdev ||
|
||||||
// Don't look at size for dirs, its not a good measure of change
|
// Don't look at size for dirs, its not a good measure of change
|
||||||
(oldStat.Size != newStat.Size && oldStat.Mode&syscall.S_IFDIR != syscall.S_IFDIR) ||
|
(oldStat.Size != newStat.Size && oldStat.Mode&syscall.S_IFDIR != syscall.S_IFDIR) ||
|
||||||
getLastModification(oldStat) != getLastModification(newStat) {
|
!sameFsTimeSpec(getLastModification(oldStat), getLastModification(newStat)) {
|
||||||
change := Change{
|
change := Change{
|
||||||
Path: newChild.path(),
|
Path: newChild.path(),
|
||||||
Kind: ChangeModify,
|
Kind: ChangeModify,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue