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