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