1// +build linux 2 3package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay" 4 5import ( 6 "bufio" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path" 13 "path/filepath" 14 "strconv" 15 "strings" 16 17 "github.com/docker/docker/daemon/graphdriver" 18 "github.com/docker/docker/daemon/graphdriver/copy" 19 "github.com/docker/docker/daemon/graphdriver/overlayutils" 20 "github.com/docker/docker/pkg/archive" 21 "github.com/docker/docker/pkg/containerfs" 22 "github.com/docker/docker/pkg/fsutils" 23 "github.com/docker/docker/pkg/idtools" 24 "github.com/docker/docker/pkg/locker" 25 "github.com/docker/docker/pkg/mount" 26 "github.com/docker/docker/pkg/parsers" 27 "github.com/docker/docker/pkg/system" 28 "github.com/opencontainers/selinux/go-selinux/label" 29 "github.com/sirupsen/logrus" 30 "golang.org/x/sys/unix" 31) 32 33// This is a small wrapper over the NaiveDiffWriter that lets us have a custom 34// implementation of ApplyDiff() 35 36var ( 37 // ErrApplyDiffFallback is returned to indicate that a normal ApplyDiff is applied as a fallback from Naive diff writer. 38 ErrApplyDiffFallback = fmt.Errorf("Fall back to normal ApplyDiff") 39 backingFs = "<unknown>" 40) 41 42// ApplyDiffProtoDriver wraps the ProtoDriver by extending the interface with ApplyDiff method. 43type ApplyDiffProtoDriver interface { 44 graphdriver.ProtoDriver 45 // ApplyDiff writes the diff to the archive for the given id and parent id. 46 // It returns the size in bytes written if successful, an error ErrApplyDiffFallback is returned otherwise. 47 ApplyDiff(id, parent string, diff io.Reader) (size int64, err error) 48} 49 50type naiveDiffDriverWithApply struct { 51 graphdriver.Driver 52 applyDiff ApplyDiffProtoDriver 53} 54 55// NaiveDiffDriverWithApply returns a NaiveDiff driver with custom ApplyDiff. 56func NaiveDiffDriverWithApply(driver ApplyDiffProtoDriver, uidMaps, gidMaps []idtools.IDMap) graphdriver.Driver { 57 return &naiveDiffDriverWithApply{ 58 Driver: graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), 59 applyDiff: driver, 60 } 61} 62 63// ApplyDiff creates a diff layer with either the NaiveDiffDriver or with a fallback. 64func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff io.Reader) (int64, error) { 65 b, err := d.applyDiff.ApplyDiff(id, parent, diff) 66 if err == ErrApplyDiffFallback { 67 return d.Driver.ApplyDiff(id, parent, diff) 68 } 69 return b, err 70} 71 72// This backend uses the overlay union filesystem for containers 73// plus hard link file sharing for images. 74 75// Each container/image can have a "root" subdirectory which is a plain 76// filesystem hierarchy, or they can use overlay. 77 78// If they use overlay there is a "upper" directory and a "lower-id" 79// file, as well as "merged" and "work" directories. The "upper" 80// directory has the upper layer of the overlay, and "lower-id" contains 81// the id of the parent whose "root" directory shall be used as the lower 82// layer in the overlay. The overlay itself is mounted in the "merged" 83// directory, and the "work" dir is needed for overlay to work. 84 85// When an overlay layer is created there are two cases, either the 86// parent has a "root" dir, then we start out with an empty "upper" 87// directory overlaid on the parents root. This is typically the 88// case with the init layer of a container which is based on an image. 89// If there is no "root" in the parent, we inherit the lower-id from 90// the parent and start by making a copy in the parent's "upper" dir. 91// This is typically the case for a container layer which copies 92// its parent -init upper layer. 93 94// Additionally we also have a custom implementation of ApplyLayer 95// which makes a recursive copy of the parent "root" layer using 96// hardlinks to share file data, and then applies the layer on top 97// of that. This means all child images share file (but not directory) 98// data with the parent. 99 100type overlayOptions struct{} 101 102// Driver contains information about the home directory and the list of active mounts that are created using this driver. 103type Driver struct { 104 home string 105 uidMaps []idtools.IDMap 106 gidMaps []idtools.IDMap 107 ctr *graphdriver.RefCounter 108 supportsDType bool 109 locker *locker.Locker 110} 111 112func init() { 113 graphdriver.Register("overlay", Init) 114} 115 116// Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem. 117// If overlay filesystem is not supported on the host, the error 118// graphdriver.ErrNotSupported is returned. 119// If an overlay filesystem is not supported over an existing filesystem then 120// error graphdriver.ErrIncompatibleFS is returned. 121func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 122 _, err := parseOptions(options) 123 if err != nil { 124 return nil, err 125 } 126 127 if err := supportsOverlay(); err != nil { 128 return nil, graphdriver.ErrNotSupported 129 } 130 131 // Perform feature detection on /var/lib/docker/overlay if it's an existing directory. 132 // This covers situations where /var/lib/docker/overlay is a mount, and on a different 133 // filesystem than /var/lib/docker. 134 // If the path does not exist, fall back to using /var/lib/docker for feature detection. 135 testdir := home 136 if _, err := os.Stat(testdir); os.IsNotExist(err) { 137 testdir = filepath.Dir(testdir) 138 } 139 140 fsMagic, err := graphdriver.GetFSMagic(testdir) 141 if err != nil { 142 return nil, err 143 } 144 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 145 backingFs = fsName 146 } 147 148 switch fsMagic { 149 case graphdriver.FsMagicAufs, graphdriver.FsMagicBtrfs, graphdriver.FsMagicEcryptfs, graphdriver.FsMagicNfsFs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs: 150 logrus.WithField("storage-driver", "overlay").Errorf("'overlay' is not supported over %s", backingFs) 151 return nil, graphdriver.ErrIncompatibleFS 152 } 153 154 supportsDType, err := fsutils.SupportsDType(testdir) 155 if err != nil { 156 return nil, err 157 } 158 if !supportsDType { 159 if !graphdriver.IsInitialized(home) { 160 return nil, overlayutils.ErrDTypeNotSupported("overlay", backingFs) 161 } 162 // allow running without d_type only for existing setups (#27443) 163 logrus.WithField("storage-driver", "overlay").Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs)) 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.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { 172 return nil, err 173 } 174 175 d := &Driver{ 176 home: home, 177 uidMaps: uidMaps, 178 gidMaps: gidMaps, 179 ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), 180 supportsDType: supportsDType, 181 locker: locker.New(), 182 } 183 184 return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil 185} 186 187func parseOptions(options []string) (*overlayOptions, error) { 188 o := &overlayOptions{} 189 for _, option := range options { 190 key, _, err := parsers.ParseKeyValueOpt(option) 191 if err != nil { 192 return nil, err 193 } 194 key = strings.ToLower(key) 195 switch key { 196 default: 197 return nil, fmt.Errorf("overlay: unknown option %s", key) 198 } 199 } 200 return o, nil 201} 202 203func supportsOverlay() error { 204 // We can try to modprobe overlay first before looking at 205 // proc/filesystems for when overlay is supported 206 exec.Command("modprobe", "overlay").Run() 207 208 f, err := os.Open("/proc/filesystems") 209 if err != nil { 210 return err 211 } 212 defer f.Close() 213 214 s := bufio.NewScanner(f) 215 for s.Scan() { 216 if s.Text() == "nodev\toverlay" { 217 return nil 218 } 219 } 220 logrus.WithField("storage-driver", "overlay").Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 221 return graphdriver.ErrNotSupported 222} 223 224func (d *Driver) String() string { 225 return "overlay" 226} 227 228// Status returns current driver information in a two dimensional string array. 229// Output contains "Backing Filesystem" used in this implementation. 230func (d *Driver) Status() [][2]string { 231 return [][2]string{ 232 {"Backing Filesystem", backingFs}, 233 {"Supports d_type", strconv.FormatBool(d.supportsDType)}, 234 } 235} 236 237// GetMetadata returns metadata about the overlay driver such as root, 238// LowerDir, UpperDir, WorkDir and MergeDir used to store data. 239func (d *Driver) GetMetadata(id string) (map[string]string, error) { 240 dir := d.dir(id) 241 if _, err := os.Stat(dir); err != nil { 242 return nil, err 243 } 244 245 metadata := make(map[string]string) 246 247 // If id has a root, it is an image 248 rootDir := path.Join(dir, "root") 249 if _, err := os.Stat(rootDir); err == nil { 250 metadata["RootDir"] = rootDir 251 return metadata, nil 252 } 253 254 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 255 if err != nil { 256 return nil, err 257 } 258 259 metadata["LowerDir"] = path.Join(d.dir(string(lowerID)), "root") 260 metadata["UpperDir"] = path.Join(dir, "upper") 261 metadata["WorkDir"] = path.Join(dir, "work") 262 metadata["MergedDir"] = path.Join(dir, "merged") 263 264 return metadata, nil 265} 266 267// Cleanup any state created by overlay which should be cleaned when daemon 268// is being shutdown. For now, we just have to unmount the bind mounted 269// we had created. 270func (d *Driver) Cleanup() error { 271 return mount.RecursiveUnmount(d.home) 272} 273 274// CreateReadWrite creates a layer that is writable for use as a container 275// file system. 276func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { 277 return d.Create(id, parent, opts) 278} 279 280// Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. 281// The parent filesystem is used to configure these directories for the overlay. 282func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { 283 284 if opts != nil && len(opts.StorageOpt) != 0 { 285 return fmt.Errorf("--storage-opt is not supported for overlay") 286 } 287 288 dir := d.dir(id) 289 290 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 291 if err != nil { 292 return err 293 } 294 root := idtools.Identity{UID: rootUID, GID: rootGID} 295 296 if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { 297 return err 298 } 299 if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { 300 return err 301 } 302 303 defer func() { 304 // Clean up on failure 305 if retErr != nil { 306 os.RemoveAll(dir) 307 } 308 }() 309 310 // Toplevel images are just a "root" dir 311 if parent == "" { 312 return idtools.MkdirAndChown(path.Join(dir, "root"), 0755, root) 313 } 314 315 parentDir := d.dir(parent) 316 317 // Ensure parent exists 318 if _, err := os.Lstat(parentDir); err != nil { 319 return err 320 } 321 322 // If parent has a root, just do an overlay to it 323 parentRoot := path.Join(parentDir, "root") 324 325 if s, err := os.Lstat(parentRoot); err == nil { 326 if err := idtools.MkdirAndChown(path.Join(dir, "upper"), s.Mode(), root); err != nil { 327 return err 328 } 329 if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { 330 return err 331 } 332 return ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666) 333 } 334 335 // Otherwise, copy the upper and the lower-id from the parent 336 337 lowerID, err := ioutil.ReadFile(path.Join(parentDir, "lower-id")) 338 if err != nil { 339 return err 340 } 341 342 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil { 343 return err 344 } 345 346 parentUpperDir := path.Join(parentDir, "upper") 347 s, err := os.Lstat(parentUpperDir) 348 if err != nil { 349 return err 350 } 351 352 upperDir := path.Join(dir, "upper") 353 if err := idtools.MkdirAndChown(upperDir, s.Mode(), root); err != nil { 354 return err 355 } 356 if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { 357 return err 358 } 359 360 return copy.DirCopy(parentUpperDir, upperDir, copy.Content, true) 361} 362 363func (d *Driver) dir(id string) string { 364 return path.Join(d.home, id) 365} 366 367// Remove cleans the directories that are created for this id. 368func (d *Driver) Remove(id string) error { 369 if id == "" { 370 return fmt.Errorf("refusing to remove the directories: id is empty") 371 } 372 d.locker.Lock(id) 373 defer d.locker.Unlock(id) 374 return system.EnsureRemoveAll(d.dir(id)) 375} 376 377// Get creates and mounts the required file system for the given id and returns the mount path. 378func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err error) { 379 d.locker.Lock(id) 380 defer d.locker.Unlock(id) 381 dir := d.dir(id) 382 if _, err := os.Stat(dir); err != nil { 383 return nil, err 384 } 385 // If id has a root, just return it 386 rootDir := path.Join(dir, "root") 387 if _, err := os.Stat(rootDir); err == nil { 388 return containerfs.NewLocalContainerFS(rootDir), nil 389 } 390 391 mergedDir := path.Join(dir, "merged") 392 if count := d.ctr.Increment(mergedDir); count > 1 { 393 return containerfs.NewLocalContainerFS(mergedDir), nil 394 } 395 defer func() { 396 if err != nil { 397 if c := d.ctr.Decrement(mergedDir); c <= 0 { 398 if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil { 399 logrus.WithField("storage-driver", "overlay").Debugf("Failed to unmount %s: %v: %v", id, mntErr, err) 400 } 401 // Cleanup the created merged directory; see the comment in Put's rmdir 402 if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) { 403 logrus.WithField("storage-driver", "overlay").Warnf("Failed to remove %s: %v: %v", id, rmErr, err) 404 } 405 } 406 } 407 }() 408 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 409 if err != nil { 410 return nil, err 411 } 412 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 413 if err != nil { 414 return nil, err 415 } 416 if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { 417 return nil, err 418 } 419 var ( 420 lowerDir = path.Join(d.dir(string(lowerID)), "root") 421 upperDir = path.Join(dir, "upper") 422 workDir = path.Join(dir, "work") 423 opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) 424 ) 425 if err := unix.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { 426 return nil, fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) 427 } 428 // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a 429 // user namespace requires this to move a directory from lower to upper. 430 if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { 431 return nil, err 432 } 433 return containerfs.NewLocalContainerFS(mergedDir), nil 434} 435 436// Put unmounts the mount path created for the give id. 437// It also removes the 'merged' directory to force the kernel to unmount the 438// overlay mount in other namespaces. 439func (d *Driver) Put(id string) error { 440 d.locker.Lock(id) 441 defer d.locker.Unlock(id) 442 // If id has a root, just return 443 if _, err := os.Stat(path.Join(d.dir(id), "root")); err == nil { 444 return nil 445 } 446 mountpoint := path.Join(d.dir(id), "merged") 447 logger := logrus.WithField("storage-driver", "overlay") 448 if count := d.ctr.Decrement(mountpoint); count > 0 { 449 return nil 450 } 451 if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil { 452 logger.Debugf("Failed to unmount %s overlay: %v", id, err) 453 } 454 455 // Remove the mountpoint here. Removing the mountpoint (in newer kernels) 456 // will cause all other instances of this mount in other mount namespaces 457 // to be unmounted. This is necessary to avoid cases where an overlay mount 458 // that is present in another namespace will cause subsequent mounts 459 // operations to fail with ebusy. We ignore any errors here because this may 460 // fail on older kernels which don't have 461 // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. 462 if err := unix.Rmdir(mountpoint); err != nil { 463 logger.Debugf("Failed to remove %s overlay: %v", id, err) 464 } 465 return nil 466} 467 468// ApplyDiff applies the new layer on top of the root, if parent does not exist with will return an ErrApplyDiffFallback error. 469func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) { 470 dir := d.dir(id) 471 472 if parent == "" { 473 return 0, ErrApplyDiffFallback 474 } 475 476 parentRootDir := path.Join(d.dir(parent), "root") 477 if _, err := os.Stat(parentRootDir); err != nil { 478 return 0, ErrApplyDiffFallback 479 } 480 481 // We now know there is a parent, and it has a "root" directory containing 482 // the full root filesystem. We can just hardlink it and apply the 483 // layer. This relies on two things: 484 // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container 485 // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) 486 // These are all currently true and are not expected to break 487 488 tmpRootDir, err := ioutil.TempDir(dir, "tmproot") 489 if err != nil { 490 return 0, err 491 } 492 defer func() { 493 if err != nil { 494 os.RemoveAll(tmpRootDir) 495 } else { 496 os.RemoveAll(path.Join(dir, "upper")) 497 os.RemoveAll(path.Join(dir, "work")) 498 os.RemoveAll(path.Join(dir, "merged")) 499 os.RemoveAll(path.Join(dir, "lower-id")) 500 } 501 }() 502 503 if err = copy.DirCopy(parentRootDir, tmpRootDir, copy.Hardlink, true); err != nil { 504 return 0, err 505 } 506 507 options := &archive.TarOptions{UIDMaps: d.uidMaps, GIDMaps: d.gidMaps} 508 if size, err = graphdriver.ApplyUncompressedLayer(tmpRootDir, diff, options); err != nil { 509 return 0, err 510 } 511 512 rootDir := path.Join(dir, "root") 513 if err := os.Rename(tmpRootDir, rootDir); err != nil { 514 return 0, err 515 } 516 517 return 518} 519 520// Exists checks to see if the id is already mounted. 521func (d *Driver) Exists(id string) bool { 522 _, err := os.Stat(d.dir(id)) 523 return err == nil 524} 525