diff --git a/changes.go b/changes.go index 43573cd606..00c9cc7c77 100644 --- a/changes.go +++ b/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 +} diff --git a/container.go b/container.go index 9b4412c6ba..33b410ee5a 100644 --- a/container.go +++ b/container.go @@ -1161,11 +1161,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) { diff --git a/image.go b/image.go index 9d9a64cceb..119e07c70e 100644 --- a/image.go +++ b/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 {