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