github.com/portworx/docker@v1.12.1/daemon/graphdriver/fsdiff.go (about) 1 package graphdriver 2 3 import ( 4 "time" 5 6 "github.com/Sirupsen/logrus" 7 8 "github.com/docker/docker/pkg/archive" 9 "github.com/docker/docker/pkg/chrootarchive" 10 "github.com/docker/docker/pkg/idtools" 11 "github.com/docker/docker/pkg/ioutils" 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 which it may or may not 22 // support on its own. See the comment on the exported 23 // 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 archive.Archive, err error) { 47 driver := gdw.ProtoDriver 48 49 layerFs, err := driver.Get(id, "") 50 if err != nil { 51 return nil, err 52 } 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.uidMaps, gdw.gidMaps) 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 return err 92 }), nil 93 } 94 95 // Changes produces a list of changes between the specified layer 96 // and its parent layer. If parent is "", then all changes will be ADD changes. 97 func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) { 98 driver := gdw.ProtoDriver 99 100 layerFs, err := driver.Get(id, "") 101 if err != nil { 102 return nil, err 103 } 104 defer driver.Put(id) 105 106 parentFs := "" 107 108 if parent != "" { 109 parentFs, err = driver.Get(parent, "") 110 if err != nil { 111 return nil, err 112 } 113 defer driver.Put(parent) 114 } 115 116 return archive.ChangesDirs(layerFs, parentFs) 117 } 118 119 // ApplyDiff extracts the changeset from the given diff into the 120 // layer with the specified id and parent, returning the size of the 121 // new layer in bytes. 122 func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) { 123 driver := gdw.ProtoDriver 124 125 // Mount the root filesystem so we can apply the diff/layer. 126 layerFs, err := driver.Get(id, "") 127 if err != nil { 128 return 129 } 130 defer driver.Put(id) 131 132 options := &archive.TarOptions{UIDMaps: gdw.uidMaps, 133 GIDMaps: gdw.gidMaps} 134 start := time.Now().UTC() 135 logrus.Debug("Start untar layer") 136 if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil { 137 return 138 } 139 logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) 140 141 return 142 } 143 144 // DiffSize calculates the changes between the specified layer 145 // and its parent and returns the size in bytes of the changes 146 // relative to its base filesystem directory. 147 func (gdw *NaiveDiffDriver) DiffSize(id, parent string) (size int64, err error) { 148 driver := gdw.ProtoDriver 149 150 changes, err := gdw.Changes(id, parent) 151 if err != nil { 152 return 153 } 154 155 layerFs, err := driver.Get(id, "") 156 if err != nil { 157 return 158 } 159 defer driver.Put(id) 160 161 return archive.ChangesSize(layerFs, changes), nil 162 }