github.com/lmars/docker@v1.6.0-rc2/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 log "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/docker/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 log.Error("'overlay' is not supported over btrfs.") 117 return nil, graphdriver.ErrIncompatibleFS 118 case graphdriver.FsMagicAufs: 119 log.Error("'overlay' is not supported over aufs.") 120 return nil, graphdriver.ErrIncompatibleFS 121 case graphdriver.FsMagicZfs: 122 log.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 log.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) Cleanup() error { 171 return nil 172 } 173 174 func (d *Driver) Create(id string, parent string) (retErr error) { 175 dir := d.dir(id) 176 if err := os.MkdirAll(path.Dir(dir), 0700); err != nil { 177 return err 178 } 179 if err := os.Mkdir(dir, 0700); err != nil { 180 return err 181 } 182 183 defer func() { 184 // Clean up on failure 185 if retErr != nil { 186 os.RemoveAll(dir) 187 } 188 }() 189 190 // Toplevel images are just a "root" dir 191 if parent == "" { 192 if err := os.Mkdir(path.Join(dir, "root"), 0755); err != nil { 193 return err 194 } 195 return nil 196 } 197 198 parentDir := d.dir(parent) 199 200 // Ensure parent exists 201 if _, err := os.Lstat(parentDir); err != nil { 202 return err 203 } 204 205 // If parent has a root, just do a overlay to it 206 parentRoot := path.Join(parentDir, "root") 207 208 if s, err := os.Lstat(parentRoot); err == nil { 209 if err := os.Mkdir(path.Join(dir, "upper"), s.Mode()); err != nil { 210 return err 211 } 212 if err := os.Mkdir(path.Join(dir, "work"), 0700); err != nil { 213 return err 214 } 215 if err := os.Mkdir(path.Join(dir, "merged"), 0700); err != nil { 216 return err 217 } 218 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil { 219 return err 220 } 221 return nil 222 } 223 224 // Otherwise, copy the upper and the lower-id from the parent 225 226 lowerId, err := ioutil.ReadFile(path.Join(parentDir, "lower-id")) 227 if err != nil { 228 return err 229 } 230 231 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerId, 0666); err != nil { 232 return err 233 } 234 235 parentUpperDir := path.Join(parentDir, "upper") 236 s, err := os.Lstat(parentUpperDir) 237 if err != nil { 238 return err 239 } 240 241 upperDir := path.Join(dir, "upper") 242 if err := os.Mkdir(upperDir, s.Mode()); err != nil { 243 return err 244 } 245 if err := os.Mkdir(path.Join(dir, "work"), 0700); err != nil { 246 return err 247 } 248 if err := os.Mkdir(path.Join(dir, "merged"), 0700); err != nil { 249 return err 250 } 251 252 return copyDir(parentUpperDir, upperDir, 0) 253 } 254 255 func (d *Driver) dir(id string) string { 256 return path.Join(d.home, id) 257 } 258 259 func (d *Driver) Remove(id string) error { 260 dir := d.dir(id) 261 if _, err := os.Stat(dir); err != nil { 262 return err 263 } 264 return os.RemoveAll(dir) 265 } 266 267 func (d *Driver) Get(id string, mountLabel string) (string, error) { 268 // Protect the d.active from concurrent access 269 d.Lock() 270 defer d.Unlock() 271 272 mount := d.active[id] 273 if mount != nil { 274 mount.count++ 275 return mount.path, nil 276 } else { 277 mount = &ActiveMount{count: 1} 278 } 279 280 dir := d.dir(id) 281 if _, err := os.Stat(dir); err != nil { 282 return "", err 283 } 284 285 // If id has a root, just return it 286 rootDir := path.Join(dir, "root") 287 if _, err := os.Stat(rootDir); err == nil { 288 mount.path = rootDir 289 d.active[id] = mount 290 return mount.path, nil 291 } 292 293 lowerId, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 294 if err != nil { 295 return "", err 296 } 297 lowerDir := path.Join(d.dir(string(lowerId)), "root") 298 upperDir := path.Join(dir, "upper") 299 workDir := path.Join(dir, "work") 300 mergedDir := path.Join(dir, "merged") 301 302 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) 303 if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { 304 return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) 305 } 306 mount.path = mergedDir 307 mount.mounted = true 308 d.active[id] = mount 309 310 return mount.path, nil 311 } 312 313 func (d *Driver) Put(id string) error { 314 // Protect the d.active from concurrent access 315 d.Lock() 316 defer d.Unlock() 317 318 mount := d.active[id] 319 if mount == nil { 320 log.Debugf("Put on a non-mounted device %s", id) 321 return nil 322 } 323 324 mount.count-- 325 if mount.count > 0 { 326 return nil 327 } 328 329 defer delete(d.active, id) 330 if mount.mounted { 331 err := syscall.Unmount(mount.path, 0) 332 if err != nil { 333 log.Debugf("Failed to unmount %s overlay: %v", id, err) 334 } 335 return err 336 } 337 return nil 338 } 339 340 func (d *Driver) ApplyDiff(id string, parent string, diff archive.ArchiveReader) (size int64, err error) { 341 dir := d.dir(id) 342 343 if parent == "" { 344 return 0, ErrApplyDiffFallback 345 } 346 347 parentRootDir := path.Join(d.dir(parent), "root") 348 if _, err := os.Stat(parentRootDir); err != nil { 349 return 0, ErrApplyDiffFallback 350 } 351 352 // We now know there is a parent, and it has a "root" directory containing 353 // the full root filesystem. We can just hardlink it and apply the 354 // layer. This relies on two things: 355 // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container 356 // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) 357 // These are all currently true and are not expected to break 358 359 tmpRootDir, err := ioutil.TempDir(dir, "tmproot") 360 if err != nil { 361 return 0, err 362 } 363 defer func() { 364 if err != nil { 365 os.RemoveAll(tmpRootDir) 366 } else { 367 os.RemoveAll(path.Join(dir, "upper")) 368 os.RemoveAll(path.Join(dir, "work")) 369 os.RemoveAll(path.Join(dir, "merged")) 370 os.RemoveAll(path.Join(dir, "lower-id")) 371 } 372 }() 373 374 if err = copyDir(parentRootDir, tmpRootDir, CopyHardlink); err != nil { 375 return 0, err 376 } 377 378 if size, err = chrootarchive.ApplyLayer(tmpRootDir, diff); err != nil { 379 return 0, err 380 } 381 382 rootDir := path.Join(dir, "root") 383 if err := os.Rename(tmpRootDir, rootDir); err != nil { 384 return 0, err 385 } 386 387 return 388 } 389 390 func (d *Driver) Exists(id string) bool { 391 _, err := os.Stat(d.dir(id)) 392 return err == nil 393 }