github.com/pritambaral/docker@v1.4.2-0.20150120174542-b2fe1b3dd952/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 } 122 123 // Create the driver home dir 124 if err := os.MkdirAll(home, 0755); err != nil && !os.IsExist(err) { 125 return nil, err 126 } 127 128 d := &Driver{ 129 home: home, 130 active: make(map[string]*ActiveMount), 131 } 132 133 return NaiveDiffDriverWithApply(d), nil 134 } 135 136 func supportsOverlay() error { 137 // We can try to modprobe overlay first before looking at 138 // proc/filesystems for when overlay is supported 139 exec.Command("modprobe", "overlay").Run() 140 141 f, err := os.Open("/proc/filesystems") 142 if err != nil { 143 return err 144 } 145 defer f.Close() 146 147 s := bufio.NewScanner(f) 148 for s.Scan() { 149 if s.Text() == "nodev\toverlay" { 150 return nil 151 } 152 } 153 log.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 154 return graphdriver.ErrNotSupported 155 } 156 157 func (d *Driver) String() string { 158 return "overlay" 159 } 160 161 func (d *Driver) Status() [][2]string { 162 return [][2]string{ 163 {"Backing Filesystem", backingFs}, 164 } 165 } 166 167 func (d *Driver) Cleanup() error { 168 return nil 169 } 170 171 func (d *Driver) Create(id string, parent string) (retErr error) { 172 dir := d.dir(id) 173 if err := os.MkdirAll(path.Dir(dir), 0700); err != nil { 174 return err 175 } 176 if err := os.Mkdir(dir, 0700); err != nil { 177 return err 178 } 179 180 defer func() { 181 // Clean up on failure 182 if retErr != nil { 183 os.RemoveAll(dir) 184 } 185 }() 186 187 // Toplevel images are just a "root" dir 188 if parent == "" { 189 if err := os.Mkdir(path.Join(dir, "root"), 0755); err != nil { 190 return err 191 } 192 return nil 193 } 194 195 parentDir := d.dir(parent) 196 197 // Ensure parent exists 198 if _, err := os.Lstat(parentDir); err != nil { 199 return err 200 } 201 202 // If parent has a root, just do a overlay to it 203 parentRoot := path.Join(parentDir, "root") 204 205 if s, err := os.Lstat(parentRoot); err == nil { 206 if err := os.Mkdir(path.Join(dir, "upper"), s.Mode()); err != nil { 207 return err 208 } 209 if err := os.Mkdir(path.Join(dir, "work"), 0700); err != nil { 210 return err 211 } 212 if err := os.Mkdir(path.Join(dir, "merged"), 0700); err != nil { 213 return err 214 } 215 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil { 216 return err 217 } 218 return nil 219 } 220 221 // Otherwise, copy the upper and the lower-id from the parent 222 223 lowerId, err := ioutil.ReadFile(path.Join(parentDir, "lower-id")) 224 if err != nil { 225 return err 226 } 227 228 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerId, 0666); err != nil { 229 return err 230 } 231 232 parentUpperDir := path.Join(parentDir, "upper") 233 s, err := os.Lstat(parentUpperDir) 234 if err != nil { 235 return err 236 } 237 238 upperDir := path.Join(dir, "upper") 239 if err := os.Mkdir(upperDir, s.Mode()); err != nil { 240 return err 241 } 242 if err := os.Mkdir(path.Join(dir, "work"), 0700); err != nil { 243 return err 244 } 245 if err := os.Mkdir(path.Join(dir, "merged"), 0700); err != nil { 246 return err 247 } 248 249 return copyDir(parentUpperDir, upperDir, 0) 250 } 251 252 func (d *Driver) dir(id string) string { 253 return path.Join(d.home, id) 254 } 255 256 func (d *Driver) Remove(id string) error { 257 dir := d.dir(id) 258 if _, err := os.Stat(dir); err != nil { 259 return err 260 } 261 return os.RemoveAll(dir) 262 } 263 264 func (d *Driver) Get(id string, mountLabel string) (string, error) { 265 // Protect the d.active from concurrent access 266 d.Lock() 267 defer d.Unlock() 268 269 mount := d.active[id] 270 if mount != nil { 271 mount.count++ 272 return mount.path, nil 273 } else { 274 mount = &ActiveMount{count: 1} 275 } 276 277 dir := d.dir(id) 278 if _, err := os.Stat(dir); err != nil { 279 return "", err 280 } 281 282 // If id has a root, just return it 283 rootDir := path.Join(dir, "root") 284 if _, err := os.Stat(rootDir); err == nil { 285 mount.path = rootDir 286 d.active[id] = mount 287 return mount.path, nil 288 } 289 290 lowerId, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 291 if err != nil { 292 return "", err 293 } 294 lowerDir := path.Join(d.dir(string(lowerId)), "root") 295 upperDir := path.Join(dir, "upper") 296 workDir := path.Join(dir, "work") 297 mergedDir := path.Join(dir, "merged") 298 299 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) 300 if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { 301 return "", err 302 } 303 mount.path = mergedDir 304 mount.mounted = true 305 d.active[id] = mount 306 307 return mount.path, nil 308 } 309 310 func (d *Driver) Put(id string) error { 311 // Protect the d.active from concurrent access 312 d.Lock() 313 defer d.Unlock() 314 315 mount := d.active[id] 316 if mount == nil { 317 log.Debugf("Put on a non-mounted device %s", id) 318 return nil 319 } 320 321 mount.count-- 322 if mount.count > 0 { 323 return nil 324 } 325 326 defer delete(d.active, id) 327 if mount.mounted { 328 err := syscall.Unmount(mount.path, 0) 329 if err != nil { 330 log.Debugf("Failed to unmount %s overlay: %v", id, err) 331 } 332 return err 333 } 334 return nil 335 } 336 337 func (d *Driver) ApplyDiff(id string, parent string, diff archive.ArchiveReader) (size int64, err error) { 338 dir := d.dir(id) 339 340 if parent == "" { 341 return 0, ErrApplyDiffFallback 342 } 343 344 parentRootDir := path.Join(d.dir(parent), "root") 345 if _, err := os.Stat(parentRootDir); err != nil { 346 return 0, ErrApplyDiffFallback 347 } 348 349 // We now know there is a parent, and it has a "root" directory containing 350 // the full root filesystem. We can just hardlink it and apply the 351 // layer. This relies on two things: 352 // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container 353 // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) 354 // These are all currently true and are not expected to break 355 356 tmpRootDir, err := ioutil.TempDir(dir, "tmproot") 357 if err != nil { 358 return 0, err 359 } 360 defer func() { 361 if err != nil { 362 os.RemoveAll(tmpRootDir) 363 } else { 364 os.RemoveAll(path.Join(dir, "upper")) 365 os.RemoveAll(path.Join(dir, "work")) 366 os.RemoveAll(path.Join(dir, "merged")) 367 os.RemoveAll(path.Join(dir, "lower-id")) 368 } 369 }() 370 371 if err = copyDir(parentRootDir, tmpRootDir, CopyHardlink); err != nil { 372 return 0, err 373 } 374 375 if size, err = chrootarchive.ApplyLayer(tmpRootDir, diff); err != nil { 376 return 0, err 377 } 378 379 rootDir := path.Join(dir, "root") 380 if err := os.Rename(tmpRootDir, rootDir); err != nil { 381 return 0, err 382 } 383 384 return 385 } 386 387 func (d *Driver) Exists(id string) bool { 388 _, err := os.Stat(d.dir(id)) 389 return err == nil 390 }