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