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