2013-11-01 01:06:35 -04:00
|
|
|
package archive
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
2013-11-07 18:58:03 -05:00
|
|
|
"path/filepath"
|
2013-11-01 01:06:35 -04:00
|
|
|
"strings"
|
2013-11-19 03:35:03 -05:00
|
|
|
"syscall"
|
|
|
|
"time"
|
2013-11-01 01:06:35 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// ApplyLayer parses a diff in the standard layer format from `layer`, and
|
|
|
|
// applies it to the directory `dest`.
|
|
|
|
func ApplyLayer(dest string, layer Archive) error {
|
|
|
|
// Poor man's diff applyer in 2 steps:
|
|
|
|
|
|
|
|
// Step 1: untar everything in place
|
2013-11-11 20:17:38 -05:00
|
|
|
if err := Untar(layer, dest, nil); err != nil {
|
2013-11-01 01:06:35 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-11-19 03:35:03 -05:00
|
|
|
modifiedDirs := make(map[string]*syscall.Stat_t)
|
|
|
|
addDir := func(file string) {
|
|
|
|
d := filepath.Dir(file)
|
|
|
|
if _, exists := modifiedDirs[d]; !exists {
|
|
|
|
if s, err := os.Lstat(d); err == nil {
|
|
|
|
if sys := s.Sys(); sys != nil {
|
|
|
|
if stat, ok := sys.(*syscall.Stat_t); ok {
|
|
|
|
modifiedDirs[d] = stat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-01 01:06:35 -04:00
|
|
|
// Step 2: walk for whiteouts and apply them, removing them in the process
|
|
|
|
err := filepath.Walk(dest, func(fullPath string, f os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
2013-11-11 11:39:19 -05:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
// This happens in the case of whiteouts in parent dir removing a directory
|
|
|
|
// We just ignore it
|
|
|
|
return filepath.SkipDir
|
|
|
|
}
|
2013-11-01 01:06:35 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rebase path
|
|
|
|
path, err := filepath.Rel(dest, fullPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
path = filepath.Join("/", path)
|
|
|
|
|
|
|
|
// Skip AUFS metadata
|
|
|
|
if matched, err := filepath.Match("/.wh..wh.*", path); err != nil {
|
|
|
|
return err
|
|
|
|
} else if matched {
|
2013-11-19 03:35:03 -05:00
|
|
|
addDir(fullPath)
|
|
|
|
if err := os.RemoveAll(fullPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-11-01 01:06:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
filename := filepath.Base(path)
|
|
|
|
if strings.HasPrefix(filename, ".wh.") {
|
|
|
|
rmTargetName := filename[len(".wh."):]
|
|
|
|
rmTargetPath := filepath.Join(filepath.Dir(fullPath), rmTargetName)
|
2013-11-19 03:35:03 -05:00
|
|
|
|
2013-11-01 01:06:35 -04:00
|
|
|
// Remove the file targeted by the whiteout
|
2013-11-19 03:35:03 -05:00
|
|
|
addDir(rmTargetPath)
|
|
|
|
if err := os.RemoveAll(rmTargetPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-11-01 01:06:35 -04:00
|
|
|
// Remove the whiteout itself
|
2013-11-19 03:35:03 -05:00
|
|
|
addDir(fullPath)
|
|
|
|
if err := os.RemoveAll(fullPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-11-01 01:06:35 -04:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-11-19 03:35:03 -05:00
|
|
|
|
|
|
|
for k, v := range modifiedDirs {
|
|
|
|
aTime := time.Unix(v.Atim.Unix())
|
|
|
|
mTime := time.Unix(v.Mtim.Unix())
|
|
|
|
|
|
|
|
if err := os.Chtimes(k, aTime, mTime); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-01 01:06:35 -04:00
|
|
|
return nil
|
|
|
|
}
|