From ad0a6a03e3595aa04cf731cf17e90be87163389a Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 13 Sep 2013 15:56:06 +0200 Subject: [PATCH] Add Changes.ChangesLayers() This calculates the difference between a set of layers and a directory tree. --- changes.go | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/changes.go b/changes.go index 53fa2d49b4..49802d3170 100644 --- a/changes.go +++ b/changes.go @@ -235,11 +235,88 @@ func (info *FileInfo)Changes(oldInfo *FileInfo) []Change { } -func collectFileInfo(sourceDir string) (*FileInfo, error) { +func newRootFileInfo() *FileInfo { root := &FileInfo { name: "/", children: make(map[string]*FileInfo), } + return root +} + +func applyLayer(root *FileInfo, layer string) error { + err := filepath.Walk(layer, func(layerPath string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + // Skip root + if layerPath == layer { + return nil + } + + // rebase path + relPath, err := filepath.Rel(layer, layerPath) + if err != nil { + return err + } + relPath = filepath.Join("/", relPath) + + // Skip AUFS metadata + if matched, err := filepath.Match("/.wh..wh.*", relPath); err != nil || matched { + if err != nil || !f.IsDir() { + return err + } + return filepath.SkipDir + } + + var layerStat syscall.Stat_t + err = syscall.Lstat(layerPath, &layerStat) + if err != nil { + return err + } + + file := filepath.Base(relPath) + // If there is a whiteout, then the file was removed + if strings.HasPrefix(file, ".wh.") { + originalFile := file[len(".wh."):] + deletePath := filepath.Join(filepath.Dir(relPath), originalFile) + + root.Remove(deletePath) + } else { + // Added or changed file + existing := root.LookUp(relPath) + if existing != nil { + // Changed file + existing.stat = layerStat + if !existing.isDir() { + // Changed from dir to non-dir, delete all previous files + existing.children = make(map[string]*FileInfo) + } + } else { + // Added file + parent := root.LookUp(filepath.Dir(relPath)) + if parent == nil { + return fmt.Errorf("collectFileInfo: Unexpectedly no parent for %s", relPath) + } + + info := &FileInfo { + name: filepath.Base(relPath), + children: make(map[string]*FileInfo), + parent: parent, + stat: layerStat, + } + + parent.children[info.name] = info + } + } + return nil + }) + return err +} + + +func collectFileInfo(sourceDir string) (*FileInfo, error) { + root := newRootFileInfo() err := filepath.Walk(sourceDir, func(path string, f os.FileInfo, err error) error { if err != nil { @@ -282,6 +359,22 @@ func collectFileInfo(sourceDir string) (*FileInfo, error) { return root, nil } +func ChangesLayers(newDir string, layers []string) ([]Change, error) { + newRoot, err := collectFileInfo(newDir) + if err != nil { + return nil, err + } + oldRoot := newRootFileInfo() + for i := len(layers)-1; i >= 0; i-- { + layer := layers[i] + if err = applyLayer(oldRoot, layer); err != nil { + return nil, err + } + } + + return newRoot.Changes(oldRoot), nil +} + func ChangesDirs(newDir, oldDir string) ([]Change, error) { oldRoot, err := collectFileInfo(oldDir) if err != nil {