github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/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 "strings" 33 "sync" 34 "syscall" 35 36 "github.com/Sirupsen/logrus" 37 38 "github.com/docker/docker/daemon/graphdriver" 39 "github.com/docker/docker/pkg/archive" 40 "github.com/docker/docker/pkg/chrootarchive" 41 "github.com/docker/docker/pkg/directory" 42 "github.com/docker/docker/pkg/idtools" 43 mountpk "github.com/docker/docker/pkg/mount" 44 "github.com/docker/docker/pkg/stringid" 45 46 "github.com/opencontainers/runc/libcontainer/label" 47 ) 48 49 var ( 50 // ErrAufsNotSupported is returned if aufs is not supported by the host. 51 ErrAufsNotSupported = fmt.Errorf("AUFS was not found in /proc/filesystems") 52 incompatibleFsMagic = []graphdriver.FsMagic{ 53 graphdriver.FsMagicBtrfs, 54 graphdriver.FsMagicAufs, 55 } 56 backingFs = "<unknown>" 57 58 enableDirpermLock sync.Once 59 enableDirperm bool 60 ) 61 62 func init() { 63 graphdriver.Register("aufs", Init) 64 } 65 66 type data struct { 67 referenceCount int 68 path string 69 } 70 71 // Driver contains information about the filesystem mounted. 72 // root of the filesystem 73 // sync.Mutex to protect against concurrent modifications 74 // active maps mount id to the count 75 type Driver struct { 76 root string 77 uidMaps []idtools.IDMap 78 gidMaps []idtools.IDMap 79 sync.Mutex // Protects concurrent modification to active 80 active map[string]*data 81 } 82 83 // Init returns a new AUFS driver. 84 // An error is returned if AUFS is not supported. 85 func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 86 87 // Try to load the aufs kernel module 88 if err := supportsAufs(); err != nil { 89 return nil, graphdriver.ErrNotSupported 90 } 91 92 fsMagic, err := graphdriver.GetFSMagic(root) 93 if err != nil { 94 return nil, err 95 } 96 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 97 backingFs = fsName 98 } 99 100 for _, magic := range incompatibleFsMagic { 101 if fsMagic == magic { 102 return nil, graphdriver.ErrIncompatibleFS 103 } 104 } 105 106 paths := []string{ 107 "mnt", 108 "diff", 109 "layers", 110 } 111 112 a := &Driver{ 113 root: root, 114 active: make(map[string]*data), 115 uidMaps: uidMaps, 116 gidMaps: gidMaps, 117 } 118 119 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 120 if err != nil { 121 return nil, err 122 } 123 // Create the root aufs driver dir and return 124 // if it already exists 125 // If not populate the dir structure 126 if err := idtools.MkdirAllAs(root, 0700, rootUID, rootGID); err != nil { 127 if os.IsExist(err) { 128 return a, nil 129 } 130 return nil, err 131 } 132 133 if err := mountpk.MakePrivate(root); err != nil { 134 return nil, err 135 } 136 137 // Populate the dir structure 138 for _, p := range paths { 139 if err := idtools.MkdirAllAs(path.Join(root, p), 0700, rootUID, rootGID); err != nil { 140 return nil, err 141 } 142 } 143 return a, nil 144 } 145 146 // Return a nil error if the kernel supports aufs 147 // We cannot modprobe because inside dind modprobe fails 148 // to run 149 func supportsAufs() error { 150 // We can try to modprobe aufs first before looking at 151 // proc/filesystems for when aufs is supported 152 exec.Command("modprobe", "aufs").Run() 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 // Create three folders for each id 203 // mnt, layers, and diff 204 func (a *Driver) Create(id, parent, mountLabel string) error { 205 if err := a.createDirsFor(id); err != nil { 206 return err 207 } 208 // Write the layers metadata 209 f, err := os.Create(path.Join(a.rootPath(), "layers", id)) 210 if err != nil { 211 return err 212 } 213 defer f.Close() 214 215 if parent != "" { 216 ids, err := getParentIds(a.rootPath(), parent) 217 if err != nil { 218 return err 219 } 220 221 if _, err := fmt.Fprintln(f, parent); err != nil { 222 return err 223 } 224 for _, i := range ids { 225 if _, err := fmt.Fprintln(f, i); err != nil { 226 return err 227 } 228 } 229 } 230 a.active[id] = &data{} 231 return nil 232 } 233 234 func (a *Driver) createDirsFor(id string) error { 235 paths := []string{ 236 "mnt", 237 "diff", 238 } 239 240 rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps) 241 if err != nil { 242 return err 243 } 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 // Protect the a.active from concurrent access 255 a.Lock() 256 defer a.Unlock() 257 258 m := a.active[id] 259 if m != nil { 260 if m.referenceCount > 0 { 261 return nil 262 } 263 // Make sure the dir is umounted first 264 if err := a.unmount(m); err != nil { 265 return err 266 } 267 } 268 tmpDirs := []string{ 269 "mnt", 270 "diff", 271 } 272 273 // Atomically remove each directory in turn by first moving it out of the 274 // way (so that docker doesn't find it anymore) before doing removal of 275 // the whole tree. 276 for _, p := range tmpDirs { 277 realPath := path.Join(a.rootPath(), p, id) 278 tmpPath := path.Join(a.rootPath(), p, fmt.Sprintf("%s-removing", id)) 279 if err := os.Rename(realPath, tmpPath); err != nil && !os.IsNotExist(err) { 280 return err 281 } 282 defer os.RemoveAll(tmpPath) 283 } 284 // Remove the layers file for the id 285 if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) { 286 return err 287 } 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 ids, err := getParentIds(a.rootPath(), id) 295 if err != nil { 296 if !os.IsNotExist(err) { 297 return "", err 298 } 299 ids = []string{} 300 } 301 302 // Protect the a.active from concurrent access 303 a.Lock() 304 defer a.Unlock() 305 306 m := a.active[id] 307 if m == nil { 308 m = &data{} 309 a.active[id] = m 310 } 311 312 // If a dir does not have a parent ( no layers )do not try to mount 313 // just return the diff path to the data 314 m.path = path.Join(a.rootPath(), "diff", id) 315 if len(ids) > 0 { 316 m.path = path.Join(a.rootPath(), "mnt", id) 317 if m.referenceCount == 0 { 318 if err := a.mount(id, m, mountLabel); err != nil { 319 return "", err 320 } 321 } 322 } 323 m.referenceCount++ 324 return m.path, nil 325 } 326 327 // Put unmounts and updates list of active mounts. 328 func (a *Driver) Put(id string) error { 329 // Protect the a.active from concurrent access 330 a.Lock() 331 defer a.Unlock() 332 333 m := a.active[id] 334 if m == nil { 335 // but it might be still here 336 if a.Exists(id) { 337 path := path.Join(a.rootPath(), "mnt", id) 338 err := Unmount(path) 339 if err != nil { 340 logrus.Debugf("Failed to unmount %s aufs: %v", id, err) 341 } 342 } 343 return nil 344 } 345 if count := m.referenceCount; count > 1 { 346 m.referenceCount = count - 1 347 } else { 348 ids, _ := getParentIds(a.rootPath(), id) 349 // We only mounted if there are any parents 350 if ids != nil && len(ids) > 0 { 351 a.unmount(m) 352 } 353 delete(a.active, id) 354 } 355 return nil 356 } 357 358 // Diff produces an archive of the changes between the specified 359 // layer and its parent layer which may be "". 360 func (a *Driver) Diff(id, parent string) (archive.Archive, error) { 361 // AUFS doesn't need the parent layer to produce a diff. 362 return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ 363 Compression: archive.Uncompressed, 364 ExcludePatterns: []string{archive.WhiteoutMetaPrefix + "*", "!" + archive.WhiteoutOpaqueDir}, 365 UIDMaps: a.uidMaps, 366 GIDMaps: a.gidMaps, 367 }) 368 } 369 370 // DiffPath returns path to the directory that contains files for the layer 371 // differences. Used for direct access for tar-split. 372 func (a *Driver) DiffPath(id string) (string, func() error, error) { 373 return path.Join(a.rootPath(), "diff", id), func() error { return nil }, nil 374 } 375 376 func (a *Driver) applyDiff(id string, diff archive.Reader) error { 377 dir := path.Join(a.rootPath(), "diff", id) 378 if err := chrootarchive.UntarUncompressed(diff, dir, &archive.TarOptions{ 379 UIDMaps: a.uidMaps, 380 GIDMaps: a.gidMaps, 381 }); err != nil { 382 return err 383 } 384 385 // show invalid whiteouts warning. 386 files, err := ioutil.ReadDir(path.Join(dir, archive.WhiteoutLinkDir)) 387 if err == nil && len(files) > 0 { 388 logrus.Warnf("Archive contains aufs hardlink references that are not supported.") 389 } 390 return nil 391 } 392 393 // DiffSize calculates the changes between the specified id 394 // and its parent and returns the size in bytes of the changes 395 // relative to its base filesystem directory. 396 func (a *Driver) DiffSize(id, parent string) (size int64, err error) { 397 // AUFS doesn't need the parent layer to calculate the diff size. 398 return directory.Size(path.Join(a.rootPath(), "diff", id)) 399 } 400 401 // ApplyDiff extracts the changeset from the given diff into the 402 // layer with the specified id and parent, returning the size of the 403 // new layer in bytes. 404 func (a *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) { 405 // AUFS doesn't need the parent id to apply the diff. 406 if err = a.applyDiff(id, diff); err != nil { 407 return 408 } 409 410 return a.DiffSize(id, parent) 411 } 412 413 // Changes produces a list of changes between the specified layer 414 // and its parent layer. If parent is "", then all changes will be ADD changes. 415 func (a *Driver) Changes(id, parent string) ([]archive.Change, error) { 416 // AUFS doesn't have snapshots, so we need to get changes from all parent 417 // layers. 418 layers, err := a.getParentLayerPaths(id) 419 if err != nil { 420 return nil, err 421 } 422 return archive.Changes(layers, path.Join(a.rootPath(), "diff", id)) 423 } 424 425 func (a *Driver) getParentLayerPaths(id string) ([]string, error) { 426 parentIds, err := getParentIds(a.rootPath(), id) 427 if err != nil { 428 return nil, err 429 } 430 layers := make([]string, len(parentIds)) 431 432 // Get the diff paths for all the parent ids 433 for i, p := range parentIds { 434 layers[i] = path.Join(a.rootPath(), "diff", p) 435 } 436 return layers, nil 437 } 438 439 func (a *Driver) mount(id string, m *data, mountLabel string) error { 440 // If the id is mounted or we get an error return 441 if mounted, err := a.mounted(m); err != nil || mounted { 442 return err 443 } 444 445 var ( 446 target = m.path 447 rw = path.Join(a.rootPath(), "diff", id) 448 ) 449 450 layers, err := a.getParentLayerPaths(id) 451 if err != nil { 452 return err 453 } 454 455 if err := a.aufsMount(layers, rw, target, mountLabel); err != nil { 456 return fmt.Errorf("error creating aufs mount to %s: %v", target, err) 457 } 458 return nil 459 } 460 461 func (a *Driver) unmount(m *data) error { 462 if mounted, err := a.mounted(m); err != nil || !mounted { 463 return err 464 } 465 return Unmount(m.path) 466 } 467 468 func (a *Driver) mounted(m *data) (bool, error) { 469 return mountpk.Mounted(m.path) 470 } 471 472 // Cleanup aufs and unmount all mountpoints 473 func (a *Driver) Cleanup() error { 474 for id, m := range a.active { 475 if err := a.unmount(m); err != nil { 476 logrus.Errorf("Unmounting %s: %s", stringid.TruncateID(id), err) 477 } 478 } 479 return mountpk.Unmount(a.root) 480 } 481 482 func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) { 483 defer func() { 484 if err != nil { 485 Unmount(target) 486 } 487 }() 488 489 // Mount options are clipped to page size(4096 bytes). If there are more 490 // layers then these are remounted individually using append. 491 492 offset := 54 493 if useDirperm() { 494 offset += len("dirperm1") 495 } 496 b := make([]byte, syscall.Getpagesize()-len(mountLabel)-offset) // room for xino & mountLabel 497 bp := copy(b, fmt.Sprintf("br:%s=rw", rw)) 498 499 firstMount := true 500 i := 0 501 502 for { 503 for ; i < len(ro); i++ { 504 layer := fmt.Sprintf(":%s=ro+wh", ro[i]) 505 506 if firstMount { 507 if bp+len(layer) > len(b) { 508 break 509 } 510 bp += copy(b[bp:], layer) 511 } else { 512 data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), mountLabel) 513 if err = mount("none", target, "aufs", syscall.MS_REMOUNT, data); err != nil { 514 return 515 } 516 } 517 } 518 519 if firstMount { 520 opts := "dio,noplink,xino=/dev/shm/aufs.xino" 521 if useDirperm() { 522 opts += ",dirperm1" 523 } 524 data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), mountLabel) 525 if err = mount("none", target, "aufs", 0, data); err != nil { 526 return 527 } 528 firstMount = false 529 } 530 531 if i == len(ro) { 532 break 533 } 534 } 535 536 return 537 } 538 539 // useDirperm checks dirperm1 mount option can be used with the current 540 // version of aufs. 541 func useDirperm() bool { 542 enableDirpermLock.Do(func() { 543 base, err := ioutil.TempDir("", "docker-aufs-base") 544 if err != nil { 545 logrus.Errorf("error checking dirperm1: %v", err) 546 return 547 } 548 defer os.RemoveAll(base) 549 550 union, err := ioutil.TempDir("", "docker-aufs-union") 551 if err != nil { 552 logrus.Errorf("error checking dirperm1: %v", err) 553 return 554 } 555 defer os.RemoveAll(union) 556 557 opts := fmt.Sprintf("br:%s,dirperm1,xino=/dev/shm/aufs.xino", base) 558 if err := mount("none", union, "aufs", 0, opts); err != nil { 559 return 560 } 561 enableDirperm = true 562 if err := Unmount(union); err != nil { 563 logrus.Errorf("error checking dirperm1: failed to unmount %v", err) 564 } 565 }) 566 return enableDirperm 567 }