github.com/rawahars/moby@v24.0.4+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  type NaiveDiffDriver struct {
    25  	ProtoDriver
    26  	idMap idtools.IdentityMapping
    27  }
    28  
    29  // NewNaiveDiffDriver returns a fully functional driver that wraps the
    30  // given ProtoDriver and adds the capability of the following methods which
    31  // it may or may not support on its own:
    32  //
    33  //	Diff(id, parent string) (archive.Archive, error)
    34  //	Changes(id, parent string) ([]archive.Change, error)
    35  //	ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error)
    36  //	DiffSize(id, parent string) (size int64, err error)
    37  func NewNaiveDiffDriver(driver ProtoDriver, idMap idtools.IdentityMapping) Driver {
    38  	return &NaiveDiffDriver{ProtoDriver: driver,
    39  		idMap: idMap}
    40  }
    41  
    42  // Diff produces an archive of the changes between the specified
    43  // layer and its parent layer which may be "".
    44  func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err error) {
    45  	startTime := time.Now()
    46  	driver := gdw.ProtoDriver
    47  
    48  	layerRootFs, err := driver.Get(id, "")
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	layerFs := layerRootFs
    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.idMap)
    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  
    92  		// NaiveDiffDriver compares file metadata with parent layers. Parent layers
    93  		// are extracted from tar's with full second precision on modified time.
    94  		// We need this hack here to make sure calls within same second receive
    95  		// correct result.
    96  		time.Sleep(time.Until(startTime.Truncate(time.Second).Add(time.Second)))
    97  		return err
    98  	}), nil
    99  }
   100  
   101  // Changes produces a list of changes between the specified layer
   102  // and its parent layer. If parent is "", then all changes will be ADD changes.
   103  func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) {
   104  	driver := gdw.ProtoDriver
   105  
   106  	layerFs, err := driver.Get(id, "")
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	defer driver.Put(id)
   111  
   112  	parentFs := ""
   113  
   114  	if parent != "" {
   115  		parentFs, err = driver.Get(parent, "")
   116  		if err != nil {
   117  			return nil, err
   118  		}
   119  		defer driver.Put(parent)
   120  	}
   121  
   122  	return archive.ChangesDirs(layerFs, parentFs)
   123  }
   124  
   125  // ApplyDiff extracts the changeset from the given diff into the
   126  // layer with the specified id and parent, returning the size of the
   127  // new layer in bytes.
   128  func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff io.Reader) (size int64, err error) {
   129  	driver := gdw.ProtoDriver
   130  
   131  	// Mount the root filesystem so we can apply the diff/layer.
   132  	layerRootFs, err := driver.Get(id, "")
   133  	if err != nil {
   134  		return
   135  	}
   136  	defer driver.Put(id)
   137  
   138  	layerFs := layerRootFs
   139  	options := &archive.TarOptions{IDMap: gdw.idMap}
   140  	start := time.Now().UTC()
   141  	logrus.WithField("id", id).Debug("Start untar layer")
   142  	if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil {
   143  		return
   144  	}
   145  	logrus.WithField("id", id).Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
   146  
   147  	return
   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) (size 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  }