github.com/anuvu/storage@v1.12.13/drivers/overlay/overlay.go (about) 1 // +build linux 2 3 package overlay 4 5 import ( 6 "bytes" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path" 13 "path/filepath" 14 "strconv" 15 "strings" 16 "sync" 17 "syscall" 18 19 graphdriver "github.com/containers/storage/drivers" 20 "github.com/containers/storage/drivers/overlayutils" 21 "github.com/containers/storage/drivers/quota" 22 "github.com/containers/storage/pkg/archive" 23 "github.com/containers/storage/pkg/chrootarchive" 24 "github.com/containers/storage/pkg/directory" 25 "github.com/containers/storage/pkg/fsutils" 26 "github.com/containers/storage/pkg/idtools" 27 "github.com/containers/storage/pkg/locker" 28 "github.com/containers/storage/pkg/mount" 29 "github.com/containers/storage/pkg/ostree" 30 "github.com/containers/storage/pkg/parsers" 31 "github.com/containers/storage/pkg/system" 32 units "github.com/docker/go-units" 33 rsystem "github.com/opencontainers/runc/libcontainer/system" 34 "github.com/opencontainers/selinux/go-selinux/label" 35 "github.com/pkg/errors" 36 "github.com/sirupsen/logrus" 37 "golang.org/x/sys/unix" 38 ) 39 40 var ( 41 // untar defines the untar method 42 untar = chrootarchive.UntarUncompressed 43 ) 44 45 // This backend uses the overlay union filesystem for containers 46 // with diff directories for each layer. 47 48 // This version of the overlay driver requires at least kernel 49 // 4.0.0 in order to support mounting multiple diff directories. 50 51 // Each container/image has at least a "diff" directory and "link" file. 52 // If there is also a "lower" file when there are diff layers 53 // below as well as "merged" and "work" directories. The "diff" directory 54 // has the upper layer of the overlay and is used to capture any 55 // changes to the layer. The "lower" file contains all the lower layer 56 // mounts separated by ":" and ordered from uppermost to lowermost 57 // layers. The overlay itself is mounted in the "merged" directory, 58 // and the "work" dir is needed for overlay to work. 59 60 // The "link" file for each layer contains a unique string for the layer. 61 // Under the "l" directory at the root there will be a symbolic link 62 // with that unique string pointing the "diff" directory for the layer. 63 // The symbolic links are used to reference lower layers in the "lower" 64 // file and on mount. The links are used to shorten the total length 65 // of a layer reference without requiring changes to the layer identifier 66 // or root directory. Mounts are always done relative to root and 67 // referencing the symbolic links in order to ensure the number of 68 // lower directories can fit in a single page for making the mount 69 // syscall. A hard upper limit of 128 lower layers is enforced to ensure 70 // that mounts do not fail due to length. 71 72 const ( 73 linkDir = "l" 74 lowerFile = "lower" 75 maxDepth = 128 76 77 // idLength represents the number of random characters 78 // which can be used to create the unique link identifer 79 // for every layer. If this value is too long then the 80 // page size limit for the mount command may be exceeded. 81 // The idLength should be selected such that following equation 82 // is true (512 is a buffer for label metadata). 83 // ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512) 84 idLength = 26 85 ) 86 87 type overlayOptions struct { 88 imageStores []string 89 quota quota.Quota 90 mountProgram string 91 ostreeRepo string 92 skipMountHome bool 93 mountOptions string 94 } 95 96 // Driver contains information about the home directory and the list of active mounts that are created using this driver. 97 type Driver struct { 98 name string 99 home string 100 runhome string 101 uidMaps []idtools.IDMap 102 gidMaps []idtools.IDMap 103 ctr *graphdriver.RefCounter 104 quotaCtl *quota.Control 105 options overlayOptions 106 naiveDiff graphdriver.DiffDriver 107 supportsDType bool 108 usingMetacopy bool 109 locker *locker.Locker 110 convert map[string]bool 111 } 112 113 var ( 114 backingFs = "<unknown>" 115 projectQuotaSupported = false 116 117 useNaiveDiffLock sync.Once 118 useNaiveDiffOnly bool 119 ) 120 121 func init() { 122 graphdriver.Register("overlay", Init) 123 graphdriver.Register("overlay2", Init) 124 } 125 126 // Init returns the a native diff driver for overlay filesystem. 127 // If overlay filesystem is not supported on the host, a wrapped graphdriver.ErrNotSupported is returned as error. 128 // If an overlay filesystem is not supported over an existing filesystem then a wrapped graphdriver.ErrIncompatibleFS is returned. 129 func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) { 130 opts, err := parseOptions(options.DriverOptions) 131 if err != nil { 132 return nil, err 133 } 134 135 fsMagic, err := graphdriver.GetFSMagic(home) 136 if err != nil { 137 return nil, err 138 } 139 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 140 backingFs = fsName 141 } 142 143 // check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs 144 if opts.mountProgram == "" { 145 switch fsMagic { 146 case graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs: 147 logrus.Errorf("'overlay' is not supported over %s", backingFs) 148 return nil, errors.Wrapf(graphdriver.ErrIncompatibleFS, "'overlay' is not supported over %s", backingFs) 149 } 150 } 151 152 rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps) 153 if err != nil { 154 return nil, err 155 } 156 157 // Create the driver home dir 158 if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 159 return nil, err 160 } 161 runhome := filepath.Join(options.RunRoot, filepath.Base(home)) 162 if err := idtools.MkdirAllAs(runhome, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 163 return nil, err 164 } 165 166 var usingMetacopy bool 167 var supportsDType bool 168 if opts.mountProgram != "" { 169 supportsDType = true 170 } else { 171 feature := "overlay" 172 overlayCacheResult, overlayCacheText, err := cachedFeatureCheck(runhome, feature) 173 if err == nil { 174 if overlayCacheResult { 175 logrus.Debugf("cached value indicated that overlay is supported") 176 } else { 177 logrus.Debugf("cached value indicated that overlay is not supported") 178 } 179 supportsDType = overlayCacheResult 180 if !supportsDType { 181 return nil, errors.New(overlayCacheText) 182 } 183 } else { 184 supportsDType, err = supportsOverlay(home, fsMagic, rootUID, rootGID) 185 if err != nil { 186 os.Remove(filepath.Join(home, linkDir)) 187 os.Remove(home) 188 patherr, ok := err.(*os.PathError) 189 if ok && patherr.Err == syscall.ENOSPC { 190 return nil, err 191 } 192 err = errors.Wrap(err, "kernel does not support overlay fs") 193 if err2 := cachedFeatureRecord(runhome, feature, false, err.Error()); err2 != nil { 194 return nil, errors.Wrapf(err2, "error recording overlay not being supported (%v)", err) 195 } 196 return nil, err 197 } 198 if err = cachedFeatureRecord(runhome, feature, supportsDType, ""); err != nil { 199 return nil, errors.Wrap(err, "error recording overlay support status") 200 } 201 } 202 203 feature = fmt.Sprintf("metacopy(%s)", opts.mountOptions) 204 metacopyCacheResult, _, err := cachedFeatureCheck(runhome, feature) 205 if err == nil { 206 if metacopyCacheResult { 207 logrus.Debugf("cached value indicated that metacopy is being used") 208 } else { 209 logrus.Debugf("cached value indicated that metacopy is not being used") 210 } 211 usingMetacopy = metacopyCacheResult 212 } else { 213 usingMetacopy, err = doesMetacopy(home, opts.mountOptions) 214 if err == nil { 215 if usingMetacopy { 216 logrus.Debugf("overlay test mount indicated that metacopy is being used") 217 } else { 218 logrus.Debugf("overlay test mount indicated that metacopy is not being used") 219 } 220 if err = cachedFeatureRecord(runhome, feature, usingMetacopy, ""); err != nil { 221 return nil, errors.Wrap(err, "error recording metacopy-being-used status") 222 } 223 } else { 224 logrus.Warnf("overlay test mount did not indicate whether or not metacopy is being used: %v", err) 225 return nil, err 226 } 227 } 228 } 229 230 if !opts.skipMountHome { 231 if err := mount.MakePrivate(home); err != nil { 232 return nil, err 233 } 234 } 235 236 if opts.ostreeRepo != "" { 237 if err := ostree.CreateOSTreeRepository(opts.ostreeRepo, rootUID, rootGID); err != nil { 238 return nil, err 239 } 240 } 241 242 d := &Driver{ 243 name: "overlay", 244 home: home, 245 runhome: runhome, 246 uidMaps: options.UIDMaps, 247 gidMaps: options.GIDMaps, 248 ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), 249 supportsDType: supportsDType, 250 usingMetacopy: usingMetacopy, 251 locker: locker.New(), 252 options: *opts, 253 convert: make(map[string]bool), 254 } 255 256 d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, d) 257 258 if backingFs == "xfs" { 259 // Try to enable project quota support over xfs. 260 if d.quotaCtl, err = quota.NewControl(home); err == nil { 261 projectQuotaSupported = true 262 } else if opts.quota.Size > 0 { 263 return nil, fmt.Errorf("Storage option overlay.size not supported. Filesystem does not support Project Quota: %v", err) 264 } 265 } else if opts.quota.Size > 0 { 266 // if xfs is not the backing fs then error out if the storage-opt overlay.size is used. 267 return nil, fmt.Errorf("Storage option overlay.size only supported for backingFS XFS. Found %v", backingFs) 268 } 269 270 logrus.Debugf("backingFs=%s, projectQuotaSupported=%v, useNativeDiff=%v, usingMetacopy=%v", backingFs, projectQuotaSupported, !d.useNaiveDiff(), d.usingMetacopy) 271 272 return d, nil 273 } 274 275 func parseOptions(options []string) (*overlayOptions, error) { 276 o := &overlayOptions{} 277 for _, option := range options { 278 key, val, err := parsers.ParseKeyValueOpt(option) 279 if err != nil { 280 return nil, err 281 } 282 key = strings.ToLower(key) 283 switch key { 284 case ".override_kernel_check", "overlay.override_kernel_check", "overlay2.override_kernel_check": 285 logrus.Warnf("overlay: override_kernel_check option was specified, but is no longer necessary") 286 case ".mountopt", "overlay.mountopt", "overlay2.mountopt": 287 o.mountOptions = val 288 case ".size", "overlay.size", "overlay2.size": 289 logrus.Debugf("overlay: size=%s", val) 290 size, err := units.RAMInBytes(val) 291 if err != nil { 292 return nil, err 293 } 294 o.quota.Size = uint64(size) 295 case ".imagestore", "overlay.imagestore", "overlay2.imagestore": 296 logrus.Debugf("overlay: imagestore=%s", val) 297 // Additional read only image stores to use for lower paths 298 for _, store := range strings.Split(val, ",") { 299 store = filepath.Clean(store) 300 if !filepath.IsAbs(store) { 301 return nil, fmt.Errorf("overlay: image path %q is not absolute. Can not be relative", store) 302 } 303 st, err := os.Stat(store) 304 if err != nil { 305 return nil, fmt.Errorf("overlay: can't stat imageStore dir %s: %v", store, err) 306 } 307 if !st.IsDir() { 308 return nil, fmt.Errorf("overlay: image path %q must be a directory", store) 309 } 310 o.imageStores = append(o.imageStores, store) 311 } 312 case ".mount_program", "overlay.mount_program", "overlay2.mount_program": 313 logrus.Debugf("overlay: mount_program=%s", val) 314 _, err := os.Stat(val) 315 if err != nil { 316 return nil, fmt.Errorf("overlay: can't stat program %s: %v", val, err) 317 } 318 o.mountProgram = val 319 case "overlay2.ostree_repo", "overlay.ostree_repo", ".ostree_repo": 320 logrus.Debugf("overlay: ostree_repo=%s", val) 321 if !ostree.OstreeSupport() { 322 return nil, fmt.Errorf("overlay: ostree_repo specified but support for ostree is missing") 323 } 324 o.ostreeRepo = val 325 case "overlay2.skip_mount_home", "overlay.skip_mount_home", ".skip_mount_home": 326 logrus.Debugf("overlay: skip_mount_home=%s", val) 327 o.skipMountHome, err = strconv.ParseBool(val) 328 if err != nil { 329 return nil, err 330 } 331 default: 332 return nil, fmt.Errorf("overlay: Unknown option %s", key) 333 } 334 } 335 return o, nil 336 } 337 338 func cachedFeatureSet(feature string, set bool) string { 339 if set { 340 return fmt.Sprintf("%s-true", feature) 341 } 342 return fmt.Sprintf("%s-false", feature) 343 } 344 345 func cachedFeatureCheck(runhome, feature string) (supported bool, text string, err error) { 346 content, err := ioutil.ReadFile(filepath.Join(runhome, cachedFeatureSet(feature, true))) 347 if err == nil { 348 return true, string(content), nil 349 } 350 content, err = ioutil.ReadFile(filepath.Join(runhome, cachedFeatureSet(feature, false))) 351 if err == nil { 352 return false, string(content), nil 353 } 354 return false, "", err 355 } 356 357 func cachedFeatureRecord(runhome, feature string, supported bool, text string) (err error) { 358 f, err := os.Create(filepath.Join(runhome, cachedFeatureSet(feature, supported))) 359 if f != nil { 360 if text != "" { 361 fmt.Fprintf(f, "%s", text) 362 } 363 f.Close() 364 } 365 return err 366 } 367 368 func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGID int) (supportsDType bool, err error) { 369 // We can try to modprobe overlay first 370 371 exec.Command("modprobe", "overlay").Run() 372 373 layerDir, err := ioutil.TempDir(home, "compat") 374 if err != nil { 375 patherr, ok := err.(*os.PathError) 376 if ok && patherr.Err == syscall.ENOSPC { 377 return false, err 378 } 379 } 380 if err == nil { 381 // Check if reading the directory's contents populates the d_type field, which is required 382 // for proper operation of the overlay filesystem. 383 supportsDType, err = fsutils.SupportsDType(layerDir) 384 if err != nil { 385 return false, err 386 } 387 if !supportsDType { 388 return false, overlayutils.ErrDTypeNotSupported("overlay", backingFs) 389 } 390 391 // Try a test mount in the specific location we're looking at using. 392 mergedDir := filepath.Join(layerDir, "merged") 393 lower1Dir := filepath.Join(layerDir, "lower1") 394 lower2Dir := filepath.Join(layerDir, "lower2") 395 upperDir := filepath.Join(layerDir, "upper") 396 workDir := filepath.Join(layerDir, "work") 397 defer func() { 398 // Permitted to fail, since the various subdirectories 399 // can be empty or not even there, and the home might 400 // legitimately be not empty 401 _ = unix.Unmount(mergedDir, unix.MNT_DETACH) 402 _ = os.RemoveAll(layerDir) 403 _ = os.Remove(home) 404 }() 405 _ = idtools.MkdirAs(mergedDir, 0700, rootUID, rootGID) 406 _ = idtools.MkdirAs(lower1Dir, 0700, rootUID, rootGID) 407 _ = idtools.MkdirAs(lower2Dir, 0700, rootUID, rootGID) 408 _ = idtools.MkdirAs(upperDir, 0700, rootUID, rootGID) 409 _ = idtools.MkdirAs(workDir, 0700, rootUID, rootGID) 410 flags := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", lower1Dir, lower2Dir, upperDir, workDir) 411 if len(flags) < unix.Getpagesize() { 412 err := mountFrom(filepath.Dir(home), "overlay", mergedDir, "overlay", 0, flags) 413 if err == nil { 414 logrus.Debugf("overlay test mount with multiple lowers succeeded") 415 return supportsDType, nil 416 } else { 417 logrus.Debugf("overlay test mount with multiple lowers failed %v", err) 418 } 419 } 420 flags = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lower1Dir, upperDir, workDir) 421 if len(flags) < unix.Getpagesize() { 422 err := mountFrom(filepath.Dir(home), "overlay", mergedDir, "overlay", 0, flags) 423 if err == nil { 424 logrus.Errorf("overlay test mount with multiple lowers failed, but succeeded with a single lower") 425 return supportsDType, errors.Wrap(graphdriver.ErrNotSupported, "kernel too old to provide multiple lowers feature for overlay") 426 } else { 427 logrus.Debugf("overlay test mount with a single lower failed %v", err) 428 } 429 } 430 logrus.Errorf("'overlay' is not supported over %s at %q", backingFs, home) 431 return supportsDType, errors.Wrapf(graphdriver.ErrIncompatibleFS, "'overlay' is not supported over %s at %q", backingFs, home) 432 } 433 434 logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 435 return supportsDType, errors.Wrap(graphdriver.ErrNotSupported, "'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 436 } 437 438 func (d *Driver) useNaiveDiff() bool { 439 useNaiveDiffLock.Do(func() { 440 if d.options.mountProgram != "" { 441 useNaiveDiffOnly = true 442 return 443 } 444 feature := fmt.Sprintf("native-diff(%s)", d.options.mountOptions) 445 nativeDiffCacheResult, nativeDiffCacheText, err := cachedFeatureCheck(d.runhome, feature) 446 if err == nil { 447 if nativeDiffCacheResult { 448 logrus.Debugf("cached value indicated that native-diff is usable") 449 } else { 450 logrus.Debugf("cached value indicated that native-diff is not being used") 451 logrus.Warn(nativeDiffCacheText) 452 } 453 useNaiveDiffOnly = !nativeDiffCacheResult 454 return 455 } 456 if err := doesSupportNativeDiff(d.home, d.options.mountOptions); err != nil { 457 nativeDiffCacheText = fmt.Sprintf("Not using native diff for overlay, this may cause degraded performance for building images: %v", err) 458 logrus.Warn(nativeDiffCacheText) 459 useNaiveDiffOnly = true 460 } 461 cachedFeatureRecord(d.runhome, feature, !useNaiveDiffOnly, nativeDiffCacheText) 462 }) 463 return useNaiveDiffOnly 464 } 465 466 func (d *Driver) String() string { 467 return d.name 468 } 469 470 // Status returns current driver information in a two dimensional string array. 471 // Output contains "Backing Filesystem" used in this implementation. 472 func (d *Driver) Status() [][2]string { 473 return [][2]string{ 474 {"Backing Filesystem", backingFs}, 475 {"Supports d_type", strconv.FormatBool(d.supportsDType)}, 476 {"Native Overlay Diff", strconv.FormatBool(!d.useNaiveDiff())}, 477 {"Using metacopy", strconv.FormatBool(d.usingMetacopy)}, 478 } 479 } 480 481 // Metadata returns meta data about the overlay driver such as 482 // LowerDir, UpperDir, WorkDir and MergeDir used to store data. 483 func (d *Driver) Metadata(id string) (map[string]string, error) { 484 dir := d.dir(id) 485 if _, err := os.Stat(dir); err != nil { 486 return nil, err 487 } 488 489 metadata := map[string]string{ 490 "WorkDir": path.Join(dir, "work"), 491 "MergedDir": path.Join(dir, "merged"), 492 "UpperDir": path.Join(dir, "diff"), 493 } 494 495 lowerDirs, err := d.getLowerDirs(id) 496 if err != nil { 497 return nil, err 498 } 499 if len(lowerDirs) > 0 { 500 metadata["LowerDir"] = strings.Join(lowerDirs, ":") 501 } 502 503 return metadata, nil 504 } 505 506 // Cleanup any state created by overlay which should be cleaned when daemon 507 // is being shutdown. For now, we just have to unmount the bind mounted 508 // we had created. 509 func (d *Driver) Cleanup() error { 510 return mount.Unmount(d.home) 511 } 512 513 // CreateFromTemplate creates a layer with the same contents and parent as another layer. 514 func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error { 515 if readWrite { 516 return d.CreateReadWrite(id, template, opts) 517 } 518 return d.Create(id, template, opts) 519 } 520 521 // CreateReadWrite creates a layer that is writable for use as a container 522 // file system. 523 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { 524 if opts != nil && len(opts.StorageOpt) != 0 && !projectQuotaSupported { 525 return fmt.Errorf("--storage-opt is supported only for overlay over xfs with 'pquota' mount option") 526 } 527 528 if opts == nil { 529 opts = &graphdriver.CreateOpts{ 530 StorageOpt: map[string]string{}, 531 } 532 } 533 534 if _, ok := opts.StorageOpt["size"]; !ok { 535 if opts.StorageOpt == nil { 536 opts.StorageOpt = map[string]string{} 537 } 538 opts.StorageOpt["size"] = strconv.FormatUint(d.options.quota.Size, 10) 539 } 540 541 return d.create(id, parent, opts) 542 } 543 544 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. 545 // The parent filesystem is used to configure these directories for the overlay. 546 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { 547 if opts != nil && len(opts.StorageOpt) != 0 { 548 if _, ok := opts.StorageOpt["size"]; ok { 549 return fmt.Errorf("--storage-opt size is only supported for ReadWrite Layers") 550 } 551 } 552 553 if d.options.ostreeRepo != "" { 554 d.convert[id] = true 555 } 556 557 return d.create(id, parent, opts) 558 } 559 560 func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { 561 dir := d.dir(id) 562 563 uidMaps := d.uidMaps 564 gidMaps := d.gidMaps 565 566 if opts != nil && opts.IDMappings != nil { 567 uidMaps = opts.IDMappings.UIDs() 568 gidMaps = opts.IDMappings.GIDs() 569 } 570 571 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 572 if err != nil { 573 return err 574 } 575 // Make the link directory if it does not exist 576 if err := idtools.MkdirAllAs(path.Join(d.home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 577 return err 578 } 579 if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { 580 return err 581 } 582 if parent != "" { 583 st, err := system.Stat(d.dir(parent)) 584 if err != nil { 585 return err 586 } 587 rootUID = int(st.UID()) 588 rootGID = int(st.GID()) 589 } 590 if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { 591 return err 592 } 593 594 defer func() { 595 // Clean up on failure 596 if retErr != nil { 597 os.RemoveAll(dir) 598 } 599 }() 600 601 if opts != nil && len(opts.StorageOpt) > 0 { 602 driver := &Driver{} 603 if err := d.parseStorageOpt(opts.StorageOpt, driver); err != nil { 604 return err 605 } 606 607 if driver.options.quota.Size > 0 { 608 // Set container disk quota limit 609 if err := d.quotaCtl.SetQuota(dir, driver.options.quota); err != nil { 610 return err 611 } 612 } 613 } 614 615 if err := idtools.MkdirAs(path.Join(dir, "diff"), 0755, rootUID, rootGID); err != nil { 616 return err 617 } 618 619 lid := generateID(idLength) 620 if err := os.Symlink(path.Join("..", id, "diff"), path.Join(d.home, linkDir, lid)); err != nil { 621 return err 622 } 623 624 // Write link id to link file 625 if err := ioutil.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil { 626 return err 627 } 628 629 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { 630 return err 631 } 632 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { 633 return err 634 } 635 636 // if no parent directory, create a dummy lower directory and skip writing a "lowers" file 637 if parent == "" { 638 return idtools.MkdirAs(path.Join(dir, "empty"), 0700, rootUID, rootGID) 639 } 640 641 lower, err := d.getLower(parent) 642 if err != nil { 643 return err 644 } 645 if lower != "" { 646 if err := ioutil.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil { 647 return err 648 } 649 } 650 651 return nil 652 } 653 654 // Parse overlay storage options 655 func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) error { 656 // Read size to set the disk project quota per container 657 for key, val := range storageOpt { 658 key := strings.ToLower(key) 659 switch key { 660 case "size": 661 size, err := units.RAMInBytes(val) 662 if err != nil { 663 return err 664 } 665 driver.options.quota.Size = uint64(size) 666 default: 667 return fmt.Errorf("Unknown option %s", key) 668 } 669 } 670 671 return nil 672 } 673 674 func (d *Driver) getLower(parent string) (string, error) { 675 parentDir := d.dir(parent) 676 677 // Ensure parent exists 678 if _, err := os.Lstat(parentDir); err != nil { 679 return "", err 680 } 681 682 // Read Parent link fileA 683 parentLink, err := ioutil.ReadFile(path.Join(parentDir, "link")) 684 if err != nil { 685 return "", err 686 } 687 lowers := []string{path.Join(linkDir, string(parentLink))} 688 689 parentLower, err := ioutil.ReadFile(path.Join(parentDir, lowerFile)) 690 if err == nil { 691 parentLowers := strings.Split(string(parentLower), ":") 692 lowers = append(lowers, parentLowers...) 693 } 694 if len(lowers) > maxDepth { 695 return "", errors.New("max depth exceeded") 696 } 697 return strings.Join(lowers, ":"), nil 698 } 699 700 func (d *Driver) dir(id string) string { 701 newpath := path.Join(d.home, id) 702 if _, err := os.Stat(newpath); err != nil { 703 for _, p := range d.AdditionalImageStores() { 704 l := path.Join(p, d.name, id) 705 _, err = os.Stat(l) 706 if err == nil { 707 return l 708 } 709 } 710 } 711 return newpath 712 } 713 714 func (d *Driver) getLowerDirs(id string) ([]string, error) { 715 var lowersArray []string 716 lowers, err := ioutil.ReadFile(path.Join(d.dir(id), lowerFile)) 717 if err == nil { 718 for _, s := range strings.Split(string(lowers), ":") { 719 lower := d.dir(s) 720 lp, err := os.Readlink(lower) 721 if err != nil { 722 return nil, err 723 } 724 lowersArray = append(lowersArray, path.Clean(d.dir(path.Join("link", lp)))) 725 } 726 } else if !os.IsNotExist(err) { 727 return nil, err 728 } 729 return lowersArray, nil 730 } 731 732 func (d *Driver) optsAppendMappings(opts string, uidMaps, gidMaps []idtools.IDMap) string { 733 if uidMaps == nil { 734 uidMaps = d.uidMaps 735 } 736 if gidMaps == nil { 737 gidMaps = d.gidMaps 738 } 739 if uidMaps != nil { 740 var uids, gids bytes.Buffer 741 for _, i := range uidMaps { 742 if uids.Len() > 0 { 743 uids.WriteString(":") 744 } 745 uids.WriteString(fmt.Sprintf("%d:%d:%d", i.ContainerID, i.HostID, i.Size)) 746 } 747 for _, i := range gidMaps { 748 if gids.Len() > 0 { 749 gids.WriteString(":") 750 } 751 gids.WriteString(fmt.Sprintf("%d:%d:%d", i.ContainerID, i.HostID, i.Size)) 752 } 753 return fmt.Sprintf("%s,uidmapping=%s,gidmapping=%s", opts, uids.String(), gids.String()) 754 } 755 return opts 756 } 757 758 // Remove cleans the directories that are created for this id. 759 func (d *Driver) Remove(id string) error { 760 d.locker.Lock(id) 761 defer d.locker.Unlock(id) 762 763 // Ignore errors, we don't want to fail if the ostree branch doesn't exist, 764 if d.options.ostreeRepo != "" { 765 ostree.DeleteOSTree(d.options.ostreeRepo, id) 766 } 767 768 dir := d.dir(id) 769 lid, err := ioutil.ReadFile(path.Join(dir, "link")) 770 if err == nil { 771 if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil { 772 logrus.Debugf("Failed to remove link: %v", err) 773 } 774 } 775 776 if err := system.EnsureRemoveAll(dir); err != nil && !os.IsNotExist(err) { 777 return err 778 } 779 return nil 780 } 781 782 // recreateSymlinks goes through the driver's home directory and checks if the diff directory 783 // under each layer has a symlink created for it under the linkDir. If the symlink does not 784 // exist, it creates them 785 func (d *Driver) recreateSymlinks() error { 786 // List all the directories under the home directory 787 dirs, err := ioutil.ReadDir(d.home) 788 if err != nil { 789 return fmt.Errorf("error reading driver home directory %q: %v", d.home, err) 790 } 791 // This makes the link directory if it doesn't exist 792 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 793 if err != nil { 794 return err 795 } 796 if err := idtools.MkdirAllAs(path.Join(d.home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 797 return err 798 } 799 for _, dir := range dirs { 800 // Skip over the linkDir and anything that is not a directory 801 if dir.Name() == linkDir || !dir.Mode().IsDir() { 802 continue 803 } 804 // Read the "link" file under each layer to get the name of the symlink 805 data, err := ioutil.ReadFile(path.Join(d.dir(dir.Name()), "link")) 806 if err != nil { 807 return fmt.Errorf("error reading name of symlink for %q: %v", dir, err) 808 } 809 linkPath := path.Join(d.home, linkDir, strings.Trim(string(data), "\n")) 810 // Check if the symlink exists, and if it doesn't create it again with the name we 811 // got from the "link" file 812 _, err = os.Stat(linkPath) 813 if err != nil && os.IsNotExist(err) { 814 if err := os.Symlink(path.Join("..", dir.Name(), "diff"), linkPath); err != nil { 815 return err 816 } 817 } else if err != nil { 818 return fmt.Errorf("error trying to stat %q: %v", linkPath, err) 819 } 820 } 821 return nil 822 } 823 824 // Get creates and mounts the required file system for the given id and returns the mount path. 825 func (d *Driver) Get(id string, options graphdriver.MountOpts) (_ string, retErr error) { 826 return d.get(id, false, options) 827 } 828 829 func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountOpts) (_ string, retErr error) { 830 d.locker.Lock(id) 831 defer d.locker.Unlock(id) 832 dir := d.dir(id) 833 if _, err := os.Stat(dir); err != nil { 834 return "", err 835 } 836 837 diffDir := path.Join(dir, "diff") 838 lowers, err := ioutil.ReadFile(path.Join(dir, lowerFile)) 839 if err != nil && !os.IsNotExist(err) { 840 return "", err 841 } 842 843 // absLowers is the list of lowers as absolute paths, which works well with additional stores. 844 absLowers := []string{} 845 // relLowers is the list of lowers as paths relative to the driver's home directory. 846 relLowers := []string{} 847 848 // Check if $link/../diff{1-*} exist. If they do, add them, in order, as the front of the lowers 849 // lists that we're building. "diff" itself is the upper, so it won't be in the lists. 850 link, err := ioutil.ReadFile(path.Join(dir, "link")) 851 if err != nil { 852 return "", err 853 } 854 diffN := 1 855 _, err = os.Stat(filepath.Join(dir, nameWithSuffix("diff", diffN))) 856 for err == nil { 857 absLowers = append(absLowers, filepath.Join(dir, nameWithSuffix("diff", diffN))) 858 relLowers = append(relLowers, dumbJoin(string(link), "..", nameWithSuffix("diff", diffN))) 859 diffN++ 860 _, err = os.Stat(filepath.Join(dir, nameWithSuffix("diff", diffN))) 861 } 862 863 // For each lower, resolve its path, and append it and any additional diffN 864 // directories to the lowers list. 865 for _, l := range strings.Split(string(lowers), ":") { 866 if l == "" { 867 continue 868 } 869 lower := "" 870 newpath := path.Join(d.home, l) 871 if _, err := os.Stat(newpath); err != nil { 872 for _, p := range d.AdditionalImageStores() { 873 lower = path.Join(p, d.name, l) 874 if _, err2 := os.Stat(lower); err2 == nil { 875 break 876 } 877 lower = "" 878 } 879 // if it is a "not found" error, that means the symlinks were lost in a sudden reboot 880 // so call the recreateSymlinks function to go through all the layer dirs and recreate 881 // the symlinks with the name from their respective "link" files 882 if lower == "" && os.IsNotExist(err) { 883 logrus.Warnf("Can't stat lower layer %q because it does not exist. Going through storage to recreate the missing symlinks.", newpath) 884 if err := d.recreateSymlinks(); err != nil { 885 return "", fmt.Errorf("error recreating the missing symlinks: %v", err) 886 } 887 lower = newpath 888 } else if lower == "" { 889 return "", fmt.Errorf("Can't stat lower layer %q: %v", newpath, err) 890 } 891 } else { 892 lower = newpath 893 } 894 absLowers = append(absLowers, lower) 895 relLowers = append(relLowers, l) 896 diffN = 1 897 _, err = os.Stat(dumbJoin(lower, "..", nameWithSuffix("diff", diffN))) 898 for err == nil { 899 absLowers = append(absLowers, dumbJoin(lower, "..", nameWithSuffix("diff", diffN))) 900 relLowers = append(relLowers, dumbJoin(l, "..", nameWithSuffix("diff", diffN))) 901 diffN++ 902 _, err = os.Stat(dumbJoin(lower, "..", nameWithSuffix("diff", diffN))) 903 } 904 } 905 906 // If the lowers list is still empty, use an empty lower so that we can still force an 907 // SELinux context for the mount. 908 if len(absLowers) == 0 { 909 absLowers = append(absLowers, path.Join(dir, "empty")) 910 relLowers = append(relLowers, path.Join(id, "empty")) 911 } 912 913 mergedDir := path.Join(dir, "merged") 914 if count := d.ctr.Increment(mergedDir); count > 1 { 915 return mergedDir, nil 916 } 917 defer func() { 918 if retErr != nil { 919 if c := d.ctr.Decrement(mergedDir); c <= 0 { 920 if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil { 921 logrus.Errorf("error unmounting %v: %v", mergedDir, mntErr) 922 } 923 } 924 } 925 }() 926 927 workDir := path.Join(dir, "work") 928 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workDir) 929 if len(options.Options) > 0 { 930 opts = fmt.Sprintf("%s,%s", strings.Join(options.Options, ","), opts) 931 } else if d.options.mountOptions != "" { 932 opts = fmt.Sprintf("%s,%s", d.options.mountOptions, opts) 933 } 934 mountData := label.FormatMountLabel(opts, options.MountLabel) 935 mountFunc := unix.Mount 936 mountTarget := mergedDir 937 938 pageSize := unix.Getpagesize() 939 940 // Use relative paths and mountFrom when the mount data has exceeded 941 // the page size. The mount syscall fails if the mount data cannot 942 // fit within a page and relative links make the mount data much 943 // smaller at the expense of requiring a fork exec to chroot. 944 if d.options.mountProgram != "" { 945 mountFunc = func(source string, target string, mType string, flags uintptr, label string) error { 946 if !disableShifting { 947 label = d.optsAppendMappings(label, options.UidMaps, options.GidMaps) 948 } 949 950 mountProgram := exec.Command(d.options.mountProgram, "-o", label, target) 951 mountProgram.Dir = d.home 952 var b bytes.Buffer 953 mountProgram.Stderr = &b 954 err := mountProgram.Run() 955 if err != nil { 956 output := b.String() 957 if output == "" { 958 output = "<stderr empty>" 959 } 960 return errors.Wrapf(err, "using mount program %s: %s", d.options.mountProgram, output) 961 } 962 return nil 963 } 964 } else if len(mountData) > pageSize { 965 //FIXME: We need to figure out to get this to work with additional stores 966 opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(relLowers, ":"), path.Join(id, "diff"), path.Join(id, "work")) 967 mountData = label.FormatMountLabel(opts, options.MountLabel) 968 if len(mountData) > pageSize { 969 return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData)) 970 } 971 mountFunc = func(source string, target string, mType string, flags uintptr, label string) error { 972 return mountFrom(d.home, source, target, mType, flags, label) 973 } 974 mountTarget = path.Join(id, "merged") 975 } 976 flags, data := mount.ParseOptions(mountData) 977 logrus.Debugf("overlay: mount_data=%s", mountData) 978 if err := mountFunc("overlay", mountTarget, "overlay", uintptr(flags), data); err != nil { 979 return "", fmt.Errorf("error creating overlay mount to %s: %v", mountTarget, err) 980 } 981 982 // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a 983 // user namespace requires this to move a directory from lower to upper. 984 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 985 if err != nil { 986 return "", err 987 } 988 989 if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { 990 return "", err 991 } 992 993 return mergedDir, nil 994 } 995 996 // Put unmounts the mount path created for the give id. 997 func (d *Driver) Put(id string) error { 998 d.locker.Lock(id) 999 defer d.locker.Unlock(id) 1000 dir := d.dir(id) 1001 if _, err := os.Stat(dir); err != nil { 1002 return err 1003 } 1004 mountpoint := path.Join(d.dir(id), "merged") 1005 if count := d.ctr.Decrement(mountpoint); count > 0 { 1006 return nil 1007 } 1008 if _, err := ioutil.ReadFile(path.Join(dir, lowerFile)); err != nil && !os.IsNotExist(err) { 1009 return err 1010 } 1011 if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil { 1012 logrus.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err) 1013 } 1014 return nil 1015 } 1016 1017 // Exists checks to see if the id is already mounted. 1018 func (d *Driver) Exists(id string) bool { 1019 _, err := os.Stat(d.dir(id)) 1020 return err == nil 1021 } 1022 1023 // isParent returns if the passed in parent is the direct parent of the passed in layer 1024 func (d *Driver) isParent(id, parent string) bool { 1025 lowers, err := d.getLowerDirs(id) 1026 if err != nil { 1027 return false 1028 } 1029 if parent == "" && len(lowers) > 0 { 1030 return false 1031 } 1032 1033 parentDir := d.dir(parent) 1034 var ld string 1035 if len(lowers) > 0 { 1036 ld = filepath.Dir(lowers[0]) 1037 } 1038 if ld == "" && parent == "" { 1039 return true 1040 } 1041 return ld == parentDir 1042 } 1043 1044 func (d *Driver) getWhiteoutFormat() archive.WhiteoutFormat { 1045 whiteoutFormat := archive.OverlayWhiteoutFormat 1046 if d.options.mountProgram != "" { 1047 // If we are using a mount program, we are most likely running 1048 // as an unprivileged user that cannot use mknod, so fallback to the 1049 // AUFS whiteout format. 1050 whiteoutFormat = archive.AUFSWhiteoutFormat 1051 } 1052 return whiteoutFormat 1053 } 1054 1055 // ApplyDiff applies the new layer into a root 1056 func (d *Driver) ApplyDiff(id string, idMappings *idtools.IDMappings, parent string, mountLabel string, diff io.Reader) (size int64, err error) { 1057 if !d.isParent(id, parent) { 1058 return d.naiveDiff.ApplyDiff(id, idMappings, parent, mountLabel, diff) 1059 } 1060 1061 if idMappings == nil { 1062 idMappings = &idtools.IDMappings{} 1063 } 1064 1065 applyDir := d.getDiffPath(id) 1066 1067 logrus.Debugf("Applying tar in %s", applyDir) 1068 // Overlay doesn't need the parent id to apply the diff 1069 if err := untar(diff, applyDir, &archive.TarOptions{ 1070 UIDMaps: idMappings.UIDs(), 1071 GIDMaps: idMappings.GIDs(), 1072 WhiteoutFormat: d.getWhiteoutFormat(), 1073 InUserNS: rsystem.RunningInUserNS(), 1074 }); err != nil { 1075 return 0, err 1076 } 1077 1078 _, convert := d.convert[id] 1079 if convert { 1080 if err := ostree.ConvertToOSTree(d.options.ostreeRepo, applyDir, id); err != nil { 1081 return 0, err 1082 } 1083 } 1084 1085 return directory.Size(applyDir) 1086 } 1087 1088 func (d *Driver) getDiffPath(id string) string { 1089 dir := d.dir(id) 1090 1091 return path.Join(dir, "diff") 1092 } 1093 1094 // DiffSize calculates the changes between the specified id 1095 // and its parent and returns the size in bytes of the changes 1096 // relative to its base filesystem directory. 1097 func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) { 1098 if d.useNaiveDiff() || !d.isParent(id, parent) { 1099 return d.naiveDiff.DiffSize(id, idMappings, parent, parentMappings, mountLabel) 1100 } 1101 return directory.Size(d.getDiffPath(id)) 1102 } 1103 1104 // Diff produces an archive of the changes between the specified 1105 // layer and its parent layer which may be "". 1106 func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error) { 1107 if d.useNaiveDiff() || !d.isParent(id, parent) { 1108 return d.naiveDiff.Diff(id, idMappings, parent, parentMappings, mountLabel) 1109 } 1110 1111 if idMappings == nil { 1112 idMappings = &idtools.IDMappings{} 1113 } 1114 1115 lowerDirs, err := d.getLowerDirs(id) 1116 if err != nil { 1117 return nil, err 1118 } 1119 1120 diffPath := d.getDiffPath(id) 1121 logrus.Debugf("Tar with options on %s", diffPath) 1122 return archive.TarWithOptions(diffPath, &archive.TarOptions{ 1123 Compression: archive.Uncompressed, 1124 UIDMaps: idMappings.UIDs(), 1125 GIDMaps: idMappings.GIDs(), 1126 WhiteoutFormat: d.getWhiteoutFormat(), 1127 WhiteoutData: lowerDirs, 1128 }) 1129 } 1130 1131 // Changes produces a list of changes between the specified layer 1132 // and its parent layer. If parent is "", then all changes will be ADD changes. 1133 func (d *Driver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) { 1134 if d.useNaiveDiff() || !d.isParent(id, parent) { 1135 return d.naiveDiff.Changes(id, idMappings, parent, parentMappings, mountLabel) 1136 } 1137 // Overlay doesn't have snapshots, so we need to get changes from all parent 1138 // layers. 1139 diffPath := d.getDiffPath(id) 1140 layers, err := d.getLowerDirs(id) 1141 if err != nil { 1142 return nil, err 1143 } 1144 1145 return archive.OverlayChanges(layers, diffPath) 1146 } 1147 1148 // AdditionalImageStores returns additional image stores supported by the driver 1149 func (d *Driver) AdditionalImageStores() []string { 1150 return d.options.imageStores 1151 } 1152 1153 // UpdateLayerIDMap updates ID mappings in a from matching the ones specified 1154 // by toContainer to those specified by toHost. 1155 func (d *Driver) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error { 1156 var err error 1157 dir := d.dir(id) 1158 diffDir := filepath.Join(dir, "diff") 1159 1160 rootUID, rootGID := 0, 0 1161 if toHost != nil { 1162 rootUID, rootGID, err = idtools.GetRootUIDGID(toHost.UIDs(), toHost.GIDs()) 1163 if err != nil { 1164 return err 1165 } 1166 } 1167 1168 // Mount the new layer and handle ownership changes and possible copy_ups in it. 1169 options := graphdriver.MountOpts{ 1170 MountLabel: mountLabel, 1171 Options: strings.Split(d.options.mountOptions, ","), 1172 } 1173 layerFs, err := d.get(id, true, options) 1174 if err != nil { 1175 return err 1176 } 1177 err = graphdriver.ChownPathByMaps(layerFs, toContainer, toHost) 1178 if err != nil { 1179 if err2 := d.Put(id); err2 != nil { 1180 logrus.Errorf("%v; error unmounting %v: %v", err, id, err2) 1181 } 1182 return err 1183 } 1184 if err = d.Put(id); err != nil { 1185 return err 1186 } 1187 1188 // Rotate the diff directories. 1189 i := 0 1190 _, err = os.Stat(nameWithSuffix(diffDir, i)) 1191 for err == nil { 1192 i++ 1193 _, err = os.Stat(nameWithSuffix(diffDir, i)) 1194 } 1195 for i > 0 { 1196 err = os.Rename(nameWithSuffix(diffDir, i-1), nameWithSuffix(diffDir, i)) 1197 if err != nil { 1198 return err 1199 } 1200 i-- 1201 } 1202 1203 // Re-create the directory that we're going to use as the upper layer. 1204 if err := idtools.MkdirAs(diffDir, 0755, rootUID, rootGID); err != nil { 1205 return err 1206 } 1207 return nil 1208 } 1209 1210 // SupportsShifting tells whether the driver support shifting of the UIDs/GIDs in an userNS 1211 func (d *Driver) SupportsShifting() bool { 1212 if os.Getenv("_TEST_FORCE_SUPPORT_SHIFTING") == "yes-please" { 1213 return true 1214 } 1215 return d.options.mountProgram != "" 1216 } 1217 1218 // dumbJoin is more or less a dumber version of filepath.Join, but one which 1219 // won't Clean() the path, allowing us to append ".." as a component and trust 1220 // pathname resolution to do some non-obvious work. 1221 func dumbJoin(names ...string) string { 1222 if len(names) == 0 { 1223 return string(os.PathSeparator) 1224 } 1225 return strings.Join(names, string(os.PathSeparator)) 1226 } 1227 1228 func nameWithSuffix(name string, number int) string { 1229 if number == 0 { 1230 return name 1231 } 1232 return fmt.Sprintf("%s%d", name, number) 1233 }