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