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