github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/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 17 "github.com/docker/docker/daemon/graphdriver" 18 "github.com/docker/docker/pkg/archive" 19 "github.com/docker/docker/pkg/chrootarchive" 20 "github.com/docker/docker/pkg/idtools" 21 22 "github.com/opencontainers/runc/libcontainer/label" 23 ) 24 25 // This is a small wrapper over the NaiveDiffWriter that lets us have a custom 26 // implementation of ApplyDiff() 27 28 var ( 29 // ErrApplyDiffFallback is returned to indicate that a normal ApplyDiff is applied as a fallback from Naive diff writer. 30 ErrApplyDiffFallback = fmt.Errorf("Fall back to normal ApplyDiff") 31 ) 32 33 // ApplyDiffProtoDriver wraps the ProtoDriver by extending the interface with ApplyDiff method. 34 type ApplyDiffProtoDriver interface { 35 graphdriver.ProtoDriver 36 // ApplyDiff writes the diff to the archive for the given id and parent id. 37 // It returns the size in bytes written if successful, an error ErrApplyDiffFallback is returned otherwise. 38 ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) 39 } 40 41 type naiveDiffDriverWithApply struct { 42 graphdriver.Driver 43 applyDiff ApplyDiffProtoDriver 44 } 45 46 // NaiveDiffDriverWithApply returns a NaiveDiff driver with custom ApplyDiff. 47 func NaiveDiffDriverWithApply(driver ApplyDiffProtoDriver, uidMaps, gidMaps []idtools.IDMap) graphdriver.Driver { 48 return &naiveDiffDriverWithApply{ 49 Driver: graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), 50 applyDiff: driver, 51 } 52 } 53 54 // ApplyDiff creates a diff layer with either the NaiveDiffDriver or with a fallback. 55 func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) { 56 b, err := d.applyDiff.ApplyDiff(id, parent, diff) 57 if err == ErrApplyDiffFallback { 58 return d.Driver.ApplyDiff(id, parent, diff) 59 } 60 return b, err 61 } 62 63 // This backend uses the overlay union filesystem for containers 64 // plus hard link file sharing for images. 65 66 // Each container/image can have a "root" subdirectory which is a plain 67 // filesystem hierarchy, or they can use overlay. 68 69 // If they use overlay there is a "upper" directory and a "lower-id" 70 // file, as well as "merged" and "work" directories. The "upper" 71 // directory has the upper layer of the overlay, and "lower-id" contains 72 // the id of the parent whose "root" directory shall be used as the lower 73 // layer in the overlay. The overlay itself is mounted in the "merged" 74 // directory, and the "work" dir is needed for overlay to work. 75 76 // When a overlay layer is created there are two cases, either the 77 // parent has a "root" dir, then we start out with a empty "upper" 78 // directory overlaid on the parents root. This is typically the 79 // case with the init layer of a container which is based on an image. 80 // If there is no "root" in the parent, we inherit the lower-id from 81 // the parent and start by making a copy in the parent's "upper" dir. 82 // This is typically the case for a container layer which copies 83 // its parent -init upper layer. 84 85 // Additionally we also have a custom implementation of ApplyLayer 86 // which makes a recursive copy of the parent "root" layer using 87 // hardlinks to share file data, and then applies the layer on top 88 // of that. This means all child images share file (but not directory) 89 // data with the parent. 90 91 // Driver contains information about the home directory and the list of active mounts that are created using this driver. 92 type Driver struct { 93 home string 94 pathCacheLock sync.Mutex 95 pathCache map[string]string 96 uidMaps []idtools.IDMap 97 gidMaps []idtools.IDMap 98 } 99 100 var backingFs = "<unknown>" 101 102 func init() { 103 graphdriver.Register("overlay", Init) 104 } 105 106 // Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem. 107 // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. 108 // If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned. 109 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 110 111 if err := supportsOverlay(); err != nil { 112 return nil, graphdriver.ErrNotSupported 113 } 114 115 fsMagic, err := graphdriver.GetFSMagic(home) 116 if err != nil { 117 return nil, err 118 } 119 if fsName, ok := graphdriver.FsNames[fsMagic]; ok { 120 backingFs = fsName 121 } 122 123 // check if they are running over btrfs, aufs, zfs or overlay 124 switch fsMagic { 125 case graphdriver.FsMagicBtrfs: 126 logrus.Error("'overlay' is not supported over btrfs.") 127 return nil, graphdriver.ErrIncompatibleFS 128 case graphdriver.FsMagicAufs: 129 logrus.Error("'overlay' is not supported over aufs.") 130 return nil, graphdriver.ErrIncompatibleFS 131 case graphdriver.FsMagicZfs: 132 logrus.Error("'overlay' is not supported over zfs.") 133 return nil, graphdriver.ErrIncompatibleFS 134 case graphdriver.FsMagicOverlay: 135 logrus.Error("'overlay' is not supported over overlay.") 136 return nil, graphdriver.ErrIncompatibleFS 137 } 138 139 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 140 if err != nil { 141 return nil, err 142 } 143 // Create the driver home dir 144 if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { 145 return nil, err 146 } 147 148 d := &Driver{ 149 home: home, 150 pathCache: make(map[string]string), 151 uidMaps: uidMaps, 152 gidMaps: gidMaps, 153 } 154 155 return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil 156 } 157 158 func supportsOverlay() error { 159 // We can try to modprobe overlay first before looking at 160 // proc/filesystems for when overlay is supported 161 exec.Command("modprobe", "overlay").Run() 162 163 f, err := os.Open("/proc/filesystems") 164 if err != nil { 165 return err 166 } 167 defer f.Close() 168 169 s := bufio.NewScanner(f) 170 for s.Scan() { 171 if s.Text() == "nodev\toverlay" { 172 return nil 173 } 174 } 175 logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") 176 return graphdriver.ErrNotSupported 177 } 178 179 func (d *Driver) String() string { 180 return "overlay" 181 } 182 183 // Status returns current driver information in a two dimensional string array. 184 // Output contains "Backing Filesystem" used in this implementation. 185 func (d *Driver) Status() [][2]string { 186 return [][2]string{ 187 {"Backing Filesystem", backingFs}, 188 } 189 } 190 191 // GetMetadata returns meta data about the overlay driver such as root, LowerDir, UpperDir, WorkDir and MergeDir used to store data. 192 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 193 dir := d.dir(id) 194 if _, err := os.Stat(dir); err != nil { 195 return nil, err 196 } 197 198 metadata := make(map[string]string) 199 200 // If id has a root, it is an image 201 rootDir := path.Join(dir, "root") 202 if _, err := os.Stat(rootDir); err == nil { 203 metadata["RootDir"] = rootDir 204 return metadata, nil 205 } 206 207 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 208 if err != nil { 209 return nil, err 210 } 211 212 metadata["LowerDir"] = path.Join(d.dir(string(lowerID)), "root") 213 metadata["UpperDir"] = path.Join(dir, "upper") 214 metadata["WorkDir"] = path.Join(dir, "work") 215 metadata["MergedDir"] = path.Join(dir, "merged") 216 217 return metadata, nil 218 } 219 220 // Cleanup simply returns nil and do not change the existing filesystem. 221 // This is required to satisfy the graphdriver.Driver interface. 222 func (d *Driver) Cleanup() error { 223 return nil 224 } 225 226 // CreateReadWrite creates a layer that is writable for use as a container 227 // file system. 228 func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error { 229 return d.Create(id, parent, mountLabel, storageOpt) 230 } 231 232 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. 233 // The parent filesystem is used to configure these directories for the overlay. 234 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) (retErr error) { 235 236 if len(storageOpt) != 0 { 237 return fmt.Errorf("--storage-opt is not supported for overlay") 238 } 239 240 dir := d.dir(id) 241 242 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 243 if err != nil { 244 return err 245 } 246 if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { 247 return err 248 } 249 if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { 250 return err 251 } 252 253 defer func() { 254 // Clean up on failure 255 if retErr != nil { 256 os.RemoveAll(dir) 257 } 258 }() 259 260 // Toplevel images are just a "root" dir 261 if parent == "" { 262 if err := idtools.MkdirAs(path.Join(dir, "root"), 0755, rootUID, rootGID); err != nil { 263 return err 264 } 265 return nil 266 } 267 268 parentDir := d.dir(parent) 269 270 // Ensure parent exists 271 if _, err := os.Lstat(parentDir); err != nil { 272 return err 273 } 274 275 // If parent has a root, just do a overlay to it 276 parentRoot := path.Join(parentDir, "root") 277 278 if s, err := os.Lstat(parentRoot); err == nil { 279 if err := idtools.MkdirAs(path.Join(dir, "upper"), s.Mode(), rootUID, rootGID); err != nil { 280 return err 281 } 282 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { 283 return err 284 } 285 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { 286 return err 287 } 288 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil { 289 return err 290 } 291 return nil 292 } 293 294 // Otherwise, copy the upper and the lower-id from the parent 295 296 lowerID, err := ioutil.ReadFile(path.Join(parentDir, "lower-id")) 297 if err != nil { 298 return err 299 } 300 301 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil { 302 return err 303 } 304 305 parentUpperDir := path.Join(parentDir, "upper") 306 s, err := os.Lstat(parentUpperDir) 307 if err != nil { 308 return err 309 } 310 311 upperDir := path.Join(dir, "upper") 312 if err := idtools.MkdirAs(upperDir, s.Mode(), rootUID, rootGID); err != nil { 313 return err 314 } 315 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { 316 return err 317 } 318 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { 319 return err 320 } 321 322 return copyDir(parentUpperDir, upperDir, 0) 323 } 324 325 func (d *Driver) dir(id string) string { 326 return path.Join(d.home, id) 327 } 328 329 // Remove cleans the directories that are created for this id. 330 func (d *Driver) Remove(id string) error { 331 if err := os.RemoveAll(d.dir(id)); err != nil && !os.IsNotExist(err) { 332 return err 333 } 334 d.pathCacheLock.Lock() 335 delete(d.pathCache, id) 336 d.pathCacheLock.Unlock() 337 return nil 338 } 339 340 // Get creates and mounts the required file system for the given id and returns the mount path. 341 func (d *Driver) Get(id string, mountLabel string) (string, error) { 342 dir := d.dir(id) 343 if _, err := os.Stat(dir); err != nil { 344 return "", err 345 } 346 347 // If id has a root, just return it 348 rootDir := path.Join(dir, "root") 349 if _, err := os.Stat(rootDir); err == nil { 350 d.pathCacheLock.Lock() 351 d.pathCache[id] = rootDir 352 d.pathCacheLock.Unlock() 353 return rootDir, nil 354 } 355 356 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) 357 if err != nil { 358 return "", err 359 } 360 lowerDir := path.Join(d.dir(string(lowerID)), "root") 361 upperDir := path.Join(dir, "upper") 362 workDir := path.Join(dir, "work") 363 mergedDir := path.Join(dir, "merged") 364 365 opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) 366 367 // if it's mounted already, just return 368 mounted, err := d.mounted(mergedDir) 369 if err != nil { 370 return "", err 371 } 372 if mounted { 373 return mergedDir, nil 374 } 375 376 if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { 377 return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) 378 } 379 // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a 380 // user namespace requires this to move a directory from lower to upper. 381 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 382 if err != nil { 383 return "", err 384 } 385 386 if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { 387 return "", err 388 } 389 390 d.pathCacheLock.Lock() 391 d.pathCache[id] = mergedDir 392 d.pathCacheLock.Unlock() 393 394 return mergedDir, nil 395 } 396 397 func (d *Driver) mounted(dir string) (bool, error) { 398 return graphdriver.Mounted(graphdriver.FsMagicOverlay, dir) 399 } 400 401 // Put unmounts the mount path created for the give id. 402 func (d *Driver) Put(id string) error { 403 d.pathCacheLock.Lock() 404 mountpoint, exists := d.pathCache[id] 405 d.pathCacheLock.Unlock() 406 407 if !exists { 408 logrus.Debugf("Put on a non-mounted device %s", id) 409 // but it might be still here 410 if d.Exists(id) { 411 mountpoint = path.Join(d.dir(id), "merged") 412 } 413 414 d.pathCacheLock.Lock() 415 d.pathCache[id] = mountpoint 416 d.pathCacheLock.Unlock() 417 } 418 419 if mounted, err := d.mounted(mountpoint); mounted || err != nil { 420 if err = syscall.Unmount(mountpoint, 0); err != nil { 421 logrus.Debugf("Failed to unmount %s overlay: %v", id, err) 422 } 423 return err 424 } 425 return nil 426 } 427 428 // ApplyDiff applies the new layer on top of the root, if parent does not exist with will return a ErrApplyDiffFallback error. 429 func (d *Driver) ApplyDiff(id string, parent string, diff archive.Reader) (size int64, err error) { 430 dir := d.dir(id) 431 432 if parent == "" { 433 return 0, ErrApplyDiffFallback 434 } 435 436 parentRootDir := path.Join(d.dir(parent), "root") 437 if _, err := os.Stat(parentRootDir); err != nil { 438 return 0, ErrApplyDiffFallback 439 } 440 441 // We now know there is a parent, and it has a "root" directory containing 442 // the full root filesystem. We can just hardlink it and apply the 443 // layer. This relies on two things: 444 // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container 445 // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) 446 // These are all currently true and are not expected to break 447 448 tmpRootDir, err := ioutil.TempDir(dir, "tmproot") 449 if err != nil { 450 return 0, err 451 } 452 defer func() { 453 if err != nil { 454 os.RemoveAll(tmpRootDir) 455 } else { 456 os.RemoveAll(path.Join(dir, "upper")) 457 os.RemoveAll(path.Join(dir, "work")) 458 os.RemoveAll(path.Join(dir, "merged")) 459 os.RemoveAll(path.Join(dir, "lower-id")) 460 } 461 }() 462 463 if err = copyDir(parentRootDir, tmpRootDir, copyHardlink); err != nil { 464 return 0, err 465 } 466 467 options := &archive.TarOptions{UIDMaps: d.uidMaps, GIDMaps: d.gidMaps} 468 if size, err = chrootarchive.ApplyUncompressedLayer(tmpRootDir, diff, options); err != nil { 469 return 0, err 470 } 471 472 rootDir := path.Join(dir, "root") 473 if err := os.Rename(tmpRootDir, rootDir); err != nil { 474 return 0, err 475 } 476 477 return 478 } 479 480 // Exists checks to see if the id is already mounted. 481 func (d *Driver) Exists(id string) bool { 482 _, err := os.Stat(d.dir(id)) 483 return err == nil 484 }