github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/daemon/graphdriver/fsdiff.go (about) 1 package graphdriver // import "github.com/demonoid81/moby/daemon/graphdriver" 2 3 import ( 4 "io" 5 "time" 6 7 "github.com/demonoid81/moby/pkg/archive" 8 "github.com/demonoid81/moby/pkg/chrootarchive" 9 "github.com/demonoid81/moby/pkg/idtools" 10 "github.com/demonoid81/moby/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 uidMaps []idtools.IDMap 28 gidMaps []idtools.IDMap 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, 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, uidMaps, gidMaps []idtools.IDMap) Driver { 39 return &NaiveDiffDriver{ProtoDriver: driver, 40 uidMaps: uidMaps, 41 gidMaps: gidMaps} 42 } 43 44 // Diff produces an archive of the changes between the specified 45 // layer and its parent layer which may be "". 46 func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err error) { 47 startTime := time.Now() 48 driver := gdw.ProtoDriver 49 50 layerRootFs, err := driver.Get(id, "") 51 if err != nil { 52 return nil, err 53 } 54 layerFs := layerRootFs.Path() 55 56 defer func() { 57 if err != nil { 58 driver.Put(id) 59 } 60 }() 61 62 if parent == "" { 63 archive, err := archive.Tar(layerFs, archive.Uncompressed) 64 if err != nil { 65 return nil, err 66 } 67 return ioutils.NewReadCloserWrapper(archive, func() error { 68 err := archive.Close() 69 driver.Put(id) 70 return err 71 }), nil 72 } 73 74 parentRootFs, err := driver.Get(parent, "") 75 if err != nil { 76 return nil, err 77 } 78 defer driver.Put(parent) 79 80 parentFs := parentRootFs.Path() 81 82 changes, err := archive.ChangesDirs(layerFs, parentFs) 83 if err != nil { 84 return nil, err 85 } 86 87 archive, err := archive.ExportChanges(layerFs, changes, gdw.uidMaps, gdw.gidMaps) 88 if err != nil { 89 return nil, err 90 } 91 92 return ioutils.NewReadCloserWrapper(archive, func() error { 93 err := archive.Close() 94 driver.Put(id) 95 96 // NaiveDiffDriver compares file metadata with parent layers. Parent layers 97 // are extracted from tar's with full second precision on modified time. 98 // We need this hack here to make sure calls within same second receive 99 // correct result. 100 time.Sleep(time.Until(startTime.Truncate(time.Second).Add(time.Second))) 101 return err 102 }), nil 103 } 104 105 // Changes produces a list of changes between the specified layer 106 // and its parent layer. If parent is "", then all changes will be ADD changes. 107 func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) { 108 driver := gdw.ProtoDriver 109 110 layerRootFs, err := driver.Get(id, "") 111 if err != nil { 112 return nil, err 113 } 114 defer driver.Put(id) 115 116 layerFs := layerRootFs.Path() 117 parentFs := "" 118 119 if parent != "" { 120 parentRootFs, err := driver.Get(parent, "") 121 if err != nil { 122 return nil, err 123 } 124 defer driver.Put(parent) 125 parentFs = parentRootFs.Path() 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.Path() 145 options := &archive.TarOptions{UIDMaps: gdw.uidMaps, 146 GIDMaps: gdw.gidMaps} 147 start := time.Now().UTC() 148 logrus.WithField("id", id).Debug("Start untar layer") 149 if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil { 150 return 151 } 152 logrus.WithField("id", id).Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) 153 154 return 155 } 156 157 // DiffSize calculates the changes between the specified layer 158 // and its parent and returns the size in bytes of the changes 159 // relative to its base filesystem directory. 160 func (gdw *NaiveDiffDriver) DiffSize(id, parent string) (size int64, err error) { 161 driver := gdw.ProtoDriver 162 163 changes, err := gdw.Changes(id, parent) 164 if err != nil { 165 return 166 } 167 168 layerFs, err := driver.Get(id, "") 169 if err != nil { 170 return 171 } 172 defer driver.Put(id) 173 174 return archive.ChangesSize(layerFs.Path(), changes), nil 175 }