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