github.com/titanous/docker@v1.4.1/daemon/graphdriver/fsdiff.go (about)

     1  // +build daemon
     2  
     3  package graphdriver
     4  
     5  import (
     6  	"fmt"
     7  	"time"
     8  
     9  	log "github.com/Sirupsen/logrus"
    10  	"github.com/docker/docker/pkg/archive"
    11  	"github.com/docker/docker/pkg/chrootarchive"
    12  	"github.com/docker/docker/pkg/ioutils"
    13  	"github.com/docker/docker/utils"
    14  )
    15  
    16  // naiveDiffDriver takes a ProtoDriver and adds the
    17  // capability of the Diffing methods which it may or may not
    18  // support on its own. See the comment on the exported
    19  // NaiveDiffDriver function below.
    20  // Notably, the AUFS driver doesn't need to be wrapped like this.
    21  type naiveDiffDriver struct {
    22  	ProtoDriver
    23  }
    24  
    25  // NaiveDiffDriver returns a fully functional driver that wraps the
    26  // given ProtoDriver and adds the capability of the following methods which
    27  // it may or may not support on its own:
    28  //     Diff(id, parent string) (archive.Archive, error)
    29  //     Changes(id, parent string) ([]archive.Change, error)
    30  //     ApplyDiff(id, parent string, diff archive.ArchiveReader) (bytes int64, err error)
    31  //     DiffSize(id, parent string) (bytes int64, err error)
    32  func NaiveDiffDriver(driver ProtoDriver) Driver {
    33  	return &naiveDiffDriver{ProtoDriver: driver}
    34  }
    35  
    36  // Diff produces an archive of the changes between the specified
    37  // layer and its parent layer which may be "".
    38  func (gdw *naiveDiffDriver) Diff(id, parent string) (arch archive.Archive, err error) {
    39  	driver := gdw.ProtoDriver
    40  
    41  	layerFs, err := driver.Get(id, "")
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	defer func() {
    47  		if err != nil {
    48  			driver.Put(id)
    49  		}
    50  	}()
    51  
    52  	if parent == "" {
    53  		archive, err := archive.Tar(layerFs, archive.Uncompressed)
    54  		if err != nil {
    55  			return nil, err
    56  		}
    57  		return ioutils.NewReadCloserWrapper(archive, func() error {
    58  			err := archive.Close()
    59  			driver.Put(id)
    60  			return err
    61  		}), nil
    62  	}
    63  
    64  	parentFs, err := driver.Get(parent, "")
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	defer driver.Put(parent)
    69  
    70  	changes, err := archive.ChangesDirs(layerFs, parentFs)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	archive, err := archive.ExportChanges(layerFs, changes)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	return ioutils.NewReadCloserWrapper(archive, func() error {
    81  		err := archive.Close()
    82  		driver.Put(id)
    83  		return err
    84  	}), nil
    85  }
    86  
    87  // Changes produces a list of changes between the specified layer
    88  // and its parent layer. If parent is "", then all changes will be ADD changes.
    89  func (gdw *naiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) {
    90  	driver := gdw.ProtoDriver
    91  
    92  	layerFs, err := driver.Get(id, "")
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	defer driver.Put(id)
    97  
    98  	parentFs := ""
    99  
   100  	if parent != "" {
   101  		parentFs, err = driver.Get(parent, "")
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  		defer driver.Put(parent)
   106  	}
   107  
   108  	return archive.ChangesDirs(layerFs, parentFs)
   109  }
   110  
   111  // ApplyDiff extracts the changeset from the given diff into the
   112  // layer with the specified id and parent, returning the size of the
   113  // new layer in bytes.
   114  func (gdw *naiveDiffDriver) ApplyDiff(id, parent string, diff archive.ArchiveReader) (bytes int64, err error) {
   115  	driver := gdw.ProtoDriver
   116  
   117  	// Mount the root filesystem so we can apply the diff/layer.
   118  	layerFs, err := driver.Get(id, "")
   119  	if err != nil {
   120  		return
   121  	}
   122  	defer driver.Put(id)
   123  
   124  	start := time.Now().UTC()
   125  	log.Debugf("Start untar layer")
   126  	if err = chrootarchive.ApplyLayer(layerFs, diff); err != nil {
   127  		return
   128  	}
   129  	log.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
   130  
   131  	if parent == "" {
   132  		return utils.TreeSize(layerFs)
   133  	}
   134  
   135  	parentFs, err := driver.Get(parent, "")
   136  	if err != nil {
   137  		err = fmt.Errorf("Driver %s failed to get image parent %s: %s", driver, parent, err)
   138  		return
   139  	}
   140  	defer driver.Put(parent)
   141  
   142  	changes, err := archive.ChangesDirs(layerFs, parentFs)
   143  	if err != nil {
   144  		return
   145  	}
   146  
   147  	return archive.ChangesSize(layerFs, changes), nil
   148  }
   149  
   150  // DiffSize calculates the changes between the specified layer
   151  // and its parent and returns the size in bytes of the changes
   152  // relative to its base filesystem directory.
   153  func (gdw *naiveDiffDriver) DiffSize(id, parent string) (bytes int64, err error) {
   154  	driver := gdw.ProtoDriver
   155  
   156  	changes, err := gdw.Changes(id, parent)
   157  	if err != nil {
   158  		return
   159  	}
   160  
   161  	layerFs, err := driver.Get(id, "")
   162  	if err != nil {
   163  		return
   164  	}
   165  	defer driver.Put(id)
   166  
   167  	return archive.ChangesSize(layerFs, changes), nil
   168  }