github.com/portworx/docker@v1.12.1/daemon/graphdriver/fsdiff.go (about)

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