github.com/sshnaidm/storage@v1.12.13/drivers/fsdiff.go (about)

     1  package graphdriver
     2  
     3  import (
     4  	"io"
     5  	"time"
     6  
     7  	"github.com/containers/storage/pkg/archive"
     8  	"github.com/containers/storage/pkg/chrootarchive"
     9  	"github.com/containers/storage/pkg/idtools"
    10  	"github.com/containers/storage/pkg/ioutils"
    11  	rsystem "github.com/opencontainers/runc/libcontainer/system"
    12  	"github.com/sirupsen/logrus"
    13  )
    14  
    15  var (
    16  	// ApplyUncompressedLayer defines the unpack method used by the graph
    17  	// driver.
    18  	ApplyUncompressedLayer = chrootarchive.ApplyUncompressedLayer
    19  )
    20  
    21  // NaiveDiffDriver takes a ProtoDriver and adds the
    22  // capability of the Diffing methods which it may or may not
    23  // support on its own. See the comment on the exported
    24  // NewNaiveDiffDriver function below.
    25  // Notably, the AUFS driver doesn't need to be wrapped like this.
    26  type NaiveDiffDriver struct {
    27  	ProtoDriver
    28  	LayerIDMapUpdater
    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 string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error)
    35  //     Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error)
    36  //     ApplyDiff(id string, idMappings *idtools.IDMappings, parent, mountLabel string, diff io.Reader) (size int64, err error)
    37  //     DiffSize(id string, idMappings *idtools.IDMappings, parent, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error)
    38  func NewNaiveDiffDriver(driver ProtoDriver, updater LayerIDMapUpdater) Driver {
    39  	return &NaiveDiffDriver{ProtoDriver: driver, LayerIDMapUpdater: updater}
    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 string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (arch io.ReadCloser, err error) {
    45  	startTime := time.Now()
    46  	driver := gdw.ProtoDriver
    47  
    48  	if idMappings == nil {
    49  		idMappings = &idtools.IDMappings{}
    50  	}
    51  	if parentMappings == nil {
    52  		parentMappings = &idtools.IDMappings{}
    53  	}
    54  
    55  	options := MountOpts{
    56  		MountLabel: mountLabel,
    57  	}
    58  	layerFs, err := driver.Get(id, options)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	defer func() {
    64  		if err != nil {
    65  			driver.Put(id)
    66  		}
    67  	}()
    68  
    69  	if parent == "" {
    70  		archive, err := archive.TarWithOptions(layerFs, &archive.TarOptions{
    71  			Compression: archive.Uncompressed,
    72  			UIDMaps:     idMappings.UIDs(),
    73  			GIDMaps:     idMappings.GIDs(),
    74  		})
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  		return ioutils.NewReadCloserWrapper(archive, func() error {
    79  			err := archive.Close()
    80  			driver.Put(id)
    81  			return err
    82  		}), nil
    83  	}
    84  
    85  	parentFs, err := driver.Get(parent, options)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	defer driver.Put(parent)
    90  
    91  	changes, err := archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	archive, err := archive.ExportChanges(layerFs, changes, idMappings.UIDs(), idMappings.GIDs())
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	return ioutils.NewReadCloserWrapper(archive, func() error {
   102  		err := archive.Close()
   103  		driver.Put(id)
   104  
   105  		// NaiveDiffDriver compares file metadata with parent layers. Parent layers
   106  		// are extracted from tar's with full second precision on modified time.
   107  		// We need this hack here to make sure calls within same second receive
   108  		// correct result.
   109  		time.Sleep(startTime.Truncate(time.Second).Add(time.Second).Sub(time.Now()))
   110  		return err
   111  	}), nil
   112  }
   113  
   114  // Changes produces a list of changes between the specified layer
   115  // and its parent layer. If parent is "", then all changes will be ADD changes.
   116  func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) {
   117  	driver := gdw.ProtoDriver
   118  
   119  	if idMappings == nil {
   120  		idMappings = &idtools.IDMappings{}
   121  	}
   122  	if parentMappings == nil {
   123  		parentMappings = &idtools.IDMappings{}
   124  	}
   125  
   126  	options := MountOpts{
   127  		MountLabel: mountLabel,
   128  	}
   129  	layerFs, err := driver.Get(id, options)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	defer driver.Put(id)
   134  
   135  	parentFs := ""
   136  
   137  	if parent != "" {
   138  		options := MountOpts{
   139  			MountLabel: mountLabel,
   140  		}
   141  		parentFs, err = driver.Get(parent, options)
   142  		if err != nil {
   143  			return nil, err
   144  		}
   145  		defer driver.Put(parent)
   146  	}
   147  
   148  	return archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings)
   149  }
   150  
   151  // ApplyDiff extracts the changeset from the given diff into the
   152  // layer with the specified id and parent, returning the size of the
   153  // new layer in bytes.
   154  func (gdw *NaiveDiffDriver) ApplyDiff(id string, applyMappings *idtools.IDMappings, parent, mountLabel string, diff io.Reader) (size int64, err error) {
   155  	driver := gdw.ProtoDriver
   156  
   157  	if applyMappings == nil {
   158  		applyMappings = &idtools.IDMappings{}
   159  	}
   160  
   161  	// Mount the root filesystem so we can apply the diff/layer.
   162  	mountOpts := MountOpts{
   163  		MountLabel: mountLabel,
   164  	}
   165  	layerFs, err := driver.Get(id, mountOpts)
   166  	if err != nil {
   167  		return
   168  	}
   169  	defer driver.Put(id)
   170  
   171  	options := &archive.TarOptions{
   172  		InUserNS: rsystem.RunningInUserNS(),
   173  	}
   174  	if applyMappings != nil {
   175  		options.UIDMaps = applyMappings.UIDs()
   176  		options.GIDMaps = applyMappings.GIDs()
   177  	}
   178  	start := time.Now().UTC()
   179  	logrus.Debug("Start untar layer")
   180  	if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil {
   181  		logrus.Errorf("Error while applying layer: %s", err)
   182  		return
   183  	}
   184  	logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
   185  
   186  	return
   187  }
   188  
   189  // DiffSize calculates the changes between the specified layer
   190  // and its parent and returns the size in bytes of the changes
   191  // relative to its base filesystem directory.
   192  func (gdw *NaiveDiffDriver) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) {
   193  	driver := gdw.ProtoDriver
   194  
   195  	if idMappings == nil {
   196  		idMappings = &idtools.IDMappings{}
   197  	}
   198  	if parentMappings == nil {
   199  		parentMappings = &idtools.IDMappings{}
   200  	}
   201  
   202  	changes, err := gdw.Changes(id, idMappings, parent, parentMappings, mountLabel)
   203  	if err != nil {
   204  		return
   205  	}
   206  
   207  	options := MountOpts{
   208  		MountLabel: mountLabel,
   209  	}
   210  	layerFs, err := driver.Get(id, options)
   211  	if err != nil {
   212  		return
   213  	}
   214  	defer driver.Put(id)
   215  
   216  	return archive.ChangesSize(layerFs, changes), nil
   217  }