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