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