func (d *Driver) ApplyDiff(id string, parent string, diff archive.ArchiveReader) (size int64, err error) { dir := d.dir(id) if parent == "" { return 0, ErrApplyDiffFallback } parentRootDir := path.Join(d.dir(parent), "root") if _, err := os.Stat(parentRootDir); err != nil { return 0, ErrApplyDiffFallback } // We now know there is a parent, and it has a "root" directory containing // the full root filesystem. We can just hardlink it and apply the // layer. This relies on two things: // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) // These are all currently true and are not expected to break tmpRootDir, err := ioutil.TempDir(dir, "tmproot") if err != nil { return 0, err } defer func() { if err != nil { os.RemoveAll(tmpRootDir) } else { os.RemoveAll(path.Join(dir, "upper")) os.RemoveAll(path.Join(dir, "work")) os.RemoveAll(path.Join(dir, "merged")) os.RemoveAll(path.Join(dir, "lower-id")) } }() if err = copyDir(parentRootDir, tmpRootDir, CopyHardlink); err != nil { return 0, err } if size, err = chrootarchive.ApplyLayer(tmpRootDir, diff); err != nil { return 0, err } rootDir := path.Join(dir, "root") if err := os.Rename(tmpRootDir, rootDir); err != nil { return 0, err } return }
// ApplyDiff extracts the changeset from the given diff into the // layer with the specified id and parent, returning the size of the // new layer in bytes. func (gdw *naiveDiffDriver) ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error) { driver := gdw.ProtoDriver // Mount the root filesystem so we can apply the diff/layer. layerFs, err := driver.Get(id, "") if err != nil { return } defer driver.Put(id) start := time.Now().UTC() log.Debugf("Start untar layer") if size, err = chrootarchive.ApplyLayer(layerFs, diff); err != nil { return } log.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) return }