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