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