diff --git a/changes.go b/changes.go index 43573cd606..7a88ef0d01 100644 --- a/changes.go +++ b/changes.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "strings" + "syscall" ) type ChangeType int @@ -104,3 +105,104 @@ func Changes(layers []string, rw string) ([]Change, error) { } return changes, nil } + +func ChangesDirs(newDir, oldDir string) ([]Change, error) { + var changes []Change + err := filepath.Walk(newDir, func(newPath string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + var newStat syscall.Stat_t + err = syscall.Lstat(newPath, &newStat) + if err != nil { + return err + } + + // Rebase path + relPath, err := filepath.Rel(newDir, newPath) + if err != nil { + return err + } + relPath = filepath.Join("/", relPath) + + // Skip root + if relPath == "/" || relPath == "/.docker-id" { + return nil + } + + change := Change{ + Path: relPath, + } + + oldPath := filepath.Join(oldDir, relPath) + + var oldStat = &syscall.Stat_t{} + err = syscall.Lstat(oldPath, oldStat) + if err != nil { + if !os.IsNotExist(err) { + return err + } + oldStat = nil + } + + if oldStat == nil { + change.Kind = ChangeAdd + changes = append(changes, change) + } else { + if oldStat.Ino != newStat.Ino || + oldStat.Mode != newStat.Mode || + oldStat.Uid != newStat.Uid || + oldStat.Gid != newStat.Gid || + oldStat.Rdev != newStat.Rdev || + oldStat.Size != newStat.Size || + oldStat.Blocks != newStat.Blocks || + oldStat.Mtim != newStat.Mtim || + oldStat.Ctim != newStat.Ctim { + change.Kind = ChangeModify + changes = append(changes, change) + } + } + + return nil + }) + if err != nil { + return nil, err + } + err = filepath.Walk(oldDir, func(oldPath string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + // Rebase path + relPath, err := filepath.Rel(oldDir, oldPath) + if err != nil { + return err + } + relPath = filepath.Join("/", relPath) + + // Skip root + if relPath == "/" { + return nil + } + + change := Change{ + Path: relPath, + } + + newPath := filepath.Join(newDir, relPath) + + var newStat = &syscall.Stat_t{} + err = syscall.Lstat(newPath, newStat) + if err != nil && os.IsNotExist(err) { + change.Kind = ChangeDelete + changes = append(changes, change) + } + + return nil + }) + if err != nil { + return nil, err + } + return changes, nil +}