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