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