github.com/thy00/storage@v1.12.8/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 }