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