github.com/akerouanton/docker@v1.11.0-rc3/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 // Driver contains information about the home directory and the list of active mounts that are created using this driver. 92 type Driver struct { 93 home string 94 pathCacheLock sync.Mutex 95 pathCache map[string]string 96 uidMaps []idtools.IDMap 97 gidMaps []idtools.IDMap 98 } 99 100 var backingFs = "<unknown>" 101 102 func init() { 103 graphdriver.Register("overlay", Init) 104 } 105 106 // Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem. 107 // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. 108 // If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned. 109 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 110 111 if err := supportsOverlay(); err != nil { 112 return nil, graphdriver.ErrNotSupported 113 } 114 115 fsMagic, err := graphdriver.GetFSMagic(home) 116 if err != nil { 117 return nil, err 118 } 119 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 120 backingFs = fsName 121 } 122 123 // check if they are running over btrfs or aufs 124 switch fsMagic { 125 case graphdriver.FsMagicBtrfs: 126 logrus.Error("'overlay' is not supported over btrfs.") 127 return nil, graphdriver.ErrIncompatibleFS 128 case graphdriver.FsMagicAufs: 129 logrus.Error("'overlay' is not supported over aufs.") 130 return nil, graphdriver.ErrIncompatibleFS 131 case graphdriver.FsMagicZfs: 132 logrus.Error("'overlay' is not supported over zfs.") 133 return nil, graphdriver.ErrIncompatibleFS 134 } 135 136 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 137 if err != nil { 138 return nil, err 139 } 140 // Create the driver home dir 141 if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 142 return nil, err 143 } 144 145 d := &Driver{ 146 home: home, 147 pathCache: make(map[string]string), 148 uidMaps: uidMaps, 149 gidMaps: gidMaps, 150 } 151 152 return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil 153 } 154 155 func supportsOverlay() error { 156 // We can try to modprobe overlay first before looking at 157 // proc/filesystems for when overlay is supported 158 exec.Command("modprobe", "overlay").Run() 159 160 f, err := os.Open("/proc/filesystems") 161 if err != nil { 162 return err 163 } 164 defer f.Close() 165 166 s := bufio.NewScanner(f) 167 for s.Scan() { 168 if s.Text() == "nodev\toverlay" { 169 return nil 170 } 171 } 172 logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 173 return graphdriver.ErrNotSupported 174 } 175 176 func (d *Driver) String() string { 177 return "overlay" 178 } 179 180 // Status returns current driver information in a two dimensional string array. 181 // Output contains "Backing Filesystem" used in this implementation. 182 func (d *Driver) Status() [][2]string { 183 return [][2]string{ 184 {"Backing Filesystem", backingFs}, 185 } 186 } 187 188 // GetMetadata returns meta data about the overlay driver such as root, LowerDir, UpperDir, WorkDir and MergeDir used to store data. 189 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 190 dir := d.dir(id) 191 if _, err := os.Stat(dir); err != nil { 192 return nil, err 193 } 194 195 metadata := make(map[string]string) 196 197 // If id has a root, it is an image 198 rootDir := path.Join(dir, "root") 199 if _, err := os.Stat(rootDir); err == nil { 200 metadata["RootDir"] = rootDir 201 return metadata, nil 202 } 203 204 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 205 if err != nil { 206 return nil, err 207 } 208 209 metadata["LowerDir"] = path.Join(d.dir(string(lowerID)), "root") 210 metadata["UpperDir"] = path.Join(dir, "upper") 211 metadata["WorkDir"] = path.Join(dir, "work") 212 metadata["MergedDir"] = path.Join(dir, "merged") 213 214 return metadata, nil 215 } 216 217 // Cleanup simply returns nil and do not change the existing filesystem. 218 // This is required to satisfy the graphdriver.Driver interface. 219 func (d *Driver) Cleanup() error { 220 return nil 221 } 222 223 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. 224 // The parent filesystem is used to configure these directories for the overlay. 225 func (d *Driver) Create(id, parent, mountLabel string) (retErr error) { 226 dir := d.dir(id) 227 228 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 229 if err != nil { 230 return err 231 } 232 if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { 233 return err 234 } 235 if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { 236 return err 237 } 238 239 defer func() { 240 // Clean up on failure 241 if retErr != nil { 242 os.RemoveAll(dir) 243 } 244 }() 245 246 // Toplevel images are just a "root" dir 247 if parent == "" { 248 if err := idtools.MkdirAs(path.Join(dir, "root"), 0755, rootUID, rootGID); err != nil { 249 return err 250 } 251 return nil 252 } 253 254 parentDir := d.dir(parent) 255 256 // Ensure parent exists 257 if _, err := os.Lstat(parentDir); err != nil { 258 return err 259 } 260 261 // If parent has a root, just do a overlay to it 262 parentRoot := path.Join(parentDir, "root") 263 264 if s, err := os.Lstat(parentRoot); err == nil { 265 if err := idtools.MkdirAs(path.Join(dir, "upper"), s.Mode(), rootUID, rootGID); err != nil { 266 return err 267 } 268 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { 269 return err 270 } 271 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { 272 return err 273 } 274 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil { 275 return err 276 } 277 return nil 278 } 279 280 // Otherwise, copy the upper and the lower-id from the parent 281 282 lowerID, err := ioutil.ReadFile(path.Join(parentDir, "lower-id")) 283 if err != nil { 284 return err 285 } 286 287 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil { 288 return err 289 } 290 291 parentUpperDir := path.Join(parentDir, "upper") 292 s, err := os.Lstat(parentUpperDir) 293 if err != nil { 294 return err 295 } 296 297 upperDir := path.Join(dir, "upper") 298 if err := idtools.MkdirAs(upperDir, s.Mode(), rootUID, rootGID); err != nil { 299 return err 300 } 301 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { 302 return err 303 } 304 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { 305 return err 306 } 307 308 return copyDir(parentUpperDir, upperDir, 0) 309 } 310 311 func (d *Driver) dir(id string) string { 312 return path.Join(d.home, id) 313 } 314 315 // Remove cleans the directories that are created for this id. 316 func (d *Driver) Remove(id string) error { 317 if err := os.RemoveAll(d.dir(id)); err != nil && !os.IsNotExist(err) { 318 return err 319 } 320 d.pathCacheLock.Lock() 321 delete(d.pathCache, id) 322 d.pathCacheLock.Unlock() 323 return nil 324 } 325 326 // Get creates and mounts the required file system for the given id and returns the mount path. 327 func (d *Driver) Get(id string, mountLabel string) (string, error) { 328 dir := d.dir(id) 329 if _, err := os.Stat(dir); err != nil { 330 return "", err 331 } 332 333 // If id has a root, just return it 334 rootDir := path.Join(dir, "root") 335 if _, err := os.Stat(rootDir); err == nil { 336 d.pathCacheLock.Lock() 337 d.pathCache[id] = rootDir 338 d.pathCacheLock.Unlock() 339 return rootDir, nil 340 } 341 342 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 343 if err != nil { 344 return "", err 345 } 346 lowerDir := path.Join(d.dir(string(lowerID)), "root") 347 upperDir := path.Join(dir, "upper") 348 workDir := path.Join(dir, "work") 349 mergedDir := path.Join(dir, "merged") 350 351 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) 352 353 // if it's mounted already, just return 354 mounted, err := d.mounted(mergedDir) 355 if err != nil { 356 return "", err 357 } 358 if mounted { 359 return mergedDir, nil 360 } 361 362 if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { 363 return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) 364 } 365 // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a 366 // user namespace requires this to move a directory from lower to upper. 367 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 368 if err != nil { 369 return "", err 370 } 371 372 if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { 373 return "", err 374 } 375 376 d.pathCacheLock.Lock() 377 d.pathCache[id] = mergedDir 378 d.pathCacheLock.Unlock() 379 380 return mergedDir, nil 381 } 382 383 func (d *Driver) mounted(dir string) (bool, error) { 384 return graphdriver.Mounted(graphdriver.FsMagicOverlay, dir) 385 } 386 387 // Put unmounts the mount path created for the give id. 388 func (d *Driver) Put(id string) error { 389 d.pathCacheLock.Lock() 390 mountpoint, exists := d.pathCache[id] 391 d.pathCacheLock.Unlock() 392 393 if !exists { 394 logrus.Debugf("Put on a non-mounted device %s", id) 395 // but it might be still here 396 if d.Exists(id) { 397 mountpoint = path.Join(d.dir(id), "merged") 398 } 399 400 d.pathCacheLock.Lock() 401 d.pathCache[id] = mountpoint 402 d.pathCacheLock.Unlock() 403 } 404 405 if mounted, err := d.mounted(mountpoint); mounted || err != nil { 406 if err = syscall.Unmount(mountpoint, 0); err != nil { 407 logrus.Debugf("Failed to unmount %s overlay: %v", id, err) 408 } 409 return err 410 } 411 return nil 412 } 413 414 // ApplyDiff applies the new layer on top of the root, if parent does not exist with will return a ErrApplyDiffFallback error. 415 func (d *Driver) ApplyDiff(id string, parent string, diff archive.Reader) (size int64, err error) { 416 dir := d.dir(id) 417 418 if parent == "" { 419 return 0, ErrApplyDiffFallback 420 } 421 422 parentRootDir := path.Join(d.dir(parent), "root") 423 if _, err := os.Stat(parentRootDir); err != nil { 424 return 0, ErrApplyDiffFallback 425 } 426 427 // We now know there is a parent, and it has a "root" directory containing 428 // the full root filesystem. We can just hardlink it and apply the 429 // layer. This relies on two things: 430 // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container 431 // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) 432 // These are all currently true and are not expected to break 433 434 tmpRootDir, err := ioutil.TempDir(dir, "tmproot") 435 if err != nil { 436 return 0, err 437 } 438 defer func() { 439 if err != nil { 440 os.RemoveAll(tmpRootDir) 441 } else { 442 os.RemoveAll(path.Join(dir, "upper")) 443 os.RemoveAll(path.Join(dir, "work")) 444 os.RemoveAll(path.Join(dir, "merged")) 445 os.RemoveAll(path.Join(dir, "lower-id")) 446 } 447 }() 448 449 if err = copyDir(parentRootDir, tmpRootDir, copyHardlink); err != nil { 450 return 0, err 451 } 452 453 options := &archive.TarOptions{UIDMaps: d.uidMaps, GIDMaps: d.gidMaps} 454 if size, err = chrootarchive.ApplyUncompressedLayer(tmpRootDir, diff, options); err != nil { 455 return 0, err 456 } 457 458 rootDir := path.Join(dir, "root") 459 if err := os.Rename(tmpRootDir, rootDir); err != nil { 460 return 0, err 461 } 462 463 return 464 } 465 466 // Exists checks to see if the id is already mounted. 467 func (d *Driver) Exists(id string) bool { 468 _, err := os.Stat(d.dir(id)) 469 return err == nil 470 }