github.com/rumpl/bof@v23.0.0-rc.2+incompatible/daemon/graphdriver/fsdiff.go (about)

     1  package graphdriver // import "github.com/docker/docker/daemon/graphdriver"
     2  
     3  import (
     4  	"io"
     5  	"time"
     6  
     7  	"github.com/docker/docker/pkg/archive"
     8  	"github.com/docker/docker/pkg/chrootarchive"
     9  	"github.com/docker/docker/pkg/idtools"
    10  	"github.com/docker/docker/pkg/ioutils"
    11  	"github.com/sirupsen/logrus"
    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 on the local file system,
    22  // which it may or may not support on its own. See the comment
    23  // on the exported NewNaiveDiffDriver function below.
    24  // Notably, the AUFS driver doesn't need to be wrapped like this.
    25  type NaiveDiffDriver struct {
    26  	ProtoDriver
    27  	idMap idtools.IdentityMapping
    28  }
    29  
    30  // NewNaiveDiffDriver returns a fully functional driver that wraps the
    31  // given ProtoDriver and adds the capability of the following methods which
    32  // it may or may not support on its own:
    33  //
    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, idMap idtools.IdentityMapping) Driver {
    39  	return &NaiveDiffDriver{ProtoDriver: driver,
    40  		idMap: idMap}
    41  }
    42  
    43  // Diff produces an archive of the changes between the specified
    44  // layer and its parent layer which may be "".
    45  func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err error) {
    46  	startTime := time.Now()
    47  	driver := gdw.ProtoDriver
    48  
    49  	layerRootFs, err := driver.Get(id, "")
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	layerFs := layerRootFs.Path()
    54  
    55  	defer func() {
    56  		if err != nil {
    57  			driver.Put(id)
    58  		}
    59  	}()
    60  
    61  	if parent == "" {
    62  		archive, err := archive.Tar(layerFs, archive.Uncompressed)
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  		return ioutils.NewReadCloserWrapper(archive, func() error {
    67  			err := archive.Close()
    68  			driver.Put(id)
    69  			return err
    70  		}), nil
    71  	}
    72  
    73  	parentRootFs, err := driver.Get(parent, "")
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	defer driver.Put(parent)
    78  
    79  	parentFs := parentRootFs.Path()
    80  
    81  	changes, err := archive.ChangesDirs(layerFs, parentFs)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	archive, err := archive.ExportChanges(layerFs, changes, gdw.idMap)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	return ioutils.NewReadCloserWrapper(archive, func() error {
    92  		err := archive.Close()
    93  		driver.Put(id)
    94  
    95  		// NaiveDiffDriver compares file metadata with parent layers. Parent layers
    96  		// are extracted from tar's with full second precision on modified time.
    97  		// We need this hack here to make sure calls within same second receive
    98  		// correct result.
    99  		time.Sleep(time.Until(startTime.Truncate(time.Second).Add(time.Second)))
   100  		return err
   101  	}), nil
   102  }
   103  
   104  // Changes produces a list of changes between the specified layer
   105  // and its parent layer. If parent is "", then all changes will be ADD changes.
   106  func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) {
   107  	driver := gdw.ProtoDriver
   108  
   109  	layerRootFs, err := driver.Get(id, "")
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	defer driver.Put(id)
   114  
   115  	layerFs := layerRootFs.Path()
   116  	parentFs := ""
   117  
   118  	if parent != "" {
   119  		parentRootFs, err := driver.Get(parent, "")
   120  		if err != nil {
   121  			return nil, err
   122  		}
   123  		defer driver.Put(parent)
   124  		parentFs = parentRootFs.Path()
   125  	}
   126  
   127  	return archive.ChangesDirs(layerFs, parentFs)
   128  }
   129  
   130  // ApplyDiff extracts the changeset from the given diff into the
   131  // layer with the specified id and parent, returning the size of the
   132  // new layer in bytes.
   133  func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff io.Reader) (size int64, err error) {
   134  	driver := gdw.ProtoDriver
   135  
   136  	// Mount the root filesystem so we can apply the diff/layer.
   137  	layerRootFs, err := driver.Get(id, "")
   138  	if err != nil {
   139  		return
   140  	}
   141  	defer driver.Put(id)
   142  
   143  	layerFs := layerRootFs.Path()
   144  	options := &archive.TarOptions{IDMap: gdw.idMap}
   145  	start := time.Now().UTC()
   146  	logrus.WithField("id", id).Debug("Start untar layer")
   147  	if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil {
   148  		return
   149  	}
   150  	logrus.WithField("id", id).Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
   151  
   152  	return
   153  }
   154  
   155  // DiffSize calculates the changes between the specified layer
   156  // and its parent and returns the size in bytes of the changes
   157  // relative to its base filesystem directory.
   158  func (gdw *NaiveDiffDriver) DiffSize(id, parent string) (size int64, err error) {
   159  	driver := gdw.ProtoDriver
   160  
   161  	changes, err := gdw.Changes(id, parent)
   162  	if err != nil {
   163  		return
   164  	}
   165  
   166  	layerFs, err := driver.Get(id, "")
   167  	if err != nil {
   168  		return
   169  	}
   170  	defer driver.Put(id)
   171  
   172  	return archive.ChangesSize(layerFs.Path(), changes), nil
   173  }