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