diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 26e94ba4df..db8e0547d5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,8 +5,7 @@ builder/** @tonistiigi contrib/mkimage/** @tianon -daemon/graphdriver/devmapper/** @rhvgoyal -daemon/graphdriver/overlay/** @dmcgowan +daemon/graphdriver/devmapper/** @rhvgoyal daemon/graphdriver/overlay2/** @dmcgowan daemon/graphdriver/windows/** @johnstep daemon/logger/awslogs/** @samuelkarp diff --git a/daemon/daemon_linux.go b/daemon/daemon_linux.go index afaeabd4f3..80dff4ac95 100644 --- a/daemon/daemon_linux.go +++ b/daemon/daemon_linux.go @@ -116,7 +116,7 @@ func getCleanPatterns(id string) (regexps []*regexp.Regexp) { id = "[0-9a-f]{64}" patterns = append(patterns, "containers/"+id+"/mounts/shm", "containers/"+id+"/shm") } - patterns = append(patterns, "overlay2/"+id+"/merged$", "overlay/"+id+"/merged$", "zfs/graph/"+id+"$") + patterns = append(patterns, "overlay2/"+id+"/merged$", "zfs/graph/"+id+"$") for _, p := range patterns { r, err := regexp.Compile(p) if err == nil { diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 503c227261..2d32d19a8e 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -817,8 +817,8 @@ func configureKernelSecuritySupport(config *config.Config, driverName string) er return nil } - if driverName == "overlay" || driverName == "overlay2" || driverName == "overlayfs" { - // If driver is overlay or overlay2, make sure kernel + if driverName == "overlay2" || driverName == "overlayfs" { + // If driver is overlay2, make sure kernel // supports selinux with overlay. supported, err := overlaySupportsSelinux() if err != nil { diff --git a/daemon/graphdriver/driver.go b/daemon/graphdriver/driver.go index 396eb229d4..2853fa9682 100644 --- a/daemon/graphdriver/driver.go +++ b/daemon/graphdriver/driver.go @@ -318,7 +318,7 @@ func isEmptyDir(name string) bool { func isDeprecated(name string) bool { switch name { // NOTE: when deprecating a driver, update daemon.fillDriverInfo() accordingly - case "devicemapper", "overlay": + case "devicemapper": return true } return false @@ -327,7 +327,7 @@ func isDeprecated(name string) bool { // checkRemoved checks if a storage-driver has been deprecated (and removed) func checkRemoved(name string) error { switch name { - case "aufs": + case "aufs", "overlay": return NotSupportedError(fmt.Sprintf("[graphdriver] ERROR: the %s storage-driver has been deprecated and removed; visit https://docs.docker.com/go/storage-driver/ for more information", name)) } return nil diff --git a/daemon/graphdriver/driver_linux.go b/daemon/graphdriver/driver_linux.go index f819adfae9..4341b763aa 100644 --- a/daemon/graphdriver/driver_linux.go +++ b/daemon/graphdriver/driver_linux.go @@ -50,7 +50,7 @@ const ( var ( // List of drivers that should be used in an order - priority = "overlay2,fuse-overlayfs,btrfs,zfs,overlay,devicemapper,vfs" + priority = "overlay2,fuse-overlayfs,btrfs,zfs,devicemapper,vfs" // FsNames maps filesystem id to name of the filesystem. FsNames = map[FsMagic]string{ diff --git a/daemon/graphdriver/overlay/overlay.go b/daemon/graphdriver/overlay/overlay.go deleted file mode 100644 index 9da1f9ae7e..0000000000 --- a/daemon/graphdriver/overlay/overlay.go +++ /dev/null @@ -1,484 +0,0 @@ -//go:build linux -// +build linux - -package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay" - -import ( - "fmt" - "io" - "os" - "path" - "path/filepath" - "strconv" - "strings" - - "github.com/containerd/continuity/fs" - "github.com/docker/docker/daemon/graphdriver" - "github.com/docker/docker/daemon/graphdriver/copy" - "github.com/docker/docker/daemon/graphdriver/overlayutils" - "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/containerfs" - "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/parsers" - "github.com/moby/locker" - "github.com/moby/sys/mount" - "github.com/opencontainers/selinux/go-selinux/label" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" -) - -// This is a small wrapper over the NaiveDiffWriter that lets us have a custom -// implementation of ApplyDiff() - -var ( - // ErrApplyDiffFallback is returned to indicate that a normal ApplyDiff is applied as a fallback from Naive diff writer. - ErrApplyDiffFallback = fmt.Errorf("Fall back to normal ApplyDiff") - backingFs = "" -) - -// ApplyDiffProtoDriver wraps the ProtoDriver by extending the interface with ApplyDiff method. -type ApplyDiffProtoDriver interface { - graphdriver.ProtoDriver - // ApplyDiff writes the diff to the archive for the given id and parent id. - // It returns the size in bytes written if successful, an error ErrApplyDiffFallback is returned otherwise. - ApplyDiff(id, parent string, diff io.Reader) (size int64, err error) -} - -type naiveDiffDriverWithApply struct { - graphdriver.Driver - applyDiff ApplyDiffProtoDriver -} - -// NaiveDiffDriverWithApply returns a NaiveDiff driver with custom ApplyDiff. -func NaiveDiffDriverWithApply(driver ApplyDiffProtoDriver, idMap idtools.IdentityMapping) graphdriver.Driver { - return &naiveDiffDriverWithApply{ - Driver: graphdriver.NewNaiveDiffDriver(driver, idMap), - applyDiff: driver, - } -} - -// ApplyDiff creates a diff layer with either the NaiveDiffDriver or with a fallback. -func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff io.Reader) (int64, error) { - b, err := d.applyDiff.ApplyDiff(id, parent, diff) - if err == ErrApplyDiffFallback { - return d.Driver.ApplyDiff(id, parent, diff) - } - return b, err -} - -// This backend uses the overlay union filesystem for containers -// plus hard link file sharing for images. - -// Each container/image can have a "root" subdirectory which is a plain -// filesystem hierarchy, or they can use overlay. - -// If they use overlay there is a "upper" directory and a "lower-id" -// file, as well as "merged" and "work" directories. The "upper" -// directory has the upper layer of the overlay, and "lower-id" contains -// the id of the parent whose "root" directory shall be used as the lower -// layer in the overlay. The overlay itself is mounted in the "merged" -// directory, and the "work" dir is needed for overlay to work. - -// When an overlay layer is created there are two cases, either the -// parent has a "root" dir, then we start out with an empty "upper" -// directory overlaid on the parents root. This is typically the -// case with the init layer of a container which is based on an image. -// If there is no "root" in the parent, we inherit the lower-id from -// the parent and start by making a copy in the parent's "upper" dir. -// This is typically the case for a container layer which copies -// its parent -init upper layer. - -// Additionally we also have a custom implementation of ApplyLayer -// which makes a recursive copy of the parent "root" layer using -// hardlinks to share file data, and then applies the layer on top -// of that. This means all child images share file (but not directory) -// data with the parent. - -type overlayOptions struct{} - -// Driver contains information about the home directory and the list of active mounts that are created using this driver. -type Driver struct { - home string - idMap idtools.IdentityMapping - ctr *graphdriver.RefCounter - supportsDType bool - locker *locker.Locker -} - -func init() { - graphdriver.Register("overlay", Init) -} - -// Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem. -// If overlay filesystem is not supported on the host, the error -// graphdriver.ErrNotSupported is returned. -// If an overlay filesystem is not supported over an existing filesystem then -// error graphdriver.ErrIncompatibleFS is returned. -func Init(home string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { - _, err := parseOptions(options) - if err != nil { - return nil, err - } - - // Perform feature detection on /var/lib/docker/overlay if it's an existing directory. - // This covers situations where /var/lib/docker/overlay is a mount, and on a different - // filesystem than /var/lib/docker. - // If the path does not exist, fall back to using /var/lib/docker for feature detection. - testdir := home - if _, err := os.Stat(testdir); os.IsNotExist(err) { - testdir = filepath.Dir(testdir) - } - - if err := overlayutils.SupportsOverlay(testdir, false); err != nil { - logrus.WithField("storage-driver", "overlay").Error(err) - return nil, graphdriver.ErrNotSupported - } - - fsMagic, err := graphdriver.GetFSMagic(testdir) - if err != nil { - return nil, err - } - if fsName, ok := graphdriver.FsNames[fsMagic]; ok { - backingFs = fsName - } - - supportsDType, err := fs.SupportsDType(testdir) - if err != nil { - return nil, err - } - if !supportsDType { - return nil, overlayutils.ErrDTypeNotSupported("overlay", backingFs) - } - - currentID := idtools.CurrentIdentity() - dirID := idtools.Identity{ - UID: currentID.UID, - GID: idMap.RootPair().GID, - } - - // Create the driver home dir - if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { - return nil, err - } - d := &Driver{ - home: home, - idMap: idMap, - ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), - supportsDType: supportsDType, - locker: locker.New(), - } - - return NaiveDiffDriverWithApply(d, d.idMap), nil -} - -func parseOptions(options []string) (*overlayOptions, error) { - o := &overlayOptions{} - for _, option := range options { - key, _, err := parsers.ParseKeyValueOpt(option) - if err != nil { - return nil, err - } - key = strings.ToLower(key) - switch key { - default: - return nil, fmt.Errorf("overlay: unknown option %s", key) - } - } - return o, nil -} - -func (d *Driver) String() string { - return "overlay" -} - -// Status returns current driver information in a two dimensional string array. -// Output contains "Backing Filesystem" used in this implementation. -func (d *Driver) Status() [][2]string { - return [][2]string{ - {"Backing Filesystem", backingFs}, - {"Supports d_type", strconv.FormatBool(d.supportsDType)}, - } -} - -// GetMetadata returns metadata about the overlay driver such as root, -// LowerDir, UpperDir, WorkDir and MergeDir used to store data. -func (d *Driver) GetMetadata(id string) (map[string]string, error) { - dir := d.dir(id) - if _, err := os.Stat(dir); err != nil { - return nil, err - } - - metadata := make(map[string]string) - - // If id has a root, it is an image - rootDir := path.Join(dir, "root") - if _, err := os.Stat(rootDir); err == nil { - metadata["RootDir"] = rootDir - return metadata, nil - } - - lowerID, err := os.ReadFile(path.Join(dir, "lower-id")) - if err != nil { - return nil, err - } - - metadata["LowerDir"] = path.Join(d.dir(string(lowerID)), "root") - metadata["UpperDir"] = path.Join(dir, "upper") - metadata["WorkDir"] = path.Join(dir, "work") - metadata["MergedDir"] = path.Join(dir, "merged") - - return metadata, nil -} - -// Cleanup any state created by overlay which should be cleaned when daemon -// is being shutdown. For now, we just have to unmount the bind mounted -// we had created. -func (d *Driver) Cleanup() error { - return mount.RecursiveUnmount(d.home) -} - -// CreateReadWrite creates a layer that is writable for use as a container -// file system. -func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { - return d.Create(id, parent, opts) -} - -// Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. -// The parent filesystem is used to configure these directories for the overlay. -func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { - if opts != nil && len(opts.StorageOpt) != 0 { - return fmt.Errorf("--storage-opt is not supported for overlay") - } - - dir := d.dir(id) - root := d.idMap.RootPair() - - currentID := idtools.CurrentIdentity() - dirID := idtools.Identity{ - UID: currentID.UID, - GID: root.GID, - } - if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil { - return err - } - - defer func() { - // Clean up on failure - if retErr != nil { - os.RemoveAll(dir) - } - }() - - // Toplevel images are just a "root" dir - if parent == "" { - // This must be 0755 otherwise unprivileged users will in the container will not be able to read / in the container - return idtools.MkdirAndChown(path.Join(dir, "root"), 0755, root) - } - - parentDir := d.dir(parent) - - // Ensure parent exists - if _, err := os.Lstat(parentDir); err != nil { - return err - } - - // If parent has a root, just do an overlay to it - parentRoot := path.Join(parentDir, "root") - - if s, err := os.Lstat(parentRoot); err == nil { - if err := idtools.MkdirAndChown(path.Join(dir, "upper"), s.Mode(), root); err != nil { - return err - } - if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { - return err - } - return os.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0600) - } - - // Otherwise, copy the upper and the lower-id from the parent - - lowerID, err := os.ReadFile(path.Join(parentDir, "lower-id")) - if err != nil { - return err - } - - if err := os.WriteFile(path.Join(dir, "lower-id"), lowerID, 0600); err != nil { - return err - } - - parentUpperDir := path.Join(parentDir, "upper") - s, err := os.Lstat(parentUpperDir) - if err != nil { - return err - } - - upperDir := path.Join(dir, "upper") - if err := idtools.MkdirAndChown(upperDir, s.Mode(), root); err != nil { - return err - } - if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { - return err - } - - return copy.DirCopy(parentUpperDir, upperDir, copy.Content, true) -} - -func (d *Driver) dir(id string) string { - return path.Join(d.home, id) -} - -// Remove cleans the directories that are created for this id. -func (d *Driver) Remove(id string) error { - if id == "" { - return fmt.Errorf("refusing to remove the directories: id is empty") - } - d.locker.Lock(id) - defer d.locker.Unlock(id) - return containerfs.EnsureRemoveAll(d.dir(id)) -} - -// Get creates and mounts the required file system for the given id and returns the mount path. -func (d *Driver) Get(id, mountLabel string) (_ string, err error) { - d.locker.Lock(id) - defer d.locker.Unlock(id) - dir := d.dir(id) - if _, err := os.Stat(dir); err != nil { - return "", err - } - // If id has a root, just return it - rootDir := path.Join(dir, "root") - if _, err := os.Stat(rootDir); err == nil { - return rootDir, nil - } - - mergedDir := path.Join(dir, "merged") - if count := d.ctr.Increment(mergedDir); count > 1 { - return mergedDir, nil - } - defer func() { - if err != nil { - if c := d.ctr.Decrement(mergedDir); c <= 0 { - if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil { - logrus.WithField("storage-driver", "overlay").Debugf("Failed to unmount %s: %v: %v", id, mntErr, err) - } - // Cleanup the created merged directory; see the comment in Put's rmdir - if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) { - logrus.WithField("storage-driver", "overlay").Warnf("Failed to remove %s: %v: %v", id, rmErr, err) - } - } - } - }() - lowerID, err := os.ReadFile(path.Join(dir, "lower-id")) - if err != nil { - return "", err - } - root := d.idMap.RootPair() - if err := idtools.MkdirAndChown(mergedDir, 0700, root); err != nil { - return "", err - } - var ( - lowerDir = path.Join(d.dir(string(lowerID)), "root") - upperDir = path.Join(dir, "upper") - workDir = path.Join(dir, "work") - opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) - ) - if err := unix.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { - return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) - } - // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a - // user namespace requires this to move a directory from lower to upper. - if err := root.Chown(path.Join(workDir, "work")); err != nil { - return "", err - } - return mergedDir, nil -} - -// Put unmounts the mount path created for the give id. -// It also removes the 'merged' directory to force the kernel to unmount the -// overlay mount in other namespaces. -func (d *Driver) Put(id string) error { - d.locker.Lock(id) - defer d.locker.Unlock(id) - // If id has a root, just return - if _, err := os.Stat(path.Join(d.dir(id), "root")); err == nil { - return nil - } - mountpoint := path.Join(d.dir(id), "merged") - logger := logrus.WithField("storage-driver", "overlay") - if count := d.ctr.Decrement(mountpoint); count > 0 { - return nil - } - if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil { - logger.Debugf("Failed to unmount %s overlay: %v", id, err) - } - - // Remove the mountpoint here. Removing the mountpoint (in newer kernels) - // will cause all other instances of this mount in other mount namespaces - // to be unmounted. This is necessary to avoid cases where an overlay mount - // that is present in another namespace will cause subsequent mounts - // operations to fail with ebusy. We ignore any errors here because this may - // fail on older kernels which don't have - // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. - if err := unix.Rmdir(mountpoint); err != nil { - logger.Debugf("Failed to remove %s overlay: %v", id, err) - } - return nil -} - -// ApplyDiff applies the new layer on top of the root, if parent does not exist with will return an ErrApplyDiffFallback error. -func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) { - dir := d.dir(id) - - if parent == "" { - return 0, ErrApplyDiffFallback - } - - parentRootDir := path.Join(d.dir(parent), "root") - if _, err := os.Stat(parentRootDir); err != nil { - return 0, ErrApplyDiffFallback - } - - // We now know there is a parent, and it has a "root" directory containing - // the full root filesystem. We can just hardlink it and apply the - // layer. This relies on two things: - // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container - // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) - // These are all currently true and are not expected to break - - tmpRootDir, err := os.MkdirTemp(dir, "tmproot") - if err != nil { - return 0, err - } - defer func() { - if err != nil { - os.RemoveAll(tmpRootDir) - } else { - os.RemoveAll(path.Join(dir, "upper")) - os.RemoveAll(path.Join(dir, "work")) - os.RemoveAll(path.Join(dir, "merged")) - os.RemoveAll(path.Join(dir, "lower-id")) - } - }() - - if err = copy.DirCopy(parentRootDir, tmpRootDir, copy.Hardlink, true); err != nil { - return 0, err - } - - options := &archive.TarOptions{IDMap: d.idMap} - if size, err = graphdriver.ApplyUncompressedLayer(tmpRootDir, diff, options); err != nil { - return 0, err - } - - rootDir := path.Join(dir, "root") - if err := os.Rename(tmpRootDir, rootDir); err != nil { - return 0, err - } - - return -} - -// Exists checks to see if the id is already mounted. -func (d *Driver) Exists(id string) bool { - _, err := os.Stat(d.dir(id)) - return err == nil -} diff --git a/daemon/graphdriver/overlay/overlay_test.go b/daemon/graphdriver/overlay/overlay_test.go deleted file mode 100644 index 414d5f97c4..0000000000 --- a/daemon/graphdriver/overlay/overlay_test.go +++ /dev/null @@ -1,94 +0,0 @@ -//go:build linux -// +build linux - -package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay" - -import ( - "testing" - - "github.com/docker/docker/daemon/graphdriver" - "github.com/docker/docker/daemon/graphdriver/graphtest" - "github.com/docker/docker/pkg/archive" -) - -func init() { - // Do not sure chroot to speed run time and allow archive - // errors or hangs to be debugged directly from the test process. - graphdriver.ApplyUncompressedLayer = archive.ApplyUncompressedLayer -} - -// This avoids creating a new driver for each test if all tests are run -// Make sure to put new tests between TestOverlaySetup and TestOverlayTeardown -func TestOverlaySetup(t *testing.T) { - graphtest.GetDriver(t, "overlay") -} - -func TestOverlayCreateEmpty(t *testing.T) { - graphtest.DriverTestCreateEmpty(t, "overlay") -} - -func TestOverlayCreateBase(t *testing.T) { - graphtest.DriverTestCreateBase(t, "overlay") -} - -func TestOverlayCreateSnap(t *testing.T) { - graphtest.DriverTestCreateSnap(t, "overlay") -} - -func TestOverlay50LayerRead(t *testing.T) { - graphtest.DriverTestDeepLayerRead(t, 50, "overlay") -} - -// Fails due to bug in calculating changes after apply -// likely related to https://github.com/docker/docker/issues/21555 -func TestOverlayDiffApply10Files(t *testing.T) { - t.Skipf("Fails to compute changes after apply intermittently") - graphtest.DriverTestDiffApply(t, 10, "overlay") -} - -func TestOverlayChanges(t *testing.T) { - t.Skipf("Fails to compute changes intermittently") - graphtest.DriverTestChanges(t, "overlay") -} - -func TestOverlayTeardown(t *testing.T) { - graphtest.PutDriver(t) -} - -// Benchmarks should always setup new driver - -func BenchmarkExists(b *testing.B) { - graphtest.DriverBenchExists(b, "overlay") -} - -func BenchmarkGetEmpty(b *testing.B) { - graphtest.DriverBenchGetEmpty(b, "overlay") -} - -func BenchmarkDiffBase(b *testing.B) { - graphtest.DriverBenchDiffBase(b, "overlay") -} - -func BenchmarkDiffSmallUpper(b *testing.B) { - graphtest.DriverBenchDiffN(b, 10, 10, "overlay") -} - -func BenchmarkDiff10KFileUpper(b *testing.B) { - graphtest.DriverBenchDiffN(b, 10, 10000, "overlay") -} - -func BenchmarkDiff10KFilesBottom(b *testing.B) { - graphtest.DriverBenchDiffN(b, 10000, 10, "overlay") -} - -func BenchmarkDiffApply100(b *testing.B) { - graphtest.DriverBenchDiffApplyN(b, 100, "overlay") -} - -func BenchmarkDiff20Layers(b *testing.B) { - graphtest.DriverBenchDeepLayerDiff(b, 20, "overlay") -} - -func BenchmarkRead20Layers(b *testing.B) { - graphtest.DriverBenchDeepLayerRead(b, 20, "overlay") -} diff --git a/daemon/graphdriver/overlay/overlay_unsupported.go b/daemon/graphdriver/overlay/overlay_unsupported.go deleted file mode 100644 index 73128b58cd..0000000000 --- a/daemon/graphdriver/overlay/overlay_unsupported.go +++ /dev/null @@ -1,4 +0,0 @@ -//go:build !linux -// +build !linux - -package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay" diff --git a/daemon/graphdriver/register/register_overlay.go b/daemon/graphdriver/register/register_overlay.go deleted file mode 100644 index 9b6c8c36db..0000000000 --- a/daemon/graphdriver/register/register_overlay.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !exclude_graphdriver_overlay && linux -// +build !exclude_graphdriver_overlay,linux - -package register // import "github.com/docker/docker/daemon/graphdriver/register" - -import ( - // register the overlay graphdriver - _ "github.com/docker/docker/daemon/graphdriver/overlay" -)