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