github.com/kobeld/docker@v1.12.0-rc1/daemon/graphdriver/overlay2/overlay.go (about) 1 // +build linux 2 3 package overlay2 4 5 import ( 6 "bufio" 7 "errors" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path" 13 "strings" 14 "syscall" 15 16 "github.com/Sirupsen/logrus" 17 18 "github.com/docker/docker/daemon/graphdriver" 19 "github.com/docker/docker/pkg/archive" 20 "github.com/docker/docker/pkg/chrootarchive" 21 "github.com/docker/docker/pkg/directory" 22 "github.com/docker/docker/pkg/idtools" 23 "github.com/docker/docker/pkg/mount" 24 "github.com/docker/docker/pkg/parsers/kernel" 25 26 "github.com/opencontainers/runc/libcontainer/label" 27 ) 28 29 var ( 30 // untar defines the untar method 31 untar = chrootarchive.UntarUncompressed 32 ) 33 34 // This backend uses the overlay union filesystem for containers 35 // with diff directories for each layer. 36 37 // This version of the overlay driver requires at least kernel 38 // 4.0.0 in order to support mounting multiple diff directories. 39 40 // Each container/image has at least a "diff" directory and "link" file. 41 // If there is also a "lower" file when there are diff layers 42 // below as well as "merged" and "work" directories. The "diff" directory 43 // has the upper layer of the overlay and is used to capture any 44 // changes to the layer. The "lower" file contains all the lower layer 45 // mounts separated by ":" and ordered from uppermost to lowermost 46 // layers. The overlay itself is mounted in the "merged" directory, 47 // and the "work" dir is needed for overlay to work. 48 49 // The "link" file for each layer contains a unique string for the layer. 50 // Under the "l" directory at the root there will be a symbolic link 51 // with that unique string pointing the "diff" directory for the layer. 52 // The symbolic links are used to reference lower layers in the "lower" 53 // file and on mount. The links are used to shorten the total length 54 // of a layer reference without requiring changes to the layer identifier 55 // or root directory. Mounts are always done relative to root and 56 // referencing the symbolic links in order to ensure the number of 57 // lower directories can fit in a single page for making the mount 58 // syscall. A hard upper limit of 128 lower layers is enforced to ensure 59 // that mounts do not fail due to length. 60 61 const ( 62 driverName = "overlay2" 63 linkDir = "l" 64 lowerFile = "lower" 65 maxDepth = 128 66 67 // idLength represents the number of random characters 68 // which can be used to create the unique link identifer 69 // for every layer. If this value is too long then the 70 // page size limit for the mount command may be exceeded. 71 // The idLength should be selected such that following equation 72 // is true (512 is a buffer for label metadata). 73 // ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512) 74 idLength = 26 75 ) 76 77 // Driver contains information about the home directory and the list of active mounts that are created using this driver. 78 type Driver struct { 79 home string 80 uidMaps []idtools.IDMap 81 gidMaps []idtools.IDMap 82 ctr *graphdriver.RefCounter 83 } 84 85 var backingFs = "<unknown>" 86 87 func init() { 88 graphdriver.Register(driverName, Init) 89 } 90 91 // Init returns the a native diff driver for overlay filesystem. 92 // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. 93 // If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned. 94 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 95 96 if err := supportsOverlay(); err != nil { 97 return nil, graphdriver.ErrNotSupported 98 } 99 100 // require kernel 4.0.0 to ensure multiple lower dirs are supported 101 v, err := kernel.GetKernelVersion() 102 if err != nil { 103 return nil, err 104 } 105 if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 0, Minor: 0}) < 0 { 106 return nil, graphdriver.ErrNotSupported 107 } 108 109 fsMagic, err := graphdriver.GetFSMagic(home) 110 if err != nil { 111 return nil, err 112 } 113 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 114 backingFs = fsName 115 } 116 117 // check if they are running over btrfs, aufs, zfs or overlay 118 switch fsMagic { 119 case graphdriver.FsMagicBtrfs: 120 logrus.Error("'overlay' is not supported over btrfs.") 121 return nil, graphdriver.ErrIncompatibleFS 122 case graphdriver.FsMagicAufs: 123 logrus.Error("'overlay' is not supported over aufs.") 124 return nil, graphdriver.ErrIncompatibleFS 125 case graphdriver.FsMagicZfs: 126 logrus.Error("'overlay' is not supported over zfs.") 127 return nil, graphdriver.ErrIncompatibleFS 128 case graphdriver.FsMagicOverlay: 129 logrus.Error("'overlay' is not supported over overlay.") 130 return nil, graphdriver.ErrIncompatibleFS 131 } 132 133 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 134 if err != nil { 135 return nil, err 136 } 137 // Create the driver home dir 138 if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 139 return nil, err 140 } 141 142 if err := mount.MakePrivate(home); err != nil { 143 return nil, err 144 } 145 146 d := &Driver{ 147 home: home, 148 uidMaps: uidMaps, 149 gidMaps: gidMaps, 150 ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), 151 } 152 153 return d, nil 154 } 155 156 func supportsOverlay() error { 157 // We can try to modprobe overlay first before looking at 158 // proc/filesystems for when overlay is supported 159 exec.Command("modprobe", "overlay").Run() 160 161 f, err := os.Open("/proc/filesystems") 162 if err != nil { 163 return err 164 } 165 defer f.Close() 166 167 s := bufio.NewScanner(f) 168 for s.Scan() { 169 if s.Text() == "nodev\toverlay" { 170 return nil 171 } 172 } 173 logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 174 return graphdriver.ErrNotSupported 175 } 176 177 func (d *Driver) String() string { 178 return driverName 179 } 180 181 // Status returns current driver information in a two dimensional string array. 182 // Output contains "Backing Filesystem" used in this implementation. 183 func (d *Driver) Status() [][2]string { 184 return [][2]string{ 185 {"Backing Filesystem", backingFs}, 186 } 187 } 188 189 // GetMetadata returns meta data about the overlay driver such as 190 // LowerDir, UpperDir, WorkDir and MergeDir used to store data. 191 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 192 dir := d.dir(id) 193 if _, err := os.Stat(dir); err != nil { 194 return nil, err 195 } 196 197 metadata := map[string]string{ 198 "WorkDir": path.Join(dir, "work"), 199 "MergedDir": path.Join(dir, "merged"), 200 "UpperDir": path.Join(dir, "diff"), 201 } 202 203 lowerDirs, err := d.getLowerDirs(id) 204 if err != nil { 205 return nil, err 206 } 207 if len(lowerDirs) > 0 { 208 metadata["LowerDir"] = strings.Join(lowerDirs, ":") 209 } 210 211 return metadata, nil 212 } 213 214 // Cleanup any state created by overlay which should be cleaned when daemon 215 // is being shutdown. For now, we just have to unmount the bind mounted 216 // we had created. 217 func (d *Driver) Cleanup() error { 218 return mount.Unmount(d.home) 219 } 220 221 // CreateReadWrite creates a layer that is writable for use as a container 222 // file system. 223 func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error { 224 return d.Create(id, parent, mountLabel, storageOpt) 225 } 226 227 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. 228 // The parent filesystem is used to configure these directories for the overlay. 229 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) (retErr error) { 230 231 if len(storageOpt) != 0 { 232 return fmt.Errorf("--storage-opt is not supported for overlay") 233 } 234 235 dir := d.dir(id) 236 237 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 238 if err != nil { 239 return err 240 } 241 if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { 242 return err 243 } 244 if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { 245 return err 246 } 247 248 defer func() { 249 // Clean up on failure 250 if retErr != nil { 251 os.RemoveAll(dir) 252 } 253 }() 254 255 if err := idtools.MkdirAs(path.Join(dir, "diff"), 0755, rootUID, rootGID); err != nil { 256 return err 257 } 258 259 lid := generateID(idLength) 260 if err := os.Symlink(path.Join("..", id, "diff"), path.Join(d.home, linkDir, lid)); err != nil { 261 return err 262 } 263 264 // Write link id to link file 265 if err := ioutil.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil { 266 return err 267 } 268 269 // if no parent directory, done 270 if parent == "" { 271 return nil 272 } 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 281 lower, err := d.getLower(parent) 282 if err != nil { 283 return err 284 } 285 if lower != "" { 286 if err := ioutil.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil { 287 return err 288 } 289 } 290 291 return nil 292 } 293 294 func (d *Driver) getLower(parent string) (string, error) { 295 parentDir := d.dir(parent) 296 297 // Ensure parent exists 298 if _, err := os.Lstat(parentDir); err != nil { 299 return "", err 300 } 301 302 // Read Parent link fileA 303 parentLink, err := ioutil.ReadFile(path.Join(parentDir, "link")) 304 if err != nil { 305 return "", err 306 } 307 lowers := []string{path.Join(linkDir, string(parentLink))} 308 309 parentLower, err := ioutil.ReadFile(path.Join(parentDir, lowerFile)) 310 if err == nil { 311 parentLowers := strings.Split(string(parentLower), ":") 312 lowers = append(lowers, parentLowers...) 313 } 314 if len(lowers) > maxDepth { 315 return "", errors.New("max depth exceeded") 316 } 317 return strings.Join(lowers, ":"), nil 318 } 319 320 func (d *Driver) dir(id string) string { 321 return path.Join(d.home, id) 322 } 323 324 func (d *Driver) getLowerDirs(id string) ([]string, error) { 325 var lowersArray []string 326 lowers, err := ioutil.ReadFile(path.Join(d.dir(id), lowerFile)) 327 if err == nil { 328 for _, s := range strings.Split(string(lowers), ":") { 329 lp, err := os.Readlink(path.Join(d.home, s)) 330 if err != nil { 331 return nil, err 332 } 333 lowersArray = append(lowersArray, path.Clean(path.Join(d.home, "link", lp))) 334 } 335 } else if !os.IsNotExist(err) { 336 return nil, err 337 } 338 return lowersArray, nil 339 } 340 341 // Remove cleans the directories that are created for this id. 342 func (d *Driver) Remove(id string) error { 343 dir := d.dir(id) 344 lid, err := ioutil.ReadFile(path.Join(dir, "link")) 345 if err == nil { 346 if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil { 347 logrus.Debugf("Failed to remove link: %v", err) 348 } 349 } 350 351 if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) { 352 return err 353 } 354 return nil 355 } 356 357 // Get creates and mounts the required file system for the given id and returns the mount path. 358 func (d *Driver) Get(id string, mountLabel string) (s string, err error) { 359 dir := d.dir(id) 360 if _, err := os.Stat(dir); err != nil { 361 return "", err 362 } 363 364 diffDir := path.Join(dir, "diff") 365 lowers, err := ioutil.ReadFile(path.Join(dir, lowerFile)) 366 if err != nil { 367 // If no lower, just return diff directory 368 if os.IsNotExist(err) { 369 return diffDir, nil 370 } 371 return "", err 372 } 373 374 mergedDir := path.Join(dir, "merged") 375 if count := d.ctr.Increment(mergedDir); count > 1 { 376 return mergedDir, nil 377 } 378 defer func() { 379 if err != nil { 380 if c := d.ctr.Decrement(mergedDir); c <= 0 { 381 syscall.Unmount(mergedDir, 0) 382 } 383 } 384 }() 385 386 workDir := path.Join(dir, "work") 387 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work")) 388 mountLabel = label.FormatMountLabel(opts, mountLabel) 389 if len(mountLabel) > syscall.Getpagesize() { 390 return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountLabel)) 391 } 392 393 if err := mountFrom(d.home, "overlay", path.Join(id, "merged"), "overlay", mountLabel); err != nil { 394 return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) 395 } 396 397 // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a 398 // user namespace requires this to move a directory from lower to upper. 399 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 400 if err != nil { 401 return "", err 402 } 403 404 if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { 405 return "", err 406 } 407 408 return mergedDir, nil 409 } 410 411 // Put unmounts the mount path created for the give id. 412 func (d *Driver) Put(id string) error { 413 mountpoint := path.Join(d.dir(id), "merged") 414 if count := d.ctr.Decrement(mountpoint); count > 0 { 415 return nil 416 } 417 if err := syscall.Unmount(mountpoint, 0); err != nil { 418 logrus.Debugf("Failed to unmount %s overlay: %v", id, err) 419 } 420 return nil 421 } 422 423 // Exists checks to see if the id is already mounted. 424 func (d *Driver) Exists(id string) bool { 425 _, err := os.Stat(d.dir(id)) 426 return err == nil 427 } 428 429 // ApplyDiff applies the new layer into a root 430 func (d *Driver) ApplyDiff(id string, parent string, diff archive.Reader) (size int64, err error) { 431 applyDir := d.getDiffPath(id) 432 433 logrus.Debugf("Applying tar in %s", applyDir) 434 // Overlay doesn't need the parent id to apply the diff 435 if err := untar(diff, applyDir, &archive.TarOptions{ 436 UIDMaps: d.uidMaps, 437 GIDMaps: d.gidMaps, 438 WhiteoutFormat: archive.OverlayWhiteoutFormat, 439 }); err != nil { 440 return 0, err 441 } 442 443 return d.DiffSize(id, parent) 444 } 445 446 func (d *Driver) getDiffPath(id string) string { 447 dir := d.dir(id) 448 449 return path.Join(dir, "diff") 450 } 451 452 // DiffSize calculates the changes between the specified id 453 // and its parent and returns the size in bytes of the changes 454 // relative to its base filesystem directory. 455 func (d *Driver) DiffSize(id, parent string) (size int64, err error) { 456 return directory.Size(d.getDiffPath(id)) 457 } 458 459 // Diff produces an archive of the changes between the specified 460 // layer and its parent layer which may be "". 461 func (d *Driver) Diff(id, parent string) (archive.Archive, error) { 462 diffPath := d.getDiffPath(id) 463 logrus.Debugf("Tar with options on %s", diffPath) 464 return archive.TarWithOptions(diffPath, &archive.TarOptions{ 465 Compression: archive.Uncompressed, 466 UIDMaps: d.uidMaps, 467 GIDMaps: d.gidMaps, 468 WhiteoutFormat: archive.OverlayWhiteoutFormat, 469 }) 470 } 471 472 // Changes produces a list of changes between the specified layer 473 // and its parent layer. If parent is "", then all changes will be ADD changes. 474 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { 475 // Overlay doesn't have snapshots, so we need to get changes from all parent 476 // layers. 477 diffPath := d.getDiffPath(id) 478 layers, err := d.getLowerDirs(id) 479 if err != nil { 480 return nil, err 481 } 482 483 return archive.OverlayChanges(layers, diffPath) 484 }