github.com/scorpionis/docker@v1.6.0-rc7/daemon/graphdriver/aufs/aufs.go (about) 1 /* 2 3 aufs driver directory structure 4 5 . 6 ├── layers // Metadata of layers 7 │ ├── 1 8 │ ├── 2 9 │ └── 3 10 ├── diff // Content of the layer 11 │ ├── 1 // Contains layers that need to be mounted for the id 12 │ ├── 2 13 │ └── 3 14 └── mnt // Mount points for the rw layers to be mounted 15 ├── 1 16 ├── 2 17 └── 3 18 19 */ 20 21 package aufs 22 23 import ( 24 "bufio" 25 "fmt" 26 "io/ioutil" 27 "os" 28 "os/exec" 29 "path" 30 "strings" 31 "sync" 32 "syscall" 33 34 log "github.com/Sirupsen/logrus" 35 "github.com/docker/docker/daemon/graphdriver" 36 "github.com/docker/docker/pkg/archive" 37 "github.com/docker/docker/pkg/chrootarchive" 38 "github.com/docker/docker/pkg/common" 39 "github.com/docker/docker/pkg/directory" 40 mountpk "github.com/docker/docker/pkg/mount" 41 "github.com/docker/libcontainer/label" 42 ) 43 44 var ( 45 ErrAufsNotSupported = fmt.Errorf("AUFS was not found in /proc/filesystems") 46 incompatibleFsMagic = []graphdriver.FsMagic{ 47 graphdriver.FsMagicBtrfs, 48 graphdriver.FsMagicAufs, 49 } 50 backingFs = "<unknown>" 51 52 enableDirpermLock sync.Once 53 enableDirperm bool 54 ) 55 56 func init() { 57 graphdriver.Register("aufs", Init) 58 } 59 60 type Driver struct { 61 root string 62 sync.Mutex // Protects concurrent modification to active 63 active map[string]int 64 } 65 66 // New returns a new AUFS driver. 67 // An error is returned if AUFS is not supported. 68 func Init(root string, options []string) (graphdriver.Driver, error) { 69 70 // Try to load the aufs kernel module 71 if err := supportsAufs(); err != nil { 72 return nil, graphdriver.ErrNotSupported 73 } 74 75 fsMagic, err := graphdriver.GetFSMagic(root) 76 if err != nil { 77 return nil, err 78 } 79 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 80 backingFs = fsName 81 } 82 83 for _, magic := range incompatibleFsMagic { 84 if fsMagic == magic { 85 return nil, graphdriver.ErrIncompatibleFS 86 } 87 } 88 89 paths := []string{ 90 "mnt", 91 "diff", 92 "layers", 93 } 94 95 a := &Driver{ 96 root: root, 97 active: make(map[string]int), 98 } 99 100 // Create the root aufs driver dir and return 101 // if it already exists 102 // If not populate the dir structure 103 if err := os.MkdirAll(root, 0755); err != nil { 104 if os.IsExist(err) { 105 return a, nil 106 } 107 return nil, err 108 } 109 110 if err := mountpk.MakePrivate(root); err != nil { 111 return nil, err 112 } 113 114 for _, p := range paths { 115 if err := os.MkdirAll(path.Join(root, p), 0755); err != nil { 116 return nil, err 117 } 118 } 119 return a, nil 120 } 121 122 // Return a nil error if the kernel supports aufs 123 // We cannot modprobe because inside dind modprobe fails 124 // to run 125 func supportsAufs() error { 126 // We can try to modprobe aufs first before looking at 127 // proc/filesystems for when aufs is supported 128 exec.Command("modprobe", "aufs").Run() 129 130 f, err := os.Open("/proc/filesystems") 131 if err != nil { 132 return err 133 } 134 defer f.Close() 135 136 s := bufio.NewScanner(f) 137 for s.Scan() { 138 if strings.Contains(s.Text(), "aufs") { 139 return nil 140 } 141 } 142 return ErrAufsNotSupported 143 } 144 145 func (a *Driver) rootPath() string { 146 return a.root 147 } 148 149 func (*Driver) String() string { 150 return "aufs" 151 } 152 153 func (a *Driver) Status() [][2]string { 154 ids, _ := loadIds(path.Join(a.rootPath(), "layers")) 155 return [][2]string{ 156 {"Root Dir", a.rootPath()}, 157 {"Backing Filesystem", backingFs}, 158 {"Dirs", fmt.Sprintf("%d", len(ids))}, 159 {"Dirperm1 Supported", fmt.Sprintf("%v", useDirperm())}, 160 } 161 } 162 163 // Exists returns true if the given id is registered with 164 // this driver 165 func (a *Driver) Exists(id string) bool { 166 if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil { 167 return false 168 } 169 return true 170 } 171 172 // Three folders are created for each id 173 // mnt, layers, and diff 174 func (a *Driver) Create(id, parent string) error { 175 if err := a.createDirsFor(id); err != nil { 176 return err 177 } 178 // Write the layers metadata 179 f, err := os.Create(path.Join(a.rootPath(), "layers", id)) 180 if err != nil { 181 return err 182 } 183 defer f.Close() 184 185 if parent != "" { 186 ids, err := getParentIds(a.rootPath(), parent) 187 if err != nil { 188 return err 189 } 190 191 if _, err := fmt.Fprintln(f, parent); err != nil { 192 return err 193 } 194 for _, i := range ids { 195 if _, err := fmt.Fprintln(f, i); err != nil { 196 return err 197 } 198 } 199 } 200 return nil 201 } 202 203 func (a *Driver) createDirsFor(id string) error { 204 paths := []string{ 205 "mnt", 206 "diff", 207 } 208 209 for _, p := range paths { 210 if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil { 211 return err 212 } 213 } 214 return nil 215 } 216 217 // Unmount and remove the dir information 218 func (a *Driver) Remove(id string) error { 219 // Protect the a.active from concurrent access 220 a.Lock() 221 defer a.Unlock() 222 223 if a.active[id] != 0 { 224 log.Errorf("Removing active id %s", id) 225 } 226 227 // Make sure the dir is umounted first 228 if err := a.unmount(id); err != nil { 229 return err 230 } 231 tmpDirs := []string{ 232 "mnt", 233 "diff", 234 } 235 236 // Atomically remove each directory in turn by first moving it out of the 237 // way (so that docker doesn't find it anymore) before doing removal of 238 // the whole tree. 239 for _, p := range tmpDirs { 240 241 realPath := path.Join(a.rootPath(), p, id) 242 tmpPath := path.Join(a.rootPath(), p, fmt.Sprintf("%s-removing", id)) 243 if err := os.Rename(realPath, tmpPath); err != nil && !os.IsNotExist(err) { 244 return err 245 } 246 defer os.RemoveAll(tmpPath) 247 } 248 249 // Remove the layers file for the id 250 if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) { 251 return err 252 } 253 return nil 254 } 255 256 // Return the rootfs path for the id 257 // This will mount the dir at it's given path 258 func (a *Driver) Get(id, mountLabel string) (string, error) { 259 ids, err := getParentIds(a.rootPath(), id) 260 if err != nil { 261 if !os.IsNotExist(err) { 262 return "", err 263 } 264 ids = []string{} 265 } 266 267 // Protect the a.active from concurrent access 268 a.Lock() 269 defer a.Unlock() 270 271 count := a.active[id] 272 273 // If a dir does not have a parent ( no layers )do not try to mount 274 // just return the diff path to the data 275 out := path.Join(a.rootPath(), "diff", id) 276 if len(ids) > 0 { 277 out = path.Join(a.rootPath(), "mnt", id) 278 279 if count == 0 { 280 if err := a.mount(id, mountLabel); err != nil { 281 return "", err 282 } 283 } 284 } 285 286 a.active[id] = count + 1 287 288 return out, nil 289 } 290 291 func (a *Driver) Put(id string) error { 292 // Protect the a.active from concurrent access 293 a.Lock() 294 defer a.Unlock() 295 296 if count := a.active[id]; count > 1 { 297 a.active[id] = count - 1 298 } else { 299 ids, _ := getParentIds(a.rootPath(), id) 300 // We only mounted if there are any parents 301 if ids != nil && len(ids) > 0 { 302 a.unmount(id) 303 } 304 delete(a.active, id) 305 } 306 return nil 307 } 308 309 // Diff produces an archive of the changes between the specified 310 // layer and its parent layer which may be "". 311 func (a *Driver) Diff(id, parent string) (archive.Archive, error) { 312 // AUFS doesn't need the parent layer to produce a diff. 313 return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ 314 Compression: archive.Uncompressed, 315 ExcludePatterns: []string{".wh..wh.*"}, 316 }) 317 } 318 319 func (a *Driver) applyDiff(id string, diff archive.ArchiveReader) error { 320 return chrootarchive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil) 321 } 322 323 // DiffSize calculates the changes between the specified id 324 // and its parent and returns the size in bytes of the changes 325 // relative to its base filesystem directory. 326 func (a *Driver) DiffSize(id, parent string) (size int64, err error) { 327 // AUFS doesn't need the parent layer to calculate the diff size. 328 return directory.Size(path.Join(a.rootPath(), "diff", id)) 329 } 330 331 // ApplyDiff extracts the changeset from the given diff into the 332 // layer with the specified id and parent, returning the size of the 333 // new layer in bytes. 334 func (a *Driver) ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error) { 335 // AUFS doesn't need the parent id to apply the diff. 336 if err = a.applyDiff(id, diff); err != nil { 337 return 338 } 339 340 return a.DiffSize(id, parent) 341 } 342 343 // Changes produces a list of changes between the specified layer 344 // and its parent layer. If parent is "", then all changes will be ADD changes. 345 func (a *Driver) Changes(id, parent string) ([]archive.Change, error) { 346 // AUFS doesn't have snapshots, so we need to get changes from all parent 347 // layers. 348 layers, err := a.getParentLayerPaths(id) 349 if err != nil { 350 return nil, err 351 } 352 return archive.Changes(layers, path.Join(a.rootPath(), "diff", id)) 353 } 354 355 func (a *Driver) getParentLayerPaths(id string) ([]string, error) { 356 parentIds, err := getParentIds(a.rootPath(), id) 357 if err != nil { 358 return nil, err 359 } 360 layers := make([]string, len(parentIds)) 361 362 // Get the diff paths for all the parent ids 363 for i, p := range parentIds { 364 layers[i] = path.Join(a.rootPath(), "diff", p) 365 } 366 return layers, nil 367 } 368 369 func (a *Driver) mount(id, mountLabel string) error { 370 // If the id is mounted or we get an error return 371 if mounted, err := a.mounted(id); err != nil || mounted { 372 return err 373 } 374 375 var ( 376 target = path.Join(a.rootPath(), "mnt", id) 377 rw = path.Join(a.rootPath(), "diff", id) 378 ) 379 380 layers, err := a.getParentLayerPaths(id) 381 if err != nil { 382 return err 383 } 384 385 if err := a.aufsMount(layers, rw, target, mountLabel); err != nil { 386 return fmt.Errorf("error creating aufs mount to %s: %v", target, err) 387 } 388 return nil 389 } 390 391 func (a *Driver) unmount(id string) error { 392 if mounted, err := a.mounted(id); err != nil || !mounted { 393 return err 394 } 395 target := path.Join(a.rootPath(), "mnt", id) 396 return Unmount(target) 397 } 398 399 func (a *Driver) mounted(id string) (bool, error) { 400 target := path.Join(a.rootPath(), "mnt", id) 401 return mountpk.Mounted(target) 402 } 403 404 // During cleanup aufs needs to unmount all mountpoints 405 func (a *Driver) Cleanup() error { 406 ids, err := loadIds(path.Join(a.rootPath(), "layers")) 407 if err != nil { 408 return err 409 } 410 411 for _, id := range ids { 412 if err := a.unmount(id); err != nil { 413 log.Errorf("Unmounting %s: %s", common.TruncateID(id), err) 414 } 415 } 416 417 return mountpk.Unmount(a.root) 418 } 419 420 func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) { 421 defer func() { 422 if err != nil { 423 Unmount(target) 424 } 425 }() 426 427 // Mount options are clipped to page size(4096 bytes). If there are more 428 // layers then these are remounted individually using append. 429 430 offset := 54 431 if useDirperm() { 432 offset += len("dirperm1") 433 } 434 b := make([]byte, syscall.Getpagesize()-len(mountLabel)-offset) // room for xino & mountLabel 435 bp := copy(b, fmt.Sprintf("br:%s=rw", rw)) 436 437 firstMount := true 438 i := 0 439 440 for { 441 for ; i < len(ro); i++ { 442 layer := fmt.Sprintf(":%s=ro+wh", ro[i]) 443 444 if firstMount { 445 if bp+len(layer) > len(b) { 446 break 447 } 448 bp += copy(b[bp:], layer) 449 } else { 450 data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), mountLabel) 451 if err = mount("none", target, "aufs", MsRemount, data); err != nil { 452 return 453 } 454 } 455 } 456 457 if firstMount { 458 opts := "dio,xino=/dev/shm/aufs.xino" 459 if useDirperm() { 460 opts += ",dirperm1" 461 } 462 data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), mountLabel) 463 if err = mount("none", target, "aufs", 0, data); err != nil { 464 return 465 } 466 firstMount = false 467 } 468 469 if i == len(ro) { 470 break 471 } 472 } 473 474 return 475 } 476 477 // useDirperm checks dirperm1 mount option can be used with the current 478 // version of aufs. 479 func useDirperm() bool { 480 enableDirpermLock.Do(func() { 481 base, err := ioutil.TempDir("", "docker-aufs-base") 482 if err != nil { 483 log.Errorf("error checking dirperm1: %v", err) 484 return 485 } 486 defer os.RemoveAll(base) 487 488 union, err := ioutil.TempDir("", "docker-aufs-union") 489 if err != nil { 490 log.Errorf("error checking dirperm1: %v", err) 491 return 492 } 493 defer os.RemoveAll(union) 494 495 opts := fmt.Sprintf("br:%s,dirperm1,xino=/dev/shm/aufs.xino", base) 496 if err := mount("none", union, "aufs", 0, opts); err != nil { 497 return 498 } 499 enableDirperm = true 500 if err := Unmount(union); err != nil { 501 log.Errorf("error checking dirperm1: failed to unmount %v", err) 502 } 503 }) 504 return enableDirperm 505 }