github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/daemon/graphdriver/overlay2/overlay.go (about) 1 // +build linux 2 3 package overlay2 4 5 import ( 6 "bufio" 7 "errors" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path" 13 "strconv" 14 "strings" 15 "syscall" 16 17 "github.com/Sirupsen/logrus" 18 19 "github.com/docker/docker/daemon/graphdriver" 20 "github.com/docker/docker/pkg/archive" 21 "github.com/docker/docker/pkg/chrootarchive" 22 "github.com/docker/docker/pkg/directory" 23 "github.com/docker/docker/pkg/idtools" 24 "github.com/docker/docker/pkg/mount" 25 "github.com/docker/docker/pkg/parsers" 26 "github.com/docker/docker/pkg/parsers/kernel" 27 28 "github.com/opencontainers/runc/libcontainer/label" 29 ) 30 31 var ( 32 // untar defines the untar method 33 untar = chrootarchive.UntarUncompressed 34 ) 35 36 // This backend uses the overlay union filesystem for containers 37 // with diff directories for each layer. 38 39 // This version of the overlay driver requires at least kernel 40 // 4.0.0 in order to support mounting multiple diff directories. 41 42 // Each container/image has at least a "diff" directory and "link" file. 43 // If there is also a "lower" file when there are diff layers 44 // below as well as "merged" and "work" directories. The "diff" directory 45 // has the upper layer of the overlay and is used to capture any 46 // changes to the layer. The "lower" file contains all the lower layer 47 // mounts separated by ":" and ordered from uppermost to lowermost 48 // layers. The overlay itself is mounted in the "merged" directory, 49 // and the "work" dir is needed for overlay to work. 50 51 // The "link" file for each layer contains a unique string for the layer. 52 // Under the "l" directory at the root there will be a symbolic link 53 // with that unique string pointing the "diff" directory for the layer. 54 // The symbolic links are used to reference lower layers in the "lower" 55 // file and on mount. The links are used to shorten the total length 56 // of a layer reference without requiring changes to the layer identifier 57 // or root directory. Mounts are always done relative to root and 58 // referencing the symbolic links in order to ensure the number of 59 // lower directories can fit in a single page for making the mount 60 // syscall. A hard upper limit of 128 lower layers is enforced to ensure 61 // that mounts do not fail due to length. 62 63 const ( 64 driverName = "overlay2" 65 linkDir = "l" 66 lowerFile = "lower" 67 maxDepth = 128 68 69 // idLength represents the number of random characters 70 // which can be used to create the unique link identifer 71 // for every layer. If this value is too long then the 72 // page size limit for the mount command may be exceeded. 73 // The idLength should be selected such that following equation 74 // is true (512 is a buffer for label metadata). 75 // ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512) 76 idLength = 26 77 ) 78 79 // Driver contains information about the home directory and the list of active mounts that are created using this driver. 80 type Driver struct { 81 home string 82 uidMaps []idtools.IDMap 83 gidMaps []idtools.IDMap 84 ctr *graphdriver.RefCounter 85 } 86 87 var backingFs = "<unknown>" 88 89 func init() { 90 graphdriver.Register(driverName, Init) 91 } 92 93 // Init returns the a native diff driver for overlay filesystem. 94 // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. 95 // If an overlay filesystem is not supported over an existing filesystem then error graphdriver.ErrIncompatibleFS is returned. 96 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 97 opts, err := parseOptions(options) 98 if err != nil { 99 return nil, err 100 } 101 102 if err := supportsOverlay(); err != nil { 103 return nil, graphdriver.ErrNotSupported 104 } 105 106 // require kernel 4.0.0 to ensure multiple lower dirs are supported 107 v, err := kernel.GetKernelVersion() 108 if err != nil { 109 return nil, err 110 } 111 if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 0, Minor: 0}) < 0 { 112 if !opts.overrideKernelCheck { 113 return nil, graphdriver.ErrNotSupported 114 } 115 logrus.Warnf("Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update") 116 } 117 118 fsMagic, err := graphdriver.GetFSMagic(home) 119 if err != nil { 120 return nil, err 121 } 122 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 123 backingFs = fsName 124 } 125 126 // check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs 127 switch fsMagic { 128 case graphdriver.FsMagicBtrfs, graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs: 129 logrus.Errorf("'overlay2' is not supported over %s", backingFs) 130 return nil, graphdriver.ErrIncompatibleFS 131 } 132 133 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 134 if err != nil { 135 return nil, err 136 } 137 // Create the driver home dir 138 if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 139 return nil, err 140 } 141 142 if err := mount.MakePrivate(home); err != nil { 143 return nil, err 144 } 145 146 d := &Driver{ 147 home: home, 148 uidMaps: uidMaps, 149 gidMaps: gidMaps, 150 ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), 151 } 152 153 return d, nil 154 } 155 156 type overlayOptions struct { 157 overrideKernelCheck bool 158 } 159 160 func parseOptions(options []string) (*overlayOptions, error) { 161 o := &overlayOptions{} 162 for _, option := range options { 163 key, val, err := parsers.ParseKeyValueOpt(option) 164 if err != nil { 165 return nil, err 166 } 167 key = strings.ToLower(key) 168 switch key { 169 case "overlay2.override_kernel_check": 170 o.overrideKernelCheck, err = strconv.ParseBool(val) 171 if err != nil { 172 return nil, err 173 } 174 default: 175 return nil, fmt.Errorf("overlay2: Unknown option %s\n", key) 176 } 177 } 178 return o, nil 179 } 180 181 func supportsOverlay() error { 182 // We can try to modprobe overlay first before looking at 183 // proc/filesystems for when overlay is supported 184 exec.Command("modprobe", "overlay").Run() 185 186 f, err := os.Open("/proc/filesystems") 187 if err != nil { 188 return err 189 } 190 defer f.Close() 191 192 s := bufio.NewScanner(f) 193 for s.Scan() { 194 if s.Text() == "nodev\toverlay" { 195 return nil 196 } 197 } 198 logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 199 return graphdriver.ErrNotSupported 200 } 201 202 func (d *Driver) String() string { 203 return driverName 204 } 205 206 // Status returns current driver information in a two dimensional string array. 207 // Output contains "Backing Filesystem" used in this implementation. 208 func (d *Driver) Status() [][2]string { 209 return [][2]string{ 210 {"Backing Filesystem", backingFs}, 211 } 212 } 213 214 // GetMetadata returns meta data about the overlay driver such as 215 // LowerDir, UpperDir, WorkDir and MergeDir used to store data. 216 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 217 dir := d.dir(id) 218 if _, err := os.Stat(dir); err != nil { 219 return nil, err 220 } 221 222 metadata := map[string]string{ 223 "WorkDir": path.Join(dir, "work"), 224 "MergedDir": path.Join(dir, "merged"), 225 "UpperDir": path.Join(dir, "diff"), 226 } 227 228 lowerDirs, err := d.getLowerDirs(id) 229 if err != nil { 230 return nil, err 231 } 232 if len(lowerDirs) > 0 { 233 metadata["LowerDir"] = strings.Join(lowerDirs, ":") 234 } 235 236 return metadata, nil 237 } 238 239 // Cleanup any state created by overlay which should be cleaned when daemon 240 // is being shutdown. For now, we just have to unmount the bind mounted 241 // we had created. 242 func (d *Driver) Cleanup() error { 243 return mount.Unmount(d.home) 244 } 245 246 // CreateReadWrite creates a layer that is writable for use as a container 247 // file system. 248 func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error { 249 return d.Create(id, parent, mountLabel, storageOpt) 250 } 251 252 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. 253 // The parent filesystem is used to configure these directories for the overlay. 254 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) (retErr error) { 255 256 if len(storageOpt) != 0 { 257 return fmt.Errorf("--storage-opt is not supported for overlay") 258 } 259 260 dir := d.dir(id) 261 262 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 263 if err != nil { 264 return err 265 } 266 if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { 267 return err 268 } 269 if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { 270 return err 271 } 272 273 defer func() { 274 // Clean up on failure 275 if retErr != nil { 276 os.RemoveAll(dir) 277 } 278 }() 279 280 if err := idtools.MkdirAs(path.Join(dir, "diff"), 0755, rootUID, rootGID); err != nil { 281 return err 282 } 283 284 lid := generateID(idLength) 285 if err := os.Symlink(path.Join("..", id, "diff"), path.Join(d.home, linkDir, lid)); err != nil { 286 return err 287 } 288 289 // Write link id to link file 290 if err := ioutil.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil { 291 return err 292 } 293 294 // if no parent directory, done 295 if parent == "" { 296 return nil 297 } 298 299 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { 300 return err 301 } 302 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { 303 return err 304 } 305 306 lower, err := d.getLower(parent) 307 if err != nil { 308 return err 309 } 310 if lower != "" { 311 if err := ioutil.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil { 312 return err 313 } 314 } 315 316 return nil 317 } 318 319 func (d *Driver) getLower(parent string) (string, error) { 320 parentDir := d.dir(parent) 321 322 // Ensure parent exists 323 if _, err := os.Lstat(parentDir); err != nil { 324 return "", err 325 } 326 327 // Read Parent link fileA 328 parentLink, err := ioutil.ReadFile(path.Join(parentDir, "link")) 329 if err != nil { 330 return "", err 331 } 332 lowers := []string{path.Join(linkDir, string(parentLink))} 333 334 parentLower, err := ioutil.ReadFile(path.Join(parentDir, lowerFile)) 335 if err == nil { 336 parentLowers := strings.Split(string(parentLower), ":") 337 lowers = append(lowers, parentLowers...) 338 } 339 if len(lowers) > maxDepth { 340 return "", errors.New("max depth exceeded") 341 } 342 return strings.Join(lowers, ":"), nil 343 } 344 345 func (d *Driver) dir(id string) string { 346 return path.Join(d.home, id) 347 } 348 349 func (d *Driver) getLowerDirs(id string) ([]string, error) { 350 var lowersArray []string 351 lowers, err := ioutil.ReadFile(path.Join(d.dir(id), lowerFile)) 352 if err == nil { 353 for _, s := range strings.Split(string(lowers), ":") { 354 lp, err := os.Readlink(path.Join(d.home, s)) 355 if err != nil { 356 return nil, err 357 } 358 lowersArray = append(lowersArray, path.Clean(path.Join(d.home, "link", lp))) 359 } 360 } else if !os.IsNotExist(err) { 361 return nil, err 362 } 363 return lowersArray, nil 364 } 365 366 // Remove cleans the directories that are created for this id. 367 func (d *Driver) Remove(id string) error { 368 dir := d.dir(id) 369 lid, err := ioutil.ReadFile(path.Join(dir, "link")) 370 if err == nil { 371 if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil { 372 logrus.Debugf("Failed to remove link: %v", err) 373 } 374 } 375 376 if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) { 377 return err 378 } 379 return nil 380 } 381 382 // Get creates and mounts the required file system for the given id and returns the mount path. 383 func (d *Driver) Get(id string, mountLabel string) (s string, err error) { 384 dir := d.dir(id) 385 if _, err := os.Stat(dir); err != nil { 386 return "", err 387 } 388 389 diffDir := path.Join(dir, "diff") 390 lowers, err := ioutil.ReadFile(path.Join(dir, lowerFile)) 391 if err != nil { 392 // If no lower, just return diff directory 393 if os.IsNotExist(err) { 394 return diffDir, nil 395 } 396 return "", err 397 } 398 399 mergedDir := path.Join(dir, "merged") 400 if count := d.ctr.Increment(mergedDir); count > 1 { 401 return mergedDir, nil 402 } 403 defer func() { 404 if err != nil { 405 if c := d.ctr.Decrement(mergedDir); c <= 0 { 406 syscall.Unmount(mergedDir, 0) 407 } 408 } 409 }() 410 411 workDir := path.Join(dir, "work") 412 splitLowers := strings.Split(string(lowers), ":") 413 absLowers := make([]string, len(splitLowers)) 414 for i, s := range splitLowers { 415 absLowers[i] = path.Join(d.home, s) 416 } 417 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), path.Join(dir, "diff"), path.Join(dir, "work")) 418 mountData := label.FormatMountLabel(opts, mountLabel) 419 mount := syscall.Mount 420 mountTarget := mergedDir 421 422 pageSize := syscall.Getpagesize() 423 424 // Use relative paths and mountFrom when the mount data has exceeded 425 // the page size. The mount syscall fails if the mount data cannot 426 // fit within a page and relative links make the mount data much 427 // smaller at the expense of requiring a fork exec to chroot. 428 if len(mountData) > pageSize { 429 opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work")) 430 mountData = label.FormatMountLabel(opts, mountLabel) 431 if len(mountData) > pageSize { 432 return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData)) 433 } 434 435 mount = func(source string, target string, mType string, flags uintptr, label string) error { 436 return mountFrom(d.home, source, target, mType, flags, label) 437 } 438 mountTarget = path.Join(id, "merged") 439 } 440 441 if err := mount("overlay", mountTarget, "overlay", 0, mountData); err != nil { 442 return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) 443 } 444 445 // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a 446 // user namespace requires this to move a directory from lower to upper. 447 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 448 if err != nil { 449 return "", err 450 } 451 452 if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { 453 return "", err 454 } 455 456 return mergedDir, nil 457 } 458 459 // Put unmounts the mount path created for the give id. 460 func (d *Driver) Put(id string) error { 461 mountpoint := path.Join(d.dir(id), "merged") 462 if count := d.ctr.Decrement(mountpoint); count > 0 { 463 return nil 464 } 465 if err := syscall.Unmount(mountpoint, 0); err != nil { 466 logrus.Debugf("Failed to unmount %s overlay: %v", id, err) 467 } 468 return nil 469 } 470 471 // Exists checks to see if the id is already mounted. 472 func (d *Driver) Exists(id string) bool { 473 _, err := os.Stat(d.dir(id)) 474 return err == nil 475 } 476 477 // ApplyDiff applies the new layer into a root 478 func (d *Driver) ApplyDiff(id string, parent string, diff archive.Reader) (size int64, err error) { 479 applyDir := d.getDiffPath(id) 480 481 logrus.Debugf("Applying tar in %s", applyDir) 482 // Overlay doesn't need the parent id to apply the diff 483 if err := untar(diff, applyDir, &archive.TarOptions{ 484 UIDMaps: d.uidMaps, 485 GIDMaps: d.gidMaps, 486 WhiteoutFormat: archive.OverlayWhiteoutFormat, 487 }); err != nil { 488 return 0, err 489 } 490 491 return d.DiffSize(id, parent) 492 } 493 494 func (d *Driver) getDiffPath(id string) string { 495 dir := d.dir(id) 496 497 return path.Join(dir, "diff") 498 } 499 500 // DiffSize calculates the changes between the specified id 501 // and its parent and returns the size in bytes of the changes 502 // relative to its base filesystem directory. 503 func (d *Driver) DiffSize(id, parent string) (size int64, err error) { 504 return directory.Size(d.getDiffPath(id)) 505 } 506 507 // Diff produces an archive of the changes between the specified 508 // layer and its parent layer which may be "". 509 func (d *Driver) Diff(id, parent string) (archive.Archive, error) { 510 diffPath := d.getDiffPath(id) 511 logrus.Debugf("Tar with options on %s", diffPath) 512 return archive.TarWithOptions(diffPath, &archive.TarOptions{ 513 Compression: archive.Uncompressed, 514 UIDMaps: d.uidMaps, 515 GIDMaps: d.gidMaps, 516 WhiteoutFormat: archive.OverlayWhiteoutFormat, 517 }) 518 } 519 520 // Changes produces a list of changes between the specified layer 521 // and its parent layer. If parent is "", then all changes will be ADD changes. 522 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { 523 // Overlay doesn't have snapshots, so we need to get changes from all parent 524 // layers. 525 diffPath := d.getDiffPath(id) 526 layers, err := d.getLowerDirs(id) 527 if err != nil { 528 return nil, err 529 } 530 531 return archive.OverlayChanges(layers, diffPath) 532 }