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