github.com/cvmfs/docker-graphdriver@v0.0.0-20181206110523-155ec6df0521/plugins/overlay2_cvmfs/overlay2/overlay.go (about) 1 // +build linux 2 3 package overlay2 4 5 import ( 6 "bufio" 7 "errors" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path" 14 "path/filepath" 15 "strconv" 16 "strings" 17 "sync" 18 "syscall" 19 20 "github.com/Sirupsen/logrus" 21 22 "github.com/docker/docker/daemon/graphdriver" 23 "github.com/docker/docker/daemon/graphdriver/overlayutils" 24 "github.com/docker/docker/daemon/graphdriver/quota" 25 "github.com/docker/docker/pkg/archive" 26 "github.com/docker/docker/pkg/chrootarchive" 27 "github.com/docker/docker/pkg/directory" 28 "github.com/docker/docker/pkg/fsutils" 29 "github.com/docker/docker/pkg/idtools" 30 "github.com/docker/docker/pkg/mount" 31 "github.com/docker/docker/pkg/parsers" 32 "github.com/docker/docker/pkg/parsers/kernel" 33 "github.com/docker/go-units" 34 35 "github.com/cvmfs/docker-graphdriver/plugins/util" 36 "github.com/opencontainers/selinux/go-selinux/label" 37 ) 38 39 var ( 40 // untar defines the untar method 41 untar = chrootarchive.UntarUncompressed 42 // The colon is used to separate lower dirs but it can also be part of the 43 // lower dir path names 44 sanitizer = strings.NewReplacer(`:`, `\:`) 45 ) 46 47 // This backend uses the overlay union filesystem for containers 48 // with diff directories for each layer. 49 50 // This version of the overlay driver requires at least kernel 51 // 4.0.0 in order to support mounting multiple diff directories. 52 53 // Each container/image has at least a "diff" directory and "link" file. 54 // If there is also a "lower" file when there are diff layers 55 // below as well as "merged" and "work" directories. The "diff" directory 56 // has the upper layer of the overlay and is used to capture any 57 // changes to the layer. The "lower" file contains all the lower layer 58 // mounts separated by ":" and ordered from uppermost to lowermost 59 // layers. The overlay itself is mounted in the "merged" directory, 60 // and the "work" dir is needed for overlay to work. 61 62 // The "link" file for each layer contains a unique string for the layer. 63 // Under the "l" directory at the root there will be a symbolic link 64 // with that unique string pointing the "diff" directory for the layer. 65 // The symbolic links are used to reference lower layers in the "lower" 66 // file and on mount. The links are used to shorten the total length 67 // of a layer reference without requiring changes to the layer identifier 68 // or root directory. Mounts are always done relative to root and 69 // referencing the symbolic links in order to ensure the number of 70 // lower directories can fit in a single page for making the mount 71 // syscall. A hard upper limit of 128 lower layers is enforced to ensure 72 // that mounts do not fail due to length. 73 74 const ( 75 driverName = "overlay2" 76 linkDir = "l" 77 lowerFile = "lower" 78 maxDepth = 128 79 80 // idLength represents the number of random characters 81 // which can be used to create the unique link identifer 82 // for every layer. If this value is too long then the 83 // page size limit for the mount command may be exceeded. 84 // The idLength should be selected such that following equation 85 // is true (512 is a buffer for label metadata). 86 // ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512) 87 idLength = 26 88 ) 89 90 type overlayOptions struct { 91 overrideKernelCheck bool 92 quota quota.Quota 93 } 94 95 // Driver contains information about the home directory and the list of active 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 cvmfsManager util.ICvmfsManager 106 107 cvmfsMountMethod string 108 cvmfsMountPath string 109 cvmfsDefaultRepo string 110 } 111 112 var ( 113 backingFs = "<unknown>" 114 projectQuotaSupported = false 115 116 useNaiveDiffLock sync.Once 117 useNaiveDiffOnly bool 118 ) 119 120 func init() { 121 graphdriver.Register(driverName, Init) 122 } 123 124 // Init returns the a native diff driver for overlay filesystem. 125 // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. 126 // If an overlay filesystem is not supported over an existing filesystem then error graphdriver.ErrIncompatibleFS is returned. 127 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 128 os.MkdirAll(home, os.ModePerm) 129 130 opts, err := parseOptions(options) 131 if err != nil { 132 return nil, err 133 } 134 135 if err := supportsOverlay(); err != nil { 136 return nil, graphdriver.ErrNotSupported 137 } 138 139 // require kernel 4.0.0 to ensure multiple lower dirs are supported 140 v, err := kernel.GetKernelVersion() 141 if err != nil { 142 return nil, err 143 } 144 if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 0, Minor: 0}) < 0 { 145 if !opts.overrideKernelCheck { 146 return nil, graphdriver.ErrNotSupported 147 } 148 logrus.Warn("Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update") 149 } 150 151 fsMagic, err := graphdriver.GetFSMagic(home) 152 if err != nil { 153 return nil, err 154 } 155 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 156 backingFs = fsName 157 } 158 159 // check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs 160 switch fsMagic { 161 case graphdriver.FsMagicBtrfs, graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs: 162 logrus.Errorf("'overlay2' is not supported over %s", backingFs) 163 return nil, graphdriver.ErrIncompatibleFS 164 } 165 166 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 167 if err != nil { 168 return nil, err 169 } 170 // Create the driver home dir 171 if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 172 return nil, err 173 } 174 175 // if err := mount.MakePrivate(home); err != nil { 176 // return nil, err 177 // } 178 179 supportsDType, err := fsutils.SupportsDType(home) 180 if err != nil { 181 return nil, err 182 } 183 if !supportsDType { 184 // not a fatal error until v1.16 (#27443) 185 logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs)) 186 } 187 188 d := &Driver{ 189 home: home, 190 uidMaps: uidMaps, 191 gidMaps: gidMaps, 192 ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), 193 supportsDType: supportsDType, 194 } 195 196 if err := d.configureCvmfs(options); err != nil { 197 return nil, err 198 } 199 d.cvmfsManager = util.NewCvmfsManager(d.cvmfsMountPath, d.cvmfsMountMethod) 200 201 d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps) 202 203 if backingFs == "xfs" { 204 // Try to enable project quota support over xfs. 205 if d.quotaCtl, err = quota.NewControl(home); err == nil { 206 projectQuotaSupported = true 207 } 208 } 209 210 logrus.Debugf("backingFs=%s, projectQuotaSupported=%v", backingFs, projectQuotaSupported) 211 212 return d, nil 213 } 214 215 func parseOptions(options []string) (*overlayOptions, error) { 216 o := &overlayOptions{} 217 for _, option := range options { 218 key, val, err := parsers.ParseKeyValueOpt(option) 219 if err != nil { 220 return nil, err 221 } 222 key = strings.ToLower(key) 223 switch key { 224 case "overlay2.override_kernel_check": 225 o.overrideKernelCheck, err = strconv.ParseBool(val) 226 if err != nil { 227 return nil, err 228 } 229 230 case "cvmfsmountmethod": 231 default: 232 return nil, fmt.Errorf("overlay2: Unknown option %s\n", key) 233 } 234 } 235 return o, nil 236 } 237 238 func supportsOverlay() error { 239 // We can try to modprobe overlay first before looking at 240 // proc/filesystems for when overlay is supported 241 exec.Command("modprobe", "overlay").Run() 242 243 f, err := os.Open("/proc/filesystems") 244 if err != nil { 245 return err 246 } 247 defer f.Close() 248 249 s := bufio.NewScanner(f) 250 for s.Scan() { 251 if s.Text() == "nodev\toverlay" { 252 return nil 253 } 254 } 255 logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 256 return graphdriver.ErrNotSupported 257 } 258 259 func useNaiveDiff(home string) bool { 260 useNaiveDiffLock.Do(func() { 261 if err := hasOpaqueCopyUpBug(home); err != nil { 262 logrus.Warnf("Not using native diff for overlay2: %v", err) 263 useNaiveDiffOnly = true 264 } 265 }) 266 return useNaiveDiffOnly 267 } 268 269 func (d *Driver) String() string { 270 return driverName 271 } 272 273 // Status returns current driver information in a two dimensional string array. 274 // Output contains "Backing Filesystem" used in this implementation. 275 func (d *Driver) Status() [][2]string { 276 return [][2]string{ 277 {"Backing Filesystem", backingFs}, 278 {"Supports d_type", strconv.FormatBool(d.supportsDType)}, 279 {"Native Overlay Diff", strconv.FormatBool(!useNaiveDiff(d.home))}, 280 } 281 } 282 283 // GetMetadata returns meta data about the overlay driver such as 284 // LowerDir, UpperDir, WorkDir and MergeDir used to store data. 285 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 286 dir := d.dir(id) 287 if _, err := os.Stat(dir); err != nil { 288 return nil, err 289 } 290 291 metadata := map[string]string{ 292 "WorkDir": path.Join(dir, "work"), 293 "MergedDir": path.Join(dir, "merged"), 294 "UpperDir": path.Join(dir, "diff"), 295 } 296 297 lowerDirs, err := d.getLowerDirs(id) 298 if err != nil { 299 return nil, err 300 } 301 if len(lowerDirs) > 0 { 302 metadata["LowerDir"] = strings.Join(lowerDirs, ":") 303 } 304 305 return metadata, nil 306 } 307 308 // Cleanup any state created by overlay which should be cleaned when daemon 309 // is being shutdown. For now, we just have to unmount the bind mounted 310 // we had created. 311 func (d *Driver) Cleanup() error { 312 if d.cvmfsMountMethod == "internal" { 313 defer d.cvmfsManager.PutAll() 314 } 315 316 return mount.Unmount(d.home) 317 } 318 319 // CreateReadWrite creates a layer that is writable for use as a container 320 // file system. 321 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { 322 return d.Create(id, parent, opts) 323 } 324 325 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. 326 // The parent filesystem is used to configure these directories for the overlay. 327 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { 328 if opts != nil && len(opts.StorageOpt) != 0 && !projectQuotaSupported { 329 return fmt.Errorf("--storage-opt is supported only for overlay over xfs with 'pquota' mount option") 330 } 331 332 dir := d.dir(id) 333 334 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 335 if err != nil { 336 return err 337 } 338 if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { 339 return err 340 } 341 if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { 342 return err 343 } 344 345 defer func() { 346 // Clean up on failure 347 if retErr != nil { 348 os.RemoveAll(dir) 349 } 350 }() 351 352 if opts != nil && len(opts.StorageOpt) > 0 { 353 driver := &Driver{} 354 if err := d.parseStorageOpt(opts.StorageOpt, driver); err != nil { 355 return err 356 } 357 358 if driver.options.quota.Size > 0 { 359 // Set container disk quota limit 360 if err := d.quotaCtl.SetQuota(dir, driver.options.quota); err != nil { 361 return err 362 } 363 } 364 } 365 366 if err := idtools.MkdirAs(path.Join(dir, "diff"), 0755, rootUID, rootGID); err != nil { 367 return err 368 } 369 370 lid := generateID(idLength) 371 if err := os.Symlink(path.Join("..", id, "diff"), path.Join(d.home, linkDir, lid)); err != nil { 372 return err 373 } 374 375 // Write link id to link file 376 if err := ioutil.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil { 377 return err 378 } 379 380 // if no parent directory, done 381 if parent == "" { 382 return nil 383 } 384 385 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { 386 return err 387 } 388 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { 389 return err 390 } 391 392 lower, err := d.getLower(parent) 393 if err != nil { 394 return err 395 } 396 397 if util.IsThinImageLayer(d.getDiffPath(parent)) { 398 if err := ioutil.WriteFile(path.Join(dir, "thin_parent"), []byte(parent), 0644); err != nil { 399 return err 400 } 401 } 402 403 if lower != "" { 404 if err := ioutil.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil { 405 return err 406 } 407 } 408 409 return nil 410 } 411 412 // Parse overlay storage options 413 func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) error { 414 // Read size to set the disk project quota per container 415 for key, val := range storageOpt { 416 key := strings.ToLower(key) 417 switch key { 418 case "size": 419 size, err := units.RAMInBytes(val) 420 if err != nil { 421 return err 422 } 423 driver.options.quota.Size = uint64(size) 424 default: 425 return fmt.Errorf("Unknown option %s", key) 426 } 427 } 428 429 return nil 430 } 431 432 func (d *Driver) getLower(parent string) (string, error) { 433 parentDir := d.dir(parent) 434 435 // Ensure parent exists 436 if _, err := os.Lstat(parentDir); err != nil { 437 return "", err 438 } 439 440 // Read Parent link fileA 441 parentLink, err := ioutil.ReadFile(path.Join(parentDir, "link")) 442 if err != nil { 443 return "", err 444 } 445 446 lowers := make([]string, 0) 447 if !util.IsThinImageLayer(path.Join(parentDir, "diff")) { 448 lowers = append(lowers, path.Join(linkDir, string(parentLink))) 449 } 450 451 parentLower, err := ioutil.ReadFile(path.Join(parentDir, lowerFile)) 452 if err == nil { 453 parentLowers := strings.Split(string(parentLower), ":") 454 lowers = append(lowers, parentLowers...) 455 } 456 if len(lowers) > maxDepth { 457 return "", errors.New("max depth exceeded") 458 } 459 return strings.Join(lowers, ":"), nil 460 } 461 462 func (d *Driver) dir(id string) string { 463 return path.Join(d.home, id) 464 } 465 466 func (d *Driver) getLowerDirs(id string) ([]string, error) { 467 var lowersArray []string 468 lowers, err := ioutil.ReadFile(path.Join(d.dir(id), lowerFile)) 469 if err == nil { 470 for _, s := range strings.Split(string(lowers), ":") { 471 lp, err := os.Readlink(path.Join(d.home, s)) 472 if err != nil { 473 return nil, err 474 } 475 lowersArray = append(lowersArray, path.Clean(path.Join(d.home, linkDir, lp))) 476 } 477 } else if !os.IsNotExist(err) { 478 return nil, err 479 } 480 return lowersArray, nil 481 } 482 483 // Remove cleans the directories that are created for this id. 484 func (d *Driver) Remove(id string) error { 485 dir := d.dir(id) 486 487 if util.IsThinImageLayer(path.Join(dir, "diff")) { 488 lowers, _ := ioutil.ReadFile(path.Join(dir, "lower")) 489 for _, lowerLink := range strings.Split(string(lowers), ":") { 490 link := path.Join(d.home, lowerLink) 491 fmt.Printf("Removing: %s\n", link) 492 os.Remove(link) 493 } 494 } 495 496 lid, err := ioutil.ReadFile(path.Join(dir, "link")) 497 if err == nil { 498 if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil { 499 fmt.Printf("Failed to remove link: %v", err) 500 } 501 } 502 503 if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) { 504 return err 505 } 506 return nil 507 } 508 509 // Get creates and mounts the required file system for the given id and returns the mount path. 510 func (d *Driver) Get(id string, mountLabel string) (s string, err error) { 511 dir := d.dir(id) 512 if _, err := os.Stat(dir); err != nil { 513 return "", err 514 } 515 516 diffDir := path.Join(dir, "diff") 517 518 if util.IsThinImageLayer(diffDir) { 519 return diffDir, nil 520 } 521 522 lowers, err := ioutil.ReadFile(path.Join(dir, lowerFile)) 523 if err != nil { 524 // If no lower, just return diff directory 525 if os.IsNotExist(err) { 526 return diffDir, nil 527 } 528 return "", err 529 } 530 531 if thinParent := d.getThinParent(id); d.cvmfsMountMethod == "internal" && thinParent != "" { 532 f := path.Join(d.getDiffPath(thinParent), "thin.json") 533 t := util.ReadThinFile(f) 534 d.cvmfsManager.GetLayers(t.Layers...) 535 } 536 537 mergedDir := path.Join(dir, "merged") 538 if count := d.ctr.Increment(mergedDir); count > 1 { 539 return mergedDir, nil 540 } 541 defer func() { 542 if err != nil { 543 if c := d.ctr.Decrement(mergedDir); c <= 0 { 544 syscall.Unmount(mergedDir, 0) 545 } 546 } 547 }() 548 549 workDir := path.Join(dir, "work") 550 splitLowers := strings.Split(string(lowers), ":") 551 absLowers := make([]string, len(splitLowers)) 552 553 for i, s := range splitLowers { 554 absLowers[i] = sanitizer.Replace(path.Join(d.home, s)) 555 } 556 557 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), path.Join(dir, "diff"), path.Join(dir, "work")) 558 mountData := label.FormatMountLabel(opts, mountLabel) 559 mount := syscall.Mount 560 mountTarget := mergedDir 561 562 pageSize := syscall.Getpagesize() 563 564 // Go can return a larger page size than supported by the system 565 // as of go 1.7. This will be fixed in 1.8 and this block can be 566 // removed when building with 1.8. 567 // See https://github.com/golang/go/commit/1b9499b06989d2831e5b156161d6c07642926ee1 568 // See https://github.com/docker/docker/issues/27384 569 if pageSize > 4096 { 570 pageSize = 4096 571 } 572 573 // Use relative paths and mountFrom when the mount data has exceeded 574 // the page size. The mount syscall fails if the mount data cannot 575 // fit within a page and relative links make the mount data much 576 // smaller at the expense of requiring a fork exec to chroot. 577 if len(mountData) > pageSize { 578 opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work")) 579 mountData = label.FormatMountLabel(opts, mountLabel) 580 if len(mountData) > pageSize { 581 return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData)) 582 } 583 584 mount = func(source string, target string, mType string, flags uintptr, label string) error { 585 return mountFrom(d.home, source, target, mType, flags, label) 586 } 587 mountTarget = path.Join(id, "merged") 588 } 589 590 if err := mount("overlay", mountTarget, "overlay", 0, mountData); err != nil { 591 return "", fmt.Errorf("error creating overlay mount to %s: %v [%s, %s]", 592 mergedDir, err, mountTarget, mountData) 593 } 594 595 // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a 596 // user namespace requires this to move a directory from lower to upper. 597 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 598 if err != nil { 599 return "", err 600 } 601 602 if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { 603 return "", err 604 } 605 606 return mergedDir, nil 607 } 608 609 // Put unmounts the mount path created for the give id. 610 func (d *Driver) Put(id string) error { 611 p := d.getDiffPath(id) 612 if util.IsThinImageLayer(p) && d.cvmfsMountMethod == "internal" { 613 f := path.Join(p, "thin.json") 614 t := util.ReadThinFile(f) 615 d.cvmfsManager.PutLayers(t.Layers...) 616 } 617 618 mountpoint := path.Join(d.dir(id), "merged") 619 if count := d.ctr.Decrement(mountpoint); count > 0 { 620 return nil 621 } 622 if err := syscall.Unmount(mountpoint, 0); err != nil { 623 fmt.Printf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err) 624 } 625 return nil 626 } 627 628 // Exists checks to see if the id is already mounted. 629 func (d *Driver) Exists(id string) bool { 630 _, err := os.Stat(d.dir(id)) 631 return err == nil 632 } 633 634 // isParent returns if the passed in parent is the direct parent of the passed in layer 635 func (d *Driver) isParent(id, parent string) bool { 636 thin_parent_id := d.getThinParent(id) 637 638 if thin_parent_id == parent { 639 return true 640 } else if thin_parent_id != "" { 641 return false 642 } 643 644 lowers, err := d.getLowerDirs(id) 645 if err != nil { 646 return false 647 } 648 if parent == "" && len(lowers) > 0 { 649 return false 650 } 651 652 parentDir := d.dir(parent) 653 var ld string 654 if len(lowers) > 0 { 655 ld = filepath.Dir(lowers[0]) 656 } 657 if ld == "" && parent == "" { 658 return true 659 } 660 return ld == parentDir 661 } 662 663 // ApplyDiff applies the new layer into a root 664 func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) { 665 if !d.isParent(id, parent) { 666 return d.naiveDiff.ApplyDiff(id, parent, diff) 667 } 668 669 applyDir := d.getDiffPath(id) 670 671 logrus.Debugf("Applying tar in %s", applyDir) 672 // Overlay doesn't need the parent id to apply the diff 673 if err := untar(diff, applyDir, &archive.TarOptions{ 674 UIDMaps: d.uidMaps, 675 GIDMaps: d.gidMaps, 676 WhiteoutFormat: archive.OverlayWhiteoutFormat, 677 }); err != nil { 678 return 0, err 679 } 680 681 // TODO: check if thin layer had any regular parents 682 if util.IsThinImageLayer(applyDir) { 683 thin_layers := util.GetNestedLayerIDs(applyDir) 684 lowers := make([]string, len(thin_layers)) 685 686 for i, layer := range thin_layers { 687 lid := generateID(idLength) 688 689 _, location := util.ParseThinUrl(layer.Url) 690 repo, folder := util.ParseCvmfsLocation(location) 691 cvmfsPath := path.Join("..", "cvmfs", repo, folder) 692 693 os.Symlink(cvmfsPath, path.Join(d.home, linkDir, lid)) 694 695 lowers[i] = path.Join(linkDir, lid) 696 } 697 698 newLowers := strings.Join(lowers, ":") 699 lowerFilePath := path.Join(d.dir(id), lowerFile) 700 701 if len(lowers) > 0 { 702 ioutil.WriteFile(lowerFilePath, []byte(newLowers), 0666) 703 } 704 } 705 706 return directory.Size(applyDir) 707 } 708 709 func (d *Driver) getDiffPath(id string) string { 710 dir := d.dir(id) 711 712 return path.Join(dir, "diff") 713 } 714 715 // DiffSize calculates the changes between the specified id 716 // and its parent and returns the size in bytes of the changes 717 // relative to its base filesystem directory. 718 func (d *Driver) DiffSize(id, parent string) (size int64, err error) { 719 if useNaiveDiff(d.home) || !d.isParent(id, parent) { 720 return d.naiveDiff.DiffSize(id, parent) 721 } 722 return directory.Size(d.getDiffPath(id)) 723 } 724 725 // Diff produces an archive of the changes between the specified 726 // layer and its parent layer which may be "". 727 func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) { 728 var newThinLayer string 729 var isThin bool = false 730 731 diffPath := d.getDiffPath(id) 732 exportPath := diffPath 733 logrus.Debugf("Tar with options on %s", diffPath) 734 735 var thin_parent_id string 736 if id := d.getThinParent(parent); id != "" { 737 thin_parent_id = id 738 isThin = true 739 } 740 741 parent_thin_path := d.dir(thin_parent_id) 742 743 if isThin { 744 thin := util.ReadThinFile(path.Join(parent_thin_path, "diff", "thin.json")) 745 newLayer, _ := d.cvmfsManager.UploadNewLayer(diffPath) 746 thin.AddLayer(newLayer) 747 newThinLayer, _ = util.WriteThinFile(thin) 748 exportPath = newThinLayer 749 } 750 751 if useNaiveDiff(d.home) || !d.isParent(id, parent) { 752 return d.naiveDiff.Diff(id, parent) 753 } 754 755 return archive.TarWithOptions(exportPath, &archive.TarOptions{ 756 Compression: archive.Uncompressed, 757 UIDMaps: d.uidMaps, 758 GIDMaps: d.gidMaps, 759 WhiteoutFormat: archive.OverlayWhiteoutFormat, 760 }) 761 } 762 763 // Changes produces a list of changes between the specified layer 764 // and its parent layer. If parent is "", then all changes will be ADD changes. 765 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { 766 if useNaiveDiff(d.home) || !d.isParent(id, parent) { 767 return d.naiveDiff.Changes(id, parent) 768 } 769 // Overlay doesn't have snapshots, so we need to get changes from all parent 770 // layers. 771 diffPath := d.getDiffPath(id) 772 layers, err := d.getLowerDirs(id) 773 if err != nil { 774 return nil, err 775 } 776 777 return archive.OverlayChanges(layers, diffPath) 778 } 779 780 func (d *Driver) configureCvmfs(options []string) error { 781 m, err := util.ParseOptions(options) 782 783 if err != nil { 784 return err 785 } 786 787 if method, ok := m["cvmfsMountMethod"]; !ok { 788 d.cvmfsMountMethod = "internal" 789 } else { 790 d.cvmfsMountMethod = method 791 } 792 793 d.cvmfsMountPath = path.Join(d.home, "cvmfs") 794 os.MkdirAll(d.cvmfsMountPath, os.ModePerm) 795 796 return nil 797 } 798 799 func (d *Driver) getThinParent(id string) (thinParent string) { 800 dir := d.dir(id) 801 f := path.Join(dir, "thin_parent") 802 803 if _, err := os.Stat(f); os.IsNotExist(err) { 804 return "" 805 } 806 807 out, _ := ioutil.ReadFile(f) 808 return string(out) 809 }