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