github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/daemon/graphdriver/overlay2/overlay.go (about) 1 // +build linux 2 3 package overlay2 // import "github.com/demonoid81/moby/daemon/graphdriver/overlay2" 4 5 import ( 6 "context" 7 "errors" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "path" 13 "path/filepath" 14 "strconv" 15 "strings" 16 "sync" 17 18 "github.com/demonoid81/moby/daemon/graphdriver" 19 "github.com/demonoid81/moby/daemon/graphdriver/overlayutils" 20 "github.com/demonoid81/moby/daemon/graphdriver/quota" 21 "github.com/demonoid81/moby/pkg/archive" 22 "github.com/demonoid81/moby/pkg/chrootarchive" 23 "github.com/demonoid81/moby/pkg/containerfs" 24 "github.com/demonoid81/moby/pkg/directory" 25 "github.com/demonoid81/moby/pkg/fsutils" 26 "github.com/demonoid81/moby/pkg/idtools" 27 "github.com/demonoid81/moby/pkg/locker" 28 "github.com/demonoid81/moby/pkg/parsers" 29 "github.com/demonoid81/moby/pkg/system" 30 units "github.com/docker/go-units" 31 "github.com/moby/sys/mount" 32 rsystem "github.com/opencontainers/runc/libcontainer/system" 33 "github.com/opencontainers/selinux/go-selinux/label" 34 "github.com/sirupsen/logrus" 35 "golang.org/x/sys/unix" 36 ) 37 38 var ( 39 // untar defines the untar method 40 untar = chrootarchive.UntarUncompressed 41 ) 42 43 // This backend uses the overlay union filesystem for containers 44 // with diff directories for each layer. 45 46 // This version of the overlay driver requires at least kernel 47 // 4.0.0 in order to support mounting multiple diff directories. 48 49 // Each container/image has at least a "diff" directory and "link" file. 50 // If there is also a "lower" file when there are diff layers 51 // below as well as "merged" and "work" directories. The "diff" directory 52 // has the upper layer of the overlay and is used to capture any 53 // changes to the layer. The "lower" file contains all the lower layer 54 // mounts separated by ":" and ordered from uppermost to lowermost 55 // layers. The overlay itself is mounted in the "merged" directory, 56 // and the "work" dir is needed for overlay to work. 57 58 // The "link" file for each layer contains a unique string for the layer. 59 // Under the "l" directory at the root there will be a symbolic link 60 // with that unique string pointing the "diff" directory for the layer. 61 // The symbolic links are used to reference lower layers in the "lower" 62 // file and on mount. The links are used to shorten the total length 63 // of a layer reference without requiring changes to the layer identifier 64 // or root directory. Mounts are always done relative to root and 65 // referencing the symbolic links in order to ensure the number of 66 // lower directories can fit in a single page for making the mount 67 // syscall. A hard upper limit of 128 lower layers is enforced to ensure 68 // that mounts do not fail due to length. 69 70 const ( 71 driverName = "overlay2" 72 linkDir = "l" 73 diffDirName = "diff" 74 workDirName = "work" 75 mergedDirName = "merged" 76 lowerFile = "lower" 77 maxDepth = 128 78 79 // idLength represents the number of random characters 80 // which can be used to create the unique link identifier 81 // for every layer. If this value is too long then the 82 // page size limit for the mount command may be exceeded. 83 // The idLength should be selected such that following equation 84 // is true (512 is a buffer for label metadata). 85 // ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512) 86 idLength = 26 87 ) 88 89 type overlayOptions struct { 90 overrideKernelCheck bool 91 quota quota.Quota 92 } 93 94 // Driver contains information about the home directory and the list of active 95 // mounts that are created using this driver. 96 type Driver struct { 97 home string 98 uidMaps []idtools.IDMap 99 gidMaps []idtools.IDMap 100 ctr *graphdriver.RefCounter 101 quotaCtl *quota.Control 102 options overlayOptions 103 naiveDiff graphdriver.DiffDriver 104 supportsDType bool 105 locker *locker.Locker 106 } 107 108 var ( 109 logger = logrus.WithField("storage-driver", "overlay2") 110 backingFs = "<unknown>" 111 projectQuotaSupported = false 112 113 useNaiveDiffLock sync.Once 114 useNaiveDiffOnly bool 115 116 indexOff string 117 ) 118 119 func init() { 120 graphdriver.Register(driverName, Init) 121 } 122 123 // Init returns the native diff driver for overlay filesystem. 124 // If overlay filesystem is not supported on the host, the error 125 // graphdriver.ErrNotSupported is returned. 126 // If an overlay filesystem is not supported over an existing filesystem then 127 // the error graphdriver.ErrIncompatibleFS is returned. 128 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 129 opts, err := parseOptions(options) 130 if err != nil { 131 return nil, err 132 } 133 134 // Perform feature detection on /var/lib/docker/overlay2 if it's an existing directory. 135 // This covers situations where /var/lib/docker/overlay2 is a mount, and on a different 136 // filesystem than /var/lib/docker. 137 // If the path does not exist, fall back to using /var/lib/docker for feature detection. 138 testdir := home 139 if _, err := os.Stat(testdir); os.IsNotExist(err) { 140 testdir = filepath.Dir(testdir) 141 } 142 143 if err := overlayutils.SupportsOverlay(testdir, true); err != nil { 144 logger.Error(err) 145 return nil, graphdriver.ErrNotSupported 146 } 147 148 fsMagic, err := graphdriver.GetFSMagic(testdir) 149 if err != nil { 150 return nil, err 151 } 152 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 153 backingFs = fsName 154 } 155 156 supportsDType, err := fsutils.SupportsDType(testdir) 157 if err != nil { 158 return nil, err 159 } 160 if !supportsDType { 161 if !graphdriver.IsInitialized(home) { 162 return nil, overlayutils.ErrDTypeNotSupported("overlay2", backingFs) 163 } 164 // allow running without d_type only for existing setups (#27443) 165 logger.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs)) 166 } 167 168 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 169 if err != nil { 170 return nil, err 171 } 172 // Create the driver home dir 173 if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { 174 return nil, err 175 } 176 177 d := &Driver{ 178 home: home, 179 uidMaps: uidMaps, 180 gidMaps: gidMaps, 181 ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), 182 supportsDType: supportsDType, 183 locker: locker.New(), 184 options: *opts, 185 } 186 187 d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps) 188 189 if backingFs == "xfs" { 190 // Try to enable project quota support over xfs. 191 if d.quotaCtl, err = quota.NewControl(home); err == nil { 192 projectQuotaSupported = true 193 } else if opts.quota.Size > 0 { 194 return nil, fmt.Errorf("Storage option overlay2.size not supported. Filesystem does not support Project Quota: %v", err) 195 } 196 } else if opts.quota.Size > 0 { 197 // if xfs is not the backing fs then error out if the storage-opt overlay2.size is used. 198 return nil, fmt.Errorf("Storage Option overlay2.size only supported for backingFS XFS. Found %v", backingFs) 199 } 200 201 // figure out whether "index=off" option is recognized by the kernel 202 _, err = os.Stat("/sys/module/overlay/parameters/index") 203 switch { 204 case err == nil: 205 indexOff = "index=off," 206 case os.IsNotExist(err): 207 // old kernel, no index -- do nothing 208 default: 209 logger.Warnf("Unable to detect whether overlay kernel module supports index parameter: %s", err) 210 } 211 212 logger.Debugf("backingFs=%s, projectQuotaSupported=%v, indexOff=%q", backingFs, projectQuotaSupported, indexOff) 213 214 return d, nil 215 } 216 217 func parseOptions(options []string) (*overlayOptions, error) { 218 o := &overlayOptions{} 219 for _, option := range options { 220 key, val, err := parsers.ParseKeyValueOpt(option) 221 if err != nil { 222 return nil, err 223 } 224 key = strings.ToLower(key) 225 switch key { 226 case "overlay2.override_kernel_check": 227 o.overrideKernelCheck, err = strconv.ParseBool(val) 228 if err != nil { 229 return nil, err 230 } 231 case "overlay2.size": 232 size, err := units.RAMInBytes(val) 233 if err != nil { 234 return nil, err 235 } 236 o.quota.Size = uint64(size) 237 default: 238 return nil, fmt.Errorf("overlay2: unknown option %s", key) 239 } 240 } 241 return o, nil 242 } 243 244 func useNaiveDiff(home string) bool { 245 useNaiveDiffLock.Do(func() { 246 if err := doesSupportNativeDiff(home); err != nil { 247 logger.Warnf("Not using native diff for overlay2, this may cause degraded performance for building images: %v", err) 248 useNaiveDiffOnly = true 249 } 250 }) 251 return useNaiveDiffOnly 252 } 253 254 func (d *Driver) String() string { 255 return driverName 256 } 257 258 // Status returns current driver information in a two dimensional string array. 259 // Output contains "Backing Filesystem" used in this implementation. 260 func (d *Driver) Status() [][2]string { 261 return [][2]string{ 262 {"Backing Filesystem", backingFs}, 263 {"Supports d_type", strconv.FormatBool(d.supportsDType)}, 264 {"Native Overlay Diff", strconv.FormatBool(!useNaiveDiff(d.home))}, 265 } 266 } 267 268 // GetMetadata returns metadata about the overlay driver such as the LowerDir, 269 // UpperDir, WorkDir, and MergeDir used to store data. 270 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 271 dir := d.dir(id) 272 if _, err := os.Stat(dir); err != nil { 273 return nil, err 274 } 275 276 metadata := map[string]string{ 277 "WorkDir": path.Join(dir, workDirName), 278 "MergedDir": path.Join(dir, mergedDirName), 279 "UpperDir": path.Join(dir, diffDirName), 280 } 281 282 lowerDirs, err := d.getLowerDirs(id) 283 if err != nil { 284 return nil, err 285 } 286 if len(lowerDirs) > 0 { 287 metadata["LowerDir"] = strings.Join(lowerDirs, ":") 288 } 289 290 return metadata, nil 291 } 292 293 // Cleanup any state created by overlay which should be cleaned when daemon 294 // is being shutdown. For now, we just have to unmount the bind mounted 295 // we had created. 296 func (d *Driver) Cleanup() error { 297 return mount.RecursiveUnmount(d.home) 298 } 299 300 // CreateReadWrite creates a layer that is writable for use as a container 301 // file system. 302 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { 303 if opts == nil { 304 opts = &graphdriver.CreateOpts{ 305 StorageOpt: make(map[string]string), 306 } 307 } else if opts.StorageOpt == nil { 308 opts.StorageOpt = make(map[string]string) 309 } 310 311 // Merge daemon default config. 312 if _, ok := opts.StorageOpt["size"]; !ok && d.options.quota.Size != 0 { 313 opts.StorageOpt["size"] = strconv.FormatUint(d.options.quota.Size, 10) 314 } 315 316 if _, ok := opts.StorageOpt["size"]; ok && !projectQuotaSupported { 317 return fmt.Errorf("--storage-opt is supported only for overlay over xfs with 'pquota' mount option") 318 } 319 320 return d.create(id, parent, opts) 321 } 322 323 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. 324 // The parent filesystem is used to configure these directories for the overlay. 325 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { 326 if opts != nil && len(opts.StorageOpt) != 0 { 327 if _, ok := opts.StorageOpt["size"]; ok { 328 return fmt.Errorf("--storage-opt size is only supported for ReadWrite Layers") 329 } 330 } 331 return d.create(id, parent, opts) 332 } 333 334 func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { 335 dir := d.dir(id) 336 337 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 338 if err != nil { 339 return err 340 } 341 root := idtools.Identity{UID: rootUID, GID: rootGID} 342 343 if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { 344 return err 345 } 346 if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { 347 return err 348 } 349 350 defer func() { 351 // Clean up on failure 352 if retErr != nil { 353 os.RemoveAll(dir) 354 } 355 }() 356 357 if opts != nil && len(opts.StorageOpt) > 0 { 358 driver := &Driver{} 359 if err := d.parseStorageOpt(opts.StorageOpt, driver); err != nil { 360 return err 361 } 362 363 if driver.options.quota.Size > 0 { 364 // Set container disk quota limit 365 if err := d.quotaCtl.SetQuota(dir, driver.options.quota); err != nil { 366 return err 367 } 368 } 369 } 370 371 if err := idtools.MkdirAndChown(path.Join(dir, diffDirName), 0755, root); err != nil { 372 return err 373 } 374 375 lid := overlayutils.GenerateID(idLength, logger) 376 if err := os.Symlink(path.Join("..", id, diffDirName), path.Join(d.home, linkDir, lid)); err != nil { 377 return err 378 } 379 380 // Write link id to link file 381 if err := ioutil.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil { 382 return err 383 } 384 385 // if no parent directory, done 386 if parent == "" { 387 return nil 388 } 389 390 if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0700, root); err != nil { 391 return err 392 } 393 394 if err := ioutil.WriteFile(path.Join(d.dir(parent), "committed"), []byte{}, 0600); err != nil { 395 return err 396 } 397 398 lower, err := d.getLower(parent) 399 if err != nil { 400 return err 401 } 402 if lower != "" { 403 if err := ioutil.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil { 404 return err 405 } 406 } 407 408 return nil 409 } 410 411 // Parse overlay storage options 412 func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) error { 413 // Read size to set the disk project quota per container 414 for key, val := range storageOpt { 415 key := strings.ToLower(key) 416 switch key { 417 case "size": 418 size, err := units.RAMInBytes(val) 419 if err != nil { 420 return err 421 } 422 driver.options.quota.Size = uint64(size) 423 default: 424 return fmt.Errorf("Unknown option %s", key) 425 } 426 } 427 428 return nil 429 } 430 431 func (d *Driver) getLower(parent string) (string, error) { 432 parentDir := d.dir(parent) 433 434 // Ensure parent exists 435 if _, err := os.Lstat(parentDir); err != nil { 436 return "", err 437 } 438 439 // Read Parent link fileA 440 parentLink, err := ioutil.ReadFile(path.Join(parentDir, "link")) 441 if err != nil { 442 return "", err 443 } 444 lowers := []string{path.Join(linkDir, string(parentLink))} 445 446 parentLower, err := ioutil.ReadFile(path.Join(parentDir, lowerFile)) 447 if err == nil { 448 parentLowers := strings.Split(string(parentLower), ":") 449 lowers = append(lowers, parentLowers...) 450 } 451 if len(lowers) > maxDepth { 452 return "", errors.New("max depth exceeded") 453 } 454 return strings.Join(lowers, ":"), nil 455 } 456 457 func (d *Driver) dir(id string) string { 458 return path.Join(d.home, id) 459 } 460 461 func (d *Driver) getLowerDirs(id string) ([]string, error) { 462 var lowersArray []string 463 lowers, err := ioutil.ReadFile(path.Join(d.dir(id), lowerFile)) 464 if err == nil { 465 for _, s := range strings.Split(string(lowers), ":") { 466 lp, err := os.Readlink(path.Join(d.home, s)) 467 if err != nil { 468 return nil, err 469 } 470 lowersArray = append(lowersArray, path.Clean(path.Join(d.home, linkDir, lp))) 471 } 472 } else if !os.IsNotExist(err) { 473 return nil, err 474 } 475 return lowersArray, nil 476 } 477 478 // Remove cleans the directories that are created for this id. 479 func (d *Driver) Remove(id string) error { 480 if id == "" { 481 return fmt.Errorf("refusing to remove the directories: id is empty") 482 } 483 d.locker.Lock(id) 484 defer d.locker.Unlock(id) 485 dir := d.dir(id) 486 lid, err := ioutil.ReadFile(path.Join(dir, "link")) 487 if err == nil { 488 if len(lid) == 0 { 489 logger.Errorf("refusing to remove empty link for layer %v", id) 490 } else if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil { 491 logger.Debugf("Failed to remove link: %v", err) 492 } 493 } 494 495 if err := system.EnsureRemoveAll(dir); err != nil && !os.IsNotExist(err) { 496 return err 497 } 498 return nil 499 } 500 501 // Get creates and mounts the required file system for the given id and returns the mount path. 502 func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr error) { 503 d.locker.Lock(id) 504 defer d.locker.Unlock(id) 505 dir := d.dir(id) 506 if _, err := os.Stat(dir); err != nil { 507 return nil, err 508 } 509 510 diffDir := path.Join(dir, diffDirName) 511 lowers, err := ioutil.ReadFile(path.Join(dir, lowerFile)) 512 if err != nil { 513 // If no lower, just return diff directory 514 if os.IsNotExist(err) { 515 return containerfs.NewLocalContainerFS(diffDir), nil 516 } 517 return nil, err 518 } 519 520 mergedDir := path.Join(dir, mergedDirName) 521 if count := d.ctr.Increment(mergedDir); count > 1 { 522 return containerfs.NewLocalContainerFS(mergedDir), nil 523 } 524 defer func() { 525 if retErr != nil { 526 if c := d.ctr.Decrement(mergedDir); c <= 0 { 527 if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil { 528 logger.Errorf("error unmounting %v: %v", mergedDir, mntErr) 529 } 530 // Cleanup the created merged directory; see the comment in Put's rmdir 531 if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) { 532 logger.Debugf("Failed to remove %s: %v: %v", id, rmErr, err) 533 } 534 } 535 } 536 }() 537 538 workDir := path.Join(dir, workDirName) 539 splitLowers := strings.Split(string(lowers), ":") 540 absLowers := make([]string, len(splitLowers)) 541 for i, s := range splitLowers { 542 absLowers[i] = path.Join(d.home, s) 543 } 544 var readonly bool 545 if _, err := os.Stat(path.Join(dir, "committed")); err == nil { 546 readonly = true 547 } else if !os.IsNotExist(err) { 548 return nil, err 549 } 550 551 var opts string 552 if readonly { 553 opts = indexOff + "lowerdir=" + diffDir + ":" + strings.Join(absLowers, ":") 554 } else { 555 opts = indexOff + "lowerdir=" + strings.Join(absLowers, ":") + ",upperdir=" + diffDir + ",workdir=" + workDir 556 } 557 558 mountData := label.FormatMountLabel(opts, mountLabel) 559 mount := unix.Mount 560 mountTarget := mergedDir 561 562 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 563 if err != nil { 564 return nil, err 565 } 566 if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { 567 return nil, err 568 } 569 570 pageSize := unix.Getpagesize() 571 572 // Use relative paths and mountFrom when the mount data has exceeded 573 // the page size. The mount syscall fails if the mount data cannot 574 // fit within a page and relative links make the mount data much 575 // smaller at the expense of requiring a fork exec to chroot. 576 if len(mountData) > pageSize { 577 if readonly { 578 opts = indexOff + "lowerdir=" + path.Join(id, diffDirName) + ":" + string(lowers) 579 } else { 580 opts = indexOff + "lowerdir=" + string(lowers) + ",upperdir=" + path.Join(id, diffDirName) + ",workdir=" + path.Join(id, workDirName) 581 } 582 mountData = label.FormatMountLabel(opts, mountLabel) 583 if len(mountData) > pageSize { 584 return nil, fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData)) 585 } 586 587 mount = func(source string, target string, mType string, flags uintptr, label string) error { 588 return mountFrom(d.home, source, target, mType, flags, label) 589 } 590 mountTarget = path.Join(id, mergedDirName) 591 } 592 593 if err := mount("overlay", mountTarget, "overlay", 0, mountData); err != nil { 594 return nil, fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) 595 } 596 597 if !readonly { 598 // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a 599 // user namespace requires this to move a directory from lower to upper. 600 if err := os.Chown(path.Join(workDir, workDirName), rootUID, rootGID); err != nil { 601 return nil, err 602 } 603 } 604 605 return containerfs.NewLocalContainerFS(mergedDir), nil 606 } 607 608 // Put unmounts the mount path created for the give id. 609 // It also removes the 'merged' directory to force the kernel to unmount the 610 // overlay mount in other namespaces. 611 func (d *Driver) Put(id string) error { 612 d.locker.Lock(id) 613 defer d.locker.Unlock(id) 614 dir := d.dir(id) 615 _, err := ioutil.ReadFile(path.Join(dir, lowerFile)) 616 if err != nil { 617 // If no lower, no mount happened and just return directly 618 if os.IsNotExist(err) { 619 return nil 620 } 621 return err 622 } 623 624 mountpoint := path.Join(dir, mergedDirName) 625 if count := d.ctr.Decrement(mountpoint); count > 0 { 626 return nil 627 } 628 if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil { 629 logger.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err) 630 } 631 // Remove the mountpoint here. Removing the mountpoint (in newer kernels) 632 // will cause all other instances of this mount in other mount namespaces 633 // to be unmounted. This is necessary to avoid cases where an overlay mount 634 // that is present in another namespace will cause subsequent mounts 635 // operations to fail with ebusy. We ignore any errors here because this may 636 // fail on older kernels which don't have 637 // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. 638 if err := unix.Rmdir(mountpoint); err != nil && !os.IsNotExist(err) { 639 logger.Debugf("Failed to remove %s overlay: %v", id, err) 640 } 641 return nil 642 } 643 644 // Exists checks to see if the id is already mounted. 645 func (d *Driver) Exists(id string) bool { 646 _, err := os.Stat(d.dir(id)) 647 return err == nil 648 } 649 650 // isParent determines whether the given parent is the direct parent of the 651 // given layer id 652 func (d *Driver) isParent(id, parent string) bool { 653 lowers, err := d.getLowerDirs(id) 654 if err != nil { 655 return false 656 } 657 if parent == "" && len(lowers) > 0 { 658 return false 659 } 660 661 parentDir := d.dir(parent) 662 var ld string 663 if len(lowers) > 0 { 664 ld = filepath.Dir(lowers[0]) 665 } 666 if ld == "" && parent == "" { 667 return true 668 } 669 return ld == parentDir 670 } 671 672 // ApplyDiff applies the new layer into a root 673 func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) { 674 if !d.isParent(id, parent) { 675 return d.naiveDiff.ApplyDiff(id, parent, diff) 676 } 677 678 applyDir := d.getDiffPath(id) 679 680 logger.Debugf("Applying tar in %s", applyDir) 681 // Overlay doesn't need the parent id to apply the diff 682 if err := untar(diff, applyDir, &archive.TarOptions{ 683 UIDMaps: d.uidMaps, 684 GIDMaps: d.gidMaps, 685 WhiteoutFormat: archive.OverlayWhiteoutFormat, 686 InUserNS: rsystem.RunningInUserNS(), 687 }); err != nil { 688 return 0, err 689 } 690 691 return directory.Size(context.TODO(), applyDir) 692 } 693 694 func (d *Driver) getDiffPath(id string) string { 695 dir := d.dir(id) 696 697 return path.Join(dir, diffDirName) 698 } 699 700 // DiffSize calculates the changes between the specified id 701 // and its parent and returns the size in bytes of the changes 702 // relative to its base filesystem directory. 703 func (d *Driver) DiffSize(id, parent string) (size int64, err error) { 704 if useNaiveDiff(d.home) || !d.isParent(id, parent) { 705 return d.naiveDiff.DiffSize(id, parent) 706 } 707 return directory.Size(context.TODO(), d.getDiffPath(id)) 708 } 709 710 // Diff produces an archive of the changes between the specified 711 // layer and its parent layer which may be "". 712 func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) { 713 if useNaiveDiff(d.home) || !d.isParent(id, parent) { 714 return d.naiveDiff.Diff(id, parent) 715 } 716 717 diffPath := d.getDiffPath(id) 718 logger.Debugf("Tar with options on %s", diffPath) 719 return archive.TarWithOptions(diffPath, &archive.TarOptions{ 720 Compression: archive.Uncompressed, 721 UIDMaps: d.uidMaps, 722 GIDMaps: d.gidMaps, 723 WhiteoutFormat: archive.OverlayWhiteoutFormat, 724 }) 725 } 726 727 // Changes produces a list of changes between the specified layer and its 728 // parent layer. If parent is "", then all changes will be ADD changes. 729 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { 730 return d.naiveDiff.Changes(id, parent) 731 }