Selaa lähdekoodia

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.
Alexander Larsson 12 vuotta sitten
vanhempi
commit
1c5dc26a7c
3 muutettua tiedostoa jossa 142 lisäystä ja 7 poistoa
  1. 103 1
      changes.go
  2. 5 1
      container.go
  3. 34 5
      image.go

+ 103 - 1
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
+}

+ 5 - 1
container.go

@@ -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) {

+ 34 - 5
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 {