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