1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Implement docker diff for device-mapper

To do diffing we just compare file metadata, so this relies
on things like size and mtime/ctime to catch any changes.
Its *possible* to trick this by updating a file without
changing the size and setting back the mtime/ctime, but
that seems pretty unlikely to happen in reality, and lets
us avoid comparing the actual file data.
This commit is contained in:
Alexander Larsson 2013-09-05 18:03:49 +02:00 committed by Victor Vieux
parent 8f343ea65a
commit 8e8ef7cb5b
3 changed files with 142 additions and 7 deletions

View file

@ -5,6 +5,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
) )
type ChangeType int type ChangeType int
@ -33,7 +34,7 @@ func (change *Change) String() string {
return fmt.Sprintf("%s %s", kind, change.Path) return fmt.Sprintf("%s %s", kind, change.Path)
} }
func Changes(layers []string, rw string) ([]Change, error) { func ChangesAUFS(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 {
if err != nil { if err != nil {
@ -104,3 +105,104 @@ func Changes(layers []string, rw string) ([]Change, error) {
} }
return changes, nil 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
}

View file

@ -1161,11 +1161,15 @@ func (container *Container) Mount() error {
} }
func (container *Container) Changes() ([]Change, error) { func (container *Container) Changes() ([]Change, error) {
if err := container.EnsureMounted(); err != nil {
return nil, err
}
image, err := container.GetImage() image, err := container.GetImage()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return image.Changes(container.rwPath()) return image.Changes(container.runtime, container.RootfsPath(), container.rwPath(), container.ID)
} }
func (container *Container) GetImage() (*Image, error) { func (container *Container) GetImage() (*Image, error) {

View file

@ -486,12 +486,41 @@ func (image *Image) Unmount(runtime *Runtime, root string, id string) error {
return nil return nil
} }
func (image *Image) Changes(rw string) ([]Change, error) { func (image *Image) Changes(runtime *Runtime, root, rw, id string) ([]Change, error) {
layers, err := image.layers() switch runtime.GetMountMethod() {
if err != nil { case MountMethodAUFS:
return nil, err layers, err := image.layers()
if err != nil {
return nil, err
}
return ChangesAUFS(layers, rw)
case MountMethodDeviceMapper:
devices, err := runtime.GetDeviceSet()
if err != nil {
return nil, err
}
if err := os.Mkdir(rw, 0755); err != nil && !os.IsExist(err) {
return nil, err
}
// We re-use rw for the temporary mount of the base image as its
// not used by device-mapper otherwise
err = devices.MountDevice(image.ID, rw)
if err != nil {
return nil, err
}
changes, err := ChangesDirs(root, rw)
_ = syscall.Unmount(rw, 0)
if err != nil {
return nil, err
}
return changes, nil
} }
return Changes(layers, rw)
return nil, fmt.Errorf("No supported Changes implementation")
} }
func (image *Image) ShortID() string { func (image *Image) ShortID() string {