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 }