github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/daemon/graphdriver/overlay/overlay.go (about) 1 // +build linux 2 3 package overlay 4 5 import ( 6 "bufio" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "os/exec" 11 "path" 12 "sync" 13 "syscall" 14 15 "github.com/Sirupsen/logrus" 16 "github.com/docker/docker/daemon/graphdriver" 17 "github.com/docker/docker/pkg/archive" 18 "github.com/docker/docker/pkg/chrootarchive" 19 "github.com/opencontainers/runc/libcontainer/label" 20 ) 21 22 // This is a small wrapper over the NaiveDiffWriter that lets us have a custom 23 // implementation of ApplyDiff() 24 25 var ( 26 // ErrApplyDiffFallback is returned to indicate that a normal ApplyDiff is applied as a fallback from Naive diff writer. 27 ErrApplyDiffFallback = fmt.Errorf("Fall back to normal ApplyDiff") 28 ) 29 30 // ApplyDiffProtoDriver wraps the ProtoDriver by extending the inteface with ApplyDiff method. 31 type ApplyDiffProtoDriver interface { 32 graphdriver.ProtoDriver 33 // ApplyDiff writes the diff to the archive for the given id and parent id. 34 // It returns the size in bytes written if successful, an error ErrApplyDiffFallback is returned otherwise. 35 ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) 36 } 37 38 type naiveDiffDriverWithApply struct { 39 graphdriver.Driver 40 applyDiff ApplyDiffProtoDriver 41 } 42 43 // NaiveDiffDriverWithApply returns a NaiveDiff driver with custom ApplyDiff. 44 func NaiveDiffDriverWithApply(driver ApplyDiffProtoDriver) graphdriver.Driver { 45 return &naiveDiffDriverWithApply{ 46 Driver: graphdriver.NaiveDiffDriver(driver), 47 applyDiff: driver, 48 } 49 } 50 51 // ApplyDiff creates a diff layer with either the NaiveDiffDriver or with a fallback. 52 func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) { 53 b, err := d.applyDiff.ApplyDiff(id, parent, diff) 54 if err == ErrApplyDiffFallback { 55 return d.Driver.ApplyDiff(id, parent, diff) 56 } 57 return b, err 58 } 59 60 // This backend uses the overlay union filesystem for containers 61 // plus hard link file sharing for images. 62 63 // Each container/image can have a "root" subdirectory which is a plain 64 // filesystem hierarchy, or they can use overlay. 65 66 // If they use overlay there is a "upper" directory and a "lower-id" 67 // file, as well as "merged" and "work" directories. The "upper" 68 // directory has the upper layer of the overlay, and "lower-id" contains 69 // the id of the parent whose "root" directory shall be used as the lower 70 // layer in the overlay. The overlay itself is mounted in the "merged" 71 // directory, and the "work" dir is needed for overlay to work. 72 73 // When a overlay layer is created there are two cases, either the 74 // parent has a "root" dir, then we start out with a empty "upper" 75 // directory overlaid on the parents root. This is typically the 76 // case with the init layer of a container which is based on an image. 77 // If there is no "root" in the parent, we inherit the lower-id from 78 // the parent and start by making a copy in the parent's "upper" dir. 79 // This is typically the case for a container layer which copies 80 // its parent -init upper layer. 81 82 // Additionally we also have a custom implementation of ApplyLayer 83 // which makes a recursive copy of the parent "root" layer using 84 // hardlinks to share file data, and then applies the layer on top 85 // of that. This means all child images share file (but not directory) 86 // data with the parent. 87 88 // ActiveMount contains information about the count, path and whether is mounted or not. 89 // This information is part of the Driver, that contains list of active mounts that are part of this overlay. 90 type ActiveMount struct { 91 count int 92 path string 93 mounted bool 94 } 95 96 // Driver contains information about the home directory and the list of active mounts that are created using this driver. 97 type Driver struct { 98 home string 99 sync.Mutex // Protects concurrent modification to active 100 active map[string]*ActiveMount 101 } 102 103 var backingFs = "<unknown>" 104 105 func init() { 106 graphdriver.Register("overlay", Init) 107 } 108 109 // Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem. 110 // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. 111 // If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned. 112 func Init(home string, options []string) (graphdriver.Driver, error) { 113 114 if err := supportsOverlay(); err != nil { 115 return nil, graphdriver.ErrNotSupported 116 } 117 118 fsMagic, err := graphdriver.GetFSMagic(home) 119 if err != nil { 120 return nil, err 121 } 122 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 123 backingFs = fsName 124 } 125 126 // check if they are running over btrfs or aufs 127 switch fsMagic { 128 case graphdriver.FsMagicBtrfs: 129 logrus.Error("'overlay' is not supported over btrfs.") 130 return nil, graphdriver.ErrIncompatibleFS 131 case graphdriver.FsMagicAufs: 132 logrus.Error("'overlay' is not supported over aufs.") 133 return nil, graphdriver.ErrIncompatibleFS 134 case graphdriver.FsMagicZfs: 135 logrus.Error("'overlay' is not supported over zfs.") 136 return nil, graphdriver.ErrIncompatibleFS 137 } 138 139 // Create the driver home dir 140 if err := os.MkdirAll(home, 0755); err != nil { 141 return nil, err 142 } 143 144 d := &Driver{ 145 home: home, 146 active: make(map[string]*ActiveMount), 147 } 148 149 return NaiveDiffDriverWithApply(d), nil 150 } 151 152 func supportsOverlay() error { 153 // We can try to modprobe overlay first before looking at 154 // proc/filesystems for when overlay is supported 155 exec.Command("modprobe", "overlay").Run() 156 157 f, err := os.Open("/proc/filesystems") 158 if err != nil { 159 return err 160 } 161 defer f.Close() 162 163 s := bufio.NewScanner(f) 164 for s.Scan() { 165 if s.Text() == "nodev\toverlay" { 166 return nil 167 } 168 } 169 logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 170 return graphdriver.ErrNotSupported 171 } 172 173 func (d *Driver) String() string { 174 return "overlay" 175 } 176 177 // Status returns current driver information in a two dimensional string array. 178 // Output contains "Backing Filesystem" used in this implementation. 179 func (d *Driver) Status() [][2]string { 180 return [][2]string{ 181 {"Backing Filesystem", backingFs}, 182 } 183 } 184 185 // GetMetadata returns meta data about the overlay driver such as root, LowerDir, UpperDir, WorkDir and MergeDir used to store data. 186 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 187 dir := d.dir(id) 188 if _, err := os.Stat(dir); err != nil { 189 return nil, err 190 } 191 192 metadata := make(map[string]string) 193 194 // If id has a root, it is an image 195 rootDir := path.Join(dir, "root") 196 if _, err := os.Stat(rootDir); err == nil { 197 metadata["RootDir"] = rootDir 198 return metadata, nil 199 } 200 201 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 202 if err != nil { 203 return nil, err 204 } 205 206 metadata["LowerDir"] = path.Join(d.dir(string(lowerID)), "root") 207 metadata["UpperDir"] = path.Join(dir, "upper") 208 metadata["WorkDir"] = path.Join(dir, "work") 209 metadata["MergedDir"] = path.Join(dir, "merged") 210 211 return metadata, nil 212 } 213 214 // Cleanup simply returns nil and do not change the existing filesystem. 215 // This is required to satisfy the graphdriver.Driver interface. 216 func (d *Driver) Cleanup() error { 217 return nil 218 } 219 220 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. 221 // The parent filesystem is used to configure these directories for the overlay. 222 func (d *Driver) Create(id string, parent string) (retErr error) { 223 dir := d.dir(id) 224 if err := os.MkdirAll(path.Dir(dir), 0700); err != nil { 225 return err 226 } 227 if err := os.Mkdir(dir, 0700); err != nil { 228 return err 229 } 230 231 defer func() { 232 // Clean up on failure 233 if retErr != nil { 234 os.RemoveAll(dir) 235 } 236 }() 237 238 // Toplevel images are just a "root" dir 239 if parent == "" { 240 if err := os.Mkdir(path.Join(dir, "root"), 0755); err != nil { 241 return err 242 } 243 return nil 244 } 245 246 parentDir := d.dir(parent) 247 248 // Ensure parent exists 249 if _, err := os.Lstat(parentDir); err != nil { 250 return err 251 } 252 253 // If parent has a root, just do a overlay to it 254 parentRoot := path.Join(parentDir, "root") 255 256 if s, err := os.Lstat(parentRoot); err == nil { 257 if err := os.Mkdir(path.Join(dir, "upper"), s.Mode()); err != nil { 258 return err 259 } 260 if err := os.Mkdir(path.Join(dir, "work"), 0700); err != nil { 261 return err 262 } 263 if err := os.Mkdir(path.Join(dir, "merged"), 0700); err != nil { 264 return err 265 } 266 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil { 267 return err 268 } 269 return nil 270 } 271 272 // Otherwise, copy the upper and the lower-id from the parent 273 274 lowerID, err := ioutil.ReadFile(path.Join(parentDir, "lower-id")) 275 if err != nil { 276 return err 277 } 278 279 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil { 280 return err 281 } 282 283 parentUpperDir := path.Join(parentDir, "upper") 284 s, err := os.Lstat(parentUpperDir) 285 if err != nil { 286 return err 287 } 288 289 upperDir := path.Join(dir, "upper") 290 if err := os.Mkdir(upperDir, s.Mode()); err != nil { 291 return err 292 } 293 if err := os.Mkdir(path.Join(dir, "work"), 0700); err != nil { 294 return err 295 } 296 if err := os.Mkdir(path.Join(dir, "merged"), 0700); err != nil { 297 return err 298 } 299 300 return copyDir(parentUpperDir, upperDir, 0) 301 } 302 303 func (d *Driver) dir(id string) string { 304 return path.Join(d.home, id) 305 } 306 307 // Remove cleans the directories that are created for this id. 308 func (d *Driver) Remove(id string) error { 309 dir := d.dir(id) 310 if _, err := os.Stat(dir); err != nil { 311 return err 312 } 313 return os.RemoveAll(dir) 314 } 315 316 // Get creates and mounts the required file system for the given id and returns the mount path. 317 func (d *Driver) Get(id string, mountLabel string) (string, error) { 318 // Protect the d.active from concurrent access 319 d.Lock() 320 defer d.Unlock() 321 322 mount := d.active[id] 323 if mount != nil { 324 mount.count++ 325 return mount.path, nil 326 } 327 328 mount = &ActiveMount{count: 1} 329 330 dir := d.dir(id) 331 if _, err := os.Stat(dir); err != nil { 332 return "", err 333 } 334 335 // If id has a root, just return it 336 rootDir := path.Join(dir, "root") 337 if _, err := os.Stat(rootDir); err == nil { 338 mount.path = rootDir 339 d.active[id] = mount 340 return mount.path, nil 341 } 342 343 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 344 if err != nil { 345 return "", err 346 } 347 lowerDir := path.Join(d.dir(string(lowerID)), "root") 348 upperDir := path.Join(dir, "upper") 349 workDir := path.Join(dir, "work") 350 mergedDir := path.Join(dir, "merged") 351 352 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) 353 if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { 354 return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) 355 } 356 mount.path = mergedDir 357 mount.mounted = true 358 d.active[id] = mount 359 360 return mount.path, nil 361 } 362 363 // Put unmounts the mount path created for the give id. 364 func (d *Driver) Put(id string) error { 365 // Protect the d.active from concurrent access 366 d.Lock() 367 defer d.Unlock() 368 369 mount := d.active[id] 370 if mount == nil { 371 logrus.Debugf("Put on a non-mounted device %s", id) 372 // but it might be still here 373 if d.Exists(id) { 374 mergedDir := path.Join(d.dir(id), "merged") 375 err := syscall.Unmount(mergedDir, 0) 376 if err != nil { 377 logrus.Debugf("Failed to unmount %s overlay: %v", id, err) 378 } 379 } 380 return nil 381 } 382 383 mount.count-- 384 if mount.count > 0 { 385 return nil 386 } 387 388 defer delete(d.active, id) 389 if mount.mounted { 390 err := syscall.Unmount(mount.path, 0) 391 if err != nil { 392 logrus.Debugf("Failed to unmount %s overlay: %v", id, err) 393 } 394 return err 395 } 396 return nil 397 } 398 399 // ApplyDiff applies the new layer on top of the root, if parent does not exist with will return a ErrApplyDiffFallback error. 400 func (d *Driver) ApplyDiff(id string, parent string, diff archive.Reader) (size int64, err error) { 401 dir := d.dir(id) 402 403 if parent == "" { 404 return 0, ErrApplyDiffFallback 405 } 406 407 parentRootDir := path.Join(d.dir(parent), "root") 408 if _, err := os.Stat(parentRootDir); err != nil { 409 return 0, ErrApplyDiffFallback 410 } 411 412 // We now know there is a parent, and it has a "root" directory containing 413 // the full root filesystem. We can just hardlink it and apply the 414 // layer. This relies on two things: 415 // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container 416 // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) 417 // These are all currently true and are not expected to break 418 419 tmpRootDir, err := ioutil.TempDir(dir, "tmproot") 420 if err != nil { 421 return 0, err 422 } 423 defer func() { 424 if err != nil { 425 os.RemoveAll(tmpRootDir) 426 } else { 427 os.RemoveAll(path.Join(dir, "upper")) 428 os.RemoveAll(path.Join(dir, "work")) 429 os.RemoveAll(path.Join(dir, "merged")) 430 os.RemoveAll(path.Join(dir, "lower-id")) 431 } 432 }() 433 434 if err = copyDir(parentRootDir, tmpRootDir, copyHardlink); err != nil { 435 return 0, err 436 } 437 438 if size, err = chrootarchive.ApplyUncompressedLayer(tmpRootDir, diff); err != nil { 439 return 0, err 440 } 441 442 rootDir := path.Join(dir, "root") 443 if err := os.Rename(tmpRootDir, rootDir); err != nil { 444 return 0, err 445 } 446 447 return 448 } 449 450 // Exists checks to see if the id is already mounted. 451 func (d *Driver) Exists(id string) bool { 452 _, err := os.Stat(d.dir(id)) 453 return err == nil 454 }