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