gopkg.in/dotcloud/docker.v1@v1.13.1/daemon/graphdriver/fsdiff.go (about) 1 package graphdriver 2 3 import ( 4 "io" 5 "time" 6 7 "github.com/Sirupsen/logrus" 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 io.ReadCloser, err error) { 47 startTime := time.Now() 48 driver := gdw.ProtoDriver 49 50 layerFs, err := driver.Get(id, "") 51 if err != nil { 52 return nil, err 53 } 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 parentFs, err := driver.Get(parent, "") 74 if err != nil { 75 return nil, err 76 } 77 defer driver.Put(parent) 78 79 changes, err := archive.ChangesDirs(layerFs, parentFs) 80 if err != nil { 81 return nil, err 82 } 83 84 archive, err := archive.ExportChanges(layerFs, changes, gdw.uidMaps, gdw.gidMaps) 85 if err != nil { 86 return nil, err 87 } 88 89 return ioutils.NewReadCloserWrapper(archive, func() error { 90 err := archive.Close() 91 driver.Put(id) 92 93 // NaiveDiffDriver compares file metadata with parent layers. Parent layers 94 // are extracted from tar's with full second precision on modified time. 95 // We need this hack here to make sure calls within same second receive 96 // correct result. 97 time.Sleep(startTime.Truncate(time.Second).Add(time.Second).Sub(time.Now())) 98 return err 99 }), nil 100 } 101 102 // Changes produces a list of changes between the specified layer 103 // and its parent layer. If parent is "", then all changes will be ADD changes. 104 func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) { 105 driver := gdw.ProtoDriver 106 107 layerFs, err := driver.Get(id, "") 108 if err != nil { 109 return nil, err 110 } 111 defer driver.Put(id) 112 113 parentFs := "" 114 115 if parent != "" { 116 parentFs, err = driver.Get(parent, "") 117 if err != nil { 118 return nil, err 119 } 120 defer driver.Put(parent) 121 } 122 123 return archive.ChangesDirs(layerFs, parentFs) 124 } 125 126 // ApplyDiff extracts the changeset from the given diff into the 127 // layer with the specified id and parent, returning the size of the 128 // new layer in bytes. 129 func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff io.Reader) (size int64, err error) { 130 driver := gdw.ProtoDriver 131 132 // Mount the root filesystem so we can apply the diff/layer. 133 layerFs, err := driver.Get(id, "") 134 if err != nil { 135 return 136 } 137 defer driver.Put(id) 138 139 options := &archive.TarOptions{UIDMaps: gdw.uidMaps, 140 GIDMaps: gdw.gidMaps} 141 start := time.Now().UTC() 142 logrus.Debug("Start untar layer") 143 if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil { 144 return 145 } 146 logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) 147 148 return 149 } 150 151 // DiffSize calculates the changes between the specified layer 152 // and its parent and returns the size in bytes of the changes 153 // relative to its base filesystem directory. 154 func (gdw *NaiveDiffDriver) DiffSize(id, parent string) (size int64, err error) { 155 driver := gdw.ProtoDriver 156 157 changes, err := gdw.Changes(id, parent) 158 if err != nil { 159 return 160 } 161 162 layerFs, err := driver.Get(id, "") 163 if err != nil { 164 return 165 } 166 defer driver.Put(id) 167 168 return archive.ChangesSize(layerFs, changes), nil 169 }