github.com/openflowlabs/storage@v1.12.13/layers.go (about) 1 package storage 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "reflect" 12 "sort" 13 "time" 14 15 drivers "github.com/containers/storage/drivers" 16 "github.com/containers/storage/pkg/archive" 17 "github.com/containers/storage/pkg/idtools" 18 "github.com/containers/storage/pkg/ioutils" 19 "github.com/containers/storage/pkg/stringid" 20 "github.com/containers/storage/pkg/system" 21 "github.com/containers/storage/pkg/truncindex" 22 "github.com/klauspost/pgzip" 23 digest "github.com/opencontainers/go-digest" 24 "github.com/opencontainers/selinux/go-selinux/label" 25 "github.com/pkg/errors" 26 "github.com/vbatts/tar-split/tar/asm" 27 "github.com/vbatts/tar-split/tar/storage" 28 ) 29 30 const ( 31 tarSplitSuffix = ".tar-split.gz" 32 incompleteFlag = "incomplete" 33 ) 34 35 // A Layer is a record of a copy-on-write layer that's stored by the lower 36 // level graph driver. 37 type Layer struct { 38 // ID is either one which was specified at create-time, or a random 39 // value which was generated by the library. 40 ID string `json:"id"` 41 42 // Names is an optional set of user-defined convenience values. The 43 // layer can be referred to by its ID or any of its names. Names are 44 // unique among layers. 45 Names []string `json:"names,omitempty"` 46 47 // Parent is the ID of a layer from which this layer inherits data. 48 Parent string `json:"parent,omitempty"` 49 50 // Metadata is data we keep for the convenience of the caller. It is not 51 // expected to be large, since it is kept in memory. 52 Metadata string `json:"metadata,omitempty"` 53 54 // MountLabel is an SELinux label which should be used when attempting to mount 55 // the layer. 56 MountLabel string `json:"mountlabel,omitempty"` 57 58 // MountPoint is the path where the layer is mounted, or where it was most 59 // recently mounted. This can change between subsequent Unmount() and 60 // Mount() calls, so the caller should consult this value after Mount() 61 // succeeds to find the location of the container's root filesystem. 62 MountPoint string `json:"-"` 63 64 // MountCount is used as a reference count for the container's layer being 65 // mounted at the mount point. 66 MountCount int `json:"-"` 67 68 // Created is the datestamp for when this layer was created. Older 69 // versions of the library did not track this information, so callers 70 // will likely want to use the IsZero() method to verify that a value 71 // is set before using it. 72 Created time.Time `json:"created,omitempty"` 73 74 // CompressedDigest is the digest of the blob that was last passed to 75 // ApplyDiff() or Put(), as it was presented to us. 76 CompressedDigest digest.Digest `json:"compressed-diff-digest,omitempty"` 77 78 // CompressedSize is the length of the blob that was last passed to 79 // ApplyDiff() or Put(), as it was presented to us. If 80 // CompressedDigest is not set, this should be treated as if it were an 81 // uninitialized value. 82 CompressedSize int64 `json:"compressed-size,omitempty"` 83 84 // UncompressedDigest is the digest of the blob that was last passed to 85 // ApplyDiff() or Put(), after we decompressed it. Often referred to 86 // as a DiffID. 87 UncompressedDigest digest.Digest `json:"diff-digest,omitempty"` 88 89 // UncompressedSize is the length of the blob that was last passed to 90 // ApplyDiff() or Put(), after we decompressed it. If 91 // UncompressedDigest is not set, this should be treated as if it were 92 // an uninitialized value. 93 UncompressedSize int64 `json:"diff-size,omitempty"` 94 95 // CompressionType is the type of compression which we detected on the blob 96 // that was last passed to ApplyDiff() or Put(). 97 CompressionType archive.Compression `json:"compression,omitempty"` 98 99 // Flags is arbitrary data about the layer. 100 Flags map[string]interface{} `json:"flags,omitempty"` 101 102 // UIDMap and GIDMap are used for setting up a layer's contents 103 // for use inside of a user namespace where UID mapping is being used. 104 UIDMap []idtools.IDMap `json:"uidmap,omitempty"` 105 GIDMap []idtools.IDMap `json:"gidmap,omitempty"` 106 107 // ReadOnly is true if this layer resides in a read-only layer store. 108 ReadOnly bool `json:"-"` 109 } 110 111 type layerMountPoint struct { 112 ID string `json:"id"` 113 MountPoint string `json:"path"` 114 MountCount int `json:"count"` 115 } 116 117 // DiffOptions override the default behavior of Diff() methods. 118 type DiffOptions struct { 119 // Compression, if set overrides the default compressor when generating a diff. 120 Compression *archive.Compression 121 } 122 123 // ROLayerStore wraps a graph driver, adding the ability to refer to layers by 124 // name, and keeping track of parent-child relationships, along with a list of 125 // all known layers. 126 type ROLayerStore interface { 127 ROFileBasedStore 128 ROMetadataStore 129 130 // Exists checks if a layer with the specified name or ID is known. 131 Exists(id string) bool 132 133 // Get retrieves information about a layer given an ID or name. 134 Get(id string) (*Layer, error) 135 136 // Status returns an slice of key-value pairs, suitable for human consumption, 137 // relaying whatever status information the underlying driver can share. 138 Status() ([][2]string, error) 139 140 // Changes returns a slice of Change structures, which contain a pathname 141 // (Path) and a description of what sort of change (Kind) was made by the 142 // layer (either ChangeModify, ChangeAdd, or ChangeDelete), relative to a 143 // specified layer. By default, the layer's parent is used as a reference. 144 Changes(from, to string) ([]archive.Change, error) 145 146 // Diff produces a tarstream which can be applied to a layer with the contents 147 // of the first layer to produce a layer with the contents of the second layer. 148 // By default, the parent of the second layer is used as the first 149 // layer, so it need not be specified. Options can be used to override 150 // default behavior, but are also not required. 151 Diff(from, to string, options *DiffOptions) (io.ReadCloser, error) 152 153 // DiffSize produces an estimate of the length of the tarstream which would be 154 // produced by Diff. 155 DiffSize(from, to string) (int64, error) 156 157 // Size produces a cached value for the uncompressed size of the layer, 158 // if one is known, or -1 if it is not known. If the layer can not be 159 // found, it returns an error. 160 Size(name string) (int64, error) 161 162 // Lookup attempts to translate a name to an ID. Most methods do this 163 // implicitly. 164 Lookup(name string) (string, error) 165 166 // LayersByCompressedDigest returns a slice of the layers with the 167 // specified compressed digest value recorded for them. 168 LayersByCompressedDigest(d digest.Digest) ([]Layer, error) 169 170 // LayersByUncompressedDigest returns a slice of the layers with the 171 // specified uncompressed digest value recorded for them. 172 LayersByUncompressedDigest(d digest.Digest) ([]Layer, error) 173 174 // Layers returns a slice of the known layers. 175 Layers() ([]Layer, error) 176 } 177 178 // LayerStore wraps a graph driver, adding the ability to refer to layers by 179 // name, and keeping track of parent-child relationships, along with a list of 180 // all known layers. 181 type LayerStore interface { 182 ROLayerStore 183 RWFileBasedStore 184 RWMetadataStore 185 FlaggableStore 186 187 // Create creates a new layer, optionally giving it a specified ID rather than 188 // a randomly-generated one, either inheriting data from another specified 189 // layer or the empty base layer. The new layer can optionally be given names 190 // and have an SELinux label specified for use when mounting it. Some 191 // underlying drivers can accept a "size" option. At this time, most 192 // underlying drivers do not themselves distinguish between writeable 193 // and read-only layers. 194 Create(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool) (*Layer, error) 195 196 // CreateWithFlags combines the functions of Create and SetFlag. 197 CreateWithFlags(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}) (layer *Layer, err error) 198 199 // Put combines the functions of CreateWithFlags and ApplyDiff. 200 Put(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}, diff io.Reader) (*Layer, int64, error) 201 202 // SetNames replaces the list of names associated with a layer with the 203 // supplied values. 204 SetNames(id string, names []string) error 205 206 // Delete deletes a layer with the specified name or ID. 207 Delete(id string) error 208 209 // Wipe deletes all layers. 210 Wipe() error 211 212 // Mount mounts a layer for use. If the specified layer is the parent of other 213 // layers, it should not be written to. An SELinux label to be applied to the 214 // mount can be specified to override the one configured for the layer. 215 // The mappings used by the container can be specified. 216 Mount(id string, options drivers.MountOpts) (string, error) 217 218 // Unmount unmounts a layer when it is no longer in use. 219 Unmount(id string, force bool) (bool, error) 220 221 // Mounted returns number of times the layer has been mounted. 222 Mounted(id string) (int, error) 223 224 // ParentOwners returns the UIDs and GIDs of parents of the layer's mountpoint 225 // for which the layer's UID and GID maps don't contain corresponding entries. 226 ParentOwners(id string) (uids, gids []int, err error) 227 228 // ApplyDiff reads a tarstream which was created by a previous call to Diff and 229 // applies its changes to a specified layer. 230 ApplyDiff(to string, diff io.Reader) (int64, error) 231 } 232 233 type layerStore struct { 234 lockfile Locker 235 mountsLockfile Locker 236 rundir string 237 driver drivers.Driver 238 layerdir string 239 layers []*Layer 240 idindex *truncindex.TruncIndex 241 byid map[string]*Layer 242 byname map[string]*Layer 243 bymount map[string]*Layer 244 bycompressedsum map[digest.Digest][]string 245 byuncompressedsum map[digest.Digest][]string 246 uidMap []idtools.IDMap 247 gidMap []idtools.IDMap 248 } 249 250 func copyLayer(l *Layer) *Layer { 251 return &Layer{ 252 ID: l.ID, 253 Names: copyStringSlice(l.Names), 254 Parent: l.Parent, 255 Metadata: l.Metadata, 256 MountLabel: l.MountLabel, 257 MountPoint: l.MountPoint, 258 MountCount: l.MountCount, 259 Created: l.Created, 260 CompressedDigest: l.CompressedDigest, 261 CompressedSize: l.CompressedSize, 262 UncompressedDigest: l.UncompressedDigest, 263 UncompressedSize: l.UncompressedSize, 264 CompressionType: l.CompressionType, 265 ReadOnly: l.ReadOnly, 266 Flags: copyStringInterfaceMap(l.Flags), 267 UIDMap: copyIDMap(l.UIDMap), 268 GIDMap: copyIDMap(l.GIDMap), 269 } 270 } 271 272 func (r *layerStore) Layers() ([]Layer, error) { 273 layers := make([]Layer, len(r.layers)) 274 for i := range r.layers { 275 layers[i] = *copyLayer(r.layers[i]) 276 } 277 return layers, nil 278 } 279 280 func (r *layerStore) mountspath() string { 281 return filepath.Join(r.rundir, "mountpoints.json") 282 } 283 284 func (r *layerStore) layerspath() string { 285 return filepath.Join(r.layerdir, "layers.json") 286 } 287 288 func (r *layerStore) Load() error { 289 shouldSave := false 290 rpath := r.layerspath() 291 data, err := ioutil.ReadFile(rpath) 292 if err != nil && !os.IsNotExist(err) { 293 return err 294 } 295 layers := []*Layer{} 296 idlist := []string{} 297 ids := make(map[string]*Layer) 298 names := make(map[string]*Layer) 299 compressedsums := make(map[digest.Digest][]string) 300 uncompressedsums := make(map[digest.Digest][]string) 301 if r.lockfile.IsReadWrite() { 302 label.ClearLabels() 303 } 304 if err = json.Unmarshal(data, &layers); len(data) == 0 || err == nil { 305 idlist = make([]string, 0, len(layers)) 306 for n, layer := range layers { 307 ids[layer.ID] = layers[n] 308 idlist = append(idlist, layer.ID) 309 for _, name := range layer.Names { 310 if conflict, ok := names[name]; ok { 311 r.removeName(conflict, name) 312 shouldSave = true 313 } 314 names[name] = layers[n] 315 } 316 if layer.CompressedDigest != "" { 317 compressedsums[layer.CompressedDigest] = append(compressedsums[layer.CompressedDigest], layer.ID) 318 } 319 if layer.UncompressedDigest != "" { 320 uncompressedsums[layer.UncompressedDigest] = append(uncompressedsums[layer.UncompressedDigest], layer.ID) 321 } 322 if layer.MountLabel != "" { 323 label.ReserveLabel(layer.MountLabel) 324 } 325 layer.ReadOnly = !r.IsReadWrite() 326 } 327 err = nil 328 } 329 if shouldSave && (!r.IsReadWrite() || !r.Locked()) { 330 return ErrDuplicateLayerNames 331 } 332 r.layers = layers 333 r.idindex = truncindex.NewTruncIndex(idlist) 334 r.byid = ids 335 r.byname = names 336 r.bycompressedsum = compressedsums 337 r.byuncompressedsum = uncompressedsums 338 // Load and merge information about which layers are mounted, and where. 339 if r.IsReadWrite() { 340 r.mountsLockfile.RLock() 341 defer r.mountsLockfile.Unlock() 342 if err = r.loadMounts(); err != nil { 343 return err 344 } 345 } 346 // Last step: if we're writable, try to remove anything that a previous 347 // user of this storage area marked for deletion but didn't manage to 348 // actually delete. 349 if r.IsReadWrite() && r.Locked() { 350 for _, layer := range r.layers { 351 if layer.Flags == nil { 352 layer.Flags = make(map[string]interface{}) 353 } 354 if cleanup, ok := layer.Flags[incompleteFlag]; ok { 355 if b, ok := cleanup.(bool); ok && b { 356 err = r.Delete(layer.ID) 357 if err != nil { 358 break 359 } 360 shouldSave = true 361 } 362 } 363 } 364 if shouldSave { 365 return r.Save() 366 } 367 } 368 return err 369 } 370 371 func (r *layerStore) loadMounts() error { 372 mounts := make(map[string]*Layer) 373 mpath := r.mountspath() 374 data, err := ioutil.ReadFile(mpath) 375 if err != nil && !os.IsNotExist(err) { 376 return err 377 } 378 layerMounts := []layerMountPoint{} 379 if err = json.Unmarshal(data, &layerMounts); len(data) == 0 || err == nil { 380 for _, mount := range layerMounts { 381 if mount.MountPoint != "" { 382 if layer, ok := r.lookup(mount.ID); ok { 383 mounts[mount.MountPoint] = layer 384 layer.MountPoint = mount.MountPoint 385 layer.MountCount = mount.MountCount 386 } 387 } 388 } 389 err = nil 390 } 391 r.bymount = mounts 392 return err 393 } 394 395 func (r *layerStore) Save() error { 396 if !r.IsReadWrite() { 397 return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify the layer store at %q", r.layerspath()) 398 } 399 if !r.Locked() { 400 return errors.New("layer store is not locked for writing") 401 } 402 rpath := r.layerspath() 403 if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil { 404 return err 405 } 406 jldata, err := json.Marshal(&r.layers) 407 if err != nil { 408 return err 409 } 410 defer r.Touch() 411 if err := ioutils.AtomicWriteFile(rpath, jldata, 0600); err != nil { 412 return err 413 } 414 r.mountsLockfile.Lock() 415 defer r.mountsLockfile.Unlock() 416 defer r.mountsLockfile.Touch() 417 return r.saveMounts() 418 } 419 420 func (r *layerStore) saveMounts() error { 421 if !r.IsReadWrite() { 422 return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify the layer store at %q", r.layerspath()) 423 } 424 if !r.mountsLockfile.Locked() { 425 return errors.New("layer store mount information is not locked for writing") 426 } 427 mpath := r.mountspath() 428 if err := os.MkdirAll(filepath.Dir(mpath), 0700); err != nil { 429 return err 430 } 431 mounts := make([]layerMountPoint, 0, len(r.layers)) 432 for _, layer := range r.layers { 433 if layer.MountPoint != "" && layer.MountCount > 0 { 434 mounts = append(mounts, layerMountPoint{ 435 ID: layer.ID, 436 MountPoint: layer.MountPoint, 437 MountCount: layer.MountCount, 438 }) 439 } 440 } 441 jmdata, err := json.Marshal(&mounts) 442 if err != nil { 443 return err 444 } 445 if err = ioutils.AtomicWriteFile(mpath, jmdata, 0600); err != nil { 446 return err 447 } 448 return r.loadMounts() 449 } 450 451 func newLayerStore(rundir string, layerdir string, driver drivers.Driver, uidMap, gidMap []idtools.IDMap) (LayerStore, error) { 452 if err := os.MkdirAll(rundir, 0700); err != nil { 453 return nil, err 454 } 455 if err := os.MkdirAll(layerdir, 0700); err != nil { 456 return nil, err 457 } 458 lockfile, err := GetLockfile(filepath.Join(layerdir, "layers.lock")) 459 if err != nil { 460 return nil, err 461 } 462 lockfile.Lock() 463 defer lockfile.Unlock() 464 mountsLockfile, err := GetLockfile(filepath.Join(rundir, "mountpoints.lock")) 465 if err != nil { 466 return nil, err 467 } 468 rlstore := layerStore{ 469 lockfile: lockfile, 470 mountsLockfile: mountsLockfile, 471 driver: driver, 472 rundir: rundir, 473 layerdir: layerdir, 474 byid: make(map[string]*Layer), 475 bymount: make(map[string]*Layer), 476 byname: make(map[string]*Layer), 477 uidMap: copyIDMap(uidMap), 478 gidMap: copyIDMap(gidMap), 479 } 480 if err := rlstore.Load(); err != nil { 481 return nil, err 482 } 483 return &rlstore, nil 484 } 485 486 func newROLayerStore(rundir string, layerdir string, driver drivers.Driver) (ROLayerStore, error) { 487 lockfile, err := GetROLockfile(filepath.Join(layerdir, "layers.lock")) 488 if err != nil { 489 return nil, err 490 } 491 lockfile.Lock() 492 defer lockfile.Unlock() 493 rlstore := layerStore{ 494 lockfile: lockfile, 495 mountsLockfile: nil, 496 driver: driver, 497 rundir: rundir, 498 layerdir: layerdir, 499 byid: make(map[string]*Layer), 500 bymount: make(map[string]*Layer), 501 byname: make(map[string]*Layer), 502 } 503 if err := rlstore.Load(); err != nil { 504 return nil, err 505 } 506 return &rlstore, nil 507 } 508 509 func (r *layerStore) lookup(id string) (*Layer, bool) { 510 if layer, ok := r.byid[id]; ok { 511 return layer, ok 512 } else if layer, ok := r.byname[id]; ok { 513 return layer, ok 514 } else if longid, err := r.idindex.Get(id); err == nil { 515 layer, ok := r.byid[longid] 516 return layer, ok 517 } 518 return nil, false 519 } 520 521 func (r *layerStore) Size(name string) (int64, error) { 522 layer, ok := r.lookup(name) 523 if !ok { 524 return -1, ErrLayerUnknown 525 } 526 // We use the presence of a non-empty digest as an indicator that the size value was intentionally set, and that 527 // a zero value is not just present because it was never set to anything else (which can happen if the layer was 528 // created by a version of this library that didn't keep track of digest and size information). 529 if layer.UncompressedDigest != "" { 530 return layer.UncompressedSize, nil 531 } 532 return -1, nil 533 } 534 535 func (r *layerStore) ClearFlag(id string, flag string) error { 536 if !r.IsReadWrite() { 537 return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to clear flags on layers at %q", r.layerspath()) 538 } 539 layer, ok := r.lookup(id) 540 if !ok { 541 return ErrLayerUnknown 542 } 543 delete(layer.Flags, flag) 544 return r.Save() 545 } 546 547 func (r *layerStore) SetFlag(id string, flag string, value interface{}) error { 548 if !r.IsReadWrite() { 549 return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to set flags on layers at %q", r.layerspath()) 550 } 551 layer, ok := r.lookup(id) 552 if !ok { 553 return ErrLayerUnknown 554 } 555 if layer.Flags == nil { 556 layer.Flags = make(map[string]interface{}) 557 } 558 layer.Flags[flag] = value 559 return r.Save() 560 } 561 562 func (r *layerStore) Status() ([][2]string, error) { 563 return r.driver.Status(), nil 564 } 565 566 func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}, diff io.Reader) (layer *Layer, size int64, err error) { 567 if !r.IsReadWrite() { 568 return nil, -1, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new layers at %q", r.layerspath()) 569 } 570 size = -1 571 if err := os.MkdirAll(r.rundir, 0700); err != nil { 572 return nil, -1, err 573 } 574 if err := os.MkdirAll(r.layerdir, 0700); err != nil { 575 return nil, -1, err 576 } 577 if id == "" { 578 id = stringid.GenerateRandomID() 579 _, idInUse := r.byid[id] 580 for idInUse { 581 id = stringid.GenerateRandomID() 582 _, idInUse = r.byid[id] 583 } 584 } 585 if duplicateLayer, idInUse := r.byid[id]; idInUse { 586 return duplicateLayer, -1, ErrDuplicateID 587 } 588 names = dedupeNames(names) 589 for _, name := range names { 590 if _, nameInUse := r.byname[name]; nameInUse { 591 return nil, -1, ErrDuplicateName 592 } 593 } 594 parent := "" 595 if parentLayer != nil { 596 parent = parentLayer.ID 597 } 598 var parentMappings, templateIDMappings, oldMappings *idtools.IDMappings 599 if moreOptions.TemplateLayer != "" { 600 templateLayer, ok := r.lookup(moreOptions.TemplateLayer) 601 if !ok { 602 return nil, -1, ErrLayerUnknown 603 } 604 templateIDMappings = idtools.NewIDMappingsFromMaps(templateLayer.UIDMap, templateLayer.GIDMap) 605 } else { 606 templateIDMappings = &idtools.IDMappings{} 607 } 608 if parentLayer != nil { 609 parentMappings = idtools.NewIDMappingsFromMaps(parentLayer.UIDMap, parentLayer.GIDMap) 610 } else { 611 parentMappings = &idtools.IDMappings{} 612 } 613 if mountLabel != "" { 614 label.ReserveLabel(mountLabel) 615 } 616 idMappings := idtools.NewIDMappingsFromMaps(moreOptions.UIDMap, moreOptions.GIDMap) 617 opts := drivers.CreateOpts{ 618 MountLabel: mountLabel, 619 StorageOpt: options, 620 IDMappings: idMappings, 621 } 622 if moreOptions.TemplateLayer != "" { 623 if err = r.driver.CreateFromTemplate(id, moreOptions.TemplateLayer, templateIDMappings, parent, parentMappings, &opts, writeable); err != nil { 624 if id != "" { 625 return nil, -1, errors.Wrapf(err, "error creating copy of template layer %q with ID %q", moreOptions.TemplateLayer, id) 626 } 627 return nil, -1, errors.Wrapf(err, "error creating copy of template layer %q", moreOptions.TemplateLayer) 628 } 629 oldMappings = templateIDMappings 630 } else { 631 if writeable { 632 if err = r.driver.CreateReadWrite(id, parent, &opts); err != nil { 633 if id != "" { 634 return nil, -1, errors.Wrapf(err, "error creating read-write layer with ID %q", id) 635 } 636 return nil, -1, errors.Wrapf(err, "error creating read-write layer") 637 } 638 } else { 639 if err = r.driver.Create(id, parent, &opts); err != nil { 640 if id != "" { 641 return nil, -1, errors.Wrapf(err, "error creating layer with ID %q", id) 642 } 643 return nil, -1, errors.Wrapf(err, "error creating layer") 644 } 645 } 646 oldMappings = parentMappings 647 } 648 if !reflect.DeepEqual(oldMappings.UIDs(), idMappings.UIDs()) || !reflect.DeepEqual(oldMappings.GIDs(), idMappings.GIDs()) { 649 if err = r.driver.UpdateLayerIDMap(id, oldMappings, idMappings, mountLabel); err != nil { 650 // We don't have a record of this layer, but at least 651 // try to clean it up underneath us. 652 r.driver.Remove(id) 653 return nil, -1, err 654 } 655 } 656 if err == nil { 657 layer = &Layer{ 658 ID: id, 659 Parent: parent, 660 Names: names, 661 MountLabel: mountLabel, 662 Created: time.Now().UTC(), 663 Flags: make(map[string]interface{}), 664 UIDMap: copyIDMap(moreOptions.UIDMap), 665 GIDMap: copyIDMap(moreOptions.GIDMap), 666 } 667 r.layers = append(r.layers, layer) 668 r.idindex.Add(id) 669 r.byid[id] = layer 670 for _, name := range names { 671 r.byname[name] = layer 672 } 673 for flag, value := range flags { 674 layer.Flags[flag] = value 675 } 676 if diff != nil { 677 layer.Flags[incompleteFlag] = true 678 err = r.Save() 679 if err != nil { 680 // We don't have a record of this layer, but at least 681 // try to clean it up underneath us. 682 r.driver.Remove(id) 683 return nil, -1, err 684 } 685 size, err = r.ApplyDiff(layer.ID, diff) 686 if err != nil { 687 if r.Delete(layer.ID) != nil { 688 // Either a driver error or an error saving. 689 // We now have a layer that's been marked for 690 // deletion but which we failed to remove. 691 } 692 return nil, -1, err 693 } 694 delete(layer.Flags, incompleteFlag) 695 } 696 err = r.Save() 697 if err != nil { 698 // We don't have a record of this layer, but at least 699 // try to clean it up underneath us. 700 r.driver.Remove(id) 701 return nil, -1, err 702 } 703 layer = copyLayer(layer) 704 } 705 return layer, size, err 706 } 707 708 func (r *layerStore) CreateWithFlags(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}) (layer *Layer, err error) { 709 layer, _, err = r.Put(id, parent, names, mountLabel, options, moreOptions, writeable, flags, nil) 710 return layer, err 711 } 712 713 func (r *layerStore) Create(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool) (layer *Layer, err error) { 714 return r.CreateWithFlags(id, parent, names, mountLabel, options, moreOptions, writeable, nil) 715 } 716 717 func (r *layerStore) Mounted(id string) (int, error) { 718 if !r.IsReadWrite() { 719 return 0, errors.Wrapf(ErrStoreIsReadOnly, "no mount information for layers at %q", r.mountspath()) 720 } 721 r.mountsLockfile.RLock() 722 defer r.mountsLockfile.Unlock() 723 if modified, err := r.mountsLockfile.Modified(); modified || err != nil { 724 if err = r.loadMounts(); err != nil { 725 return 0, err 726 } 727 } 728 layer, ok := r.lookup(id) 729 if !ok { 730 return 0, ErrLayerUnknown 731 } 732 return layer.MountCount, nil 733 } 734 735 func (r *layerStore) Mount(id string, options drivers.MountOpts) (string, error) { 736 if !r.IsReadWrite() { 737 return "", errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath()) 738 } 739 r.mountsLockfile.Lock() 740 defer r.mountsLockfile.Unlock() 741 if modified, err := r.mountsLockfile.Modified(); modified || err != nil { 742 if err = r.loadMounts(); err != nil { 743 return "", err 744 } 745 } 746 defer r.mountsLockfile.Touch() 747 layer, ok := r.lookup(id) 748 if !ok { 749 return "", ErrLayerUnknown 750 } 751 if layer.MountCount > 0 { 752 layer.MountCount++ 753 return layer.MountPoint, r.saveMounts() 754 } 755 if options.MountLabel == "" { 756 options.MountLabel = layer.MountLabel 757 } 758 759 if (options.UidMaps != nil || options.GidMaps != nil) && !r.driver.SupportsShifting() { 760 if !reflect.DeepEqual(options.UidMaps, layer.UIDMap) || !reflect.DeepEqual(options.GidMaps, layer.GIDMap) { 761 return "", fmt.Errorf("cannot mount layer %v: shifting not enabled", layer.ID) 762 } 763 } 764 mountpoint, err := r.driver.Get(id, options) 765 if mountpoint != "" && err == nil { 766 if layer.MountPoint != "" { 767 delete(r.bymount, layer.MountPoint) 768 } 769 layer.MountPoint = filepath.Clean(mountpoint) 770 layer.MountCount++ 771 r.bymount[layer.MountPoint] = layer 772 err = r.saveMounts() 773 } 774 return mountpoint, err 775 } 776 777 func (r *layerStore) Unmount(id string, force bool) (bool, error) { 778 if !r.IsReadWrite() { 779 return false, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath()) 780 } 781 r.mountsLockfile.Lock() 782 defer r.mountsLockfile.Unlock() 783 if modified, err := r.mountsLockfile.Modified(); modified || err != nil { 784 if err = r.loadMounts(); err != nil { 785 return false, err 786 } 787 } 788 defer r.mountsLockfile.Touch() 789 layer, ok := r.lookup(id) 790 if !ok { 791 layerByMount, ok := r.bymount[filepath.Clean(id)] 792 if !ok { 793 return false, ErrLayerUnknown 794 } 795 layer = layerByMount 796 } 797 if force { 798 layer.MountCount = 1 799 } 800 if layer.MountCount > 1 { 801 layer.MountCount-- 802 return true, r.saveMounts() 803 } 804 err := r.driver.Put(id) 805 if err == nil || os.IsNotExist(err) { 806 if layer.MountPoint != "" { 807 delete(r.bymount, layer.MountPoint) 808 } 809 layer.MountCount-- 810 layer.MountPoint = "" 811 return false, r.saveMounts() 812 } 813 return true, err 814 } 815 816 func (r *layerStore) ParentOwners(id string) (uids, gids []int, err error) { 817 if !r.IsReadWrite() { 818 return nil, nil, errors.Wrapf(ErrStoreIsReadOnly, "no mount information for layers at %q", r.mountspath()) 819 } 820 r.mountsLockfile.RLock() 821 defer r.mountsLockfile.Unlock() 822 if modified, err := r.mountsLockfile.Modified(); modified || err != nil { 823 if err = r.loadMounts(); err != nil { 824 return nil, nil, err 825 } 826 } 827 layer, ok := r.lookup(id) 828 if !ok { 829 return nil, nil, ErrLayerUnknown 830 } 831 if len(layer.UIDMap) == 0 && len(layer.GIDMap) == 0 { 832 // We're not using any mappings, so there aren't any unmapped IDs on parent directories. 833 return nil, nil, nil 834 } 835 if layer.MountPoint == "" { 836 // We don't know which directories to examine. 837 return nil, nil, ErrLayerNotMounted 838 } 839 rootuid, rootgid, err := idtools.GetRootUIDGID(layer.UIDMap, layer.GIDMap) 840 if err != nil { 841 return nil, nil, errors.Wrapf(err, "error reading root ID values for layer %q", layer.ID) 842 } 843 m := idtools.NewIDMappingsFromMaps(layer.UIDMap, layer.GIDMap) 844 fsuids := make(map[int]struct{}) 845 fsgids := make(map[int]struct{}) 846 for dir := filepath.Dir(layer.MountPoint); dir != "" && dir != string(os.PathSeparator); dir = filepath.Dir(dir) { 847 st, err := system.Stat(dir) 848 if err != nil { 849 return nil, nil, errors.Wrapf(err, "error reading ownership of directory %q", dir) 850 } 851 lst, err := system.Lstat(dir) 852 if err != nil { 853 return nil, nil, errors.Wrapf(err, "error reading ownership of directory-in-case-it's-a-symlink %q", dir) 854 } 855 fsuid := int(st.UID()) 856 fsgid := int(st.GID()) 857 if _, _, err := m.ToContainer(idtools.IDPair{UID: fsuid, GID: rootgid}); err != nil { 858 fsuids[fsuid] = struct{}{} 859 } 860 if _, _, err := m.ToContainer(idtools.IDPair{UID: rootuid, GID: fsgid}); err != nil { 861 fsgids[fsgid] = struct{}{} 862 } 863 fsuid = int(lst.UID()) 864 fsgid = int(lst.GID()) 865 if _, _, err := m.ToContainer(idtools.IDPair{UID: fsuid, GID: rootgid}); err != nil { 866 fsuids[fsuid] = struct{}{} 867 } 868 if _, _, err := m.ToContainer(idtools.IDPair{UID: rootuid, GID: fsgid}); err != nil { 869 fsgids[fsgid] = struct{}{} 870 } 871 } 872 for uid := range fsuids { 873 uids = append(uids, uid) 874 } 875 for gid := range fsgids { 876 gids = append(gids, gid) 877 } 878 if len(uids) > 1 { 879 sort.Ints(uids) 880 } 881 if len(gids) > 1 { 882 sort.Ints(gids) 883 } 884 return uids, gids, nil 885 } 886 887 func (r *layerStore) removeName(layer *Layer, name string) { 888 layer.Names = stringSliceWithoutValue(layer.Names, name) 889 } 890 891 func (r *layerStore) SetNames(id string, names []string) error { 892 if !r.IsReadWrite() { 893 return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to change layer name assignments at %q", r.layerspath()) 894 } 895 names = dedupeNames(names) 896 if layer, ok := r.lookup(id); ok { 897 for _, name := range layer.Names { 898 delete(r.byname, name) 899 } 900 for _, name := range names { 901 if otherLayer, ok := r.byname[name]; ok { 902 r.removeName(otherLayer, name) 903 } 904 r.byname[name] = layer 905 } 906 layer.Names = names 907 return r.Save() 908 } 909 return ErrLayerUnknown 910 } 911 912 func (r *layerStore) Metadata(id string) (string, error) { 913 if layer, ok := r.lookup(id); ok { 914 return layer.Metadata, nil 915 } 916 return "", ErrLayerUnknown 917 } 918 919 func (r *layerStore) SetMetadata(id, metadata string) error { 920 if !r.IsReadWrite() { 921 return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify layer metadata at %q", r.layerspath()) 922 } 923 if layer, ok := r.lookup(id); ok { 924 layer.Metadata = metadata 925 return r.Save() 926 } 927 return ErrLayerUnknown 928 } 929 930 func (r *layerStore) tspath(id string) string { 931 return filepath.Join(r.layerdir, id+tarSplitSuffix) 932 } 933 934 func (r *layerStore) Delete(id string) error { 935 if !r.IsReadWrite() { 936 return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete layers at %q", r.layerspath()) 937 } 938 layer, ok := r.lookup(id) 939 if !ok { 940 return ErrLayerUnknown 941 } 942 id = layer.ID 943 // The layer may already have been explicitly unmounted, but if not, we 944 // should try to clean that up before we start deleting anything at the 945 // driver level. 946 mountCount, err := r.Mounted(id) 947 if err != nil { 948 return errors.Wrapf(err, "error checking if layer %q is still mounted", id) 949 } 950 for mountCount > 0 { 951 if _, err := r.Unmount(id, false); err != nil { 952 return err 953 } 954 mountCount, err = r.Mounted(id) 955 if err != nil { 956 return errors.Wrapf(err, "error checking if layer %q is still mounted", id) 957 } 958 } 959 err = r.driver.Remove(id) 960 if err == nil { 961 os.Remove(r.tspath(id)) 962 delete(r.byid, id) 963 r.idindex.Delete(id) 964 mountLabel := layer.MountLabel 965 if layer.MountPoint != "" { 966 delete(r.bymount, layer.MountPoint) 967 } 968 toDeleteIndex := -1 969 for i, candidate := range r.layers { 970 if candidate.ID == id { 971 toDeleteIndex = i 972 break 973 } 974 } 975 if toDeleteIndex != -1 { 976 // delete the layer at toDeleteIndex 977 if toDeleteIndex == len(r.layers)-1 { 978 r.layers = r.layers[:len(r.layers)-1] 979 } else { 980 r.layers = append(r.layers[:toDeleteIndex], r.layers[toDeleteIndex+1:]...) 981 } 982 } 983 if mountLabel != "" { 984 var found bool 985 for _, candidate := range r.layers { 986 if candidate.MountLabel == mountLabel { 987 found = true 988 break 989 } 990 } 991 if !found { 992 label.ReleaseLabel(mountLabel) 993 } 994 } 995 if err = r.Save(); err != nil { 996 return err 997 } 998 } 999 return err 1000 } 1001 1002 func (r *layerStore) Lookup(name string) (id string, err error) { 1003 if layer, ok := r.lookup(name); ok { 1004 return layer.ID, nil 1005 } 1006 return "", ErrLayerUnknown 1007 } 1008 1009 func (r *layerStore) Exists(id string) bool { 1010 _, ok := r.lookup(id) 1011 return ok 1012 } 1013 1014 func (r *layerStore) Get(id string) (*Layer, error) { 1015 if layer, ok := r.lookup(id); ok { 1016 return copyLayer(layer), nil 1017 } 1018 return nil, ErrLayerUnknown 1019 } 1020 1021 func (r *layerStore) Wipe() error { 1022 if !r.IsReadWrite() { 1023 return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete layers at %q", r.layerspath()) 1024 } 1025 ids := make([]string, 0, len(r.byid)) 1026 for id := range r.byid { 1027 ids = append(ids, id) 1028 } 1029 for _, id := range ids { 1030 if err := r.Delete(id); err != nil { 1031 return err 1032 } 1033 } 1034 return nil 1035 } 1036 1037 func (r *layerStore) findParentAndLayer(from, to string) (fromID string, toID string, fromLayer, toLayer *Layer, err error) { 1038 var ok bool 1039 toLayer, ok = r.lookup(to) 1040 if !ok { 1041 return "", "", nil, nil, ErrLayerUnknown 1042 } 1043 to = toLayer.ID 1044 if from == "" { 1045 from = toLayer.Parent 1046 } 1047 if from != "" { 1048 fromLayer, ok = r.lookup(from) 1049 if ok { 1050 from = fromLayer.ID 1051 } else { 1052 fromLayer, ok = r.lookup(toLayer.Parent) 1053 if ok { 1054 from = fromLayer.ID 1055 } 1056 } 1057 } 1058 return from, to, fromLayer, toLayer, nil 1059 } 1060 1061 func (r *layerStore) layerMappings(layer *Layer) *idtools.IDMappings { 1062 if layer == nil { 1063 return &idtools.IDMappings{} 1064 } 1065 return idtools.NewIDMappingsFromMaps(layer.UIDMap, layer.GIDMap) 1066 } 1067 1068 func (r *layerStore) Changes(from, to string) ([]archive.Change, error) { 1069 from, to, fromLayer, toLayer, err := r.findParentAndLayer(from, to) 1070 if err != nil { 1071 return nil, ErrLayerUnknown 1072 } 1073 return r.driver.Changes(to, r.layerMappings(toLayer), from, r.layerMappings(fromLayer), toLayer.MountLabel) 1074 } 1075 1076 type simpleGetCloser struct { 1077 r *layerStore 1078 path string 1079 id string 1080 } 1081 1082 func (s *simpleGetCloser) Get(path string) (io.ReadCloser, error) { 1083 return os.Open(filepath.Join(s.path, path)) 1084 } 1085 1086 func (s *simpleGetCloser) Close() error { 1087 _, err := s.r.Unmount(s.id, false) 1088 return err 1089 } 1090 1091 func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) { 1092 if getter, ok := r.driver.(drivers.DiffGetterDriver); ok { 1093 return getter.DiffGetter(id) 1094 } 1095 path, err := r.Mount(id, drivers.MountOpts{}) 1096 if err != nil { 1097 return nil, err 1098 } 1099 return &simpleGetCloser{ 1100 r: r, 1101 path: path, 1102 id: id, 1103 }, nil 1104 } 1105 1106 func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, error) { 1107 var metadata storage.Unpacker 1108 1109 from, to, fromLayer, toLayer, err := r.findParentAndLayer(from, to) 1110 if err != nil { 1111 return nil, ErrLayerUnknown 1112 } 1113 // Default to applying the type of compression that we noted was used 1114 // for the layerdiff when it was applied. 1115 compression := toLayer.CompressionType 1116 // If a particular compression type (or no compression) was selected, 1117 // use that instead. 1118 if options != nil && options.Compression != nil { 1119 compression = *options.Compression 1120 } 1121 maybeCompressReadCloser := func(rc io.ReadCloser) (io.ReadCloser, error) { 1122 // Depending on whether or not compression is desired, return either the 1123 // passed-in ReadCloser, or a new one that provides its readers with a 1124 // compressed version of the data that the original would have provided 1125 // to its readers. 1126 if compression == archive.Uncompressed { 1127 return rc, nil 1128 } 1129 preader, pwriter := io.Pipe() 1130 compressor, err := archive.CompressStream(pwriter, compression) 1131 if err != nil { 1132 rc.Close() 1133 pwriter.Close() 1134 preader.Close() 1135 return nil, err 1136 } 1137 go func() { 1138 defer pwriter.Close() 1139 defer compressor.Close() 1140 defer rc.Close() 1141 io.Copy(compressor, rc) 1142 }() 1143 return preader, nil 1144 } 1145 1146 if from != toLayer.Parent { 1147 diff, err := r.driver.Diff(to, r.layerMappings(toLayer), from, r.layerMappings(fromLayer), toLayer.MountLabel) 1148 if err != nil { 1149 return nil, err 1150 } 1151 return maybeCompressReadCloser(diff) 1152 } 1153 1154 tsfile, err := os.Open(r.tspath(to)) 1155 if err != nil { 1156 if !os.IsNotExist(err) { 1157 return nil, err 1158 } 1159 diff, err := r.driver.Diff(to, r.layerMappings(toLayer), from, r.layerMappings(fromLayer), toLayer.MountLabel) 1160 if err != nil { 1161 return nil, err 1162 } 1163 return maybeCompressReadCloser(diff) 1164 } 1165 defer tsfile.Close() 1166 1167 decompressor, err := pgzip.NewReader(tsfile) 1168 if err != nil { 1169 return nil, err 1170 } 1171 defer decompressor.Close() 1172 1173 tsbytes, err := ioutil.ReadAll(decompressor) 1174 if err != nil { 1175 return nil, err 1176 } 1177 1178 metadata = storage.NewJSONUnpacker(bytes.NewBuffer(tsbytes)) 1179 1180 fgetter, err := r.newFileGetter(to) 1181 if err != nil { 1182 return nil, err 1183 } 1184 1185 tarstream := asm.NewOutputTarStream(fgetter, metadata) 1186 rc := ioutils.NewReadCloserWrapper(tarstream, func() error { 1187 err1 := tarstream.Close() 1188 err2 := fgetter.Close() 1189 if err2 == nil { 1190 return err1 1191 } 1192 return err2 1193 }) 1194 return maybeCompressReadCloser(rc) 1195 } 1196 1197 func (r *layerStore) DiffSize(from, to string) (size int64, err error) { 1198 var fromLayer, toLayer *Layer 1199 from, to, fromLayer, toLayer, err = r.findParentAndLayer(from, to) 1200 if err != nil { 1201 return -1, ErrLayerUnknown 1202 } 1203 return r.driver.DiffSize(to, r.layerMappings(toLayer), from, r.layerMappings(fromLayer), toLayer.MountLabel) 1204 } 1205 1206 func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error) { 1207 if !r.IsReadWrite() { 1208 return -1, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify layer contents at %q", r.layerspath()) 1209 } 1210 1211 layer, ok := r.lookup(to) 1212 if !ok { 1213 return -1, ErrLayerUnknown 1214 } 1215 1216 header := make([]byte, 10240) 1217 n, err := diff.Read(header) 1218 if err != nil && err != io.EOF { 1219 return -1, err 1220 } 1221 1222 compression := archive.DetectCompression(header[:n]) 1223 compressedDigest := digest.Canonical.Digester() 1224 compressedCounter := ioutils.NewWriteCounter(compressedDigest.Hash()) 1225 defragmented := io.TeeReader(io.MultiReader(bytes.NewBuffer(header[:n]), diff), compressedCounter) 1226 1227 tsdata := bytes.Buffer{} 1228 compressor, err := pgzip.NewWriterLevel(&tsdata, pgzip.BestSpeed) 1229 if err != nil { 1230 compressor = pgzip.NewWriter(&tsdata) 1231 } 1232 metadata := storage.NewJSONPacker(compressor) 1233 uncompressed, err := archive.DecompressStream(defragmented) 1234 if err != nil { 1235 return -1, err 1236 } 1237 uncompressedDigest := digest.Canonical.Digester() 1238 uncompressedCounter := ioutils.NewWriteCounter(uncompressedDigest.Hash()) 1239 payload, err := asm.NewInputTarStream(io.TeeReader(uncompressed, uncompressedCounter), metadata, storage.NewDiscardFilePutter()) 1240 if err != nil { 1241 return -1, err 1242 } 1243 size, err = r.driver.ApplyDiff(layer.ID, r.layerMappings(layer), layer.Parent, layer.MountLabel, payload) 1244 if err != nil { 1245 return -1, err 1246 } 1247 compressor.Close() 1248 if err == nil { 1249 if err := os.MkdirAll(filepath.Dir(r.tspath(layer.ID)), 0700); err != nil { 1250 return -1, err 1251 } 1252 if err := ioutils.AtomicWriteFile(r.tspath(layer.ID), tsdata.Bytes(), 0600); err != nil { 1253 return -1, err 1254 } 1255 } 1256 1257 updateDigestMap := func(m *map[digest.Digest][]string, oldvalue, newvalue digest.Digest, id string) { 1258 var newList []string 1259 if oldvalue != "" { 1260 for _, value := range (*m)[oldvalue] { 1261 if value != id { 1262 newList = append(newList, value) 1263 } 1264 } 1265 if len(newList) > 0 { 1266 (*m)[oldvalue] = newList 1267 } else { 1268 delete(*m, oldvalue) 1269 } 1270 } 1271 if newvalue != "" { 1272 (*m)[newvalue] = append((*m)[newvalue], id) 1273 } 1274 } 1275 updateDigestMap(&r.bycompressedsum, layer.CompressedDigest, compressedDigest.Digest(), layer.ID) 1276 layer.CompressedDigest = compressedDigest.Digest() 1277 layer.CompressedSize = compressedCounter.Count 1278 updateDigestMap(&r.byuncompressedsum, layer.UncompressedDigest, uncompressedDigest.Digest(), layer.ID) 1279 layer.UncompressedDigest = uncompressedDigest.Digest() 1280 layer.UncompressedSize = uncompressedCounter.Count 1281 layer.CompressionType = compression 1282 1283 err = r.Save() 1284 1285 return size, err 1286 } 1287 1288 func (r *layerStore) layersByDigestMap(m map[digest.Digest][]string, d digest.Digest) ([]Layer, error) { 1289 var layers []Layer 1290 for _, layerID := range m[d] { 1291 layer, ok := r.lookup(layerID) 1292 if !ok { 1293 return nil, ErrLayerUnknown 1294 } 1295 layers = append(layers, *copyLayer(layer)) 1296 } 1297 return layers, nil 1298 } 1299 1300 func (r *layerStore) LayersByCompressedDigest(d digest.Digest) ([]Layer, error) { 1301 return r.layersByDigestMap(r.bycompressedsum, d) 1302 } 1303 1304 func (r *layerStore) LayersByUncompressedDigest(d digest.Digest) ([]Layer, error) { 1305 return r.layersByDigestMap(r.byuncompressedsum, d) 1306 } 1307 1308 func (r *layerStore) Lock() { 1309 r.lockfile.Lock() 1310 } 1311 1312 func (r *layerStore) RecursiveLock() { 1313 r.lockfile.RecursiveLock() 1314 } 1315 1316 func (r *layerStore) RLock() { 1317 r.lockfile.RLock() 1318 } 1319 1320 func (r *layerStore) Unlock() { 1321 r.lockfile.Unlock() 1322 } 1323 1324 func (r *layerStore) Touch() error { 1325 return r.lockfile.Touch() 1326 } 1327 1328 func (r *layerStore) Modified() (bool, error) { 1329 var mmodified bool 1330 lmodified, err := r.lockfile.Modified() 1331 if err != nil { 1332 return lmodified, err 1333 } 1334 if r.IsReadWrite() { 1335 r.mountsLockfile.RLock() 1336 defer r.mountsLockfile.Unlock() 1337 mmodified, err = r.mountsLockfile.Modified() 1338 if err != nil { 1339 return lmodified, err 1340 } 1341 } 1342 return lmodified || mmodified, nil 1343 } 1344 1345 func (r *layerStore) IsReadWrite() bool { 1346 return r.lockfile.IsReadWrite() 1347 } 1348 1349 func (r *layerStore) TouchedSince(when time.Time) bool { 1350 return r.lockfile.TouchedSince(when) 1351 } 1352 1353 func (r *layerStore) Locked() bool { 1354 return r.lockfile.Locked() 1355 }