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:
parent
8e7cbbff50
commit
1c5dc26a7c
3 changed files with 142 additions and 7 deletions
104
changes.go
104
changes.go
|
@ -5,6 +5,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type ChangeType int
|
||||
|
@ -33,7 +34,7 @@ func (change *Change) String() string {
|
|||
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
|
||||
err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1162,11 +1162,15 @@ func (container *Container) Mount() error {
|
|||
}
|
||||
|
||||
func (container *Container) Changes() ([]Change, error) {
|
||||
if err := container.EnsureMounted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
image, err := container.GetImage()
|
||||
if err != nil {
|
||||
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) {
|
||||
|
|
39
image.go
39
image.go
|
@ -486,12 +486,41 @@ func (image *Image) Unmount(runtime *Runtime, root string, id string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (image *Image) Changes(rw string) ([]Change, error) {
|
||||
layers, err := image.layers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (image *Image) Changes(runtime *Runtime, root, rw, id string) ([]Change, error) {
|
||||
switch runtime.GetMountMethod() {
|
||||
case MountMethodAUFS:
|
||||
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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue