github.com/boynux/docker@v1.11.0-rc4/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 "github.com/docker/docker/pkg/stringid" 47 48 "github.com/opencontainers/runc/libcontainer/label" 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 incompatibleFsMagic = []graphdriver.FsMagic{ 55 graphdriver.FsMagicBtrfs, 56 graphdriver.FsMagicAufs, 57 } 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 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 for _, magic := range incompatibleFsMagic { 96 if fsMagic == magic { 97 return nil, graphdriver.ErrIncompatibleFS 98 } 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 } 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 f, err := os.Open("/proc/filesystems") 150 if err != nil { 151 return err 152 } 153 defer f.Close() 154 155 s := bufio.NewScanner(f) 156 for s.Scan() { 157 if strings.Contains(s.Text(), "aufs") { 158 return nil 159 } 160 } 161 return ErrAufsNotSupported 162 } 163 164 func (a *Driver) rootPath() string { 165 return a.root 166 } 167 168 func (*Driver) String() string { 169 return "aufs" 170 } 171 172 // Status returns current information about the filesystem such as root directory, number of directories mounted, etc. 173 func (a *Driver) Status() [][2]string { 174 ids, _ := loadIds(path.Join(a.rootPath(), "layers")) 175 return [][2]string{ 176 {"Root Dir", a.rootPath()}, 177 {"Backing Filesystem", backingFs}, 178 {"Dirs", fmt.Sprintf("%d", len(ids))}, 179 {"Dirperm1 Supported", fmt.Sprintf("%v", useDirperm())}, 180 } 181 } 182 183 // GetMetadata not implemented 184 func (a *Driver) GetMetadata(id string) (map[string]string, error) { 185 return nil, nil 186 } 187 188 // Exists returns true if the given id is registered with 189 // this driver 190 func (a *Driver) Exists(id string) bool { 191 if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil { 192 return false 193 } 194 return true 195 } 196 197 // Create three folders for each id 198 // mnt, layers, and diff 199 func (a *Driver) Create(id, parent, mountLabel string) error { 200 if err := a.createDirsFor(id); err != nil { 201 return err 202 } 203 // Write the layers metadata 204 f, err := os.Create(path.Join(a.rootPath(), "layers", id)) 205 if err != nil { 206 return err 207 } 208 defer f.Close() 209 210 if parent != "" { 211 ids, err := getParentIds(a.rootPath(), parent) 212 if err != nil { 213 return err 214 } 215 216 if _, err := fmt.Fprintln(f, parent); err != nil { 217 return err 218 } 219 for _, i := range ids { 220 if _, err := fmt.Fprintln(f, i); err != nil { 221 return err 222 } 223 } 224 } 225 226 return nil 227 } 228 229 // createDirsFor creates two directories for the given id. 230 // mnt and diff 231 func (a *Driver) createDirsFor(id string) error { 232 paths := []string{ 233 "mnt", 234 "diff", 235 } 236 237 rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps) 238 if err != nil { 239 return err 240 } 241 // Directory permission is 0755. 242 // The path of directories are <aufs_root_path>/mnt/<image_id> 243 // and <aufs_root_path>/diff/<image_id> 244 for _, p := range paths { 245 if err := idtools.MkdirAllAs(path.Join(a.rootPath(), p, id), 0755, rootUID, rootGID); err != nil { 246 return err 247 } 248 } 249 return nil 250 } 251 252 // Remove will unmount and remove the given id. 253 func (a *Driver) Remove(id string) error { 254 a.pathCacheLock.Lock() 255 mountpoint, exists := a.pathCache[id] 256 a.pathCacheLock.Unlock() 257 if !exists { 258 mountpoint = a.getMountpoint(id) 259 } 260 if err := a.unmount(mountpoint); err != nil { 261 // no need to return here, we can still try to remove since the `Rename` will fail below if still mounted 262 logrus.Debugf("aufs: error while unmounting %s: %v", mountpoint, err) 263 } 264 265 // Atomically remove each directory in turn by first moving it out of the 266 // way (so that docker doesn't find it anymore) before doing removal of 267 // the whole tree. 268 tmpMntPath := path.Join(a.mntPath(), fmt.Sprintf("%s-removing", id)) 269 if err := os.Rename(mountpoint, tmpMntPath); err != nil && !os.IsNotExist(err) { 270 return err 271 } 272 defer os.RemoveAll(tmpMntPath) 273 274 tmpDiffpath := path.Join(a.diffPath(), fmt.Sprintf("%s-removing", id)) 275 if err := os.Rename(a.getDiffPath(id), tmpDiffpath); err != nil && !os.IsNotExist(err) { 276 return err 277 } 278 defer os.RemoveAll(tmpDiffpath) 279 280 // Remove the layers file for the id 281 if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) { 282 return err 283 } 284 285 a.pathCacheLock.Lock() 286 delete(a.pathCache, id) 287 a.pathCacheLock.Unlock() 288 return nil 289 } 290 291 // Get returns the rootfs path for the id. 292 // This will mount the dir at it's given path 293 func (a *Driver) Get(id, mountLabel string) (string, error) { 294 parents, err := a.getParentLayerPaths(id) 295 if err != nil && !os.IsNotExist(err) { 296 return "", err 297 } 298 299 a.pathCacheLock.Lock() 300 m, exists := a.pathCache[id] 301 a.pathCacheLock.Unlock() 302 303 if !exists { 304 m = a.getDiffPath(id) 305 if len(parents) > 0 { 306 m = a.getMountpoint(id) 307 } 308 } 309 310 // If a dir does not have a parent ( no layers )do not try to mount 311 // just return the diff path to the data 312 if len(parents) > 0 { 313 if err := a.mount(id, m, mountLabel, parents); err != nil { 314 return "", err 315 } 316 } 317 318 a.pathCacheLock.Lock() 319 a.pathCache[id] = m 320 a.pathCacheLock.Unlock() 321 return m, nil 322 } 323 324 // Put unmounts and updates list of active mounts. 325 func (a *Driver) Put(id string) error { 326 a.pathCacheLock.Lock() 327 m, exists := a.pathCache[id] 328 if !exists { 329 m = a.getMountpoint(id) 330 a.pathCache[id] = m 331 } 332 a.pathCacheLock.Unlock() 333 334 err := a.unmount(m) 335 if err != nil { 336 logrus.Debugf("Failed to unmount %s aufs: %v", id, err) 337 } 338 return err 339 } 340 341 // Diff produces an archive of the changes between the specified 342 // layer and its parent layer which may be "". 343 func (a *Driver) Diff(id, parent string) (archive.Archive, error) { 344 // AUFS doesn't need the parent layer to produce a diff. 345 return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ 346 Compression: archive.Uncompressed, 347 ExcludePatterns: []string{archive.WhiteoutMetaPrefix + "*", "!" + archive.WhiteoutOpaqueDir}, 348 UIDMaps: a.uidMaps, 349 GIDMaps: a.gidMaps, 350 }) 351 } 352 353 type fileGetNilCloser struct { 354 storage.FileGetter 355 } 356 357 func (f fileGetNilCloser) Close() error { 358 return nil 359 } 360 361 // DiffGetter returns a FileGetCloser that can read files from the directory that 362 // contains files for the layer differences. Used for direct access for tar-split. 363 func (a *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { 364 p := path.Join(a.rootPath(), "diff", id) 365 return fileGetNilCloser{storage.NewPathFileGetter(p)}, nil 366 } 367 368 func (a *Driver) applyDiff(id string, diff archive.Reader) error { 369 return chrootarchive.UntarUncompressed(diff, path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ 370 UIDMaps: a.uidMaps, 371 GIDMaps: a.gidMaps, 372 }) 373 } 374 375 // DiffSize calculates the changes between the specified id 376 // and its parent and returns the size in bytes of the changes 377 // relative to its base filesystem directory. 378 func (a *Driver) DiffSize(id, parent string) (size int64, err error) { 379 // AUFS doesn't need the parent layer to calculate the diff size. 380 return directory.Size(path.Join(a.rootPath(), "diff", id)) 381 } 382 383 // ApplyDiff extracts the changeset from the given diff into the 384 // layer with the specified id and parent, returning the size of the 385 // new layer in bytes. 386 func (a *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) { 387 // AUFS doesn't need the parent id to apply the diff. 388 if err = a.applyDiff(id, diff); err != nil { 389 return 390 } 391 392 return a.DiffSize(id, parent) 393 } 394 395 // Changes produces a list of changes between the specified layer 396 // and its parent layer. If parent is "", then all changes will be ADD changes. 397 func (a *Driver) Changes(id, parent string) ([]archive.Change, error) { 398 // AUFS doesn't have snapshots, so we need to get changes from all parent 399 // layers. 400 layers, err := a.getParentLayerPaths(id) 401 if err != nil { 402 return nil, err 403 } 404 return archive.Changes(layers, path.Join(a.rootPath(), "diff", id)) 405 } 406 407 func (a *Driver) getParentLayerPaths(id string) ([]string, error) { 408 parentIds, err := getParentIds(a.rootPath(), id) 409 if err != nil { 410 return nil, err 411 } 412 layers := make([]string, len(parentIds)) 413 414 // Get the diff paths for all the parent ids 415 for i, p := range parentIds { 416 layers[i] = path.Join(a.rootPath(), "diff", p) 417 } 418 return layers, nil 419 } 420 421 func (a *Driver) mount(id string, target string, mountLabel string, layers []string) error { 422 a.Lock() 423 defer a.Unlock() 424 425 // If the id is mounted or we get an error return 426 if mounted, err := a.mounted(target); err != nil || mounted { 427 return err 428 } 429 430 rw := a.getDiffPath(id) 431 432 if err := a.aufsMount(layers, rw, target, mountLabel); err != nil { 433 return fmt.Errorf("error creating aufs mount to %s: %v", target, err) 434 } 435 return nil 436 } 437 438 func (a *Driver) unmount(mountPath string) error { 439 a.Lock() 440 defer a.Unlock() 441 442 if mounted, err := a.mounted(mountPath); err != nil || !mounted { 443 return err 444 } 445 if err := Unmount(mountPath); err != nil { 446 return err 447 } 448 return nil 449 } 450 451 func (a *Driver) mounted(mountpoint string) (bool, error) { 452 return graphdriver.Mounted(graphdriver.FsMagicAufs, mountpoint) 453 } 454 455 // Cleanup aufs and unmount all mountpoints 456 func (a *Driver) Cleanup() error { 457 var dirs []string 458 if err := filepath.Walk(a.mntPath(), func(path string, info os.FileInfo, err error) error { 459 if err != nil { 460 return err 461 } 462 if !info.IsDir() { 463 return nil 464 } 465 dirs = append(dirs, path) 466 return nil 467 }); err != nil { 468 return err 469 } 470 471 for _, m := range dirs { 472 if err := a.unmount(m); err != nil { 473 logrus.Debugf("aufs error unmounting %s: %s", stringid.TruncateID(m), err) 474 } 475 } 476 return mountpk.Unmount(a.root) 477 } 478 479 func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) { 480 defer func() { 481 if err != nil { 482 Unmount(target) 483 } 484 }() 485 486 // Mount options are clipped to page size(4096 bytes). If there are more 487 // layers then these are remounted individually using append. 488 489 offset := 54 490 if useDirperm() { 491 offset += len("dirperm1") 492 } 493 b := make([]byte, syscall.Getpagesize()-len(mountLabel)-offset) // room for xino & mountLabel 494 bp := copy(b, fmt.Sprintf("br:%s=rw", rw)) 495 496 firstMount := true 497 i := 0 498 499 for { 500 for ; i < len(ro); i++ { 501 layer := fmt.Sprintf(":%s=ro+wh", ro[i]) 502 503 if firstMount { 504 if bp+len(layer) > len(b) { 505 break 506 } 507 bp += copy(b[bp:], layer) 508 } else { 509 data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), mountLabel) 510 if err = mount("none", target, "aufs", syscall.MS_REMOUNT, data); err != nil { 511 return 512 } 513 } 514 } 515 516 if firstMount { 517 opts := "dio,xino=/dev/shm/aufs.xino" 518 if useDirperm() { 519 opts += ",dirperm1" 520 } 521 data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), mountLabel) 522 if err = mount("none", target, "aufs", 0, data); err != nil { 523 return 524 } 525 firstMount = false 526 } 527 528 if i == len(ro) { 529 break 530 } 531 } 532 533 return 534 } 535 536 // useDirperm checks dirperm1 mount option can be used with the current 537 // version of aufs. 538 func useDirperm() bool { 539 enableDirpermLock.Do(func() { 540 base, err := ioutil.TempDir("", "docker-aufs-base") 541 if err != nil { 542 logrus.Errorf("error checking dirperm1: %v", err) 543 return 544 } 545 defer os.RemoveAll(base) 546 547 union, err := ioutil.TempDir("", "docker-aufs-union") 548 if err != nil { 549 logrus.Errorf("error checking dirperm1: %v", err) 550 return 551 } 552 defer os.RemoveAll(union) 553 554 opts := fmt.Sprintf("br:%s,dirperm1,xino=/dev/shm/aufs.xino", base) 555 if err := mount("none", union, "aufs", 0, opts); err != nil { 556 return 557 } 558 enableDirperm = true 559 if err := Unmount(union); err != nil { 560 logrus.Errorf("error checking dirperm1: failed to unmount %v", err) 561 } 562 }) 563 return enableDirperm 564 }