github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/daemon/graphdriver/aufs/aufs.go (about) 1 // +build linux 2 3 /* 4 5 aufs driver directory structure 6 7 . 8 ├── layers // Metadata of layers 9 │ ├── 1 10 │ ├── 2 11 │ └── 3 12 ├── diff // Content of the layer 13 │ ├── 1 // Contains layers that need to be mounted for the id 14 │ ├── 2 15 │ └── 3 16 └── mnt // Mount points for the rw layers to be mounted 17 ├── 1 18 ├── 2 19 └── 3 20 21 */ 22 23 package aufs 24 25 import ( 26 "bufio" 27 "fmt" 28 "io" 29 "io/ioutil" 30 "os" 31 "os/exec" 32 "path" 33 "path/filepath" 34 "strings" 35 "sync" 36 "time" 37 38 "github.com/docker/docker/daemon/graphdriver" 39 "github.com/docker/docker/pkg/archive" 40 "github.com/docker/docker/pkg/chrootarchive" 41 "github.com/docker/docker/pkg/containerfs" 42 "github.com/docker/docker/pkg/directory" 43 "github.com/docker/docker/pkg/idtools" 44 "github.com/docker/docker/pkg/locker" 45 mountpk "github.com/docker/docker/pkg/mount" 46 "github.com/docker/docker/pkg/system" 47 rsystem "github.com/opencontainers/runc/libcontainer/system" 48 "github.com/opencontainers/selinux/go-selinux/label" 49 "github.com/pkg/errors" 50 "github.com/sirupsen/logrus" 51 "github.com/vbatts/tar-split/tar/storage" 52 "golang.org/x/sys/unix" 53 ) 54 55 var ( 56 // ErrAufsNotSupported is returned if aufs is not supported by the host. 57 ErrAufsNotSupported = fmt.Errorf("AUFS was not found in /proc/filesystems") 58 // ErrAufsNested means aufs cannot be used bc we are in a user namespace 59 ErrAufsNested = fmt.Errorf("AUFS cannot be used in non-init user namespace") 60 backingFs = "<unknown>" 61 62 enableDirpermLock sync.Once 63 enableDirperm bool 64 ) 65 66 func init() { 67 graphdriver.Register("aufs", Init) 68 } 69 70 // Driver contains information about the filesystem mounted. 71 type Driver struct { 72 sync.Mutex 73 root string 74 uidMaps []idtools.IDMap 75 gidMaps []idtools.IDMap 76 ctr *graphdriver.RefCounter 77 pathCacheLock sync.Mutex 78 pathCache map[string]string 79 naiveDiff graphdriver.DiffDriver 80 locker *locker.Locker 81 } 82 83 // Init returns a new AUFS driver. 84 // An error is returned if AUFS is not supported. 85 func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 86 87 // Try to load the aufs kernel module 88 if err := supportsAufs(); err != nil { 89 return nil, graphdriver.ErrNotSupported 90 } 91 92 // Perform feature detection on /var/lib/docker/aufs if it's an existing directory. 93 // This covers situations where /var/lib/docker/aufs is a mount, and on a different 94 // filesystem than /var/lib/docker. 95 // If the path does not exist, fall back to using /var/lib/docker for feature detection. 96 testdir := root 97 if _, err := os.Stat(testdir); os.IsNotExist(err) { 98 testdir = filepath.Dir(testdir) 99 } 100 101 fsMagic, err := graphdriver.GetFSMagic(testdir) 102 if err != nil { 103 return nil, err 104 } 105 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 106 backingFs = fsName 107 } 108 109 switch fsMagic { 110 case graphdriver.FsMagicAufs, graphdriver.FsMagicBtrfs, graphdriver.FsMagicEcryptfs: 111 logrus.Errorf("AUFS is not supported over %s", backingFs) 112 return nil, graphdriver.ErrIncompatibleFS 113 } 114 115 paths := []string{ 116 "mnt", 117 "diff", 118 "layers", 119 } 120 121 a := &Driver{ 122 root: root, 123 uidMaps: uidMaps, 124 gidMaps: gidMaps, 125 pathCache: make(map[string]string), 126 ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicAufs)), 127 locker: locker.New(), 128 } 129 130 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 131 if err != nil { 132 return nil, err 133 } 134 // Create the root aufs driver dir 135 if err := idtools.MkdirAllAndChown(root, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil { 136 return nil, err 137 } 138 139 // Populate the dir structure 140 for _, p := range paths { 141 if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil { 142 return nil, err 143 } 144 } 145 logger := logrus.WithFields(logrus.Fields{ 146 "module": "graphdriver", 147 "driver": "aufs", 148 }) 149 150 for _, path := range []string{"mnt", "diff"} { 151 p := filepath.Join(root, path) 152 entries, err := ioutil.ReadDir(p) 153 if err != nil { 154 logger.WithError(err).WithField("dir", p).Error("error reading dir entries") 155 continue 156 } 157 for _, entry := range entries { 158 if !entry.IsDir() { 159 continue 160 } 161 if strings.HasSuffix(entry.Name(), "-removing") { 162 logger.WithField("dir", entry.Name()).Debug("Cleaning up stale layer dir") 163 if err := system.EnsureRemoveAll(filepath.Join(p, entry.Name())); err != nil { 164 logger.WithField("dir", entry.Name()).WithError(err).Error("Error removing stale layer dir") 165 } 166 } 167 } 168 } 169 170 a.naiveDiff = graphdriver.NewNaiveDiffDriver(a, uidMaps, gidMaps) 171 return a, nil 172 } 173 174 // Return a nil error if the kernel supports aufs 175 // We cannot modprobe because inside dind modprobe fails 176 // to run 177 func supportsAufs() error { 178 // We can try to modprobe aufs first before looking at 179 // proc/filesystems for when aufs is supported 180 exec.Command("modprobe", "aufs").Run() 181 182 if rsystem.RunningInUserNS() { 183 return ErrAufsNested 184 } 185 186 f, err := os.Open("/proc/filesystems") 187 if err != nil { 188 return err 189 } 190 defer f.Close() 191 192 s := bufio.NewScanner(f) 193 for s.Scan() { 194 if strings.Contains(s.Text(), "aufs") { 195 return nil 196 } 197 } 198 return ErrAufsNotSupported 199 } 200 201 func (a *Driver) rootPath() string { 202 return a.root 203 } 204 205 func (*Driver) String() string { 206 return "aufs" 207 } 208 209 // Status returns current information about the filesystem such as root directory, number of directories mounted, etc. 210 func (a *Driver) Status() [][2]string { 211 ids, _ := loadIds(path.Join(a.rootPath(), "layers")) 212 return [][2]string{ 213 {"Root Dir", a.rootPath()}, 214 {"Backing Filesystem", backingFs}, 215 {"Dirs", fmt.Sprintf("%d", len(ids))}, 216 {"Dirperm1 Supported", fmt.Sprintf("%v", useDirperm())}, 217 } 218 } 219 220 // GetMetadata not implemented 221 func (a *Driver) GetMetadata(id string) (map[string]string, error) { 222 return nil, nil 223 } 224 225 // Exists returns true if the given id is registered with 226 // this driver 227 func (a *Driver) Exists(id string) bool { 228 if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil { 229 return false 230 } 231 return true 232 } 233 234 // CreateReadWrite creates a layer that is writable for use as a container 235 // file system. 236 func (a *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { 237 return a.Create(id, parent, opts) 238 } 239 240 // Create three folders for each id 241 // mnt, layers, and diff 242 func (a *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { 243 244 if opts != nil && len(opts.StorageOpt) != 0 { 245 return fmt.Errorf("--storage-opt is not supported for aufs") 246 } 247 248 if err := a.createDirsFor(id); err != nil { 249 return err 250 } 251 // Write the layers metadata 252 f, err := os.Create(path.Join(a.rootPath(), "layers", id)) 253 if err != nil { 254 return err 255 } 256 defer f.Close() 257 258 if parent != "" { 259 ids, err := getParentIDs(a.rootPath(), parent) 260 if err != nil { 261 return err 262 } 263 264 if _, err := fmt.Fprintln(f, parent); err != nil { 265 return err 266 } 267 for _, i := range ids { 268 if _, err := fmt.Fprintln(f, i); err != nil { 269 return err 270 } 271 } 272 } 273 274 return nil 275 } 276 277 // createDirsFor creates two directories for the given id. 278 // mnt and diff 279 func (a *Driver) createDirsFor(id string) error { 280 paths := []string{ 281 "mnt", 282 "diff", 283 } 284 285 rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps) 286 if err != nil { 287 return err 288 } 289 // Directory permission is 0755. 290 // The path of directories are <aufs_root_path>/mnt/<image_id> 291 // and <aufs_root_path>/diff/<image_id> 292 for _, p := range paths { 293 if err := idtools.MkdirAllAndChown(path.Join(a.rootPath(), p, id), 0755, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil { 294 return err 295 } 296 } 297 return nil 298 } 299 300 // Remove will unmount and remove the given id. 301 func (a *Driver) Remove(id string) error { 302 a.locker.Lock(id) 303 defer a.locker.Unlock(id) 304 a.pathCacheLock.Lock() 305 mountpoint, exists := a.pathCache[id] 306 a.pathCacheLock.Unlock() 307 if !exists { 308 mountpoint = a.getMountpoint(id) 309 } 310 311 logger := logrus.WithFields(logrus.Fields{ 312 "module": "graphdriver", 313 "driver": "aufs", 314 "layer": id, 315 }) 316 317 var retries int 318 for { 319 mounted, err := a.mounted(mountpoint) 320 if err != nil { 321 if os.IsNotExist(err) { 322 break 323 } 324 return err 325 } 326 if !mounted { 327 break 328 } 329 330 err = a.unmount(mountpoint) 331 if err == nil { 332 break 333 } 334 335 if err != unix.EBUSY { 336 return errors.Wrapf(err, "aufs: unmount error: %s", mountpoint) 337 } 338 if retries >= 5 { 339 return errors.Wrapf(err, "aufs: unmount error after retries: %s", mountpoint) 340 } 341 // If unmount returns EBUSY, it could be a transient error. Sleep and retry. 342 retries++ 343 logger.Warnf("unmount failed due to EBUSY: retry count: %d", retries) 344 time.Sleep(100 * time.Millisecond) 345 } 346 347 // Remove the layers file for the id 348 if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) { 349 return errors.Wrapf(err, "error removing layers dir for %s", id) 350 } 351 352 if err := atomicRemove(a.getDiffPath(id)); err != nil { 353 return errors.Wrapf(err, "could not remove diff path for id %s", id) 354 } 355 356 // Atomically remove each directory in turn by first moving it out of the 357 // way (so that docker doesn't find it anymore) before doing removal of 358 // the whole tree. 359 if err := atomicRemove(mountpoint); err != nil { 360 if errors.Cause(err) == unix.EBUSY { 361 logger.WithField("dir", mountpoint).WithError(err).Warn("error performing atomic remove due to EBUSY") 362 } 363 return errors.Wrapf(err, "could not remove mountpoint for id %s", id) 364 } 365 366 a.pathCacheLock.Lock() 367 delete(a.pathCache, id) 368 a.pathCacheLock.Unlock() 369 return nil 370 } 371 372 func atomicRemove(source string) error { 373 target := source + "-removing" 374 375 err := os.Rename(source, target) 376 switch { 377 case err == nil, os.IsNotExist(err): 378 case os.IsExist(err): 379 // Got error saying the target dir already exists, maybe the source doesn't exist due to a previous (failed) remove 380 if _, e := os.Stat(source); !os.IsNotExist(e) { 381 return errors.Wrapf(err, "target rename dir '%s' exists but should not, this needs to be manually cleaned up") 382 } 383 default: 384 return errors.Wrapf(err, "error preparing atomic delete") 385 } 386 387 return system.EnsureRemoveAll(target) 388 } 389 390 // Get returns the rootfs path for the id. 391 // This will mount the dir at its given path 392 func (a *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { 393 a.locker.Lock(id) 394 defer a.locker.Unlock(id) 395 parents, err := a.getParentLayerPaths(id) 396 if err != nil && !os.IsNotExist(err) { 397 return nil, err 398 } 399 400 a.pathCacheLock.Lock() 401 m, exists := a.pathCache[id] 402 a.pathCacheLock.Unlock() 403 404 if !exists { 405 m = a.getDiffPath(id) 406 if len(parents) > 0 { 407 m = a.getMountpoint(id) 408 } 409 } 410 if count := a.ctr.Increment(m); count > 1 { 411 return containerfs.NewLocalContainerFS(m), nil 412 } 413 414 // If a dir does not have a parent ( no layers )do not try to mount 415 // just return the diff path to the data 416 if len(parents) > 0 { 417 if err := a.mount(id, m, mountLabel, parents); err != nil { 418 return nil, err 419 } 420 } 421 422 a.pathCacheLock.Lock() 423 a.pathCache[id] = m 424 a.pathCacheLock.Unlock() 425 return containerfs.NewLocalContainerFS(m), nil 426 } 427 428 // Put unmounts and updates list of active mounts. 429 func (a *Driver) Put(id string) error { 430 a.locker.Lock(id) 431 defer a.locker.Unlock(id) 432 a.pathCacheLock.Lock() 433 m, exists := a.pathCache[id] 434 if !exists { 435 m = a.getMountpoint(id) 436 a.pathCache[id] = m 437 } 438 a.pathCacheLock.Unlock() 439 if count := a.ctr.Decrement(m); count > 0 { 440 return nil 441 } 442 443 err := a.unmount(m) 444 if err != nil { 445 logrus.Debugf("Failed to unmount %s aufs: %v", id, err) 446 } 447 return err 448 } 449 450 // isParent returns if the passed in parent is the direct parent of the passed in layer 451 func (a *Driver) isParent(id, parent string) bool { 452 parents, _ := getParentIDs(a.rootPath(), id) 453 if parent == "" && len(parents) > 0 { 454 return false 455 } 456 return !(len(parents) > 0 && parent != parents[0]) 457 } 458 459 // Diff produces an archive of the changes between the specified 460 // layer and its parent layer which may be "". 461 func (a *Driver) Diff(id, parent string) (io.ReadCloser, error) { 462 if !a.isParent(id, parent) { 463 return a.naiveDiff.Diff(id, parent) 464 } 465 466 // AUFS doesn't need the parent layer to produce a diff. 467 return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ 468 Compression: archive.Uncompressed, 469 ExcludePatterns: []string{archive.WhiteoutMetaPrefix + "*", "!" + archive.WhiteoutOpaqueDir}, 470 UIDMaps: a.uidMaps, 471 GIDMaps: a.gidMaps, 472 }) 473 } 474 475 type fileGetNilCloser struct { 476 storage.FileGetter 477 } 478 479 func (f fileGetNilCloser) Close() error { 480 return nil 481 } 482 483 // DiffGetter returns a FileGetCloser that can read files from the directory that 484 // contains files for the layer differences. Used for direct access for tar-split. 485 func (a *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { 486 p := path.Join(a.rootPath(), "diff", id) 487 return fileGetNilCloser{storage.NewPathFileGetter(p)}, nil 488 } 489 490 func (a *Driver) applyDiff(id string, diff io.Reader) error { 491 return chrootarchive.UntarUncompressed(diff, path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ 492 UIDMaps: a.uidMaps, 493 GIDMaps: a.gidMaps, 494 }) 495 } 496 497 // DiffSize calculates the changes between the specified id 498 // and its parent and returns the size in bytes of the changes 499 // relative to its base filesystem directory. 500 func (a *Driver) DiffSize(id, parent string) (size int64, err error) { 501 if !a.isParent(id, parent) { 502 return a.naiveDiff.DiffSize(id, parent) 503 } 504 // AUFS doesn't need the parent layer to calculate the diff size. 505 return directory.Size(path.Join(a.rootPath(), "diff", id)) 506 } 507 508 // ApplyDiff extracts the changeset from the given diff into the 509 // layer with the specified id and parent, returning the size of the 510 // new layer in bytes. 511 func (a *Driver) ApplyDiff(id, parent string, diff io.Reader) (size int64, err error) { 512 if !a.isParent(id, parent) { 513 return a.naiveDiff.ApplyDiff(id, parent, diff) 514 } 515 516 // AUFS doesn't need the parent id to apply the diff if it is the direct parent. 517 if err = a.applyDiff(id, diff); err != nil { 518 return 519 } 520 521 return a.DiffSize(id, parent) 522 } 523 524 // Changes produces a list of changes between the specified layer 525 // and its parent layer. If parent is "", then all changes will be ADD changes. 526 func (a *Driver) Changes(id, parent string) ([]archive.Change, error) { 527 if !a.isParent(id, parent) { 528 return a.naiveDiff.Changes(id, parent) 529 } 530 531 // AUFS doesn't have snapshots, so we need to get changes from all parent 532 // layers. 533 layers, err := a.getParentLayerPaths(id) 534 if err != nil { 535 return nil, err 536 } 537 return archive.Changes(layers, path.Join(a.rootPath(), "diff", id)) 538 } 539 540 func (a *Driver) getParentLayerPaths(id string) ([]string, error) { 541 parentIds, err := getParentIDs(a.rootPath(), id) 542 if err != nil { 543 return nil, err 544 } 545 layers := make([]string, len(parentIds)) 546 547 // Get the diff paths for all the parent ids 548 for i, p := range parentIds { 549 layers[i] = path.Join(a.rootPath(), "diff", p) 550 } 551 return layers, nil 552 } 553 554 func (a *Driver) mount(id string, target string, mountLabel string, layers []string) error { 555 a.Lock() 556 defer a.Unlock() 557 558 // If the id is mounted or we get an error return 559 if mounted, err := a.mounted(target); err != nil || mounted { 560 return err 561 } 562 563 rw := a.getDiffPath(id) 564 565 if err := a.aufsMount(layers, rw, target, mountLabel); err != nil { 566 return fmt.Errorf("error creating aufs mount to %s: %v", target, err) 567 } 568 return nil 569 } 570 571 func (a *Driver) unmount(mountPath string) error { 572 a.Lock() 573 defer a.Unlock() 574 575 if mounted, err := a.mounted(mountPath); err != nil || !mounted { 576 return err 577 } 578 if err := Unmount(mountPath); err != nil { 579 return err 580 } 581 return nil 582 } 583 584 func (a *Driver) mounted(mountpoint string) (bool, error) { 585 return graphdriver.Mounted(graphdriver.FsMagicAufs, mountpoint) 586 } 587 588 // Cleanup aufs and unmount all mountpoints 589 func (a *Driver) Cleanup() error { 590 var dirs []string 591 if err := filepath.Walk(a.mntPath(), func(path string, info os.FileInfo, err error) error { 592 if err != nil { 593 return err 594 } 595 if !info.IsDir() { 596 return nil 597 } 598 dirs = append(dirs, path) 599 return nil 600 }); err != nil { 601 return err 602 } 603 604 for _, m := range dirs { 605 if err := a.unmount(m); err != nil { 606 logrus.Debugf("aufs error unmounting %s: %s", m, err) 607 } 608 } 609 return mountpk.RecursiveUnmount(a.root) 610 } 611 612 func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) { 613 defer func() { 614 if err != nil { 615 Unmount(target) 616 } 617 }() 618 619 // Mount options are clipped to page size(4096 bytes). If there are more 620 // layers then these are remounted individually using append. 621 622 offset := 54 623 if useDirperm() { 624 offset += len(",dirperm1") 625 } 626 b := make([]byte, unix.Getpagesize()-len(mountLabel)-offset) // room for xino & mountLabel 627 bp := copy(b, fmt.Sprintf("br:%s=rw", rw)) 628 629 index := 0 630 for ; index < len(ro); index++ { 631 layer := fmt.Sprintf(":%s=ro+wh", ro[index]) 632 if bp+len(layer) > len(b) { 633 break 634 } 635 bp += copy(b[bp:], layer) 636 } 637 638 opts := "dio,xino=/dev/shm/aufs.xino" 639 if useDirperm() { 640 opts += ",dirperm1" 641 } 642 data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), mountLabel) 643 if err = mount("none", target, "aufs", 0, data); err != nil { 644 return 645 } 646 647 for ; index < len(ro); index++ { 648 layer := fmt.Sprintf(":%s=ro+wh", ro[index]) 649 data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), mountLabel) 650 if err = mount("none", target, "aufs", unix.MS_REMOUNT, data); err != nil { 651 return 652 } 653 } 654 655 return 656 } 657 658 // useDirperm checks dirperm1 mount option can be used with the current 659 // version of aufs. 660 func useDirperm() bool { 661 enableDirpermLock.Do(func() { 662 base, err := ioutil.TempDir("", "docker-aufs-base") 663 if err != nil { 664 logrus.Errorf("error checking dirperm1: %v", err) 665 return 666 } 667 defer os.RemoveAll(base) 668 669 union, err := ioutil.TempDir("", "docker-aufs-union") 670 if err != nil { 671 logrus.Errorf("error checking dirperm1: %v", err) 672 return 673 } 674 defer os.RemoveAll(union) 675 676 opts := fmt.Sprintf("br:%s,dirperm1,xino=/dev/shm/aufs.xino", base) 677 if err := mount("none", union, "aufs", 0, opts); err != nil { 678 return 679 } 680 enableDirperm = true 681 if err := Unmount(union); err != nil { 682 logrus.Errorf("error checking dirperm1: failed to unmount %v", err) 683 } 684 }) 685 return enableDirperm 686 }