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