github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/graphdriver/fsdiff.go (about)

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